mirror of
https://github.com/italicsjenga/valence.git
synced 2025-01-11 07:11:30 +11:00
Add more client events
This commit is contained in:
parent
4aca4e24a7
commit
6c0eef1ae7
|
@ -7,8 +7,10 @@ use std::sync::Mutex;
|
|||
use log::LevelFilter;
|
||||
use num::Integer;
|
||||
use rayon::iter::{IndexedParallelIterator, IntoParallelRefMutIterator, ParallelIterator};
|
||||
use valence::client::{ClientId, ClientEvent, GameMode};
|
||||
use valence::client::{ClientEvent, ClientId, GameMode};
|
||||
use valence::config::{Config, ServerListPing};
|
||||
use valence::entity::EntityMeta;
|
||||
use valence::entity::meta::Pose;
|
||||
use valence::text::Color;
|
||||
use valence::{
|
||||
async_trait, ident, Biome, BlockState, Dimension, DimensionId, EntityId, EntityType, Server,
|
||||
|
@ -150,7 +152,7 @@ impl Config for Game {
|
|||
client_id,
|
||||
server
|
||||
.entities
|
||||
.create_with_uuid(client.uuid(), EntityType::Player)
|
||||
.create_with_uuid(EntityType::Player, client.uuid())
|
||||
.unwrap()
|
||||
.0,
|
||||
);
|
||||
|
@ -167,6 +169,7 @@ impl Config for Game {
|
|||
.entities
|
||||
.get_mut(player_entities[&client_id])
|
||||
.unwrap();
|
||||
|
||||
while let Some(event) = client.pop_event() {
|
||||
match event {
|
||||
ClientEvent::Digging(e) => {
|
||||
|
@ -191,6 +194,33 @@ impl Config for Game {
|
|||
player.set_pitch(client.pitch());
|
||||
player.set_on_ground(client.on_ground());
|
||||
}
|
||||
ClientEvent::StartSneaking => {
|
||||
if let EntityMeta::Player(e) = player.meta_mut() {
|
||||
e.set_crouching(true);
|
||||
e.set_pose(Pose::Sneaking);
|
||||
}
|
||||
}
|
||||
ClientEvent::StopSneaking => {
|
||||
if let EntityMeta::Player(e) = player.meta_mut() {
|
||||
e.set_pose(Pose::Standing);
|
||||
e.set_crouching(false);
|
||||
}
|
||||
}
|
||||
ClientEvent::StartSprinting => {
|
||||
if let EntityMeta::Player(e) = player.meta_mut() {
|
||||
e.set_sprinting(true);
|
||||
}
|
||||
}
|
||||
ClientEvent::StopSprinting => {
|
||||
if let EntityMeta::Player(e) = player.meta_mut() {
|
||||
e.set_sprinting(false);
|
||||
}
|
||||
}
|
||||
ClientEvent::ArmSwing(_) => {
|
||||
if let EntityMeta::Player(_) = player.meta_mut() {
|
||||
// TODO: swing arm.
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
|
140
src/client.rs
140
src/client.rs
|
@ -4,6 +4,7 @@ use std::collections::{HashSet, VecDeque};
|
|||
use std::iter::FusedIterator;
|
||||
use std::time::Duration;
|
||||
|
||||
use bitfield_struct::bitfield;
|
||||
pub use event::*;
|
||||
use flume::{Receiver, Sender, TrySendError};
|
||||
use rayon::iter::ParallelIterator;
|
||||
|
@ -15,18 +16,20 @@ use crate::dimension::{Dimension, DimensionEffects};
|
|||
use crate::entity::types::Player;
|
||||
use crate::entity::{velocity_to_packet_units, EntityType};
|
||||
use crate::player_textures::SignedPlayerTextures;
|
||||
use crate::protocol::packets::play::c2s::{C2sPlayPacket, DiggingStatus, InteractType};
|
||||
pub use crate::protocol::packets::play::s2c::EntityEventStatus as EntityEvent;
|
||||
use crate::protocol::packets::play::c2s::{
|
||||
C2sPlayPacket, DiggingStatus, InteractType, PlayerCommandId,
|
||||
};
|
||||
pub use crate::protocol::packets::play::s2c::EntityEvent;
|
||||
use crate::protocol::packets::play::s2c::{
|
||||
Biome as BiomeRegistryBiome, BiomeAdditionsSound, BiomeEffects, BiomeMoodSound, BiomeMusic,
|
||||
BiomeParticle, BiomeParticleOptions, BiomeProperty, BiomeRegistry, BlockChangeAck, ChatType,
|
||||
ChatTypeChat, ChatTypeNarration, ChatTypeRegistry, ChatTypeRegistryEntry, DimensionType,
|
||||
DimensionTypeRegistry, DimensionTypeRegistryEntry, Disconnect,
|
||||
EntityEvent as EntityEventPacket, ForgetLevelChunk, GameEvent, GameEventReason, KeepAlive,
|
||||
Login, MoveEntityPosition, MoveEntityPositionAndRotation, MoveEntityRotation, PlayerPosition,
|
||||
PlayerPositionFlags, RegistryCodec, RemoveEntities, Respawn, RotateHead, S2cPlayPacket,
|
||||
SetChunkCacheCenter, SetChunkCacheRadius, SetEntityMetadata, SetEntityMotion, SpawnPosition,
|
||||
SystemChat, TeleportEntity,
|
||||
DimensionTypeRegistry, DimensionTypeRegistryEntry, Disconnect, DoEntityEvent, ForgetLevelChunk,
|
||||
GameEvent, GameEventReason, KeepAlive, Login, MoveEntityPosition,
|
||||
MoveEntityPositionAndRotation, MoveEntityRotation, PlayerPosition, PlayerPositionFlags,
|
||||
RegistryCodec, RemoveEntities, Respawn, RotateHead, S2cPlayPacket, SetChunkCacheCenter,
|
||||
SetChunkCacheRadius, SetEntityMetadata, SetEntityMotion, SpawnPosition, SystemChat,
|
||||
TeleportEntity,
|
||||
};
|
||||
use crate::protocol::{BoundedInt, ByteAngle, Nbt, RawBytes, VarInt};
|
||||
use crate::server::C2sPacketChannels;
|
||||
|
@ -106,17 +109,12 @@ pub struct Client {
|
|||
username: String,
|
||||
textures: Option<SignedPlayerTextures>,
|
||||
world: WorldId,
|
||||
spawn: bool,
|
||||
on_ground: bool,
|
||||
new_position: Vec3<f64>,
|
||||
old_position: Vec3<f64>,
|
||||
/// Measured in degrees
|
||||
yaw: f32,
|
||||
/// Measured in degrees
|
||||
pitch: f32,
|
||||
/// If any of position, yaw, or pitch were modified by the
|
||||
/// user this tick.
|
||||
teleported_this_tick: bool,
|
||||
/// Counts up as teleports are made.
|
||||
teleport_id_counter: u32,
|
||||
/// The number of pending client teleports that have yet to receive a
|
||||
|
@ -125,14 +123,10 @@ pub struct Client {
|
|||
pending_teleports: u32,
|
||||
spawn_position: BlockPos,
|
||||
spawn_position_yaw: f32,
|
||||
/// If spawn_position or spawn_position_yaw were modified this tick.
|
||||
modified_spawn_position: bool,
|
||||
death_location: Option<(DimensionId, BlockPos)>,
|
||||
events: VecDeque<ClientEvent>,
|
||||
/// The ID of the last keepalive sent.
|
||||
last_keepalive_id: i64,
|
||||
/// If the last sent keepalive got a response.
|
||||
got_keepalive: bool,
|
||||
new_max_view_distance: u8,
|
||||
old_max_view_distance: u8,
|
||||
/// Entities that were visible to this client at the end of the last tick.
|
||||
|
@ -146,10 +140,27 @@ pub struct Client {
|
|||
dug_blocks: Vec<i32>,
|
||||
msgs_to_send: Vec<Text>,
|
||||
entity_events: Vec<EntityEvent>,
|
||||
flags: ClientFlags,
|
||||
/// The metadata for the client's own player entity.
|
||||
player_meta: Player,
|
||||
}
|
||||
|
||||
#[bitfield(u8)]
|
||||
pub(crate) struct ClientFlags {
|
||||
spawn: bool,
|
||||
sneaking: bool,
|
||||
sprinting: bool,
|
||||
jumping_with_horse: bool,
|
||||
on_ground: bool,
|
||||
/// If any of position, yaw, or pitch were modified by the
|
||||
/// user this tick.
|
||||
teleported_this_tick: bool,
|
||||
/// If spawn_position or spawn_position_yaw were modified this tick.
|
||||
modified_spawn_position: bool,
|
||||
/// If the last sent keepalive got a response.
|
||||
got_keepalive: bool,
|
||||
}
|
||||
|
||||
impl Client {
|
||||
pub(crate) fn new(
|
||||
packet_channels: C2sPacketChannels,
|
||||
|
@ -166,22 +177,17 @@ impl Client {
|
|||
username: ncd.username,
|
||||
textures: ncd.textures,
|
||||
world: WorldId::default(),
|
||||
spawn: false,
|
||||
on_ground: false,
|
||||
new_position: Vec3::default(),
|
||||
old_position: Vec3::default(),
|
||||
yaw: 0.0,
|
||||
pitch: 0.0,
|
||||
teleported_this_tick: false,
|
||||
teleport_id_counter: 0,
|
||||
pending_teleports: 0,
|
||||
spawn_position: BlockPos::default(),
|
||||
spawn_position_yaw: 0.0,
|
||||
modified_spawn_position: true,
|
||||
death_location: None,
|
||||
events: VecDeque::new(),
|
||||
last_keepalive_id: 0,
|
||||
got_keepalive: true,
|
||||
new_max_view_distance: 16,
|
||||
old_max_view_distance: 0,
|
||||
loaded_entities: HashSet::new(),
|
||||
|
@ -192,6 +198,9 @@ impl Client {
|
|||
dug_blocks: Vec::new(),
|
||||
msgs_to_send: Vec::new(),
|
||||
entity_events: Vec::new(),
|
||||
flags: ClientFlags::new()
|
||||
.with_modified_spawn_position(true)
|
||||
.with_got_keepalive(true),
|
||||
player_meta: Player::new(),
|
||||
}
|
||||
}
|
||||
|
@ -218,7 +227,7 @@ impl Client {
|
|||
|
||||
pub fn spawn(&mut self, world: WorldId) {
|
||||
self.world = world;
|
||||
self.spawn = true;
|
||||
self.flags.set_spawn(true);
|
||||
}
|
||||
|
||||
/// Sends a system message to the player.
|
||||
|
@ -236,8 +245,8 @@ impl Client {
|
|||
self.yaw = yaw;
|
||||
self.pitch = pitch;
|
||||
|
||||
if !self.teleported_this_tick {
|
||||
self.teleported_this_tick = true;
|
||||
if !self.flags.teleported_this_tick() {
|
||||
self.flags.set_teleported_this_tick(true);
|
||||
|
||||
self.pending_teleports = match self.pending_teleports.checked_add(1) {
|
||||
Some(n) => n,
|
||||
|
@ -273,7 +282,7 @@ impl Client {
|
|||
if pos != self.spawn_position || yaw_degrees != self.spawn_position_yaw {
|
||||
self.spawn_position = pos;
|
||||
self.spawn_position_yaw = yaw_degrees;
|
||||
self.modified_spawn_position = true;
|
||||
self.flags.set_modified_spawn_position(true);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -305,7 +314,7 @@ impl Client {
|
|||
}
|
||||
|
||||
pub fn on_ground(&self) -> bool {
|
||||
self.on_ground
|
||||
self.flags.on_ground()
|
||||
}
|
||||
|
||||
pub fn is_disconnected(&self) -> bool {
|
||||
|
@ -396,13 +405,13 @@ impl Client {
|
|||
position: client.new_position,
|
||||
yaw: client.yaw,
|
||||
pitch: client.pitch,
|
||||
on_ground: client.on_ground,
|
||||
on_ground: client.flags.on_ground(),
|
||||
};
|
||||
|
||||
client.new_position = new_position;
|
||||
client.yaw = new_yaw;
|
||||
client.pitch = new_pitch;
|
||||
client.on_ground = new_on_ground;
|
||||
client.flags.set_on_ground(new_on_ground);
|
||||
|
||||
client.events.push_back(event);
|
||||
}
|
||||
|
@ -481,7 +490,7 @@ impl Client {
|
|||
C2sPlayPacket::JigsawGenerate(_) => {}
|
||||
C2sPlayPacket::KeepAlive(p) => {
|
||||
let last_keepalive_id = self.last_keepalive_id;
|
||||
if self.got_keepalive {
|
||||
if self.flags.got_keepalive() {
|
||||
log::warn!("unexpected keepalive from player {}", self.username());
|
||||
self.disconnect_no_reason();
|
||||
} else if p.id != last_keepalive_id {
|
||||
|
@ -493,7 +502,7 @@ impl Client {
|
|||
);
|
||||
self.disconnect_no_reason();
|
||||
} else {
|
||||
self.got_keepalive = true;
|
||||
self.flags.set_got_keepalive(true);
|
||||
}
|
||||
}
|
||||
C2sPlayPacket::LockDifficulty(_) => {}
|
||||
|
@ -515,7 +524,14 @@ impl Client {
|
|||
p.on_ground,
|
||||
),
|
||||
C2sPlayPacket::MoveVehicle(p) => {
|
||||
handle_movement_packet(self, true, p.position, p.yaw, p.pitch, self.on_ground);
|
||||
handle_movement_packet(
|
||||
self,
|
||||
true,
|
||||
p.position,
|
||||
p.yaw,
|
||||
p.pitch,
|
||||
self.flags.on_ground(),
|
||||
);
|
||||
}
|
||||
C2sPlayPacket::PaddleBoat(p) => {
|
||||
self.events.push_back(ClientEvent::SteerBoat {
|
||||
|
@ -556,7 +572,43 @@ impl Client {
|
|||
DiggingStatus::SwapItemInHand => return,
|
||||
});
|
||||
}
|
||||
C2sPlayPacket::PlayerCommand(_) => {}
|
||||
C2sPlayPacket::PlayerCommand(e) => {
|
||||
// TODO: validate:
|
||||
// - Can't sprint and sneak at the same time
|
||||
// - Can't leave bed while not in a bed.
|
||||
// - Can't jump with a horse if not on a horse
|
||||
// - Can't open horse inventory if not on a horse.
|
||||
// - Can't fly with elytra if not wearing an elytra.
|
||||
self.events.push_back(match e.action_id {
|
||||
PlayerCommandId::StartSneaking => {
|
||||
self.flags.set_sneaking(true);
|
||||
ClientEvent::StartSneaking
|
||||
}
|
||||
PlayerCommandId::StopSneaking => {
|
||||
self.flags.set_sneaking(false);
|
||||
ClientEvent::StopSneaking
|
||||
}
|
||||
PlayerCommandId::LeaveBed => ClientEvent::LeaveBed,
|
||||
PlayerCommandId::StartSprinting => {
|
||||
self.flags.set_sprinting(true);
|
||||
ClientEvent::StartSprinting
|
||||
}
|
||||
PlayerCommandId::StopSprinting => {
|
||||
self.flags.set_sprinting(false);
|
||||
ClientEvent::StopSprinting
|
||||
}
|
||||
PlayerCommandId::StartJumpWithHorse => {
|
||||
self.flags.set_jumping_with_horse(true);
|
||||
ClientEvent::StartJumpWithHorse(e.jump_boost.0 .0 as u8)
|
||||
}
|
||||
PlayerCommandId::StopJumpWithHorse => {
|
||||
self.flags.set_jumping_with_horse(false);
|
||||
ClientEvent::StopJumpWithHorse
|
||||
}
|
||||
PlayerCommandId::OpenHorseInventory => ClientEvent::OpenHorseInventory,
|
||||
PlayerCommandId::StartFlyingWithElytra => ClientEvent::StartFlyingWithElytra,
|
||||
});
|
||||
}
|
||||
C2sPlayPacket::PlayerInput(_) => {}
|
||||
C2sPlayPacket::Pong(_) => {}
|
||||
C2sPlayPacket::RecipeBookChangeSettings(_) => {}
|
||||
|
@ -646,7 +698,7 @@ impl Client {
|
|||
|
||||
self.teleport(self.position(), self.yaw(), self.pitch());
|
||||
} else {
|
||||
if self.spawn {
|
||||
if self.flags.spawn() {
|
||||
self.loaded_entities.clear();
|
||||
self.loaded_chunks.clear();
|
||||
|
||||
|
@ -703,8 +755,8 @@ impl Client {
|
|||
}
|
||||
|
||||
// Update the players spawn position (compass position)
|
||||
if self.modified_spawn_position {
|
||||
self.modified_spawn_position = false;
|
||||
if self.flags.modified_spawn_position() {
|
||||
self.flags.set_modified_spawn_position(false);
|
||||
|
||||
self.send_packet(SpawnPosition {
|
||||
location: self.spawn_position,
|
||||
|
@ -724,11 +776,11 @@ impl Client {
|
|||
|
||||
// Check if it's time to send another keepalive.
|
||||
if current_tick % (shared.tick_rate() * 8) == 0 {
|
||||
if self.got_keepalive {
|
||||
if self.flags.got_keepalive() {
|
||||
let id = rand::random();
|
||||
self.send_packet(KeepAlive { id });
|
||||
self.last_keepalive_id = id;
|
||||
self.got_keepalive = false;
|
||||
self.flags.set_got_keepalive(false);
|
||||
} else {
|
||||
log::warn!(
|
||||
"player {} timed out (no keepalive response)",
|
||||
|
@ -808,8 +860,8 @@ impl Client {
|
|||
|
||||
// This is done after the chunks are loaded so that the "downloading terrain"
|
||||
// screen is closed at the appropriate time.
|
||||
if self.teleported_this_tick {
|
||||
self.teleported_this_tick = false;
|
||||
if self.flags.teleported_this_tick() {
|
||||
self.flags.set_teleported_this_tick(false);
|
||||
|
||||
self.send_packet(PlayerPosition {
|
||||
position: self.new_position,
|
||||
|
@ -922,7 +974,7 @@ impl Client {
|
|||
for &e in entity.events() {
|
||||
send_packet(
|
||||
&mut self.send,
|
||||
EntityEventPacket {
|
||||
DoEntityEvent {
|
||||
entity_id: id.to_network_id(),
|
||||
entity_status: e,
|
||||
},
|
||||
|
@ -982,7 +1034,7 @@ impl Client {
|
|||
for &e in entity.events() {
|
||||
send_packet(
|
||||
&mut self.send,
|
||||
EntityEventPacket {
|
||||
DoEntityEvent {
|
||||
entity_id: id.to_network_id(),
|
||||
entity_status: e,
|
||||
},
|
||||
|
@ -997,7 +1049,7 @@ impl Client {
|
|||
for event in self.entity_events.drain(..) {
|
||||
send_packet(
|
||||
&mut self.send,
|
||||
EntityEventPacket {
|
||||
DoEntityEvent {
|
||||
entity_id: 0,
|
||||
entity_status: event,
|
||||
},
|
||||
|
@ -1005,7 +1057,7 @@ impl Client {
|
|||
}
|
||||
|
||||
self.old_position = self.new_position;
|
||||
self.spawn = false;
|
||||
self.flags.set_spawn(false);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -24,6 +24,15 @@ pub enum ClientEvent {
|
|||
pitch: f32,
|
||||
on_ground: bool,
|
||||
},
|
||||
StartSneaking,
|
||||
StopSneaking,
|
||||
StartSprinting,
|
||||
StopSprinting,
|
||||
StartJumpWithHorse(u8),
|
||||
StopJumpWithHorse,
|
||||
LeaveBed,
|
||||
OpenHorseInventory,
|
||||
StartFlyingWithElytra,
|
||||
ArmSwing(Hand),
|
||||
InteractWithEntity {
|
||||
/// The ID of the entity being interacted with.
|
||||
|
|
|
@ -12,7 +12,7 @@ pub use types::{EntityMeta, EntityType};
|
|||
use uuid::Uuid;
|
||||
use vek::{Aabb, Vec3};
|
||||
|
||||
pub use crate::protocol::packets::play::s2c::EntityEventStatus as EntityEvent;
|
||||
pub use crate::protocol::packets::play::s2c::EntityEvent as EntityEvent;
|
||||
use crate::protocol::packets::play::s2c::{
|
||||
AddEntity, AddExperienceOrb, AddPlayer, S2cPlayPacket, SetEntityMetadata,
|
||||
};
|
||||
|
@ -39,7 +39,7 @@ impl Entities {
|
|||
/// Spawns a new entity with the default data. The new entity's [`EntityId`]
|
||||
/// is returned.
|
||||
pub fn create(&mut self, typ: EntityType) -> (EntityId, &mut Entity) {
|
||||
self.create_with_uuid(Uuid::from_bytes(rand::random()), typ)
|
||||
self.create_with_uuid(typ, Uuid::from_bytes(rand::random()))
|
||||
.expect("UUID collision")
|
||||
}
|
||||
|
||||
|
@ -50,8 +50,8 @@ impl Entities {
|
|||
/// world. If it does, `None` is returned and the entity is not spawned.
|
||||
pub fn create_with_uuid(
|
||||
&mut self,
|
||||
uuid: Uuid,
|
||||
typ: EntityType,
|
||||
uuid: Uuid,
|
||||
) -> Option<(EntityId, &mut Entity)> {
|
||||
match self.uuid_to_entity.entry(uuid) {
|
||||
Entry::Occupied(_) => None,
|
||||
|
|
|
@ -742,15 +742,15 @@ pub mod play {
|
|||
}
|
||||
|
||||
def_struct! {
|
||||
EntityEvent 0x18 {
|
||||
DoEntityEvent 0x18 {
|
||||
entity_id: i32,
|
||||
entity_status: EntityEventStatus,
|
||||
entity_status: EntityEvent,
|
||||
}
|
||||
}
|
||||
|
||||
def_enum! {
|
||||
#[derive(Copy, PartialEq, Eq)]
|
||||
EntityEventStatus: u8 {
|
||||
EntityEvent: u8 {
|
||||
Jump = 1,
|
||||
Hurt = 2,
|
||||
Death = 3,
|
||||
|
@ -1294,7 +1294,7 @@ pub mod play {
|
|||
BlockUpdate,
|
||||
BossEvent,
|
||||
Disconnect,
|
||||
EntityEvent,
|
||||
DoEntityEvent,
|
||||
ForgetLevelChunk,
|
||||
GameEvent,
|
||||
KeepAlive,
|
||||
|
|
Loading…
Reference in a new issue