diff --git a/examples/capture_parser.rs b/examples/capture_parser/main.rs similarity index 55% rename from examples/capture_parser.rs rename to examples/capture_parser/main.rs index 7169bbe..5d2d1aa 100644 --- a/examples/capture_parser.rs +++ b/examples/capture_parser/main.rs @@ -1,3 +1,5 @@ +mod zip_context; + use std::collections::HashMap; use std::env; use std::io::{BufReader, Result as Res}; @@ -6,39 +8,21 @@ use std::fs::File; use std::path::Path; use std::time::Instant; -use endio_bit::BEBitReader; use lu_packets::{ auth::server::Message as AuthServerMessage, - raknet::client::replica::{ - ComponentConstruction, ComponentSerialization, ReplicaContext, - base_combat_ai::{BaseCombatAiConstruction, BaseCombatAiSerialization}, - bbb::{BbbConstruction, BbbSerialization}, - buff::BuffConstruction, - character::{CharacterConstruction, CharacterSerialization}, - controllable_physics::{ControllablePhysicsConstruction, ControllablePhysicsSerialization}, - destroyable::{DestroyableConstruction, DestroyableSerialization}, - fx::FxConstruction, - inventory::{InventoryConstruction, InventorySerialization}, - level_progression::{LevelProgressionConstruction, LevelProgressionSerialization}, - phantom_physics::{PhantomPhysicsConstruction, PhantomPhysicsSerialization}, - player_forced_movement::{PlayerForcedMovementConstruction, PlayerForcedMovementSerialization}, - possession_control::{PossessionControlConstruction, PossessionControlSerialization}, - simple_physics::{SimplePhysicsConstruction, SimplePhysicsSerialization}, - script::ScriptConstruction, - skill::SkillConstruction, - }, world::Lot, world::server::Message as WorldServerMessage, world::client::Message as WorldClientMessage, }; use rusqlite::{params, Connection}; -use zip::{ZipArchive, read::ZipFile}; +use zip::ZipArchive; +use self::zip_context::ZipContext; static mut PRINT_PACKETS: bool = false; -const COMP_ORDER : [u32; 11] = [1, 3, 40, 7, 4, 17, 5, 9, 60, 2, 107]; +const COMP_ORDER : [u32; 17] = [1, 3, 40, 98, 7, 110, 109, 106, 4, 17, 5, 9, 60, 48, 2, 44, 107]; -struct Cdclient { +pub struct Cdclient { conn: Connection, comp_cache: HashMap>, } @@ -50,9 +34,21 @@ impl Cdclient { let rows = stmt.query_map(params![lot], |row| row.get(0)).unwrap(); let mut comps = vec![]; for row in rows { - comps.push(row.unwrap()); + let value = row.unwrap(); + comps.push(value); + // special case: implied components + match value { + 2 => { comps.push(44); } + 4 => { comps.push(110); comps.push(109); comps.push(106); } + 7 => { comps.push(98); } + 48 => { comps.push(7); } + _ => {}, + } } dbg!(&comps); + comps.sort(); + comps.dedup(); + dbg!(&comps); comps.sort_by_key(|x| COMP_ORDER.iter().position(|y| y == x).unwrap_or(usize::MAX)); dbg!(&comps); self.comp_cache.insert(lot, comps); @@ -61,121 +57,6 @@ impl Cdclient { } } -struct ZipContext<'a> { - zip: ZipFile<'a>, - lots: &'a mut HashMap, - cdclient: &'a mut Cdclient, - assert_fully_read: bool, -} - -impl std::io::Read for ZipContext<'_> { - fn read(&mut self, buf: &mut [u8]) -> Res { - self.zip.read(buf) - } -} - -// hacky hardcoded components to be able to read player replicas without DB lookup -impl ReplicaContext for ZipContext<'_> { - fn get_comp_constructions(&mut self, network_id: u16, lot: Lot) -> Vec) -> Res>> { - use endio::Deserialize; - - let comps = self.cdclient.get_comps(lot); - - let mut constrs: Vec) -> Res>> = vec![]; - for comp in comps { - match comp { - 1 => { - constrs.push(|x| Ok(Box::new(ControllablePhysicsConstruction::deserialize(x)?))); - } - 2 => { - constrs.push(|x| Ok(Box::new(FxConstruction::deserialize(x)?))); - } - 3 => { - constrs.push(|x| Ok(Box::new(SimplePhysicsConstruction::deserialize(x)?))); - } - 4 => { - constrs.push(|x| Ok(Box::new(PossessionControlConstruction::deserialize(x)?))); - constrs.push(|x| Ok(Box::new(LevelProgressionConstruction::deserialize(x)?))); - constrs.push(|x| Ok(Box::new(PlayerForcedMovementConstruction::deserialize(x)?))); - constrs.push(|x| Ok(Box::new(CharacterConstruction::deserialize(x)?))); - } - 5 => { - constrs.push(|x| Ok(Box::new(ScriptConstruction::deserialize(x)?))); - } - 7 => { - constrs.push(|x| Ok(Box::new(BuffConstruction::deserialize(x)?))); - constrs.push(|x| Ok(Box::new(DestroyableConstruction::deserialize(x)?))); - } - 9 => { - constrs.push(|x| Ok(Box::new(SkillConstruction::deserialize(x)?))); - } - 17 => { - constrs.push(|x| Ok(Box::new(InventoryConstruction::deserialize(x)?))); - } - 40 => { - constrs.push(|x| Ok(Box::new(PhantomPhysicsConstruction::deserialize(x)?))); - } - 60 => { - constrs.push(|x| Ok(Box::new(BaseCombatAiConstruction::deserialize(x)?))); - } - 107 => { - constrs.push(|x| Ok(Box::new(BbbConstruction::deserialize(x)?))); - } - 31 | 55 | 56 | 68 => {}, - x => panic!("{}", x), - } - } - self.lots.insert(network_id, lot); - constrs - } - - fn get_comp_serializations(&mut self, network_id: u16) -> Vec) -> Res>> { - use endio::Deserialize; - - if let Some(lot) = self.lots.get(&network_id) { - let comps = self.cdclient.get_comps(*lot); - let mut sers: Vec) -> Res>> = vec![]; - for comp in comps { - match comp { - 1 => { - sers.push(|x| Ok(Box::new(ControllablePhysicsSerialization::deserialize(x)?))); - } - 3 => { - sers.push(|x| Ok(Box::new(SimplePhysicsSerialization::deserialize(x)?))); - } - 4 => { - sers.push(|x| Ok(Box::new(PossessionControlSerialization::deserialize(x)?))); - sers.push(|x| Ok(Box::new(LevelProgressionSerialization::deserialize(x)?))); - sers.push(|x| Ok(Box::new(PlayerForcedMovementSerialization::deserialize(x)?))); - sers.push(|x| Ok(Box::new(CharacterSerialization::deserialize(x)?))); - } - 7 => { - sers.push(|x| Ok(Box::new(DestroyableSerialization::deserialize(x)?))); - } - 17 => { - sers.push(|x| Ok(Box::new(InventorySerialization::deserialize(x)?))); - } - 40 => { - sers.push(|x| Ok(Box::new(PhantomPhysicsSerialization::deserialize(x)?))); - } - 60 => { - sers.push(|x| Ok(Box::new(BaseCombatAiSerialization::deserialize(x)?))); - } - 107 => { - sers.push(|x| Ok(Box::new(BbbSerialization::deserialize(x)?))); - } - 2 | 5 | 9 | 31 | 55 | 56 | 68 => {}, - x => panic!("{}", x), - } - } - self.assert_fully_read = true; - return sers; - } - self.assert_fully_read = false; - vec![] - } -} - fn visit_dirs(dir: &Path, cdclient: &mut Cdclient, level: usize) -> Res { let mut packet_count = 0; if dir.is_dir() { diff --git a/examples/capture_parser/zip_context.rs b/examples/capture_parser/zip_context.rs new file mode 100644 index 0000000..14f10cc --- /dev/null +++ b/examples/capture_parser/zip_context.rs @@ -0,0 +1,164 @@ +use std::collections::HashMap; +use std::io::Result as Res; + +use endio_bit::BEBitReader; +use lu_packets::{ + raknet::client::replica::{ + ComponentConstruction, ComponentSerialization, ReplicaContext, + base_combat_ai::{BaseCombatAiConstruction, BaseCombatAiSerialization}, + bbb::{BbbConstruction, BbbSerialization}, + buff::BuffConstruction, + character::{CharacterConstruction, CharacterSerialization}, + controllable_physics::{ControllablePhysicsConstruction, ControllablePhysicsSerialization}, + destroyable::{DestroyableConstruction, DestroyableSerialization}, + fx::FxConstruction, + inventory::{InventoryConstruction, InventorySerialization}, + level_progression::{LevelProgressionConstruction, LevelProgressionSerialization}, + phantom_physics::{PhantomPhysicsConstruction, PhantomPhysicsSerialization}, + player_forced_movement::{PlayerForcedMovementConstruction, PlayerForcedMovementSerialization}, + possession_control::{PossessionControlConstruction, PossessionControlSerialization}, + quickbuild::{QuickbuildConstruction, QuickbuildSerialization}, + simple_physics::{SimplePhysicsConstruction, SimplePhysicsSerialization}, + script::ScriptConstruction, + skill::SkillConstruction, + }, + world::Lot, +}; +use zip::read::ZipFile; + +use super::Cdclient; + +pub struct ZipContext<'a> { + pub zip: ZipFile<'a>, + pub lots: &'a mut HashMap, + pub cdclient: &'a mut Cdclient, + pub assert_fully_read: bool, +} + +impl std::io::Read for ZipContext<'_> { + fn read(&mut self, buf: &mut [u8]) -> Res { + self.zip.read(buf) + } +} + +// hacky hardcoded components to be able to read player replicas without DB lookup +impl ReplicaContext for ZipContext<'_> { + fn get_comp_constructions(&mut self, network_id: u16, lot: Lot) -> Vec) -> Res>> { + use endio::Deserialize; + + let comps = self.cdclient.get_comps(lot); + + let mut constrs: Vec) -> Res>> = vec![]; + for comp in comps { + match comp { + 1 => { + constrs.push(|x| Ok(Box::new(ControllablePhysicsConstruction::deserialize(x)?))); + } + 3 => { + constrs.push(|x| Ok(Box::new(SimplePhysicsConstruction::deserialize(x)?))); + } + 4 => { + constrs.push(|x| Ok(Box::new(CharacterConstruction::deserialize(x)?))); + } + 5 => { + constrs.push(|x| Ok(Box::new(ScriptConstruction::deserialize(x)?))); + } + 7 => { + constrs.push(|x| Ok(Box::new(DestroyableConstruction::deserialize(x)?))); + } + 9 => { + constrs.push(|x| Ok(Box::new(SkillConstruction::deserialize(x)?))); + } + 17 => { + constrs.push(|x| Ok(Box::new(InventoryConstruction::deserialize(x)?))); + } + 40 => { + constrs.push(|x| Ok(Box::new(PhantomPhysicsConstruction::deserialize(x)?))); + } + 44 => { + constrs.push(|x| Ok(Box::new(FxConstruction::deserialize(x)?))); + } + 48 => { + constrs.push(|x| Ok(Box::new(QuickbuildConstruction::deserialize(x)?))); + } + 60 => { + constrs.push(|x| Ok(Box::new(BaseCombatAiConstruction::deserialize(x)?))); + } + 98 => { + constrs.push(|x| Ok(Box::new(BuffConstruction::deserialize(x)?))); + } + 106 => { + constrs.push(|x| Ok(Box::new(PlayerForcedMovementConstruction::deserialize(x)?))); + } + 107 => { + constrs.push(|x| Ok(Box::new(BbbConstruction::deserialize(x)?))); + } + 109 => { + constrs.push(|x| Ok(Box::new(LevelProgressionConstruction::deserialize(x)?))); + } + 110 => { + constrs.push(|x| Ok(Box::new(PossessionControlConstruction::deserialize(x)?))); + } + 2 | 31 | 55 | 56 | 68 => {}, + x => panic!("{}", x), + } + } + self.lots.insert(network_id, lot); + constrs + } + + fn get_comp_serializations(&mut self, network_id: u16) -> Vec) -> Res>> { + use endio::Deserialize; + + if let Some(lot) = self.lots.get(&network_id) { + let comps = self.cdclient.get_comps(*lot); + let mut sers: Vec) -> Res>> = vec![]; + for comp in comps { + match comp { + 1 => { + sers.push(|x| Ok(Box::new(ControllablePhysicsSerialization::deserialize(x)?))); + } + 3 => { + sers.push(|x| Ok(Box::new(SimplePhysicsSerialization::deserialize(x)?))); + } + 4 => { + sers.push(|x| Ok(Box::new(CharacterSerialization::deserialize(x)?))); + } + 7 => { + sers.push(|x| Ok(Box::new(DestroyableSerialization::deserialize(x)?))); + } + 17 => { + sers.push(|x| Ok(Box::new(InventorySerialization::deserialize(x)?))); + } + 40 => { + sers.push(|x| Ok(Box::new(PhantomPhysicsSerialization::deserialize(x)?))); + } + 48 => { + sers.push(|x| Ok(Box::new(QuickbuildSerialization::deserialize(x)?))); + } + 60 => { + sers.push(|x| Ok(Box::new(BaseCombatAiSerialization::deserialize(x)?))); + } + 106 => { + sers.push(|x| Ok(Box::new(PlayerForcedMovementSerialization::deserialize(x)?))); + } + 107 => { + sers.push(|x| Ok(Box::new(BbbSerialization::deserialize(x)?))); + } + 109 => { + sers.push(|x| Ok(Box::new(LevelProgressionSerialization::deserialize(x)?))); + } + 110 => { + sers.push(|x| Ok(Box::new(PossessionControlSerialization::deserialize(x)?))); + } + 2 | 5 | 9 | 31 | 44 | 55 | 56 | 68 | 98 => {}, + x => panic!("{}", x), + } + } + self.assert_fully_read = true; + return sers; + } + self.assert_fully_read = false; + vec![] + } +} diff --git a/src/raknet/client/replica/mod.rs b/src/raknet/client/replica/mod.rs index 49a1dcf..d9027c2 100644 --- a/src/raknet/client/replica/mod.rs +++ b/src/raknet/client/replica/mod.rs @@ -10,6 +10,7 @@ pub mod level_progression; pub mod phantom_physics; pub mod player_forced_movement; pub mod possession_control; +pub mod quickbuild; pub mod script; pub mod simple_physics; pub mod skill; diff --git a/src/raknet/client/replica/quickbuild.rs b/src/raknet/client/replica/quickbuild.rs new file mode 100644 index 0000000..0e54d01 --- /dev/null +++ b/src/raknet/client/replica/quickbuild.rs @@ -0,0 +1,72 @@ +use std::io::Result as Res; + +use endio::Serialize; +use endio_bit::BEBitWriter; +use lu_packets_derive::{BitVariantTests, ReplicaSerde}; + +use crate::common::{LVec, ObjId}; +use crate::world::Vector3; +use crate::world::gm::client::RebuildChallengeState; +use super::{ComponentConstruction, ComponentSerialization}; + +#[derive(Debug, PartialEq, ReplicaSerde)] +pub struct ActivityUserInfo { + pub user_object_id: ObjId, + // todo[min_const_generics] + // pub activity_values: [f32; 10], + pub activity_value_0: f32, + pub activity_value_1: f32, + pub activity_value_2: f32, + pub activity_value_3: f32, + pub activity_value_4: f32, + pub activity_value_5: f32, + pub activity_value_6: f32, + pub activity_value_7: f32, + pub activity_value_8: f32, + pub activity_value_9: f32, +} + +#[derive(Debug, PartialEq, ReplicaSerde)] +pub struct QuickbuildConstructionInfo { + pub current_state: RebuildChallengeState, + pub show_reset_effect: bool, + pub has_activator: bool, + pub duration_timer: f32, + pub total_incomplete_time: f32, + pub unknown: Option, + pub activator_position: Vector3, + pub reposition_player: bool, +} + +#[derive(BitVariantTests, Debug, PartialEq, ReplicaSerde)] +pub struct QuickbuildConstruction { + pub activity_user_infos: Option>, + pub quickbuild_construction_info: Option, +} + +impl ComponentConstruction for QuickbuildConstruction { + fn ser(&self, writer: &mut BEBitWriter>) -> Res<()> { + self.serialize(writer) + } +} + +#[derive(Debug, PartialEq, ReplicaSerde)] +pub struct QuickbuildSerializationInfo { + pub current_state: RebuildChallengeState, + pub show_reset_effect: bool, + pub has_activator: bool, + pub duration_timer: f32, + pub total_incomplete_time: f32, +} + +#[derive(BitVariantTests, Debug, PartialEq, ReplicaSerde)] +pub struct QuickbuildSerialization { + pub activity_user_infos: Option>, + pub quickbuild_serialization_info: Option, +} + +impl ComponentSerialization for QuickbuildSerialization { + fn ser(&self, writer: &mut BEBitWriter>) -> Res<()> { + self.serialize(writer) + } +} diff --git a/src/raknet/client/replica/tests/QuickbuildConstruction.bin b/src/raknet/client/replica/tests/QuickbuildConstruction.bin new file mode 100644 index 0000000..954063b Binary files /dev/null and b/src/raknet/client/replica/tests/QuickbuildConstruction.bin differ diff --git a/src/raknet/client/replica/tests/QuickbuildConstruction.rs b/src/raknet/client/replica/tests/QuickbuildConstruction.rs new file mode 100644 index 0000000..71b515b --- /dev/null +++ b/src/raknet/client/replica/tests/QuickbuildConstruction.rs @@ -0,0 +1,31 @@ +QuickbuildConstruction { + activity_user_infos: Some(vec![ + ActivityUserInfo { + user_object_id: 1152921507960010807, + activity_value_0: 0.0, + activity_value_1: 1.0, + activity_value_2: 2.0, + activity_value_3: 3.0, + activity_value_4: 4.0, + activity_value_5: 5.0, + activity_value_6: 6.0, + activity_value_7: 7.0, + activity_value_8: 8.0, + activity_value_9: 9.0, + } + ].into()), + quickbuild_construction_info: Some(QuickbuildConstructionInfo { + current_state: RebuildChallengeState::Open, + show_reset_effect: true, + has_activator: true, + duration_timer: 10.0, + total_incomplete_time: 11.0, + unknown: Some(12), + activator_position: Vector3 { + x: 13.0, + y: 14.0, + z: 15.0, + }, + reposition_player: true, + }), +} diff --git a/src/raknet/client/replica/tests/QuickbuildSerialization.bin b/src/raknet/client/replica/tests/QuickbuildSerialization.bin new file mode 100644 index 0000000..d5b1abd Binary files /dev/null and b/src/raknet/client/replica/tests/QuickbuildSerialization.bin differ diff --git a/src/raknet/client/replica/tests/QuickbuildSerialization.rs b/src/raknet/client/replica/tests/QuickbuildSerialization.rs new file mode 100644 index 0000000..07127ba --- /dev/null +++ b/src/raknet/client/replica/tests/QuickbuildSerialization.rs @@ -0,0 +1,24 @@ +QuickbuildSerialization { + activity_user_infos: Some(vec![ + ActivityUserInfo { + user_object_id: 1152921507960010807, + activity_value_0: 0.0, + activity_value_1: 1.0, + activity_value_2: 2.0, + activity_value_3: 3.0, + activity_value_4: 4.0, + activity_value_5: 5.0, + activity_value_6: 6.0, + activity_value_7: 7.0, + activity_value_8: 8.0, + activity_value_9: 9.0, + } + ].into()), + quickbuild_serialization_info: Some(QuickbuildSerializationInfo { + current_state: RebuildChallengeState::Open, + show_reset_effect: true, + has_activator: true, + duration_timer: 10.0, + total_incomplete_time: 11.0, + }), +}