1use std::{
2 io::{self, Write},
3 sync::Arc,
4};
5
6use azalea_buf::{AzBuf, AzBufLimited, AzBufVar, BufReadError};
7use indexmap::IndexMap;
8use serde::{Deserialize, Serialize, Serializer};
9use uuid::Uuid;
10
11#[derive(AzBuf, Clone, Debug, Default, Eq, PartialEq)]
13pub struct GameProfile {
14 pub uuid: Uuid,
19 pub name: String,
23 pub properties: Arc<GameProfileProperties>,
27}
28
29impl GameProfile {
30 pub fn new(uuid: Uuid, name: String) -> Self {
31 GameProfile {
32 uuid,
33 name,
34 properties: Arc::new(GameProfileProperties::default()),
35 }
36 }
37}
38
39impl From<SerializableGameProfile> for GameProfile {
40 fn from(value: SerializableGameProfile) -> Self {
41 Self {
42 uuid: value.id.unwrap_or_default(),
43 name: value.name.unwrap_or_default(),
44 properties: Arc::new(value.properties.into()),
45 }
46 }
47}
48
49#[derive(Clone, Debug, Default, Eq, PartialEq)]
51pub struct GameProfileProperties {
52 pub map: IndexMap<String, ProfilePropertyValue>,
53}
54impl AzBuf for GameProfileProperties {
55 fn azalea_read(buf: &mut io::Cursor<&[u8]>) -> Result<Self, BufReadError> {
56 let mut properties = IndexMap::new();
57 let properties_len = u32::azalea_read_var(buf)?;
58 if properties_len > 16 {
59 return Err(BufReadError::VecLengthTooLong {
60 length: properties_len,
61 max_length: 16,
62 });
63 }
64 for _ in 0..properties_len {
65 let key = String::azalea_read_limited(buf, 16)?;
66 let value = ProfilePropertyValue::azalea_read(buf)?;
67 properties.insert(key, value);
68 }
69 Ok(GameProfileProperties { map: properties })
70 }
71 fn azalea_write(&self, buf: &mut impl Write) -> io::Result<()> {
72 (self.map.len() as u64).azalea_write_var(buf)?;
73 for (key, value) in &self.map {
74 key.azalea_write(buf)?;
75 value.azalea_write(buf)?;
76 }
77 Ok(())
78 }
79}
80
81#[derive(Clone, Debug, Eq, PartialEq)]
82pub struct ProfilePropertyValue {
83 pub value: String,
84 pub signature: Option<String>,
85}
86impl AzBuf for ProfilePropertyValue {
87 fn azalea_read(buf: &mut io::Cursor<&[u8]>) -> Result<Self, BufReadError> {
88 let value = String::azalea_read_limited(buf, 32767)?;
89 let signature = Option::<String>::azalea_read_limited(buf, 1024)?;
90 Ok(ProfilePropertyValue { value, signature })
91 }
92 fn azalea_write(&self, buf: &mut impl Write) -> io::Result<()> {
93 self.value.azalea_write(buf)?;
94 self.signature.azalea_write(buf)?;
95 Ok(())
96 }
97}
98
99#[derive(Clone, Debug, Deserialize, Serialize)]
100pub struct SerializableGameProfile {
101 #[serde(default)]
102 #[serde(skip_serializing_if = "Option::is_none")]
103 pub id: Option<Uuid>,
104 #[serde(default)]
105 #[serde(skip_serializing_if = "Option::is_none")]
106 pub name: Option<String>,
107 #[serde(default)]
108 #[serde(skip_serializing_if = "SerializableProfileProperties::is_empty")]
109 pub properties: SerializableProfileProperties,
110}
111
112impl From<GameProfile> for SerializableGameProfile {
113 fn from(value: GameProfile) -> Self {
114 Self {
115 id: Some(value.uuid),
116 name: Some(value.name),
117 properties: (*value.properties).clone().into(),
118 }
119 }
120}
121
122#[derive(Clone, Debug, Default, Deserialize, Serialize)]
123#[serde(transparent)]
124pub struct SerializableProfileProperties {
125 pub list: Vec<SerializableProfilePropertyValue>,
126}
127impl SerializableProfileProperties {
128 pub fn is_empty(&self) -> bool {
129 self.list.is_empty()
130 }
131}
132#[derive(Clone, Debug, Deserialize, Serialize)]
133pub struct SerializableProfilePropertyValue {
134 pub name: String,
135 pub value: String,
136 #[serde(default)]
137 #[serde(skip_serializing_if = "Option::is_none")]
138 pub signature: Option<String>,
139}
140
141impl From<GameProfileProperties> for SerializableProfileProperties {
142 fn from(value: GameProfileProperties) -> Self {
143 let mut list = Vec::new();
144 for (name, entry) in value.map {
145 list.push(SerializableProfilePropertyValue {
146 name,
147 value: entry.value,
148 signature: entry.signature,
149 });
150 }
151 Self { list }
152 }
153}
154impl From<SerializableProfileProperties> for GameProfileProperties {
155 fn from(value: SerializableProfileProperties) -> Self {
156 let mut map = IndexMap::new();
157 for entry in value.list {
158 map.insert(
159 entry.name,
160 ProfilePropertyValue {
161 value: entry.value,
162 signature: entry.signature,
163 },
164 );
165 }
166 Self { map }
167 }
168}
169impl Serialize for GameProfile {
170 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
171 let serializable = SerializableGameProfile::from(self.clone());
172 serializable.serialize(serializer)
173 }
174}
175
176#[cfg(test)]
177mod tests {
178 use super::*;
179
180 #[test]
181 fn test_deserialize_game_profile() {
182 let json = r#"{
183 "id": "f1a2b3c4-d5e6-f7a8-b9c0-d1e2f3a4b5c6",
184 "name": "Notch",
185 "properties": [
186 {
187 "name": "qwer",
188 "value": "asdf",
189 "signature": "zxcv"
190 }
191 ]
192 }"#;
193 let profile =
194 GameProfile::from(serde_json::from_str::<SerializableGameProfile>(json).unwrap());
195 assert_eq!(
196 profile,
197 GameProfile {
198 uuid: Uuid::parse_str("f1a2b3c4-d5e6-f7a8-b9c0-d1e2f3a4b5c6").unwrap(),
199 name: "Notch".to_owned(),
200 properties: {
201 let mut map = IndexMap::new();
202 map.insert(
203 "qwer".to_owned(),
204 ProfilePropertyValue {
205 value: "asdf".to_owned(),
206 signature: Some("zxcv".to_owned()),
207 },
208 );
209 GameProfileProperties { map }.into()
210 },
211 }
212 );
213 }
214}