Skip to content

Commit

Permalink
demo: Make DDNet demo writer agnostic over the protocol
Browse files Browse the repository at this point in the history
Needed for #53.
  • Loading branch information
heinrich5991 committed May 15, 2024
1 parent 2fb3aeb commit bf3717e
Show file tree
Hide file tree
Showing 4 changed files with 50 additions and 34 deletions.
1 change: 0 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion demo/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ binrw = "0.11.1"
buffer = "0.1.9"
libtw2-common = { path = "../common/" }
libtw2-gamenet-common = { path = "../gamenet/common/" }
libtw2-gamenet-ddnet = { path = "../gamenet/ddnet/" }
libtw2-huffman = { path = "../huffman/" }
libtw2-packer = { path = "../packer/" }
libtw2-snapshot = { path = "../snapshot/" }
Expand Down
58 changes: 35 additions & 23 deletions demo/src/ddnet/reader.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
use libtw2_gamenet_common::snap_obj::TypeId;
use libtw2_gamenet_ddnet::msg::Game;
use libtw2_gamenet_ddnet::snap_obj;
use libtw2_gamenet_ddnet::snap_obj::SnapObj;
use libtw2_gamenet_common::traits;
use libtw2_gamenet_common::traits::MessageExt as _;
use libtw2_gamenet_common::traits::Protocol;
use libtw2_gamenet_common::traits::ProtocolStatic;
use libtw2_packer::IntUnpacker;
use libtw2_packer::Unpacker;
use libtw2_snapshot::snap;
Expand All @@ -10,6 +11,7 @@ use libtw2_snapshot::Snap;
use libtw2_snapshot::SnapReader;
use std::collections::HashMap;
use std::io;
use std::marker::PhantomData;
use std::mem;
use std::slice;
use thiserror::Error;
Expand All @@ -35,13 +37,14 @@ pub enum ReadError {
ChunkOrder,
}

