Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
e823b3e
feat: player data storage initialized
KingTimer12 Aug 23, 2025
cae8c54
fix: cargo fmt and cargo audit
KingTimer12 Aug 23, 2025
2eeab0e
fix: cargo clippy
KingTimer12 Aug 23, 2025
4f8db95
feat: added sqlite support
KingTimer12 Aug 24, 2025
ed2e353
fix: cargo clippy
KingTimer12 Aug 24, 2025
67a9bf5
fix: benches test of storage
KingTimer12 Aug 24, 2025
4fff8d3
chore: changed to sqlite
KingTimer12 Sep 13, 2025
d9b0bfa
fix: cargo audit error
KingTimer12 Sep 13, 2025
400d746
fix: cargo fmt
KingTimer12 Sep 13, 2025
b7bea43
fix: cargo clippy
KingTimer12 Sep 13, 2025
a2b92ff
fix: cargo clippy
KingTimer12 Sep 13, 2025
234fb78
Merge branch 'master' of github.com:KingTimer12/ferrumc into feature/…
KingTimer12 Oct 19, 2025
64491ab
feat: player data storage initialized
KingTimer12 Oct 19, 2025
8ac0648
fix: cargo fmt and cargo audit
KingTimer12 Aug 23, 2025
cbfa307
fix: cargo clippy
KingTimer12 Aug 23, 2025
7fc22ef
feat: added sqlite support
KingTimer12 Aug 24, 2025
6d3c4c5
fix: cargo clippy
KingTimer12 Aug 24, 2025
9f0d3aa
fix: benches test of storage
KingTimer12 Aug 24, 2025
e093a28
chore: changed to sqlite
KingTimer12 Sep 13, 2025
cc4d935
fix: cargo audit error
KingTimer12 Sep 13, 2025
c383f0c
fix: cargo fmt
KingTimer12 Sep 13, 2025
813e9a3
fix: cargo clippy
KingTimer12 Sep 13, 2025
b35c2f5
fix: cargo clippy
KingTimer12 Sep 13, 2025
1ad2bca
Merge branch 'feature/player-state' of github.com:KingTimer12/ferrumc…
KingTimer12 Oct 19, 2025
b0e6aa7
chore: now is updating player data
KingTimer12 Oct 19, 2025
c408791
fix: i forgot fmt :p
KingTimer12 Oct 19, 2025
1a5ca03
chore: updated the way playerdata is used
KingTimer12 Oct 22, 2025
c31ee14
chore: saving data through event
KingTimer12 Oct 22, 2025
01f24fb
Merge branch 'master' of github.com:KingTimer12/ferrumc into feature/…
KingTimer12 Oct 23, 2025
b70b7b2
fix: problem in my gitignore; fix: cargo.toml with old dep;
KingTimer12 Oct 23, 2025
dcd5148
chore: added player disconnect event in connection killer
KingTimer12 Oct 23, 2025
3d8d02c
chore: removed PlayerState
KingTimer12 Oct 26, 2025
d386bd8
chore: added Display to player data
KingTimer12 Oct 26, 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
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ result-*
profile.json.gz
.DS_Store

world/
/world/
whitelist.txt

