mirror of
https://github.com/italicsjenga/valence.git
synced 2024-12-23 14:31:30 +11:00
Add more client events
This commit is contained in:
parent
a58258e8d3
commit
d7d922399a
18
src/chunk.rs
18
src/chunk.rs
|
@ -55,7 +55,7 @@ impl Chunks {
|
|||
|
||||
pub fn get_block_state(&self, pos: impl Into<BlockPos>) -> Option<BlockState> {
|
||||
let pos = pos.into();
|
||||
let chunk_pos = ChunkPos::new(pos.x / 16, pos.z / 16);
|
||||
let chunk_pos = ChunkPos::from(pos);
|
||||
|
||||
let chunk = self.get(chunk_pos)?;
|
||||
|
||||
|
@ -80,6 +80,10 @@ impl<'a> ChunksMut<'a> {
|
|||
Self(chunks)
|
||||
}
|
||||
|
||||
pub fn reborrow(&mut self) -> ChunksMut {
|
||||
ChunksMut(self.0)
|
||||
}
|
||||
|
||||
pub fn create(&mut self, pos: impl Into<ChunkPos>) -> bool {
|
||||
let section_count = (self.server.dimension(self.dimension).height / 16) as u32;
|
||||
let chunk = Chunk::new(section_count, self.server.current_tick());
|
||||
|
@ -110,7 +114,7 @@ impl<'a> ChunksMut<'a> {
|
|||
|
||||
pub fn set_block_state(&mut self, pos: impl Into<BlockPos>, block: BlockState) -> bool {
|
||||
let pos = pos.into();
|
||||
let chunk_pos = ChunkPos::new(pos.x / 16, pos.z / 16);
|
||||
let chunk_pos = ChunkPos::from(pos);
|
||||
|
||||
if let Some(chunk) = self.0.chunks.get_mut(&chunk_pos) {
|
||||
let min_y = self.0.server.dimension(self.0.dimension).min_y;
|
||||
|
@ -269,7 +273,7 @@ impl Chunk {
|
|||
|
||||
let chunk_section_position = (pos.x as i64) << 42
|
||||
| (pos.z as i64 & 0x3fffff) << 20
|
||||
| (sect_y as i64) & 0xfffff;
|
||||
| (sect_y as i64 + min_y.div_euclid(16) as i64) & 0xfffff;
|
||||
|
||||
packet(BlockChangePacket::Multi(MultiBlockChange {
|
||||
chunk_section_position,
|
||||
|
@ -401,7 +405,7 @@ impl ChunkPos {
|
|||
}
|
||||
|
||||
pub fn at(x: f64, z: f64) -> Self {
|
||||
Self::new((x / 16.0) as i32, (z / 16.0) as i32)
|
||||
Self::new((x / 16.0).floor() as i32, (z / 16.0).floor() as i32)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -429,6 +433,12 @@ impl Into<[i32; 2]> for ChunkPos {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<BlockPos> for ChunkPos {
|
||||
fn from(pos: BlockPos) -> Self {
|
||||
Self::new(pos.x.div_euclid(16), pos.z.div_euclid(16))
|
||||
}
|
||||
}
|
||||
|
||||
/// A 16x16x16 section of blocks, biomes, and light in a chunk.
|
||||
#[derive(Clone)]
|
||||
struct ChunkSection {
|
||||
|
|
441
src/client.rs
441
src/client.rs
|
@ -1,7 +1,10 @@
|
|||
/// Contains the [`Event`] enum and related data types.
|
||||
mod event;
|
||||
use std::collections::HashSet;
|
||||
use std::iter::FusedIterator;
|
||||
use std::ops::Deref;
|
||||
|
||||
pub use event::*;
|
||||
use flume::{Receiver, Sender, TrySendError};
|
||||
use rayon::iter::ParallelIterator;
|
||||
use uuid::Uuid;
|
||||
|
@ -12,16 +15,15 @@ use crate::block_pos::BlockPos;
|
|||
use crate::byte_angle::ByteAngle;
|
||||
use crate::dimension::{Dimension, DimensionEffects};
|
||||
use crate::entity::{velocity_to_packet_units, EntityType};
|
||||
use crate::packets::play::c2s::C2sPlayPacket;
|
||||
pub use crate::packets::play::s2c::GameMode;
|
||||
use crate::packets::play::c2s::{C2sPlayPacket, DiggingStatus};
|
||||
use crate::packets::play::s2c::{
|
||||
Biome as BiomeRegistryBiome, BiomeAdditionsSound, BiomeEffects, BiomeMoodSound, BiomeMusic,
|
||||
BiomeParticle, BiomeParticleOptions, BiomeProperty, BiomeRegistry, ChangeGameState,
|
||||
ChangeGameStateReason, ChatTypeRegistry, DestroyEntities, DimensionType, DimensionTypeRegistry,
|
||||
DimensionTypeRegistryEntry, Disconnect, EntityHeadLook, EntityPosition,
|
||||
EntityPositionAndRotation, EntityRotation, EntityTeleport, EntityVelocity, JoinGame,
|
||||
KeepAliveClientbound, PlayerPositionAndLook, PlayerPositionAndLookFlags, RegistryCodec,
|
||||
S2cPlayPacket, SpawnPosition, UnloadChunk, UpdateViewDistance, UpdateViewPosition,
|
||||
AcknowledgeBlockChanges, Biome as BiomeRegistryBiome, BiomeAdditionsSound, BiomeEffects,
|
||||
BiomeMoodSound, BiomeMusic, BiomeParticle, BiomeParticleOptions, BiomeProperty, BiomeRegistry,
|
||||
ChangeGameState, ChangeGameStateReason, ChatTypeRegistry, DestroyEntities, DimensionType,
|
||||
DimensionTypeRegistry, DimensionTypeRegistryEntry, Disconnect, EntityHeadLook, EntityPosition,
|
||||
EntityPositionAndRotation, EntityRotation, EntityTeleport, EntityVelocity, JoinGame, KeepAlive,
|
||||
PlayerPositionAndLook, PlayerPositionAndLookFlags, RegistryCodec, S2cPlayPacket, SpawnPosition,
|
||||
UnloadChunk, UpdateViewDistance, UpdateViewPosition,
|
||||
};
|
||||
use crate::protocol::{BoundedInt, Nbt};
|
||||
use crate::server::C2sPacketChannels;
|
||||
|
@ -156,6 +158,9 @@ pub struct Client {
|
|||
new_game_mode: GameMode,
|
||||
old_game_mode: GameMode,
|
||||
settings: Option<Settings>,
|
||||
dug_blocks: Vec<i32>,
|
||||
// /// The metadata for the client's own player entity.
|
||||
// player_meta: Player,
|
||||
}
|
||||
|
||||
pub struct ClientMut<'a>(&'a mut Client);
|
||||
|
@ -205,6 +210,7 @@ impl Client {
|
|||
new_game_mode: GameMode::Survival,
|
||||
old_game_mode: GameMode::Survival,
|
||||
settings: None,
|
||||
dug_blocks: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -258,7 +264,7 @@ impl Client {
|
|||
self.send.is_none()
|
||||
}
|
||||
|
||||
pub fn events(&self) -> &[Event] {
|
||||
pub fn events(&self) -> &Vec<Event> {
|
||||
&self.events
|
||||
}
|
||||
|
||||
|
@ -288,8 +294,13 @@ impl<'a> ClientMut<'a> {
|
|||
ClientMut(self.0)
|
||||
}
|
||||
|
||||
pub fn events_mut(&mut self) -> &mut Vec<Event> {
|
||||
&mut self.0.events
|
||||
}
|
||||
|
||||
pub fn teleport(&mut self, pos: impl Into<Vec3<f64>>, yaw: f32, pitch: f32) {
|
||||
self.0.new_position = pos.into();
|
||||
|
||||
self.0.yaw = yaw;
|
||||
self.0.pitch = pitch;
|
||||
|
||||
|
@ -363,6 +374,215 @@ impl<'a> ClientMut<'a> {
|
|||
self.0.new_max_view_distance = dist.clamp(2, 32);
|
||||
}
|
||||
|
||||
pub(crate) fn handle_serverbound_packets(&mut self, entities: &Entities) {
|
||||
self.0.events.clear();
|
||||
for _ in 0..self.recv.len() {
|
||||
self.handle_serverbound_packet(entities, self.recv.try_recv().unwrap());
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_serverbound_packet(&mut self, entities: &Entities, pkt: C2sPlayPacket) {
|
||||
let client = &mut self.0;
|
||||
|
||||
fn handle_movement_packet(
|
||||
client: &mut Client,
|
||||
_vehicle: bool,
|
||||
new_position: Vec3<f64>,
|
||||
new_yaw: f32,
|
||||
new_pitch: f32,
|
||||
new_on_ground: bool,
|
||||
) {
|
||||
if client.pending_teleports == 0 {
|
||||
// TODO: validate movement using swept AABB collision with the blocks.
|
||||
// TODO: validate that the client is actually inside/outside the vehicle?
|
||||
let event = Event::Movement {
|
||||
position: client.new_position,
|
||||
yaw: client.yaw,
|
||||
pitch: client.pitch,
|
||||
on_ground: client.on_ground,
|
||||
};
|
||||
|
||||
client.new_position = new_position;
|
||||
client.yaw = new_yaw;
|
||||
client.pitch = new_pitch;
|
||||
client.on_ground = new_on_ground;
|
||||
|
||||
client.events.push(event);
|
||||
}
|
||||
}
|
||||
|
||||
match pkt {
|
||||
C2sPlayPacket::TeleportConfirm(p) => {
|
||||
if client.pending_teleports == 0 {
|
||||
self.disconnect("Unexpected teleport confirmation");
|
||||
return;
|
||||
}
|
||||
|
||||
let got = p.teleport_id.0 as u32;
|
||||
let expected = client
|
||||
.teleport_id_counter
|
||||
.wrapping_sub(client.pending_teleports);
|
||||
|
||||
if got == expected {
|
||||
client.pending_teleports -= 1;
|
||||
} else {
|
||||
self.disconnect(format!(
|
||||
"Unexpected teleport ID (expected {expected}, got {got})"
|
||||
));
|
||||
}
|
||||
}
|
||||
C2sPlayPacket::QueryBlockNbt(_) => {}
|
||||
C2sPlayPacket::SetDifficulty(_) => {}
|
||||
C2sPlayPacket::ChatCommand(_) => {}
|
||||
C2sPlayPacket::ChatMessage(_) => {}
|
||||
C2sPlayPacket::ChatPreview(_) => {}
|
||||
C2sPlayPacket::ClientStatus(_) => {}
|
||||
C2sPlayPacket::ClientSettings(p) => {
|
||||
let old = client.settings.replace(Settings {
|
||||
locale: p.locale.0,
|
||||
view_distance: p.view_distance.0,
|
||||
chat_mode: p.chat_mode,
|
||||
chat_colors: p.chat_colors,
|
||||
main_hand: p.main_hand,
|
||||
displayed_skin_parts: p.displayed_skin_parts,
|
||||
allow_server_listings: p.allow_server_listings,
|
||||
});
|
||||
|
||||
client.events.push(Event::SettingsChanged(old));
|
||||
}
|
||||
C2sPlayPacket::TabComplete(_) => {}
|
||||
C2sPlayPacket::ClickWindowButton(_) => {}
|
||||
C2sPlayPacket::CloseWindow(_) => {}
|
||||
C2sPlayPacket::PluginMessage(_) => {}
|
||||
C2sPlayPacket::EditBook(_) => {}
|
||||
C2sPlayPacket::QueryEntityNbt(_) => {}
|
||||
C2sPlayPacket::InteractEntity(p) => {
|
||||
if let Some(id) = entities.get_with_network_id(p.entity_id.0) {
|
||||
// TODO: verify that the client has line of sight to the targeted entity and
|
||||
// that the distance is <=4 blocks.
|
||||
|
||||
use crate::packets::play::c2s::InteractType;
|
||||
client.events.push(Event::InteractWithEntity {
|
||||
id,
|
||||
sneaking: p.sneaking,
|
||||
typ: match p.typ {
|
||||
InteractType::Interact(hand) => InteractWithEntity::Interact(hand),
|
||||
InteractType::Attack => InteractWithEntity::Attack,
|
||||
InteractType::InteractAt((target, hand)) => {
|
||||
InteractWithEntity::InteractAt { target, hand }
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
C2sPlayPacket::GenerateStructure(_) => {}
|
||||
C2sPlayPacket::KeepAlive(p) => {
|
||||
let last_keepalive_id = client.last_keepalive_id;
|
||||
if client.got_keepalive {
|
||||
self.disconnect("Unexpected keepalive");
|
||||
} else if p.id != last_keepalive_id {
|
||||
self.disconnect(format!(
|
||||
"Keepalive ids don't match (expected {}, got {})",
|
||||
last_keepalive_id, p.id
|
||||
));
|
||||
} else {
|
||||
client.got_keepalive = true;
|
||||
}
|
||||
}
|
||||
C2sPlayPacket::LockDifficulty(_) => {}
|
||||
C2sPlayPacket::PlayerPosition(p) => handle_movement_packet(
|
||||
client,
|
||||
false,
|
||||
p.position,
|
||||
client.yaw,
|
||||
client.pitch,
|
||||
p.on_ground,
|
||||
),
|
||||
C2sPlayPacket::PlayerPositionAndRotation(p) => {
|
||||
handle_movement_packet(client, false, p.position, p.yaw, p.pitch, p.on_ground)
|
||||
}
|
||||
C2sPlayPacket::PlayerRotation(p) => handle_movement_packet(
|
||||
client,
|
||||
false,
|
||||
client.new_position,
|
||||
p.yaw,
|
||||
p.pitch,
|
||||
p.on_ground,
|
||||
),
|
||||
C2sPlayPacket::PlayerMovement(p) => handle_movement_packet(
|
||||
client,
|
||||
false,
|
||||
client.new_position,
|
||||
client.yaw,
|
||||
client.pitch,
|
||||
p.on_ground,
|
||||
),
|
||||
C2sPlayPacket::VehicleMove(p) => {
|
||||
handle_movement_packet(client, true, p.position, p.yaw, p.pitch, client.on_ground);
|
||||
}
|
||||
C2sPlayPacket::SteerBoat(p) => {
|
||||
client.events.push(Event::SteerBoat {
|
||||
left_paddle_turning: p.left_paddle_turning,
|
||||
right_paddle_turning: p.right_paddle_turning,
|
||||
});
|
||||
}
|
||||
C2sPlayPacket::PickItem(_) => {}
|
||||
C2sPlayPacket::CraftRecipeRequest(_) => {}
|
||||
C2sPlayPacket::PlayerAbilities(_) => {}
|
||||
C2sPlayPacket::PlayerDigging(p) => {
|
||||
// TODO: verify dug block is within the correct distance from the client.
|
||||
// TODO: verify that the broken block is allowed to be broken?
|
||||
|
||||
if p.sequence.0 != 0 {
|
||||
client.dug_blocks.push(p.sequence.0);
|
||||
}
|
||||
|
||||
client.events.push(match p.status {
|
||||
DiggingStatus::StartedDigging => Event::Digging(Digging {
|
||||
status: event::DiggingStatus::Start,
|
||||
position: p.location,
|
||||
face: p.face,
|
||||
}),
|
||||
DiggingStatus::CancelledDigging => Event::Digging(Digging {
|
||||
status: event::DiggingStatus::Cancel,
|
||||
position: p.location,
|
||||
face: p.face,
|
||||
}),
|
||||
DiggingStatus::FinishedDigging => Event::Digging(Digging {
|
||||
status: event::DiggingStatus::Finish,
|
||||
position: p.location,
|
||||
face: p.face,
|
||||
}),
|
||||
DiggingStatus::DropItemStack => return,
|
||||
DiggingStatus::DropItem => return,
|
||||
DiggingStatus::ShootArrowOrFinishEating => return,
|
||||
DiggingStatus::SwapItemInHand => return,
|
||||
});
|
||||
}
|
||||
C2sPlayPacket::EntityAction(_) => {}
|
||||
C2sPlayPacket::SteerVehicle(_) => {}
|
||||
C2sPlayPacket::Pong(_) => {}
|
||||
C2sPlayPacket::SetRecipeBookState(_) => {}
|
||||
C2sPlayPacket::SetDisplayedRecipe(_) => {}
|
||||
C2sPlayPacket::NameItem(_) => {}
|
||||
C2sPlayPacket::ResourcePackStatus(_) => {}
|
||||
C2sPlayPacket::AdvancementTab(_) => {}
|
||||
C2sPlayPacket::SelectTrade(_) => {}
|
||||
C2sPlayPacket::SetBeaconEffect(_) => {}
|
||||
C2sPlayPacket::HeldItemChange(_) => {}
|
||||
C2sPlayPacket::UpdateCommandBlock(_) => {}
|
||||
C2sPlayPacket::UpdateCommandBlockMinecart(_) => {}
|
||||
C2sPlayPacket::CreativeInventoryAction(_) => {}
|
||||
C2sPlayPacket::UpdateJigsawBlock(_) => {}
|
||||
C2sPlayPacket::UpdateStructureBlock(_) => {}
|
||||
C2sPlayPacket::UpdateSign(_) => {}
|
||||
C2sPlayPacket::PlayerArmSwing(_) => {}
|
||||
C2sPlayPacket::Spectate(_) => {}
|
||||
C2sPlayPacket::PlayerBlockPlacement(_) => {}
|
||||
C2sPlayPacket::UseItem(_) => {}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn update(
|
||||
&mut self,
|
||||
server: &Server,
|
||||
|
@ -371,18 +591,7 @@ impl<'a> ClientMut<'a> {
|
|||
chunks: &Chunks,
|
||||
meta: &WorldMeta,
|
||||
) {
|
||||
self.0.events.clear();
|
||||
|
||||
if self.is_disconnected() {
|
||||
return;
|
||||
}
|
||||
|
||||
for _ in 0..self.recv.len() {
|
||||
self.handle_serverbound_packet(self.recv.try_recv().unwrap());
|
||||
}
|
||||
|
||||
// Mark the client as disconnected when appropriate.
|
||||
// We do this check after handling serverbound packets so that none are lost.
|
||||
if self.recv.is_disconnected() || self.send.as_ref().map_or(true, |s| s.is_disconnected()) {
|
||||
self.0.send = None;
|
||||
return;
|
||||
|
@ -454,7 +663,7 @@ impl<'a> ClientMut<'a> {
|
|||
if current_tick % (server.tick_rate() * 8) == 0 {
|
||||
if self.0.got_keepalive {
|
||||
let id = rand::random();
|
||||
self.send_packet(KeepAliveClientbound { id });
|
||||
self.send_packet(KeepAlive { id });
|
||||
self.0.last_keepalive_id = id;
|
||||
self.0.got_keepalive = false;
|
||||
} else {
|
||||
|
@ -464,16 +673,13 @@ impl<'a> ClientMut<'a> {
|
|||
|
||||
let view_dist = self.view_distance();
|
||||
|
||||
let center = ChunkPos::new(
|
||||
(self.new_position.x / 16.0) as i32,
|
||||
(self.new_position.z / 16.0) as i32,
|
||||
);
|
||||
let center = ChunkPos::at(self.new_position.x, self.new_position.z);
|
||||
|
||||
// Send the update view position packet if the client changes the chunk section
|
||||
// they're in.
|
||||
{
|
||||
let old_section = self.0.old_position.map(|n| (n / 16.0) as i32);
|
||||
let new_section = self.0.new_position.map(|n| (n / 16.0) as i32);
|
||||
let old_section = self.0.old_position.map(|n| (n / 16.0).floor() as i32);
|
||||
let new_section = self.0.new_position.map(|n| (n / 16.0).floor() as i32);
|
||||
|
||||
if old_section != new_section {
|
||||
self.send_packet(UpdateViewPosition {
|
||||
|
@ -523,6 +729,16 @@ impl<'a> ClientMut<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
// Acknowledge broken blocks.
|
||||
for seq in self.0.dug_blocks.drain(..) {
|
||||
send_packet(
|
||||
&mut self.0.send,
|
||||
AcknowledgeBlockChanges {
|
||||
sequence: VarInt(seq),
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
// This is done after the chunks are loaded so that the "downloading terrain"
|
||||
// screen is closed at the appropriate time.
|
||||
if self.0.teleported_this_tick {
|
||||
|
@ -667,141 +883,6 @@ impl<'a> ClientMut<'a> {
|
|||
|
||||
self.0.old_position = self.0.new_position;
|
||||
}
|
||||
|
||||
fn handle_serverbound_packet(&mut self, pkt: C2sPlayPacket) {
|
||||
let client = &mut self.0;
|
||||
|
||||
fn handle_movement_packet(
|
||||
client: &mut Client,
|
||||
new_position: Vec3<f64>,
|
||||
new_yaw: f32,
|
||||
new_pitch: f32,
|
||||
new_on_ground: bool,
|
||||
) {
|
||||
if client.pending_teleports == 0 {
|
||||
let event = Event::Movement {
|
||||
position: client.new_position,
|
||||
yaw: client.yaw,
|
||||
pitch: client.pitch,
|
||||
on_ground: client.on_ground,
|
||||
};
|
||||
|
||||
client.new_position = new_position;
|
||||
client.yaw = new_yaw;
|
||||
client.pitch = new_pitch;
|
||||
client.on_ground = new_on_ground;
|
||||
|
||||
client.events.push(event);
|
||||
}
|
||||
}
|
||||
|
||||
match pkt {
|
||||
C2sPlayPacket::TeleportConfirm(p) => {
|
||||
if client.pending_teleports == 0 {
|
||||
self.disconnect("Unexpected teleport confirmation");
|
||||
return;
|
||||
}
|
||||
|
||||
let got = p.teleport_id.0 as u32;
|
||||
let expected = client
|
||||
.teleport_id_counter
|
||||
.wrapping_sub(client.pending_teleports);
|
||||
|
||||
if got == expected {
|
||||
client.pending_teleports -= 1;
|
||||
} else {
|
||||
self.disconnect(format!(
|
||||
"Unexpected teleport ID (expected {expected}, got {got})"
|
||||
));
|
||||
}
|
||||
}
|
||||
C2sPlayPacket::QueryBlockNbt(_) => {}
|
||||
C2sPlayPacket::SetDifficulty(_) => {}
|
||||
C2sPlayPacket::ChatCommand(_) => {}
|
||||
C2sPlayPacket::ChatMessageServerbound(_) => {}
|
||||
C2sPlayPacket::ChatPreview(_) => {}
|
||||
C2sPlayPacket::ClientStatus(_) => {}
|
||||
C2sPlayPacket::ClientSettings(p) => {
|
||||
let old = client.settings.replace(Settings {
|
||||
locale: p.locale.0,
|
||||
view_distance: p.view_distance.0,
|
||||
chat_mode: p.chat_mode,
|
||||
chat_colors: p.chat_colors,
|
||||
main_hand: p.main_hand,
|
||||
displayed_skin_parts: p.displayed_skin_parts,
|
||||
allow_server_listings: p.allow_server_listings,
|
||||
});
|
||||
|
||||
client.events.push(Event::SettingsChanged(old));
|
||||
}
|
||||
C2sPlayPacket::TabCompleteServerbound(_) => {}
|
||||
C2sPlayPacket::ClickWindowButton(_) => {}
|
||||
C2sPlayPacket::CloseWindow(_) => {}
|
||||
C2sPlayPacket::PluginMessageServerbound(_) => {}
|
||||
C2sPlayPacket::EditBook(_) => {}
|
||||
C2sPlayPacket::QueryEntityNbt(_) => {}
|
||||
C2sPlayPacket::InteractEntity(_) => {}
|
||||
C2sPlayPacket::GenerateStructure(_) => {}
|
||||
C2sPlayPacket::KeepAliveServerbound(p) => {
|
||||
let last_keepalive_id = client.last_keepalive_id;
|
||||
if client.got_keepalive {
|
||||
self.disconnect("Unexpected keepalive");
|
||||
} else if p.id != last_keepalive_id {
|
||||
self.disconnect(format!(
|
||||
"Keepalive ids don't match (expected {}, got {})",
|
||||
last_keepalive_id, p.id
|
||||
));
|
||||
} else {
|
||||
client.got_keepalive = true;
|
||||
}
|
||||
}
|
||||
C2sPlayPacket::LockDifficulty(_) => {}
|
||||
C2sPlayPacket::PlayerPosition(p) => {
|
||||
handle_movement_packet(client, p.position, client.yaw, client.pitch, p.on_ground)
|
||||
}
|
||||
C2sPlayPacket::PlayerPositionAndRotation(p) => {
|
||||
handle_movement_packet(client, p.position, p.yaw, p.pitch, p.on_ground)
|
||||
}
|
||||
C2sPlayPacket::PlayerRotation(p) => {
|
||||
handle_movement_packet(client, client.new_position, p.yaw, p.pitch, p.on_ground)
|
||||
}
|
||||
|
||||
C2sPlayPacket::PlayerMovement(p) => handle_movement_packet(
|
||||
client,
|
||||
client.new_position,
|
||||
client.yaw,
|
||||
client.pitch,
|
||||
p.on_ground,
|
||||
),
|
||||
C2sPlayPacket::VehicleMoveServerbound(_) => {}
|
||||
C2sPlayPacket::SteerBoat(_) => {}
|
||||
C2sPlayPacket::PickItem(_) => {}
|
||||
C2sPlayPacket::CraftRecipeRequest(_) => {}
|
||||
C2sPlayPacket::PlayerAbilitiesServerbound(_) => {}
|
||||
C2sPlayPacket::PlayerDigging(_) => {}
|
||||
C2sPlayPacket::EntityAction(_) => {}
|
||||
C2sPlayPacket::SteerVehicle(_) => {}
|
||||
C2sPlayPacket::Pong(_) => {}
|
||||
C2sPlayPacket::SetRecipeBookState(_) => {}
|
||||
C2sPlayPacket::SetDisplayedRecipe(_) => {}
|
||||
C2sPlayPacket::NameItem(_) => {}
|
||||
C2sPlayPacket::ResourcePackStatus(_) => {}
|
||||
C2sPlayPacket::AdvancementTab(_) => {}
|
||||
C2sPlayPacket::SelectTrade(_) => {}
|
||||
C2sPlayPacket::SetBeaconEffect(_) => {}
|
||||
C2sPlayPacket::HeldItemChangeServerbound(_) => {}
|
||||
C2sPlayPacket::UpdateCommandBlock(_) => {}
|
||||
C2sPlayPacket::UpdateCommandBlockMinecart(_) => {}
|
||||
C2sPlayPacket::CreativeInventoryAction(_) => {}
|
||||
C2sPlayPacket::UpdateJigsawBlock(_) => {}
|
||||
C2sPlayPacket::UpdateStructureBlock(_) => {}
|
||||
C2sPlayPacket::UpdateSign(_) => {}
|
||||
C2sPlayPacket::PlayerArmSwing(_) => {}
|
||||
C2sPlayPacket::Spectate(_) => {}
|
||||
C2sPlayPacket::PlayerBlockPlacement(_) => {}
|
||||
C2sPlayPacket::UseItem(_) => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Client {
|
||||
|
@ -810,40 +891,6 @@ impl Drop for Client {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Event {
|
||||
/// Settings were changed. The value in this variant is the previous client
|
||||
/// settings.
|
||||
SettingsChanged(Option<Settings>),
|
||||
|
||||
/// The client has moved. The values in this variant are the previous
|
||||
/// position and look.
|
||||
Movement {
|
||||
position: Vec3<f64>,
|
||||
yaw: f32,
|
||||
pitch: f32,
|
||||
on_ground: bool,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Debug)]
|
||||
pub struct Settings {
|
||||
/// e.g. en_US
|
||||
pub locale: String,
|
||||
/// The client side render distance, in chunks.
|
||||
///
|
||||
/// The value is always in `2..=32`.
|
||||
pub view_distance: u8,
|
||||
pub chat_mode: ChatMode,
|
||||
/// `true` if the client has chat colors enabled, `false` otherwise.
|
||||
pub chat_colors: bool,
|
||||
pub main_hand: MainHand,
|
||||
pub displayed_skin_parts: DisplayedSkinParts,
|
||||
pub allow_server_listings: bool,
|
||||
}
|
||||
|
||||
pub use crate::packets::play::c2s::{ChatMode, DisplayedSkinParts, MainHand};
|
||||
|
||||
fn send_packet(send_opt: &mut Option<Sender<S2cPlayPacket>>, pkt: impl Into<S2cPlayPacket>) {
|
||||
if let Some(send) = send_opt {
|
||||
match send.try_send(pkt.into()) {
|
||||
|
|
71
src/client/event.rs
Normal file
71
src/client/event.rs
Normal file
|
@ -0,0 +1,71 @@
|
|||
use vek::Vec3;
|
||||
|
||||
use crate::packets::play::c2s::BlockFace;
|
||||
pub use crate::packets::play::c2s::{ChatMode, DisplayedSkinParts, Hand, MainHand};
|
||||
pub use crate::packets::play::s2c::GameMode;
|
||||
use crate::{BlockPos, EntityId};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Event {
|
||||
/// Settings were changed. The value in this variant is the previous client
|
||||
/// settings.
|
||||
SettingsChanged(Option<Settings>),
|
||||
/// The client has moved. The values in this
|
||||
/// variant are the _previous_ position and look.
|
||||
Movement {
|
||||
position: Vec3<f64>,
|
||||
yaw: f32,
|
||||
pitch: f32,
|
||||
on_ground: bool,
|
||||
},
|
||||
InteractWithEntity {
|
||||
/// The ID of the entity being interacted with.
|
||||
id: EntityId,
|
||||
/// If the client was sneaking during the interaction.
|
||||
sneaking: bool,
|
||||
/// The type of interaction that occurred.
|
||||
typ: InteractWithEntity,
|
||||
},
|
||||
SteerBoat {
|
||||
left_paddle_turning: bool,
|
||||
right_paddle_turning: bool,
|
||||
},
|
||||
Digging(Digging),
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Debug)]
|
||||
pub struct Settings {
|
||||
/// e.g. en_US
|
||||
pub locale: String,
|
||||
/// The client side render distance, in chunks.
|
||||
///
|
||||
/// The value is always in `2..=32`.
|
||||
pub view_distance: u8,
|
||||
pub chat_mode: ChatMode,
|
||||
/// `true` if the client has chat colors enabled, `false` otherwise.
|
||||
pub chat_colors: bool,
|
||||
pub main_hand: MainHand,
|
||||
pub displayed_skin_parts: DisplayedSkinParts,
|
||||
pub allow_server_listings: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Debug)]
|
||||
pub enum InteractWithEntity {
|
||||
Interact(Hand),
|
||||
InteractAt { target: Vec3<f32>, hand: Hand },
|
||||
Attack,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
||||
pub struct Digging {
|
||||
pub status: DiggingStatus,
|
||||
pub position: BlockPos,
|
||||
pub face: BlockFace,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
||||
pub enum DiggingStatus {
|
||||
Start,
|
||||
Cancel,
|
||||
Finish,
|
||||
}
|
|
@ -64,6 +64,12 @@ impl Entities {
|
|||
self.sm.get(entity.0)
|
||||
}
|
||||
|
||||
pub(crate) fn get_with_network_id(&self, network_id: i32) -> Option<EntityId> {
|
||||
let version = NonZeroU32::new(network_id as u32)?;
|
||||
let index = *self.network_id_to_entity.get(&version)?;
|
||||
Some(EntityId(Key::new(index, version)))
|
||||
}
|
||||
|
||||
pub fn iter(&self) -> impl FusedIterator<Item = (EntityId, &Entity)> + Clone + '_ {
|
||||
self.sm.iter().map(|(k, v)| (EntityId(k), v))
|
||||
}
|
||||
|
|
|
@ -32,6 +32,7 @@ pub mod world;
|
|||
|
||||
pub use async_trait::async_trait;
|
||||
pub use biome::{Biome, BiomeId};
|
||||
pub use block::BlockState;
|
||||
pub use block_pos::BlockPos;
|
||||
pub use chunk::{Chunk, ChunkPos, Chunks, ChunksMut};
|
||||
pub use client::{Client, ClientMut, Clients, ClientsMut};
|
||||
|
|
|
@ -472,9 +472,9 @@ pub mod login {
|
|||
/// Packets and types used during the play state.
|
||||
pub mod play {
|
||||
pub mod s2c {
|
||||
use crate::packets::login::{s2c::Property, c2s::SignatureData};
|
||||
|
||||
use super::super::*;
|
||||
use crate::packets::login::c2s::SignatureData;
|
||||
use crate::packets::login::s2c::Property;
|
||||
|
||||
def_struct! {
|
||||
SpawnEntity 0x00 {
|
||||
|
@ -536,7 +536,7 @@ pub mod play {
|
|||
}
|
||||
|
||||
def_struct! {
|
||||
AcknoledgeBlockChanges 0x05 {
|
||||
AcknowledgeBlockChanges 0x05 {
|
||||
sequence: VarInt,
|
||||
}
|
||||
}
|
||||
|
@ -749,7 +749,7 @@ pub mod play {
|
|||
}
|
||||
|
||||
def_struct! {
|
||||
KeepAliveClientbound 0x1e {
|
||||
KeepAlive 0x1e {
|
||||
id: i64,
|
||||
}
|
||||
}
|
||||
|
@ -995,7 +995,7 @@ pub mod play {
|
|||
}
|
||||
|
||||
def_struct! {
|
||||
ChatMessageClientbound 0x30 {
|
||||
ChatMessage 0x30 {
|
||||
message: Text,
|
||||
typ: ChatMessageType,
|
||||
sender: Uuid,
|
||||
|
@ -1081,7 +1081,7 @@ pub mod play {
|
|||
}
|
||||
|
||||
def_struct! {
|
||||
HeldItemChangeClientbound 0x47 {
|
||||
HeldItemChange 0x47 {
|
||||
slot: BoundedInt<u8, 0, 9>,
|
||||
}
|
||||
}
|
||||
|
@ -1203,7 +1203,7 @@ pub mod play {
|
|||
SpawnExperienceOrb,
|
||||
SpawnPlayer,
|
||||
EntityAnimation,
|
||||
AcknoledgeBlockChanges,
|
||||
AcknowledgeBlockChanges,
|
||||
BlockBreakAnimation,
|
||||
BlockEntityData,
|
||||
BlockAction,
|
||||
|
@ -1213,7 +1213,7 @@ pub mod play {
|
|||
EntityStatus,
|
||||
UnloadChunk,
|
||||
ChangeGameState,
|
||||
KeepAliveClientbound,
|
||||
KeepAlive,
|
||||
ChunkDataAndUpdateLight,
|
||||
JoinGame,
|
||||
EntityPosition,
|
||||
|
@ -1223,7 +1223,7 @@ pub mod play {
|
|||
DestroyEntities,
|
||||
EntityHeadLook,
|
||||
MultiBlockChange,
|
||||
HeldItemChangeClientbound,
|
||||
HeldItemChange,
|
||||
UpdateViewPosition,
|
||||
UpdateViewDistance,
|
||||
SpawnPosition,
|
||||
|
@ -1268,9 +1268,9 @@ pub mod play {
|
|||
}
|
||||
|
||||
def_struct! {
|
||||
ChatMessageServerbound 0x04 {
|
||||
ChatMessage 0x04 {
|
||||
message: BoundedString<0, 256>
|
||||
// TODO:
|
||||
// TODO:
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1337,7 +1337,7 @@ pub mod play {
|
|||
}
|
||||
|
||||
def_struct! {
|
||||
TabCompleteServerbound 0x08 {
|
||||
TabComplete 0x08 {
|
||||
transaction_id: VarInt,
|
||||
/// Text behind the cursor without the '/'.
|
||||
text: BoundedString<0, 32500>
|
||||
|
@ -1358,7 +1358,7 @@ pub mod play {
|
|||
}
|
||||
|
||||
def_struct! {
|
||||
PluginMessageServerbound 0x0c {
|
||||
PluginMessage 0x0c {
|
||||
channel: Ident,
|
||||
data: RawBytes,
|
||||
}
|
||||
|
@ -1391,18 +1391,12 @@ pub mod play {
|
|||
InteractType: VarInt {
|
||||
Interact: Hand = 0,
|
||||
Attack = 1,
|
||||
InteractAt: InteractAtData = 2
|
||||
}
|
||||
}
|
||||
|
||||
def_struct! {
|
||||
InteractAtData {
|
||||
target: Vec3<f64>,
|
||||
hand: Hand,
|
||||
InteractAt: (Vec3<f32>, Hand) = 2
|
||||
}
|
||||
}
|
||||
|
||||
def_enum! {
|
||||
#[derive(Copy, PartialEq, Eq)]
|
||||
Hand: VarInt {
|
||||
Main = 0,
|
||||
Off = 1,
|
||||
|
@ -1418,7 +1412,7 @@ pub mod play {
|
|||
}
|
||||
|
||||
def_struct! {
|
||||
KeepAliveServerbound 0x11 {
|
||||
KeepAlive 0x11 {
|
||||
id: i64,
|
||||
}
|
||||
}
|
||||
|
@ -1465,7 +1459,7 @@ pub mod play {
|
|||
}
|
||||
|
||||
def_struct! {
|
||||
VehicleMoveServerbound 0x17 {
|
||||
VehicleMove 0x17 {
|
||||
/// Absolute position
|
||||
position: Vec3<f64>,
|
||||
/// Degrees
|
||||
|
@ -1497,7 +1491,7 @@ pub mod play {
|
|||
}
|
||||
|
||||
def_enum! {
|
||||
PlayerAbilitiesServerbound 0x1b: i8 {
|
||||
PlayerAbilities 0x1b: i8 {
|
||||
NotFlying = 0,
|
||||
Flying = 0b10,
|
||||
}
|
||||
|
@ -1525,6 +1519,7 @@ pub mod play {
|
|||
}
|
||||
|
||||
def_enum! {
|
||||
#[derive(Copy, PartialEq, Eq)]
|
||||
BlockFace: i8 {
|
||||
/// -Y
|
||||
Bottom = 0,
|
||||
|
@ -1644,7 +1639,7 @@ pub mod play {
|
|||
}
|
||||
|
||||
def_struct! {
|
||||
HeldItemChangeServerbound 0x27 {
|
||||
HeldItemChange 0x27 {
|
||||
slot: BoundedInt<i16, 0, 8>,
|
||||
}
|
||||
}
|
||||
|
@ -1784,7 +1779,7 @@ pub mod play {
|
|||
hand: Hand,
|
||||
location: BlockPos,
|
||||
face: BlockFace,
|
||||
cursor_pos: Vec3<f64>,
|
||||
cursor_pos: Vec3<f32>,
|
||||
head_inside_block: bool,
|
||||
sequence: VarInt,
|
||||
}
|
||||
|
@ -1852,29 +1847,29 @@ pub mod play {
|
|||
QueryBlockNbt,
|
||||
SetDifficulty,
|
||||
ChatCommand,
|
||||
ChatMessageServerbound,
|
||||
ChatMessage,
|
||||
ChatPreview,
|
||||
ClientStatus,
|
||||
ClientSettings,
|
||||
TabCompleteServerbound,
|
||||
TabComplete,
|
||||
ClickWindowButton,
|
||||
CloseWindow,
|
||||
PluginMessageServerbound,
|
||||
PluginMessage,
|
||||
EditBook,
|
||||
QueryEntityNbt,
|
||||
InteractEntity,
|
||||
GenerateStructure,
|
||||
KeepAliveServerbound,
|
||||
KeepAlive,
|
||||
LockDifficulty,
|
||||
PlayerPosition,
|
||||
PlayerPositionAndRotation,
|
||||
PlayerRotation,
|
||||
PlayerMovement,
|
||||
VehicleMoveServerbound,
|
||||
VehicleMove,
|
||||
SteerBoat,
|
||||
PickItem,
|
||||
CraftRecipeRequest,
|
||||
PlayerAbilitiesServerbound,
|
||||
PlayerAbilities,
|
||||
PlayerDigging,
|
||||
EntityAction,
|
||||
SteerVehicle,
|
||||
|
@ -1886,7 +1881,7 @@ pub mod play {
|
|||
AdvancementTab,
|
||||
SelectTrade,
|
||||
SetBeaconEffect,
|
||||
HeldItemChangeServerbound,
|
||||
HeldItemChange,
|
||||
UpdateCommandBlock,
|
||||
UpdateCommandBlockMinecart,
|
||||
CreativeInventoryAction,
|
||||
|
|
|
@ -360,6 +360,13 @@ fn do_update_loop(server: Server, mut worlds: WorldsMut) -> ShutdownResult {
|
|||
join_player(&server, worlds.reborrow(), msg);
|
||||
}
|
||||
|
||||
// Get serverbound packets first so they are not dealt with a tick late.
|
||||
worlds.par_iter_mut().for_each(|(_, mut world)| {
|
||||
world.clients.par_iter_mut().for_each(|(_, mut client)| {
|
||||
client.handle_serverbound_packets(&world.entities);
|
||||
});
|
||||
});
|
||||
|
||||
server.config().update(&server, worlds.reborrow());
|
||||
|
||||
worlds.par_iter_mut().for_each(|(_, mut world)| {
|
||||
|
|
|
@ -47,6 +47,8 @@ impl SpatialIndex {
|
|||
})
|
||||
}
|
||||
|
||||
// TODO: accept predicate here. Might want to skip invisible entities, for
|
||||
// instance.
|
||||
pub fn raycast(&self, origin: Vec3<f64>, direction: Vec3<f64>) -> Option<RaycastHit> {
|
||||
debug_assert!(
|
||||
direction.is_normalized(),
|
||||
|
|
Loading…
Reference in a new issue