diff options
Diffstat (limited to 'client')
-rw-r--r-- | client/Cargo.toml | 13 | ||||
-rw-r--r-- | client/src/client.rs | 157 | ||||
-rw-r--r-- | client/src/main.rs | 10 |
3 files changed, 145 insertions, 35 deletions
diff --git a/client/Cargo.toml b/client/Cargo.toml index ab58a0f..471a905 100644 --- a/client/Cargo.toml +++ b/client/Cargo.toml @@ -9,12 +9,23 @@ env_logger = "0.11.6" anyhow = "1.0.95" arrayvec = "0.5.2" warn = ">=0.1.1,<0.3.0" +xdg = "2.5.2" winit = "0.30.8" twgame = { path = "../../twgame/twgame" } twgpu = { path = "../../twgpu/twgpu" } +twsnap = { path = "../../twsnap" } libtw2-gamenet-ddnet = { path = "../../libtw2/gamenet/ddnet" } +libtw2-gamenet-common = { path = "../../libtw2/gamenet/common" } libtw2-event-loop = { path = "../../libtw2/event-loop" } libtw2-packer = { path = "../../libtw2/packer" } -libtw2-snapshot = { path = "../../libtw2/snapshot" }
\ No newline at end of file +libtw2-demo = { path = "../../libtw2/demo" } +libtw2-snapshot = { path = "../../libtw2/snapshot" } + +# pre-rfc3243-libtw2-gamenet-ddnet = "*" +# pre-rfc3243-libtw2-gamenet-common = "*" +# pre-rfc3243-libtw2-event-loop = "*" +# pre-rfc3243-libtw2-packer = "*" +# pre-rfc3243-libtw2-snapshot = "*" +# pre-rfc3243-libtw2-demo = "*" 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 + )) +} diff --git a/client/src/main.rs b/client/src/main.rs index 02b9055..ab05178 100644 --- a/client/src/main.rs +++ b/client/src/main.rs @@ -3,18 +3,16 @@ use client::Client; use window::WindowState; use winit::event_loop::EventLoop; -pub mod window; pub mod client; +pub mod window; fn main() -> Result<()> { env_logger::init_from_env("LOG"); - Client::new()?; - - - // let evloop = EventLoop::new()?; - // evloop.run_app(&mut WindowState::new())?; + Client::new()?; + let evloop = EventLoop::new()?; + evloop.run_app(&mut WindowState::new())?; Ok(()) } |