diff --git a/pumpkin-entity/src/entity_type.rs b/pumpkin-entity/src/entity_type.rs index e5cbc49df..f1bd36196 100644 --- a/pumpkin-entity/src/entity_type.rs +++ b/pumpkin-entity/src/entity_type.rs @@ -1,7 +1,6 @@ -use num_derive::ToPrimitive; - // TODO -#[derive(ToPrimitive, Clone)] +#[derive(Clone)] +#[repr(i32)] pub enum EntityType { Zombie = 124, Player = 128, diff --git a/pumpkin-world/src/lib.rs b/pumpkin-world/src/lib.rs index cb46ca2d3..d39541411 100644 --- a/pumpkin-world/src/lib.rs +++ b/pumpkin-world/src/lib.rs @@ -1,5 +1,3 @@ -use level::Level; - pub mod biome; pub mod block; pub mod chunk; @@ -7,7 +5,7 @@ pub mod coordinates; pub mod dimension; pub mod global_registry; pub mod item; -mod level; +pub mod level; pub mod radial_chunk_iterator; pub mod vector2; pub mod vector3; @@ -17,14 +15,3 @@ pub const WORLD_HEIGHT: usize = 384; pub const WORLD_LOWEST_Y: i16 = -64; pub const WORLD_MAX_Y: i16 = WORLD_HEIGHT as i16 - WORLD_LOWEST_Y.abs(); pub const DIRECT_PALETTE_BITS: u32 = 15; - -pub struct World { - pub level: Level, - // entities, players... -} - -impl World { - pub fn load(level: Level) -> Self { - Self { level } - } -} diff --git a/pumpkin/src/client/player_packet.rs b/pumpkin/src/client/player_packet.rs index dc49458e7..be6e08ccc 100644 --- a/pumpkin/src/client/player_packet.rs +++ b/pumpkin/src/client/player_packet.rs @@ -68,7 +68,7 @@ impl Player { pos.clamp(-2.0E7, 2.0E7) } - pub fn handle_position(&mut self, server: &mut Server, position: SPlayerPosition) { + pub async fn handle_position(&mut self, _server: &mut Server, position: SPlayerPosition) { if position.x.is_nan() || position.feet_y.is_nan() || position.z.is_nan() { self.kick(TextComponent::text("Invalid movement")); return; @@ -88,9 +88,9 @@ impl Player { let (x, lastx) = (entity.x, entity.lastx); let (y, lasty) = (entity.y, entity.lasty); let (z, lastz) = (entity.z, entity.lastz); - - server.broadcast_packet( - self, + let world = self.world.lock().await; + world.broadcast_packet( + &[&self.client.token], &CUpdateEntityPos::new( entity_id.into(), (x * 4096.0 - lastx * 4096.0) as i16, @@ -101,9 +101,9 @@ impl Player { ); } - pub fn handle_position_rotation( + pub async fn handle_position_rotation( &mut self, - server: &mut Server, + _server: &mut Server, position_rotation: SPlayerPositionRotation, ) { if position_rotation.x.is_nan() @@ -137,9 +137,10 @@ impl Player { let yaw = modulus(entity.yaw * 256.0 / 360.0, 256.0); let pitch = modulus(entity.pitch * 256.0 / 360.0, 256.0); // let head_yaw = (entity.head_yaw * 256.0 / 360.0).floor(); + let world = self.world.lock().await; - server.broadcast_packet( - self, + world.broadcast_packet( + &[&self.client.token], &CUpdateEntityPosRot::new( entity_id.into(), (x * 4096.0 - lastx * 4096.0) as i16, @@ -150,10 +151,13 @@ impl Player { on_ground, ), ); - server.broadcast_packet(self, &CHeadRot::new(entity_id.into(), yaw as u8)); + world.broadcast_packet( + &[&self.client.token], + &CHeadRot::new(entity_id.into(), yaw as u8), + ); } - pub fn handle_rotation(&mut self, server: &mut Server, rotation: SPlayerRotation) { + pub async fn handle_rotation(&mut self, _server: &mut Server, rotation: SPlayerRotation) { if !rotation.yaw.is_finite() || !rotation.pitch.is_finite() { self.kick(TextComponent::text("Invalid rotation")); return; @@ -168,11 +172,13 @@ impl Player { let pitch = modulus(entity.pitch * 256.0 / 360.0, 256.0); // let head_yaw = modulus(entity.head_yaw * 256.0 / 360.0, 256.0); - server.broadcast_packet( - self, - &CUpdateEntityRot::new(entity_id.into(), yaw as u8, pitch as u8, on_ground), - ); - server.broadcast_packet(self, &CHeadRot::new(entity_id.into(), yaw as u8)); + let world = self.world.lock().await; + let packet = CUpdateEntityRot::new(entity_id.into(), yaw as u8, pitch as u8, on_ground); + // self.client.send_packet(&packet); + world.broadcast_packet(&[&self.client.token], &packet); + let packet = CHeadRot::new(entity_id.into(), yaw as u8); + // self.client.send_packet(&packet); + world.broadcast_packet(&[&self.client.token], &packet); } pub fn handle_chat_command(&mut self, server: &mut Server, command: SChatCommand) { @@ -183,7 +189,7 @@ impl Player { self.on_ground = ground.on_ground; } - pub fn handle_player_command(&mut self, server: &mut Server, command: SPlayerCommand) { + pub async fn handle_player_command(&mut self, _server: &mut Server, command: SPlayerCommand) { if command.entity_id != self.entity.entity_id.into() { return; } @@ -192,23 +198,23 @@ impl Player { match action { pumpkin_protocol::server::play::Action::StartSneaking => { if !self.sneaking { - self.set_sneaking(server, true) + self.set_sneaking(true).await } } pumpkin_protocol::server::play::Action::StopSneaking => { if self.sneaking { - self.set_sneaking(server, false) + self.set_sneaking(false).await } } pumpkin_protocol::server::play::Action::LeaveBed => todo!(), pumpkin_protocol::server::play::Action::StartSprinting => { if !self.sprinting { - self.set_sprinting(server, true) + self.set_sprinting(true).await } } pumpkin_protocol::server::play::Action::StopSprinting => { if self.sprinting { - self.set_sprinting(server, false) + self.set_sprinting(false).await } } pumpkin_protocol::server::play::Action::StartHorseJump => todo!(), @@ -221,7 +227,7 @@ impl Player { } } - pub fn handle_swing_arm(&mut self, server: &mut Server, swing_arm: SSwingArm) { + pub async fn handle_swing_arm(&mut self, _server: &mut Server, swing_arm: SSwingArm) { match Hand::from_i32(swing_arm.hand.0) { Some(hand) => { let animation = match hand { @@ -229,7 +235,8 @@ impl Player { Hand::Off => Animation::SwingOffhand, }; let id = self.entity_id(); - server.broadcast_packet_except( + let world = self.world.lock().await; + world.broadcast_packet( &[&self.client.token], &CEntityAnimation::new(id.into(), animation as u8), ) @@ -240,7 +247,7 @@ impl Player { }; } - pub fn handle_chat_message(&mut self, server: &mut Server, chat_message: SChatMessage) { + pub async fn handle_chat_message(&mut self, _server: &mut Server, chat_message: SChatMessage) { dbg!("got message"); let message = chat_message.message; @@ -252,8 +259,9 @@ impl Player { // TODO: filter message & validation let gameprofile = &self.gameprofile; - server.broadcast_packet( - self, + let world = self.world.lock().await; + world.broadcast_packet( + &[&self.client.token], &CPlayerChatMessage::new( pumpkin_protocol::uuid::UUID(gameprofile.id), 1.into(), @@ -305,10 +313,10 @@ impl Player { } } - pub fn handle_interact(&mut self, server: &mut Server, interact: SInteract) { + pub async fn handle_interact(&mut self, server: &mut Server, interact: SInteract) { let sneaking = interact.sneaking; if self.sneaking != sneaking { - self.set_sneaking(server, sneaking); + self.set_sneaking(sneaking).await; } match ActionType::from_i32(interact.typ.0) { Some(action) => match action { @@ -317,7 +325,9 @@ impl Player { // TODO: do validation and stuff let config = &server.advanced_config.pvp; if config.enabled { - let attacked_player = server.get_by_entityid(self, entity_id.0 as EntityId); + let world = self.world.clone(); + let world = world.lock().await; + let attacked_player = world.get_by_entityid(self, entity_id.0 as EntityId); if let Some(mut player) = attacked_player { let token = player.client.token.clone(); let velo = player.velocity; @@ -349,8 +359,8 @@ impl Player { let packet = &CHurtAnimation::new(&entity_id, self.entity.yaw); self.client.send_packet(packet); player.client.send_packet(packet); - server.broadcast_packet_except( - &[self.client.token.as_ref(), token.as_ref()], + world.broadcast_packet( + &[&self.client.token, &token], &CHurtAnimation::new(&entity_id, 10.0), ) } @@ -370,7 +380,11 @@ impl Player { None => self.kick(TextComponent::text("Invalid action type")), } } - pub fn handle_player_action(&mut self, server: &mut Server, player_action: SPlayerAction) { + pub async fn handle_player_action( + &mut self, + _server: &mut Server, + player_action: SPlayerAction, + ) { match Status::from_i32(player_action.status.0) { Some(status) => match status { Status::StartedDigging => { @@ -384,10 +398,16 @@ impl Player { let location = player_action.location; // Block break & block break sound // TODO: currently this is always dirt replace it - server - .broadcast_packet(self, &CWorldEvent::new(2001, &location, 11, false)); + let world = self.world.lock().await; + world.broadcast_packet( + &[&self.client.token], + &CWorldEvent::new(2001, &location, 11, false), + ); // AIR - server.broadcast_packet(self, &CBlockUpdate::new(&location, 0.into())); + world.broadcast_packet( + &[&self.client.token], + &CBlockUpdate::new(&location, 0.into()), + ); } } Status::CancelledDigging => { @@ -406,9 +426,16 @@ impl Player { } // Block break & block break sound // TODO: currently this is always dirt replace it - server.broadcast_packet(self, &CWorldEvent::new(2001, &location, 11, false)); + let world = self.world.lock().await; + world.broadcast_packet( + &[&self.client.token], + &CWorldEvent::new(2001, &location, 11, false), + ); // AIR - server.broadcast_packet(self, &CBlockUpdate::new(&location, 0.into())); + world.broadcast_packet( + &[&self.client.token], + &CBlockUpdate::new(&location, 0.into()), + ); // TODO: Send this every tick self.client .send_packet(&CAcknowledgeBlockChange::new(player_action.sequence)); @@ -435,7 +462,7 @@ impl Player { .send_packet(&CPingResponse::new(request.payload)); } - pub fn handle_use_item_on(&mut self, server: &mut Server, use_item_on: SUseItemOn) { + pub async fn handle_use_item_on(&mut self, _server: &mut Server, use_item_on: SUseItemOn) { let location = use_item_on.location; if !self.can_interact_with_block_at(&location, 1.0) { @@ -451,12 +478,13 @@ impl Player { ) .expect("All item ids are in the global registry"); if let Ok(block_state_id) = BlockId::new(minecraft_id, None) { - server.broadcast_packet( - self, + let world = self.world.lock().await; + world.broadcast_packet( + &[&self.client.token], &CBlockUpdate::new(&location, block_state_id.get_id_mojang_repr().into()), ); - server.broadcast_packet( - self, + world.broadcast_packet( + &[&self.client.token], &CBlockUpdate::new( &WorldPosition(location.0 + face.to_offset()), block_state_id.get_id_mojang_repr().into(), diff --git a/pumpkin/src/commands/cmd_gamemode.rs b/pumpkin/src/commands/cmd_gamemode.rs index 767de2254..375172f27 100644 --- a/pumpkin/src/commands/cmd_gamemode.rs +++ b/pumpkin/src/commands/cmd_gamemode.rs @@ -60,8 +60,8 @@ pub(crate) fn init_command_tree<'a>() -> CommandTree<'a> { CommandTree::new(NAMES, DESCRIPTION).with_child( require(&|sender| sender.permission_lvl() >= 2).with_child( argument(ARG_GAMEMODE, consume_arg_gamemode) - .with_child(require(&|sender| sender.is_player()).execute( - &|sender, server, args| { + .with_child( + require(&|sender| sender.is_player()).execute(&|sender, _, args| { let gamemode = parse_arg_gamemode(args)?; return if let Player(target) = sender { @@ -71,7 +71,9 @@ pub(crate) fn init_command_tree<'a>() -> CommandTree<'a> { gamemode ))); } else { - target.set_gamemode(server, gamemode); + // TODO + #[allow(clippy::let_underscore_future)] + let _ = target.set_gamemode(gamemode); target.send_system_message(TextComponent::text(&format!( "Game mode was set to {:?}", gamemode @@ -81,10 +83,10 @@ pub(crate) fn init_command_tree<'a>() -> CommandTree<'a> { } else { Err(InvalidRequirementError) }; - }, - )) + }), + ) .with_child(argument(ARG_TARGET, consume_arg_player).execute( - &|sender, server, args| { + &|sender, _, args| { let gamemode = parse_arg_gamemode(args)?; let target = parse_arg_player(sender, ARG_TARGET, args)?; @@ -94,7 +96,9 @@ pub(crate) fn init_command_tree<'a>() -> CommandTree<'a> { gamemode ))); } else { - target.set_gamemode(server, gamemode); + // TODO + #[allow(clippy::let_underscore_future)] + let _ = target.set_gamemode(gamemode); target.send_system_message(TextComponent::text(&format!( "Game mode was set to {:?}", gamemode diff --git a/pumpkin/src/entity/player.rs b/pumpkin/src/entity/player.rs index ff0c084c9..2ee561f41 100644 --- a/pumpkin/src/entity/player.rs +++ b/pumpkin/src/entity/player.rs @@ -1,4 +1,4 @@ -use std::str::FromStr; +use std::{str::FromStr, sync::Arc}; use num_derive::{FromPrimitive, ToPrimitive}; use num_traits::ToPrimitive; @@ -27,6 +27,7 @@ use crate::{ client::{authentication::GameProfile, Client}, server::Server, util::boundingbox::BoundingBox, + world::World, }; pub struct PlayerAbilities { @@ -55,7 +56,9 @@ pub struct Player { pub gameprofile: GameProfile, pub client: Client, pub entity: Entity, - // current gamemode + // TODO: Put this into entity + pub world: Arc>, + /// Current gamemode pub gamemode: GameMode, // TODO: prbly should put this into an Living Entitiy or something pub health: f32, @@ -83,7 +86,12 @@ pub struct Player { } impl Player { - pub fn new(client: Client, entity_id: EntityId, gamemode: GameMode) -> Self { + pub fn new( + client: Client, + world: Arc>, + entity_id: EntityId, + gamemode: GameMode, + ) -> Self { let gameprofile = match client.gameprofile.clone() { Some(profile) => profile, None => { @@ -101,6 +109,7 @@ impl Player { gameprofile, client, entity: Entity::new(entity_id, EntityType::Player, 1.62), + world, on_ground: false, awaiting_teleport: None, sneaking: false, @@ -118,6 +127,12 @@ impl Player { } } + // TODO: Put this into entity + /// Removes the Player out of the current World + pub async fn remove(&mut self) { + self.world.lock().await.remove_player(self); + } + pub fn entity_id(&self) -> EntityId { self.entity.entity_id } @@ -167,21 +182,21 @@ impl Player { )); } - pub fn set_sneaking(&mut self, server: &mut Server, sneaking: bool) { + pub async fn set_sneaking(&mut self, sneaking: bool) { assert!(self.sneaking != sneaking); self.sneaking = sneaking; - self.set_flag(server, Self::SNEAKING_FLAG_INDEX, sneaking); + self.set_flag(Self::SNEAKING_FLAG_INDEX, sneaking).await; if sneaking { - self.set_pose(server, EntityPose::Crouching); + self.set_pose(EntityPose::Crouching).await; } else { - self.set_pose(server, EntityPose::Standing); + self.set_pose(EntityPose::Standing).await; } } - pub fn set_sprinting(&mut self, server: &mut Server, sprinting: bool) { + pub async fn set_sprinting(&mut self, sprinting: bool) { assert!(self.sprinting != sprinting); self.sprinting = sprinting; - self.set_flag(server, Self::SPRINTING_FLAG_INDEX, sprinting); + self.set_flag(Self::SPRINTING_FLAG_INDEX, sprinting).await; } pub const ON_FIRE_FLAG_INDEX: u32 = 0; @@ -191,29 +206,34 @@ impl Player { pub const INVISIBLE_FLAG_INDEX: u32 = 5; pub const GLOWING_FLAG_INDEX: u32 = 6; pub const FALL_FLYING_FLAG_INDEX: u32 = 7; - fn set_flag(&mut self, server: &mut Server, index: u32, value: bool) { + async fn set_flag(&mut self, index: u32, value: bool) { let mut b = 0i8; if value { b |= 1 << index; } else { b &= !(1 << index); } - server.broadcast_packet( - self, - &CSetEntityMetadata::new(self.entity_id().into(), Metadata::new(0, 0.into(), b)), - ); + let packet = + CSetEntityMetadata::new(self.entity_id().into(), Metadata::new(0, 0.into(), b)); + self.client.send_packet(&packet); + self.world + .lock() + .await + .broadcast_packet(&[&self.client.token], &packet); } - pub fn set_pose(&mut self, server: &mut Server, pose: EntityPose) { + pub async fn set_pose(&mut self, pose: EntityPose) { self.entity.pose = pose; let pose = self.entity.pose as i32; - server.broadcast_packet( - self, - &CSetEntityMetadata::::new( - self.entity_id().into(), - Metadata::new(6, 20.into(), (pose).into()), - ), - ) + let packet = CSetEntityMetadata::::new( + self.entity_id().into(), + Metadata::new(6, 20.into(), (pose).into()), + ); + self.client.send_packet(&packet); + self.world + .lock() + .await + .broadcast_packet(&[&self.client.token], &packet) } pub fn teleport(&mut self, x: f64, y: f64, z: f64, yaw: f32, pitch: f32) { @@ -278,7 +298,7 @@ impl Player { self.food_saturation = food_saturation; } - pub fn set_gamemode(&mut self, server: &mut Server, gamemode: GameMode) { + pub async fn set_gamemode(&mut self, gamemode: GameMode) { // We could send the same gamemode without problems. But why waste bandwidth ? assert!( self.gamemode != gamemode, @@ -287,8 +307,17 @@ impl Player { self.gamemode = gamemode; // So a little story time. I actually made an abitlties_from_gamemode function. I looked at vanilla and they always send the abilties from the gamemode. But the funny thing actually is. That the client // does actually use the same method and set the abilties when receiving the CGameEvent gamemode packet. Just Mojang nonsense - server.broadcast_packet( - self, + + // TODO: fix this ugly mess :c, It gives me a liftime error when saving packet as a var + self.client.send_packet(&CPlayerInfoUpdate::new( + 0x04, + &[pumpkin_protocol::client::play::Player { + uuid: self.gameprofile.id, + actions: vec![PlayerAction::UpdateGameMode((self.gamemode as i32).into())], + }], + )); + self.world.lock().await.broadcast_packet( + &[&self.client.token], &CPlayerInfoUpdate::new( 0x04, &[pumpkin_protocol::client::play::Player { @@ -308,9 +337,9 @@ impl Player { } impl Player { - pub fn process_packets(&mut self, server: &mut Server) { + pub async fn process_packets(&mut self, server: &mut Server) { while let Some(mut packet) = self.client.client_packets_queue.pop() { - match self.handle_play_packet(server, &mut packet) { + match self.handle_play_packet(server, &mut packet).await { Ok(_) => {} Err(e) => { let text = format!("Error while reading incoming packet {}", e); @@ -321,7 +350,7 @@ impl Player { } } - pub fn handle_play_packet( + pub async fn handle_play_packet( &mut self, server: &mut Server, packet: &mut RawPacket, @@ -337,15 +366,18 @@ impl Player { Ok(()) } SPlayerPosition::PACKET_ID => { - self.handle_position(server, SPlayerPosition::read(bytebuf)?); + self.handle_position(server, SPlayerPosition::read(bytebuf)?) + .await; Ok(()) } SPlayerPositionRotation::PACKET_ID => { - self.handle_position_rotation(server, SPlayerPositionRotation::read(bytebuf)?); + self.handle_position_rotation(server, SPlayerPositionRotation::read(bytebuf)?) + .await; Ok(()) } SPlayerRotation::PACKET_ID => { - self.handle_rotation(server, SPlayerRotation::read(bytebuf)?); + self.handle_rotation(server, SPlayerRotation::read(bytebuf)?) + .await; Ok(()) } SSetPlayerGround::PACKET_ID => { @@ -353,15 +385,18 @@ impl Player { Ok(()) } SPlayerCommand::PACKET_ID => { - self.handle_player_command(server, SPlayerCommand::read(bytebuf)?); + self.handle_player_command(server, SPlayerCommand::read(bytebuf)?) + .await; Ok(()) } SSwingArm::PACKET_ID => { - self.handle_swing_arm(server, SSwingArm::read(bytebuf)?); + self.handle_swing_arm(server, SSwingArm::read(bytebuf)?) + .await; Ok(()) } SChatMessage::PACKET_ID => { - self.handle_chat_message(server, SChatMessage::read(bytebuf)?); + self.handle_chat_message(server, SChatMessage::read(bytebuf)?) + .await; Ok(()) } SClientInformationPlay::PACKET_ID => { @@ -369,15 +404,18 @@ impl Player { Ok(()) } SInteract::PACKET_ID => { - self.handle_interact(server, SInteract::read(bytebuf)?); + self.handle_interact(server, SInteract::read(bytebuf)?) + .await; Ok(()) } SPlayerAction::PACKET_ID => { - self.handle_player_action(server, SPlayerAction::read(bytebuf)?); + self.handle_player_action(server, SPlayerAction::read(bytebuf)?) + .await; Ok(()) } SUseItemOn::PACKET_ID => { - self.handle_use_item_on(server, SUseItemOn::read(bytebuf)?); + self.handle_use_item_on(server, SUseItemOn::read(bytebuf)?) + .await; Ok(()) } SUseItem::PACKET_ID => { diff --git a/pumpkin/src/main.rs b/pumpkin/src/main.rs index 1a8aaa1e9..56281f274 100644 --- a/pumpkin/src/main.rs +++ b/pumpkin/src/main.rs @@ -28,6 +28,7 @@ pub mod proxy; pub mod rcon; pub mod server; pub mod util; +pub mod world; fn main() -> io::Result<()> { use std::sync::{Arc, Mutex}; @@ -177,7 +178,7 @@ fn main() -> io::Result<()> { let mut player = player.lock().unwrap(); player.client.poll(event).await; let mut server = server.lock().await; - player.process_packets(&mut server); + player.process_packets(&mut server).await; player.client.closed } else { false @@ -185,9 +186,8 @@ fn main() -> io::Result<()> { if done { if let Some(player) = players.remove(&token) { - let mut server = server.lock().await; - server.remove_player(&token); let mut player = player.lock().unwrap(); + player.remove().await; poll.registry().deregister(&mut player.client.connection)?; } } @@ -210,10 +210,11 @@ fn main() -> io::Result<()> { } else if make_player { let token = client.token.clone(); let mut server = server.lock().await; - let player = server.add_player(token.clone(), client); + let (player, world) = + server.add_player(token.clone(), client).await; players.insert(token, player.clone()); - let mut player = player.lock().unwrap(); - server.spawn_player(&mut player).await; + let mut world = world.lock().await; + world.spawn_player(&server.base_config, player).await; } } } diff --git a/pumpkin/src/server.rs b/pumpkin/src/server.rs index 563866571..582d4e992 100644 --- a/pumpkin/src/server.rs +++ b/pumpkin/src/server.rs @@ -1,10 +1,9 @@ use std::{ - collections::HashMap, io::Cursor, path::Path, sync::{ atomic::{AtomicI32, Ordering}, - Arc, Mutex, MutexGuard, + Arc, Mutex, }, time::Duration, }; @@ -12,32 +11,23 @@ use std::{ use base64::{engine::general_purpose, Engine}; use image::GenericImageView; use mio::Token; -use num_traits::ToPrimitive; -use pumpkin_entity::{entity_type::EntityType, EntityId}; +use pumpkin_entity::EntityId; use pumpkin_plugin::PluginLoader; use pumpkin_protocol::{ - client::{ - config::CPluginMessage, - play::{ - CCenterChunk, CChunkData, CGameEvent, CLogin, CPlayerAbilities, CPlayerInfoUpdate, - CRemoveEntities, CRemovePlayerInfo, CSetEntityMetadata, CSpawnEntity, Metadata, - PlayerAction, - }, - }, - uuid::UUID, - ClientPacket, Players, Sample, StatusResponse, VarInt, Version, CURRENT_MC_PROTOCOL, + client::config::CPluginMessage, ClientPacket, Players, Sample, StatusResponse, VarInt, Version, + CURRENT_MC_PROTOCOL, }; -use pumpkin_world::{dimension::Dimension, radial_chunk_iterator::RadialIterator, World}; +use pumpkin_world::dimension::Dimension; use pumpkin_registry::Registry; use rsa::{traits::PublicKeyParts, RsaPrivateKey, RsaPublicKey}; use serde::{Deserialize, Serialize}; -use tokio::sync::mpsc; use crate::{ client::Client, config::{AdvancedConfiguration, BasicConfiguration}, entity::player::{GameMode, Player}, + world::World, }; pub const CURRENT_MC_VERSION: &str = "1.21.1"; @@ -49,7 +39,7 @@ pub struct Server { pub plugin_loader: PluginLoader, - pub world: Arc>, + pub worlds: Vec>>, pub status_response: StatusResponse, // We cache the json response here so we don't parse it every time someone makes a Status request. // Keep in mind that we must parse this again, when the StatusResponse changes which usally happen when a player joins or leaves @@ -61,9 +51,6 @@ pub struct Server { /// Cache the registry so we don't have to parse it every time a player joins pub cached_registry: Vec, - // TODO: place this into every world - pub current_players: HashMap, Arc>>, - entity_id: AtomicI32, pub base_config: BasicConfiguration, pub advanced_config: AdvancedConfiguration, @@ -112,280 +99,50 @@ impl Server { cached_registry: Registry::get_static(), // 0 is invalid entity_id: 2.into(), - world: Arc::new(tokio::sync::Mutex::new(world)), + worlds: vec![Arc::new(tokio::sync::Mutex::new(world))], public_key, cached_server_brand, private_key, status_response, status_response_json, public_key_der, - current_players: HashMap::new(), base_config: config.0, auth_client, advanced_config: config.1, } } - pub fn add_player(&mut self, token: Arc, client: Client) -> Arc> { + pub async fn add_player( + &mut self, + token: Arc, + client: Client, + ) -> (Arc>, Arc>) { let entity_id = self.new_entity_id(); let gamemode = match self.base_config.default_gamemode { GameMode::Undefined => GameMode::Survival, game_mode => game_mode, }; - let player = Arc::new(Mutex::new(Player::new(client, entity_id, gamemode))); - self.current_players.insert(token, player.clone()); - player - } - - pub fn remove_player(&mut self, token: &Token) { - let player = self.current_players.remove(token).unwrap(); - let player = player.as_ref().lock().unwrap(); - // despawn the player - // todo: put this into the entitiy struct - let id = player.entity_id(); - let uuid = player.gameprofile.id; - self.broadcast_packet_except( - &[&player.client.token], - &CRemovePlayerInfo::new(1.into(), &[UUID(uuid)]), - ); - self.broadcast_packet_except(&[&player.client.token], &CRemoveEntities::new(&[id.into()])) - } - - // here is where the magic happens - // TODO: do this in a world - pub async fn spawn_player(&mut self, player: &mut Player) { - // This code follows the vanilla packet order - let entity_id = player.entity_id(); - let gamemode = player.gamemode; - log::debug!("spawning player, entity id {}", entity_id); - - // login packet for our new player - player.client.send_packet(&CLogin::new( + // Basicly the default world + // TODO: select default from config + let world = self.worlds[0].clone(); + let player = Arc::new(Mutex::new(Player::new( + client, + world.clone(), entity_id, - self.base_config.hardcore, - &["minecraft:overworld"], - self.base_config.max_players.into(), - self.base_config.view_distance.into(), // TODO: view distance - self.base_config.simulation_distance.into(), // TODO: sim view dinstance - false, - false, - false, - 0.into(), - "minecraft:overworld", - 0, // seed - gamemode.to_u8().unwrap(), - self.base_config.default_gamemode.to_i8().unwrap(), - false, - false, - None, - 0.into(), - false, - )); - dbg!("sending abilities"); - // player abilities - player - .client - .send_packet(&CPlayerAbilities::new(0x02, 0.1, 0.1)); - - // teleport - let x = 10.0; - let y = 120.0; - let z = 10.0; - let yaw = 10.0; - let pitch = 10.0; - player.teleport(x, y, z, 10.0, 10.0); - let gameprofile = &player.gameprofile; - // first send info update to our new player, So he can see his Skin - // also send his info to everyone else - self.broadcast_packet( - player, - &CPlayerInfoUpdate::new( - 0x01 | 0x08, - &[pumpkin_protocol::client::play::Player { - uuid: gameprofile.id, - actions: vec![ - PlayerAction::AddPlayer { - name: gameprofile.name.clone(), - properties: gameprofile.properties.clone(), - }, - PlayerAction::UpdateListed(true), - ], - }], - ), - ); - - // here we send all the infos of already joined players - let mut entries = Vec::new(); - for (_, playerr) in self - .current_players - .iter() - .filter(|c| c.0 != &player.client.token) - { - let playerr = playerr.as_ref().lock().unwrap(); - let gameprofile = &playerr.gameprofile; - entries.push(pumpkin_protocol::client::play::Player { - uuid: gameprofile.id, - actions: vec![ - PlayerAction::AddPlayer { - name: gameprofile.name.clone(), - properties: gameprofile.properties.clone(), - }, - PlayerAction::UpdateListed(true), - ], - }) - } - player - .client - .send_packet(&CPlayerInfoUpdate::new(0x01 | 0x08, &entries)); - - // Start waiting for level chunks - player.client.send_packet(&CGameEvent::new(13, 0.0)); - - let gameprofile = &player.gameprofile; - - // spawn player for every client - self.broadcast_packet_except( - &[&player.client.token], - // TODO: add velo - &CSpawnEntity::new( - entity_id.into(), - UUID(gameprofile.id), - (EntityType::Player as i32).into(), - x, - y, - z, - pitch, - yaw, - yaw, - 0.into(), - 0.0, - 0.0, - 0.0, - ), - ); - // spawn players for our client - let token = player.client.token.clone(); - for (_, existing_player) in self.current_players.iter().filter(|c| c.0 != &token) { - let existing_player = existing_player.as_ref().lock().unwrap(); - let entity = &existing_player.entity; - let gameprofile = &existing_player.gameprofile; - player.client.send_packet(&CSpawnEntity::new( - existing_player.entity_id().into(), - UUID(gameprofile.id), - EntityType::Player.to_i32().unwrap().into(), - entity.x, - entity.y, - entity.z, - entity.yaw, - entity.pitch, - entity.pitch, - 0.into(), - 0.0, - 0.0, - 0.0, - )) - } - // entity meta data - if let Some(config) = player.client.config.as_ref() { - self.broadcast_packet( - player, - &CSetEntityMetadata::new( - entity_id.into(), - Metadata::new(17, VarInt(0), config.skin_parts), - ), - ) - } - - self.spawn_test_chunk(player, self.base_config.view_distance as u32) - .await; - } - - /// TODO: This definitly should be in world - pub fn get_by_entityid(&self, from: &Player, id: EntityId) -> Option> { - for (_, player) in self - .current_players - .iter() - .filter(|c| c.0 != &from.client.token) - { - let player = player.lock().unwrap(); - if player.entity_id() == id { - return Some(player); - } - } - None + gamemode, + ))); + world.lock().await.add_player(token, player.clone()); + (player, world) } - /// Sends a Packet to all Players - pub fn broadcast_packet

