diff --git a/src/ipc/zone/event_scene.rs b/src/ipc/zone/event_scene.rs index d0fc38b..68d7498 100644 --- a/src/ipc/zone/event_scene.rs +++ b/src/ipc/zone/event_scene.rs @@ -1,11 +1,14 @@ use binrw::binrw; use crate::common::ObjectTypeId; +use crate::ipc::zone::{ServerZoneIpcData, ServerZoneIpcType}; +#[derive(Debug, Clone)] #[binrw] #[brw(little)] -#[derive(Debug, Clone)] -pub struct EventScene { +#[brw(import{max_params: usize})] +#[brw(assert(params.len() <= max_params, "Too many params! {} > {}", params.len(), max_params))] +pub struct EventScene { pub actor_id: ObjectTypeId, pub event_id: u32, pub scene: u16, @@ -15,10 +18,12 @@ pub struct EventScene { pub params_count: u8, // Extra padding seems needed after or the client will seemingly softlock even with 2 params, possibly used for alignment? #[brw(pad_before = 3, pad_after = 4)] - pub params: [u32; MAX_PARAMS], + #[br(count = max_params)] + #[bw(pad_size_to = 4 * max_params)] + pub params: Vec, } -impl Default for EventScene<{ MAX_PARAMS }> { +impl Default for EventScene { fn default() -> Self { Self { actor_id: ObjectTypeId::default(), @@ -27,11 +32,58 @@ impl Default for EventScene<{ MAX_PARAMS }> { scene_flags: 0, unk1: 0, params_count: 0, - params: [0u32; MAX_PARAMS], + params: Vec::::new(), } } } +impl EventScene { + pub fn package_scene(&self) -> Option<(ServerZoneIpcType, ServerZoneIpcData)> { + let op_code; + let data; + match self.params.len() { + // TODO: it would be nice to avoid cloning if possible + 0..=2 => { + op_code = ServerZoneIpcType::EventScene; + data = ServerZoneIpcData::EventScene { data: self.clone() }; + } + 3..=4 => { + op_code = ServerZoneIpcType::EventScene4; + data = ServerZoneIpcData::EventScene4 { data: self.clone() }; + } + 5..=8 => { + op_code = ServerZoneIpcType::EventScene8; + data = ServerZoneIpcData::EventScene8 { data: self.clone() }; + } + 9..=16 => { + op_code = ServerZoneIpcType::EventScene16; + data = ServerZoneIpcData::EventScene16 { data: self.clone() }; + } + 17..=32 => { + op_code = ServerZoneIpcType::EventScene32; + data = ServerZoneIpcData::EventScene32 { data: self.clone() }; + } + 33..=64 => { + op_code = ServerZoneIpcType::EventScene64; + data = ServerZoneIpcData::EventScene64 { data: self.clone() }; + } + 65..=128 => { + op_code = ServerZoneIpcType::EventScene128; + data = ServerZoneIpcData::EventScene128 { data: self.clone() }; + } + 129..255 => { + op_code = ServerZoneIpcType::EventScene255; + data = ServerZoneIpcData::EventScene255 { data: self.clone() }; + } + _ => { + return None; + } + } + + Some((op_code, data)) + } +} + #[cfg(test)] mod tests { use std::{fs::read, io::Cursor, path::PathBuf}; @@ -50,7 +102,8 @@ mod tests { let buffer = read(d).unwrap(); let mut buffer = Cursor::new(&buffer); - let event_play = EventScene::<2>::read_le(&mut buffer).unwrap(); + let event_play = + EventScene::read_le_args(&mut buffer, EventSceneBinReadArgs { max_params: 2 }).unwrap(); assert_eq!( event_play.actor_id, ObjectTypeId { diff --git a/src/ipc/zone/mod.rs b/src/ipc/zone/mod.rs index d52321b..d2fd0b5 100644 --- a/src/ipc/zone/mod.rs +++ b/src/ipc/zone/mod.rs @@ -252,21 +252,53 @@ pub enum ServerZoneIpcData { ContainerInfo(ContainerInfo), /// Sent to tell the client to play a scene #[br(pre_assert(*magic == ServerZoneIpcType::EventScene))] - EventScene(EventScene<2>), + #[brw(little)] + EventScene { + #[brw(args { max_params: 2 } )] + data: EventScene, + }, #[br(pre_assert(*magic == ServerZoneIpcType::EventScene4))] - EventScene4(EventScene<4>), + #[brw(little)] + EventScene4 { + #[brw(args { max_params: 4 } )] + data: EventScene, + }, #[br(pre_assert(*magic == ServerZoneIpcType::EventScene8))] - EventScene8(EventScene<8>), + #[brw(little)] + EventScene8 { + #[brw(args { max_params: 8 } )] + data: EventScene, + }, #[br(pre_assert(*magic == ServerZoneIpcType::EventScene16))] - EventScene16(EventScene<16>), + #[brw(little)] + EventScene16 { + #[brw(args { max_params: 16 } )] + data: EventScene, + }, #[br(pre_assert(*magic == ServerZoneIpcType::EventScene32))] - EventScene32(EventScene<32>), + #[brw(little)] + EventScene32 { + #[brw(args { max_params: 32 } )] + data: EventScene, + }, #[br(pre_assert(*magic == ServerZoneIpcType::EventScene64))] - EventScene64(EventScene<64>), + #[brw(little)] + EventScene64 { + #[brw(args { max_params: 64 } )] + data: EventScene, + }, #[br(pre_assert(*magic == ServerZoneIpcType::EventScene128))] - EventScene128(EventScene<128>), + #[brw(little)] + EventScene128 { + #[brw(args { max_params: 128 } )] + data: EventScene, + }, #[br(pre_assert(*magic == ServerZoneIpcType::EventScene255))] - EventScene255(EventScene<255>), + #[brw(little)] + EventScene255 { + #[brw(args { max_params: 255 } )] + data: EventScene, + }, /// Sent to tell the client to load a scene, but not play it #[br(pre_assert(*magic == ServerZoneIpcType::EventStart))] EventStart(EventStart), @@ -651,35 +683,51 @@ mod tests { ), ( ServerZoneIpcType::EventScene, - ServerZoneIpcData::EventScene(EventScene::default()), + ServerZoneIpcData::EventScene { + data: EventScene::default(), + }, ), ( ServerZoneIpcType::EventScene4, - ServerZoneIpcData::EventScene4(EventScene::<4>::default()), + ServerZoneIpcData::EventScene4 { + data: EventScene::default(), + }, ), ( ServerZoneIpcType::EventScene8, - ServerZoneIpcData::EventScene8(EventScene::<8>::default()), + ServerZoneIpcData::EventScene8 { + data: EventScene::default(), + }, ), ( ServerZoneIpcType::EventScene16, - ServerZoneIpcData::EventScene16(EventScene::<16>::default()), + ServerZoneIpcData::EventScene16 { + data: EventScene::default(), + }, ), ( ServerZoneIpcType::EventScene32, - ServerZoneIpcData::EventScene32(EventScene::<32>::default()), + ServerZoneIpcData::EventScene32 { + data: EventScene::default(), + }, ), ( ServerZoneIpcType::EventScene64, - ServerZoneIpcData::EventScene64(EventScene::<64>::default()), + ServerZoneIpcData::EventScene64 { + data: EventScene::default(), + }, ), ( ServerZoneIpcType::EventScene128, - ServerZoneIpcData::EventScene128(EventScene::<128>::default()), + ServerZoneIpcData::EventScene128 { + data: EventScene::default(), + }, ), ( ServerZoneIpcType::EventScene255, - ServerZoneIpcData::EventScene255(EventScene::<255>::default()), + ServerZoneIpcData::EventScene255 { + data: EventScene::default(), + }, ), ( ServerZoneIpcType::EventStart, diff --git a/src/world/lua.rs b/src/world/lua.rs index 4a99957..4c19768 100644 --- a/src/world/lua.rs +++ b/src/world/lua.rs @@ -125,131 +125,26 @@ impl LuaPlayer { event_id: u32, scene: u16, scene_flags: u32, - params: &[u32], + params: Vec, ) { - let op_code; - let data; - match params.len() { - // TODO: it would be nice to de-duplicate these - 0..=2 => { - let mut scene = EventScene { - actor_id: target, - event_id, - scene, - scene_flags, - params_count: params.len() as u8, - ..Default::default() - }; - scene.params[..params.len()].copy_from_slice(¶ms[0..params.len()]); + let scene = EventScene { + actor_id: target, + event_id, + scene, + scene_flags, + params_count: params.len() as u8, + params: params.clone(), + ..Default::default() + }; - op_code = ServerZoneIpcType::EventScene; - data = ServerZoneIpcData::EventScene(scene); - } - 3..=4 => { - let mut scene = EventScene::<4> { - actor_id: target, - event_id, - scene, - scene_flags, - params_count: params.len() as u8, - ..Default::default() - }; - scene.params[..params.len()].copy_from_slice(¶ms[0..params.len()]); - - op_code = ServerZoneIpcType::EventScene4; - data = ServerZoneIpcData::EventScene4(scene); - } - 5..=8 => { - let mut scene = EventScene::<8> { - actor_id: target, - event_id, - scene, - scene_flags, - params_count: params.len() as u8, - ..Default::default() - }; - scene.params[..params.len()].copy_from_slice(¶ms[0..params.len()]); - - op_code = ServerZoneIpcType::EventScene8; - data = ServerZoneIpcData::EventScene8(scene); - } - 9..=16 => { - let mut scene = EventScene::<16> { - actor_id: target, - event_id, - scene, - scene_flags, - params_count: params.len() as u8, - ..Default::default() - }; - scene.params[..params.len()].copy_from_slice(¶ms[0..params.len()]); - - op_code = ServerZoneIpcType::EventScene16; - data = ServerZoneIpcData::EventScene16(scene); - } - 17..=32 => { - let mut scene = EventScene::<32> { - actor_id: target, - event_id, - scene, - scene_flags, - params_count: params.len() as u8, - ..Default::default() - }; - scene.params[..params.len()].copy_from_slice(¶ms[0..params.len()]); - - op_code = ServerZoneIpcType::EventScene32; - data = ServerZoneIpcData::EventScene32(scene); - } - 33..=64 => { - let mut scene = EventScene::<64> { - actor_id: target, - event_id, - scene, - scene_flags, - params_count: params.len() as u8, - ..Default::default() - }; - scene.params.copy_from_slice(¶ms[0..params.len()]); - - op_code = ServerZoneIpcType::EventScene64; - data = ServerZoneIpcData::EventScene64(scene); - } - 65..=128 => { - let mut scene = EventScene::<128> { - actor_id: target, - event_id, - scene, - scene_flags, - params_count: params.len() as u8, - ..Default::default() - }; - scene.params[..params.len()].copy_from_slice(¶ms[0..params.len()]); - - op_code = ServerZoneIpcType::EventScene128; - data = ServerZoneIpcData::EventScene128(scene); - } - 129..255 => { - let mut scene = EventScene::<255> { - actor_id: target, - event_id, - scene, - scene_flags, - params_count: params.len() as u8, - ..Default::default() - }; - scene.params[..params.len()].copy_from_slice(¶ms[0..params.len()]); - - op_code = ServerZoneIpcType::EventScene255; - data = ServerZoneIpcData::EventScene255(scene); - } - _ => { - tracing::warn!("Unsupported amount of parameters in play_scene!"); - return; - } + if let Some((op_code, data)) = scene.package_scene() { + self.create_segment_self(op_code, data); + } else { + let error_message = "Unsupported amount of parameters in play_scene! This is likely a bug in your script! Cancelling event...".to_string(); + tracing::warn!(error_message); + self.send_message(&error_message, 0); + self.finish_event(event_id); } - - self.create_segment_self(op_code, data); } fn set_position(&mut self, position: Position, rotation: f32) { @@ -404,7 +299,7 @@ impl UserData for LuaPlayer { u32, Vec, )| { - this.play_scene(target, event_id, scene, scene_flags, ¶ms); + this.play_scene(target, event_id, scene, scene_flags, params); Ok(()) }, ); diff --git a/src/world/server.rs b/src/world/server.rs index aac5d4a..cd2f1fd 100644 --- a/src/world/server.rs +++ b/src/world/server.rs @@ -324,8 +324,7 @@ pub async fn server_main_loop(mut recv: Receiver) -> Result<(), std::i } } - if let ClientTriggerCommand::EventRelatedUnk { .. } = &trigger.trigger - { + if let ClientTriggerCommand::EventRelatedUnk { .. } = &trigger.trigger { let msg = FromServer::ActorControlSelf(ActorControlSelf { category: ActorControlCategory::EventRelatedUnk1 { unk1: 1 }, });