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:
parent
6a45746703
commit
ee4e18282e
@ -1,7 +1,6 @@
|
||||
use num_derive::ToPrimitive;
|
||||
|
||||
// TODO
|
||||
#[derive(ToPrimitive, Clone)]
|
||||
#[derive(Clone)]
|
||||
#[repr(i32)]
|
||||
pub enum EntityType {
|
||||
Zombie = 124,
|
||||
Player = 128,
|
||||
|
||||
@ -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 }
|
||||
}
|
||||
}
|
||||
|
||||
@ -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(),
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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 => {
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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)
|
||||
|
||||
Loading…
Reference in New Issue
Block a user