summaryrefslogtreecommitdiff
path: root/server
diff options
context:
space:
mode:
Diffstat (limited to 'server')
-rw-r--r--server/src/main.rs15
-rw-r--r--server/src/server.rs145
-rw-r--r--server/src/state.rs114
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,