1use super::{Array8, Array16, Pair, SColor, Wrapped32, aabb3f, s8, s16, v2f, v2s16, v3f};
2use crate::wire::{
3 deser::{Deserialize, DeserializeResult, Deserializer},
4 ser::{Serialize, SerializeResult, Serializer},
5};
6use anyhow::bail;
7use luanti_protocol_derive::{LuantiDeserialize, LuantiSerialize};
8
9#[derive(Debug, Clone, PartialEq, LuantiSerialize, LuantiDeserialize)]
11pub struct GenericInitData {
12 pub version: u8,
13 pub name: String,
14 pub is_player: bool,
15 pub id: u16,
16 pub position: v3f,
17 pub rotation: v3f,
18 pub hp: u16,
19 #[wrap(Array8<Wrapped32<ActiveObjectCommand>>)]
20 pub messages: Vec<ActiveObjectCommand>,
21}
22
23#[derive(Debug, Clone, PartialEq)]
25#[expect(clippy::large_enum_variant, reason = "consider `Box`ing variants")]
26pub enum ActiveObjectCommand {
27 SetProperties(AOCSetProperties),
28 UpdatePosition(AOCUpdatePosition),
29 SetTextureMod(AOCSetTextureMod),
30 SetSprite(AOCSetSprite),
31 SetPhysicsOverride(AOCSetPhysicsOverride),
32 SetAnimation(AOCSetAnimation),
33 SetAnimationSpeed(AOCSetAnimationSpeed),
34 SetBonePosition(AOCSetBonePosition),
35 AttachTo(AOCAttachTo),
36 Punched(AOCPunched),
37 UpdateArmorGroups(AOCUpdateArmorGroups),
38 SpawnInfant(AOCSpawnInfant),
39 Obsolete1(AOCObsolete1),
40}
41
42const AO_CMD_SET_PROPERTIES: u8 = 0;
43const AO_CMD_UPDATE_POSITION: u8 = 1;
44const AO_CMD_SET_TEXTURE_MOD: u8 = 2;
45const AO_CMD_SET_SPRITE: u8 = 3;
46const AO_CMD_PUNCHED: u8 = 4;
47const AO_CMD_UPDATE_ARMOR_GROUPS: u8 = 5;
48const AO_CMD_SET_ANIMATION: u8 = 6;
49const AO_CMD_SET_BONE_POSITION: u8 = 7;
50const AO_CMD_ATTACH_TO: u8 = 8;
51const AO_CMD_SET_PHYSICS_OVERRIDE: u8 = 9;
52const AO_CMD_OBSOLETE1: u8 = 10;
53const AO_CMD_SPAWN_INFANT: u8 = 11;
54const AO_CMD_SET_ANIMATION_SPEED: u8 = 12;
55
56impl ActiveObjectCommand {
57 fn get_command_prefix(&self) -> u8 {
58 match self {
59 ActiveObjectCommand::SetProperties(_) => AO_CMD_SET_PROPERTIES,
60 ActiveObjectCommand::UpdatePosition(_) => AO_CMD_UPDATE_POSITION,
61 ActiveObjectCommand::SetTextureMod(_) => AO_CMD_SET_TEXTURE_MOD,
62 ActiveObjectCommand::SetSprite(_) => AO_CMD_SET_SPRITE,
63 ActiveObjectCommand::SetPhysicsOverride(_) => AO_CMD_SET_PHYSICS_OVERRIDE,
64 ActiveObjectCommand::SetAnimation(_) => AO_CMD_SET_ANIMATION,
65 ActiveObjectCommand::SetAnimationSpeed(_) => AO_CMD_SET_ANIMATION_SPEED,
66 ActiveObjectCommand::SetBonePosition(_) => AO_CMD_SET_BONE_POSITION,
67 ActiveObjectCommand::AttachTo(_) => AO_CMD_ATTACH_TO,
68 ActiveObjectCommand::Punched(_) => AO_CMD_PUNCHED,
69 ActiveObjectCommand::UpdateArmorGroups(_) => AO_CMD_UPDATE_ARMOR_GROUPS,
70 ActiveObjectCommand::SpawnInfant(_) => AO_CMD_SPAWN_INFANT,
71 ActiveObjectCommand::Obsolete1(_) => AO_CMD_OBSOLETE1,
72 }
73 }
74}
75
76impl Serialize for ActiveObjectCommand {
77 type Input = Self;
78 fn serialize<S: Serializer>(value: &Self::Input, ser: &mut S) -> SerializeResult {
79 u8::serialize(&value.get_command_prefix(), ser)?;
80 match value {
81 ActiveObjectCommand::SetProperties(command) => {
82 AOCSetProperties::serialize(command, ser)?;
83 }
84 ActiveObjectCommand::UpdatePosition(command) => {
85 AOCUpdatePosition::serialize(command, ser)?;
86 }
87 ActiveObjectCommand::SetTextureMod(command) => {
88 AOCSetTextureMod::serialize(command, ser)?;
89 }
90 ActiveObjectCommand::SetSprite(command) => AOCSetSprite::serialize(command, ser)?,
91 ActiveObjectCommand::SetPhysicsOverride(command) => {
92 AOCSetPhysicsOverride::serialize(command, ser)?;
93 }
94 ActiveObjectCommand::SetAnimation(command) => AOCSetAnimation::serialize(command, ser)?,
95 ActiveObjectCommand::SetAnimationSpeed(command) => {
96 AOCSetAnimationSpeed::serialize(command, ser)?;
97 }
98 ActiveObjectCommand::SetBonePosition(command) => {
99 AOCSetBonePosition::serialize(command, ser)?;
100 }
101 ActiveObjectCommand::AttachTo(command) => AOCAttachTo::serialize(command, ser)?,
102 ActiveObjectCommand::Punched(command) => AOCPunched::serialize(command, ser)?,
103 ActiveObjectCommand::UpdateArmorGroups(command) => {
104 AOCUpdateArmorGroups::serialize(command, ser)?;
105 }
106 ActiveObjectCommand::SpawnInfant(command) => AOCSpawnInfant::serialize(command, ser)?,
107 ActiveObjectCommand::Obsolete1(command) => AOCObsolete1::serialize(command, ser)?,
108 }
109 Ok(())
110 }
111}
112
113impl Deserialize for ActiveObjectCommand {
114 type Output = Self;
115 fn deserialize(deser: &mut Deserializer<'_>) -> DeserializeResult<Self> {
116 #[allow(
117 clippy::enum_glob_use,
118 reason = "this improves readability and is very local"
119 )]
120 use ActiveObjectCommand::*;
121 let cmd = u8::deserialize(deser)?;
122 Ok(match cmd {
123 AO_CMD_SET_PROPERTIES => SetProperties(AOCSetProperties::deserialize(deser)?),
124 AO_CMD_UPDATE_POSITION => UpdatePosition(AOCUpdatePosition::deserialize(deser)?),
125 AO_CMD_SET_TEXTURE_MOD => SetTextureMod(AOCSetTextureMod::deserialize(deser)?),
126 AO_CMD_SET_SPRITE => SetSprite(AOCSetSprite::deserialize(deser)?),
127 AO_CMD_PUNCHED => Punched(AOCPunched::deserialize(deser)?),
128 AO_CMD_UPDATE_ARMOR_GROUPS => {
129 UpdateArmorGroups(AOCUpdateArmorGroups::deserialize(deser)?)
130 }
131 AO_CMD_SET_ANIMATION => SetAnimation(AOCSetAnimation::deserialize(deser)?),
132 AO_CMD_SET_BONE_POSITION => SetBonePosition(AOCSetBonePosition::deserialize(deser)?),
133 AO_CMD_ATTACH_TO => AttachTo(AOCAttachTo::deserialize(deser)?),
134 AO_CMD_SET_PHYSICS_OVERRIDE => {
135 SetPhysicsOverride(AOCSetPhysicsOverride::deserialize(deser)?)
136 }
137 AO_CMD_OBSOLETE1 => Obsolete1(AOCObsolete1::deserialize(deser)?),
138 AO_CMD_SPAWN_INFANT => SpawnInfant(AOCSpawnInfant::deserialize(deser)?),
139 AO_CMD_SET_ANIMATION_SPEED => {
140 SetAnimationSpeed(AOCSetAnimationSpeed::deserialize(deser)?)
141 }
142 _ => bail!("ActiveObjectCommand: Invalid cmd={}", cmd),
143 })
144 }
145}
146
147#[derive(Debug, Clone, PartialEq, LuantiSerialize, LuantiDeserialize)]
148pub struct AOCSetProperties {
149 pub newprops: ObjectProperties,
150}
151
152#[derive(Debug, Clone, PartialEq, LuantiSerialize, LuantiDeserialize)]
153#[expect(clippy::struct_excessive_bools, reason = "this is mandated by the API")]
154pub struct ObjectProperties {
155 pub version: u8, pub hp_max: u16,
157 pub physical: bool,
158 pub _unused: u32,
159 pub collision_box: aabb3f,
160 pub selection_box: aabb3f,
161 pub pointable: bool,
162 pub visual: String,
163 pub visual_size: v3f,
164 #[wrap(Array16<String>)]
165 pub textures: Vec<String>,
166 pub spritediv: v2s16,
167 pub initial_sprite_basepos: v2s16,
168 pub is_visible: bool,
169 pub makes_footstep_sound: bool,
170 pub automatic_rotate: f32,
171 pub mesh: String,
172 #[wrap(Array16<SColor>)]
173 pub colors: Vec<SColor>,
174 pub collide_with_objects: bool,
175 pub stepheight: f32,
176 pub automatic_face_movement_dir: bool,
177 pub automatic_face_movement_dir_offset: f32,
178 pub backface_culling: bool,
179 pub nametag: String,
180 pub nametag_color: SColor,
181 pub automatic_face_movement_max_rotation_per_sec: f32,
182 pub infotext: String,
183 pub wield_item: String,
184 pub glow: s8,
185 pub breath_max: u16,
186 pub eye_height: f32,
187 pub zoom_fov: f32,
188 pub use_texture_alpha: bool,
189 pub damage_texture_modifier: Option<String>,
190 pub shaded: Option<bool>,
191 pub show_on_minimap: Option<bool>,
192 pub nametag_bgcolor: Option<SColor>,
193 pub rotate_selectionbox: Option<bool>,
194}
195
196#[derive(Debug, Clone, PartialEq, LuantiSerialize, LuantiDeserialize)]
197pub struct AOCUpdatePosition {
198 pub position: v3f,
199 pub velocity: v3f,
200 pub acceleration: v3f,
201 pub rotation: v3f,
202 pub do_interpolate: bool,
203 pub is_end_position: bool,
204 pub update_interval: f32,
205}
206
207#[derive(Debug, Clone, PartialEq, LuantiSerialize, LuantiDeserialize)]
208pub struct AOCSetTextureMod {
209 pub modifier: String,
210}
211
212#[derive(Debug, Clone, PartialEq, LuantiSerialize, LuantiDeserialize)]
213pub struct AOCSetSprite {
214 pub base_pos: v2s16,
215 pub anum_num_frames: u16,
216 pub anim_frame_length: f32,
217 pub select_horiz_by_yawpitch: bool,
218}
219
220#[derive(Debug, Clone, PartialEq, LuantiSerialize, LuantiDeserialize)]
221pub struct AOCSetPhysicsOverride {
222 pub override_speed: f32,
223 pub override_jump: f32,
224 pub override_gravity: f32,
225 pub not_sneak: bool,
226 pub not_sneak_glitch: bool,
227 pub not_new_move: bool,
228}
229
230#[derive(Debug, Clone, PartialEq, LuantiSerialize, LuantiDeserialize)]
231pub struct AOCSetAnimation {
232 pub range: v2f, pub speed: f32,
234 pub blend: f32,
235 pub no_loop: bool,
236}
237
238#[derive(Debug, Clone, PartialEq, LuantiSerialize, LuantiDeserialize)]
239pub struct AOCSetAnimationSpeed {
240 pub speed: f32,
241}
242
243#[derive(Debug, Clone, PartialEq, LuantiSerialize, LuantiDeserialize)]
244pub struct AOCSetBonePosition {
245 pub bone: String,
246 pub position: v3f,
247 pub rotation: v3f,
248}
249
250#[derive(Debug, Clone, PartialEq, LuantiSerialize, LuantiDeserialize)]
251pub struct AOCAttachTo {
252 pub parent_id: s16,
253 pub bone: String,
254 pub position: v3f,
255 pub rotation: v3f,
256 pub force_visible: bool,
257}
258
259#[derive(Debug, Clone, PartialEq, LuantiSerialize, LuantiDeserialize)]
260pub struct AOCPunched {
261 pub hp: u16,
262}
263
264#[derive(Debug, Clone, PartialEq, LuantiSerialize, LuantiDeserialize)]
265pub struct AOCUpdateArmorGroups {
266 #[wrap(Array16<Pair<String, s16>>)]
268 pub ratings: Vec<(String, s16)>,
269}
270
271#[derive(Debug, Clone, PartialEq, LuantiSerialize, LuantiDeserialize)]
272pub struct AOCSpawnInfant {
273 pub child_id: u16,
274 pub typ: u8,
275}
276
277#[derive(Debug, Clone, PartialEq, LuantiSerialize, LuantiDeserialize)]
278pub struct AOCObsolete1;