1
Fork 0
mirror of https://github.com/redstrate/Kawari.git synced 2025-07-17 10:47:44 +00:00

Begin implementing packet replay

It doesn't work fantastic yet, I need to move it to the global
server state to truly work as intended.
This commit is contained in:
Joshua Goins 2025-07-13 18:01:59 -04:00
parent ba1a92c9bc
commit 06debd5eb0
3 changed files with 97 additions and 3 deletions

View file

@ -1,3 +1,4 @@
use std::collections::VecDeque;
use std::net::SocketAddr;
use std::sync::{Arc, Mutex};
use std::time::{Duration, Instant};
@ -1184,6 +1185,49 @@ async fn client_loop(
intended_use: zone.intended_use,
};
}
if let Some(entry) = connection.replay_entries.pop_front() {
// only care about Ipc packets
let filename = entry.file_name().unwrap().to_str().unwrap();
if !filename.contains("Ipc") {
continue;
}
// only care about packets from the server
if !filename.contains("(to client)") {
continue;
}
let path = entry.to_str().unwrap();
tracing::info!("- Replaying {path}");
let source_actor_bytes = std::fs::read(format!("{path}/source_actor.bin")).unwrap();
let target_actor_bytes = std::fs::read(format!("{path}/target_actor.bin")).unwrap();
let source_actor = u32::from_le_bytes(source_actor_bytes[0..4].try_into().unwrap());
let target_actor = u32::from_le_bytes(target_actor_bytes[0..4].try_into().unwrap());
let ipc_header_bytes = std::fs::read(format!("{path}/ipc_header.bin")).unwrap();
let opcode = u16::from_le_bytes(ipc_header_bytes[2..4].try_into().unwrap());
let unk = std::fs::read(format!("{path}/data.bin")).unwrap();
connection.send_segment(PacketSegment {
source_actor,
target_actor,
segment_type: SegmentType::Ipc,
data: SegmentData::Ipc {
data: ServerZoneIpcSegment {
unk1: 20,
unk2: 0,
op_code: ServerZoneIpcType::Unknown(opcode),
option: 0,
timestamp: timestamp_secs(),
data: ServerZoneIpcData::Unknown { unk },
}
}
}).await;
}
}
},
Err(_) => {
@ -1303,6 +1347,7 @@ async fn main() {
gracefully_logged_out: false,
weather_id: 0,
obsfucation_data: ObsfucationData::default(),
replay_entries: VecDeque::default(),
});
}
Some((mut socket, _)) = handle_rcon(&rcon_listener) => {

View file

@ -111,6 +111,12 @@ impl ChatHandler {
}
true
}
"!replay" => {
let (_, path) = chat_message.message.split_once(' ').unwrap();
connection.replay_packets(path).await;
true
}
_ => false,
}
}

View file

@ -1,6 +1,7 @@
use std::{
collections::HashMap,
collections::{HashMap, VecDeque},
net::SocketAddr,
path::PathBuf,
sync::{Arc, Mutex},
time::Instant,
};
@ -134,6 +135,8 @@ pub struct ZoneConnection {
pub weather_id: u16,
pub obsfucation_data: ObsfucationData,
pub replay_entries: VecDeque<PathBuf>,
}
impl ZoneConnection {
@ -256,8 +259,11 @@ impl ZoneConnection {
}
pub async fn spawn_actor(&mut self, mut actor: Actor, mut spawn: NpcSpawn) {
// There is no reason for us to spawn our own player again. It's probably a bug!'
assert!(actor.id.0 != self.player_data.actor_id);
// skip during replay
if self.replay_entries.is_empty() {
// There is no reason for us to spawn our own player again. It's probably a bug!'
assert!(actor.id.0 != self.player_data.actor_id);
}
actor.spawn_index = self.get_free_spawn_index() as u32;
spawn.common.spawn_index = actor.spawn_index as u8;
@ -1484,4 +1490,41 @@ impl ZoneConnection {
.await;
}
}
pub async fn replay_packets(&mut self, path: &str) {
tracing::info!("Beginning replay from {path}...");
let mut entries = std::fs::read_dir(path)
.unwrap()
.map(|res| res.map(|e| e.path()))
.collect::<Result<Vec<_>, std::io::Error>>()
.unwrap();
entries.sort_by(|a, b| {
let a_seq = a
.file_name()
.unwrap()
.to_str()
.unwrap()
.split_once('-')
.unwrap()
.0
.parse::<i32>()
.unwrap();
let b_seq = b
.file_name()
.unwrap()
.to_str()
.unwrap()
.split_once('-')
.unwrap()
.0
.parse::<i32>()
.unwrap();
a_seq.cmp(&b_seq)
});
self.replay_entries = entries.into();
}
}