mcproto_types/profile/
resolvable_profile.rs1use std::io::{Read, Write};
4
5use mcproto_codec::{
6 error::{CodecError, CodecKind, InvalidEncodingReason},
7 varint::{VarIntRead, VarIntWrite},
8};
9
10use crate::{
11 GameProfile, GameProfileProperties, GameProfileUsername, Identifier, PrefixedOptional,
12 ProtocolEnum, TypeCodec, TypeStructCodec, Uuid, VarInt,
13};
14
15#[derive(Debug, Clone, PartialEq, Eq, Hash, TypeStructCodec)]
20#[type_struct_codec(kind = PartialProfile)]
21pub struct PartialProfile {
22 pub username: PrefixedOptional<GameProfileUsername>,
24 pub uuid: PrefixedOptional<Uuid>,
26 pub properties: GameProfileProperties,
28}
29
30impl Default for PartialProfile {
31 fn default() -> Self {
32 Self {
33 username: PrefixedOptional::none(),
34 uuid: PrefixedOptional::none(),
35 properties: GameProfileProperties::default(),
36 }
37 }
38}
39
40#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default, ProtocolEnum)]
42#[protocol_enum(repr = VarInt)]
43pub enum SkinModel {
44 #[default]
46 Wide = 0,
47 Slim = 1,
49}
50
51#[derive(Debug, Clone, PartialEq, Eq, Hash)]
56pub enum ResolvableProfileData {
57 Partial(PartialProfile),
59 Complete(GameProfile),
61}
62
63impl From<PartialProfile> for ResolvableProfileData {
64 fn from(value: PartialProfile) -> Self {
65 Self::Partial(value)
66 }
67}
68
69impl From<GameProfile> for ResolvableProfileData {
70 fn from(value: GameProfile) -> Self {
71 Self::Complete(value)
72 }
73}
74
75#[derive(Debug, Clone, PartialEq, Eq, Hash)]
112pub struct ResolvableProfile {
113 pub profile: ResolvableProfileData,
115 pub body: PrefixedOptional<Identifier>,
117 pub cape: PrefixedOptional<Identifier>,
119 pub elytra: PrefixedOptional<Identifier>,
121 pub model: PrefixedOptional<SkinModel>,
123}
124
125impl ResolvableProfile {
126 #[must_use]
128 pub const fn new(profile: ResolvableProfileData) -> Self {
129 Self {
130 profile,
131 body: PrefixedOptional::none(),
132 cape: PrefixedOptional::none(),
133 elytra: PrefixedOptional::none(),
134 model: PrefixedOptional::none(),
135 }
136 }
137
138 #[must_use]
140 pub const fn partial(profile: PartialProfile) -> Self {
141 Self::new(ResolvableProfileData::Partial(profile))
142 }
143
144 #[must_use]
146 pub const fn complete(profile: GameProfile) -> Self {
147 Self::new(ResolvableProfileData::Complete(profile))
148 }
149}
150
151impl From<PartialProfile> for ResolvableProfile {
152 fn from(value: PartialProfile) -> Self {
153 Self::partial(value)
154 }
155}
156
157impl From<GameProfile> for ResolvableProfile {
158 fn from(value: GameProfile) -> Self {
159 Self::complete(value)
160 }
161}
162
163impl TypeCodec for ResolvableProfile {
164 fn encode(&self, writer: &mut impl Write) -> Result<(), CodecError> {
165 match &self.profile {
166 ResolvableProfileData::Partial(value) => {
167 writer
168 .write_varint(0)
169 .map_err(|error| error.with_context(CodecKind::ResolvableProfile))?;
170 value
171 .encode(writer)
172 .map_err(|error| error.with_context(CodecKind::ResolvableProfile))?;
173 }
174 ResolvableProfileData::Complete(value) => {
175 writer
176 .write_varint(1)
177 .map_err(|error| error.with_context(CodecKind::ResolvableProfile))?;
178 value
179 .encode(writer)
180 .map_err(|error| error.with_context(CodecKind::ResolvableProfile))?;
181 }
182 }
183
184 self.body
185 .encode(writer)
186 .map_err(|error| error.with_context(CodecKind::ResolvableProfile))?;
187 self.cape
188 .encode(writer)
189 .map_err(|error| error.with_context(CodecKind::ResolvableProfile))?;
190 self.elytra
191 .encode(writer)
192 .map_err(|error| error.with_context(CodecKind::ResolvableProfile))?;
193 self.model
194 .encode(writer)
195 .map_err(|error| error.with_context(CodecKind::ResolvableProfile))
196 }
197
198 fn decode(reader: &mut impl Read) -> Result<Self, CodecError> {
199 let (kind, kind_size) = reader
200 .read_varint_with_size()
201 .map_err(|error| error.with_context(CodecKind::ResolvableProfile))?;
202 let profile = match kind {
203 0 => PartialProfile::decode(reader)
204 .map(ResolvableProfileData::Partial)
205 .map_err(|error| error.with_context(CodecKind::ResolvableProfile))?,
206 1 => GameProfile::decode(reader)
207 .map(ResolvableProfileData::Complete)
208 .map_err(|error| error.with_context(CodecKind::ResolvableProfile))?,
209 value => {
210 return Err(CodecError::invalid_encoding(
211 CodecKind::ResolvableProfile,
212 kind_size,
213 InvalidEncodingReason::InvalidEnumValue {
214 value: i128::from(value),
215 },
216 ));
217 }
218 };
219
220 Ok(Self {
221 profile,
222 body: PrefixedOptional::decode(reader)
223 .map_err(|error| error.with_context(CodecKind::ResolvableProfile))?,
224 cape: PrefixedOptional::decode(reader)
225 .map_err(|error| error.with_context(CodecKind::ResolvableProfile))?,
226 elytra: PrefixedOptional::decode(reader)
227 .map_err(|error| error.with_context(CodecKind::ResolvableProfile))?,
228 model: PrefixedOptional::decode(reader)
229 .map_err(|error| error.with_context(CodecKind::ResolvableProfile))?,
230 })
231 }
232}