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
|
// TODO
|
||||||
#[derive(ToPrimitive, Clone)]
|
#[derive(Clone)]
|
||||||
|
#[repr(i32)]
|
||||||
pub enum EntityType {
|
pub enum EntityType {
|
||||||
Zombie = 124,
|
Zombie = 124,
|
||||||
Player = 128,
|
Player = 128,
|
||||||
|
|||||||
@ -1,5 +1,3 @@
|
|||||||
use level::Level;
|
|
||||||
|
|
||||||
pub mod biome;
|
pub mod biome;
|
||||||
pub mod block;
|
pub mod block;
|
||||||
pub mod chunk;
|
pub mod chunk;
|
||||||
@ -7,7 +5,7 @@ pub mod coordinates;
|
|||||||
pub mod dimension;
|
pub mod dimension;
|
||||||
pub mod global_registry;
|
pub mod global_registry;
|
||||||
pub mod item;
|
pub mod item;
|
||||||
mod level;
|
pub mod level;
|
||||||
pub mod radial_chunk_iterator;
|
pub mod radial_chunk_iterator;
|
||||||
pub mod vector2;
|
pub mod vector2;
|
||||||
pub mod vector3;
|
pub mod vector3;
|
||||||
@ -17,14 +15,3 @@ pub const WORLD_HEIGHT: usize = 384;
|
|||||||
pub const WORLD_LOWEST_Y: i16 = -64;
|
pub const WORLD_LOWEST_Y: i16 = -64;
|
||||||
pub const WORLD_MAX_Y: i16 = WORLD_HEIGHT as i16 - WORLD_LOWEST_Y.abs();
|
pub const WORLD_MAX_Y: i16 = WORLD_HEIGHT as i16 - WORLD_LOWEST_Y.abs();
|
||||||
pub const DIRECT_PALETTE_BITS: u32 = 15;
|
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)
|
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() {
|
if position.x.is_nan() || position.feet_y.is_nan() || position.z.is_nan() {
|
||||||
self.kick(TextComponent::text("Invalid movement"));
|
self.kick(TextComponent::text("Invalid movement"));
|
||||||
return;
|
return;
|
||||||
@ -88,9 +88,9 @@ impl Player {
|
|||||||
let (x, lastx) = (entity.x, entity.lastx);
|
let (x, lastx) = (entity.x, entity.lastx);
|
||||||
let (y, lasty) = (entity.y, entity.lasty);
|
let (y, lasty) = (entity.y, entity.lasty);
|
||||||
let (z, lastz) = (entity.z, entity.lastz);
|
let (z, lastz) = (entity.z, entity.lastz);
|
||||||
|
let world = self.world.lock().await;
|
||||||
server.broadcast_packet(
|
world.broadcast_packet(
|
||||||
self,
|
&[&self.client.token],
|
||||||
&CUpdateEntityPos::new(
|
&CUpdateEntityPos::new(
|
||||||
entity_id.into(),
|
entity_id.into(),
|
||||||
(x * 4096.0 - lastx * 4096.0) as i16,
|
(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,
|
&mut self,
|
||||||
server: &mut Server,
|
_server: &mut Server,
|
||||||
position_rotation: SPlayerPositionRotation,
|
position_rotation: SPlayerPositionRotation,
|
||||||
) {
|
) {
|
||||||
if position_rotation.x.is_nan()
|
if position_rotation.x.is_nan()
|
||||||
@ -137,9 +137,10 @@ impl Player {
|
|||||||
let yaw = modulus(entity.yaw * 256.0 / 360.0, 256.0);
|
let yaw = modulus(entity.yaw * 256.0 / 360.0, 256.0);
|
||||||
let pitch = modulus(entity.pitch * 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 head_yaw = (entity.head_yaw * 256.0 / 360.0).floor();
|
||||||
|
let world = self.world.lock().await;
|
||||||
|
|
||||||
server.broadcast_packet(
|
world.broadcast_packet(
|
||||||
self,
|
&[&self.client.token],
|
||||||
&CUpdateEntityPosRot::new(
|
&CUpdateEntityPosRot::new(
|
||||||
entity_id.into(),
|
entity_id.into(),
|
||||||
(x * 4096.0 - lastx * 4096.0) as i16,
|
(x * 4096.0 - lastx * 4096.0) as i16,
|
||||||
@ -150,10 +151,13 @@ impl Player {
|
|||||||
on_ground,
|
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() {
|
if !rotation.yaw.is_finite() || !rotation.pitch.is_finite() {
|
||||||
self.kick(TextComponent::text("Invalid rotation"));
|
self.kick(TextComponent::text("Invalid rotation"));
|
||||||
return;
|
return;
|
||||||
@ -168,11 +172,13 @@ impl Player {
|
|||||||
let pitch = modulus(entity.pitch * 256.0 / 360.0, 256.0);
|
let pitch = modulus(entity.pitch * 256.0 / 360.0, 256.0);
|
||||||
// let head_yaw = modulus(entity.head_yaw * 256.0 / 360.0, 256.0);
|
// let head_yaw = modulus(entity.head_yaw * 256.0 / 360.0, 256.0);
|
||||||
|
|
||||||
server.broadcast_packet(
|
let world = self.world.lock().await;
|
||||||
self,
|
let packet = CUpdateEntityRot::new(entity_id.into(), yaw as u8, pitch as u8, on_ground);
|
||||||
&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);
|
||||||
server.broadcast_packet(self, &CHeadRot::new(entity_id.into(), yaw as u8));
|
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) {
|
pub fn handle_chat_command(&mut self, server: &mut Server, command: SChatCommand) {
|
||||||
@ -183,7 +189,7 @@ impl Player {
|
|||||||
self.on_ground = ground.on_ground;
|
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() {
|
if command.entity_id != self.entity.entity_id.into() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -192,23 +198,23 @@ impl Player {
|
|||||||
match action {
|
match action {
|
||||||
pumpkin_protocol::server::play::Action::StartSneaking => {
|
pumpkin_protocol::server::play::Action::StartSneaking => {
|
||||||
if !self.sneaking {
|
if !self.sneaking {
|
||||||
self.set_sneaking(server, true)
|
self.set_sneaking(true).await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pumpkin_protocol::server::play::Action::StopSneaking => {
|
pumpkin_protocol::server::play::Action::StopSneaking => {
|
||||||
if self.sneaking {
|
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::LeaveBed => todo!(),
|
||||||
pumpkin_protocol::server::play::Action::StartSprinting => {
|
pumpkin_protocol::server::play::Action::StartSprinting => {
|
||||||
if !self.sprinting {
|
if !self.sprinting {
|
||||||
self.set_sprinting(server, true)
|
self.set_sprinting(true).await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pumpkin_protocol::server::play::Action::StopSprinting => {
|
pumpkin_protocol::server::play::Action::StopSprinting => {
|
||||||
if self.sprinting {
|
if self.sprinting {
|
||||||
self.set_sprinting(server, false)
|
self.set_sprinting(false).await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pumpkin_protocol::server::play::Action::StartHorseJump => todo!(),
|
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) {
|
match Hand::from_i32(swing_arm.hand.0) {
|
||||||
Some(hand) => {
|
Some(hand) => {
|
||||||
let animation = match hand {
|
let animation = match hand {
|
||||||
@ -229,7 +235,8 @@ impl Player {
|
|||||||
Hand::Off => Animation::SwingOffhand,
|
Hand::Off => Animation::SwingOffhand,
|
||||||
};
|
};
|
||||||
let id = self.entity_id();
|
let id = self.entity_id();
|
||||||
server.broadcast_packet_except(
|
let world = self.world.lock().await;
|
||||||
|
world.broadcast_packet(
|
||||||
&[&self.client.token],
|
&[&self.client.token],
|
||||||
&CEntityAnimation::new(id.into(), animation as u8),
|
&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");
|
dbg!("got message");
|
||||||
|
|
||||||
let message = chat_message.message;
|
let message = chat_message.message;
|
||||||
@ -252,8 +259,9 @@ impl Player {
|
|||||||
// TODO: filter message & validation
|
// TODO: filter message & validation
|
||||||
let gameprofile = &self.gameprofile;
|
let gameprofile = &self.gameprofile;
|
||||||
|
|
||||||
server.broadcast_packet(
|
let world = self.world.lock().await;
|
||||||
self,
|
world.broadcast_packet(
|
||||||
|
&[&self.client.token],
|
||||||
&CPlayerChatMessage::new(
|
&CPlayerChatMessage::new(
|
||||||
pumpkin_protocol::uuid::UUID(gameprofile.id),
|
pumpkin_protocol::uuid::UUID(gameprofile.id),
|
||||||
1.into(),
|
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;
|
let sneaking = interact.sneaking;
|
||||||
if self.sneaking != sneaking {
|
if self.sneaking != sneaking {
|
||||||
self.set_sneaking(server, sneaking);
|
self.set_sneaking(sneaking).await;
|
||||||
}
|
}
|
||||||
match ActionType::from_i32(interact.typ.0) {
|
match ActionType::from_i32(interact.typ.0) {
|
||||||
Some(action) => match action {
|
Some(action) => match action {
|
||||||
@ -317,7 +325,9 @@ impl Player {
|
|||||||
// TODO: do validation and stuff
|
// TODO: do validation and stuff
|
||||||
let config = &server.advanced_config.pvp;
|
let config = &server.advanced_config.pvp;
|
||||||
if config.enabled {
|
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 {
|
if let Some(mut player) = attacked_player {
|
||||||
let token = player.client.token.clone();
|
let token = player.client.token.clone();
|
||||||
let velo = player.velocity;
|
let velo = player.velocity;
|
||||||
@ -349,8 +359,8 @@ impl Player {
|
|||||||
let packet = &CHurtAnimation::new(&entity_id, self.entity.yaw);
|
let packet = &CHurtAnimation::new(&entity_id, self.entity.yaw);
|
||||||
self.client.send_packet(packet);
|
self.client.send_packet(packet);
|
||||||
player.client.send_packet(packet);
|
player.client.send_packet(packet);
|
||||||
server.broadcast_packet_except(
|
world.broadcast_packet(
|
||||||
&[self.client.token.as_ref(), token.as_ref()],
|
&[&self.client.token, &token],
|
||||||
&CHurtAnimation::new(&entity_id, 10.0),
|
&CHurtAnimation::new(&entity_id, 10.0),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -370,7 +380,11 @@ impl Player {
|
|||||||
None => self.kick(TextComponent::text("Invalid action type")),
|
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) {
|
match Status::from_i32(player_action.status.0) {
|
||||||
Some(status) => match status {
|
Some(status) => match status {
|
||||||
Status::StartedDigging => {
|
Status::StartedDigging => {
|
||||||
@ -384,10 +398,16 @@ impl Player {
|
|||||||
let location = player_action.location;
|
let location = player_action.location;
|
||||||
// Block break & block break sound
|
// Block break & block break sound
|
||||||
// TODO: currently this is always dirt replace it
|
// TODO: currently this is always dirt replace it
|
||||||
server
|
let world = self.world.lock().await;
|
||||||
.broadcast_packet(self, &CWorldEvent::new(2001, &location, 11, false));
|
world.broadcast_packet(
|
||||||
|
&[&self.client.token],
|
||||||
|
&CWorldEvent::new(2001, &location, 11, false),
|
||||||
|
);
|
||||||
// AIR
|
// AIR
|
||||||
server.broadcast_packet(self, &CBlockUpdate::new(&location, 0.into()));
|
world.broadcast_packet(
|
||||||
|
&[&self.client.token],
|
||||||
|
&CBlockUpdate::new(&location, 0.into()),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Status::CancelledDigging => {
|
Status::CancelledDigging => {
|
||||||
@ -406,9 +426,16 @@ impl Player {
|
|||||||
}
|
}
|
||||||
// Block break & block break sound
|
// Block break & block break sound
|
||||||
// TODO: currently this is always dirt replace it
|
// 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
|
// 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
|
// TODO: Send this every tick
|
||||||
self.client
|
self.client
|
||||||
.send_packet(&CAcknowledgeBlockChange::new(player_action.sequence));
|
.send_packet(&CAcknowledgeBlockChange::new(player_action.sequence));
|
||||||
@ -435,7 +462,7 @@ impl Player {
|
|||||||
.send_packet(&CPingResponse::new(request.payload));
|
.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;
|
let location = use_item_on.location;
|
||||||
|
|
||||||
if !self.can_interact_with_block_at(&location, 1.0) {
|
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");
|
.expect("All item ids are in the global registry");
|
||||||
if let Ok(block_state_id) = BlockId::new(minecraft_id, None) {
|
if let Ok(block_state_id) = BlockId::new(minecraft_id, None) {
|
||||||
server.broadcast_packet(
|
let world = self.world.lock().await;
|
||||||
self,
|
world.broadcast_packet(
|
||||||
|
&[&self.client.token],
|
||||||
&CBlockUpdate::new(&location, block_state_id.get_id_mojang_repr().into()),
|
&CBlockUpdate::new(&location, block_state_id.get_id_mojang_repr().into()),
|
||||||
);
|
);
|
||||||
server.broadcast_packet(
|
world.broadcast_packet(
|
||||||
self,
|
&[&self.client.token],
|
||||||
&CBlockUpdate::new(
|
&CBlockUpdate::new(
|
||||||
&WorldPosition(location.0 + face.to_offset()),
|
&WorldPosition(location.0 + face.to_offset()),
|
||||||
block_state_id.get_id_mojang_repr().into(),
|
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(
|
CommandTree::new(NAMES, DESCRIPTION).with_child(
|
||||||
require(&|sender| sender.permission_lvl() >= 2).with_child(
|
require(&|sender| sender.permission_lvl() >= 2).with_child(
|
||||||
argument(ARG_GAMEMODE, consume_arg_gamemode)
|
argument(ARG_GAMEMODE, consume_arg_gamemode)
|
||||||
.with_child(require(&|sender| sender.is_player()).execute(
|
.with_child(
|
||||||
&|sender, server, args| {
|
require(&|sender| sender.is_player()).execute(&|sender, _, args| {
|
||||||
let gamemode = parse_arg_gamemode(args)?;
|
let gamemode = parse_arg_gamemode(args)?;
|
||||||
|
|
||||||
return if let Player(target) = sender {
|
return if let Player(target) = sender {
|
||||||
@ -71,7 +71,9 @@ pub(crate) fn init_command_tree<'a>() -> CommandTree<'a> {
|
|||||||
gamemode
|
gamemode
|
||||||
)));
|
)));
|
||||||
} else {
|
} else {
|
||||||
target.set_gamemode(server, gamemode);
|
// TODO
|
||||||
|
#[allow(clippy::let_underscore_future)]
|
||||||
|
let _ = target.set_gamemode(gamemode);
|
||||||
target.send_system_message(TextComponent::text(&format!(
|
target.send_system_message(TextComponent::text(&format!(
|
||||||
"Game mode was set to {:?}",
|
"Game mode was set to {:?}",
|
||||||
gamemode
|
gamemode
|
||||||
@ -81,10 +83,10 @@ pub(crate) fn init_command_tree<'a>() -> CommandTree<'a> {
|
|||||||
} else {
|
} else {
|
||||||
Err(InvalidRequirementError)
|
Err(InvalidRequirementError)
|
||||||
};
|
};
|
||||||
},
|
}),
|
||||||
))
|
)
|
||||||
.with_child(argument(ARG_TARGET, consume_arg_player).execute(
|
.with_child(argument(ARG_TARGET, consume_arg_player).execute(
|
||||||
&|sender, server, args| {
|
&|sender, _, args| {
|
||||||
let gamemode = parse_arg_gamemode(args)?;
|
let gamemode = parse_arg_gamemode(args)?;
|
||||||
let target = parse_arg_player(sender, ARG_TARGET, args)?;
|
let target = parse_arg_player(sender, ARG_TARGET, args)?;
|
||||||
|
|
||||||
@ -94,7 +96,9 @@ pub(crate) fn init_command_tree<'a>() -> CommandTree<'a> {
|
|||||||
gamemode
|
gamemode
|
||||||
)));
|
)));
|
||||||
} else {
|
} else {
|
||||||
target.set_gamemode(server, gamemode);
|
// TODO
|
||||||
|
#[allow(clippy::let_underscore_future)]
|
||||||
|
let _ = target.set_gamemode(gamemode);
|
||||||
target.send_system_message(TextComponent::text(&format!(
|
target.send_system_message(TextComponent::text(&format!(
|
||||||
"Game mode was set to {:?}",
|
"Game mode was set to {:?}",
|
||||||
gamemode
|
gamemode
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
use std::str::FromStr;
|
use std::{str::FromStr, sync::Arc};
|
||||||
|
|
||||||
use num_derive::{FromPrimitive, ToPrimitive};
|
use num_derive::{FromPrimitive, ToPrimitive};
|
||||||
use num_traits::ToPrimitive;
|
use num_traits::ToPrimitive;
|
||||||
@ -27,6 +27,7 @@ use crate::{
|
|||||||
client::{authentication::GameProfile, Client},
|
client::{authentication::GameProfile, Client},
|
||||||
server::Server,
|
server::Server,
|
||||||
util::boundingbox::BoundingBox,
|
util::boundingbox::BoundingBox,
|
||||||
|
world::World,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct PlayerAbilities {
|
pub struct PlayerAbilities {
|
||||||
@ -55,7 +56,9 @@ pub struct Player {
|
|||||||
pub gameprofile: GameProfile,
|
pub gameprofile: GameProfile,
|
||||||
pub client: Client,
|
pub client: Client,
|
||||||
pub entity: Entity,
|
pub entity: Entity,
|
||||||
// current gamemode
|
// TODO: Put this into entity
|
||||||
|
pub world: Arc<tokio::sync::Mutex<World>>,
|
||||||
|
/// Current gamemode
|
||||||
pub gamemode: GameMode,
|
pub gamemode: GameMode,
|
||||||
// TODO: prbly should put this into an Living Entitiy or something
|
// TODO: prbly should put this into an Living Entitiy or something
|
||||||
pub health: f32,
|
pub health: f32,
|
||||||
@ -83,7 +86,12 @@ pub struct Player {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl 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() {
|
let gameprofile = match client.gameprofile.clone() {
|
||||||
Some(profile) => profile,
|
Some(profile) => profile,
|
||||||
None => {
|
None => {
|
||||||
@ -101,6 +109,7 @@ impl Player {
|
|||||||
gameprofile,
|
gameprofile,
|
||||||
client,
|
client,
|
||||||
entity: Entity::new(entity_id, EntityType::Player, 1.62),
|
entity: Entity::new(entity_id, EntityType::Player, 1.62),
|
||||||
|
world,
|
||||||
on_ground: false,
|
on_ground: false,
|
||||||
awaiting_teleport: None,
|
awaiting_teleport: None,
|
||||||
sneaking: false,
|
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 {
|
pub fn entity_id(&self) -> EntityId {
|
||||||
self.entity.entity_id
|
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);
|
assert!(self.sneaking != sneaking);
|
||||||
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 {
|
if sneaking {
|
||||||
self.set_pose(server, EntityPose::Crouching);
|
self.set_pose(EntityPose::Crouching).await;
|
||||||
} else {
|
} 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);
|
assert!(self.sprinting != sprinting);
|
||||||
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;
|
pub const ON_FIRE_FLAG_INDEX: u32 = 0;
|
||||||
@ -191,29 +206,34 @@ impl Player {
|
|||||||
pub const INVISIBLE_FLAG_INDEX: u32 = 5;
|
pub const INVISIBLE_FLAG_INDEX: u32 = 5;
|
||||||
pub const GLOWING_FLAG_INDEX: u32 = 6;
|
pub const GLOWING_FLAG_INDEX: u32 = 6;
|
||||||
pub const FALL_FLYING_FLAG_INDEX: u32 = 7;
|
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;
|
let mut b = 0i8;
|
||||||
if value {
|
if value {
|
||||||
b |= 1 << index;
|
b |= 1 << index;
|
||||||
} else {
|
} else {
|
||||||
b &= !(1 << index);
|
b &= !(1 << index);
|
||||||
}
|
}
|
||||||
server.broadcast_packet(
|
let packet =
|
||||||
self,
|
CSetEntityMetadata::new(self.entity_id().into(), Metadata::new(0, 0.into(), b));
|
||||||
&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;
|
self.entity.pose = pose;
|
||||||
let pose = self.entity.pose as i32;
|
let pose = self.entity.pose as i32;
|
||||||
server.broadcast_packet(
|
let packet = CSetEntityMetadata::<VarInt>::new(
|
||||||
self,
|
self.entity_id().into(),
|
||||||
&CSetEntityMetadata::<VarInt>::new(
|
Metadata::new(6, 20.into(), (pose).into()),
|
||||||
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) {
|
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;
|
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 ?
|
// We could send the same gamemode without problems. But why waste bandwidth ?
|
||||||
assert!(
|
assert!(
|
||||||
self.gamemode != gamemode,
|
self.gamemode != gamemode,
|
||||||
@ -287,8 +307,17 @@ impl Player {
|
|||||||
self.gamemode = gamemode;
|
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
|
// 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
|
// 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(
|
&CPlayerInfoUpdate::new(
|
||||||
0x04,
|
0x04,
|
||||||
&[pumpkin_protocol::client::play::Player {
|
&[pumpkin_protocol::client::play::Player {
|
||||||
@ -308,9 +337,9 @@ impl Player {
|
|||||||
}
|
}
|
||||||
|
|
||||||
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() {
|
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(_) => {}
|
Ok(_) => {}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
let text = format!("Error while reading incoming packet {}", 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,
|
&mut self,
|
||||||
server: &mut Server,
|
server: &mut Server,
|
||||||
packet: &mut RawPacket,
|
packet: &mut RawPacket,
|
||||||
@ -337,15 +366,18 @@ impl Player {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
SPlayerPosition::PACKET_ID => {
|
SPlayerPosition::PACKET_ID => {
|
||||||
self.handle_position(server, SPlayerPosition::read(bytebuf)?);
|
self.handle_position(server, SPlayerPosition::read(bytebuf)?)
|
||||||
|
.await;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
SPlayerPositionRotation::PACKET_ID => {
|
SPlayerPositionRotation::PACKET_ID => {
|
||||||
self.handle_position_rotation(server, SPlayerPositionRotation::read(bytebuf)?);
|
self.handle_position_rotation(server, SPlayerPositionRotation::read(bytebuf)?)
|
||||||
|
.await;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
SPlayerRotation::PACKET_ID => {
|
SPlayerRotation::PACKET_ID => {
|
||||||
self.handle_rotation(server, SPlayerRotation::read(bytebuf)?);
|
self.handle_rotation(server, SPlayerRotation::read(bytebuf)?)
|
||||||
|
.await;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
SSetPlayerGround::PACKET_ID => {
|
SSetPlayerGround::PACKET_ID => {
|
||||||
@ -353,15 +385,18 @@ impl Player {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
SPlayerCommand::PACKET_ID => {
|
SPlayerCommand::PACKET_ID => {
|
||||||
self.handle_player_command(server, SPlayerCommand::read(bytebuf)?);
|
self.handle_player_command(server, SPlayerCommand::read(bytebuf)?)
|
||||||
|
.await;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
SSwingArm::PACKET_ID => {
|
SSwingArm::PACKET_ID => {
|
||||||
self.handle_swing_arm(server, SSwingArm::read(bytebuf)?);
|
self.handle_swing_arm(server, SSwingArm::read(bytebuf)?)
|
||||||
|
.await;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
SChatMessage::PACKET_ID => {
|
SChatMessage::PACKET_ID => {
|
||||||
self.handle_chat_message(server, SChatMessage::read(bytebuf)?);
|
self.handle_chat_message(server, SChatMessage::read(bytebuf)?)
|
||||||
|
.await;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
SClientInformationPlay::PACKET_ID => {
|
SClientInformationPlay::PACKET_ID => {
|
||||||
@ -369,15 +404,18 @@ impl Player {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
SInteract::PACKET_ID => {
|
SInteract::PACKET_ID => {
|
||||||
self.handle_interact(server, SInteract::read(bytebuf)?);
|
self.handle_interact(server, SInteract::read(bytebuf)?)
|
||||||
|
.await;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
SPlayerAction::PACKET_ID => {
|
SPlayerAction::PACKET_ID => {
|
||||||
self.handle_player_action(server, SPlayerAction::read(bytebuf)?);
|
self.handle_player_action(server, SPlayerAction::read(bytebuf)?)
|
||||||
|
.await;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
SUseItemOn::PACKET_ID => {
|
SUseItemOn::PACKET_ID => {
|
||||||
self.handle_use_item_on(server, SUseItemOn::read(bytebuf)?);
|
self.handle_use_item_on(server, SUseItemOn::read(bytebuf)?)
|
||||||
|
.await;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
SUseItem::PACKET_ID => {
|
SUseItem::PACKET_ID => {
|
||||||
|
|||||||
@ -28,6 +28,7 @@ pub mod proxy;
|
|||||||
pub mod rcon;
|
pub mod rcon;
|
||||||
pub mod server;
|
pub mod server;
|
||||||
pub mod util;
|
pub mod util;
|
||||||
|
pub mod world;
|
||||||
|
|
||||||
fn main() -> io::Result<()> {
|
fn main() -> io::Result<()> {
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
@ -177,7 +178,7 @@ fn main() -> io::Result<()> {
|
|||||||
let mut player = player.lock().unwrap();
|
let mut player = player.lock().unwrap();
|
||||||
player.client.poll(event).await;
|
player.client.poll(event).await;
|
||||||
let mut server = server.lock().await;
|
let mut server = server.lock().await;
|
||||||
player.process_packets(&mut server);
|
player.process_packets(&mut server).await;
|
||||||
player.client.closed
|
player.client.closed
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
@ -185,9 +186,8 @@ fn main() -> io::Result<()> {
|
|||||||
|
|
||||||
if done {
|
if done {
|
||||||
if let Some(player) = players.remove(&token) {
|
if let Some(player) = players.remove(&token) {
|
||||||
let mut server = server.lock().await;
|
|
||||||
server.remove_player(&token);
|
|
||||||
let mut player = player.lock().unwrap();
|
let mut player = player.lock().unwrap();
|
||||||
|
player.remove().await;
|
||||||
poll.registry().deregister(&mut player.client.connection)?;
|
poll.registry().deregister(&mut player.client.connection)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -210,10 +210,11 @@ fn main() -> io::Result<()> {
|
|||||||
} else if make_player {
|
} else if make_player {
|
||||||
let token = client.token.clone();
|
let token = client.token.clone();
|
||||||
let mut server = server.lock().await;
|
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());
|
players.insert(token, player.clone());
|
||||||
let mut player = player.lock().unwrap();
|
let mut world = world.lock().await;
|
||||||
server.spawn_player(&mut player).await;
|
world.spawn_player(&server.base_config, player).await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,10 +1,9 @@
|
|||||||
use std::{
|
use std::{
|
||||||
collections::HashMap,
|
|
||||||
io::Cursor,
|
io::Cursor,
|
||||||
path::Path,
|
path::Path,
|
||||||
sync::{
|
sync::{
|
||||||
atomic::{AtomicI32, Ordering},
|
atomic::{AtomicI32, Ordering},
|
||||||
Arc, Mutex, MutexGuard,
|
Arc, Mutex,
|
||||||
},
|
},
|
||||||
time::Duration,
|
time::Duration,
|
||||||
};
|
};
|
||||||
@ -12,32 +11,23 @@ use std::{
|
|||||||
use base64::{engine::general_purpose, Engine};
|
use base64::{engine::general_purpose, Engine};
|
||||||
use image::GenericImageView;
|
use image::GenericImageView;
|
||||||
use mio::Token;
|
use mio::Token;
|
||||||
use num_traits::ToPrimitive;
|
use pumpkin_entity::EntityId;
|
||||||
use pumpkin_entity::{entity_type::EntityType, EntityId};
|
|
||||||
use pumpkin_plugin::PluginLoader;
|
use pumpkin_plugin::PluginLoader;
|
||||||
use pumpkin_protocol::{
|
use pumpkin_protocol::{
|
||||||
client::{
|
client::config::CPluginMessage, ClientPacket, Players, Sample, StatusResponse, VarInt, Version,
|
||||||
config::CPluginMessage,
|
CURRENT_MC_PROTOCOL,
|
||||||
play::{
|
|
||||||
CCenterChunk, CChunkData, CGameEvent, CLogin, CPlayerAbilities, CPlayerInfoUpdate,
|
|
||||||
CRemoveEntities, CRemovePlayerInfo, CSetEntityMetadata, CSpawnEntity, Metadata,
|
|
||||||
PlayerAction,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
uuid::UUID,
|
|
||||||
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 pumpkin_registry::Registry;
|
||||||
use rsa::{traits::PublicKeyParts, RsaPrivateKey, RsaPublicKey};
|
use rsa::{traits::PublicKeyParts, RsaPrivateKey, RsaPublicKey};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use tokio::sync::mpsc;
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
client::Client,
|
client::Client,
|
||||||
config::{AdvancedConfiguration, BasicConfiguration},
|
config::{AdvancedConfiguration, BasicConfiguration},
|
||||||
entity::player::{GameMode, Player},
|
entity::player::{GameMode, Player},
|
||||||
|
world::World,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const CURRENT_MC_VERSION: &str = "1.21.1";
|
pub const CURRENT_MC_VERSION: &str = "1.21.1";
|
||||||
@ -49,7 +39,7 @@ pub struct Server {
|
|||||||
|
|
||||||
pub plugin_loader: PluginLoader,
|
pub plugin_loader: PluginLoader,
|
||||||
|
|
||||||
pub world: Arc<tokio::sync::Mutex<World>>,
|
pub worlds: Vec<Arc<tokio::sync::Mutex<World>>>,
|
||||||
pub status_response: StatusResponse,
|
pub status_response: StatusResponse,
|
||||||
// We cache the json response here so we don't parse it every time someone makes a Status request.
|
// 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
|
// 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
|
/// Cache the registry so we don't have to parse it every time a player joins
|
||||||
pub cached_registry: Vec<Registry>,
|
pub cached_registry: Vec<Registry>,
|
||||||
|
|
||||||
// TODO: place this into every world
|
|
||||||
pub current_players: HashMap<Arc<Token>, Arc<Mutex<Player>>>,
|
|
||||||
|
|
||||||
entity_id: AtomicI32,
|
entity_id: AtomicI32,
|
||||||
pub base_config: BasicConfiguration,
|
pub base_config: BasicConfiguration,
|
||||||
pub advanced_config: AdvancedConfiguration,
|
pub advanced_config: AdvancedConfiguration,
|
||||||
@ -112,282 +99,52 @@ impl Server {
|
|||||||
cached_registry: Registry::get_static(),
|
cached_registry: Registry::get_static(),
|
||||||
// 0 is invalid
|
// 0 is invalid
|
||||||
entity_id: 2.into(),
|
entity_id: 2.into(),
|
||||||
world: Arc::new(tokio::sync::Mutex::new(world)),
|
worlds: vec![Arc::new(tokio::sync::Mutex::new(world))],
|
||||||
public_key,
|
public_key,
|
||||||
cached_server_brand,
|
cached_server_brand,
|
||||||
private_key,
|
private_key,
|
||||||
status_response,
|
status_response,
|
||||||
status_response_json,
|
status_response_json,
|
||||||
public_key_der,
|
public_key_der,
|
||||||
current_players: HashMap::new(),
|
|
||||||
base_config: config.0,
|
base_config: config.0,
|
||||||
auth_client,
|
auth_client,
|
||||||
advanced_config: config.1,
|
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 entity_id = self.new_entity_id();
|
||||||
let gamemode = match self.base_config.default_gamemode {
|
let gamemode = match self.base_config.default_gamemode {
|
||||||
GameMode::Undefined => GameMode::Survival,
|
GameMode::Undefined => GameMode::Survival,
|
||||||
game_mode => game_mode,
|
game_mode => game_mode,
|
||||||
};
|
};
|
||||||
let player = Arc::new(Mutex::new(Player::new(client, entity_id, gamemode)));
|
// Basicly the default world
|
||||||
self.current_players.insert(token, player.clone());
|
// TODO: select default from config
|
||||||
player
|
let world = self.worlds[0].clone();
|
||||||
}
|
let player = Arc::new(Mutex::new(Player::new(
|
||||||
|
client,
|
||||||
pub fn remove_player(&mut self, token: &Token) {
|
world.clone(),
|
||||||
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(
|
|
||||||
entity_id,
|
entity_id,
|
||||||
self.base_config.hardcore,
|
gamemode,
|
||||||
&["minecraft:overworld"],
|
)));
|
||||||
self.base_config.max_players.into(),
|
world.lock().await.add_player(token, player.clone());
|
||||||
self.base_config.view_distance.into(), // TODO: view distance
|
(player, world)
|
||||||
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
|
/// Sends a Packet to all Players in all worlds
|
||||||
pub fn get_by_entityid(&self, from: &Player, id: EntityId) -> Option<MutexGuard<Player>> {
|
pub fn broadcast_packet_all<P>(&self, expect: &[&Arc<Token>], packet: &P)
|
||||||
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)
|
|
||||||
where
|
where
|
||||||
P: ClientPacket,
|
P: ClientPacket,
|
||||||
{
|
{
|
||||||
// we can't borrow twice at same time
|
for world in &self.worlds {
|
||||||
from.client.send_packet(packet);
|
world.blocking_lock().broadcast_packet(expect, 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<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
|
// move to world
|
||||||
pub fn new_entity_id(&self) -> EntityId {
|
pub fn new_entity_id(&self) -> EntityId {
|
||||||
self.entity_id.fetch_add(1, Ordering::SeqCst)
|
self.entity_id.fetch_add(1, Ordering::SeqCst)
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user