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