From 2cc562de3839a75921e93022283424e1b75023f1 Mon Sep 17 00:00:00 2001 From: Joshua Goins Date: Sun, 13 Jul 2025 11:24:14 -0400 Subject: [PATCH] Add support for the QuestActiveList IPC opcode --- resources/opcodes.json | 5 +++++ src/ipc/zone/mod.rs | 9 +++++++++ src/ipc/zone/quest_active_list.rs | 24 ++++++++++++++++++++++++ src/world/connection.rs | 24 +++++++++++++++++++++--- 4 files changed, 59 insertions(+), 3 deletions(-) create mode 100644 src/ipc/zone/quest_active_list.rs diff --git a/resources/opcodes.json b/resources/opcodes.json index f2f755a..8b0bf93 100644 --- a/resources/opcodes.json +++ b/resources/opcodes.json @@ -284,6 +284,11 @@ "name": "TitleList", "opcode": 731, "size": 112 + }, + { + "name": "QuestActiveList", + "opcode": 398, + "size": 480 } ], "ClientZoneIpcType": [ diff --git a/src/ipc/zone/mod.rs b/src/ipc/zone/mod.rs index 720bc61..bcfa6f1 100644 --- a/src/ipc/zone/mod.rs +++ b/src/ipc/zone/mod.rs @@ -91,6 +91,9 @@ pub use event_yield_handler::EventYieldHandler; mod object_spawn; pub use object_spawn::ObjectSpawn; +mod quest_active_list; +pub use quest_active_list::QuestActiveList; + use crate::COMPLETED_QUEST_BITMASK_SIZE; use crate::TITLE_UNLOCK_BITMASK_SIZE; use crate::common::ObjectTypeId; @@ -467,6 +470,8 @@ pub enum ServerZoneIpcData { TitleList { unlock_bitmask: [u8; TITLE_UNLOCK_BITMASK_SIZE], }, + #[br(pre_assert(*magic == ServerZoneIpcType::QuestActiveList))] + QuestActiveList(QuestActiveList), Unknown { #[br(count = size - 32)] unk: Vec, @@ -943,6 +948,10 @@ mod tests { ServerZoneIpcType::FreeCompanyInfo, ServerZoneIpcData::FreeCompanyInfo { unk: [0; 80] }, ), + ( + ServerZoneIpcType::QuestActiveList, + ServerZoneIpcData::QuestActiveList(QuestActiveList::default()), + ), ]; for (opcode, data) in &ipc_types { diff --git a/src/ipc/zone/quest_active_list.rs b/src/ipc/zone/quest_active_list.rs new file mode 100644 index 0000000..15c0a02 --- /dev/null +++ b/src/ipc/zone/quest_active_list.rs @@ -0,0 +1,24 @@ +use binrw::binrw; + +#[binrw] +#[derive(Debug, Clone, Copy, Default)] +pub struct ActiveQuest { + pub id: u16, + pub sequence: u8, + #[brw(pad_after = 1)] // padding + pub flags: u8, + #[brw(pad_after = 1)] // padding + pub bitflags: [u8; 6], +} + +impl ActiveQuest { + pub const SIZE: usize = 16; +} + +#[binrw] +#[derive(Debug, Clone, Default)] +pub struct QuestActiveList { + #[br(count = 30)] + #[brw(pad_size_to = 30 * ActiveQuest::SIZE)] + pub quests: Vec, +} diff --git a/src/world/connection.rs b/src/world/connection.rs index 8964d70..1d00701 100644 --- a/src/world/connection.rs +++ b/src/world/connection.rs @@ -21,9 +21,9 @@ use crate::{ ActionEffect, ActionRequest, ActionResult, ActorControl, ActorControlCategory, ActorControlSelf, ActorControlTarget, ClientZoneIpcSegment, CommonSpawn, Config, ContainerInfo, CurrencyInfo, DisplayFlag, EffectKind, Equip, GameMasterRank, InitZone, - ItemInfo, Move, NpcSpawn, ObjectKind, PlayerStats, PlayerSubKind, ServerZoneIpcData, - ServerZoneIpcSegment, StatusEffect, StatusEffectList, UpdateClassInfo, Warp, - WeatherChange, + ItemInfo, Move, NpcSpawn, ObjectKind, PlayerStats, PlayerSubKind, QuestActiveList, + ServerZoneIpcData, ServerZoneIpcSegment, StatusEffect, StatusEffectList, + UpdateClassInfo, Warp, WeatherChange, }, }, opcodes::ServerZoneIpcType, @@ -1295,6 +1295,24 @@ impl ZoneConnection { } pub async fn send_quest_information(&mut self) { + // quest active list + { + let ipc = ServerZoneIpcSegment { + op_code: ServerZoneIpcType::QuestActiveList, + timestamp: timestamp_secs(), + data: ServerZoneIpcData::QuestActiveList(QuestActiveList::default()), + ..Default::default() + }; + + self.send_segment(PacketSegment { + source_actor: self.player_data.actor_id, + target_actor: self.player_data.actor_id, + segment_type: SegmentType::Ipc, + data: SegmentData::Ipc { data: ipc }, + }) + .await; + } + // quest complete list { let ipc = ServerZoneIpcSegment {