Less unwraps
This commit is contained in:
parent
0ba9b37048
commit
c36b6d3464
@ -31,6 +31,8 @@ use super::{
|
|||||||
|
|
||||||
/// Processes incoming Packets from the Client to the Server
|
/// Processes incoming Packets from the Client to the Server
|
||||||
/// Implements the `Client` Packets
|
/// Implements the `Client` Packets
|
||||||
|
/// NEVER TRUST THE CLIENT. HANDLE EVERY ERROR, UNWRAP/EXPECT
|
||||||
|
/// TODO: REMOVE ALL UNWRAPS
|
||||||
impl Client {
|
impl Client {
|
||||||
pub fn handle_handshake(&mut self, _server: &mut Server, handshake: SHandShake) {
|
pub fn handle_handshake(&mut self, _server: &mut Server, handshake: SHandShake) {
|
||||||
dbg!("handshake");
|
dbg!("handshake");
|
||||||
|
|||||||
@ -35,6 +35,7 @@ fn modulus(a: f32, b: f32) -> f32 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Handles all Play Packets send by a real Player
|
/// Handles all Play Packets send by a real Player
|
||||||
|
/// NEVER TRUST THE CLIENT. HANDLE EVERY ERROR, UNWRAP/EXPECT ARE FORBIDDEN
|
||||||
impl Player {
|
impl Player {
|
||||||
pub fn handle_confirm_teleport(
|
pub fn handle_confirm_teleport(
|
||||||
&mut self,
|
&mut self,
|
||||||
@ -205,15 +206,22 @@ impl Player {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn handle_swing_arm(&mut self, server: &mut Server, swing_arm: SSwingArm) {
|
pub fn handle_swing_arm(&mut self, server: &mut Server, swing_arm: SSwingArm) {
|
||||||
let animation = match Hand::from_i32(swing_arm.hand.0).unwrap() {
|
match Hand::from_i32(swing_arm.hand.0) {
|
||||||
Hand::Main => Animation::SwingMainArm,
|
Some(hand) => {
|
||||||
Hand::Off => Animation::SwingOffhand,
|
let animation = match hand {
|
||||||
|
Hand::Main => Animation::SwingMainArm,
|
||||||
|
Hand::Off => Animation::SwingOffhand,
|
||||||
|
};
|
||||||
|
let id = self.entity_id();
|
||||||
|
server.broadcast_packet_except(
|
||||||
|
&[&self.client.token],
|
||||||
|
&CEntityAnimation::new(id.into(), animation as u8),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
self.kick(TextComponent::text("Invalid hand"));
|
||||||
|
}
|
||||||
};
|
};
|
||||||
let id = self.entity_id();
|
|
||||||
server.broadcast_packet_except(
|
|
||||||
&[&self.client.token],
|
|
||||||
&CEntityAnimation::new(id.into(), animation as u8),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn handle_chat_message(&mut self, server: &mut Server, chat_message: SChatMessage) {
|
pub fn handle_chat_message(&mut self, server: &mut Server, chat_message: SChatMessage) {
|
||||||
@ -226,7 +234,7 @@ impl Player {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO: filter message & validation
|
// TODO: filter message & validation
|
||||||
let gameprofile = self.client.gameprofile.as_ref().unwrap();
|
let gameprofile = &self.gameprofile;
|
||||||
|
|
||||||
server.broadcast_packet(
|
server.broadcast_packet(
|
||||||
self,
|
self,
|
||||||
@ -262,123 +270,144 @@ impl Player {
|
|||||||
_server: &mut Server,
|
_server: &mut Server,
|
||||||
client_information: SClientInformationPlay,
|
client_information: SClientInformationPlay,
|
||||||
) {
|
) {
|
||||||
self.client.config = Some(PlayerConfig {
|
if let (Some(main_hand), Some(chat_mode)) = (
|
||||||
locale: client_information.locale,
|
Hand::from_i32(client_information.main_hand.into()),
|
||||||
view_distance: client_information.view_distance,
|
ChatMode::from_i32(client_information.chat_mode.into()),
|
||||||
chat_mode: ChatMode::from_i32(client_information.chat_mode.into()).unwrap(),
|
) {
|
||||||
chat_colors: client_information.chat_colors,
|
self.client.config = Some(PlayerConfig {
|
||||||
skin_parts: client_information.skin_parts,
|
locale: client_information.locale,
|
||||||
main_hand: Hand::from_i32(client_information.main_hand.into()).unwrap(),
|
view_distance: client_information.view_distance,
|
||||||
text_filtering: client_information.text_filtering,
|
chat_mode,
|
||||||
server_listing: client_information.server_listing,
|
chat_colors: client_information.chat_colors,
|
||||||
});
|
skin_parts: client_information.skin_parts,
|
||||||
|
main_hand,
|
||||||
|
text_filtering: client_information.text_filtering,
|
||||||
|
server_listing: client_information.server_listing,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
self.kick(TextComponent::text("Invalid hand or chat type"))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn handle_interact(&mut self, server: &mut Server, interact: SInteract) {
|
pub fn handle_interact(&mut self, server: &mut Server, interact: SInteract) {
|
||||||
let action = ActionType::from_i32(interact.typ.0).unwrap();
|
match ActionType::from_i32(interact.typ.0) {
|
||||||
if action == ActionType::Attack {
|
Some(action) => match action {
|
||||||
let entity_id = interact.entity_id;
|
ActionType::Attack => {
|
||||||
// TODO: do validation and stuff
|
let entity_id = interact.entity_id;
|
||||||
let config = &server.advanced_config.pvp;
|
// TODO: do validation and stuff
|
||||||
if config.enabled {
|
let config = &server.advanced_config.pvp;
|
||||||
let attacked_player = server.get_by_entityid(self, entity_id.0 as EntityId);
|
if config.enabled {
|
||||||
self.sneaking = interact.sneaking;
|
let attacked_player = server.get_by_entityid(self, entity_id.0 as EntityId);
|
||||||
if let Some(mut player) = attacked_player {
|
self.sneaking = interact.sneaking;
|
||||||
let token = player.client.token.clone();
|
if let Some(mut player) = attacked_player {
|
||||||
let velo = player.velocity;
|
let token = player.client.token.clone();
|
||||||
if config.protect_creative && player.gamemode == GameMode::Creative {
|
let velo = player.velocity;
|
||||||
return;
|
if config.protect_creative && player.gamemode == GameMode::Creative {
|
||||||
}
|
return;
|
||||||
if config.knockback {
|
}
|
||||||
let yaw = self.entity.yaw;
|
if config.knockback {
|
||||||
let strength = 1.0;
|
let yaw = self.entity.yaw;
|
||||||
player.knockback(
|
let strength = 1.0;
|
||||||
strength * 0.5,
|
player.knockback(
|
||||||
(yaw * (PI / 180.0)).sin() as f64,
|
strength * 0.5,
|
||||||
-(yaw * (PI / 180.0)).cos() as f64,
|
(yaw * (PI / 180.0)).sin() as f64,
|
||||||
);
|
-(yaw * (PI / 180.0)).cos() as f64,
|
||||||
let packet = &CEntityVelocity::new(
|
);
|
||||||
&entity_id,
|
let packet = &CEntityVelocity::new(
|
||||||
player.velocity.x as f32,
|
&entity_id,
|
||||||
player.velocity.y as f32,
|
player.velocity.x as f32,
|
||||||
player.velocity.z as f32,
|
player.velocity.y as f32,
|
||||||
);
|
player.velocity.z as f32,
|
||||||
self.velocity = self.velocity.multiply(0.6, 1.0, 0.6);
|
);
|
||||||
|
self.velocity = self.velocity.multiply(0.6, 1.0, 0.6);
|
||||||
|
|
||||||
player.velocity = velo;
|
player.velocity = velo;
|
||||||
player.client.send_packet(packet);
|
player.client.send_packet(packet);
|
||||||
|
}
|
||||||
|
if config.hurt_animation {
|
||||||
|
// TODO
|
||||||
|
// thats how we prevent borrow errors :c
|
||||||
|
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()],
|
||||||
|
&CHurtAnimation::new(&entity_id, 10.0),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if config.swing {}
|
||||||
|
} else {
|
||||||
|
self.kick(TextComponent::text("Interacted with invalid entity id"))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if config.hurt_animation {
|
|
||||||
// TODO
|
|
||||||
// thats how we prevent borrow errors :c
|
|
||||||
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()],
|
|
||||||
&CHurtAnimation::new(&entity_id, 10.0),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
if config.swing {}
|
|
||||||
} else {
|
|
||||||
self.kick(TextComponent::text("Interacted with invalid entity id"))
|
|
||||||
}
|
}
|
||||||
}
|
ActionType::Interact => {
|
||||||
|
dbg!("todo");
|
||||||
|
}
|
||||||
|
ActionType::InteractAt => {
|
||||||
|
dbg!("todo");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
None => self.kick(TextComponent::text("Invalid action type")),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn handle_player_action(&mut self, server: &mut Server, player_action: SPlayerAction) {
|
pub fn handle_player_action(&mut self, server: &mut Server, player_action: SPlayerAction) {
|
||||||
match Status::from_i32(player_action.status.0).unwrap() {
|
match Status::from_i32(player_action.status.0) {
|
||||||
Status::StartedDigging => {
|
Some(status) => match status {
|
||||||
if !self.can_interact_with_block_at(&player_action.location, 1.0) {
|
Status::StartedDigging => {
|
||||||
// TODO: maybe log?
|
if !self.can_interact_with_block_at(&player_action.location, 1.0) {
|
||||||
return;
|
// TODO: maybe log?
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// TODO: do validation
|
||||||
|
// TODO: Config
|
||||||
|
if self.gamemode == GameMode::Creative {
|
||||||
|
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));
|
||||||
|
// AIR
|
||||||
|
server.broadcast_packet(self, &CBlockUpdate::new(&location, 0.into()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// TODO: do validation
|
Status::CancelledDigging => {
|
||||||
// TODO: Config
|
if !self.can_interact_with_block_at(&player_action.location, 1.0) {
|
||||||
if self.gamemode == GameMode::Creative {
|
// TODO: maybe log?
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
self.current_block_destroy_stage = 0;
|
||||||
|
}
|
||||||
|
Status::FinishedDigging => {
|
||||||
|
// TODO: do validation
|
||||||
let location = player_action.location;
|
let location = player_action.location;
|
||||||
|
if !self.can_interact_with_block_at(&location, 1.0) {
|
||||||
|
// TODO: maybe log?
|
||||||
|
return;
|
||||||
|
}
|
||||||
// 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));
|
server.broadcast_packet(self, &CWorldEvent::new(2001, &location, 11, false));
|
||||||
// AIR
|
// AIR
|
||||||
server.broadcast_packet(self, &CBlockUpdate::new(&location, 0.into()));
|
server.broadcast_packet(self, &CBlockUpdate::new(&location, 0.into()));
|
||||||
|
// TODO: Send this every tick
|
||||||
|
self.client
|
||||||
|
.send_packet(&CAcknowledgeBlockChange::new(player_action.sequence));
|
||||||
}
|
}
|
||||||
}
|
Status::DropItemStack => {
|
||||||
Status::CancelledDigging => {
|
dbg!("todo");
|
||||||
if !self.can_interact_with_block_at(&player_action.location, 1.0) {
|
|
||||||
// TODO: maybe log?
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
self.current_block_destroy_stage = 0;
|
Status::DropItem => {
|
||||||
}
|
dbg!("todo");
|
||||||
Status::FinishedDigging => {
|
|
||||||
// TODO: do validation
|
|
||||||
let location = player_action.location;
|
|
||||||
if !self.can_interact_with_block_at(&location, 1.0) {
|
|
||||||
// TODO: maybe log?
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
// Block break & block break sound
|
Status::ShootArrowOrFinishEating => {
|
||||||
// TODO: currently this is always dirt replace it
|
dbg!("todo");
|
||||||
server.broadcast_packet(self, &CWorldEvent::new(2001, &location, 11, false));
|
}
|
||||||
// AIR
|
Status::SwapItem => {
|
||||||
server.broadcast_packet(self, &CBlockUpdate::new(&location, 0.into()));
|
dbg!("todo");
|
||||||
// TODO: Send this every tick
|
}
|
||||||
self.client
|
},
|
||||||
.send_packet(&CAcknowledgeBlockChange::new(player_action.sequence));
|
None => self.kick(TextComponent::text("Invalid status")),
|
||||||
}
|
|
||||||
Status::DropItemStack => {
|
|
||||||
dbg!("todo");
|
|
||||||
}
|
|
||||||
Status::DropItem => {
|
|
||||||
dbg!("todo");
|
|
||||||
}
|
|
||||||
Status::ShootArrowOrFinishEating => {
|
|
||||||
dbg!("todo");
|
|
||||||
}
|
|
||||||
Status::SwapItem => {
|
|
||||||
dbg!("todo");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -395,27 +424,32 @@ impl Player {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let face = BlockFace::from_i32(use_item_on.face.0).unwrap();
|
if let Some(face) = BlockFace::from_i32(use_item_on.face.0) {
|
||||||
if let Some(item) = self.inventory.held_item() {
|
if let Some(item) = self.inventory.held_item() {
|
||||||
let minecraft_id =
|
let minecraft_id = global_registry::find_minecraft_id(
|
||||||
global_registry::find_minecraft_id(global_registry::ITEM_REGISTRY, item.item_id)
|
global_registry::ITEM_REGISTRY,
|
||||||
.expect("All item ids are in the global registry");
|
item.item_id,
|
||||||
if let Ok(block_state_id) = BlockId::new(minecraft_id, None) {
|
)
|
||||||
server.broadcast_packet(
|
.expect("All item ids are in the global registry");
|
||||||
self,
|
if let Ok(block_state_id) = BlockId::new(minecraft_id, None) {
|
||||||
&CBlockUpdate::new(&location, block_state_id.get_id_mojang_repr().into()),
|
server.broadcast_packet(
|
||||||
);
|
self,
|
||||||
server.broadcast_packet(
|
&CBlockUpdate::new(&location, block_state_id.get_id_mojang_repr().into()),
|
||||||
self,
|
);
|
||||||
&CBlockUpdate::new(
|
server.broadcast_packet(
|
||||||
&WorldPosition(location.0 + face.to_offset()),
|
self,
|
||||||
block_state_id.get_id_mojang_repr().into(),
|
&CBlockUpdate::new(
|
||||||
),
|
&WorldPosition(location.0 + face.to_offset()),
|
||||||
);
|
block_state_id.get_id_mojang_repr().into(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
self.client
|
||||||
|
.send_packet(&CAcknowledgeBlockChange::new(use_item_on.sequence));
|
||||||
|
} else {
|
||||||
|
self.kick(TextComponent::text("Invalid block face"))
|
||||||
}
|
}
|
||||||
self.client
|
|
||||||
.send_packet(&CAcknowledgeBlockChange::new(use_item_on.sequence));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn handle_use_item(&mut self, _server: &mut Server, _use_item: SUseItem) {
|
pub fn handle_use_item(&mut self, _server: &mut Server, _use_item: SUseItem) {
|
||||||
|
|||||||
@ -16,10 +16,9 @@ pub fn consume_arg_player(src: &CommandSender, args: &mut RawArgs) -> Option<Str
|
|||||||
_ => {
|
_ => {
|
||||||
// todo: implement any other player than sender
|
// todo: implement any other player than sender
|
||||||
if let Player(player) = src {
|
if let Player(player) = src {
|
||||||
if let Some(profile) = &player.client.gameprofile {
|
let profile = &player.gameprofile;
|
||||||
if profile.name == s {
|
if profile.name == s {
|
||||||
return Some(s.into());
|
return Some(s.into());
|
||||||
};
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
None
|
None
|
||||||
@ -46,10 +45,9 @@ pub fn parse_arg_player<'a>(
|
|||||||
_ => {
|
_ => {
|
||||||
// todo: implement any other player than sender
|
// todo: implement any other player than sender
|
||||||
if let Player(player) = src {
|
if let Player(player) = src {
|
||||||
if let Some(profile) = &player.client.gameprofile {
|
let profile = &player.gameprofile;
|
||||||
if profile.name == s {
|
if profile.name == s {
|
||||||
return Ok(player);
|
return Ok(player);
|
||||||
};
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
Err(InvalidConsumptionError(Some(s.into())))
|
Err(InvalidConsumptionError(Some(s.into())))
|
||||||
|
|||||||
@ -23,7 +23,11 @@ use pumpkin_protocol::{
|
|||||||
use pumpkin_world::vector3::Vector3;
|
use pumpkin_world::vector3::Vector3;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::{client::Client, server::Server, util::boundingbox::BoundingBox};
|
use crate::{
|
||||||
|
client::{authentication::GameProfile, Client},
|
||||||
|
server::Server,
|
||||||
|
util::boundingbox::BoundingBox,
|
||||||
|
};
|
||||||
|
|
||||||
pub struct PlayerAbilities {
|
pub struct PlayerAbilities {
|
||||||
pub invulnerable: bool,
|
pub invulnerable: bool,
|
||||||
@ -48,6 +52,7 @@ impl Default for PlayerAbilities {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub struct Player {
|
pub struct Player {
|
||||||
|
pub gameprofile: GameProfile,
|
||||||
pub client: Client,
|
pub client: Client,
|
||||||
pub entity: Entity,
|
pub entity: Entity,
|
||||||
// current gamemode
|
// current gamemode
|
||||||
@ -79,7 +84,21 @@ pub struct Player {
|
|||||||
|
|
||||||
impl Player {
|
impl Player {
|
||||||
pub fn new(client: Client, entity_id: EntityId, gamemode: GameMode) -> Self {
|
pub fn new(client: Client, entity_id: EntityId, gamemode: GameMode) -> Self {
|
||||||
|
let gameprofile = match client.gameprofile.clone() {
|
||||||
|
Some(profile) => profile,
|
||||||
|
None => {
|
||||||
|
log::error!("No gameprofile?. Impossible");
|
||||||
|
GameProfile {
|
||||||
|
id: uuid::Uuid::new_v4(),
|
||||||
|
name: "".to_string(),
|
||||||
|
properties: vec![],
|
||||||
|
profile_actions: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
|
gameprofile,
|
||||||
client,
|
client,
|
||||||
entity: Entity::new(entity_id, EntityType::Player, 1.62),
|
entity: Entity::new(entity_id, EntityType::Player, 1.62),
|
||||||
on_ground: false,
|
on_ground: false,
|
||||||
|
|||||||
@ -143,7 +143,7 @@ impl Server {
|
|||||||
// despawn the player
|
// despawn the player
|
||||||
// todo: put this into the entitiy struct
|
// todo: put this into the entitiy struct
|
||||||
let id = player.entity_id();
|
let id = player.entity_id();
|
||||||
let uuid = player.client.gameprofile.as_ref().unwrap().id;
|
let uuid = player.gameprofile.id;
|
||||||
self.broadcast_packet_except(
|
self.broadcast_packet_except(
|
||||||
&[&player.client.token],
|
&[&player.client.token],
|
||||||
&CRemovePlayerInfo::new(1.into(), &[UUID(uuid)]),
|
&CRemovePlayerInfo::new(1.into(), &[UUID(uuid)]),
|
||||||
@ -194,7 +194,7 @@ impl Server {
|
|||||||
let yaw = 10.0;
|
let yaw = 10.0;
|
||||||
let pitch = 10.0;
|
let pitch = 10.0;
|
||||||
player.teleport(x, y, z, 10.0, 10.0);
|
player.teleport(x, y, z, 10.0, 10.0);
|
||||||
let gameprofile = player.client.gameprofile.as_ref().unwrap();
|
let gameprofile = &player.gameprofile;
|
||||||
// first send info update to our new player, So he can see his Skin
|
// first send info update to our new player, So he can see his Skin
|
||||||
// also send his info to everyone else
|
// also send his info to everyone else
|
||||||
self.broadcast_packet(
|
self.broadcast_packet(
|
||||||
@ -222,7 +222,7 @@ impl Server {
|
|||||||
.filter(|c| c.0 != &player.client.token)
|
.filter(|c| c.0 != &player.client.token)
|
||||||
{
|
{
|
||||||
let playerr = playerr.as_ref().lock().unwrap();
|
let playerr = playerr.as_ref().lock().unwrap();
|
||||||
let gameprofile = &playerr.client.gameprofile.as_ref().unwrap();
|
let gameprofile = &playerr.gameprofile;
|
||||||
entries.push(pumpkin_protocol::client::play::Player {
|
entries.push(pumpkin_protocol::client::play::Player {
|
||||||
uuid: gameprofile.id,
|
uuid: gameprofile.id,
|
||||||
actions: vec![
|
actions: vec![
|
||||||
@ -241,7 +241,7 @@ impl Server {
|
|||||||
// Start waiting for level chunks
|
// Start waiting for level chunks
|
||||||
player.client.send_packet(&CGameEvent::new(13, 0.0));
|
player.client.send_packet(&CGameEvent::new(13, 0.0));
|
||||||
|
|
||||||
let gameprofile = player.client.gameprofile.as_ref().unwrap();
|
let gameprofile = &player.gameprofile;
|
||||||
|
|
||||||
// spawn player for every client
|
// spawn player for every client
|
||||||
self.broadcast_packet_except(
|
self.broadcast_packet_except(
|
||||||
@ -268,7 +268,7 @@ impl Server {
|
|||||||
for (_, existing_player) in self.current_players.iter().filter(|c| c.0 != &token) {
|
for (_, existing_player) in self.current_players.iter().filter(|c| c.0 != &token) {
|
||||||
let existing_player = existing_player.as_ref().lock().unwrap();
|
let existing_player = existing_player.as_ref().lock().unwrap();
|
||||||
let entity = &existing_player.entity;
|
let entity = &existing_player.entity;
|
||||||
let gameprofile = existing_player.client.gameprofile.as_ref().unwrap();
|
let gameprofile = &existing_player.gameprofile;
|
||||||
player.client.send_packet(&CSpawnEntity::new(
|
player.client.send_packet(&CSpawnEntity::new(
|
||||||
existing_player.entity_id().into(),
|
existing_player.entity_id().into(),
|
||||||
UUID(gameprofile.id),
|
UUID(gameprofile.id),
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user