Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
16 changes: 11 additions & 5 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 @@ -115,11 +112,19 @@ ferrumc-world-gen = { path = "src/lib/world_gen" }
ferrumc-inventories = { path = "src/lib/inventories" }

# Asynchronous
tokio = { version = "1.47.1", features = ["macros", "net", "rt", "sync", "time", "io-util", "test-util"], default-features = false }
tokio = { version = "1.47.1", features = [
"macros",
"net",
"rt",
"sync",
"time",
"io-util",
"test-util",
], default-features = false }

# Logging
tracing = "0.1.41"
tracing-subscriber = { version = "0.3.19", features = ["env-filter"] }
tracing-subscriber = { version = ">=0.3.20", features = ["env-filter"] }
tracing-appender = "0.2.3"
log = "0.4.27"
console-subscriber = "0.4.1"
Expand Down Expand Up @@ -189,6 +194,7 @@ lz4_flex = "0.11.5"
# Database
heed = "0.22.0"
moka = "0.12.10"
rusqlite = { version = "0.37.0", features = ["bundled", "serde_json"] }

# CLI
clap = "4.5.45"
Expand Down
2 changes: 2 additions & 0 deletions src/bin/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use ferrumc_config::server_config::get_global_config;
use ferrumc_config::whitelist::create_whitelist;
use ferrumc_general_purpose::paths::get_root_path;
use ferrumc_state::player_list::PlayerList;
use ferrumc_state::player_state::PlayerState;
use ferrumc_state::{GlobalState, ServerState};
use ferrumc_threadpool::ThreadPool;
use ferrumc_world::World;
Expand Down Expand Up @@ -159,6 +160,7 @@ fn create_state(start_time: Instant) -> Result<ServerState, BinaryError> {
world: World::new(&get_global_config().database.db_path),
terrain_generator: WorldGenerator::new(0),
shut_down: false.into(),
player_state: PlayerState::default(),
players: PlayerList::default(),
thread_pool: ThreadPool::new(),
start_time,
Expand Down
75 changes: 63 additions & 12 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, &mut PlayerData, &StreamWriter)>,
) {
for (_, player) in ev.0.try_iter() {
let Ok((entity, player_pos, conn)) = query.get(player) else {
let Ok((entity, player_identity, mut player_data, conn)) = query.get_mut(player) else {
warn!("Player position not found in query.");
continue;
};
Expand All @@ -24,28 +26,77 @@ pub fn handle(
);
continue;
}

// Default player data
*player_data = PlayerData::new(Position::default(), "overworld");

// Save the player's position in the world
if let Ok(loaded) = state
.0
.world
.load_player_state(player_identity.uuid.as_u128())
{
match loaded {
Some(loaded_data) => {
*player_data = loaded_data;
tracing::info!(
"Loaded player state for {}: position=({}, {}, {}), dimension={}",
player_identity.uuid.as_u128(),
player_data.pos.x,
player_data.pos.y,
player_data.pos.z,
player_data.dimension
);
}
None => {
if let Err(e) = state
.0
.world
.save_player_state(player_identity.uuid.as_u128(), &player_data)
{
tracing::error!(
"Failed to save player state for {} ({}): {:?}",
player_identity.username,
player_identity.uuid.as_u128(),
e
);
}
}
}
} else if let Err(e) = state
.0
.world
.save_player_state(player_identity.uuid.as_u128(), &player_data)
{
tracing::error!(
"Failed to save player state for {} ({}): {:?}",
player_identity.username,
player_identity.uuid.as_u128(),
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,
player_data.pos.x as i32,
player_data.pos.y as i32,
player_data.pos.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
player_data.pos.x,
player_data.pos.y,
player_data.pos.z
);
} else {
tracing::info!(
"Player {} loaded at position: ({}, {}, {}) with head block: {:?}",
player,
player_pos.x,
player_pos.y,
player_pos.z,
player_data.pos.x,
player_data.pos.y,
player_data.pos.z,
head_block
);
// Teleport the player to the world center if their head block is not air
Expand All @@ -66,7 +117,7 @@ pub fn handle(
} else {
warn!(
"Failed to fetch head block for player {} at position: ({}, {}, {})",
player, player_pos.x, player_pos.y, player_pos.z
player, player_data.pos.x, player_data.pos.y, player_data.pos.z
);
}
}
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,10 +1,10 @@
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 @@ -28,6 +28,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
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/data/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pub mod player;
29 changes: 29 additions & 0 deletions src/lib/core/src/data/player.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
use bevy_ecs::component::Component;
use bitcode::{Decode, Encode};
use serde::{Deserialize, Serialize};

use crate::transform::position::Position;

// https://minecraft.fandom.com/wiki/Player.dat_format
#[derive(
Serialize, Deserialize, Clone, Debug, Encode, Decode, Component, typename::TypeName, PartialEq,
)]
pub struct PlayerData {
pub pos: Position,
pub dimension: String,
}

