diff options
-rw-r--r-- | server/src/customer/mod.rs | 2 | ||||
-rw-r--r-- | server/src/game.rs | 130 | ||||
-rw-r--r-- | server/src/interaction.rs | 35 | ||||
-rw-r--r-- | server/src/protocol.rs | 29 | ||||
-rw-r--r-- | test-client/main.ts | 75 | ||||
-rw-r--r-- | test-client/protocol.ts | 12 |
6 files changed, 155 insertions, 128 deletions
diff --git a/server/src/customer/mod.rs b/server/src/customer/mod.rs index 9e0dd797..c29b4852 100644 --- a/server/src/customer/mod.rs +++ b/server/src/customer/mod.rs @@ -118,7 +118,7 @@ impl DemandState { id, PacketS::Join { name: faker::name::fr_fr::Name().fake(), - character: -1 - (random::<i16>() as i32), + character: -1 - (random::<u16>() as i32), }, )); let chair = self.select_chair().ok_or(anyhow!("no free chair found"))?; diff --git a/server/src/game.rs b/server/src/game.rs index 54266351..711b66b5 100644 --- a/server/src/game.rs +++ b/server/src/game.rs @@ -18,8 +18,10 @@ use crate::{ customer::DemandState, data::Gamedata, - interaction::{interact, tick_tile, InteractEffect, TickEffect}, - protocol::{ItemIndex, Message, PacketC, PacketS, PlayerID, RecipeIndex, TileIndex}, + interaction::{interact, tick_slot, InteractEffect, TickEffect}, + protocol::{ + ItemIndex, ItemLocation, Message, PacketC, PacketS, PlayerID, RecipeIndex, TileIndex, + }, }; use anyhow::{anyhow, bail, Result}; use glam::{IVec2, Vec2}; @@ -65,6 +67,7 @@ pub struct Game { pub players: HashMap<PlayerID, Player>, packet_out: VecDeque<PacketC>, demand: Option<DemandState>, + score_changed: bool, pub points: i64, end: Option<Instant>, } @@ -78,6 +81,7 @@ impl Game { tiles: Default::default(), demand: None, end: None, + score_changed: false, points: 0, } } @@ -166,8 +170,8 @@ impl Game { name: player.name.clone(), }); if let Some(item) = &player.item { - out.push(PacketC::SetPlayerItem { - player: id, + out.push(PacketC::SetItem { + location: ItemLocation::Player(id), item: Some(item.kind), }) } @@ -191,8 +195,8 @@ impl Game { kind: Some(tdata.kind.clone()), }); if let Some(item) = &tdata.item { - out.push(PacketC::SetTileItem { - tile, + out.push(PacketC::SetItem { + location: ItemLocation::Tile(tile), item: Some(item.kind), }) } @@ -250,8 +254,8 @@ impl Game { let pos = p.position.floor().as_ivec2(); if let Some(tile) = self.tiles.get_mut(&pos) { if tile.item.is_none() { - self.packet_out.push_back(PacketC::SetTileItem { - tile: pos, + self.packet_out.push_back(PacketC::SetItem { + location: ItemLocation::Tile(pos), item: Some(item.kind), }); tile.item = Some(item); @@ -325,76 +329,59 @@ impl Game { if let Some(effect) = interact( &self.data, edge, - tile.kind, + Some(tile.kind), &mut tile.item, &mut player.item, &mut self.points, ) { match effect { - InteractEffect::Put => self.packet_out.push_back(PacketC::PutItem { - player: pid, - tile: pos, + InteractEffect::Put => self.packet_out.push_back(PacketC::MoveItem { + from: ItemLocation::Player(pid), + to: ItemLocation::Tile(pos), }), - InteractEffect::Take => self.packet_out.push_back(PacketC::TakeItem { - player: pid, - tile: pos, + InteractEffect::Take => self.packet_out.push_back(PacketC::MoveItem { + from: ItemLocation::Tile(pos), + to: ItemLocation::Player(pid), }), InteractEffect::Produce => { if tile_had_item { - self.packet_out.push_back(PacketC::SetActive { - tile: pos, + self.packet_out.push_back(PacketC::SetProgress { + item: ItemLocation::Tile(pos), progress: None, warn: false, }); - self.packet_out.push_back(PacketC::SetTileItem { - tile: pos, + self.packet_out.push_back(PacketC::SetItem { + location: ItemLocation::Tile(pos), item: None, }); } if player_had_item { - self.packet_out.push_back(PacketC::PutItem { - player: pid, - tile: pos, + self.packet_out.push_back(PacketC::MoveItem { + from: ItemLocation::Player(pid), + to: ItemLocation::Tile(pos), }); - self.packet_out.push_back(PacketC::SetTileItem { - tile: pos, + self.packet_out.push_back(PacketC::SetItem { + location: ItemLocation::Tile(pos), item: None, }); } if let Some(i) = &player.item { - self.packet_out.push_back(PacketC::SetTileItem { - tile: pos, + self.packet_out.push_back(PacketC::SetItem { + location: ItemLocation::Tile(pos), item: Some(i.kind), }); - self.packet_out.push_back(PacketC::TakeItem { - player: pid, - tile: pos, + self.packet_out.push_back(PacketC::MoveItem { + from: ItemLocation::Tile(pos), + to: ItemLocation::Player(pid), }) } if let Some(i) = &tile.item { - self.packet_out.push_back(PacketC::SetTileItem { - tile: pos, + self.packet_out.push_back(PacketC::SetItem { + location: ItemLocation::Tile(pos), item: Some(i.kind), }); } - self.packet_out.push_back({ - PacketC::Score { - time_remaining: self - .end - .map(|t| (t - Instant::now()).as_secs_f32()), - points: self.points, - demands_failed: self - .demand - .as_ref() - .map(|d| d.failed) - .unwrap_or_default(), - demands_completed: self - .demand - .as_ref() - .map(|d| d.completed) - .unwrap_or_default(), - } - }) + self.score_changed = true } } } @@ -423,10 +410,17 @@ impl Game { kind: i, active: None, }); - self.packet_out - .push_back(PacketC::SetPlayerItem { player, item }) + self.packet_out.push_back(PacketC::SetItem { + location: ItemLocation::Player(player), + item, + }) } } + + if self.score_changed { + self.score_changed = false; + self.packet_out.push_back(self.score()) + } Ok(()) } @@ -455,11 +449,11 @@ impl Game { } for (&pos, tile) in &mut self.tiles { - if let Some(effect) = tick_tile(dt, &self.data, tile) { + if let Some(effect) = tick_slot(dt, &self.data, Some(tile.kind), &mut tile.item) { match effect { - TickEffect::Progress(warn) => self.packet_out.push_back(PacketC::SetActive { + TickEffect::Progress(warn) => self.packet_out.push_back(PacketC::SetProgress { warn, - tile: pos, + item: ItemLocation::Tile(pos), progress: tile .item .as_ref() @@ -469,8 +463,8 @@ impl Game { .map(|i| i.progress), }), TickEffect::Produce => { - self.packet_out.push_back(PacketC::SetTileItem { - tile: pos, + self.packet_out.push_back(PacketC::SetItem { + location: ItemLocation::Tile(pos), item: tile.item.as_ref().map(|i| i.kind), }); } @@ -478,6 +472,30 @@ impl Game { } } + for (&pid, player) in &mut self.players { + if let Some(effect) = tick_slot(dt, &self.data, None, &mut player.item) { + match effect { + TickEffect::Progress(warn) => self.packet_out.push_back(PacketC::SetProgress { + warn, + item: ItemLocation::Player(pid), + progress: player + .item + .as_ref() + .unwrap() + .active + .as_ref() + .map(|i| i.progress), + }), + TickEffect::Produce => { + self.packet_out.push_back(PacketC::SetItem { + location: ItemLocation::Player(pid), + item: player.item.as_ref().map(|i| i.kind), + }); + } + } + } + } + return self.end.map(|t| t < Instant::now()).unwrap_or_default(); } } diff --git a/server/src/interaction.rs b/server/src/interaction.rs index 69fc9e70..85df9925 100644 --- a/server/src/interaction.rs +++ b/server/src/interaction.rs @@ -17,7 +17,7 @@ */ use crate::{ data::Gamedata, - game::{Involvement, Item, Tile}, + game::{Involvement, Item}, protocol::{ItemIndex, TileIndex}, }; use log::info; @@ -96,12 +96,12 @@ impl Recipe { } } } - pub fn supports_tile(&self, tile: TileIndex) -> bool { + pub fn supports_tile(&self, tile: Option<TileIndex>) -> bool { if let Some(tile_constraint) = self.tile() { - if tile != tile_constraint { - false + if let Some(tile) = tile { + tile == tile_constraint } else { - true + false } } else { true @@ -118,17 +118,19 @@ pub enum InteractEffect { pub fn interact( data: &Gamedata, edge: bool, - tile_kind: TileIndex, + tile: Option<TileIndex>, this: &mut Option<Item>, other: &mut Option<Item>, points: &mut i64, ) -> Option<InteractEffect> { - let interactable = data.is_tile_interactable(tile_kind); + let interactable = tile + .map(|tile| data.is_tile_interactable(tile)) + .unwrap_or(true); if interactable && other.is_none() { if let Some(item) = this { if let Some(active) = &mut item.active { let recipe = &data.recipe(active.recipe); - if recipe.supports_tile(tile_kind) { + if recipe.supports_tile(tile) { if let Recipe::Active { outputs, .. } = recipe { if edge { active.working += 1; @@ -150,7 +152,7 @@ pub fn interact( } if interactable { for (ri, recipe) in data.recipes() { - if !recipe.supports_tile(tile_kind) { + if !recipe.supports_tile(tile) { continue; } match recipe { @@ -233,18 +235,23 @@ pub enum TickEffect { Progress(bool), Produce, } -pub fn tick_tile(dt: f32, data: &Gamedata, tile: &mut Tile) -> Option<TickEffect> { - if let Some(item) = &mut tile.item { +pub fn tick_slot( + dt: f32, + data: &Gamedata, + tile: Option<TileIndex>, + slot: &mut Option<Item>, +) -> Option<TickEffect> { + if let Some(item) = slot { if let Some(a) = &mut item.active { let r = &data.recipe(a.recipe); - if r.supports_tile(tile.kind) { + if r.supports_tile(tile) { a.progress += a.working as f32 * dt / r.duration().unwrap(); } else if let Some(revert_duration) = r.revert_duration() { a.progress -= dt / revert_duration; } if a.progress >= 1. { if let Recipe::Passive { output, .. } = &data.recipe(a.recipe) { - tile.item = output.map(|kind| Item { kind, active: None }); + *slot = output.map(|kind| Item { kind, active: None }); return Some(TickEffect::Produce); }; a.progress = 1.; @@ -255,7 +262,7 @@ pub fn tick_tile(dt: f32, data: &Gamedata, tile: &mut Tile) -> Option<TickEffect return Some(TickEffect::Progress(r.warn())); } else { for (ri, recipe) in data.recipes() { - if recipe.supports_tile(tile.kind) { + if recipe.supports_tile(tile) { if let Recipe::Passive { input, .. } = recipe { if *input == item.kind { item.active = Some(Involvement { diff --git a/server/src/protocol.rs b/server/src/protocol.rs index 176eb31b..54992feb 100644 --- a/server/src/protocol.rs +++ b/server/src/protocol.rs @@ -99,24 +99,16 @@ pub enum PacketC { pos: Vec2, rot: f32, }, - TakeItem { - tile: IVec2, - player: PlayerID, - }, - PutItem { - player: PlayerID, - tile: IVec2, - }, - SetTileItem { - tile: IVec2, - item: Option<ItemIndex>, + MoveItem { + from: ItemLocation, + to: ItemLocation, }, - SetPlayerItem { - player: PlayerID, + SetItem { + location: ItemLocation, item: Option<ItemIndex>, }, - SetActive { - tile: IVec2, + SetProgress { + item: ItemLocation, progress: Option<f32>, warn: bool, }, @@ -150,3 +142,10 @@ pub enum PacketC { message: String, }, } + +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +pub enum ItemLocation { + Tile(IVec2), + Player(PlayerID), +} diff --git a/test-client/main.ts b/test-client/main.ts index aeca8084..70c07212 100644 --- a/test-client/main.ts +++ b/test-client/main.ts @@ -18,8 +18,8 @@ /// <reference lib="dom" /> import { MovementBase, update_movement } from "./movement.ts"; -import { Gamedata, ItemIndex, Message, PacketC, PacketS, PlayerID, TileIndex } from "./protocol.ts"; -import { V2, add_v2, lerp_exp_v2_mut, normalize, lerp_exp } from "./util.ts"; +import { Gamedata, ItemIndex, ItemLocation, Message, PacketC, PacketS, PlayerID, TileIndex } from "./protocol.ts"; +import { V2, lerp_exp_v2_mut, normalize, lerp_exp } from "./util.ts"; import { draw_ingame, draw_wait } from "./visual.ts"; const KEY_INTERACT = "KeyJ" @@ -82,8 +82,8 @@ export interface PlayerData extends MovementBase { } export interface TileData { - x: number - y: number + x: number, y: number // tile position + position: V2, // center position kind: TileIndex item?: ItemData } @@ -113,9 +113,15 @@ export let interact_possible_anim: number = 0 export let interact_active_anim: number = 0 let interacting: V2 | undefined; +function get_item_location(loc: ItemLocation): PlayerData | TileData { + if ("tile" in loc) return tiles.get(loc.tile.toString())! + if ("player" in loc) return players.get(loc.player)! + throw new Error("invalid item location"); +} + function send(p: PacketS) { ws.send(JSON.stringify(p)) } function packet(p: PacketC) { - if (!["position", "set_active", "update_map"].includes(p.type)) + if (!["position", "set_progress", "update_map"].includes(p.type)) console.log(p); switch (p.type) { case "init": @@ -153,45 +159,40 @@ function packet(p: PacketC) { pl.rot = p.rot break; } - case "take_item": { - const player = players.get(p.player)! - const tile = tiles.get(p.tile.toString())! - player.item = tile.item; - player.item!.tracking = player.position - tile.item = undefined - break; - } - case "put_item": { - const player = players.get(p.player)! - const tile = tiles.get(p.tile.toString())! - tile.item = player.item - tile.item!.tracking = add_v2(tile, 0.5) - player.item = undefined - break; - } - case "set_tile_item": { - const tile = tiles.get(p.tile.toString())! - if (tile.item !== undefined && tile.item !== null) items_removed.add(tile.item) - tile.item = undefined - if (p.item !== undefined && p.item !== null) tile.item = { kind: p.item, x: p.tile[0] + 0.5, y: p.tile[1] + 0.5 } + case "move_item": { + const from = get_item_location(p.from) + const to = get_item_location(p.to) + + to.item = from.item + to.item!.tracking = to.position + from.item = undefined + break; } - case "set_player_item": { - const player = players.get(p.player)! - if (player.item !== undefined && player.item !== null) items_removed.add(player.item) - player.item = undefined - if (p.item !== undefined && p.item !== null) player.item = { kind: p.item, x: player.position.x + 0.5, y: player.position.y + 0.5 } + case "set_item": { + const slot = get_item_location(p.location) + if (slot.item !== undefined && slot.item !== null) items_removed.add(slot.item) + slot.item = undefined + if (p.item !== undefined && p.item !== null) slot.item = { kind: p.item, x: slot.position.x, y: slot.position.y, tracking: slot.position } break; } - case "set_active": { - const item = tiles.get(p.tile.toString())!.item!; - item.progress = p.progress - item.progress_warn = p.warn + case "set_progress": { + const slot = get_item_location(p.item) + if (!slot.item) return + slot.item.progress = p.progress + slot.item.progress_warn = p.warn break; } case "update_map": - if (p.kind !== undefined && p.kind !== null) tiles.set(p.tile.toString(), { x: p.tile[0], y: p.tile[1], kind: p.kind }) - else tiles.delete(p.tile.toString()) + if (p.kind !== undefined && p.kind !== null) + tiles.set(p.tile.toString(), { + x: p.tile[0], + y: p.tile[1], + position: { x: p.tile[0] + 0.5, y: p.tile[1] + 0.5 }, + kind: p.kind + }) + else + tiles.delete(p.tile.toString()) break; case "communicate": { const player = players.get(p.player)! diff --git a/test-client/protocol.ts b/test-client/protocol.ts index 1e76700a..fd0f8d43 100644 --- a/test-client/protocol.ts +++ b/test-client/protocol.ts @@ -41,11 +41,9 @@ export type PacketC = | { type: "add_player", id: PlayerID, name: string, position: Vec2, character: number } // Somebody else joined (or was already in the game) | { type: "remove_player", id: PlayerID } // Somebody left | { type: "position", player: PlayerID, pos: Vec2, rot: number } // Update the position of a players (your own position is included here) - | { type: "take_item", tile: Vec2, player: PlayerID } // An item was taken from a tile - | { type: "put_item", tile: Vec2, player: PlayerID } // An item was put on a tile - | { type: "set_tile_item", tile: Vec2, item?: ItemIndex } // A tile changed its item - | { type: "set_player_item", player: PlayerID, item?: ItemIndex } // A player changed their item - | { type: "set_active", tile: Vec2, progress?: number, warn: boolean } // A tile is doing something. progress goes from 0 to 1, then null when finished + | { type: "move_item", from: ItemLocation, to: ItemLocation } // Item moved + | { type: "set_item", location: ItemLocation, item?: ItemIndex } // the item contained in a tile or player changed + | { type: "set_progress", item: ItemLocation, progress?: number, warn: boolean } // A tile is doing something. progress goes from 0 to 1, then null when finished | { type: "update_map", tile: Vec2, kind: TileIndex | null, neighbors: [TileIndex | null] } // A map tile was changed | { type: "communicate", player: PlayerID, message?: Message, persist: boolean } // A player wants to communicate something, message is null when cleared | { type: "server_message", text: string } // Text message from the server @@ -57,3 +55,7 @@ export type Message = { item: number } | { text: string } | { effect: string } + +export type ItemLocation = + { player: PlayerID } + | { tile: Vec2 } |