Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Inventories #151

Open
wants to merge 43 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 27 commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
4d5527a
Simple Inventory Implementation
Outspending Dec 29, 2024
06899a8
better slot system + sized inventories
Outspending Dec 29, 2024
5cfaa0a
Simple Viewer System
Outspending Dec 30, 2024
7dbdab0
QOL Changes
Outspending Dec 30, 2024
e542d66
tests
Outspending Dec 30, 2024
0acaa1b
Added all related container packets
Outspending Dec 30, 2024
bd973c4
container content
Outspending Dec 30, 2024
d47862b
InventoryData + viewer support
Outspending Dec 30, 2024
802c8b8
updated viewers
Outspending Dec 30, 2024
b9831f9
clippy fix
Outspending Dec 30, 2024
09bbcc4
clippy + fmt check fixed
Outspending Dec 30, 2024
cb9ee29
Merge branch 'feat/inventories' of https://github.com/Outspending/fer…
Outspending Dec 30, 2024
767f8fa
Update login_process.rs
Outspending Dec 30, 2024
26e4794
inventory events
Outspending Dec 30, 2024
cd492a7
Simple ECS Implementation
Outspending Dec 30, 2024
a765aac
Merge branch 'master' into feat/inventories
Outspending Dec 30, 2024
7f61449
Fixed Inventories + More Packets
Outspending Dec 30, 2024
1d127c4
ECS Compatibility
Outspending Dec 30, 2024
cd09d0f
better error handling
Outspending Dec 30, 2024
816391b
Inventory Clicking
Outspending Dec 30, 2024
63ad32e
better inventory packets
Outspending Dec 31, 2024
91ba5f3
Merge remote-tracking branch 'upstream/master' into feat/inventories
Outspending Dec 31, 2024
f2acd8d
content packet
Outspending Dec 31, 2024
0eac9fe
Merge pull request #145 from Outspending/feat/inventories
Sweattypalms Dec 31, 2024
7d404b2
Fixed Container Content Packet
Outspending Dec 31, 2024
dd1b152
inventory syncing
Outspending Dec 31, 2024
1e0dbab
inventory sync check
Outspending Dec 31, 2024
5619a63
Player / Creative Inventory Support
Outspending Jan 4, 2025
e39b090
Inventory Clicking + Updating
Outspending Jan 4, 2025
7628ada
InventoryBuilder + type fixes
Outspending Jan 4, 2025
6dd2513
clippy fix + fmt
Outspending Jan 4, 2025
7a9b518
Inventory Types + Macros
Outspending Jan 5, 2025
8d90ee5
Merge remote-tracking branch 'upstream/master' into feature/inventories
Outspending Jan 5, 2025
2ff5a83
fixed merge conflicts
Outspending Jan 5, 2025
4a59780
Inventory Types
Outspending Jan 5, 2025
14788fb
format fix
Outspending Jan 5, 2025
3871f59
Update login_process.rs
Outspending Jan 5, 2025
23d624e
Fixed merge issues
Outspending Jan 5, 2025
70f9cde
Stuff
Outspending Jan 5, 2025
6c3a8ba
All Inventory Types + Better Macro Support
Outspending Jan 5, 2025
c44ee95
Update mod.rs
Outspending Jan 5, 2025
20ceb40
clippy fix
Outspending Jan 5, 2025
2db4348
Components (ish) [Wont Compile]
Outspending Jan 5, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ members = [
"src/lib/derive_macros",
"src/lib/derive_macros",
"src/lib/ecs",
"src/lib/events",
"src/lib/events", "src/lib/inventory",
"src/lib/net",
"src/lib/net/crates/codec",
"src/lib/net/crates/encryption",
Expand Down Expand Up @@ -94,6 +94,7 @@ ferrumc-logging = { path = "src/lib/utils/logging" }
ferrumc-macros = { path = "src/lib/derive_macros" }
ferrumc-nbt = { path = "src/lib/adapters/nbt" }
ferrumc-net = { path = "src/lib/net" }
ferrumc-inventory = { path = "src/lib/inventory" }
ferrumc-net-codec = { path = "src/lib/net/crates/codec" }
ferrumc-net-encryption = { path = "src/lib/net/crates/encryption" }
ferrumc-plugins = { path = "src/lib/plugins" }
Expand Down
1 change: 1 addition & 0 deletions src/bin/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ ferrumc-plugins = { workspace = true }
ferrumc-storage = { workspace = true }
ferrumc-utils = { workspace = true }
ferrumc-config = { workspace = true }
ferrumc-inventory = { workspace = true }
ferrumc-profiling = { workspace = true }
ferrumc-logging = { workspace = true }
ferrumc-world = { workspace = true }
Expand Down
16 changes: 16 additions & 0 deletions src/bin/src/packet_handlers/containers.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
use ferrumc_inventory::inventory::Inventory;
use ferrumc_macros::event_handler;
use ferrumc_net::errors::NetError;
use ferrumc_net::packets::incoming::close_container::InventoryCloseEvent;
use ferrumc_state::GlobalState;

#[event_handler]
async fn container_close(
container_close_event: InventoryCloseEvent,
state: GlobalState,
) -> Result<InventoryCloseEvent, NetError> {
let conn_id = container_close_event.conn_id;

state.universe.remove_component::<Inventory>(conn_id)?;
Ok(container_close_event)
}
90 changes: 50 additions & 40 deletions src/bin/src/packet_handlers/login_process.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ use ferrumc_core::transform::grounded::OnGround;
use ferrumc_core::transform::position::Position;
use ferrumc_core::transform::rotation::Rotation;
use ferrumc_ecs::components::storage::ComponentRefMut;
use ferrumc_inventory::inventory::{Inventory, InventoryType};
use ferrumc_inventory::slot::Slot;
use ferrumc_macros::event_handler;
use ferrumc_net::connection::{ConnectionState, StreamWriter};
use ferrumc_net::errors::NetError;
Expand Down Expand Up @@ -160,51 +162,59 @@ async fn handle_ack_finish_configuration(
.add_component::<Rotation>(conn_id, Rotation::default())?
.add_component::<OnGround>(conn_id, OnGround::default())?;

let mut writer = state.universe.get_mut::<StreamWriter>(conn_id)?;

writer // 21
.send_packet(&LoginPlayPacket::new(conn_id), &NetEncodeOpts::WithLength)
.await?;
writer // 29
.send_packet(
&SynchronizePlayerPositionPacket::default(), // The coordinates here should be used for the center chunk.
&NetEncodeOpts::WithLength,
)
.await?;
writer // 37
.send_packet(
&SetDefaultSpawnPositionPacket::default(), // Player specific, aka. home, bed, where it would respawn.
&NetEncodeOpts::WithLength,
)
.await?;
writer // 38
.send_packet(
&GameEventPacket::start_waiting_for_level_chunks(),
&NetEncodeOpts::WithLength,
)
.await?;
writer // 41
.send_packet(
&SetCenterChunk::new(0, 0), // TODO - Dependent on the player spawn position.
&NetEncodeOpts::WithLength,
)
.await?;
writer // other
.send_packet(
&SetRenderDistance::new(5), // TODO
&NetEncodeOpts::WithLength,
)
.await?;
{
let mut writer = state.universe.get_mut::<StreamWriter>(conn_id)?;

writer // 21
.send_packet(&LoginPlayPacket::new(conn_id), &NetEncodeOpts::WithLength)
.await?;
writer // 29
.send_packet(
&SynchronizePlayerPositionPacket::default(), // The coordinates here should be used for the center chunk.
&NetEncodeOpts::WithLength,
)
.await?;
writer // 37
.send_packet(
&SetDefaultSpawnPositionPacket::default(), // Player specific, aka. home, bed, where it would respawn.
&NetEncodeOpts::WithLength,
)
.await?;
writer // 38
.send_packet(
&GameEventPacket::start_waiting_for_level_chunks(),
&NetEncodeOpts::WithLength,
)
.await?;
writer // 41
.send_packet(
&SetCenterChunk::new(0, 0), // TODO - Dependent on the player spawn position.
&NetEncodeOpts::WithLength,
)
.await?;
writer // other
.send_packet(
&SetRenderDistance::new(5), // TODO
&NetEncodeOpts::WithLength,
)
.await?;

let pos = state.universe.get_mut::<Position>(conn_id)?;
let mut chunk_recv = state.universe.get_mut::<ChunkReceiver>(conn_id)?;
chunk_recv.last_chunk = Some((pos.x as i32, pos.z as i32, String::from("overworld")));
chunk_recv.calculate_chunks().await;

send_keep_alive(conn_id, state.clone(), &mut writer).await?;
}

let pos = state.universe.get_mut::<Position>(conn_id)?;
let mut chunk_recv = state.universe.get_mut::<ChunkReceiver>(conn_id)?;
chunk_recv.last_chunk = Some((pos.x as i32, pos.z as i32, String::from("overworld")));
chunk_recv.calculate_chunks().await;
let mut inventory = Inventory::new(1, "Outspending's Inventory", InventoryType::Chest(6));
inventory.set_slot(0, Slot::with_item(1));

send_keep_alive(conn_id, state, &mut writer).await?;
inventory.add_viewer(state, conn_id).await.unwrap();

Ok(ack_finish_configuration_event)
}

async fn send_keep_alive(
conn_id: usize,
state: GlobalState,
Expand Down
3 changes: 2 additions & 1 deletion src/lib/derive_macros/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,5 @@ quote = { workspace = true }
syn = { workspace = true, features = ["full"] }
thiserror = { workspace = true }
proc-macro2 = { workspace = true }
proc-macro-crate = { workspace = true }
proc-macro-crate = { workspace = true }
regex = { workspace = true }
3 changes: 2 additions & 1 deletion src/lib/derive_macros/src/events/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,8 @@ pub(crate) fn derive(input: TokenStream) -> TokenStream {
quote! {crate}
}
FoundCrate::Name(name) => {
quote! {::#name}
let name = syn::Ident::new(&name, proc_macro2::Span::call_site());
quote! {#name}
}
};

Expand Down
33 changes: 17 additions & 16 deletions src/lib/derive_macros/src/net/decode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,8 @@ pub(crate) fn derive(input: TokenStream) -> TokenStream {
let mut decode_statements = Vec::new();
let mut field_names = Vec::new();

let optional_type_regex = regex::Regex::new(r"Option\s*<\s*(.+?)\s*>").unwrap();

for field in fields {
let field_name = field
.ident
Expand All @@ -168,24 +170,16 @@ pub(crate) fn derive(input: TokenStream) -> TokenStream {
// Check the `net(...)` attributes on this field
for attr in &field.attrs {
if attr.path().is_ident("net") {
// e.g., #[net(optional_trigger = { some_field == true })]

attr.parse_nested_meta(|meta| {
if let Some(ident) = meta.path.get_ident() {
if ident.to_string().as_str() == "optional_trigger" {
meta.parse_nested_meta(|meta| {
if let Some(expr) = meta.path.get_ident() {
let val = syn::parse_str::<syn::Expr>(&expr.to_string())
.expect("Failed to parse optional_trigger expression");
let value = meta.value().expect("Missing optional_trigger value");

optional_trigger_expr = Some(val);
} else {
panic!("Expected an expression for optional_trigger");
}
let value = value
.parse::<syn::Expr>()
.expect("Failed to parse optional_trigger");

Ok(())
})
.expect("Failed to parse optional_trigger expression");
optional_trigger_expr = Some(value);
}
}
Ok(())
Expand All @@ -196,9 +190,16 @@ pub(crate) fn derive(input: TokenStream) -> TokenStream {

// Generate decoding code depending on whether there's an optional trigger
if let Some(expr) = optional_trigger_expr {
// For an optional field, we decode it only if `expr` is true at runtime.
// We'll store the result in a local variable `field_name` which will be an Option<T>.
// Then at the end, we can build the struct using those local variables.
let field_type = quote! { #field_ty }.to_string();

let inner = optional_type_regex
.captures(&field_type)
.expect("Field must be Option<T>")
.get(1)
.unwrap()
.as_str();
let field_ty = syn::parse_str::<syn::Type>(inner).expect("Failed to parse field type");

decode_statements.push(quote! {
let #field_name = {
if #expr {
Expand Down
6 changes: 3 additions & 3 deletions src/lib/events/src/infrastructure.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,9 +73,9 @@ pub trait Event: Sized + Send + Sync + 'static {
///
/// Returns `Ok(())` if the execution succeeded. `Err(EventsError)` ifa listener failed.
async fn trigger(event: Self::Data, state: Self::State) -> Result<(), Self::Error> {
let listeners = EVENTS_LISTENERS
.get(Self::name())
.expect("Failed to find event listeners. Impossible;");
let Some(listeners) = EVENTS_LISTENERS.get(Self::name()) else {
return Ok(());
};

// Convert listeners iterator into Stream
stream::iter(listeners.iter())
Expand Down
23 changes: 23 additions & 0 deletions src/lib/inventory/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
[package]
name = "ferrumc-inventory"
description = "Implements Inventory Capablities to FerrumC"
version = "0.1.0"
edition = "2024"

[dependencies]
ferrumc-net = { workspace = true }
ferrumc-net-codec = { workspace = true }
ferrumc-text = { workspace = true }
ferrumc-ecs = { workspace = true }
ferrumc-events = { workspace = true }
ferrumc-macros = { workspace = true }
ferrumc-core = { workspace = true }
ferrumc-state = { workspace = true }

tokio = {workspace = true }
dashmap = { workspace = true }
thiserror = { workspace = true }
tracing = { workspace = true }

[lints]
workspace = true
50 changes: 50 additions & 0 deletions src/lib/inventory/src/contents.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
use crate::slot::Slot;
use ferrumc_net::packets::outgoing::set_container_slot::NetworkSlot;
use ferrumc_net_codec::net_types::length_prefixed_vec::LengthPrefixedVec;
use std::collections::BTreeMap;

#[derive(Debug, Clone)]
pub struct InventoryContents {
pub contents: BTreeMap<i32, Slot>,
pub size: usize,
Outspending marked this conversation as resolved.
Show resolved Hide resolved
}

impl InventoryContents {
pub fn empty(size: usize) -> Self {
let mut empty = Self {
contents: BTreeMap::new(),
size,
};

empty.fill(Slot::empty());
empty
}

pub fn fill(&mut self, slot: Slot) {
for i in 0..self.size as i32 {
self.contents.insert(i, slot);
}
}

pub fn set_slot(&mut self, slot_id: i32, slot: Slot) -> &mut Self {
self.contents.insert(slot_id, slot);
self
}

pub fn get_slot(&self, item: i32) -> Option<Slot> {
self.contents.get(&item).copied()
}

pub(crate) fn construct_packet_contents(&self) -> LengthPrefixedVec<NetworkSlot> {
let mut contents = vec![];
self.contents.iter().for_each(|(_, slot)| {
contents.push(slot.to_network_slot());
});

LengthPrefixedVec::new(contents)
}

//to store in chunk metadata: TAG 44: byte
//to show: starts at slot 0 ALWAYS - > 26/53 smalll/large.
//other inventories are to be implemented after.
}
21 changes: 21 additions & 0 deletions src/lib/inventory/src/events/inventory_open.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
use ferrumc_macros::Event;

#[derive(Event, Debug)]
pub struct OpenInventoryEvent {
pub conn_id: usize,
pub inventory_id: Option<i32>,
}

impl OpenInventoryEvent {
pub fn new(conn_id: usize) -> Self {
Self {
conn_id,
inventory_id: None,
}
}

pub fn inventory_id(mut self, inventory_id: i32) -> Self {
self.inventory_id = Some(inventory_id);
self
}
}
1 change: 1 addition & 0 deletions src/lib/inventory/src/events/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pub mod inventory_open;
Loading
Loading