logs/
4 changes: 1 addition & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,12 @@ resolver = "2"
members = [
"src/bin",
"src/lib/adapters/anvil",
"src/lib/adapters/anvil",
"src/lib/adapters/nbt",
"src/lib/adapters/nbt",
"src/lib/commands",
"src/lib/default_commands",
"src/lib/core",
"src/lib/core/state",
"src/lib/derive_macros",
"src/lib/derive_macros",
"src/lib/net",
"src/lib/net/crates/codec",
"src/lib/net/crates/encryption",
Expand Down Expand Up @@ -186,6 +183,7 @@ yazi = "0.2.1"
# Database
heed = "0.22.0"
moka = "0.12.11"
rusqlite = { version = "0.37.0", features = ["bundled", "serde_json"] }

# CLI
clap = "4.5.49"
Expand Down
3 changes: 2 additions & 1 deletion src/bin/src/packet_handlers/mod.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
pub(crate) mod play_packets;
mod player;
pub mod player;

pub fn register_player_systems(schedule: &mut bevy_ecs::schedule::Schedule) {
schedule.add_systems(player::head_rot::handle_player_move);
schedule.add_systems(player::send_inventory_updates::handle_inventory_updates);
schedule.add_systems(player::player_disconnect::handle);
}
82 changes: 64 additions & 18 deletions src/bin/src/packet_handlers/play_packets/player_loaded.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
use bevy_ecs::prelude::{Entity, Query, Res};
use ferrumc_core::data::player::PlayerData;
use ferrumc_core::identity::player_identity::PlayerIdentity;
use ferrumc_core::transform::position::Position;
use ferrumc_net::connection::StreamWriter;
use ferrumc_net::packets::outgoing::synchronize_player_position::SynchronizePlayerPositionPacket;
Expand All @@ -10,10 +12,10 @@ use tracing::warn;
pub fn handle(
ev: Res<PlayerLoadedReceiver>,
state: Res<GlobalStateResource>,
query: Query<(Entity, &Position, &StreamWriter)>,
mut query: Query<(Entity, &PlayerIdentity, &StreamWriter, &mut Position)>,
) {
for (_, player) in ev.0.try_iter() {
let Ok((entity, player_pos, conn)) = query.get(player) else {
let Ok((entity, player_identity, conn, mut position)) = query.get_mut(player) else {
warn!("Player position not found in query.");
continue;
};
Expand All @@ -24,28 +26,72 @@ pub fn handle(
);
continue;
}

// Save the player's position in the world
match state
.0
.world
.load_player_state(player_identity.uuid.as_u128())
{
Ok(loaded) => match loaded {
Some(loaded_data) => {
*position =
Position::new(loaded_data.pos.x, loaded_data.pos.y, loaded_data.pos.z);
tracing::info!(
"Loaded player state for {}: {}",
player_identity.uuid.as_u128(),
loaded_data,
);
}
None => {
if let Err(e) = state
.0
.world
.save_player_state(player_identity.uuid.as_u128(), &PlayerData::default())
// First time saving player data
{
tracing::error!(
"Failed to save player state for {:?}: {:?}",
player_identity,
e
);
}
}
},
Err(e) => {
tracing::error!(
"Failed to load player state for {:?}: {:?}",
player_identity,
e
);
if let Err(e) = state
.0
.world
.save_player_state(player_identity.uuid.as_u128(), &PlayerData::default())
// First time saving player data
{
tracing::error!(
"Failed to save player state for {:?}: {:?}",
player_identity,
e
);
}
}
}
let head_block = state.0.world.get_block_and_fetch(
player_pos.x as i32,
player_pos.y as i32,
player_pos.z as i32,
position.x as i32,
position.y as i32,
position.z as i32,
"overworld",
);
if let Ok(head_block) = head_block {
if head_block == BlockId(0) {
tracing::info!(
"Player {} loaded at position: ({}, {}, {})",
player,
player_pos.x,
player_pos.y,
player_pos.z
);
tracing::info!("Player {} loaded at position: {:?}", player, position);
} else {
tracing::info!(
"Player {} loaded at position: ({}, {}, {}) with head block: {:?}",
"Player {} loaded at position: {:?} with head block: {:?}",
player,
player_pos.x,
player_pos.y,
player_pos.z,
position,
head_block
);
// Teleport the player to the world center if their head block is not air
Expand All @@ -65,8 +111,8 @@ pub fn handle(
}
} else {
warn!(
"Failed to fetch head block for player {} at position: ({}, {}, {})",
player, player_pos.x, player_pos.y, player_pos.z
"Failed to fetch head block for player {} at position: {:?}",
player, position
);
}
}
Expand Down
1 change: 1 addition & 0 deletions src/bin/src/packet_handlers/player/mod.rs
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
pub mod head_rot;
pub mod player_disconnect;
pub(crate) mod send_inventory_updates;
20 changes: 20 additions & 0 deletions src/bin/src/packet_handlers/player/player_disconnect.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
use bevy_ecs::{event::EventReader, prelude::Res, system::Commands};
use ferrumc_core::conn::player_disconnect_event::PlayerDisconnectEvent;
use ferrumc_state::GlobalStateResource;

pub fn handle(
mut cmd: Commands,
mut events: EventReader<PlayerDisconnectEvent>,
state: Res<GlobalStateResource>,
) {
for event in events.read() {
let uuid = event.identity.uuid.as_u128();
let username = &event.identity.username;
if let Err(e) = state.0.world.save_player_state(uuid, &event.data) {
tracing::error!("Failed to save player state for {}: {}", username, e);
} else {
tracing::info!("Player state saved for {}", username);
}
cmd.entity(event.entity).despawn();
}
}
Comment on lines +1 to +20
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

from what i see, this is an event handler, why is this in the packet handler dir? is confusing if its the disconnect event received from connection killer and server kill.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Honestly, I didn't know where to put it, after all, we didn't have a folder called events and I didn't know whether to put it inside systems or packets (the reason for putting packets is because of the "head_rot" event)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

head rot event is most likely being called from a packet. on the other hand the event you made has nothing to do with packets. why not make a separate module specifically for events? i reckon it'd be best?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree, having it here makes little sense

2 changes: 2 additions & 0 deletions src/bin/src/register_events.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@ use bevy_ecs::prelude::World;
use ferrumc_commands::events::{CommandDispatchEvent, ResolvedCommandDispatchEvent};
use ferrumc_core::chunks::cross_chunk_boundary_event::CrossChunkBoundaryEvent;
use ferrumc_core::conn::force_player_recount_event::ForcePlayerRecountEvent;
use ferrumc_core::conn::player_disconnect_event::PlayerDisconnectEvent;
use ferrumc_net::packets::packet_events::TransformEvent;

pub fn register_events(world: &mut World) {
EventRegistry::register_event::<TransformEvent>(world);
EventRegistry::register_event::<PlayerDisconnectEvent>(world);
EventRegistry::register_event::<CrossChunkBoundaryEvent>(world);
EventRegistry::register_event::<ForcePlayerRecountEvent>(world);
EventRegistry::register_event::<CommandDispatchEvent>(world);
Expand Down
32 changes: 26 additions & 6 deletions src/bin/src/systems/connection_killer.rs
Original file line number Diff line number Diff line change
@@ -1,21 +1,36 @@
use crate::systems::system_messages;
use bevy_ecs::prelude::{Commands, Entity, Query, Res};
use ferrumc_core::identity::player_identity::PlayerIdentity;
use bevy_ecs::{
event::EventWriter,
prelude::{Entity, Query, Res},
};
use ferrumc_core::{
conn::player_disconnect_event::PlayerDisconnectEvent,
data::player::PlayerData,
identity::player_identity::PlayerIdentity,
transform::{grounded::OnGround, position::Position, rotation::Rotation},
};
use ferrumc_net::connection::StreamWriter;
use ferrumc_state::GlobalStateResource;
use ferrumc_text::TextComponent;
use tracing::{info, trace, warn};

pub fn connection_killer(
query: Query<(Entity, &StreamWriter, &PlayerIdentity)>,
mut cmd: Commands,
query: Query<(
Entity,
&StreamWriter,
&PlayerIdentity,
&Position,
&OnGround,
&Rotation,
)>,
mut dispatch_events: EventWriter<PlayerDisconnectEvent>,
state: Res<GlobalStateResource>,
) {
while let Some((disconnecting_entity, reason)) = state.0.players.disconnection_queue.pop() {
let entity_result = query.get(disconnecting_entity);
match entity_result {
Ok(disconnecting_player) => {
for (entity, conn, player_identity) in query.iter() {
for (entity, conn, player_identity, position, on_ground, rotation) in query.iter() {
if disconnecting_entity == entity {
info!(
"Player {} ({}) disconnected: {}",
Expand Down Expand Up @@ -49,7 +64,12 @@ pub fn connection_killer(
} else {
system_messages::player_leave::handle(disconnecting_player.2, entity);
}
cmd.entity(entity).despawn();
let player_disconnect = PlayerDisconnectEvent {
data: PlayerData::new(position, on_ground.0, "overworld", rotation),
identity: player_identity.to_owned(),
entity,
};
dispatch_events.write(player_disconnect);
}
}
Err(e) => {
Expand Down
3 changes: 2 additions & 1 deletion src/bin/src/systems/new_connections.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
use crate::systems::system_messages;
use bevy_ecs::prelude::{Commands, Res, Resource};
use crossbeam_channel::Receiver;
use ferrumc_core::chunks::chunk_receiver::ChunkReceiver;
use ferrumc_core::conn::keepalive::KeepAliveTracker;
use ferrumc_core::transform::grounded::OnGround;
use ferrumc_core::transform::position::Position;
use ferrumc_core::transform::rotation::Rotation;
use ferrumc_core::{chunks::chunk_receiver::ChunkReceiver, data::player::PlayerData};
use ferrumc_inventories::hotbar::Hotbar;
use ferrumc_inventories::inventory::Inventory;
use ferrumc_net::connection::NewConnection;
Expand All @@ -29,6 +29,7 @@ pub fn accept_new_connections(
let entity = cmd.spawn((
new_connection.stream,
Position::default(),
PlayerData::default(),
ChunkReceiver::default(),
Rotation::default(),
OnGround::default(),
Expand Down
12 changes: 11 additions & 1 deletion src/bin/src/systems/shutdown_systems/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
use bevy_ecs::schedule::IntoScheduleConfigs;

use crate::packet_handlers::player;

mod send_shutdown_packet;

pub fn register_shutdown_systems(schedule: &mut bevy_ecs::schedule::Schedule) {
schedule.add_systems(send_shutdown_packet::handle);
schedule.add_systems(
(
send_shutdown_packet::handle,
player::player_disconnect::handle,
)
.chain(),
);
}
31 changes: 26 additions & 5 deletions src/bin/src/systems/shutdown_systems/send_shutdown_packet.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,40 @@
use bevy_ecs::prelude::{Entity, Query, Res};
use ferrumc_core::identity::player_identity::PlayerIdentity;
use bevy_ecs::{
event::EventWriter,
prelude::{Entity, Query, Res},
};
use ferrumc_core::{
conn::player_disconnect_event::PlayerDisconnectEvent,
data::player::PlayerData,
identity::player_identity::PlayerIdentity,
transform::{grounded::OnGround, position::Position, rotation::Rotation},
};
use ferrumc_net::connection::StreamWriter;
use ferrumc_state::GlobalStateResource;
use ferrumc_text::TextComponent;

pub fn handle(
query: Query<(Entity, &StreamWriter, &PlayerIdentity)>,
query: Query<(
Entity,
&StreamWriter,
&PlayerIdentity,
&Position,
&OnGround,
&Rotation,
)>,
mut dispatch_events: EventWriter<PlayerDisconnectEvent>,
state: Res<GlobalStateResource>,
) {
let packet = ferrumc_net::packets::outgoing::disconnect::DisconnectPacket {
reason: TextComponent::from("Server is shutting down"),
};

for (entity, conn, identity) in query.iter() {
for (entity, conn, identity, position, on_ground, rotation) in query.iter() {
if state.0.players.is_connected(entity) {
let player_disconnect = PlayerDisconnectEvent {
data: PlayerData::new(position, on_ground.0, "overworld", rotation),
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are we including this data here? The event handler can just fetch this data if you give it the entity

identity: identity.to_owned(),
entity,
};
dispatch_events.write(player_disconnect);
if let Err(e) = conn.send_packet_ref(&packet) {
tracing::error!(
"Failed to send shutdown packet to player {}: {}",
Expand Down
2 changes: 2 additions & 0 deletions src/lib/core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ ferrumc-text = { workspace = true }
ferrumc-net-codec = { workspace = true }
uuid = { workspace = true }
crossbeam-queue = { workspace = true }
serde = { workspace = true }
bitcode = { workspace = true }

[dev-dependencies]
criterion = { workspace = true }
Expand Down
1 change: 1 addition & 0 deletions src/lib/core/src/conn/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
pub mod force_player_recount_event;
pub mod keepalive;
pub mod player_count_update_cooldown;
pub mod player_disconnect_event;
10 changes: 10 additions & 0 deletions src/lib/core/src/conn/player_disconnect_event.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
use bevy_ecs::{entity::Entity, event::Event};

use crate::{data::player::PlayerData, identity::player_identity::PlayerIdentity};

#[derive(Event)]
pub struct PlayerDisconnectEvent {
pub entity: Entity,
pub identity: PlayerIdentity,
pub data: PlayerData,
}
1 change: 1 addition & 0 deletions src/lib/core/src/data/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pub mod player;
Loading