diff options
author | metamuffin <metamuffin@disroot.org> | 2024-06-04 15:19:39 +0200 |
---|---|---|
committer | metamuffin <metamuffin@disroot.org> | 2024-06-04 15:19:39 +0200 |
commit | ce0b808a01081322abc7ed51e09d0f452b606ad7 (patch) | |
tree | f3574b5b1a6d935629f78ec80b5ce191c41c4cfa /src/game | |
parent | 3d107ea4710f3dec0eedd91ed5bc1e52d8f15912 (diff) | |
download | gpn-tron-rust-ce0b808a01081322abc7ed51e09d0f452b606ad7.tar gpn-tron-rust-ce0b808a01081322abc7ed51e09d0f452b606ad7.tar.bz2 gpn-tron-rust-ce0b808a01081322abc7ed51e09d0f452b606ad7.tar.zst |
more code
Diffstat (limited to 'src/game')
-rw-r--r-- | src/game/protocol.rs | 2 | ||||
-rw-r--r-- | src/game/server.rs | 165 |
2 files changed, 151 insertions, 16 deletions
diff --git a/src/game/protocol.rs b/src/game/protocol.rs index c180282..abb246f 100644 --- a/src/game/protocol.rs +++ b/src/game/protocol.rs @@ -47,7 +47,7 @@ impl Packet { format!("chat|{message}") } Packet::Motd(motd) => format!("motd|{motd}"), - Packet::Error(message) => format!("erro|{message}"), + Packet::Error(message) => format!("error|{message}"), Packet::Game { my_id, width, diff --git a/src/game/server.rs b/src/game/server.rs index 1775bd1..a4472a8 100644 --- a/src/game/server.rs +++ b/src/game/server.rs @@ -1,14 +1,10 @@ -use super::{ - protocol::{Direction, Packet}, - Config, -}; +use super::{protocol::Packet, Config}; use crate::State; -use anyhow::{bail, Result}; -use glam::IVec2; -use log::{error, info}; +use anyhow::{anyhow, bail, Result}; +use log::{debug, error, info}; use std::sync::Arc; use tokio::{ - io::{AsyncBufReadExt, AsyncWriteExt, BufReader, BufWriter}, + io::{AsyncBufReadExt, AsyncWrite, AsyncWriteExt, BufReader, BufWriter}, net::{TcpListener, TcpStream}, spawn, }; @@ -17,9 +13,10 @@ pub async fn game_server(config: Config, state: Arc<State>) -> Result<()> { let listener = TcpListener::bind(config.bind).await?; info!("listening on {}", listener.local_addr()?); while let Ok((sock, addr)) = listener.accept().await { + let state = state.clone(); spawn(async move { info!("connected {addr}"); - if let Err(e) = handle_client(sock).await { + if let Err(e) = handle_client(sock, state).await { error!("client error: {e}") } info!("disconnected {addr}"); @@ -28,19 +25,157 @@ pub async fn game_server(config: Config, state: Arc<State>) -> Result<()> { bail!("accept failure") } -async fn handle_client(sock: TcpStream) -> Result<()> { +async fn handle_client(sock: TcpStream, state: Arc<State>) -> Result<()> { + let mut pid = None; + let res = handle_client_inner(sock, &state, &mut pid).await; + if let Some(pid) = pid { + state.players.write().await.remove(&pid); + } + res +} + +async fn handle_client_inner( + sock: TcpStream, + state: &Arc<State>, + pid: &mut Option<u32>, +) -> anyhow::Result<()> { let (rx, tx) = sock.into_split(); let rx = BufReader::new(rx); let mut tx = BufWriter::new(tx); - let mut lines = rx.lines(); + let mut ticks = state.tick.subscribe(); + let mut chat = state.chat.subscribe(); + tx.send_packet(Packet::Motd("This is the GPN-Tron Rust rewrite. The protocol should be compatible with the original: https://github.com/freehuntx/gpn-tron/blob/master/PROTOCOL.md".to_string())).await?; + loop { + tokio::select! { + message = chat.recv() => { + let (player, message) = message?; + tx.send_packet(Packet::Chat(format!("{player}: {message}"))).await?; + }, + new_game = ticks.recv() => { + let new_game = new_game?; + handle_tick(&mut tx, pid, state, new_game).await?; + }, + line = lines.next_line() => { + let line = line?.ok_or(anyhow!("eof"))?; + handle_packet(&mut tx, pid, state, line).await?; + }, + }; + } +} - while let Some(line) = lines.next_line().await? { - let packet = Packet::parse(&line)?; +trait SendPacketExt { + async fn send_packet(&mut self, packet: Packet) -> anyhow::Result<()>; +} +impl<T: AsyncWrite + Unpin> SendPacketExt for T { + async fn send_packet(&mut self, packet: Packet) -> anyhow::Result<()> { + debug!("-> {packet:?}"); + self.write_all(packet.dump().as_bytes()).await?; + self.write_all(b"\n").await?; + self.flush().await?; + Ok(()) + } +} - tx.write_all(packet.dump().as_bytes()).await?; - tx.flush().await?; +async fn handle_tick( + mut tx: impl AsyncWrite + Unpin, + pid: &mut Option<u32>, + state: &Arc<State>, + new_game: bool, +) -> anyhow::Result<()> { + let Some(pid) = pid else { return Ok(()) }; + let mut events = Vec::new(); + { + let g = state.game.read().await; + if new_game { + events.push(Packet::Game { + my_id: 0, + width: g.size.x as usize, + height: g.size.y as usize, + }); + for (player, (_, _, name)) in &g.heads { + events.push(Packet::Player { + id: *player, + name: name.to_owned(), + }) + } + } + for (player, (_, pos, _)) in &g.heads { + events.push(Packet::Pos { + id: *player, + x: pos.x, + y: pos.y, + }) + } + if !g.dead.is_empty() { + events.push(Packet::Die(g.dead.clone())); + } + if g.dead.contains(pid) { + events.push(Packet::Lose(0, 0)); // TODO implement stats + } + events.push(Packet::Tick); + } + for e in events { + tx.send_packet(e).await?; } + Ok(()) +} +async fn handle_packet( + mut tx: impl AsyncWrite + Unpin, + pid: &mut Option<u32>, + state: &Arc<State>, + line: String, +) -> anyhow::Result<()> { + let packet = match Packet::parse(&line) { + Ok(p) => p, + Err(e) => { + tx.send_packet(Packet::Error(format!("invalid packet: {e:?}"))) + .await?; + return Err(e); + } + }; + debug!("<- {packet:?}"); + match packet { + Packet::Join { + username, + password: _, + } => { + if pid.is_some() { + tx.send_packet(Packet::Error("already joined".to_string())) + .await? + } else { + let mut g = state.players.write().await; + let mut id = 0; + while g.contains_key(&id) { + id += 1; + } + g.insert(id, username); + *pid = Some(id); + } + } + Packet::Move(dir) => { + if let Some(pid) = pid { + let mut g = state.game.write().await; + if let Some((head_dir, _, _)) = g.heads.get_mut(pid) { + *head_dir = dir + } else { + drop(g); + tx.send_packet(Packet::Error("you are already dead".to_string())) + .await? + } + } else { + tx.send_packet(Packet::Error("you need to join first".to_string())) + .await? + } + } + Packet::Chat(message) => { + let _ = state.chat.send(("".to_string(), message)); + } + _ => { + tx.send_packet(Packet::Error("clients dont send this packet".to_string())) + .await? + } + }; Ok(()) } |