1use std::io::{Read, Write};
4
5use mcproto_codec::{
6 error::{CodecError, CodecKind, CodecOperation, InvalidEncodingReason},
7 io::{read_exact_counted, write_all_counted},
8};
9
10use crate::{
11 Boolean, Double, Float, IdOr, IdSet, Identifier, Int, Nbt, Position, PrefixedArray,
12 PrefixedOptional, PrefixedString, ProtocolEnum, SoundEvent, TextComponent, TypeCodec,
13 TypeStructCodec, VarInt,
14};
15
16use super::DataComponent;
17
18#[derive(Debug, Clone, PartialEq, Eq, Hash, Default)]
23pub struct NbtString(pub String);
24
25impl TypeCodec for NbtString {
26 fn encode(&self, writer: &mut impl Write) -> Result<(), CodecError> {
27 let encoded = cesu8::to_java_cesu8(&self.0);
28 let length = u16::try_from(encoded.len()).map_err(|_| {
29 CodecError::invalid_encoding_for_operation(
30 CodecKind::Nbt,
31 CodecOperation::Write,
32 0,
33 InvalidEncodingReason::TooLong {
34 max_bytes: u16::MAX as usize,
35 },
36 )
37 })?;
38 write_all_counted(writer, &[8], CodecKind::Nbt, 0)?;
39 write_all_counted(writer, &length.to_be_bytes(), CodecKind::Nbt, 1)?;
40 write_all_counted(writer, encoded.as_ref(), CodecKind::Nbt, 3)
41 }
42
43 fn decode(reader: &mut impl Read) -> Result<Self, CodecError> {
44 let mut tag = [0; 1];
45 read_exact_counted(reader, &mut tag, CodecKind::Nbt, 0)?;
46 if tag[0] != 8 {
47 return Err(CodecError::invalid_encoding(
48 CodecKind::Nbt,
49 1,
50 InvalidEncodingReason::InvalidNbt,
51 ));
52 }
53 let mut length = [0; 2];
54 read_exact_counted(reader, &mut length, CodecKind::Nbt, 1)?;
55 let mut encoded = vec![0; u16::from_be_bytes(length) as usize];
56 read_exact_counted(reader, &mut encoded, CodecKind::Nbt, 3)?;
57 let value = cesu8::from_java_cesu8(&encoded).map_err(|_| {
58 CodecError::invalid_encoding(
59 CodecKind::Nbt,
60 3 + encoded.len(),
61 InvalidEncodingReason::InvalidNbt,
62 )
63 })?;
64 Ok(Self(value.into_owned()))
65 }
66}
67
68macro_rules! protocol_struct {
69 ($(#[$meta:meta])* $name:ident { $($(#[$field_meta:meta])* $field:ident: $ty:ty),* $(,)? }) => {
70 $(#[$meta])*
71 #[derive(Debug, Clone, PartialEq, TypeStructCodec)]
72 #[type_struct_codec(kind = StructuredComponent)]
73 pub struct $name { $($(#[$field_meta])* pub $field: $ty,)* }
74 };
75}
76
77#[derive(Debug, Clone, PartialEq, TypeStructCodec)]
79#[type_struct_codec(kind = StructuredComponent)]
80pub struct Filterable<T> {
81 pub raw: T,
83 pub filtered: PrefixedOptional<T>,
85}
86
87protocol_struct!(EnchantmentEntry { type_id: VarInt, level: VarInt });
89
90#[derive(Debug, Clone, Copy, PartialEq, Eq, ProtocolEnum)]
91#[protocol_enum(repr = VarInt)]
92pub enum Rarity {
93 Common = 0,
94 Uncommon = 1,
95 Rare = 2,
96 Epic = 3,
97}
98
99#[derive(Debug, Clone, Copy, PartialEq, Eq, ProtocolEnum)]
100#[protocol_enum(repr = VarInt)]
101pub enum AttributeOperation {
102 Add = 0,
103 MultiplyBase = 1,
104 MultiplyTotal = 2,
105}
106
107#[derive(Debug, Clone, Copy, PartialEq, Eq, ProtocolEnum)]
108#[protocol_enum(repr = VarInt)]
109pub enum AttributeSlot {
110 Any = 0,
111 MainHand = 1,
112 OffHand = 2,
113 Hand = 3,
114 Feet = 4,
115 Legs = 5,
116 Chest = 6,
117 Head = 7,
118 Armor = 8,
119 Body = 9,
120}
121
122protocol_struct!(AttributeModifier {
124 attribute_id: VarInt,
125 modifier_id: Identifier,
126 value: Double,
127 operation: AttributeOperation,
128 slot: AttributeSlot,
129});
130
131#[derive(Debug, Clone, Copy, PartialEq, Eq, ProtocolEnum)]
132#[protocol_enum(repr = VarInt)]
133pub enum ItemUseAnimation {
134 None = 0,
135 Eat = 1,
136 Drink = 2,
137 Block = 3,
138 Bow = 4,
139 Spear = 5,
140 Crossbow = 6,
141 Spyglass = 7,
142 TootHorn = 8,
143 Brush = 9,
144}
145
146#[derive(Debug, Clone, Copy, PartialEq, Eq, ProtocolEnum)]
147#[protocol_enum(repr = VarInt)]
148pub enum EquipmentSlot {
149 MainHand = 0,
150 Feet = 1,
151 Legs = 2,
152 Chest = 3,
153 Head = 4,
154 OffHand = 5,
155 Body = 6,
156}
157
158protocol_struct!(ToolRule {
160 blocks: IdSet,
161 speed: PrefixedOptional<Float>,
162 correct_drop_for_blocks: PrefixedOptional<Boolean>,
163});
164
165protocol_struct!(DamageReduction {
167 horizontal_blocking_angle: Float,
168 damage_types: PrefixedOptional<IdSet>,
169 base: Float,
170 factor: Float,
171});
172
173protocol_struct!(KineticWeaponCondition {
175 max_duration_ticks: VarInt,
176 min_speed: Float,
177 min_relative_speed: Float,
178});
179
180#[derive(Debug, Clone, Copy, PartialEq, Eq, ProtocolEnum)]
181#[protocol_enum(repr = VarInt)]
182pub enum SwingAnimationType {
183 None = 0,
184 Whack = 1,
185 Stab = 2,
186}
187
188#[derive(Debug, Clone, Copy, PartialEq, Eq, ProtocolEnum)]
189#[protocol_enum(repr = VarInt)]
190pub enum MapPostProcessing {
191 Lock = 0,
192 Scale = 1,
193}
194
195#[derive(Debug, Clone, Copy, PartialEq, Eq, ProtocolEnum)]
196#[protocol_enum(repr = VarInt)]
197pub enum DyeColor {
198 White = 0,
199 Orange = 1,
200 Magenta = 2,
201 LightBlue = 3,
202 Yellow = 4,
203 Lime = 5,
204 Pink = 6,
205 Gray = 7,
206 LightGray = 8,
207 Cyan = 9,
208 Purple = 10,
209 Blue = 11,
210 Brown = 12,
211 Green = 13,
212 Red = 14,
213 Black = 15,
214}
215
216#[derive(Debug, Clone, Copy, PartialEq, Eq, ProtocolEnum)]
217#[protocol_enum(repr = VarInt)]
218pub enum FireworkShape {
219 SmallBall = 0,
220 LargeBall = 1,
221 Star = 2,
222 Creeper = 3,
223 Burst = 4,
224}
225
226protocol_struct!(FireworkExplosion {
228 shape: FireworkShape,
229 colors: PrefixedArray<Int>,
230 fade_colors: PrefixedArray<Int>,
231 has_trail: Boolean,
232 has_twinkle: Boolean,
233});
234
235protocol_struct!(SuspiciousStewEffect { type_id: VarInt, duration: VarInt });
237
238protocol_struct!(PotionEffect { type_id: VarInt, details: PotionEffectDetail });
240
241#[derive(Debug, Clone, PartialEq)]
243pub struct PotionEffectDetail {
244 pub amplifier: VarInt,
245 pub duration: VarInt,
246 pub ambient: Boolean,
247 pub show_particles: Boolean,
248 pub show_icon: Boolean,
249 pub hidden_effect: PrefixedOptional<Box<PotionEffectDetail>>,
250}
251
252impl TypeCodec for PotionEffectDetail {
253 fn encode(&self, writer: &mut impl Write) -> Result<(), CodecError> {
254 self.amplifier.encode(writer)?;
255 self.duration.encode(writer)?;
256 self.ambient.encode(writer)?;
257 self.show_particles.encode(writer)?;
258 self.show_icon.encode(writer)?;
259 self.hidden_effect.encode(writer)
260 }
261 fn decode(reader: &mut impl Read) -> Result<Self, CodecError> {
262 Ok(Self {
263 amplifier: VarInt::decode(reader)?,
264 duration: VarInt::decode(reader)?,
265 ambient: Boolean::decode(reader)?,
266 show_particles: Boolean::decode(reader)?,
267 show_icon: Boolean::decode(reader)?,
268 hidden_effect: PrefixedOptional::decode(reader)?,
269 })
270 }
271}
272
273protocol_struct!(TrimMaterialOverride { armor_material: Identifier, asset_name: PrefixedString });
275protocol_struct!(TrimMaterial {
277 suffix: PrefixedString,
278 overrides: PrefixedArray<TrimMaterialOverride>,
279 description: TextComponent,
280});
281protocol_struct!(TrimPattern {
283 asset_name: PrefixedString,
284 template_item: VarInt,
285 description: TextComponent,
286 decal: Boolean,
287});
288protocol_struct!(Instrument {
290 sound_event: IdOr<SoundEvent>,
291 use_duration: Float,
292 range: Float,
293 description: TextComponent,
294});
295protocol_struct!(JukeboxSong {
297 sound_event: IdOr<SoundEvent>,
298 description: TextComponent,
299 duration: Float,
300 output: VarInt,
301});
302protocol_struct!(BannerPattern { asset_id: Identifier, translation_key: PrefixedString });
304protocol_struct!(BannerPatternLayer { pattern: IdOr<BannerPattern>, color: DyeColor });
306protocol_struct!(BlockStateProperty { name: PrefixedString, value: PrefixedString });
308protocol_struct!(Bee {
310 entity_type_id: VarInt,
311 entity_data: Nbt,
312 ticks_in_hive: VarInt,
313 min_ticks_in_hive: VarInt,
314});
315protocol_struct!(GlobalPosition { dimension: Identifier, position: Position });
317pub type SlotProfileProperty = crate::GameProfileProperty;
319
320protocol_struct!(PaintingVariant {
322 width: Int,
323 height: Int,
324 asset_id: Identifier,
325 title: PrefixedOptional<TextComponent>,
326 author: PrefixedOptional<TextComponent>,
327});
328
329#[derive(Debug, Clone, PartialEq)]
331pub struct Property {
332 pub name: PrefixedString,
333 pub matcher: PropertyMatcher,
334}
335
336#[derive(Debug, Clone, PartialEq)]
337pub enum PropertyMatcher {
338 Exact(PrefixedString),
339 Range {
340 min: PrefixedString,
341 max: PrefixedString,
342 },
343}
344
345impl TypeCodec for Property {
346 fn encode(&self, writer: &mut impl Write) -> Result<(), CodecError> {
347 self.name.encode(writer)?;
348 match &self.matcher {
349 PropertyMatcher::Exact(value) => {
350 Boolean(true).encode(writer)?;
351 value.encode(writer)
352 }
353 PropertyMatcher::Range { min, max } => {
354 Boolean(false).encode(writer)?;
355 min.encode(writer)?;
356 max.encode(writer)
357 }
358 }
359 }
360 fn decode(reader: &mut impl Read) -> Result<Self, CodecError> {
361 let name = PrefixedString::decode(reader)?;
362 let matcher = if Boolean::decode(reader)?.0 {
363 PropertyMatcher::Exact(PrefixedString::decode(reader)?)
364 } else {
365 PropertyMatcher::Range {
366 min: PrefixedString::decode(reader)?,
367 max: PrefixedString::decode(reader)?,
368 }
369 };
370 Ok(Self { name, matcher })
371 }
372}
373
374protocol_struct!(ExactDataComponentMatcher { component: DataComponent });
376
377#[derive(Debug, Clone, Copy, PartialEq, Eq, ProtocolEnum)]
378#[protocol_enum(repr = VarInt)]
379pub enum PartialMatcherType {
380 Damage = 0,
381 Enchantments = 1,
382 StoredEnchantments = 2,
383 PotionContents = 3,
384 CustomData = 4,
385 Container = 5,
386 BundleContents = 6,
387 FireworkExplosion = 7,
388 Fireworks = 8,
389 WritableBookContent = 9,
390 WrittenBookContent = 10,
391 AttributeModifiers = 11,
392 Trim = 12,
393 JukeboxPlayable = 13,
394}
395protocol_struct!(PartialDataComponentMatcher { matcher_type: PartialMatcherType, predicate: Nbt });
397protocol_struct!(BlockPredicate {
399 blocks: PrefixedOptional<IdSet>,
400 properties: PrefixedOptional<PrefixedArray<Property>>,
401 nbt: PrefixedOptional<Nbt>,
402 exact_components: PrefixedArray<ExactDataComponentMatcher>,
403 partial_components: PrefixedArray<PartialDataComponentMatcher>,
404});
405
406protocol_struct!(ApplyEffects { effects: PrefixedArray<PotionEffect>, probability: Float });
408protocol_struct!(RemoveEffects { effects: IdSet });
410protocol_struct!(TeleportRandomly { diameter: Float });
412protocol_struct!(PlaySound { sound: SoundEvent });
414
415#[derive(Debug, Clone, PartialEq)]
417pub enum ConsumeEffect {
418 ApplyEffects(ApplyEffects),
419 RemoveEffects(RemoveEffects),
420 ClearAllEffects,
421 TeleportRandomly(TeleportRandomly),
422 PlaySound(PlaySound),
423}
424
425impl TypeCodec for ConsumeEffect {
426 fn encode(&self, writer: &mut impl Write) -> Result<(), CodecError> {
427 match self {
428 Self::ApplyEffects(v) => {
429 VarInt(0).encode(writer)?;
430 v.encode(writer)
431 }
432 Self::RemoveEffects(v) => {
433 VarInt(1).encode(writer)?;
434 v.encode(writer)
435 }
436 Self::ClearAllEffects => VarInt(2).encode(writer),
437 Self::TeleportRandomly(v) => {
438 VarInt(3).encode(writer)?;
439 v.encode(writer)
440 }
441 Self::PlaySound(v) => {
442 VarInt(4).encode(writer)?;
443 v.encode(writer)
444 }
445 }
446 }
447 fn decode(reader: &mut impl Read) -> Result<Self, CodecError> {
448 match VarInt::decode(reader)?.0 {
449 0 => Ok(Self::ApplyEffects(ApplyEffects::decode(reader)?)),
450 1 => Ok(Self::RemoveEffects(RemoveEffects::decode(reader)?)),
451 2 => Ok(Self::ClearAllEffects),
452 3 => Ok(Self::TeleportRandomly(TeleportRandomly::decode(reader)?)),
453 4 => Ok(Self::PlaySound(PlaySound::decode(reader)?)),
454 value => Err(CodecError::invalid_encoding(
455 CodecKind::StructuredComponent,
456 0,
457 InvalidEncodingReason::InvalidEnumValue {
458 value: i128::from(value),
459 },
460 )),
461 }
462 }
463}