Support multiple worlds

- We moved all players to seperate worlds. This will save performance when many different players are on different worlds. The only thing broken now are commands because of async stuff
This commit is contained in:
Snowiiii 2024-08-31 11:39:59 +02:00
parent 6a45746703
commit ee4e18282e
7 changed files with 192 additions and 378 deletions

View File

@ -1,7 +1,6 @@
use num_derive::ToPrimitive;
// TODO
#[derive(ToPrimitive, Clone)]
#[derive(Clone)]
#[repr(i32)]
pub enum EntityType {
Zombie = 124,
Player = 128,

View File

@ -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 }
}
}

View File

@ -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(),

View File

@ -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

View File

@ -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<tokio::sync::Mutex<World>>,
/// 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<tokio::sync::Mutex<World>>,
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::<VarInt>::new(
self.entity_id().into(),
Metadata::new(6, 20.into(), (pose).into()),
),
)
let packet = CSetEntityMetadata::<VarInt>::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 => {

View File

@ -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;
}
}
}

View File

@ -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<tokio::sync::Mutex<World>>,
pub worlds: Vec<Arc<tokio::sync::Mutex<World>>>,
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<Registry>,
// TODO: place this into every world
pub current_players: HashMap<Arc<Token>, Arc<Mutex<Player>>>,
entity_id: AtomicI32,
pub base_config: BasicConfiguration,
pub advanced_config: AdvancedConfiguration,
@ -112,282 +99,52 @@ 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<Token>, client: Client) -> Arc<Mutex<Player>> {
pub async fn add_player(
&mut self,
token: Arc<Token>,
client: Client,
) -> (Arc<Mutex<Player>>, Arc<tokio::sync::Mutex<World>>) {
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;
gamemode,
)));
world.lock().await.add_player(token, player.clone());
(player, world)
}
/// TODO: This definitly should be in world
pub fn get_by_entityid(&self, from: &Player, id: EntityId) -> Option<MutexGuard<Player>> {
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
}
/// Sends a Packet to all Players
pub fn broadcast_packet<P>(&self, from: &mut Player, packet: &P)
/// Sends a Packet to all Players in all worlds
pub fn broadcast_packet_all<P>(&self, expect: &[&Arc<Token>], 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);
for world in &self.worlds {
world.blocking_lock().broadcast_packet(expect, packet)
}
}
/// Sends a packet to all players except those specified in `from`
pub fn broadcast_packet_except<P>(&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));
}
let t = inst.elapsed();
dbg!("DONE", t);
}
// move to world
pub fn new_entity_id(&self) -> EntityId {
self.entity_id.fetch_add(1, Ordering::SeqCst)