From 46999f59bb547efddea092e61ece379d31e59cfe Mon Sep 17 00:00:00 2001 From: ice_iix Date: Tue, 11 Dec 2018 18:18:25 -0800 Subject: [PATCH] Add protocol version global mutable to merge Metadata18/19 into one Cleans up https://github.com/iceiix/steven/pull/57 1.8.9 (47) multiprotocol support which added too much code duplication, Metadata19 vs Metadata18, and different packets for each, the only difference being how it was parsed. Instead, switch back to using only one Metadata implementation, but with parsing conditionalized on a new global mutable: SUPPORTED_PROTOCOL_VERSION. Accessing global mutable state is unsafe but it is only set when initializing the connection, and only read when deep enough in the code where it is not feasible to pass otherwise. More elegant, worth it. --- src/protocol/mod.rs | 6 ++ src/protocol/packet.rs | 24 +++---- src/protocol/versions/v1_8_9.rs | 6 +- src/server/mod.rs | 4 +- src/types/metadata.rs | 118 +++++++++++++------------------- 5 files changed, 70 insertions(+), 88 deletions(-) diff --git a/src/protocol/mod.rs b/src/protocol/mod.rs index 3fcc23d..d78ac15 100644 --- a/src/protocol/mod.rs +++ b/src/protocol/mod.rs @@ -39,6 +39,8 @@ use crate::shared::Position; pub const SUPPORTED_PROTOCOLS: [i32; 8] = [340, 316, 315, 210, 109, 107, 74, 47]; +// TODO: switch to using thread_local storage?, see https://doc.rust-lang.org/std/macro.thread_local.html +pub static mut CURRENT_PROTOCOL_VERSION: i32 = SUPPORTED_PROTOCOLS[0]; /// Helper macro for defining packets #[macro_export] @@ -805,6 +807,10 @@ pub struct Conn { impl Conn { pub fn new(target: &str, protocol_version: i32) -> Result { + unsafe { + CURRENT_PROTOCOL_VERSION = protocol_version; + } + // TODO SRV record support let mut parts = target.split(':').collect::>(); let address = if parts.len() == 1 { diff --git a/src/protocol/packet.rs b/src/protocol/packet.rs index bf87440..5006d13 100644 --- a/src/protocol/packet.rs +++ b/src/protocol/packet.rs @@ -417,7 +417,7 @@ state_packets!( field velocity_x: i16 =, field velocity_y: i16 =, field velocity_z: i16 =, - field metadata: types::Metadata19 =, + field metadata: types::Metadata =, } packet SpawnMob_u8 { field entity_id: VarInt =, @@ -432,7 +432,7 @@ state_packets!( field velocity_x: i16 =, field velocity_y: i16 =, field velocity_z: i16 =, - field metadata: types::Metadata19 =, + field metadata: types::Metadata =, } packet SpawnMob_u8_i32 { field entity_id: VarInt =, @@ -447,9 +447,9 @@ state_packets!( field velocity_x: i16 =, field velocity_y: i16 =, field velocity_z: i16 =, - field metadata: types::Metadata19 =, + field metadata: types::Metadata =, } - packet SpawnMob_u8_i32_NoUUID_18 { + packet SpawnMob_u8_i32_NoUUID { field entity_id: VarInt =, field ty: u8 =, field x: i32 =, @@ -461,7 +461,7 @@ state_packets!( field velocity_x: i16 =, field velocity_y: i16 =, field velocity_z: i16 =, - field metadata: types::Metadata18 =, + field metadata: types::Metadata =, } /// SpawnPainting spawns a painting into the world when it is in range of /// the client. The title effects the size and the texture of the painting. @@ -489,7 +489,7 @@ state_packets!( field z: f64 =, field yaw: i8 =, field pitch: i8 =, - field metadata: types::Metadata19 =, + field metadata: types::Metadata =, } packet SpawnPlayer_i32 { field entity_id: VarInt =, @@ -499,9 +499,9 @@ state_packets!( field z: i32 =, field yaw: i8 =, field pitch: i8 =, - field metadata: types::Metadata19 =, + field metadata: types::Metadata =, } - packet SpawnPlayer_i32_HeldItem_18 { + packet SpawnPlayer_i32_HeldItem { field entity_id: VarInt =, field uuid: UUID =, field x: i32 =, @@ -510,7 +510,7 @@ state_packets!( field yaw: i8 =, field pitch: i8 =, field current_item: u16 =, - field metadata: types::Metadata18 =, + field metadata: types::Metadata =, } /// Animation is sent by the server to play an animation on a specific entity. packet Animation { @@ -1010,11 +1010,7 @@ state_packets!( /// EntityMetadata updates the metadata for an entity. packet EntityMetadata { field entity_id: VarInt =, - field metadata: types::Metadata19 =, - } - packet EntityMetadata_18 { - field entity_id: VarInt =, - field metadata: types::Metadata18 =, + field metadata: types::Metadata =, } /// EntityAttach attaches to entities together, either by mounting or leashing. /// -1 can be used at the EntityID to deattach. diff --git a/src/protocol/versions/v1_8_9.rs b/src/protocol/versions/v1_8_9.rs index 72d55ff..362b93c 100644 --- a/src/protocol/versions/v1_8_9.rs +++ b/src/protocol/versions/v1_8_9.rs @@ -48,10 +48,10 @@ protocol_packet_ids!( 0x09 => SetCurrentHotbarSlot 0x0a => EntityUsedBed 0x0b => Animation - 0x0c => SpawnPlayer_i32_HeldItem_18 + 0x0c => SpawnPlayer_i32_HeldItem 0x0d => CollectItem_nocount 0x0e => SpawnObject_i32_NoUUID - 0x0f => SpawnMob_u8_i32_NoUUID_18 + 0x0f => SpawnMob_u8_i32_NoUUID 0x10 => SpawnPainting_NoUUID 0x11 => SpawnExperienceOrb_i32 0x12 => EntityVelocity @@ -64,7 +64,7 @@ protocol_packet_ids!( 0x19 => EntityHeadLook 0x1a => EntityStatus 0x1b => EntityAttach_leashed - 0x1c => EntityMetadata_18 + 0x1c => EntityMetadata 0x1d => EntityEffect 0x1e => EntityRemoveEffect 0x1f => SetExperience diff --git a/src/server/mod.rs b/src/server/mod.rs index f6d4f28..ca147e8 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -399,7 +399,7 @@ impl Server { EntityDestroy => on_entity_destroy, SpawnPlayer_f64 => on_player_spawn_f64, SpawnPlayer_i32 => on_player_spawn_i32, - SpawnPlayer_i32_HeldItem_18 => on_player_spawn_i32_helditem_18, + SpawnPlayer_i32_HeldItem => on_player_spawn_i32_helditem, EntityTeleport_f64 => on_entity_teleport_f64, EntityTeleport_i32 => on_entity_teleport_i32, EntityMove_i16 => on_entity_move_i16, @@ -752,7 +752,7 @@ impl Server { self.on_player_spawn(spawn.entity_id.0, spawn.uuid, spawn.x as f64, spawn.y as f64, spawn.z as f64, spawn.yaw as f64, spawn.pitch as f64) } - fn on_player_spawn_i32_helditem_18(&mut self, spawn: packet::play::clientbound::SpawnPlayer_i32_HeldItem_18) { + fn on_player_spawn_i32_helditem(&mut self, spawn: packet::play::clientbound::SpawnPlayer_i32_HeldItem) { self.on_player_spawn(spawn.entity_id.0, spawn.uuid, spawn.x as f64, spawn.y as f64, spawn.z as f64, spawn.yaw as f64, spawn.pitch as f64) } diff --git a/src/types/metadata.rs b/src/types/metadata.rs index 498cd01..44ee0a7 100644 --- a/src/types/metadata.rs +++ b/src/types/metadata.rs @@ -38,66 +38,28 @@ impl MetadataKey { } } -pub struct Metadata18 { +pub struct Metadata { map: HashMap, } -pub struct Metadata19 { - map: HashMap, -} - -trait MetadataBase: fmt::Debug + Default { - fn map(&self) -> &HashMap; - fn map_mut(&mut self) -> &mut HashMap; - - fn get(&self, key: &MetadataKey) -> Option<&T> { - self.map().get(&key.index).map(T::unwrap) +impl Metadata { + pub fn new() -> Metadata { + Metadata { map: HashMap::new() } } - fn put(&mut self, key: &MetadataKey, val: T) { - self.map_mut().insert(key.index, val.wrap()); + pub fn get(&self, key: &MetadataKey) -> Option<&T> { + self.map.get(&key.index).map(T::unwrap) + } + + pub fn put(&mut self, key: &MetadataKey, val: T) { + self.map.insert(key.index, val.wrap()); } fn put_raw(&mut self, index: i32, val: T) { - self.map_mut().insert(index, val.wrap()); + self.map.insert(index, val.wrap()); } - fn fmt2(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "Metadata[ ")?; - for (k, v) in self.map() { - write!(f, "{:?}={:?}, ", k, v)?; - } - write!(f, "]") - } -} - -impl MetadataBase for Metadata18 { - fn map(&self) -> &HashMap { &self.map } - fn map_mut(&mut self) -> &mut HashMap { &mut self.map } -} - -impl MetadataBase for Metadata19 { - fn map(&self) -> &HashMap { &self.map } - fn map_mut(&mut self) -> &mut HashMap { &mut self.map } -} - -impl Metadata18 { - pub fn new() -> Self { - Self { map: HashMap::new() } - } -} - -impl Metadata19 { - pub fn new() -> Self { - Self { map: HashMap::new() } - } -} - - - -impl Serializable for Metadata18 { - - fn read_from(buf: &mut R) -> Result { + fn read_from18(buf: &mut R) -> Result { let mut m = Self::new(); loop { let ty_index = u8::read_from(buf)? as i32; @@ -128,7 +90,7 @@ impl Serializable for Metadata18 { Ok(m) } - fn write_to(&self, buf: &mut W) -> Result<(), protocol::Error> { + fn write_to18(&self, buf: &mut W) -> Result<(), protocol::Error> { for (k, v) in &self.map { if (*k as u8) > 0x1f { panic!("write metadata index {:x} > 0x1f", *k as u8); @@ -177,9 +139,7 @@ impl Serializable for Metadata18 { val[2].write_to(buf)?; } - Value::FormatComponent(_) | Value::Bool(_) | Value::Position(_) | - Value::OptionalPosition(_) | Value::Direction(_) | Value::OptionalUUID(_) | - Value::Block(_) | Value::NBTTag(_) => { + _ => { panic!("attempted to write 1.9+ metadata to 1.8"); } } @@ -187,11 +147,8 @@ impl Serializable for Metadata18 { u8::write_to(&0x7f, buf)?; Ok(()) } -} -impl Serializable for Metadata19 { - - fn read_from(buf: &mut R) -> Result { + fn read_from19(buf: &mut R) -> Result { let mut m = Self::new(); loop { let index = u8::read_from(buf)? as i32; @@ -243,7 +200,7 @@ impl Serializable for Metadata19 { Ok(m) } - fn write_to(&self, buf: &mut W) -> Result<(), protocol::Error> { + fn write_to19(&self, buf: &mut W) -> Result<(), protocol::Error> { for (k, v) in &self.map { (*k as u8).write_to(buf)?; match *v { @@ -316,18 +273,41 @@ impl Serializable for Metadata19 { } } -// TODO: is it possible to implement these traits on MetadataBase instead? -impl fmt::Debug for Metadata19 { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.fmt2(f) } } -impl fmt::Debug for Metadata18 { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.fmt2(f) } } +impl Serializable for Metadata { + fn read_from(buf: &mut R) -> Result { + let protocol_version = unsafe { protocol::CURRENT_PROTOCOL_VERSION }; -impl Default for Metadata19 { - fn default() -> Self { - Self::new() + if protocol_version >= 74 { + Metadata::read_from19(buf) + } else { + Metadata::read_from18(buf) + } + } + + fn write_to(&self, buf: &mut W) -> Result<(), protocol::Error> { + let protocol_version = unsafe { protocol::CURRENT_PROTOCOL_VERSION }; + + if protocol_version >= 74 { + self.write_to19(buf) + } else { + self.write_to18(buf) + } } } -impl Default for Metadata18 { - fn default() -> Self { - Self::new() + +impl fmt::Debug for Metadata { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "Metadata[ ")?; + for (k, v) in &self.map { + write!(f, "{:?}={:?}, ", k, v)?; + } + write!(f, "]") + } +} + +impl Default for Metadata { + fn default() -> Metadata { + Metadata::new() } } @@ -561,7 +541,7 @@ mod test { #[test] fn basic() { - let mut m = Metadata19::new(); + let mut m = Metadata::new(); m.put(&TEST, "Hello world".to_owned());