mirror of
https://github.com/redstrate/Kawari.git
synced 2025-04-20 14:47:45 +00:00
Add packets related to playing events and scenes
This commit is contained in:
parent
cb740a2a93
commit
bcbe62af06
12 changed files with 257 additions and 5 deletions
|
@ -129,6 +129,16 @@
|
||||||
"name": "ContainerInfo",
|
"name": "ContainerInfo",
|
||||||
"opcode": 566,
|
"opcode": 566,
|
||||||
"size": 16
|
"size": 16
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "EventPlay",
|
||||||
|
"opcode": 991,
|
||||||
|
"size": 40
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "EventStart",
|
||||||
|
"opcode": 955,
|
||||||
|
"size": 24
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"ClientZoneIpcType": [
|
"ClientZoneIpcType": [
|
||||||
|
@ -231,6 +241,11 @@
|
||||||
"name": "Unk18",
|
"name": "Unk18",
|
||||||
"opcode": 221,
|
"opcode": 221,
|
||||||
"size": 8
|
"size": 8
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "EventRelatedUnk",
|
||||||
|
"opcode": 861,
|
||||||
|
"size": 16
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"ServerLobbyIpcType": [
|
"ServerLobbyIpcType": [
|
||||||
|
|
BIN
resources/tests/event_play.bin
Normal file
BIN
resources/tests/event_play.bin
Normal file
Binary file not shown.
BIN
resources/tests/event_start.bin
Normal file
BIN
resources/tests/event_start.bin
Normal file
Binary file not shown.
|
@ -745,6 +745,16 @@ async fn main() {
|
||||||
ClientZoneIpcData::Unk18 { .. } => {
|
ClientZoneIpcData::Unk18 { .. } => {
|
||||||
tracing::info!("Recieved Unk18!");
|
tracing::info!("Recieved Unk18!");
|
||||||
}
|
}
|
||||||
|
ClientZoneIpcData::EventRelatedUnk {
|
||||||
|
unk1,
|
||||||
|
unk2,
|
||||||
|
unk3,
|
||||||
|
unk4,
|
||||||
|
} => {
|
||||||
|
tracing::info!(
|
||||||
|
"Recieved EventRelatedUnk! {unk1} {unk2} {unk3} {unk4}"
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
SegmentType::KeepAlive { id, timestamp } => {
|
SegmentType::KeepAlive { id, timestamp } => {
|
||||||
|
|
|
@ -20,7 +20,7 @@ pub use position::Position;
|
||||||
|
|
||||||
#[binrw]
|
#[binrw]
|
||||||
#[brw(little)]
|
#[brw(little)]
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub struct ObjectId(pub u32);
|
pub struct ObjectId(pub u32);
|
||||||
|
|
||||||
impl Default for ObjectId {
|
impl Default for ObjectId {
|
||||||
|
@ -32,7 +32,7 @@ impl Default for ObjectId {
|
||||||
// See https://github.com/aers/FFXIVClientStructs/blob/main/FFXIVClientStructs/FFXIV/Client/Game/Object/GameObject.cs#L158
|
// See https://github.com/aers/FFXIVClientStructs/blob/main/FFXIVClientStructs/FFXIV/Client/Game/Object/GameObject.cs#L158
|
||||||
#[binrw]
|
#[binrw]
|
||||||
#[brw(little)]
|
#[brw(little)]
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub struct ObjectTypeId {
|
pub struct ObjectTypeId {
|
||||||
pub object_id: ObjectId,
|
pub object_id: ObjectId,
|
||||||
#[brw(pad_after = 3)]
|
#[brw(pad_after = 3)]
|
||||||
|
|
|
@ -46,6 +46,8 @@ pub(crate) fn decompress<T: ReadWriteIpcSegment>(
|
||||||
|
|
||||||
let mut cursor = Cursor::new(&data);
|
let mut cursor = Cursor::new(&data);
|
||||||
|
|
||||||
|
std::fs::write("decompressed.bin", &data).unwrap();
|
||||||
|
|
||||||
for _ in 0..header.segment_count {
|
for _ in 0..header.segment_count {
|
||||||
let current_position = cursor.position();
|
let current_position = cursor.position();
|
||||||
segments.push(PacketSegment::read_options(
|
segments.push(PacketSegment::read_options(
|
||||||
|
|
|
@ -4,8 +4,9 @@ use crate::{
|
||||||
opcodes::ServerZoneIpcType,
|
opcodes::ServerZoneIpcType,
|
||||||
packet::{PacketSegment, SegmentType},
|
packet::{PacketSegment, SegmentType},
|
||||||
world::ipc::{
|
world::ipc::{
|
||||||
ActorControl, ActorControlCategory, BattleNpcSubKind, CommonSpawn, DisplayFlag, NpcSpawn,
|
ActorControl, ActorControlCategory, BattleNpcSubKind, CommonSpawn, DisplayFlag, EventPlay,
|
||||||
ObjectKind, PlayerSpawn, PlayerSubKind, ServerZoneIpcData, ServerZoneIpcSegment,
|
EventStart, NpcSpawn, ObjectKind, OnlineStatus, PlayerSpawn, PlayerSubKind,
|
||||||
|
ServerZoneIpcData, ServerZoneIpcSegment,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -265,6 +266,91 @@ impl ChatHandler {
|
||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
"!playscene" => {
|
||||||
|
// only works in ul'dah opening
|
||||||
|
|
||||||
|
// Load the game script for this event on the client
|
||||||
|
{
|
||||||
|
let ipc = ServerZoneIpcSegment {
|
||||||
|
unk1: 20,
|
||||||
|
unk2: 0,
|
||||||
|
op_code: ServerZoneIpcType::EventStart,
|
||||||
|
server_id: 0,
|
||||||
|
timestamp: timestamp_secs(),
|
||||||
|
data: ServerZoneIpcData::EventStart(EventStart {
|
||||||
|
target_id: ObjectTypeId {
|
||||||
|
object_id: ObjectId(connection.player_data.actor_id),
|
||||||
|
object_type: 0,
|
||||||
|
},
|
||||||
|
event_type: 15,
|
||||||
|
event_id: 0x130003,
|
||||||
|
flags: 0,
|
||||||
|
event_arg: 182,
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
|
||||||
|
connection
|
||||||
|
.send_segment(PacketSegment {
|
||||||
|
source_actor: connection.player_data.actor_id,
|
||||||
|
target_actor: connection.player_data.actor_id,
|
||||||
|
segment_type: SegmentType::Ipc { data: ipc },
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
|
||||||
|
// set our status icon to viewing cutscene
|
||||||
|
{
|
||||||
|
let ipc = ServerZoneIpcSegment {
|
||||||
|
unk1: 20,
|
||||||
|
unk2: 0,
|
||||||
|
op_code: ServerZoneIpcType::ActorControl,
|
||||||
|
server_id: 0,
|
||||||
|
timestamp: timestamp_secs(),
|
||||||
|
data: ServerZoneIpcData::ActorControl(ActorControl {
|
||||||
|
category: ActorControlCategory::SetStatusIcon {
|
||||||
|
icon: OnlineStatus::ViewingCutscene,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
|
||||||
|
connection
|
||||||
|
.send_segment(PacketSegment {
|
||||||
|
source_actor: connection.player_data.actor_id,
|
||||||
|
target_actor: connection.player_data.actor_id,
|
||||||
|
segment_type: SegmentType::Ipc { data: ipc },
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
|
||||||
|
// play the scene, bart
|
||||||
|
{
|
||||||
|
let ipc = ServerZoneIpcSegment {
|
||||||
|
unk1: 20,
|
||||||
|
unk2: 0,
|
||||||
|
op_code: ServerZoneIpcType::EventPlay,
|
||||||
|
server_id: 0,
|
||||||
|
timestamp: timestamp_secs(),
|
||||||
|
data: ServerZoneIpcData::EventPlay(EventPlay {
|
||||||
|
actor_id: ObjectTypeId {
|
||||||
|
object_id: ObjectId(connection.player_data.actor_id),
|
||||||
|
object_type: 0,
|
||||||
|
},
|
||||||
|
event_id: 0x130003,
|
||||||
|
scene: 1,
|
||||||
|
scene_flags: 4959237,
|
||||||
|
..Default::default()
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
|
||||||
|
connection
|
||||||
|
.send_segment(PacketSegment {
|
||||||
|
source_actor: connection.player_data.actor_id,
|
||||||
|
target_actor: connection.player_data.actor_id,
|
||||||
|
segment_type: SegmentType::Ipc { data: ipc },
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
}
|
||||||
_ => tracing::info!("Unrecognized debug command!"),
|
_ => tracing::info!("Unrecognized debug command!"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
use binrw::binrw;
|
use binrw::binrw;
|
||||||
|
|
||||||
|
use super::OnlineStatus;
|
||||||
|
|
||||||
// See https://github.com/awgil/ffxiv_reverse/blob/f35b6226c1478234ca2b7149f82d251cffca2f56/vnetlog/vnetlog/ServerIPC.cs#L266 for a REALLY useful list of known values
|
// See https://github.com/awgil/ffxiv_reverse/blob/f35b6226c1478234ca2b7149f82d251cffca2f56/vnetlog/vnetlog/ServerIPC.cs#L266 for a REALLY useful list of known values
|
||||||
#[binrw]
|
#[binrw]
|
||||||
#[derive(Debug, Eq, PartialEq, Clone)]
|
#[derive(Debug, Eq, PartialEq, Clone)]
|
||||||
|
@ -21,6 +23,11 @@ pub enum ActorControlCategory {
|
||||||
unk1: u32,
|
unk1: u32,
|
||||||
unk2: u32,
|
unk2: u32,
|
||||||
},
|
},
|
||||||
|
#[brw(magic = 0x01F8u16)]
|
||||||
|
SetStatusIcon {
|
||||||
|
#[brw(pad_before = 2)]
|
||||||
|
icon: OnlineStatus,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
#[binrw]
|
#[binrw]
|
||||||
|
|
|
@ -86,7 +86,7 @@ pub enum CharacterMode {
|
||||||
#[binrw]
|
#[binrw]
|
||||||
#[brw(little)]
|
#[brw(little)]
|
||||||
#[brw(repr = u8)]
|
#[brw(repr = u8)]
|
||||||
#[derive(Debug, Clone, Default, PartialEq)]
|
#[derive(Debug, Clone, Default, PartialEq, Eq)]
|
||||||
pub enum OnlineStatus {
|
pub enum OnlineStatus {
|
||||||
Offline = 0x0,
|
Offline = 0x0,
|
||||||
GameQA = 1,
|
GameQA = 1,
|
||||||
|
@ -94,6 +94,7 @@ pub enum OnlineStatus {
|
||||||
GameMasterBlue = 3,
|
GameMasterBlue = 3,
|
||||||
EventParticipant = 4,
|
EventParticipant = 4,
|
||||||
NewAdventurer = 32, // TODO: This is actually a flag!
|
NewAdventurer = 32, // TODO: This is actually a flag!
|
||||||
|
ViewingCutscene = 15,
|
||||||
#[default]
|
#[default]
|
||||||
Online = 47,
|
Online = 47,
|
||||||
}
|
}
|
||||||
|
|
57
src/world/ipc/event_play.rs
Normal file
57
src/world/ipc/event_play.rs
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
use binrw::binrw;
|
||||||
|
|
||||||
|
use crate::common::ObjectTypeId;
|
||||||
|
|
||||||
|
#[binrw]
|
||||||
|
#[brw(little)]
|
||||||
|
#[derive(Debug, Clone, Default)]
|
||||||
|
pub struct EventPlay {
|
||||||
|
pub actor_id: ObjectTypeId,
|
||||||
|
pub event_id: u32,
|
||||||
|
pub scene: u16,
|
||||||
|
#[brw(pad_before = 2)] // FIXME: um, i don't think this is empty!!
|
||||||
|
pub scene_flags: u32,
|
||||||
|
pub unk1: u32,
|
||||||
|
pub unk2: u8,
|
||||||
|
#[brw(pad_before = 3)]
|
||||||
|
pub unk3: u32,
|
||||||
|
pub unk4: u32,
|
||||||
|
pub unk5: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use std::{fs::read, io::Cursor, path::PathBuf};
|
||||||
|
|
||||||
|
use binrw::BinRead;
|
||||||
|
|
||||||
|
use crate::common::ObjectId;
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn read_intro_event_start() {
|
||||||
|
let mut d = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
|
||||||
|
d.push("resources/tests/event_play.bin");
|
||||||
|
|
||||||
|
let buffer = read(d).unwrap();
|
||||||
|
let mut buffer = Cursor::new(&buffer);
|
||||||
|
|
||||||
|
let event_play = EventPlay::read_le(&mut buffer).unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
event_play.actor_id,
|
||||||
|
ObjectTypeId {
|
||||||
|
object_id: ObjectId(277124129),
|
||||||
|
object_type: 0
|
||||||
|
}
|
||||||
|
);
|
||||||
|
assert_eq!(event_play.event_id, 0x130003); // aether intro
|
||||||
|
assert_eq!(event_play.scene, 0);
|
||||||
|
assert_eq!(event_play.scene_flags, 4959237);
|
||||||
|
assert_eq!(event_play.unk1, 0);
|
||||||
|
assert_eq!(event_play.unk2, 1);
|
||||||
|
assert_eq!(event_play.unk3, 0);
|
||||||
|
assert_eq!(event_play.unk4, 0);
|
||||||
|
assert_eq!(event_play.unk5, 0);
|
||||||
|
}
|
||||||
|
}
|
49
src/world/ipc/event_start.rs
Normal file
49
src/world/ipc/event_start.rs
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
use binrw::binrw;
|
||||||
|
|
||||||
|
use crate::common::ObjectTypeId;
|
||||||
|
|
||||||
|
#[binrw]
|
||||||
|
#[brw(little)]
|
||||||
|
#[derive(Debug, Clone, Default)]
|
||||||
|
pub struct EventStart {
|
||||||
|
pub target_id: ObjectTypeId,
|
||||||
|
pub event_id: u32,
|
||||||
|
pub event_type: u8,
|
||||||
|
pub flags: u8,
|
||||||
|
#[brw(pad_before = 2)]
|
||||||
|
#[brw(pad_after = 4)]
|
||||||
|
pub event_arg: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use std::{fs::read, io::Cursor, path::PathBuf};
|
||||||
|
|
||||||
|
use binrw::BinRead;
|
||||||
|
|
||||||
|
use crate::common::ObjectId;
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn read_intro_event_start() {
|
||||||
|
let mut d = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
|
||||||
|
d.push("resources/tests/event_start.bin");
|
||||||
|
|
||||||
|
let buffer = read(d).unwrap();
|
||||||
|
let mut buffer = Cursor::new(&buffer);
|
||||||
|
|
||||||
|
let event_start = EventStart::read_le(&mut buffer).unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
event_start.target_id,
|
||||||
|
ObjectTypeId {
|
||||||
|
object_id: ObjectId(277124129),
|
||||||
|
object_type: 0
|
||||||
|
}
|
||||||
|
);
|
||||||
|
assert_eq!(event_start.event_id, 0x130003); // aether intro
|
||||||
|
assert_eq!(event_start.event_type, 15);
|
||||||
|
assert_eq!(event_start.flags, 0);
|
||||||
|
assert_eq!(event_start.event_arg, 182);
|
||||||
|
}
|
||||||
|
}
|
|
@ -53,6 +53,12 @@ pub use container_info::{ContainerInfo, ContainerType};
|
||||||
mod item_info;
|
mod item_info;
|
||||||
pub use item_info::ItemInfo;
|
pub use item_info::ItemInfo;
|
||||||
|
|
||||||
|
mod event_play;
|
||||||
|
pub use event_play::EventPlay;
|
||||||
|
|
||||||
|
mod event_start;
|
||||||
|
pub use event_start::EventStart;
|
||||||
|
|
||||||
use crate::common::Position;
|
use crate::common::Position;
|
||||||
use crate::common::read_string;
|
use crate::common::read_string;
|
||||||
use crate::common::write_string;
|
use crate::common::write_string;
|
||||||
|
@ -205,6 +211,10 @@ pub enum ServerZoneIpcData {
|
||||||
ItemInfo(ItemInfo),
|
ItemInfo(ItemInfo),
|
||||||
/// Sent to inform the client of container status
|
/// Sent to inform the client of container status
|
||||||
ContainerInfo(ContainerInfo),
|
ContainerInfo(ContainerInfo),
|
||||||
|
/// Sent to tell the client to play a scene
|
||||||
|
EventPlay(EventPlay),
|
||||||
|
/// Sent to tell the client to load a scene, but not play it
|
||||||
|
EventStart(EventStart),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[binrw]
|
#[binrw]
|
||||||
|
@ -324,6 +334,13 @@ pub enum ClientZoneIpcData {
|
||||||
Unk18 {
|
Unk18 {
|
||||||
unk: [u8; 8], // TODO: unknown
|
unk: [u8; 8], // TODO: unknown
|
||||||
},
|
},
|
||||||
|
#[br(pre_assert(*magic == ClientZoneIpcType::EventRelatedUnk))]
|
||||||
|
EventRelatedUnk {
|
||||||
|
unk1: u32,
|
||||||
|
unk2: u32,
|
||||||
|
unk3: u32,
|
||||||
|
unk4: u32,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
@ -396,6 +413,14 @@ mod tests {
|
||||||
ServerZoneIpcType::ContainerInfo,
|
ServerZoneIpcType::ContainerInfo,
|
||||||
ServerZoneIpcData::ContainerInfo(ContainerInfo::default()),
|
ServerZoneIpcData::ContainerInfo(ContainerInfo::default()),
|
||||||
),
|
),
|
||||||
|
(
|
||||||
|
ServerZoneIpcType::EventPlay,
|
||||||
|
ServerZoneIpcData::EventPlay(EventPlay::default()),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
ServerZoneIpcType::EventStart,
|
||||||
|
ServerZoneIpcData::EventStart(EventStart::default()),
|
||||||
|
),
|
||||||
];
|
];
|
||||||
|
|
||||||
for (opcode, data) in &ipc_types {
|
for (opcode, data) in &ipc_types {
|
||||||
|
|
Loading…
Add table
Reference in a new issue