aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJoey Sacchini <joey@sacchini.net>2020-10-09 11:35:39 -0400
committerJoey Sacchini <joey@sacchini.net>2020-10-09 11:35:39 -0400
commit7cac1641ff1473ebb18cc788348f1c79c18ee564 (patch)
tree8780266bbfb602eac9cd0173aa63162f8b6bab2c
parent5428d78bc007a1a8557c4838655442d674327426 (diff)
downloadmcproto-rs-7cac1641ff1473ebb18cc788348f1c79c18ee564.tar.gz
mcproto-rs-7cac1641ff1473ebb18cc788348f1c79c18ee564.tar.bz2
mcproto-rs-7cac1641ff1473ebb18cc788348f1c79c18ee564.zip
implement teams packets
-rw-r--r--src/protocol.rs92
-rw-r--r--src/v1_15_2.rs248
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,