aboutsummaryrefslogtreecommitdiff
path: root/client/src/client.rs
diff options
context:
space:
mode:
Diffstat (limited to 'client/src/client.rs')
-rw-r--r--client/src/client.rs157
1 files changed, 129 insertions, 28 deletions
diff --git a/client/src/client.rs b/client/src/client.rs
index 0db4389..9f23c41 100644
--- a/client/src/client.rs
+++ b/client/src/client.rs
@@ -8,15 +8,21 @@ use libtw2_gamenet_ddnet::{
msg::{
self, Game, System, SystemOrGame,
game::{ClSetTeam, ClStartInfo},
- system::{EnterGame, Info, Ready},
+ system::{EnterGame, Info, MapChange, Ready, RequestMapData},
},
+ snap_obj::obj_size,
};
-use libtw2_packer::{Unpacker, with_packer};
-use log::warn;
+use libtw2_packer::{IntUnpacker, Unpacker, with_packer};
+use libtw2_snapshot::Manager as SnapManager;
+use log::{debug, info, warn};
use std::{
- fmt::{self, Debug},
+ fmt::Debug,
+ fs::File,
+ io::Write,
net::{IpAddr, Ipv4Addr},
+ path::PathBuf,
};
+use twsnap::{Snap, SnapObj};
pub struct Client {}
impl Client {
@@ -26,19 +32,35 @@ impl Client {
ip: IpAddr::V4(Ipv4Addr::LOCALHOST),
port: 8303,
});
- sloop.run(ClientNetwork {});
+ sloop.run(ClientNetwork {
+ state: NetworkState::New,
+ });
Ok(Self {})
}
}
-pub struct ClientNetwork {}
+pub struct ClientNetwork {
+ state: NetworkState,
+}
+enum NetworkState {
+ New,
+ Downloading {
+ chunk: i32,
+ data: Vec<u8>,
+ path: PathBuf,
+ size: usize,
+ },
+ Ready,
+ Ingame {
+ snap_manager: SnapManager,
+ snap: twsnap::Snap,
+ },
+}
impl<L: Loop> Application<L> for ClientNetwork {
fn needs_tick(&mut self) -> Timeout {
Timeout::inactive()
}
-
- fn on_tick(&mut self, loop_: &mut L) {}
-
+ fn on_tick(&mut self, _loop_: &mut L) {}
fn on_packet(&mut self, loop_: &mut L, chunk: Chunk) {
let pid = chunk.pid;
let msg;
@@ -49,21 +71,84 @@ impl<L: Loop> Application<L> for ClientNetwork {
return;
}
}
- eprintln!("{msg:?}");
+ debug!("<- {msg:?}");
match msg {
- SystemOrGame::System(System::MapChange(map)) => {
- loop_.sends(pid, Ready);
+ SystemOrGame::System(System::MapChange(MapChange { name, crc, size })) => {
+ let path = map_cache_path(name, crc);
+ if path.exists() {
+ loop_.sends(pid, Ready);
+ self.state = NetworkState::Ready
+ } else {
+ loop_.sends(pid, RequestMapData { chunk: 0 });
+ self.state = NetworkState::Downloading {
+ chunk: 0,
+ data: Vec::new(),
+ path,
+ size: size as usize,
+ }
+ }
+ }
+ SystemOrGame::System(System::MapData(p)) => {
+ if let NetworkState::Downloading {
+ chunk,
+ data,
+ path,
+ size,
+ } = &mut self.state
+ {
+ data.extend(p.data);
+ info!("download {} / {}", data.len(), size);
+ assert_eq!(*chunk, p.chunk);
+ *chunk += 1;
+ if p.last != 0 {
+ File::create(path).unwrap().write_all(&data).unwrap();
+ self.state = NetworkState::Ready;
+ loop_.sends(pid, Ready);
+ } else {
+ loop_.sends(pid, RequestMapData { chunk: *chunk });
+ }
+ }
}
SystemOrGame::System(System::ConReady(..)) => {
- loop_.sendg(pid, ClStartInfo {
- name: b"testclient",
- clan: b"metamuffin",
- country: -1,
- skin: b"limekittygirl",
- use_custom_color: true,
- color_body: 0xFF00FF,
- color_feet: 0x550055,
- });
+ if let NetworkState::Ready = &mut self.state {
+ loop_.sendg(pid, ClStartInfo {
+ name: b"testclient",
+ clan: b"metamuffin",
+ country: -1,
+ skin: b"limekittygirl",
+ use_custom_color: true,
+ color_body: 0xFF00FF,
+ color_feet: 0x550055,
+ });
+ self.state = NetworkState::Ingame {
+ snap: Snap::default(),
+ snap_manager: SnapManager::new(),
+ }
+ }
+ }
+ SystemOrGame::System(System::SnapSingle(p)) => {
+ if let NetworkState::Ingame { snap, snap_manager } = &mut self.state {
+ if let Some(new_snap) = snap_manager
+ .snap_single(&mut Warn(&[]), obj_size, p)
+ .unwrap()
+ {
+ let objs = new_snap
+ .items()
+ .filter_map(|i| {
+ let mut ints = IntUnpacker::new(i.data);
+ match SnapObj::decode_obj(&mut Warn(&[]), i.type_id, &mut ints) {
+ Ok(ob) => Some((ob, i.id)),
+ Err(e) => {
+ warn!("snap object decode error: {e:?} {:?}", i.type_id);
+ None
+ }
+ }
+ })
+ .collect::<Vec<_>>();
+ snap.process_next(objs.iter());
+ debug!("{snap:#?}");
+ }
+ }
}
SystemOrGame::Game(Game::SvReadyToEnter(..)) => {
loop_.sends(pid, EnterGame);
@@ -73,9 +158,9 @@ impl<L: Loop> Application<L> for ClientNetwork {
}
}
- fn on_connless_packet(&mut self, loop_: &mut L, chunk: ConnlessChunk) {}
+ fn on_connless_packet(&mut self, _loop_: &mut L, _chunk: ConnlessChunk) {}
- fn on_connect(&mut self, loop_: &mut L, pid: PeerId) {
+ fn on_connect(&mut self, _loop_: &mut L, _pid: PeerId) {
todo!()
}
@@ -87,12 +172,13 @@ impl<L: Loop> Application<L> for ClientNetwork {
loop_.flush(pid);
}
- fn on_disconnect(&mut self, loop_: &mut L, pid: PeerId, remote: bool, reason: &[u8]) {}
+ fn on_disconnect(&mut self, _loop_: &mut L, _pid: PeerId, _remote: bool, _reason: &[u8]) {}
}
-// Copied straight from libtw2/downloader/src/main.rs by heinrich5991, MIT-or-Apache-2.0
+// Adapted from from libtw2/downloader/src/main.rs by heinrich5991, MIT-or-Apache-2.0
trait LoopExt: Loop {
- fn sends<'a, S: Into<System<'a>>>(&mut self, pid: PeerId, msg: S) {
+ fn sends<'a, S: Into<System<'a>> + Debug>(&mut self, pid: PeerId, msg: S) {
+ debug!("-> System({msg:?})");
fn inner<L: Loop + ?Sized>(msg: System, pid: PeerId, loop_: &mut L) {
let mut buf: ArrayVec<[u8; 2048]> = ArrayVec::new();
with_packer(&mut buf, |p| msg.encode(p).unwrap());
@@ -104,7 +190,8 @@ trait LoopExt: Loop {
}
inner(msg.into(), pid, self)
}
- fn sendg<'a, G: Into<Game<'a>>>(&mut self, pid: PeerId, msg: G) {
+ fn sendg<'a, G: Into<Game<'a>> + Debug>(&mut self, pid: PeerId, msg: G) {
+ debug!("-> System({msg:?})");
fn inner<L: Loop + ?Sized>(msg: Game, pid: PeerId, loop_: &mut L) {
let mut buf: ArrayVec<[u8; 2048]> = ArrayVec::new();
with_packer(&mut buf, |p| msg.encode(p).unwrap());
@@ -119,10 +206,24 @@ trait LoopExt: Loop {
}
impl<L: Loop> LoopExt for L {}
-struct Warn<'a>(&'a [u8]);
+struct Warn<'a>(#[allow(unused)] &'a [u8]);
impl<'a, W: Debug> warn::Warn<W> for Warn<'a> {
fn warn(&mut self, w: W) {
warn!("{:?}", w);
}
}
+
+fn map_cache_path(name: &[u8], crc: i32) -> PathBuf {
+ xdg::BaseDirectories::with_prefix("twclient")
+ .unwrap()
+ .create_cache_directory("maps")
+ .unwrap()
+ .join(format!(
+ "{}_{:08x}",
+ String::from_utf8_lossy(name)
+ .replace("/", "")
+ .replace(".", ""),
+ crc as u32
+ ))
+}