summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--server/src/customer/mod.rs17
-rw-r--r--server/src/game.rs24
-rw-r--r--server/src/protocol.rs3
-rw-r--r--server/src/state.rs32
-rw-r--r--test-client/main.ts22
-rw-r--r--test-client/protocol.ts6
-rw-r--r--test-client/visual.ts46
7 files changed, 121 insertions, 29 deletions
diff --git a/server/src/customer/mod.rs b/server/src/customer/mod.rs
index d1f49655..92e6e94c 100644
--- a/server/src/customer/mod.rs
+++ b/server/src/customer/mod.rs
@@ -140,6 +140,7 @@ impl DemandState {
id,
PacketS::Communicate {
message: Some(Message::Item(data.demand(demand).from)),
+ persist: true,
},
));
p.state = CustomerState::Waiting {
@@ -157,7 +158,13 @@ impl DemandState {
debug!("{id:?} waiting");
*timeout -= dt;
if *timeout <= 0. {
- packets_out.push((id, PacketS::Communicate { message: None }));
+ packets_out.push((
+ id,
+ PacketS::Communicate {
+ message: None,
+ persist: true,
+ },
+ ));
let path = find_path(
&self.walkable,
p.movement.position.as_ivec2(),
@@ -191,7 +198,13 @@ impl DemandState {
}
});
if let Some(pos) = demand_pos {
- packets_out.push((id, PacketS::Communicate { message: None }));
+ packets_out.push((
+ id,
+ PacketS::Communicate {
+ persist: true,
+ message: None,
+ },
+ ));
for edge in [true, false] {
packets_out.push((id, PacketS::Interact { pos, edge }))
}
diff --git a/server/src/game.rs b/server/src/game.rs
index 72f653c4..4f609608 100644
--- a/server/src/game.rs
+++ b/server/src/game.rs
@@ -56,7 +56,7 @@ pub struct Player {
pub last_position_ts: Instant,
pub interacting: Option<IVec2>,
pub item: Option<Item>,
- pub communicate: Option<Message>,
+ pub communicate_persist: Option<Message>,
}
pub struct Game {
@@ -127,7 +127,7 @@ impl Game {
last_position_ts: Instant::now(),
character,
position: self.data.chef_spawn,
- communicate: None,
+ communicate_persist: None,
interacting: None,
name: name.clone(),
},
@@ -167,10 +167,11 @@ impl Game {
item: Some(item.kind),
})
}
- if let Some(c) = &player.communicate {
+ if let Some(c) = &player.communicate_persist {
out.push(PacketC::Communicate {
player: id,
message: Some(c.to_owned()),
+ persist: true,
})
}
}
@@ -223,7 +224,7 @@ impl Game {
last_position_ts: Instant::now(),
character,
position,
- communicate: None,
+ communicate_persist: None,
interacting: None,
name: name.clone(),
},
@@ -392,13 +393,18 @@ impl Game {
player.interacting = if edge { Some(pos) } else { None };
}
- PacketS::Communicate { message } => {
+ PacketS::Communicate { message, persist } => {
info!("{player:?} message {message:?}");
- if let Some(player) = self.players.get_mut(&player) {
- player.communicate = message.clone()
+ if persist {
+ if let Some(player) = self.players.get_mut(&player) {
+ player.communicate_persist = message.clone()
+ }
}
- self.packet_out
- .push_back(PacketC::Communicate { player, message })
+ self.packet_out.push_back(PacketC::Communicate {
+ player,
+ message,
+ persist,
+ })
}
PacketS::ReplaceHand { item } => {
let pdata = self
diff --git a/server/src/protocol.rs b/server/src/protocol.rs
index 24ac2a11..6ed34a65 100644
--- a/server/src/protocol.rs
+++ b/server/src/protocol.rs
@@ -58,9 +58,11 @@ pub enum PacketS {
},
Communicate {
message: Option<Message>,
+ persist: bool,
},
#[serde(skip)]
+ /// For internal use only
ReplaceHand {
item: Option<ItemIndex>,
},
@@ -130,6 +132,7 @@ pub enum PacketC {
Communicate {
player: PlayerID,
message: Option<Message>,
+ persist: bool,
},
Score {
points: i64,
diff --git a/server/src/state.rs b/server/src/state.rs
index ae3388b6..da05f33a 100644
--- a/server/src/state.rs
+++ b/server/src/state.rs
@@ -21,6 +21,9 @@ enum Command {
#[arg(default_value = "small-default-default")]
spec: String,
},
+ Effect {
+ name: String,
+ },
End,
}
@@ -45,13 +48,17 @@ impl State {
pub async fn packet_in(&mut self, player: PlayerID, packet: PacketS) -> Result<()> {
match &packet {
PacketS::Communicate {
- message: Some(Message::Text(message)),
- } if let Some(command) = message.strip_prefix("/") => {
- self.handle_command(Command::try_parse_from(
- shlex::split(command)
- .ok_or(anyhow!("quoting invalid"))?
- .into_iter(),
- )?)
+ message: Some(Message::Text(text)),
+ persist: false,
+ } if let Some(command) = text.strip_prefix("/") => {
+ self.handle_command(
+ player,
+ Command::try_parse_from(
+ shlex::split(command)
+ .ok_or(anyhow!("quoting invalid"))?
+ .into_iter(),
+ )?,
+ )
.await?;
return Ok(());
}
@@ -61,7 +68,7 @@ impl State {
Ok(())
}
- async fn handle_command(&mut self, command: Command) -> Result<()> {
+ async fn handle_command(&mut self, player: PlayerID, command: Command) -> Result<()> {
match command {
Command::Start { spec } => {
let data = self.index.generate(spec)?;
@@ -71,6 +78,15 @@ impl State {
self.game
.load(self.index.generate("lobby-none-none".to_string())?);
}
+ Command::Effect { name } => {
+ self.tx
+ .send(PacketC::Communicate {
+ player,
+ message: Some(Message::Effect(name)),
+ persist: false,
+ })
+ .ok();
+ }
}
Ok(())
}
diff --git a/test-client/main.ts b/test-client/main.ts
index 72e88c90..e3185c67 100644
--- a/test-client/main.ts
+++ b/test-client/main.ts
@@ -78,6 +78,7 @@ export interface PlayerData extends MovementBase {
character: number,
anim_position: V2,
message?: MessageData,
+ message_clear?: number,
}
export interface TileData {
@@ -98,6 +99,8 @@ export const items_removed = new Set<ItemData>()
export let data: Gamedata = { item_names: [], tile_names: [], spawn: [0, 0], tile_collide: [], tile_interact: [] }
+export let global_message: MessageData | undefined = undefined
+let global_message_clear: number | undefined = undefined
export let my_id: PlayerID = -1
export let points = 0
export let demands_completed = 0
@@ -191,8 +194,10 @@ function packet(p: PacketC) {
break;
case "communicate": {
const player = players.get(p.player)!
+ if (player.message_clear) clearTimeout(player.message_clear)
if (p.message) player.message = { inner: p.message, anim_size: 0., anim_position: player.anim_position }
- else player.message = undefined
+ if (p.persist && !p.message) player.message = undefined
+ if (!p.persist) player.message_clear = setTimeout(() => delete player.message, 3000)
break;
}
case "score":
@@ -201,8 +206,14 @@ function packet(p: PacketC) {
points = p.points
break;
case "error":
+ if (global_message_clear) clearTimeout(global_message_clear)
+ global_message = { inner: { text: p.message }, anim_size: 0., anim_position: { x: 0, y: 0 } }
+ global_message_clear = setTimeout(() => global_message = undefined, 4000)
console.warn(p.message)
break;
+ case "set_ingame":
+ console.log(`ingame ${p.state}`);
+ break;
default:
console.warn("unknown packet", p);
}
@@ -218,9 +229,9 @@ function keyboard(ev: KeyboardEvent, down: boolean) {
if (HANDLED_KEYS.includes(ev.code)) ev.preventDefault()
if (!keys_down.has(KEY_INTERACT) && ev.code == KEY_INTERACT && down) set_interact(true)
if (keys_down.has(KEY_INTERACT) && ev.code == KEY_INTERACT && !down) set_interact(false)
- if (down && ev.code == "Numpad1") send({ type: "communicate", message: { text: "/start default-small-default" } })
- if (down && ev.code == "Numpad2") send({ type: "communicate", message: { text: "/start default-big-default" } })
- if (down && ev.code == "Numpad0") send({ type: "communicate", message: { text: "/end" } })
+ if (down && ev.code == "Numpad1") send({ type: "communicate", message: { text: "/start default-small-default" }, persist: false })
+ if (down && ev.code == "Numpad2") send({ type: "communicate", message: { text: "/start default-big-default" }, persist: false })
+ if (down && ev.code == "Numpad0") send({ type: "communicate", message: { text: "/end" }, persist: false })
if (down) keys_down.add(ev.code)
else keys_down.delete(ev.code)
}
@@ -233,8 +244,7 @@ function close_chat() {
}
function toggle_chat() {
if (chat) {
- if (chat.value.length) send({ type: "communicate", message: { text: chat.value } })
- else send({ type: "communicate" })
+ if (chat.value.length) send({ type: "communicate", message: { text: chat.value }, persist: false })
chat.remove()
canvas.focus()
chat = null;
diff --git a/test-client/protocol.ts b/test-client/protocol.ts
index 94858387..f9f0dbaf 100644
--- a/test-client/protocol.ts
+++ b/test-client/protocol.ts
@@ -32,7 +32,7 @@ export type PacketS =
{ type: "join", name: string, character: number } // You join, sent as first packet.
| { type: "position", pos: Vec2, rot: number } // Update your position and rotation in radians (0 is -y)
| { type: "interact", pos: Vec2, edge: boolean } // Interact with some tile. edge is true when pressing and false when releasing interact button
- | { type: "communicate", message?: Message } // Send a message
+ | { type: "communicate", message?: Message, persist: boolean } // Send a message
| { type: "collide", player: PlayerID, force: Vec2 } // Apply force to another player as a result of a collision
export type PacketC =
@@ -47,10 +47,10 @@ export type PacketC =
| { 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: "update_map", tile: Vec2, kind: TileIndex | null, neighbors: [TileIndex | null] } // A map tile was changed
- | { type: "communicate", player: PlayerID, message?: Message } // A player wants to communicate something, message is null when cleared
+ | { type: "communicate", player: PlayerID, message?: Message, persist: boolean } // A player wants to communicate something, message is null when cleared
| { type: "score", points: number, demands_failed: number, demands_completed: number, } // Supplies information for score OSD
| { type: "set_ingame", state: boolean } // Set to false when entering the game or switching maps
- | { type: "error", message?: Message } // Your client did something wrong.
+ | { type: "error", message: string } // Your client did something wrong.
export type Message =
{ item: number }
diff --git a/test-client/visual.ts b/test-client/visual.ts
index 19866710..a26bab6c 100644
--- a/test-client/visual.ts
+++ b/test-client/visual.ts
@@ -15,7 +15,7 @@
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
-import { ItemData, MessageData, PlayerData, TileData, camera, camera_scale, canvas, ctx, data, demands_completed, demands_failed, get_interact_target, interact_active_anim, interact_possible_anim, interact_target_anim, items_removed, keys_down, my_id, players, points, tiles } from "./main.ts";
+import { ItemData, MessageData, PlayerData, TileData, camera, camera_scale, canvas, ctx, data, demands_completed, demands_failed, get_interact_target, global_message, interact_active_anim, interact_possible_anim, interact_target_anim, items_removed, keys_down, my_id, players, points, tiles } from "./main.ts";
import { PLAYER_SIZE } from "./movement.ts";
import { FALLBACK_TILE, ITEMS, TILES, FALLBACK_ITEM } from "./tiles.ts";
import { V2, ceil_v2, floor_v2 } from "./util.ts";
@@ -74,6 +74,8 @@ export function draw_ingame() {
ctx.restore()
+ draw_global_message()
+
ctx.fillStyle = "white"
ctx.textAlign = "left"
ctx.textBaseline = "bottom"
@@ -200,6 +202,48 @@ function draw_message(m: MessageData) {
for (const c of comps) c(ctx)
ctx.translate(0, 1)
}
+ if ("text" in m.inner) {
+ ctx.translate(0, -1)
+
+ ctx.textAlign = "center"
+ ctx.font = "15px sans-serif"
+ ctx.scale(2 / camera_scale, 2 / camera_scale)
+ const w = ctx.measureText(m.inner.text).width + 30
+
+ ctx.fillStyle = "#fffa"
+ ctx.beginPath()
+ ctx.roundRect(-w / 2, -15, w, 30, 5)
+ ctx.fill()
+
+ ctx.fillStyle = "black"
+ ctx.textBaseline = "middle"
+ ctx.fillText(m.inner.text, 0, 0)
+
+ ctx.translate(0, 1)
+ }
+ ctx.restore()
+}
+
+function draw_global_message() {
+ if (!global_message) return
+ ctx.save()
+ ctx.translate(canvas.width / 2, canvas.height / 6)
+ if ("text" in global_message.inner) {
+ ctx.font = "20px monospace"
+ const lines = global_message.inner.text.split("\n")
+ const w = lines.reduce((a, v) => Math.max(a, ctx.measureText(v).width), 0) + 20
+
+ ctx.fillStyle = "#fffa"
+ ctx.beginPath()
+ ctx.roundRect(-w / 2, -20, w, lines.length * 25 + 20, 5)
+ ctx.fill()
+
+ ctx.fillStyle = "black"
+ ctx.textAlign = "left"
+ ctx.textBaseline = "middle"
+ for (let i = 0; i < lines.length; i++)
+ ctx.fillText(lines[i], -w / 2 + 10, i * 25)
+ }
ctx.restore()
}