diff options
author | Joey Sacchini <joey@sacchini.net> | 2020-10-09 11:35:39 -0400 |
---|---|---|
committer | Joey Sacchini <joey@sacchini.net> | 2020-10-09 11:35:39 -0400 |
commit | 7cac1641ff1473ebb18cc788348f1c79c18ee564 (patch) | |
tree | 8780266bbfb602eac9cd0173aa63162f8b6bab2c | |
parent | 5428d78bc007a1a8557c4838655442d674327426 (diff) | |
download | mcproto-rs-7cac1641ff1473ebb18cc788348f1c79c18ee564.tar.gz mcproto-rs-7cac1641ff1473ebb18cc788348f1c79c18ee564.tar.bz2 mcproto-rs-7cac1641ff1473ebb18cc788348f1c79c18ee564.zip |
implement teams packets
-rw-r--r-- | src/protocol.rs | 92 | ||||
-rw-r--r-- | src/v1_15_2.rs | 248 |
2 files changed, 338 insertions, 2 deletions
diff --git a/src/protocol.rs b/src/protocol.rs index 0d0adae..2f3ec2e 100644 --- a/src/protocol.rs +++ b/src/protocol.rs @@ -369,6 +369,98 @@ macro_rules! proto_int_enum { } #[macro_export] +macro_rules! proto_str_enum { + ($typname: ident, $($sval: literal :: $nam: ident),*) => { + crate::as_item! { + #[derive(PartialEq, Clone, Copy)] + pub enum $typname { + $($nam),+, + } + } + + impl Serialize for $typname { + fn mc_serialize<S: Serializer>(&self, to: &mut S) -> SerializeResult { + let name = self.name().to_owned(); + to.serialize_other(&name) + } + } + + impl Deserialize for $typname { + fn mc_deserialize(data: &[u8]) -> DeserializeResult<'_, Self> { + String::mc_deserialize(data)?.and_then(move |name, rest| { + if let Some(v) = Self::from_string(&name) { + Deserialized::ok(v, rest) + } else { + Err(DeserializeErr::CannotUnderstandValue(format!("invalid {} ident '{}'", stringify!($typname), name))) + } + }) + } + } + + impl $typname { + pub fn from_str(arg: &str) -> Option<Self> { + use $typname::*; + + match arg { + $($sval => Some($nam)),+, + _ => None + } + } + + pub fn from_string(arg: &String) -> Option<Self> { + Self::from_str(arg.as_str()) + } + + pub fn name(&self) -> &str { + use $typname::*; + + match self { + $($nam => $sval),+, + } + } + } + + impl From<&$typname> for String { + fn from(arg: &$typname) -> Self { + arg.name().to_owned() + } + } + + impl From<$typname> for String { + fn from(arg: $typname) -> Self { + arg.name().to_owned() + } + } + + impl std::fmt::Display for $typname { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str(self.name()) + } + } + + impl std::fmt::Debug for $typname { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + <dyn std::fmt::Display>::fmt(self, f) + } + } + + #[cfg(test)] + impl TestRandom for $typname { + fn test_gen_random() -> Self { + let mut idx: usize = (rand::random::<usize>() % (count_num!($($nam),+))) + 1; + $( + idx -= 1; + if idx == 0 { + return $typname::$nam; + } + )+ + panic!("cannot generate random {}", stringify!($typname)); + } + } + } +} + +#[macro_export] macro_rules! proto_byte_flag { ($typname: ident, $($bval: literal :: $nam: ident),*) => { #[derive(Clone, Copy, PartialEq, Eq, Debug, Default)] diff --git a/src/v1_15_2.rs b/src/v1_15_2.rs index ec407de..fccba38 100644 --- a/src/v1_15_2.rs +++ b/src/v1_15_2.rs @@ -504,8 +504,14 @@ define_protocol!(Packet578, PacketDirection, State, i32, Id => { entity_id: VarInt, passenger_entitiy_ids: VarIntCountedArray<VarInt> }, - // todo teams - // todo update score + PlayTeams, 0x4C, Play, ClientBound => PlayTeamsSpec { + team_name: String, + action: TeamAction + }, + PlayUpdateScore, 0x4D, Play, ClientBound => PlayUpdateScoreSpec { + entity_name: TeamMember, + update: UpdateScoreSpec + }, PlaySpawnPosition, 0x4E, Play, ClientBound => PlaySpawnPositionSpec { location: IntPosition }, @@ -1181,6 +1187,226 @@ __protocol_body_def_helper!(TabCompleteMatch { tooltip: Option<Chat> }); +#[derive(Clone, Debug, PartialEq)] +pub enum TeamAction { + Create(TeamActionCreateSpec), + Remove, + UpdateInfo(TeamActionUpdateInfoSpec), + AddPlayers(TeamActionPlayerList), + RemovePlayers(TeamActionPlayerList) +} + +impl Serialize for TeamAction { + fn mc_serialize<S: Serializer>(&self, to: &mut S) -> SerializeResult { + use TeamAction::*; + + to.serialize_byte(match self { + Create(_) => 0x00, + Remove => 0x01, + UpdateInfo(_) => 0x02, + AddPlayers(_) => 0x03, + RemovePlayers(_) => 0x04 + })?; + + match self { + Create(body) => to.serialize_other(body), + UpdateInfo(body) => to.serialize_other(body), + AddPlayers(body) => to.serialize_other(body), + RemovePlayers(body) => to.serialize_other(body), + _ => Ok(()) + } + } +} + +impl Deserialize for TeamAction { + fn mc_deserialize(data: &[u8]) -> DeserializeResult<'_, Self> { + let Deserialized { value: id, data } = u8::mc_deserialize(data)?; + + use TeamAction::*; + + match id { + 0x00 => Ok(TeamActionCreateSpec::mc_deserialize(data)?.map(move |body| Create(body))), + 0x01 => Deserialized::ok(Remove, data), + 0x02 => Ok(TeamActionUpdateInfoSpec::mc_deserialize(data)?.map(move |body| UpdateInfo(body))), + 0x03 => Ok(TeamActionPlayerList::mc_deserialize(data)?.map(move |body| AddPlayers(body))), + 0x04 => Ok(TeamActionPlayerList::mc_deserialize(data)?.map(move |body| RemovePlayers(body))), + other => Err(DeserializeErr::CannotUnderstandValue(format!("invalid team action id {}", other))) + } + } +} + +#[cfg(test)] +impl TestRandom for TeamAction { + fn test_gen_random() -> Self { + let rand_idx = rand::random::<usize>() % 5; + + use TeamAction::*; + + match rand_idx { + 0 => Create(TeamActionCreateSpec::test_gen_random()), + 1 => Remove, + 2 => UpdateInfo(TeamActionUpdateInfoSpec::test_gen_random()), + 3 => AddPlayers(TeamActionPlayerList::test_gen_random()), + 4 => RemovePlayers(TeamActionPlayerList::test_gen_random()), + impossible => panic!("impossible condition because modulus {}", impossible) + } + } +} + +#[derive(Clone, Debug, PartialEq)] +pub enum TeamMember { + Player(String), + Entity(UUID4) +} + +impl Serialize for TeamMember { + fn mc_serialize<S: Serializer>(&self, to: &mut S) -> SerializeResult { + use TeamMember::*; + match self { + Player(username) => username.mc_serialize(to), + Entity(entity_id) => entity_id.to_string().mc_serialize(to) + } + } +} + +impl Deserialize for TeamMember { + fn mc_deserialize(data: &[u8]) -> DeserializeResult<'_, Self> { + use TeamMember::*; + + Ok(String::mc_deserialize(data)?.map(move |raw| { + if let Some(entity_id) = UUID4::parse(raw.as_str()) { + Entity(entity_id) + } else { + Player(raw) + } + })) + } +} + +#[cfg(test)] +impl TestRandom for TeamMember { + fn test_gen_random() -> Self { + use TeamMember::*; + + let rand_bool: bool = rand::random(); + if rand_bool { + Player(String::test_gen_random()) + } else { + Entity(UUID4::random()) + } + } +} + +proto_str_enum!(TeamTagNameVisibility, + "always" :: Always, + "hideForOtherTeams" :: HideForOtherTeams, + "hideForOwnTeam" :: HideForOwnTeam, + "never" :: Never +); + +proto_str_enum!(TeamCollisionRule, + "always" :: Always, + "pushForOtherTeams" :: PushForOtherTeams, + "pushOwnTeam" :: PushOwnTeam, + "never" :: Never +); + +__protocol_body_def_helper!(TeamActionPlayerList { + entities: VarIntCountedArray<TeamMember> +}); + +__protocol_body_def_helper!(TeamActionCreateSpec { + display_name: Chat, + friendly_flags: TeamFriendlyFlags, + tag_name_visibility: TeamTagNameVisibility, + collision_rule: TeamCollisionRule, + color: VarInt, + prefix: Chat, + suffix: Chat, + entities: VarIntCountedArray<TeamMember> +}); + +__protocol_body_def_helper!(TeamActionUpdateInfoSpec { + display_name: Chat, + friendly_flags: TeamFriendlyFlags, + tag_name_visibility: TeamTagNameVisibility, + collision_rule: TeamCollisionRule, + color: VarInt, + prefix: Chat, + suffix: Chat +}); + +proto_byte_flag!(TeamFriendlyFlags, + 0x01 :: allow_friendly_fire, + 0x02 :: show_invisible_teammates +); + +#[derive(Clone, Debug, PartialEq)] +pub enum UpdateScoreAction { + Upsert(VarInt), + Remove +} + +#[derive(Clone, Debug, PartialEq)] +pub struct UpdateScoreSpec { + pub objective_name: String, + pub action: UpdateScoreAction +} + +impl Serialize for UpdateScoreSpec { + fn mc_serialize<S: Serializer>(&self, to: &mut S) -> SerializeResult { + use UpdateScoreAction::*; + to.serialize_byte(match self.action { + Upsert(_) => 0x0, + Remove => 0x01 + })?; + to.serialize_other(&self.objective_name)?; + if let Upsert(value) = &self.action { + to.serialize_other(value)?; + } + + Ok(()) + } +} + +impl Deserialize for UpdateScoreSpec { + fn mc_deserialize(data: &[u8]) -> DeserializeResult<'_, Self> { + let Deserialized { value: action_id, data } = u8::mc_deserialize(data)?; + let Deserialized { value: objective_name, data } = String::mc_deserialize(data)?; + + use UpdateScoreAction::*; + let Deserialized{ value: action, data } = match action_id { + 0x00 => Ok(VarInt::mc_deserialize(data)?.map(move |value| Upsert(value))), + 0x01 => Deserialized::ok(Remove, data), + other => DeserializeErr::CannotUnderstandValue(format!("invalid update score action {}", other)).into() + }?; + + Deserialized::ok(Self { + objective_name, + action, + }, data) + } +} + +#[cfg(test)] +impl TestRandom for UpdateScoreSpec { + fn test_gen_random() -> Self { + use UpdateScoreAction::*; + + let rand_bool = rand::random::<bool>(); + let action = if rand_bool { + Upsert(VarInt::test_gen_random()) + } else { + Remove + }; + + Self { + objective_name: String::test_gen_random(), + action, + } + } +} + proto_varint_enum!(SoundCategory, 0x00 :: Master, 0x01 :: Music, @@ -3652,6 +3878,24 @@ pub mod tests { packet_test_cases!( Packet578, + PlayTeams, + PlayTeamsSpec, + test_play_teams, + bench_write_play_teams, + bench_read_play_teams + ); + + packet_test_cases!( + Packet578, + PlayUpdateScore, + PlayUpdateScoreSpec, + test_play_update_score, + bench_write_play_update_score, + bench_read_play_update_score + ); + + packet_test_cases!( + Packet578, PlaySpawnPosition, PlaySpawnPositionSpec, test_play_spawn_position, |