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.
This commit is contained in:
ice_iix 2018-12-11 18:18:25 -08:00
parent 1cb671093d
commit 46999f59bb
5 changed files with 70 additions and 88 deletions

View File

@ -39,6 +39,8 @@ use crate::shared::Position;
pub const SUPPORTED_PROTOCOLS: [i32; 8] = [340, 316, 315, 210, 109, 107, 74, 47]; 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 /// Helper macro for defining packets
#[macro_export] #[macro_export]
@ -805,6 +807,10 @@ pub struct Conn {
impl Conn { impl Conn {
pub fn new(target: &str, protocol_version: i32) -> Result<Conn, Error> { pub fn new(target: &str, protocol_version: i32) -> Result<Conn, Error> {
unsafe {
CURRENT_PROTOCOL_VERSION = protocol_version;
}
// TODO SRV record support // TODO SRV record support
let mut parts = target.split(':').collect::<Vec<&str>>(); let mut parts = target.split(':').collect::<Vec<&str>>();
let address = if parts.len() == 1 { let address = if parts.len() == 1 {

View File

@ -417,7 +417,7 @@ state_packets!(
field velocity_x: i16 =, field velocity_x: i16 =,
field velocity_y: i16 =, field velocity_y: i16 =,
field velocity_z: i16 =, field velocity_z: i16 =,
field metadata: types::Metadata19 =, field metadata: types::Metadata =,
} }
packet SpawnMob_u8 { packet SpawnMob_u8 {
field entity_id: VarInt =, field entity_id: VarInt =,
@ -432,7 +432,7 @@ state_packets!(
field velocity_x: i16 =, field velocity_x: i16 =,
field velocity_y: i16 =, field velocity_y: i16 =,
field velocity_z: i16 =, field velocity_z: i16 =,
field metadata: types::Metadata19 =, field metadata: types::Metadata =,
} }
packet SpawnMob_u8_i32 { packet SpawnMob_u8_i32 {
field entity_id: VarInt =, field entity_id: VarInt =,
@ -447,9 +447,9 @@ state_packets!(
field velocity_x: i16 =, field velocity_x: i16 =,
field velocity_y: i16 =, field velocity_y: i16 =,
field velocity_z: 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 entity_id: VarInt =,
field ty: u8 =, field ty: u8 =,
field x: i32 =, field x: i32 =,
@ -461,7 +461,7 @@ state_packets!(
field velocity_x: i16 =, field velocity_x: i16 =,
field velocity_y: i16 =, field velocity_y: i16 =,
field velocity_z: 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 /// 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. /// the client. The title effects the size and the texture of the painting.
@ -489,7 +489,7 @@ state_packets!(
field z: f64 =, field z: f64 =,
field yaw: i8 =, field yaw: i8 =,
field pitch: i8 =, field pitch: i8 =,
field metadata: types::Metadata19 =, field metadata: types::Metadata =,
} }
packet SpawnPlayer_i32 { packet SpawnPlayer_i32 {
field entity_id: VarInt =, field entity_id: VarInt =,
@ -499,9 +499,9 @@ state_packets!(
field z: i32 =, field z: i32 =,
field yaw: i8 =, field yaw: i8 =,
field pitch: 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 entity_id: VarInt =,
field uuid: UUID =, field uuid: UUID =,
field x: i32 =, field x: i32 =,
@ -510,7 +510,7 @@ state_packets!(
field yaw: i8 =, field yaw: i8 =,
field pitch: i8 =, field pitch: i8 =,
field current_item: u16 =, 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. /// Animation is sent by the server to play an animation on a specific entity.
packet Animation { packet Animation {
@ -1010,11 +1010,7 @@ state_packets!(
/// EntityMetadata updates the metadata for an entity. /// EntityMetadata updates the metadata for an entity.
packet EntityMetadata { packet EntityMetadata {
field entity_id: VarInt =, field entity_id: VarInt =,
field metadata: types::Metadata19 =, field metadata: types::Metadata =,
}
packet EntityMetadata_18 {
field entity_id: VarInt =,
field metadata: types::Metadata18 =,
} }
/// EntityAttach attaches to entities together, either by mounting or leashing. /// EntityAttach attaches to entities together, either by mounting or leashing.
/// -1 can be used at the EntityID to deattach. /// -1 can be used at the EntityID to deattach.

View File

@ -48,10 +48,10 @@ protocol_packet_ids!(
0x09 => SetCurrentHotbarSlot 0x09 => SetCurrentHotbarSlot
0x0a => EntityUsedBed 0x0a => EntityUsedBed
0x0b => Animation 0x0b => Animation
0x0c => SpawnPlayer_i32_HeldItem_18 0x0c => SpawnPlayer_i32_HeldItem
0x0d => CollectItem_nocount 0x0d => CollectItem_nocount
0x0e => SpawnObject_i32_NoUUID 0x0e => SpawnObject_i32_NoUUID
0x0f => SpawnMob_u8_i32_NoUUID_18 0x0f => SpawnMob_u8_i32_NoUUID
0x10 => SpawnPainting_NoUUID 0x10 => SpawnPainting_NoUUID
0x11 => SpawnExperienceOrb_i32 0x11 => SpawnExperienceOrb_i32
0x12 => EntityVelocity 0x12 => EntityVelocity
@ -64,7 +64,7 @@ protocol_packet_ids!(
0x19 => EntityHeadLook 0x19 => EntityHeadLook
0x1a => EntityStatus 0x1a => EntityStatus
0x1b => EntityAttach_leashed 0x1b => EntityAttach_leashed
0x1c => EntityMetadata_18 0x1c => EntityMetadata
0x1d => EntityEffect 0x1d => EntityEffect
0x1e => EntityRemoveEffect 0x1e => EntityRemoveEffect
0x1f => SetExperience 0x1f => SetExperience

View File

@ -399,7 +399,7 @@ impl Server {
EntityDestroy => on_entity_destroy, EntityDestroy => on_entity_destroy,
SpawnPlayer_f64 => on_player_spawn_f64, SpawnPlayer_f64 => on_player_spawn_f64,
SpawnPlayer_i32 => on_player_spawn_i32, 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_f64 => on_entity_teleport_f64,
EntityTeleport_i32 => on_entity_teleport_i32, EntityTeleport_i32 => on_entity_teleport_i32,
EntityMove_i16 => on_entity_move_i16, 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) 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) 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)
} }

View File

@ -38,66 +38,28 @@ impl <T: MetaValue> MetadataKey<T> {
} }
} }
pub struct Metadata18 { pub struct Metadata {
map: HashMap<i32, Value>, map: HashMap<i32, Value>,
} }
pub struct Metadata19 { impl Metadata {
map: HashMap<i32, Value>, pub fn new() -> Metadata {
} Metadata { map: HashMap::new() }
trait MetadataBase: fmt::Debug + Default {
fn map(&self) -> &HashMap<i32, Value>;
fn map_mut(&mut self) -> &mut HashMap<i32, Value>;
fn get<T: MetaValue>(&self, key: &MetadataKey<T>) -> Option<&T> {
self.map().get(&key.index).map(T::unwrap)
} }
fn put<T: MetaValue>(&mut self, key: &MetadataKey<T>, val: T) { pub fn get<T: MetaValue>(&self, key: &MetadataKey<T>) -> Option<&T> {
self.map_mut().insert(key.index, val.wrap()); self.map.get(&key.index).map(T::unwrap)
}
pub fn put<T: MetaValue>(&mut self, key: &MetadataKey<T>, val: T) {
self.map.insert(key.index, val.wrap());
} }
fn put_raw<T: MetaValue>(&mut self, index: i32, val: T) { fn put_raw<T: MetaValue>(&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 { fn read_from18<R: io::Read>(buf: &mut R) -> Result<Self, protocol::Error> {
write!(f, "Metadata[ ")?;
for (k, v) in self.map() {
write!(f, "{:?}={:?}, ", k, v)?;
}
write!(f, "]")
}
}
impl MetadataBase for Metadata18 {
fn map(&self) -> &HashMap<i32, Value> { &self.map }
fn map_mut(&mut self) -> &mut HashMap<i32, Value> { &mut self.map }
}
impl MetadataBase for Metadata19 {
fn map(&self) -> &HashMap<i32, Value> { &self.map }
fn map_mut(&mut self) -> &mut HashMap<i32, Value> { &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<R: io::Read>(buf: &mut R) -> Result<Self, protocol::Error> {
let mut m = Self::new(); let mut m = Self::new();
loop { loop {
let ty_index = u8::read_from(buf)? as i32; let ty_index = u8::read_from(buf)? as i32;
@ -128,7 +90,7 @@ impl Serializable for Metadata18 {
Ok(m) Ok(m)
} }
fn write_to<W: io::Write>(&self, buf: &mut W) -> Result<(), protocol::Error> { fn write_to18<W: io::Write>(&self, buf: &mut W) -> Result<(), protocol::Error> {
for (k, v) in &self.map { for (k, v) in &self.map {
if (*k as u8) > 0x1f { if (*k as u8) > 0x1f {
panic!("write metadata index {:x} > 0x1f", *k as u8); panic!("write metadata index {:x} > 0x1f", *k as u8);
@ -177,9 +139,7 @@ impl Serializable for Metadata18 {
val[2].write_to(buf)?; 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"); panic!("attempted to write 1.9+ metadata to 1.8");
} }
} }
@ -187,11 +147,8 @@ impl Serializable for Metadata18 {
u8::write_to(&0x7f, buf)?; u8::write_to(&0x7f, buf)?;
Ok(()) Ok(())
} }
}
impl Serializable for Metadata19 { fn read_from19<R: io::Read>(buf: &mut R) -> Result<Self, protocol::Error> {
fn read_from<R: io::Read>(buf: &mut R) -> Result<Self, protocol::Error> {
let mut m = Self::new(); let mut m = Self::new();
loop { loop {
let index = u8::read_from(buf)? as i32; let index = u8::read_from(buf)? as i32;
@ -243,7 +200,7 @@ impl Serializable for Metadata19 {
Ok(m) Ok(m)
} }
fn write_to<W: io::Write>(&self, buf: &mut W) -> Result<(), protocol::Error> { fn write_to19<W: io::Write>(&self, buf: &mut W) -> Result<(), protocol::Error> {
for (k, v) in &self.map { for (k, v) in &self.map {
(*k as u8).write_to(buf)?; (*k as u8).write_to(buf)?;
match *v { match *v {
@ -316,18 +273,41 @@ impl Serializable for Metadata19 {
} }
} }
// TODO: is it possible to implement these traits on MetadataBase instead? impl Serializable for Metadata {
impl fmt::Debug for Metadata19 { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.fmt2(f) } } fn read_from<R: io::Read>(buf: &mut R) -> Result<Self, protocol::Error> {
impl fmt::Debug for Metadata18 { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.fmt2(f) } } let protocol_version = unsafe { protocol::CURRENT_PROTOCOL_VERSION };
impl Default for Metadata19 { if protocol_version >= 74 {
fn default() -> Self { Metadata::read_from19(buf)
Self::new() } else {
Metadata::read_from18(buf)
}
}
fn write_to<W: io::Write>(&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 { impl fmt::Debug for Metadata {
Self::new() 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] #[test]
fn basic() { fn basic() {
let mut m = Metadata19::new(); let mut m = Metadata::new();
m.put(&TEST, "Hello world".to_owned()); m.put(&TEST, "Hello world".to_owned());