summaryrefslogtreecommitdiff
path: root/server
diff options
context:
space:
mode:
authormetamuffin <metamuffin@disroot.org>2024-06-20 01:38:00 +0200
committermetamuffin <metamuffin@disroot.org>2024-06-23 19:21:49 +0200
commit720bb2e4f53d4467832ba59f97c9b6b6786181e7 (patch)
tree10ea600436adf2c101d9a1ccbf64808414bbc354 /server
parent6f7b995dd9fa3bea95be8c24e2452f015b410839 (diff)
downloadhurrycurry-720bb2e4f53d4467832ba59f97c9b6b6786181e7.tar
hurrycurry-720bb2e4f53d4467832ba59f97c9b6b6786181e7.tar.bz2
hurrycurry-720bb2e4f53d4467832ba59f97c9b6b6786181e7.tar.zst
type safe indecies
Diffstat (limited to 'server')
-rw-r--r--server/src/customer/mod.rs58
-rw-r--r--server/src/data.rs65
-rw-r--r--server/src/game.rs4
-rw-r--r--server/src/interaction.rs16
-rw-r--r--server/src/main.rs8
-rw-r--r--server/src/protocol.rs21
6 files changed, 107 insertions, 65 deletions
diff --git a/server/src/customer/mod.rs b/server/src/customer/mod.rs
index ecd99250..4c557d41 100644
--- a/server/src/customer/mod.rs
+++ b/server/src/customer/mod.rs
@@ -47,7 +47,7 @@ struct Customer {
pub async fn customer(game: Arc<RwLock<Game>>, mut grx: broadcast::Receiver<PacketC>) {
let mut state = CustomerManager {
- customer_id_counter: 0,
+ customer_id_counter: PlayerID(0),
walkable: Default::default(),
chairs: Default::default(),
items: Default::default(),
@@ -56,14 +56,14 @@ pub async fn customer(game: Arc<RwLock<Game>>, mut grx: broadcast::Receiver<Pack
data: Gamedata::default(),
},
};
- let initial = game.write().await.prime_client(-1);
+ let initial = game.write().await.prime_client(PlayerID(-1));
for p in initial {
match p {
PacketC::Init { data, .. } => {
state.demand.data = data;
}
PacketC::UpdateMap { pos, tile, .. } => {
- let tilename = &state.demand.data.tile_names[tile];
+ let tilename = state.demand.data.tile_name(tile);
if tilename == "floor" || tilename == "door" || tilename == "chair" {
state.walkable.insert(pos);
}
@@ -119,14 +119,14 @@ impl DemandState {
pub fn generate_demand(&self) -> DemandIndex {
// TODO insert sofa magic formula
- random::<usize>() % self.data.demands.len()
+ DemandIndex(random::<usize>() % self.data.demands.len())
}
}
impl CustomerManager {
pub fn tick(&mut self, packets_out: &mut Vec<(PlayerID, PacketS)>, dt: f32) {
if self.customers.len() < self.demand.target_customer_count() {
- self.customer_id_counter -= 1;
+ self.customer_id_counter.0 -= 1;
let id = self.customer_id_counter;
packets_out.push((
id,
@@ -164,7 +164,7 @@ impl CustomerManager {
packets_out.push((
id,
PacketS::Communicate {
- message: Some(Message::Item(self.demand.data.demands[demand].from)),
+ message: Some(Message::Item(self.demand.data.demand(demand).from)),
},
));
p.state = CustomerState::Waiting {
@@ -174,41 +174,39 @@ impl CustomerManager {
}
}
CustomerState::Waiting { chair, demand } => {
+ let demand = &self.demand.data.demand(*demand);
let demand_pos = [IVec2::NEG_X, IVec2::NEG_Y, IVec2::X, IVec2::Y]
.into_iter()
.find_map(|off| {
let pos = *chair + off;
- if self.items.get(&pos) == Some(demand) {
+ if self.items.get(&pos) == Some(&demand.from) {
Some(pos)
} else {
None
}
});
if let Some(pos) = demand_pos {
- let demand = &self.demand.data.demands[*demand];
- if self.items.get(&pos) == Some(&demand.from) {
- packets_out.push((id, PacketS::Communicate { message: None }));
- for edge in [true, false] {
- packets_out.push((id, PacketS::Interact { pos, edge }))
- }
- packets_out.push((
- id,
- PacketS::ReplaceHand {
- item: Some(demand.to),
- },
- ));
- for edge in [true, false] {
- packets_out.push((id, PacketS::Interact { pos, edge }))
- }
- let path = find_path(
- &self.walkable,
- p.movement.position.as_ivec2(),
- self.demand.data.customer_spawn.as_ivec2(),
- )
- .expect("no path to exit");
- *self.chairs.get_mut(&chair).unwrap() = true;
- p.state = CustomerState::Exiting { path }
+ packets_out.push((id, PacketS::Communicate { message: None }));
+ for edge in [true, false] {
+ packets_out.push((id, PacketS::Interact { pos, edge }))
+ }
+ packets_out.push((
+ id,
+ PacketS::ReplaceHand {
+ item: Some(demand.to),
+ },
+ ));
+ for edge in [true, false] {
+ packets_out.push((id, PacketS::Interact { pos, edge }))
}
+ let path = find_path(
+ &self.walkable,
+ p.movement.position.as_ivec2(),
+ self.demand.data.customer_spawn.as_ivec2(),
+ )
+ .expect("no path to exit");
+ *self.chairs.get_mut(&chair).unwrap() = true;
+ p.state = CustomerState::Exiting { path }
}
debug!("waiting")
}
diff --git a/server/src/data.rs b/server/src/data.rs
index e467fefa..51347c7c 100644
--- a/server/src/data.rs
+++ b/server/src/data.rs
@@ -1,4 +1,7 @@
-use crate::{interaction::Recipe, protocol::TileIndex};
+use crate::{
+ interaction::Recipe,
+ protocol::{DemandIndex, ItemIndex, RecipeIndex, TileIndex},
+};
use glam::{IVec2, Vec2};
use serde::{Deserialize, Serialize};
use std::{collections::HashMap, sync::RwLock};
@@ -39,16 +42,16 @@ pub struct DemandDecl {
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Demand {
- pub from: usize,
- pub to: usize,
+ pub from: ItemIndex,
+ pub to: ItemIndex,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct Gamedata {
- pub recipes: Vec<Recipe>,
+ recipes: Vec<Recipe>,
pub demands: Vec<Demand>,
- pub item_names: Vec<String>,
- pub tile_names: Vec<String>,
+ item_names: Vec<String>,
+ tile_names: Vec<String>,
#[serde(skip)]
pub initial_map: HashMap<IVec2, TileIndex>,
pub chef_spawn: Vec2,
@@ -67,9 +70,15 @@ pub fn build_gamedata(
for r in recipes_in {
let r2 = r.clone();
- let mut inputs = r.inputs.into_iter().map(|i| register(&item_names, i));
- let mut outputs = r.outputs.into_iter().map(|o| register(&item_names, o));
- let tile = r.tile.map(|t| register(&tile_names, t));
+ let mut inputs = r
+ .inputs
+ .into_iter()
+ .map(|i| ItemIndex(register(&item_names, i)));
+ let mut outputs = r
+ .outputs
+ .into_iter()
+ .map(|o| ItemIndex(register(&item_names, o)));
+ let tile = r.tile.map(|t| TileIndex(register(&tile_names, t)));
match r.action {
Action::Never => {}
Action::Passive(duration) => recipes.push(Recipe::Passive {
@@ -98,8 +107,8 @@ pub fn build_gamedata(
for d in demands_in {
demands.push(Demand {
- from: register(&item_names, d.from),
- to: register(&item_names, d.to),
+ from: ItemIndex(register(&item_names, d.from)),
+ to: ItemIndex(register(&item_names, d.to)),
})
}
@@ -118,7 +127,7 @@ pub fn build_gamedata(
customer_spawn = pos.as_vec2();
tilename = "floor".to_owned();
}
- let tile = register(&tile_names, tilename);
+ let tile = TileIndex(register(&tile_names, tilename));
initial_map.insert(pos, tile);
}
}
@@ -146,11 +155,35 @@ fn register(db: &RwLock<Vec<String>>, name: String) -> usize {
}
impl Gamedata {
- pub fn get_tile(&self, name: &str) -> Option<TileIndex> {
- self.tile_names.iter().position(|t| t == name)
+ pub fn tile_name(&self, index: TileIndex) -> &String {
+ &self.tile_names[index.0]
}
- pub fn get_item(&self, name: &str) -> Option<TileIndex> {
- self.item_names.iter().position(|t| t == name)
+ pub fn item_name(&self, index: ItemIndex) -> &String {
+ &self.item_names[index.0]
+ }
+ pub fn recipe(&self, index: RecipeIndex) -> &Recipe {
+ &self.recipes[index.0]
+ }
+ pub fn demand(&self, index: DemandIndex) -> &Demand {
+ &self.demands[index.0]
+ }
+ pub fn get_tile_by_name(&self, name: &str) -> Option<TileIndex> {
+ self.tile_names
+ .iter()
+ .position(|t| t == name)
+ .map(TileIndex)
+ }
+ pub fn get_item_by_name(&self, name: &str) -> Option<ItemIndex> {
+ self.item_names
+ .iter()
+ .position(|t| t == name)
+ .map(ItemIndex)
+ }
+ pub fn recipes(&self) -> impl Iterator<Item = (RecipeIndex, &Recipe)> {
+ self.recipes
+ .iter()
+ .enumerate()
+ .map(|(i, e)| (RecipeIndex(i), e))
}
}
impl Action {
diff --git a/server/src/game.rs b/server/src/game.rs
index b48e1833..e4693e4a 100644
--- a/server/src/game.rs
+++ b/server/src/game.rs
@@ -123,7 +123,7 @@ impl Game {
Player {
item: None,
character,
- position: if player < 0 {
+ position: if player.0 < 0 {
self.data.customer_spawn
} else {
self.data.chef_spawn
@@ -246,7 +246,7 @@ impl Game {
player.interacting = if edge { Some(pos) } else { None };
}
PacketS::Communicate { message } => {
- info!("{player} message {message:?}");
+ info!("{player:?} message {message:?}");
if let Some(player) = self.players.get_mut(&player) {
player.communicate = message.clone()
}
diff --git a/server/src/interaction.rs b/server/src/interaction.rs
index d822340f..a4b79bd0 100644
--- a/server/src/interaction.rs
+++ b/server/src/interaction.rs
@@ -70,7 +70,7 @@ pub fn interact(
if player.item.is_none() {
if let Some(item) = &mut tile.item {
if let Some(active) = &mut item.active {
- let recipe = &data.recipes[active.recipe];
+ let recipe = &data.recipe(active.recipe);
if recipe.supports_tile(tile.kind) {
if let Recipe::Active { outputs, .. } = recipe {
if edge {
@@ -92,7 +92,7 @@ pub fn interact(
return None;
}
- for (ri, recipe) in data.recipes.iter().enumerate() {
+ for (ri, recipe) in data.recipes() {
if !recipe.supports_tile(tile.kind) {
continue;
}
@@ -102,7 +102,7 @@ pub fn interact(
if let Some(item) = &mut tile.item {
if item.kind == *input {
if item.active.is_none() {
- info!("start active recipe {ri}");
+ info!("start active recipe {ri:?}");
item.active = Some(Involvement {
recipe: ri,
working: 1,
@@ -119,7 +119,7 @@ pub fn interact(
if let Some(active) = &mut item.active {
active.working += 1;
} else {
- info!("start active recipe {ri}");
+ info!("start active recipe {ri:?}");
item.active = Some(Involvement {
recipe: ri,
working: 1,
@@ -140,7 +140,7 @@ pub fn interact(
let ok = (inputs[0] == on_tile && inputs[1] == in_hand)
|| (inputs[1] == on_tile && inputs[0] == in_hand);
if ok {
- info!("instant recipe {ri}");
+ info!("instant recipe {ri:?}");
player.item = outputs[0].map(|kind| Item { kind, active: None });
tile.item = outputs[1].map(|kind| Item { kind, active: None });
return Some(InteractEffect::Produce);
@@ -173,11 +173,11 @@ pub enum TickEffect {
pub fn tick_tile(dt: f32, data: &Gamedata, tile: &mut Tile) -> Option<TickEffect> {
if let Some(item) = &mut tile.item {
if let Some(a) = &mut item.active {
- let r = &data.recipes[a.recipe];
+ let r = &data.recipe(a.recipe);
if r.supports_tile(tile.kind) {
a.progress += a.working as f32 * dt / r.duration().unwrap();
if a.progress >= 1. {
- if let Recipe::Passive { output, .. } = &data.recipes[a.recipe] {
+ if let Recipe::Passive { output, .. } = &data.recipe(a.recipe) {
tile.item = output.map(|kind| Item { kind, active: None });
return Some(TickEffect::Produce);
};
@@ -186,7 +186,7 @@ pub fn tick_tile(dt: f32, data: &Gamedata, tile: &mut Tile) -> Option<TickEffect
return Some(TickEffect::Progress);
}
} else {
- for (ri, recipe) in data.recipes.iter().enumerate() {
+ for (ri, recipe) in data.recipes() {
if let Some(tile_constraint) = recipe.tile() {
if tile.kind != tile_constraint {
continue;
diff --git a/server/src/main.rs b/server/src/main.rs
index 4d761b0c..3af71e17 100644
--- a/server/src/main.rs
+++ b/server/src/main.rs
@@ -14,7 +14,7 @@ use undercooked::{
customer::customer,
data::build_gamedata,
game::Game,
- protocol::{PacketC, PacketS},
+ protocol::{PacketC, PacketS, PlayerID},
};
#[tokio::main]
@@ -57,7 +57,7 @@ async fn main() -> Result<()> {
spawn(customer(game.clone(), rx.resubscribe()));
- for id in 1.. {
+ for id in (1..).map(PlayerID) {
tokio::select! {
r = raw_listener.accept() => {
let (sock, addr) = r?;
@@ -88,7 +88,7 @@ async fn main() -> Result<()> {
warn!("invalid json over tcp");
break
};
- debug!("<- {id} {packet:?}");
+ debug!("<- {id:?} {packet:?}");
if let Err(e) = game.write().await.packet_in(id, packet) {
warn!("client error: {e}");
}
@@ -133,7 +133,7 @@ async fn main() -> Result<()> {
warn!("invalid json over ws");
break
};
- debug!("<- {id} {packet:?}");
+ debug!("<- {id:?} {packet:?}");
if let Err(e) = game.write().await.packet_in(id, packet) {
warn!("client error: {e}");
}
diff --git a/server/src/protocol.rs b/server/src/protocol.rs
index 24fe2468..a629ffbf 100644
--- a/server/src/protocol.rs
+++ b/server/src/protocol.rs
@@ -2,11 +2,22 @@ use crate::data::Gamedata;
use glam::{IVec2, Vec2};
use serde::{Deserialize, Serialize};
-pub type PlayerID = i64;
-pub type ItemIndex = usize;
-pub type TileIndex = usize;
-pub type RecipeIndex = usize;
-pub type DemandIndex = usize;
+#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord, Hash)]
+#[serde(transparent)]
+pub struct PlayerID(pub i64);
+
+#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord, Hash)]
+#[serde(transparent)]
+pub struct ItemIndex(pub usize);
+#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord, Hash)]
+#[serde(transparent)]
+pub struct TileIndex(pub usize);
+#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord, Hash)]
+#[serde(transparent)]
+pub struct RecipeIndex(pub usize);
+#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord, Hash)]
+#[serde(transparent)]
+pub struct DemandIndex(pub usize);
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "snake_case", tag = "type")]