(&self, from: &mut Player, packet: &P) + /// Sends a Packet to all Players in all worlds + pub fn broadcast_packet_all

(&self, expect: &[&Arc], packet: &P) where P: ClientPacket, { - // we can't borrow twice at same time - from.client.send_packet(packet); - for (_, player) in self - .current_players - .iter() - .filter(|c| c.0 != &from.client.token) - { - let mut player = player.lock().unwrap(); - player.client.send_packet(packet); - } - } - - /// Sends a packet to all players except those specified in `from` - pub fn broadcast_packet_except

(&self, from: &[&Token], packet: &P) - where - P: ClientPacket, - { - for (_, player) in self - .current_players - .iter() - .filter(|c| !from.contains(&c.0.as_ref())) - { - let mut player = player.lock().unwrap(); - player.client.send_packet(packet); - } - } - - // TODO: do this in a world - async fn spawn_test_chunk(&self, player: &mut Player, distance: u32) { - let inst = std::time::Instant::now(); - let (sender, mut chunk_receiver) = mpsc::channel(distance as usize); - let world = self.world.clone(); - - let chunks: Vec<_> = RadialIterator::new(distance).collect(); - tokio::spawn(async move { - world.lock().await.level.fetch_chunks(&chunks, sender); - }); - - player.client.send_packet(&CCenterChunk { - chunk_x: 0.into(), - chunk_z: 0.into(), - }); - - while let Some(chunk_data) = chunk_receiver.recv().await { - // dbg!(chunk_pos); - let chunk_data = match chunk_data { - Ok(d) => d, - Err(_) => continue, - }; - #[cfg(debug_assertions)] - if chunk_data.position == (0, 0).into() { - use pumpkin_protocol::bytebuf::ByteBuffer; - let mut test = ByteBuffer::empty(); - CChunkData(&chunk_data).write(&mut test); - let len = test.buf().len(); - log::debug!( - "Chunk packet size: {}B {}KB {}MB", - len, - len / 1024, - len / (1024 * 1024) - ); - } - player.client.send_packet(&CChunkData(&chunk_data)); + for world in &self.worlds { + world.blocking_lock().broadcast_packet(expect, packet) } - let t = inst.elapsed(); - dbg!("DONE", t); } // move to world