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 37 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
97 changes: 97 additions & 0 deletions src/bin/src/packet_handlers/containers.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
use ferrumc_inventory::inventory::{Inventory, InventorySyncType};
use ferrumc_inventory::slot::Slot;
use ferrumc_inventory::types::player::PlayerInventory;
use ferrumc_macros::event_handler;
use ferrumc_net::errors::NetError;
use ferrumc_net::packets::incoming::click_container::InventoryClickEvent;
use ferrumc_net::packets::incoming::close_container::InventoryCloseEvent;
use ferrumc_net::packets::incoming::set_creative_mode_slot::SetCreativeModeSlotEvent;
use ferrumc_net::packets::incoming::set_held_item::ChangeSlotEvent;
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;

let window_id = container_close_event.window_id;
if window_id != 0 {
state.universe.remove_component::<Inventory>(conn_id)?;
}

Ok(container_close_event)
}

#[event_handler]
async fn handle_container_click(
inventory_click_event: InventoryClickEvent,
state: GlobalState,
) -> Result<InventoryClickEvent, NetError> {
if inventory_click_event.is_canceled {
return Err(NetError::Other(String::default()));
}

let conn_id = inventory_click_event.conn_id;
let packet = &inventory_click_event.packet;
let mut inventory = state.universe.get_mut::<Inventory>(conn_id)?;

if inventory.is_synced {
match packet.changed_slots.data.as_slice() {
[changed_slot] => {
let slot_num = changed_slot.slot_number;

inventory
.set_slot(slot_num, Slot::from_network_slot(changed_slot.slot))
.sync_inventory(conn_id, &InventorySyncType::Single(slot_num), state)
.await
.map_err(|err| NetError::Other(err.to_string()))?;
}
changed_slots => {
changed_slots.iter().for_each(|changed_slot| {
inventory.set_slot(
changed_slot.slot_number,
Slot::from_network_slot(changed_slot.slot),
);
});

inventory
.sync_inventory(conn_id, &InventorySyncType::All, state)
.await
.map_err(|err| NetError::Other(err.to_string()))?;
}
}
}

Ok(inventory_click_event)
}

#[event_handler]
async fn set_creative_mode_slot(
creative_mode_slot: SetCreativeModeSlotEvent,
state: GlobalState,
) -> Result<SetCreativeModeSlotEvent, NetError> {
let conn_id = creative_mode_slot.conn_id;

let mut inventory = state.universe.get_mut::<PlayerInventory>(conn_id)?;
inventory.set_slot(
creative_mode_slot.slot,
Slot::from_network_slot(creative_mode_slot.clicked_item),
);

Ok(creative_mode_slot)
}

#[event_handler]
async fn handle_carried_item(
change_slot_event: ChangeSlotEvent,
state: GlobalState,
) -> Result<ChangeSlotEvent, NetError> {
let conn_id = change_slot_event.conn_id;

let mut inventory = state.universe.get_mut::<PlayerInventory>(conn_id)?;
inventory.set_carried_item(change_slot_event.slot);

Ok(change_slot_event)
}
189 changes: 60 additions & 129 deletions src/bin/src/packet_handlers/login_process.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ 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_ecs::entities::Entity;
use ferrumc_inventory::types::player::PlayerInventory;
use ferrumc_macros::event_handler;
use ferrumc_net::connection::{ConnectionState, StreamWriter};
use ferrumc_net::errors::NetError;
Expand All @@ -21,19 +21,13 @@ use ferrumc_net::packets::outgoing::keep_alive::OutgoingKeepAlivePacket;
use ferrumc_net::packets::outgoing::login_disconnect::LoginDisconnectPacket;
use ferrumc_net::packets::outgoing::login_play::LoginPlayPacket;
use ferrumc_net::packets::outgoing::login_success::LoginSuccessPacket;
use ferrumc_net::packets::outgoing::player_info_update::PlayerInfoUpdatePacket;
use ferrumc_net::packets::outgoing::registry_data::get_registry_packets;
use ferrumc_net::packets::outgoing::set_center_chunk::SetCenterChunk;
use ferrumc_net::packets::outgoing::set_default_spawn_position::SetDefaultSpawnPositionPacket;
use ferrumc_net::packets::outgoing::set_render_distance::SetRenderDistance;
use ferrumc_net::packets::outgoing::spawn_entity::SpawnEntityPacket;
use ferrumc_net::packets::outgoing::synchronize_player_position::SynchronizePlayerPositionPacket;
use ferrumc_net::utils::broadcast::{broadcast, get_all_play_players, BroadcastOptions};
use ferrumc_net::NetResult;
use ferrumc_net_codec::encode::NetEncodeOpts;
use ferrumc_state::GlobalState;
use futures::StreamExt;
use std::time::Instant;
use tracing::{debug, trace};

