use super::{DemoKind, DemoMapHash};
use crate::time::Instant;
use crate::Snap;
use libtw2_common::digest::Sha256;
use libtw2_demo::{ddnet, DemoKind as LibDemoKind};
use libtw2_gamenet_ddnet::msg::game::SvChat;
use libtw2_gamenet_ddnet::msg::{game, Game};
use libtw2_gamenet_ddnet::Protocol as DDNet;
use libtw2_gamenet_ddnet::SnapObj;
use std::cmp::min;
use std::error;
use std::fmt;
use std::io;
pub struct DemoWriter {
inner: ddnet::DemoWriter<DDNet>,
item_storage: Vec<(SnapObj, u16)>,
teams: [u8; 64],
}
#[derive(Debug)]
pub struct WriteError(ddnet::WriteError);
impl fmt::Display for WriteError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.fmt(f)
}
}
impl error::Error for WriteError {}
impl From<ddnet::WriteError> for WriteError {
fn from(value: ddnet::WriteError) -> Self {
Self(value)
}
}
impl DemoWriter {
pub fn new<T: io::Write + io::Seek + 'static>(
file: T,
kind: DemoKind,
timestamp: &str,
net_version: &str,
map_name: &str,
map_data: Option<&[u8]>,
hash: DemoMapHash,
length: i32,
) -> Result<Self, WriteError> {
let kind = match kind {
DemoKind::Client => LibDemoKind::Client,
DemoKind::Server => LibDemoKind::Server,
};
let (sha256, crc) = match hash {
DemoMapHash::Crc(crc) => (None, crc),
DemoMapHash::Sha256(sha256) => (Some(Sha256(sha256)), 0),
};
let inner = ddnet::DemoWriter::new(
file,
net_version[..min(net_version.len(), 63)].as_bytes(),
map_name[..min(map_name.len(), 63)].as_bytes(),
sha256,
crc,
kind,
length,
timestamp[..min(timestamp.len(), 19)].as_bytes(),
map_data.unwrap_or(&[]),
)?;
Ok(Self {
inner,
item_storage: Vec::new(),
teams: [0; 64],
})
}
}
impl DemoWriter {
pub fn write_snapshot(&mut self, tick: Instant, snap: &Snap) -> Result<(), WriteError> {
let new_teams = snap.to_ddnet(&mut self.item_storage);
if self.teams != new_teams {
let mut teams = [0i32; 64];
for (i, t) in new_teams.iter().enumerate() {
teams[i] = *t as i32;
}
self.inner
.write_msg(&Game::SvTeamsState(game::SvTeamsState { teams }))?;
self.teams = new_teams;
}
self.inner.write_snap(
tick.snap_tick(),
self.item_storage.iter().map(|(obj, id)| (obj, *id)),
)?;
Ok(())
}
pub fn write_chat(&mut self, msg: &str) -> Result<(), WriteError> {
self.inner
.write_msg(&libtw2_gamenet_ddnet::msg::Game::SvChat(SvChat {
team: 0,
client_id: -1,
message: msg.as_bytes(),
}))?;
Ok(())
}
pub fn write_player_chat(
&mut self,
player_id: i32, msg: &str,
) -> Result<(), WriteError> {
self.inner
.write_msg(&libtw2_gamenet_ddnet::msg::Game::SvChat(SvChat {
team: 0,
client_id: player_id,
message: msg.as_bytes(),
}))?;
Ok(())
}
}