mirror of
https://github.com/italicsjenga/valence.git
synced 2024-12-23 22:41: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> {
|
pub fn get_block_state(&self, pos: impl Into<BlockPos>) -> Option<BlockState> {
|
||||||
let pos = pos.into();
|
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)?;
|
let chunk = self.get(chunk_pos)?;
|
||||||
|
|
||||||
|
@ -80,6 +80,10 @@ impl<'a> ChunksMut<'a> {
|
||||||
Self(chunks)
|
Self(chunks)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn reborrow(&mut self) -> ChunksMut {
|
||||||
|
ChunksMut(self.0)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn create(&mut self, pos: impl Into<ChunkPos>) -> bool {
|
pub fn create(&mut self, pos: impl Into<ChunkPos>) -> bool {
|
||||||
let section_count = (self.server.dimension(self.dimension).height / 16) as u32;
|
let section_count = (self.server.dimension(self.dimension).height / 16) as u32;
|
||||||
let chunk = Chunk::new(section_count, self.server.current_tick());
|
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 {
|
pub fn set_block_state(&mut self, pos: impl Into<BlockPos>, block: BlockState) -> bool {
|
||||||
let pos = pos.into();
|
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) {
|
if let Some(chunk) = self.0.chunks.get_mut(&chunk_pos) {
|
||||||
let min_y = self.0.server.dimension(self.0.dimension).min_y;
|
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
|
let chunk_section_position = (pos.x as i64) << 42
|
||||||
| (pos.z as i64 & 0x3fffff) << 20
|
| (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 {
|
packet(BlockChangePacket::Multi(MultiBlockChange {
|
||||||
chunk_section_position,
|
chunk_section_position,
|
||||||
|
@ -401,7 +405,7 @@ impl ChunkPos {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn at(x: f64, z: f64) -> Self {
|
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.
|
/// A 16x16x16 section of blocks, biomes, and light in a chunk.
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
struct ChunkSection {
|
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::collections::HashSet;
|
||||||
use std::iter::FusedIterator;
|
use std::iter::FusedIterator;
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
|
|
||||||
|
pub use event::*;
|
||||||
use flume::{Receiver, Sender, TrySendError};
|
use flume::{Receiver, Sender, TrySendError};
|
||||||
use rayon::iter::ParallelIterator;
|
use rayon::iter::ParallelIterator;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
@ -12,16 +15,15 @@ use crate::block_pos::BlockPos;
|
||||||
use crate::byte_angle::ByteAngle;
|
use crate::byte_angle::ByteAngle;
|
||||||
use crate::dimension::{Dimension, DimensionEffects};
|
use crate::dimension::{Dimension, DimensionEffects};
|
||||||
use crate::entity::{velocity_to_packet_units, EntityType};
|
use crate::entity::{velocity_to_packet_units, EntityType};
|
||||||
use crate::packets::play::c2s::C2sPlayPacket;
|
use crate::packets::play::c2s::{C2sPlayPacket, DiggingStatus};
|
||||||
pub use crate::packets::play::s2c::GameMode;
|
|
||||||
use crate::packets::play::s2c::{
|
use crate::packets::play::s2c::{
|
||||||
Biome as BiomeRegistryBiome, BiomeAdditionsSound, BiomeEffects, BiomeMoodSound, BiomeMusic,
|
AcknowledgeBlockChanges, Biome as BiomeRegistryBiome, BiomeAdditionsSound, BiomeEffects,
|
||||||
BiomeParticle, BiomeParticleOptions, BiomeProperty, BiomeRegistry, ChangeGameState,
|
BiomeMoodSound, BiomeMusic, BiomeParticle, BiomeParticleOptions, BiomeProperty, BiomeRegistry,
|
||||||
ChangeGameStateReason, ChatTypeRegistry, DestroyEntities, DimensionType, DimensionTypeRegistry,
|
ChangeGameState, ChangeGameStateReason, ChatTypeRegistry, DestroyEntities, DimensionType,
|
||||||
DimensionTypeRegistryEntry, Disconnect, EntityHeadLook, EntityPosition,
|
DimensionTypeRegistry, DimensionTypeRegistryEntry, Disconnect, EntityHeadLook, EntityPosition,
|
||||||
EntityPositionAndRotation, EntityRotation, EntityTeleport, EntityVelocity, JoinGame,
|
EntityPositionAndRotation, EntityRotation, EntityTeleport, EntityVelocity, JoinGame, KeepAlive,
|
||||||
KeepAliveClientbound, PlayerPositionAndLook, PlayerPositionAndLookFlags, RegistryCodec,
|
PlayerPositionAndLook, PlayerPositionAndLookFlags, RegistryCodec, S2cPlayPacket, SpawnPosition,
|
||||||
S2cPlayPacket, SpawnPosition, UnloadChunk, UpdateViewDistance, UpdateViewPosition,
|
UnloadChunk, UpdateViewDistance, UpdateViewPosition,
|
||||||
};
|
};
|
||||||
use crate::protocol::{BoundedInt, Nbt};
|
use crate::protocol::{BoundedInt, Nbt};
|
||||||
use crate::server::C2sPacketChannels;
|
use crate::server::C2sPacketChannels;
|
||||||
|
@ -156,6 +158,9 @@ pub struct Client {
|
||||||
new_game_mode: GameMode,
|
new_game_mode: GameMode,
|
||||||
old_game_mode: GameMode,
|
old_game_mode: GameMode,
|
||||||
settings: Option<Settings>,
|
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);
|
pub struct ClientMut<'a>(&'a mut Client);
|
||||||
|
@ -205,6 +210,7 @@ impl Client {
|
||||||
new_game_mode: GameMode::Survival,
|
new_game_mode: GameMode::Survival,
|
||||||
old_game_mode: GameMode::Survival,
|
old_game_mode: GameMode::Survival,
|
||||||
settings: None,
|
settings: None,
|
||||||
|
dug_blocks: Vec::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -258,7 +264,7 @@ impl Client {
|
||||||
self.send.is_none()
|
self.send.is_none()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn events(&self) -> &[Event] {
|
pub fn events(&self) -> &Vec<Event> {
|
||||||
&self.events
|
&self.events
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -288,8 +294,13 @@ impl<'a> ClientMut<'a> {
|
||||||
ClientMut(self.0)
|
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) {
|
pub fn teleport(&mut self, pos: impl Into<Vec3<f64>>, yaw: f32, pitch: f32) {
|
||||||
self.0.new_position = pos.into();
|
self.0.new_position = pos.into();
|
||||||
|
|
||||||
self.0.yaw = yaw;
|
self.0.yaw = yaw;
|
||||||
self.0.pitch = pitch;
|
self.0.pitch = pitch;
|
||||||
|
|
||||||
|
@ -363,6 +374,215 @@ impl<'a> ClientMut<'a> {
|
||||||
self.0.new_max_view_distance = dist.clamp(2, 32);
|
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(
|
pub(crate) fn update(
|
||||||
&mut self,
|
&mut self,
|
||||||
server: &Server,
|
server: &Server,
|
||||||
|
@ -371,18 +591,7 @@ impl<'a> ClientMut<'a> {
|
||||||
chunks: &Chunks,
|
chunks: &Chunks,
|
||||||
meta: &WorldMeta,
|
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.
|
// 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()) {
|
if self.recv.is_disconnected() || self.send.as_ref().map_or(true, |s| s.is_disconnected()) {
|
||||||
self.0.send = None;
|
self.0.send = None;
|
||||||
return;
|
return;
|
||||||
|
@ -454,7 +663,7 @@ impl<'a> ClientMut<'a> {
|
||||||
if current_tick % (server.tick_rate() * 8) == 0 {
|
if current_tick % (server.tick_rate() * 8) == 0 {
|
||||||
if self.0.got_keepalive {
|
if self.0.got_keepalive {
|
||||||
let id = rand::random();
|
let id = rand::random();
|
||||||
self.send_packet(KeepAliveClientbound { id });
|
self.send_packet(KeepAlive { id });
|
||||||
self.0.last_keepalive_id = id;
|
self.0.last_keepalive_id = id;
|
||||||
self.0.got_keepalive = false;
|
self.0.got_keepalive = false;
|
||||||
} else {
|
} else {
|
||||||
|
@ -464,16 +673,13 @@ impl<'a> ClientMut<'a> {
|
||||||
|
|
||||||
let view_dist = self.view_distance();
|
let view_dist = self.view_distance();
|
||||||
|
|
||||||
let center = ChunkPos::new(
|
let center = ChunkPos::at(self.new_position.x, self.new_position.z);
|
||||||
(self.new_position.x / 16.0) as i32,
|
|
||||||
(self.new_position.z / 16.0) as i32,
|
|
||||||
);
|
|
||||||
|
|
||||||
// Send the update view position packet if the client changes the chunk section
|
// Send the update view position packet if the client changes the chunk section
|
||||||
// they're in.
|
// they're in.
|
||||||
{
|
{
|
||||||
let old_section = self.0.old_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) as i32);
|
let new_section = self.0.new_position.map(|n| (n / 16.0).floor() as i32);
|
||||||
|
|
||||||
if old_section != new_section {
|
if old_section != new_section {
|
||||||
self.send_packet(UpdateViewPosition {
|
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"
|
// This is done after the chunks are loaded so that the "downloading terrain"
|
||||||
// screen is closed at the appropriate time.
|
// screen is closed at the appropriate time.
|
||||||
if self.0.teleported_this_tick {
|
if self.0.teleported_this_tick {
|
||||||
|
@ -667,141 +883,6 @@ impl<'a> ClientMut<'a> {
|
||||||
|
|
||||||
self.0.old_position = self.0.new_position;
|
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 {
|
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>) {
|
fn send_packet(send_opt: &mut Option<Sender<S2cPlayPacket>>, pkt: impl Into<S2cPlayPacket>) {
|
||||||
if let Some(send) = send_opt {
|
if let Some(send) = send_opt {
|
||||||
match send.try_send(pkt.into()) {
|
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)
|
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 + '_ {
|
pub fn iter(&self) -> impl FusedIterator<Item = (EntityId, &Entity)> + Clone + '_ {
|
||||||
self.sm.iter().map(|(k, v)| (EntityId(k), v))
|
self.sm.iter().map(|(k, v)| (EntityId(k), v))
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,6 +32,7 @@ pub mod world;
|
||||||
|
|
||||||
pub use async_trait::async_trait;
|
pub use async_trait::async_trait;
|
||||||
pub use biome::{Biome, BiomeId};
|
pub use biome::{Biome, BiomeId};
|
||||||
|
pub use block::BlockState;
|
||||||
pub use block_pos::BlockPos;
|
pub use block_pos::BlockPos;
|
||||||
pub use chunk::{Chunk, ChunkPos, Chunks, ChunksMut};
|
pub use chunk::{Chunk, ChunkPos, Chunks, ChunksMut};
|
||||||
pub use client::{Client, ClientMut, Clients, ClientsMut};
|
pub use client::{Client, ClientMut, Clients, ClientsMut};
|
||||||
|
|
|
@ -472,9 +472,9 @@ pub mod login {
|
||||||
/// Packets and types used during the play state.
|
/// Packets and types used during the play state.
|
||||||
pub mod play {
|
pub mod play {
|
||||||
pub mod s2c {
|
pub mod s2c {
|
||||||
use crate::packets::login::{s2c::Property, c2s::SignatureData};
|
|
||||||
|
|
||||||
use super::super::*;
|
use super::super::*;
|
||||||
|
use crate::packets::login::c2s::SignatureData;
|
||||||
|
use crate::packets::login::s2c::Property;
|
||||||
|
|
||||||
def_struct! {
|
def_struct! {
|
||||||
SpawnEntity 0x00 {
|
SpawnEntity 0x00 {
|
||||||
|
@ -536,7 +536,7 @@ pub mod play {
|
||||||
}
|
}
|
||||||
|
|
||||||
def_struct! {
|
def_struct! {
|
||||||
AcknoledgeBlockChanges 0x05 {
|
AcknowledgeBlockChanges 0x05 {
|
||||||
sequence: VarInt,
|
sequence: VarInt,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -749,7 +749,7 @@ pub mod play {
|
||||||
}
|
}
|
||||||
|
|
||||||
def_struct! {
|
def_struct! {
|
||||||
KeepAliveClientbound 0x1e {
|
KeepAlive 0x1e {
|
||||||
id: i64,
|
id: i64,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -995,7 +995,7 @@ pub mod play {
|
||||||
}
|
}
|
||||||
|
|
||||||
def_struct! {
|
def_struct! {
|
||||||
ChatMessageClientbound 0x30 {
|
ChatMessage 0x30 {
|
||||||
message: Text,
|
message: Text,
|
||||||
typ: ChatMessageType,
|
typ: ChatMessageType,
|
||||||
sender: Uuid,
|
sender: Uuid,
|
||||||
|
@ -1081,7 +1081,7 @@ pub mod play {
|
||||||
}
|
}
|
||||||
|
|
||||||
def_struct! {
|
def_struct! {
|
||||||
HeldItemChangeClientbound 0x47 {
|
HeldItemChange 0x47 {
|
||||||
slot: BoundedInt<u8, 0, 9>,
|
slot: BoundedInt<u8, 0, 9>,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1203,7 +1203,7 @@ pub mod play {
|
||||||
SpawnExperienceOrb,
|
SpawnExperienceOrb,
|
||||||
SpawnPlayer,
|
SpawnPlayer,
|
||||||
EntityAnimation,
|
EntityAnimation,
|
||||||
AcknoledgeBlockChanges,
|
AcknowledgeBlockChanges,
|
||||||
BlockBreakAnimation,
|
BlockBreakAnimation,
|
||||||
BlockEntityData,
|
BlockEntityData,
|
||||||
BlockAction,
|
BlockAction,
|
||||||
|
@ -1213,7 +1213,7 @@ pub mod play {
|
||||||
EntityStatus,
|
EntityStatus,
|
||||||
UnloadChunk,
|
UnloadChunk,
|
||||||
ChangeGameState,
|
ChangeGameState,
|
||||||
KeepAliveClientbound,
|
KeepAlive,
|
||||||
ChunkDataAndUpdateLight,
|
ChunkDataAndUpdateLight,
|
||||||
JoinGame,
|
JoinGame,
|
||||||
EntityPosition,
|
EntityPosition,
|
||||||
|
@ -1223,7 +1223,7 @@ pub mod play {
|
||||||
DestroyEntities,
|
DestroyEntities,
|
||||||
EntityHeadLook,
|
EntityHeadLook,
|
||||||
MultiBlockChange,
|
MultiBlockChange,
|
||||||
HeldItemChangeClientbound,
|
HeldItemChange,
|
||||||
UpdateViewPosition,
|
UpdateViewPosition,
|
||||||
UpdateViewDistance,
|
UpdateViewDistance,
|
||||||
SpawnPosition,
|
SpawnPosition,
|
||||||
|
@ -1268,9 +1268,9 @@ pub mod play {
|
||||||
}
|
}
|
||||||
|
|
||||||
def_struct! {
|
def_struct! {
|
||||||
ChatMessageServerbound 0x04 {
|
ChatMessage 0x04 {
|
||||||
message: BoundedString<0, 256>
|
message: BoundedString<0, 256>
|
||||||
// TODO:
|
// TODO:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1337,7 +1337,7 @@ pub mod play {
|
||||||
}
|
}
|
||||||
|
|
||||||
def_struct! {
|
def_struct! {
|
||||||
TabCompleteServerbound 0x08 {
|
TabComplete 0x08 {
|
||||||
transaction_id: VarInt,
|
transaction_id: VarInt,
|
||||||
/// Text behind the cursor without the '/'.
|
/// Text behind the cursor without the '/'.
|
||||||
text: BoundedString<0, 32500>
|
text: BoundedString<0, 32500>
|
||||||
|
@ -1358,7 +1358,7 @@ pub mod play {
|
||||||
}
|
}
|
||||||
|
|
||||||
def_struct! {
|
def_struct! {
|
||||||
PluginMessageServerbound 0x0c {
|
PluginMessage 0x0c {
|
||||||
channel: Ident,
|
channel: Ident,
|
||||||
data: RawBytes,
|
data: RawBytes,
|
||||||
}
|
}
|
||||||
|
@ -1391,18 +1391,12 @@ pub mod play {
|
||||||
InteractType: VarInt {
|
InteractType: VarInt {
|
||||||
Interact: Hand = 0,
|
Interact: Hand = 0,
|
||||||
Attack = 1,
|
Attack = 1,
|
||||||
InteractAt: InteractAtData = 2
|
InteractAt: (Vec3<f32>, Hand) = 2
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
def_struct! {
|
|
||||||
InteractAtData {
|
|
||||||
target: Vec3<f64>,
|
|
||||||
hand: Hand,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def_enum! {
|
def_enum! {
|
||||||
|
#[derive(Copy, PartialEq, Eq)]
|
||||||
Hand: VarInt {
|
Hand: VarInt {
|
||||||
Main = 0,
|
Main = 0,
|
||||||
Off = 1,
|
Off = 1,
|
||||||
|
@ -1418,7 +1412,7 @@ pub mod play {
|
||||||
}
|
}
|
||||||
|
|
||||||
def_struct! {
|
def_struct! {
|
||||||
KeepAliveServerbound 0x11 {
|
KeepAlive 0x11 {
|
||||||
id: i64,
|
id: i64,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1465,7 +1459,7 @@ pub mod play {
|
||||||
}
|
}
|
||||||
|
|
||||||
def_struct! {
|
def_struct! {
|
||||||
VehicleMoveServerbound 0x17 {
|
VehicleMove 0x17 {
|
||||||
/// Absolute position
|
/// Absolute position
|
||||||
position: Vec3<f64>,
|
position: Vec3<f64>,
|
||||||
/// Degrees
|
/// Degrees
|
||||||
|
@ -1497,7 +1491,7 @@ pub mod play {
|
||||||
}
|
}
|
||||||
|
|
||||||
def_enum! {
|
def_enum! {
|
||||||
PlayerAbilitiesServerbound 0x1b: i8 {
|
PlayerAbilities 0x1b: i8 {
|
||||||
NotFlying = 0,
|
NotFlying = 0,
|
||||||
Flying = 0b10,
|
Flying = 0b10,
|
||||||
}
|
}
|
||||||
|
@ -1525,6 +1519,7 @@ pub mod play {
|
||||||
}
|
}
|
||||||
|
|
||||||
def_enum! {
|
def_enum! {
|
||||||
|
#[derive(Copy, PartialEq, Eq)]
|
||||||
BlockFace: i8 {
|
BlockFace: i8 {
|
||||||
/// -Y
|
/// -Y
|
||||||
Bottom = 0,
|
Bottom = 0,
|
||||||
|
@ -1644,7 +1639,7 @@ pub mod play {
|
||||||
}
|
}
|
||||||
|
|
||||||
def_struct! {
|
def_struct! {
|
||||||
HeldItemChangeServerbound 0x27 {
|
HeldItemChange 0x27 {
|
||||||
slot: BoundedInt<i16, 0, 8>,
|
slot: BoundedInt<i16, 0, 8>,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1784,7 +1779,7 @@ pub mod play {
|
||||||
hand: Hand,
|
hand: Hand,
|
||||||
location: BlockPos,
|
location: BlockPos,
|
||||||
face: BlockFace,
|
face: BlockFace,
|
||||||
cursor_pos: Vec3<f64>,
|
cursor_pos: Vec3<f32>,
|
||||||
head_inside_block: bool,
|
head_inside_block: bool,
|
||||||
sequence: VarInt,
|
sequence: VarInt,
|
||||||
}
|
}
|
||||||
|
@ -1852,29 +1847,29 @@ pub mod play {
|
||||||
QueryBlockNbt,
|
QueryBlockNbt,
|
||||||
SetDifficulty,
|
SetDifficulty,
|
||||||
ChatCommand,
|
ChatCommand,
|
||||||
ChatMessageServerbound,
|
ChatMessage,
|
||||||
ChatPreview,
|
ChatPreview,
|
||||||
ClientStatus,
|
ClientStatus,
|
||||||
ClientSettings,
|
ClientSettings,
|
||||||
TabCompleteServerbound,
|
TabComplete,
|
||||||
ClickWindowButton,
|
ClickWindowButton,
|
||||||
CloseWindow,
|
CloseWindow,
|
||||||
PluginMessageServerbound,
|
PluginMessage,
|
||||||
EditBook,
|
EditBook,
|
||||||
QueryEntityNbt,
|
QueryEntityNbt,
|
||||||
InteractEntity,
|
InteractEntity,
|
||||||
GenerateStructure,
|
GenerateStructure,
|
||||||
KeepAliveServerbound,
|
KeepAlive,
|
||||||
LockDifficulty,
|
LockDifficulty,
|
||||||
PlayerPosition,
|
PlayerPosition,
|
||||||
PlayerPositionAndRotation,
|
PlayerPositionAndRotation,
|
||||||
PlayerRotation,
|
PlayerRotation,
|
||||||
PlayerMovement,
|
PlayerMovement,
|
||||||
VehicleMoveServerbound,
|
VehicleMove,
|
||||||
SteerBoat,
|
SteerBoat,
|
||||||
PickItem,
|
PickItem,
|
||||||
CraftRecipeRequest,
|
CraftRecipeRequest,
|
||||||
PlayerAbilitiesServerbound,
|
PlayerAbilities,
|
||||||
PlayerDigging,
|
PlayerDigging,
|
||||||
EntityAction,
|
EntityAction,
|
||||||
SteerVehicle,
|
SteerVehicle,
|
||||||
|
@ -1886,7 +1881,7 @@ pub mod play {
|
||||||
AdvancementTab,
|
AdvancementTab,
|
||||||
SelectTrade,
|
SelectTrade,
|
||||||
SetBeaconEffect,
|
SetBeaconEffect,
|
||||||
HeldItemChangeServerbound,
|
HeldItemChange,
|
||||||
UpdateCommandBlock,
|
UpdateCommandBlock,
|
||||||
UpdateCommandBlockMinecart,
|
UpdateCommandBlockMinecart,
|
||||||
CreativeInventoryAction,
|
CreativeInventoryAction,
|
||||||
|
|
|
@ -360,6 +360,13 @@ fn do_update_loop(server: Server, mut worlds: WorldsMut) -> ShutdownResult {
|
||||||
join_player(&server, worlds.reborrow(), msg);
|
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());
|
server.config().update(&server, worlds.reborrow());
|
||||||
|
|
||||||
worlds.par_iter_mut().for_each(|(_, mut world)| {
|
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> {
|
pub fn raycast(&self, origin: Vec3<f64>, direction: Vec3<f64>) -> Option<RaycastHit> {
|
||||||
debug_assert!(
|
debug_assert!(
|
||||||
direction.is_normalized(),
|
direction.is_normalized(),
|
||||||
|
|
Loading…
Reference in a new issue