impl Default for PlayerData {
fn default() -> Self {
Self::new(Position::default(), "overworld")
}
}

impl PlayerData {
pub fn new(pos: Position, dimension: &str) -> Self {
Self {
pos,
dimension: dimension.to_string(),
}
}
}
1 change: 1 addition & 0 deletions src/lib/core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ pub mod errors;
pub mod chunks;
pub mod collisions;
pub mod conn;
pub mod data;
pub mod identity;
pub mod mq;
pub mod state;
Expand Down
4 changes: 3 additions & 1 deletion src/lib/core/src/transform/position.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
use bevy_ecs::prelude::Component;
use bitcode::{Decode, Encode};
use ferrumc_net_codec::net_types::network_position::NetworkPosition;
use serde::{Deserialize, Serialize};
use std::fmt::{Debug, Display, Formatter};
use typename::TypeName;

#[derive(TypeName, Component)]
#[derive(TypeName, Component, Serialize, Deserialize, Encode, Decode, Clone, PartialEq)]
pub struct Position {
pub x: f64,
pub y: f64,
Expand Down
1 change: 1 addition & 0 deletions src/lib/core/state/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@ ferrumc-world-gen = { workspace = true }
dashmap = { workspace = true }
ferrumc-threadpool = { workspace = true }
crossbeam-queue = { workspace = true }
ferrumc-core = { workspace = true }
3 changes: 3 additions & 0 deletions src/lib/core/state/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
pub mod player_list;
pub mod player_state;

use crate::player_list::PlayerList;
use crate::player_state::PlayerState;
use bevy_ecs::prelude::Resource;
use ferrumc_threadpool::ThreadPool;
use ferrumc_world::World;
Expand All @@ -12,6 +14,7 @@ use std::time::Instant;
pub struct ServerState {
pub world: World,
pub terrain_generator: WorldGenerator,
pub player_state: PlayerState,
pub shut_down: AtomicBool,
pub players: PlayerList, // (UUID, Username)
pub thread_pool: ThreadPool,
Expand Down
22 changes: 22 additions & 0 deletions src/lib/core/state/src/player_state.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
use bevy_ecs::entity::Entity;
use dashmap::DashMap;
use ferrumc_core::data::player::PlayerData;

#[derive(Debug, Default)]
pub struct PlayerState {
player_data: DashMap<Entity, PlayerData>,
}

impl PlayerState {
pub fn is_connected(&self, entity: Entity) -> bool {
self.player_data.contains_key(&entity)
}

pub fn disconnect(&self, entity: Entity) {
self.player_data.remove(&entity);
}

pub fn connect(&self, entity: Entity, data: PlayerData) {
self.player_data.insert(entity, data);
}
}
5 changes: 4 additions & 1 deletion src/lib/storage/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,10 @@ rand = { workspace = true }
heed = { workspace = true }
page_size = { workspace = true }
parking_lot = { workspace = true }

rusqlite = { workspace = true }
serde = { workspace = true }
serde_derive = { workspace = true }
serde_json = { workspace = true }

[dev-dependencies]
criterion = { workspace = true }
Expand Down
Loading
Loading