#[event_handler]
Expand All @@ -53,7 +47,7 @@ async fn handle_login_start(
login_start_event.conn_id,
PlayerIdentity::new(username.to_string(), uuid),
)?
/*.add_component::<ChunkReceiver>(login_start_event.conn_id, ChunkReceiver::default())?*/;
.add_component::<ChunkReceiver>(login_start_event.conn_id, ChunkReceiver::default())?;

//Send a Login Success Response to further the login sequence
let mut writer = state
Expand Down Expand Up @@ -152,73 +146,70 @@ async fn handle_ack_finish_configuration(
state: GlobalState,
) -> Result<AckFinishConfigurationEvent, NetError> {
trace!("Handling Ack Finish Configuration event");
let entity_id = ack_finish_configuration_event.conn_id;
{
let mut conn_state = state.universe.get_mut::<ConnectionState>(entity_id)?;

*conn_state = ConnectionState::Play;

// add components to the entity after the connection state has been set to play.
// to avoid wasting resources on entities that are fetching stuff like server status etc.
state
.universe
.add_component::<Position>(entity_id, Position::default())?
.add_component::<Rotation>(entity_id, Rotation::default())?
.add_component::<OnGround>(entity_id, OnGround::default())?
.add_component::<ChunkReceiver>(entity_id, ChunkReceiver::default())?;

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

writer // 21
.send_packet(&LoginPlayPacket::new(entity_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?;

send_keep_alive(entity_id, &state, &mut writer).await?;

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

player_info_update_packets(entity_id, &state).await?;
broadcast_spawn_entity_packet(entity_id, &state).await?;
Outspending marked this conversation as resolved.
Show resolved Hide resolved
let conn_id = ack_finish_configuration_event.conn_id;

let mut conn_state = state.universe.get_mut::<ConnectionState>(conn_id)?;

*conn_state = ConnectionState::Play;

// add components to the entity after the connection state has been set to play.
// to avoid wasting resources on entities that are fetching stuff like server status etc.
state
.universe
.add_component::<Position>(conn_id, Position::default())?
.add_component::<Rotation>(conn_id, Rotation::default())?
.add_component::<OnGround>(conn_id, OnGround::default())?
.add_component::<PlayerInventory>(conn_id, PlayerInventory::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 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, &mut writer).await?;

Ok(ack_finish_configuration_event)
}
async fn send_keep_alive(
conn_id: usize,
state: &GlobalState,
state: GlobalState,
writer: &mut ComponentRefMut<'_, StreamWriter>,
) -> Result<(), NetError> {
let keep_alive_packet = OutgoingKeepAlivePacket::default();
Expand All @@ -237,63 +228,3 @@ async fn send_keep_alive(

Ok(())
}

async fn player_info_update_packets(entity_id: Entity, state: &GlobalState) -> NetResult<()> {
Outspending marked this conversation as resolved.
Show resolved Hide resolved
// Broadcasts a player info update packet to all players.
{
let packet = PlayerInfoUpdatePacket::new_player_join_packet(entity_id, state);

let start = Instant::now();
broadcast(
&packet,
state,
BroadcastOptions::default().except([entity_id]),
)
.await?;
trace!(
"Broadcasting player info update took: {:?}",
start.elapsed()
);
}

// Tell the player about all the other players that are already connected.
{
let packet = PlayerInfoUpdatePacket::existing_player_info_packet(entity_id, state);

let start = Instant::now();
let mut writer = state.universe.get_mut::<StreamWriter>(entity_id)?;
writer
.send_packet(&packet, &NetEncodeOpts::WithLength)
.await?;
debug!("Sending player info update took: {:?}", start.elapsed());
}

Ok(())
}

async fn broadcast_spawn_entity_packet(entity_id: Entity, state: &GlobalState) -> NetResult<()> {
Outspending marked this conversation as resolved.
Show resolved Hide resolved
let packet = SpawnEntityPacket::player(entity_id, state)?;

let start = Instant::now();
broadcast(
&packet,
state,
BroadcastOptions::default().except([entity_id]),
)
.await?;
trace!("Broadcasting spawn entity took: {:?}", start.elapsed());

let writer = state.universe.get_mut::<StreamWriter>(entity_id)?;
futures::stream::iter(get_all_play_players(state))
.fold(writer, |mut writer, entity| async move {
if let Ok(packet) = SpawnEntityPacket::player(entity, state) {
let _ = writer
.send_packet(&packet, &NetEncodeOpts::WithLength)
.await;
}
writer
})
.await;

Ok(())
}
1 change: 1 addition & 0 deletions src/bin/src/packet_handlers/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
mod animations;
mod containers;
mod handshake;
mod login_process;
mod player;
Expand Down
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
Loading
Loading