use std::{mem::ManuallyDrop, ops::Range};
use anyhow::bail;
use graphite_mc_constants::{
block::Block,
entity::{Metadata, PlayerMetadata},
item::Item,
};
use graphite_net::{
network_buffer::WriteBuffer,
packet_helper::{self, PacketReadResult},
};
use parry3d::{
bounding_volume::AABB,
math::{Point, Real, Vector},
query::Ray,
};
use graphite_mc_protocol::{
play::{
client::PacketHandler,
server::{
self, AddPlayer, BlockChangedAck, BlockDestruction, LevelEvent, LevelEventType,
MoveEntityPosRot, PlayerInfo, PlayerInfoAddPlayer, RemoveEntities, RotateHead,
SetEquipment, TeleportEntity,
},
},
types::{BlockPosition, EquipmentSlot, GameProfile, Hand, Pose},
};
use queues::Buffer;
use rand::RngCore;
use graphite_sticky::Unsticky;
use graphite_text_component::TextComponent;
use crate::{
entity::position::{Coordinate, Position, Vec3f},
gamemode::Abilities,
inventory::inventory_handler::{InventoryHandler, InventorySlot, ItemSlot},
universe::{EntityId, UniverseService},
world::{
ChunkViewPosition, TickPhase, TickPhaseInner, World, WorldService, block_update,
},
};
use super::{
interaction::{Interaction, InteractionState},
packet_buffer::PacketBuffer,
player_connection::AbstractConnectionReference,
player_settings::PlayerSettings,
proto_player::ProtoPlayer,
};
pub trait PlayerService
where
Self: Sized + 'static,
{
const FAST_PACKET_RESPONSE: bool = true;
type UniverseServiceType: UniverseService;
type WorldServiceType: WorldService<UniverseServiceType = Self::UniverseServiceType>;
type InventoryHandlerType: InventoryHandler;
}
#[allow(type_alias_bounds)] type ConnectionReferenceType<P: PlayerService> =
<P::UniverseServiceType as UniverseService>::ConnectionReferenceType;
pub struct Player<P: PlayerService> {
world: *mut World<P::WorldServiceType>,
pub packets: PacketBuffer,
pub(crate) disconnected: bool,
pub entity_id: EntityId,
pub abilities: Abilities,
pub metadata: PlayerMetadata,
pub inventory: P::InventoryHandlerType,
pub settings: PlayerSettings,
pub profile: GameProfile,
last_position: Position, synced_coord: Coordinate, pub(crate) client_position: Position,
pub position: Position,
pub on_ground: bool,
pub selected_hotbar_slot: u8,
last_selected_hotbar_slot: u8,
viewable_exclusion_range: Range<usize>,
pub(crate) chunk_view_position: ChunkViewPosition,
pub(crate) new_chunk_view_position: ChunkViewPosition,
pub(crate) chunk_ref: usize,
pub(crate) teleport_id_timer: u8,
pub(crate) waiting_teleportation_id: Buffer<i32>,
pub(crate) ack_sequence_up_to: Option<i32>,
pub(crate) interaction_state: InteractionState,
pub(crate) current_keep_alive: u64,
keep_alive_timer: u8,
moved_into_proto: bool,
connection: ManuallyDrop<ConnectionReferenceType<P>>,
pub service: ManuallyDrop<P>,
}
impl<P: PlayerService> Player<P> {
pub(crate) fn new(
service: P,
world: &mut World<P::WorldServiceType>,
position: Position,
view_position: ChunkViewPosition,
proto_player: ProtoPlayer<P::UniverseServiceType>,
) -> Self {
Self {
world,
packets: PacketBuffer::new(),
disconnected: false,
entity_id: proto_player.entity_id,
abilities: proto_player.abilities,
inventory: Default::default(),
settings: PlayerSettings::new(),
profile: proto_player.profile,
metadata: Default::default(),
last_position: position,
synced_coord: position.coord,
client_position: position,
position,
on_ground: false,
selected_hotbar_slot: 0,
last_selected_hotbar_slot: 0,
viewable_exclusion_range: 0..0,
chunk_view_position: view_position,
new_chunk_view_position: view_position,
chunk_ref: usize::MAX,
teleport_id_timer: 0,
waiting_teleportation_id: Buffer::new(20),
ack_sequence_up_to: None,
interaction_state: Default::default(),
current_keep_alive: 0,
keep_alive_timer: 0,
moved_into_proto: false,
connection: ManuallyDrop::new(proto_player.connection),
service: ManuallyDrop::new(service),
}
}
pub fn get_world<'a, 'b>(&'a self) -> &'b World<P::WorldServiceType> {
unsafe { self.world.as_ref().unwrap() }
}
pub fn get_world_mut<'a, 'b>(&'a mut self) -> &'b mut World<P::WorldServiceType> {
unsafe { self.world.as_mut().unwrap() }
}
pub(crate) fn tick(&mut self, tick_phase: TickPhase) -> anyhow::Result<()> {
if self.disconnected {
bail!("player has been disconnected");
}
if tick_phase.0 == TickPhaseInner::View {
let chunk_x = self.chunk_view_position.x as i32;
let chunk_z = self.chunk_view_position.z as i32;
let chunks = &self.get_world().chunks;
let view_distance = P::WorldServiceType::ENTITY_VIEW_DISTANCE as i32;
for x in (chunk_x - view_distance).max(0)
..(chunk_x + view_distance + 1).min(chunks.get_size_x() as _)
{
for z in (chunk_z - view_distance).max(0)
..(chunk_z + view_distance + 1).min(chunks.get_size_z() as _)
{
let chunk = chunks.get(x as usize, z as usize).expect("chunk coords in bounds");
let bytes = chunk.entity_viewable_buffer.get_written();
if x == chunk_x && z == chunk_z {
self.packets
.write_raw_packets(&bytes[..self.viewable_exclusion_range.start]);
self.packets
.write_raw_packets(&bytes[self.viewable_exclusion_range.end..]);
self.viewable_exclusion_range = 0..0;
} else {
self.packets.write_raw_packets(bytes);
}
}
}
let view_distance = P::WorldServiceType::CHUNK_VIEW_DISTANCE as i32;
for x in (chunk_x - view_distance).max(0)
..(chunk_x + view_distance + 1).min(chunks.get_size_x() as _)
{
for z in (chunk_z - view_distance).max(0)
..(chunk_z + view_distance + 1).min(chunks.get_size_z() as _)
{
let chunk = self.get_world().chunks.get(x as usize, z as usize).expect("chunk coords in bounds");
self.packets
.write_raw_packets(chunk.block_viewable_buffer.get_written());
}
}
self.chunk_view_position = self.new_chunk_view_position;
if !self.packets.write_buffer.is_empty() {
self.connection
.write_bytes(self.packets.write_buffer.get_written());
self.packets.write_buffer.reset();
}
self.packets.write_buffer.tick_and_maybe_shrink();
return Ok(());
}
self.update_client_synchronization()?;
let selected_hotbar_slot_changed =
self.last_selected_hotbar_slot != self.selected_hotbar_slot;
if selected_hotbar_slot_changed {
self.last_selected_hotbar_slot = self.selected_hotbar_slot;
if self.interaction_state.using_hand == Some(Hand::Main) {
let interaction = self.interaction_state.try_abort_use(false).unwrap();
self.fire_interaction(interaction);
}
}
self.update_pose()?;
self.update_interaction_state()?;
self.update_equipment(selected_hotbar_slot_changed);
self.inventory
.write_changes(&mut self.packets.write_buffer)?;
Abilities::write_changes(self);
self.update_metadata()?;
if self.position != self.last_position {
self.position.rot.fix();
self.handle_movement(self.position, true)?;
} else {
self.client_position.rot.fix();
self.handle_movement(self.client_position, false)?;
}
if !self.packets.viewable_self_exclusion_write_buffer.is_empty() {
let chunk = self.get_world_mut().chunks
.get_mut(self.chunk_view_position.x, self.chunk_view_position.z)
.expect("chunk coords in bounds");
let write_to = &mut chunk.entity_viewable_buffer;
let start = write_to.len();
write_to.copy_from(
self.packets
.viewable_self_exclusion_write_buffer
.get_written(),
);
let end = write_to.len();
self.viewable_exclusion_range = start..end;
self.packets.viewable_self_exclusion_write_buffer.reset();
}
self.packets
.viewable_self_exclusion_write_buffer
.tick_and_maybe_shrink();
Ok(())
}
fn update_client_synchronization(&mut self) -> anyhow::Result<()> {
if self.teleport_id_timer > 0 {
self.teleport_id_timer += 1;
if self.teleport_id_timer >= 20 {
bail!("player sent incorrect teleport id and failed to rectify within time limit");
}
}
self.keep_alive_timer = self.keep_alive_timer.wrapping_add(1);
if self.keep_alive_timer == 0 {
if self.current_keep_alive != 0 {
bail!("client hasn't responded with keep alive")
}
self.current_keep_alive = rand::thread_rng().next_u64();
self.packets.write_packet(&server::KeepAlive {
id: self.current_keep_alive,
});
}
match self.ack_sequence_up_to {
Some(sequence) => {
self.packets.write_packet(&BlockChangedAck { sequence });
self.ack_sequence_up_to = None;
}
None => (),
}
Ok(())
}
fn update_metadata(&mut self) -> anyhow::Result<()> {
let write_size = self.metadata.get_write_size();
Ok(if write_size > 0 {
let chunk = self.get_world_mut().chunks
.get_mut(self.chunk_view_position.x, self.chunk_view_position.z)
.expect("chunk coords in bounds");
packet_helper::write_metadata_packet(
&mut chunk.entity_viewable_buffer,
server::PacketId::SetEntityData as _,
self.entity_id.as_i32(),
&mut self.metadata,
)?;
})
}
fn update_equipment(&mut self, selected_hotbar_slot_changed: bool) {
let mut equipment_changes = vec![];
if selected_hotbar_slot_changed {
let slot = InventorySlot::Hotbar(self.selected_hotbar_slot as _);
equipment_changes.push((EquipmentSlot::MainHand, slot));
}
if self.inventory.is_any_changed() {
let slot = InventorySlot::Hotbar(self.selected_hotbar_slot as _);
if self.inventory.has_changed(slot).unwrap() {
if self.interaction_state.using_hand == Some(Hand::Main) {
let interaction = self.interaction_state.try_abort_use(false).unwrap();
self.fire_interaction(interaction);
}
if !selected_hotbar_slot_changed {
equipment_changes.push((EquipmentSlot::MainHand, slot));
}
}
let mut write_equipment_changes =
|inventory: InventorySlot, equipment: EquipmentSlot| {
if self.inventory.has_changed(inventory).unwrap() {
equipment_changes.push((equipment, inventory));
true
} else {
false
}
};
write_equipment_changes(InventorySlot::Feet, EquipmentSlot::Feet);
write_equipment_changes(InventorySlot::Legs, EquipmentSlot::Legs);
write_equipment_changes(InventorySlot::Chest, EquipmentSlot::Chest);
write_equipment_changes(InventorySlot::Head, EquipmentSlot::Head);
if write_equipment_changes(InventorySlot::OffHand, EquipmentSlot::OffHand) {
if self.interaction_state.using_hand == Some(Hand::Off) {
let interaction = self.interaction_state.try_abort_use(false).unwrap();
self.fire_interaction(interaction);
}
}
}
if !equipment_changes.is_empty() {
let mut equipment = Vec::new();
for (equipment_slot, inventory) in equipment_changes {
let itemslot = self.inventory.get(inventory).unwrap();
equipment.push((equipment_slot, itemslot.into()));
}
self.packets.write_self_excluded_viewable_packet(
&SetEquipment {
entity_id: self.entity_id.as_i32(),
equipment,
},
);
}
}
fn update_pose(&mut self) -> anyhow::Result<()> {
let mut new_pose;
if self.is_fall_flying() {
new_pose = Pose::FallFlying;
} else if self.metadata.sleeping_pos.is_some() {
new_pose = Pose::Sleeping;
} else if self.is_swimming() {
new_pose = Pose::Swimming;
} else if self.is_spin_attacking() {
new_pose = Pose::SpinAttack;
} else if self.is_shift_key_down() && !self.abilities.is_flying {
new_pose = Pose::Sneaking;
} else {
new_pose = Pose::Standing;
}
if !self.can_enter_pose(new_pose) {
if self.can_enter_pose(Pose::Sneaking) {
new_pose = Pose::Sneaking;
} else {
new_pose = Pose::Swimming;
}
}
if self.metadata.pose != new_pose {
self.metadata.set_pose(new_pose);
}
Ok(())
}
pub fn can_enter_pose(&self, _pose: Pose) -> bool {
true }
fn update_interaction_state(&mut self) -> anyhow::Result<()> {
if let Some(hand) = self.interaction_state.get_used_hand() {
let inventory_slot = match hand {
Hand::Main => InventorySlot::Hotbar(self.selected_hotbar_slot as _),
Hand::Off => InventorySlot::OffHand,
};
let slot = self.inventory.get(inventory_slot)?;
match slot {
ItemSlot::Empty => (),
ItemSlot::Filled(item) => {
if item.properties.use_duration > 0 {
self.interaction_state
.start_using(item.properties.use_duration as _, hand);
}
}
}
}
Ok(for interaction in self.interaction_state.update() {
self.fire_interaction(interaction);
})
}
fn handle_movement(&mut self, to: Position, inform_client: bool) -> anyhow::Result<()> {
let distance_sq = to.distance_sq(self.last_position);
let rot_changed = to.rot.is_diff_u8(self.last_position.rot);
let coord_changed = distance_sq > 0.0001;
if coord_changed {
if distance_sq < 8.0 * 8.0 {
let delta_x = to.coord.x - self.synced_coord.x;
let delta_y = to.coord.y - self.synced_coord.y;
let delta_z = to.coord.z - self.synced_coord.z;
let quantized_x = (delta_x * 4096.0) as i16;
let quantized_y = (delta_y * 4096.0) as i16;
let quantized_z = (delta_z * 4096.0) as i16;
self.synced_coord.x += quantized_x as f32 / 4096.0;
self.synced_coord.y += quantized_y as f32 / 4096.0;
self.synced_coord.z += quantized_z as f32 / 4096.0;
if rot_changed {
let move_packet = MoveEntityPosRot {
entity_id: self.entity_id.as_i32(),
delta_x: quantized_x,
delta_y: quantized_y,
delta_z: quantized_z,
yaw: to.rot.yaw,
pitch: to.rot.pitch,
on_ground: self.on_ground,
};
self.packets.write_self_excluded_viewable_packet(&move_packet);
let rotate_head = RotateHead {
entity_id: self.entity_id.as_i32(),
head_yaw: to.rot.yaw,
};
self.packets.write_self_excluded_viewable_packet(&rotate_head);
} else {
let move_packet = MoveEntityPosRot {
entity_id: self.entity_id.as_i32(),
delta_x: quantized_x,
delta_y: quantized_y,
delta_z: quantized_z,
yaw: to.rot.yaw,
pitch: to.rot.pitch,
on_ground: self.on_ground,
};
self.packets.write_self_excluded_viewable_packet(&move_packet);
}
} else {
self.synced_coord = to.coord;
let teleport_packet = TeleportEntity {
entity_id: self.entity_id.as_i32(),
x: to.coord.x as _,
y: to.coord.y as _,
z: to.coord.z as _,
yaw: to.rot.yaw,
pitch: to.rot.pitch,
on_ground: self.on_ground,
};
self.packets.write_self_excluded_viewable_packet(&teleport_packet);
if rot_changed {
let rotate_head = RotateHead {
entity_id: self.entity_id.as_i32(),
head_yaw: to.rot.yaw,
};
self.packets.write_self_excluded_viewable_packet(&rotate_head);
}
}
self.get_world_mut().update_view_position(self, to)?;
} else if rot_changed {
let teleport_packet = TeleportEntity {
entity_id: self.entity_id.as_i32(),
x: to.coord.x as _,
y: to.coord.y as _,
z: to.coord.z as _,
yaw: to.rot.yaw,
pitch: to.rot.pitch,
on_ground: self.on_ground,
};
self.packets.write_self_excluded_viewable_packet(&teleport_packet);
let rotate_head = RotateHead {
entity_id: self.entity_id.as_i32(),
head_yaw: to.rot.yaw,
};
self.packets.write_self_excluded_viewable_packet(&rotate_head);
} else {
return Ok(());
}
if inform_client {
let teleport_packet = TeleportEntity {
entity_id: self.entity_id.as_i32(),
x: to.coord.x as _,
y: to.coord.y as _,
z: to.coord.z as _,
yaw: to.rot.yaw,
pitch: to.rot.pitch,
on_ground: self.on_ground,
};
self.packets.write_packet(&teleport_packet);
}
self.position = to;
self.last_position = to;
self.client_position = to;
Ok(())
}
pub fn clip_block_position(&self, position: BlockPosition) -> Option<(f32, f32)> {
let aabb = AABB::new(
Point::new(position.x as f32, position.y as f32, position.z as f32),
Point::new(
position.x as f32 + 1.0,
position.y as f32 + 1.0,
position.z as f32 + 1.0,
),
);
aabb.clip_ray_parameters(&self.get_look_ray())
}
pub fn get_look_ray(&self) -> Ray {
Ray::new(
Point::new(
self.client_position.coord.x,
self.client_position.coord.y + self.get_eye_height(),
self.client_position.coord.z,
),
self.get_look_vector(),
)
}
pub fn get_eye_height(&self) -> f32 {
match self.metadata.pose {
Pose::Sleeping => 0.2,
Pose::Swimming | Pose::FallFlying | Pose::SpinAttack => 0.4,
Pose::Sneaking => 1.27,
_ => 1.62,
}
}
pub fn get_look_vector(&self) -> Vector<Real> {
let pitch_rad = self.client_position.rot.pitch.to_radians();
let yaw_rad = -self.client_position.rot.yaw.to_radians();
let (pitch_sin, pitch_cos) = pitch_rad.sin_cos();
let (yaw_sin, yaw_cos) = yaw_rad.sin_cos();
Vector::new(yaw_sin * pitch_cos, -pitch_sin, yaw_cos * pitch_cos)
}
fn break_block(&mut self, pos: BlockPosition) {
if let Some(old) = self
.get_world_mut()
.set_block_i32(pos.x as _, pos.y as _, pos.z as _, 0)
{
self.packets.write_self_excluded_viewable_packet(
&LevelEvent {
event_type: LevelEventType::ParticlesDestroyBlock,
pos,
data: old as _,
global: false,
},
);
for offset in [(1, 0, 0), (0, 1, 0), (0, 0, 1), (-1, 0, 0), (0, -1, 0), (0, 0, -1)] {
let x = pos.x + offset.0;
let y = pos.y + offset.1;
let z = pos.z + offset.2;
let block_state_id = self.get_world().get_block_i32(x, y, z);
if let Some(block_state_id) = block_state_id {
let block: &Block = block_state_id.try_into().unwrap();
let mut block = block.clone();
if block_update::update(block_state_id, &mut block, x, y, z, self.get_world_mut()) {
self.get_world_mut().set_block_i32(x, y, z, (&block).into());
}
}
}
}
}
pub fn do_default_interaction(&mut self, interaction: Interaction) {
match interaction {
Interaction::LeftClickBlock {
position,
face: _,
instabreak,
} => {
if instabreak {
self.break_block(position);
}
}
Interaction::LeftClickEntity { entity_id: _ } => {
}
Interaction::LeftClickAir => {}
Interaction::RightClickBlock {
position,
face,
offset,
} => {
let slot = InventorySlot::Hotbar(self.selected_hotbar_slot as _);
let held_item = self
.inventory
.get(slot)
.expect("self.selected_hotbar_slot between 0..9");
if let ItemSlot::Filled(itemstack) = held_item {
let item = itemstack.item;
let world = self.get_world_mut();
if item.get_properties().has_corresponding_block {
let ctx_and_pos = world.create_placement_context(
position,
face,
offset,
self.position.rot,
item
);
if let Some(ctx_and_pos) = ctx_and_pos {
let mut ctx = ctx_and_pos.0;
let place_position = ctx_and_pos.1;
if let Some(block) = item.try_place(&mut ctx) {
let block_id: u16 = block.to_id();
world.set_block_i32(place_position.x, place_position.y, place_position.z, block_id);
for offset in [(1, 0, 0), (0, 1, 0), (0, 0, 1), (-1, 0, 0), (0, -1, 0), (0, 0, -1)] {
let x = place_position.x + offset.0;
let y = place_position.y + offset.1;
let z = place_position.z + offset.2;
let block_state_id = self.get_world().get_block_i32(x, y, z);
if let Some(block_state_id) = block_state_id {
let block: &Block = block_state_id.try_into().unwrap();
let mut block = block.clone();
if block_update::update(block_state_id, &mut block, x, y, z, self.get_world_mut()) {
self.get_world_mut().set_block_i32(x, y, z, (&block).into());
}
}
}
}
}
} else if item == Item::WaterBucket {
let relative = position.relative(face);
world.set_block_i32(
relative.x,
relative.y as _,
relative.z,
Block::Water { level: 0 }.to_id(),
);
}
}
}
Interaction::RightClickEntity {
entity_id: _,
offset: _,
} => {
}
Interaction::RightClickAir { hand: _ } => {}
Interaction::ContinuousBreak {
position,
break_time,
distance: _,
} => {
if let Some(destroy_stage) = self.get_world().get_destroy_stage(
position.x,
position.y as _,
position.z,
break_time,
self.get_break_speed_multiplier(),
) {
self.packets.write_self_excluded_viewable_packet(
&BlockDestruction {
entity_id: self.entity_id.as_i32(),
location: position,
destroy_stage,
}
);
}
}
Interaction::FinishBreak {
position,
break_time: _,
distance: _,
} => {
self.break_block(position);
}
Interaction::AbortBreak {
position,
break_time: _,
} => {
self.packets.write_self_excluded_viewable_packet(
&BlockDestruction {
entity_id: self.entity_id.as_i32(),
location: position,
destroy_stage: -1,
},
);
}
Interaction::ContinuousUse { use_time, hand } => {
if use_time == 1 {
self.set_using_item(true);
self.set_using_offhand(hand == Hand::Off);
}
}
Interaction::FinishUse {
use_time: _,
hand: _,
} => {
self.set_using_item(false);
}
Interaction::AbortUse {
use_time: _,
hand: _,
aborted_by_client: _,
} => {
self.set_using_item(false);
}
}
}
pub fn get_break_speed_multiplier(&self) -> f32 {
let speed_multiplier = 1.0;
let correct_tool_multiplier = 100.0;
correct_tool_multiplier * speed_multiplier
}
pub fn send_message<T: Into<TextComponent>>(&mut self, message: T) {
self.packets.write_packet(&server::SystemChat {
message: message.into().to_json(),
overlay: false,
})
}
pub fn disconnect(&mut self) {
self.disconnected = true;
}
pub(crate) fn fire_interaction(&mut self, interaction: Interaction) {
self.do_default_interaction(interaction);
}
pub(crate) fn write_destroy_packet(&mut self, write_buffer: &mut WriteBuffer) {
let remove_entity_packet = RemoveEntities {
entities: vec![self.entity_id.as_i32()],
};
graphite_net::packet_helper::write_packet(write_buffer, &remove_entity_packet).unwrap();
let remove_info_packet = PlayerInfo::RemovePlayer {
uuids: vec![self.profile.uuid],
};
graphite_net::packet_helper::write_packet(write_buffer, &remove_info_packet).unwrap();
}
pub(crate) fn write_create_packet(&mut self, write_buffer: &mut WriteBuffer) {
let packet = PlayerInfo::AddPlayer {
values: vec![PlayerInfoAddPlayer {
profile: self.profile.clone(),
gamemode: self.abilities.gamemode as _, ping: 69, display_name: None,
signature_data: None,
}],
};
graphite_net::packet_helper::write_packet(write_buffer, &packet).unwrap();
let add_player_packet = AddPlayer {
id: self.entity_id.as_i32(),
uuid: self.profile.uuid,
x: self.position.coord.x as _,
y: self.position.coord.y as _,
z: self.position.coord.z as _,
yaw: self.position.rot.yaw,
pitch: self.position.rot.pitch,
};
graphite_net::packet_helper::write_packet(write_buffer, &add_player_packet).unwrap();
}
pub(crate) fn write_packet_bytes(&mut self, bytes: &[u8]) {
self.packets.write_buffer.copy_from(bytes);
}
pub fn handle_packets(&mut self) -> anyhow::Result<u32> {
let mut bytes = unsafe { &*(self.connection.read_bytes() as *const _) };
loop {
let packet_read_result = graphite_net::packet_helper::try_read_packet(&mut bytes)?;
match packet_read_result {
PacketReadResult::Complete(bytes) => {
self.parse_and_handle(bytes)?;
}
PacketReadResult::Partial => break,
PacketReadResult::Empty => break,
}
}
if P::FAST_PACKET_RESPONSE {
let to_write = self.packets.write_buffer.get_written();
if !to_write.is_empty() {
self.connection.write_bytes(to_write);
}
self.packets.write_buffer.reset();
}
Ok(bytes.len() as u32)
}
pub fn handle_disconnect(&mut self) {
unsafe {
self.connection.forget();
self.disconnect();
}
}
}
impl<P: PlayerService> Drop for Player<P> {
fn drop(&mut self) {
if !std::thread::panicking() {
let chunks = &mut self.get_world_mut().chunks;
if let Some(old_chunk) = chunks.get_mut(self.chunk_view_position.x, self.chunk_view_position.z) {
old_chunk.destroy_player(self);
}
if !self.moved_into_proto {
unsafe {
ManuallyDrop::drop(&mut self.connection);
ManuallyDrop::drop(&mut self.service);
}
}
}
}
}
unsafe impl<P: PlayerService> Unsticky for Player<P> {
type UnstuckType = (ProtoPlayer<P::UniverseServiceType>, P);
fn update_pointer(&mut self, _: usize) {
let ptr: *mut Player<P> = self;
unsafe { self.connection.update_player_pointer(ptr); }
let chunk = self.get_world_mut().chunks
.get_mut(self.chunk_view_position.x, self.chunk_view_position.z)
.expect("chunk coords in bounds");
if self.chunk_ref == usize::MAX {
chunk.create_player(self);
} else {
chunk.update_player_pointer(self);
}
}
fn unstick(mut self) -> Self::UnstuckType {
self.moved_into_proto = true;
self.connection.clear_player_pointer();
let connection = unsafe { ManuallyDrop::take(&mut self.connection) };
let service = unsafe { ManuallyDrop::take(&mut self.service) };
(
ProtoPlayer::new(connection, self.profile.clone(), self.entity_id),
service,
)
}
}