diff options
author | metamuffin <metamuffin@disroot.org> | 2024-09-02 00:37:45 +0200 |
---|---|---|
committer | metamuffin <metamuffin@disroot.org> | 2024-09-02 00:37:54 +0200 |
commit | 5fec7c7daa4741189a2749d7531e64b1ba6be58b (patch) | |
tree | 7fa1bfe2791ded4493743c1a973926192e70d9b6 /server | |
parent | 5b14aab4341f099c9f5b59ad6aec08c4a58827c3 (diff) | |
download | hurrycurry-5fec7c7daa4741189a2749d7531e64b1ba6be58b.tar hurrycurry-5fec7c7daa4741189a2749d7531e64b1ba6be58b.tar.bz2 hurrycurry-5fec7c7daa4741189a2749d7531e64b1ba6be58b.tar.zst |
merge ServerState and State
Diffstat (limited to 'server')
-rw-r--r-- | server/src/main.rs | 15 | ||||
-rw-r--r-- | server/src/server.rs | 145 | ||||
-rw-r--r-- | server/src/state.rs | 114 |
3 files changed, 104 insertions, 170 deletions
diff --git a/server/src/main.rs b/server/src/main.rs index 40ea3433..797e21f9 100644 --- a/server/src/main.rs +++ b/server/src/main.rs @@ -19,7 +19,11 @@ use anyhow::{anyhow, Result}; use clap::Parser; use futures_util::{SinkExt, StreamExt}; use hurrycurry_protocol::{PacketC, PacketS, BINCODE_CONFIG, VERSION}; -use hurrycurry_server::{data::DATA_DIR, server::GameServerExt, state::State, ConnectionID}; +use hurrycurry_server::{ + data::DATA_DIR, + server::{GameServerExt, Server}, + ConnectionID, +}; use log::{debug, info, trace, warn, LevelFilter}; use std::{ net::SocketAddr, @@ -100,7 +104,10 @@ async fn run(addr: SocketAddr) -> anyhow::Result<()> { let (tx, rx) = broadcast::channel::<PacketC>(128 * 1024); - let state = Arc::new(RwLock::new(State::new(tx).await?)); + let mut state = Server::new(tx).await?; + state.load(state.index.generate("lobby").await?, None); + let state = Arc::new(RwLock::new(state)); + { let state = state.clone(); spawn(async move { @@ -108,7 +115,7 @@ async fn run(addr: SocketAddr) -> anyhow::Result<()> { let mut tick = interval(Duration::from_secs_f32(dt)); loop { tick.tick().await; - if let Err(e) = state.write().await.tick(dt).await { + if let Err(e) = state.write().await.tick_outer(dt).await { warn!("tick failed: {e}"); } } @@ -216,7 +223,7 @@ async fn run(addr: SocketAddr) -> anyhow::Result<()> { } else { debug!("<- {id:?} {packet:?}"); } - let packet_out = match state.write().await.packet_in(id, packet).await { + let packet_out = match state.write().await.packet_in_outer(id, packet).await { Ok(packets) => packets, Err(e) => { warn!("client error: {e}"); diff --git a/server/src/server.rs b/server/src/server.rs index a5d1e1d8..12eca299 100644 --- a/server/src/server.rs +++ b/server/src/server.rs @@ -16,9 +16,10 @@ */ use crate::{ - data::Serverdata, + data::{DataIndex, Serverdata}, entity::{Entities, EntityContext}, interaction::{interact, tick_slot, InteractEffect, TickEffect}, + ConnectionID, }; use anyhow::{anyhow, bail, Result}; use hurrycurry_client_lib::{Game, Item, Player, Tile}; @@ -30,29 +31,25 @@ use hurrycurry_protocol::{ use log::{info, warn}; use rand::random; use std::{ - collections::{HashMap, VecDeque}, + collections::{HashMap, HashSet, VecDeque}, sync::Arc, time::{Duration, Instant}, }; +use tokio::sync::broadcast::Sender; + +pub struct Server { + pub game: Game, -pub struct ServerState { pub data: Arc<Serverdata>, pub entities: Entities, pub player_id_counter: PlayerID, pub score_changed: bool, pub packet_loopback: VecDeque<PacketS>, pub last_movement_update: HashMap<PlayerID, Instant>, -} - -pub struct Server<'a> { - pub game: &'a mut Game, - pub state: &'a mut ServerState, -} - -impl Default for ServerState { - fn default() -> Self { - Self::new() - } + pub index: DataIndex, + pub packet_out: VecDeque<PacketC>, + pub tx: Sender<PacketC>, + pub connections: HashMap<ConnectionID, HashSet<PlayerID>>, } pub trait GameServerExt { @@ -232,46 +229,48 @@ impl GameServerExt for Game { } } -impl ServerState { - pub fn new() -> Self { - Self { +impl Server { + pub async fn new(tx: Sender<PacketC>) -> Result<Self> { + let mut index = DataIndex::default(); + index.reload()?; + Ok(Self { + game: Game::default(), + index, + tx, + packet_out: VecDeque::new(), + connections: HashMap::new(), data: Serverdata::default().into(), entities: vec![], player_id_counter: PlayerID(1), score_changed: false, packet_loopback: VecDeque::new(), last_movement_update: HashMap::default(), - } + }) } } -impl Server<'_> { +impl Server { pub fn load( &mut self, (gamedata, serverdata, entities): (Gamedata, Serverdata, Entities), timer: Option<Duration>, - packet_out: &mut VecDeque<PacketC>, ) { - self.game.load(gamedata, &serverdata, timer, packet_out); - for mut e in self.state.entities.drain(..) { + self.game + .load(gamedata, &serverdata, timer, &mut self.packet_out); + for mut e in self.entities.drain(..) { e.destructor(EntityContext { - game: self.game, - packet_out, - packet_in: &mut self.state.packet_loopback, - score_changed: &mut self.state.score_changed, + game: &mut self.game, + packet_out: &mut self.packet_out, + packet_in: &mut self.packet_loopback, + score_changed: &mut self.score_changed, dt: 0., load_map: &mut None, }); } - self.state.data = serverdata.into(); - self.state.entities = entities; + self.data = serverdata.into(); + self.entities = entities; } - pub fn packet_in( - &mut self, - packet: PacketS, - replies: &mut Vec<PacketC>, - packet_out: &mut VecDeque<PacketC>, - ) -> Result<()> { + pub fn packet_in(&mut self, packet: PacketS, replies: &mut Vec<PacketC>) -> Result<()> { match packet { PacketS::Join { name, @@ -279,12 +278,12 @@ impl Server<'_> { id, } => { let id = id.unwrap_or_else(|| { - let id = self.state.player_id_counter; - self.state.player_id_counter.0 += 1; + let id = self.player_id_counter; + self.player_id_counter.0 += 1; id }); self.game - .join_player(id, name, character, &self.state.data, Some(packet_out)); + .join_player(id, name, character, &self.data, Some(&mut self.packet_out)); replies.push(PacketC::Joined { id }) } PacketS::Leave { player } => { @@ -300,7 +299,7 @@ impl Server<'_> { let pos = p.movement.position.floor().as_ivec2(); if let Some(tile) = self.game.tiles.get_mut(&pos) { if tile.item.is_none() { - packet_out.push_back(PacketC::SetItem { + self.packet_out.push_back(PacketC::SetItem { location: ItemLocation::Tile(pos), item: Some(item.kind), }); @@ -308,7 +307,8 @@ impl Server<'_> { } } } - packet_out.push_back(PacketC::RemovePlayer { id: player }) + self.packet_out + .push_back(PacketC::RemovePlayer { id: player }) } PacketS::Movement { pos, @@ -326,7 +326,6 @@ impl Server<'_> { if let Some(pos) = pos { let last_position_update = self - .state .last_movement_update .entry(player) .or_insert_with(|| Instant::now()); @@ -399,9 +398,9 @@ impl Server<'_> { &mut other.item, ItemLocation::Player(pid), None, - packet_out, + &mut self.packet_out, &mut self.game.score, - &mut self.state.score_changed, + &mut self.score_changed, false, ) } else { @@ -419,9 +418,9 @@ impl Server<'_> { &mut player.item, ItemLocation::Player(pid), Some(tile.kind), - packet_out, + &mut self.packet_out, &mut self.game.score, - &mut self.state.score_changed, + &mut self.score_changed, false, ) } @@ -445,7 +444,7 @@ impl Server<'_> { }); } } - packet_out.push_back(PacketC::Communicate { + self.packet_out.push_back(PacketC::Communicate { player, message, timeout: timeout.map(|t| MessageTimeout { @@ -464,7 +463,7 @@ impl Server<'_> { kind: i, active: None, }); - packet_out.push_back(PacketC::SetItem { + self.packet_out.push_back(PacketC::SetItem { location: ItemLocation::Player(player), item, }) @@ -473,7 +472,7 @@ impl Server<'_> { self.game.score.demands_completed += score.demands_completed; self.game.score.demands_failed += score.demands_failed; self.game.score.points += score.points; - self.state.score_changed = true; + self.score_changed = true; } PacketS::ReplayTick { .. } => bail!("packet not supported in this session"), } @@ -481,14 +480,11 @@ impl Server<'_> { } /// Returns true if the game should end - pub fn tick( - &mut self, - dt: f32, - packet_out: &mut VecDeque<PacketC>, - ) -> Option<(String, Option<Duration>)> { - if self.state.score_changed { - self.state.score_changed = false; - packet_out.push_back(PacketC::Score(self.game.score.clone())); + pub fn tick(&mut self, dt: f32) -> Option<(String, Option<Duration>)> { + if self.score_changed { + self.score_changed = false; + self.packet_out + .push_back(PacketC::Score(self.game.score.clone())); } for (&pos, tile) in &mut self.game.tiles { @@ -500,7 +496,7 @@ impl Server<'_> { &mut self.game.score, ) { match effect { - TickEffect::Progress(warn) => packet_out.push_back(PacketC::SetProgress { + TickEffect::Progress(warn) => self.packet_out.push_back(PacketC::SetProgress { warn, item: ItemLocation::Tile(pos), progress: tile @@ -512,12 +508,12 @@ impl Server<'_> { .map(|i| i.progress), }), TickEffect::Produce => { - packet_out.push_back(PacketC::SetProgress { + self.packet_out.push_back(PacketC::SetProgress { warn: false, item: ItemLocation::Tile(pos), progress: None, }); - packet_out.push_back(PacketC::SetItem { + self.packet_out.push_back(PacketC::SetItem { location: ItemLocation::Tile(pos), item: tile.item.as_ref().map(|i| i.kind), }); @@ -545,7 +541,7 @@ impl Server<'_> { }); for (&pid, player) in &mut self.game.players { - packet_out.push_back(PacketC::Movement { + self.packet_out.push_back(PacketC::Movement { player: pid, pos: player.movement.position, dir: player.movement.input_direction, @@ -561,7 +557,7 @@ impl Server<'_> { &mut self.game.score, ) { match effect { - TickEffect::Progress(warn) => packet_out.push_back(PacketC::SetProgress { + TickEffect::Progress(warn) => self.packet_out.push_back(PacketC::SetProgress { warn, item: ItemLocation::Player(pid), progress: player @@ -573,12 +569,12 @@ impl Server<'_> { .map(|i| i.progress), }), TickEffect::Produce => { - packet_out.push_back(PacketC::SetProgress { + self.packet_out.push_back(PacketC::SetProgress { warn: false, item: ItemLocation::Player(pid), progress: None, }); - packet_out.push_back(PacketC::SetItem { + self.packet_out.push_back(PacketC::SetItem { location: ItemLocation::Player(pid), item: player.item.as_ref().map(|i| i.kind), }); @@ -608,21 +604,17 @@ impl Server<'_> { } } for player in players_auto_release.drain(..) { - let _ = self.packet_in( - PacketS::Interact { pos: None, player }, - &mut vec![], - packet_out, - ); + let _ = self.packet_in(PacketS::Interact { pos: None, player }, &mut vec![]); } let mut load_map = None; - for entity in self.state.entities.iter_mut() { + for entity in self.entities.iter_mut() { if let Err(e) = entity.tick(EntityContext { - game: self.game, + game: &mut self.game, load_map: &mut load_map, - packet_out, - score_changed: &mut self.state.score_changed, - packet_in: &mut self.state.packet_loopback, + packet_out: &mut self.packet_out, + score_changed: &mut self.score_changed, + packet_in: &mut self.packet_loopback, dt, }) { warn!("entity tick failed: {e}") @@ -632,8 +624,8 @@ impl Server<'_> { return Some((map, Some(Duration::from_secs(300)))); } - while let Some(p) = self.state.packet_loopback.pop_front() { - if let Err(e) = self.packet_in(p, &mut vec![], packet_out) { + while let Some(p) = self.packet_loopback.pop_front() { + if let Err(e) = self.packet_in(p, &mut vec![]) { warn!("internal packet errored: {e}"); } } @@ -644,14 +636,15 @@ impl Server<'_> { self.game.score.time_remaining = (end - now).as_secs_f64(); if end < now { let relative_score = - (self.game.score.points * 100) / self.state.data.score_baseline.max(1); + (self.game.score.points * 100) / self.data.score_baseline.max(1); self.game.score.stars = match relative_score { 100.. => 3, 70.. => 2, 40.. => 1, _ => 0, }; - packet_out.push_back(PacketC::Menu(Menu::Score(self.game.score.clone()))); + self.packet_out + .push_back(PacketC::Menu(Menu::Score(self.game.score.clone()))); Some(("lobby".to_string(), None)) } else { None diff --git a/server/src/state.rs b/server/src/state.rs index fe5f846e..ad9eeb1c 100644 --- a/server/src/state.rs +++ b/server/src/state.rs @@ -15,32 +15,13 @@ along with this program. If not, see <https://www.gnu.org/licenses/>. */ -use crate::{ - data::DataIndex, - entity::bot::BotDriver, - server::{Server, ServerState}, - ConnectionID, -}; +use crate::{entity::bot::BotDriver, server::Server, ConnectionID}; use anyhow::{anyhow, bail, Result}; use clap::{Parser, ValueEnum}; use hurrycurry_bot::algos::ALGO_CONSTRUCTORS; -use hurrycurry_client_lib::Game; use hurrycurry_protocol::{Message, PacketC, PacketS, PlayerID}; use log::{debug, trace}; -use std::{ - collections::{HashMap, HashSet, VecDeque}, - time::Duration, -}; -use tokio::sync::broadcast::Sender; - -pub struct State { - index: DataIndex, - packet_out: VecDeque<PacketC>, - tx: Sender<PacketC>, - connections: HashMap<ConnectionID, HashSet<PlayerID>>, - pub server: ServerState, - pub game: Game, -} +use std::time::Duration; #[derive(Parser)] #[clap(multicall = true)] @@ -85,44 +66,11 @@ enum DownloadType { Recipes, } -impl State { - pub async fn new(tx: Sender<PacketC>) -> Result<Self> { - let mut index = DataIndex::default(); - index.reload()?; - - let mut packet_out = VecDeque::new(); - let mut game = Game::default(); - let mut server = ServerState::default(); - - { - Server { - game: &mut game, - state: &mut server, - } - .load(index.generate("lobby").await?, None, &mut packet_out); - } - - Ok(Self { - game, - server, - index, - tx, - packet_out, - connections: HashMap::new(), - }) - } - - pub async fn tick(&mut self, dt: f32) -> anyhow::Result<()> { - let mut server = Server { - game: &mut self.game, - state: &mut self.server, - }; - if let Some((name, timer)) = server.tick(dt, &mut self.packet_out) { - server.load( - self.index.generate(&name).await?, - timer, - &mut self.packet_out, - ); +impl Server { + pub async fn tick_outer(&mut self, dt: f32) -> anyhow::Result<()> { + let r = self.tick(dt); + if let Some((name, timer)) = r { + self.load(self.index.generate(&name).await?, timer); } while let Some(p) = self.packet_out.pop_front() { if matches!(p, PacketC::UpdateMap { .. } | PacketC::Movement { .. }) { @@ -134,7 +82,11 @@ impl State { } Ok(()) } - pub async fn packet_in(&mut self, conn: ConnectionID, packet: PacketS) -> Result<Vec<PacketC>> { + pub async fn packet_in_outer( + &mut self, + conn: ConnectionID, + packet: PacketS, + ) -> Result<Vec<PacketC>> { if let Some(p) = get_packet_player(&packet) { if !self.connections.entry(conn).or_default().contains(&p) { bail!("Packet sent to player that is not owned by this connection."); @@ -166,11 +118,7 @@ impl State { } _ => (), } - let mut server = Server { - game: &mut self.game, - state: &mut self.server, - }; - server.packet_in(packet, &mut replies, &mut self.packet_out)?; + self.packet_in(packet, &mut replies)?; for p in &replies { match p { @@ -181,17 +129,13 @@ impl State { } } - if server.count_chefs() <= 0 && !server.game.lobby { + if self.count_chefs() <= 0 && !self.game.lobby { self.tx .send(PacketC::ServerMessage { text: "Game was aborted automatically due to a lack of players".to_string(), }) .ok(); - server.load( - self.index.generate("lobby").await?, - None, - &mut self.packet_out, - ); + self.load(self.index.generate("lobby").await?, None); } Ok(replies) } @@ -199,7 +143,7 @@ impl State { pub async fn disconnect(&mut self, conn: ConnectionID) { if let Some(players) = self.connections.get(&conn) { for player in players.to_owned() { - let _ = self.packet_in(conn, PacketS::Leave { player }).await; + let _ = self.packet_in_outer(conn, PacketS::Leave { player }).await; } } self.connections.remove(&conn); @@ -221,22 +165,17 @@ impl State { } async fn handle_command(&mut self, player: PlayerID, command: Command) -> Result<()> { - let mut server = Server { - game: &mut self.game, - state: &mut self.server, - }; match command { Command::Start { spec, timer } => { let data = self.index.generate(&spec).await?; - server.load(data, Some(Duration::from_secs(timer)), &mut self.packet_out); + self.load(data, Some(Duration::from_secs(timer))); } Command::End => { self.tx .send(PacketC::ServerMessage { text: format!( "Game was aborted by {}.", - server - .game + self.game .players .get(&player) .ok_or(anyhow!("player missing"))? @@ -244,20 +183,15 @@ impl State { ), }) .ok(); - server.load( - self.index.generate("lobby").await?, - None, - &mut self.packet_out, - ); + self.load(self.index.generate("lobby").await?, None); } Command::Reload => { - if server.count_chefs() > 1 { + if self.count_chefs() > 1 { bail!("must be at most one player to reload"); } - server.load( - self.index.generate(&server.game.data.current_map).await?, + self.load( + self.index.generate(&self.game.data.current_map).await?, None, - &mut self.packet_out, ); } Command::ReloadIndex => { @@ -287,7 +221,7 @@ impl State { .ok(); } Command::Item { name } => { - let item = server + let item = self .game .data .get_item_by_name(&name) @@ -306,7 +240,7 @@ impl State { .find(|(name, _)| *name == algo.as_str()) .ok_or(anyhow!("algo name unknown"))?; let algo = cons(); - self.server.entities.push(Box::new(BotDriver::new( + self.entities.push(Box::new(BotDriver::new( format!("{}-bot", name.unwrap_or((*aname).to_owned())), 51, algo, |