pub struct DemoReader {
pub struct DemoReader<P: for<'a> Protocol<'a>> {
raw: reader::Reader,
delta: Delta,
snap: Snap,
old_snap: Snap,
snap_reader: SnapReader,
snapshot: Snapshot,
snapshot: Snapshot<P::SnapObj>,
protocol: PhantomData<P>,
}

#[derive(Debug)]
Expand All @@ -51,7 +54,6 @@ pub enum Warning {
Packer(libtw2_packer::Warning),
ExcessItemData,
Gamenet(libtw2_gamenet_common::error::Error),
GamenetDdnet(libtw2_gamenet_ddnet::Error),
}

impl From<format::Warning> for Warning {
Expand All @@ -75,14 +77,14 @@ impl From<libtw2_packer::ExcessData> for Warning {
}
}

pub enum Chunk<'a> {
Message(Game<'a>),
Snapshot(slice::Iter<'a, (SnapObj, u16)>),
pub enum Chunk<'a, P: Protocol<'a>> {
Message(P::Game),
Snapshot(slice::Iter<'a, (P::SnapObj, u16)>),
Tick(i32),
Invalid,
}

impl DemoReader {
impl<P: for<'a> Protocol<'a>> DemoReader<P> {
pub fn new<R, W>(data: R, warn: &mut W) -> Result<Self, ReadError>
where
R: io::Read + io::Seek + 'static,
Expand All @@ -96,20 +98,21 @@ impl DemoReader {
old_snap: Snap::empty(),
snap_reader: SnapReader::new(),
snapshot: Snapshot::default(),
protocol: PhantomData,
})
}

pub fn next_chunk<W: Warn<Warning>>(
&mut self,
warn: &mut W,
) -> Result<Option<Chunk>, ReadError> {
) -> Result<Option<Chunk<P>>, ReadError> {
match self.raw.read_chunk(wrap(warn))? {
None => return Ok(None),
Some(RawChunk::Unknown) => Ok(Some(Chunk::Invalid)),
Some(RawChunk::Tick { tick, .. }) => Ok(Some(Chunk::Tick(tick))),
Some(RawChunk::Message(msg)) => {
let mut unpacker = Unpacker::new_from_demo(msg);
match Game::decode(wrap(warn), &mut unpacker) {
match P::Game::decode(wrap(warn), &mut unpacker) {
Ok(msg) => Ok(Some(Chunk::Message(msg))),
Err(err) => {
warn.warn(Warning::Gamenet(err));
Expand All @@ -125,20 +128,19 @@ impl DemoReader {
.snap_reader
.read(wrap(warn), swap, &mut unpacker)
.unwrap();
self.snapshot.build(warn, &self.snap)?;
self.snapshot.build::<P, _>(warn, &self.snap)?;
Ok(Some(Chunk::Snapshot(self.snapshot.objects.iter())))
}
Some(RawChunk::SnapshotDelta(dt)) => {
let mut unpacker = Unpacker::new(dt);
let obj_size = snap_obj::obj_size;
self.delta
.read(wrap(warn), obj_size, &mut unpacker)
.read(wrap(warn), P::obj_size, &mut unpacker)
.map_err(ReadError::Snap)?;
self.old_snap
.read_with_delta(wrap(warn), &self.snap, &self.delta)
.map_err(ReadError::Snap)?;
mem::swap(&mut self.old_snap, &mut self.snap);
self.snapshot.build(warn, &self.snap)?;
self.snapshot.build::<P, _>(warn, &self.snap)?;
Ok(Some(Chunk::Snapshot(self.snapshot.objects.iter())))
}
}
Expand All @@ -149,15 +151,25 @@ impl DemoReader {
}
}

#[derive(Default)]
struct Snapshot {
struct Snapshot<T> {
uuid_index: HashMap<u16, Uuid>,
pub objects: Vec<(SnapObj, u16)>,
pub objects: Vec<(T, u16)>,
}

impl Snapshot {
fn build<W>(&mut self, warn: &mut W, snap: &Snap) -> Result<(), ReadError>
impl<T> Default for Snapshot<T> {
fn default() -> Snapshot<T> {
Snapshot {
uuid_index: Default::default(),
objects: Default::default(),
}
}
}

impl<T> Snapshot<T> {
fn build<P, W>(&mut self, warn: &mut W, snap: &Snap) -> Result<(), ReadError>
where
P: ProtocolStatic<SnapObj = T>,
T: traits::SnapObj,
W: Warn<Warning>,
{
self.uuid_index.clear();
Expand Down Expand Up @@ -187,9 +199,9 @@ impl Snapshot {
TypeId::Uuid(*uuid)
};
let mut int_unpacker = IntUnpacker::new(item.data);
match SnapObj::decode_obj(wrap(warn), type_id, &mut int_unpacker) {
match P::SnapObj::decode_obj(wrap(warn), type_id, &mut int_unpacker) {
Ok(obj) => self.objects.push((obj, item.id)),
Err(err) => warn.warn(Warning::GamenetDdnet(err)),
Err(err) => warn.warn(Warning::Gamenet(err)),
}
}

Expand Down
24 changes: 15 additions & 9 deletions demo/src/ddnet/writer.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
use crate::format;
use libtw2_common::digest::Sha256;
use libtw2_common::num::Cast;
use libtw2_gamenet_ddnet::msg;
use libtw2_gamenet_ddnet::snap_obj;
use libtw2_gamenet_common::snap_obj;
use libtw2_gamenet_common::traits::MessageExt as _;
use libtw2_gamenet_common::traits::Protocol;
use libtw2_gamenet_common::traits::SnapObj as _;
use libtw2_packer::with_packer;
use libtw2_snapshot::snap;
use libtw2_snapshot::Delta;
use libtw2_snapshot::Snap;
use std::convert::TryInto;
use std::io;
use std::marker::PhantomData;
use std::mem;
use thiserror::Error;

Expand Down Expand Up @@ -38,9 +41,10 @@ impl From<snap::BuilderError> for WriteError {
}
}

/// DDNet demo writer, can only be used with `gamenet_ddnet`.
/// DDNet demo writer.
///
/// Automatically writes snapshot deltas.
pub struct DemoWriter {
pub struct DemoWriter<P: for<'a> Protocol<'a>> {
inner: crate::Writer,
// To verify the monotonic increase
last_tick: i32,
Expand All @@ -52,6 +56,7 @@ pub struct DemoWriter {
delta: Delta,
buf: arrayvec::ArrayVec<[u8; format::MAX_SNAPSHOT_SIZE]>,
i32_buf: Vec<i32>,
protocol: PhantomData<P>,
}

#[derive(Default)]
Expand Down Expand Up @@ -89,7 +94,7 @@ impl UuidIndex {
}
}

impl DemoWriter {
impl<P: for<'a> Protocol<'a>> DemoWriter<P> {
pub fn new<T: io::Write + io::Seek + 'static>(
file: T,
net_version: &[u8],
Expand Down Expand Up @@ -123,10 +128,11 @@ impl DemoWriter {
builder: snap::Builder::default(),
buf: arrayvec::ArrayVec::new(),
i32_buf: Vec::new(),
protocol: PhantomData,
})
}

pub fn write_snap<'a, T: Iterator<Item = (&'a snap_obj::SnapObj, u16)>>(
pub fn write_snap<'a, T: Iterator<Item = (&'a P::SnapObj, u16)>>(
&mut self,
tick: i32,
items: T,
Expand All @@ -142,7 +148,7 @@ impl DemoWriter {
Some(last_keyframe) => tick - last_keyframe > 250,
};

// Build snap with UUID items, which map the uuids of ex-items to type ids.
// Build snap with UUID items, which map the UUIDs of ex-items to type ids.
for (item, id) in items {
let type_id = match item.obj_type_id() {
snap_obj::TypeId::Ordinal(type_id) => type_id,
Expand All @@ -164,7 +170,7 @@ impl DemoWriter {
} else {
self.delta.create(&old_snap, &new_snap);
let delta = &self.delta;
with_packer(&mut self.buf, |p| delta.write(snap_obj::obj_size, p))
with_packer(&mut self.buf, |p| delta.write(P::obj_size, p))
.map_err(|_| WriteError::TooLargeSnap)?;
self.inner.write_snapshot_delta(&self.buf)?;
}
Expand All @@ -181,7 +187,7 @@ impl DemoWriter {
}
Ok(())
}
pub fn write_msg(&mut self, msg: &msg::Game) -> Result<(), WriteError> {
pub fn write_msg(&mut self, msg: &<P as Protocol<'_>>::Game) -> Result<(), WriteError> {
with_packer(&mut self.buf, |p| msg.encode(p)).map_err(|_| WriteError::TooLongNetMsg)?;
self.inner.write_message(self.buf.as_slice())?;
self.buf.clear();
Expand Down

0 comments on commit bf3717e

Please sign in to comment.