From a116b645d54aae52e567a720bdb566f0f1dff4e8 Mon Sep 17 00:00:00 2001 From: core Date: Sun, 26 Nov 2023 21:56:02 -0500 Subject: [PATCH] chat functionality --- server/src/main.rs | 83 +++++++--- server/src/packet.rs | 17 ++ starkingdoms-client/index.html | 155 ++++++++++-------- starkingdoms-client/src/css/chat.css | 48 ++++++ starkingdoms-client/src/css/popup.css | 5 +- starkingdoms-client/src/css/style.css | 3 +- .../themes/catppuccin-common/definitions.css | 1 + starkingdoms-client/src/hub.ts | 13 +- starkingdoms-client/src/protocol.ts | 25 ++- starkingdoms-client/src/rendering.ts | 2 + 10 files changed, 251 insertions(+), 101 deletions(-) create mode 100644 starkingdoms-client/src/css/chat.css diff --git a/server/src/main.rs b/server/src/main.rs index f2437696f1615b9205464a563f169a6f2028623e..7ae999b129cd9cfdad16f24fe0f31ce93e4068ed 100644 --- a/server/src/main.rs +++ b/server/src/main.rs @@ -60,22 +60,16 @@ fn on_message( player_query: Query<(Entity, &Player)>, part_query: Query<(Entity, &PartType, &Transform)>, mut packet_recv: Local>, - mut packet_send: ResMut>, + mut packet_event_send: ResMut>, ) { - let mut packets = Vec::new(); - for ev in packet_recv.read(&packet_send) { + let mut event_queue = Vec::new(); + for ev in packet_recv.read(&packet_event_send) { if let ServerEvent::Recv(addr, MessageType::Text, data) = ev { - let data = String::from_utf8_lossy(&data); - let json: serde_json::Value = err_or_cont!(serde_json::from_str(&data)); - let packet_type = json["t"].clone(); - let data = json["c"].clone(); - let packet_type = some_or_cont!(packet_type.as_str()); - match packet_type { - // handshake - "ClientLogin" => { - // spawn player - let username = data["username"].clone(); - let username = some_or_cont!(username.as_str()); + let data = String::from_utf8_lossy(data); + let packet: Packet = err_or_cont!(serde_json::from_str(&data)); + + match packet { + Packet::ClientLogin { username, .. } => { let angle: f32 = { let mut rng = rand::thread_rng(); rng.gen::() * std::f32::consts::PI * 2. @@ -108,7 +102,7 @@ fn on_message( } let packet = Packet::PlanetPositions { planets }; let buf = serde_json::to_vec(&packet).unwrap(); - packets.push(ServerEvent::Send(*addr, MessageType::Text, buf)); + event_queue.push(ServerEvent::Send(*addr, MessageType::Text, buf)); // tell the player already existing users let mut players = Vec::new(); @@ -119,7 +113,7 @@ fn on_message( players, }; let buf = serde_json::to_vec(&packet).unwrap(); - packets.push(ServerEvent::Send(*addr, MessageType::Text, buf)); + event_queue.push(ServerEvent::Send(*addr, MessageType::Text, buf)); // tell other players that a player has spawned in let packet = Packet::SpawnPlayer { @@ -127,7 +121,7 @@ fn on_message( username: username.to_string(), }; let buf = serde_json::to_vec(&packet).unwrap(); - packets.push(ServerEvent::Broadcast(MessageType::Text, buf)); + event_queue.push(ServerEvent::Broadcast(MessageType::Text, buf)); // tell the player where parts are let mut parts = Vec::new(); @@ -142,17 +136,62 @@ fn on_message( transform: proto_transform!(Transform::from_translation(transform.translation * SCALE).with_rotation(transform.rotation)), })); let packet = Packet::PartPositions { - parts + parts }; let buf = serde_json::to_vec(&packet).unwrap(); - packets.push(ServerEvent::Send(*addr, MessageType::Text, buf)); + event_queue.push(ServerEvent::Send(*addr, MessageType::Text, buf)); + + // and send the welcome message :) + let packet = Packet::Message { + message_type: packet::MessageType::Server, + actor: "SERVER".to_string(), + content: "Welcome to StarKingdoms.IO! Have fun!".to_string(), + }; + let buf = serde_json::to_vec(&packet).unwrap(); + event_queue.push(ServerEvent::Send(*addr, MessageType::Text, buf)); + } + Packet::SendMessage { target, content } => { + // find our player + let mut player = None; + for (_, q_player) in &player_query { + if q_player.addr == *addr { + player = Some(q_player); + } + } + let player = player.unwrap(); + if let Some(target_username) = target { + let mut target_player = None; + for (_, q_player) in &player_query { + if q_player.username == target_username { + target_player = Some(q_player); + } + } + let target_player = target_player.unwrap(); + let packet = Packet::Message { + message_type: packet::MessageType::Direct, + actor: player.username.clone(), + content, + }; + let buf = serde_json::to_vec(&packet).unwrap(); + event_queue.push(ServerEvent::Send(target_player.addr, MessageType::Text, buf.clone())); + event_queue.push(ServerEvent::Send(*addr, MessageType::Text, buf)); + } else { + // send to general chat + let packet = Packet::Message { + message_type: packet::MessageType::Chat, + actor: player.username.clone(), + content, + }; + let buf = serde_json::to_vec(&packet).unwrap(); + event_queue.push(ServerEvent::Broadcast(MessageType::Text, buf)); + } } _ => continue - }; + } } } - for packet in packets { - packet_send.send(packet); + for event in event_queue { + packet_event_send.send(event); } } diff --git a/server/src/packet.rs b/server/src/packet.rs index 423cc0163e498b27ed97ad4b542753470b186d0f..65327223f874655792fcb8985a2e8817b5985e63 100644 --- a/server/src/packet.rs +++ b/server/src/packet.rs @@ -30,6 +30,14 @@ pub struct Part { pub transform: ProtoTransform } +#[derive(Debug, Serialize, Deserialize)] +pub enum MessageType { + Server, + Error, + Chat, + Direct +} + #[derive(Debug, Serialize, Deserialize)] #[serde(tag = "t", content = "c")] pub enum Packet { @@ -38,6 +46,10 @@ pub enum Packet { username: String, jwt: Option, }, + SendMessage { + target: Option, + content: String + }, // clientbound SpawnPlayer { id: u32, @@ -55,4 +67,9 @@ pub enum Packet { PlayerLeave { id: u32, }, + Message { + message_type: MessageType, + actor: String, + content: String + } } diff --git a/starkingdoms-client/index.html b/starkingdoms-client/index.html index d907bfe543f7733411cd49b9ae0b7a347ec6ad70..5349d881a46f7e9586b7f15c1f16a24a571850a6 100644 --- a/starkingdoms-client/index.html +++ b/starkingdoms-client/index.html @@ -1,39 +1,40 @@ - - - - StarKingdoms - - + + + + StarKingdoms + + -
- -
+
+ +
- - + + +
+

Packet Explorer

+

Selected: --

+
+ - + + + - - + + - - + + diff --git a/starkingdoms-client/src/css/chat.css b/starkingdoms-client/src/css/chat.css new file mode 100644 index 0000000000000000000000000000000000000000..9aec0f07d0f23dc766ca14d08810f02639b6df12 --- /dev/null +++ b/starkingdoms-client/src/css/chat.css @@ -0,0 +1,48 @@ +.chat-container { + position: absolute; + top: 0.5em; + right: 0.5em; + width: 30vw; + height: min-content; + font-size: 0.875rem; /* 14px */ + line-height: 1.25rem; /* 20px */ + font-weight: 500; +} +.chat-table { + height: 23vh; + display: block; + overflow: auto; +} +.chat-box { + appearance: none; + background: transparent; + color: var(--text); + padding: 0.675em 1em; + border: 1px solid var(--links); + border-radius: 0.25rem; + cursor: text; + width: 100%; + max-width: 100%; +} +.chat-box:focus { + outline: none; + background-color: var(--links-ultratransparent); +} +.message { + padding-top: 0; + padding-bottom: 0; + margin-top: 1px; + margin-bottom: 1px; +} +.server-message { + color: #facb61; +} +.server-error { + color: #ff2222; +} +.global-message { + color: #54fa46; +} +.direct-message { + color: #66d8ed; +} \ No newline at end of file diff --git a/starkingdoms-client/src/css/popup.css b/starkingdoms-client/src/css/popup.css index 97c544050abd5a39434cea04a66df2ec823d5758..8b5564f81d9dc2fe56670fcc3b6fbd2e7fce7ca9 100644 --- a/starkingdoms-client/src/css/popup.css +++ b/starkingdoms-client/src/css/popup.css @@ -4,10 +4,13 @@ background-color: var(--bg-secondary-1); height: min-content; - max-width: 300px; + border-radius: 5px; z-index: 100000; } +.popup-max-width-300 { + max-width: 300px; +} .popup-wmin { width: min-content; } diff --git a/starkingdoms-client/src/css/style.css b/starkingdoms-client/src/css/style.css index 9e85b9aab042b2c6bfee41bbf3e64a1699f7b0f9..9f73f2728b7b2f5620cc3263d006a9b8a4b5a1aa 100644 --- a/starkingdoms-client/src/css/style.css +++ b/starkingdoms-client/src/css/style.css @@ -6,4 +6,5 @@ @import "json.css"; @import "log.css"; @import "game.css"; -@import "hud.css"; \ No newline at end of file +@import "hud.css"; +@import "chat.css"; \ No newline at end of file diff --git a/starkingdoms-client/src/css/themes/catppuccin-common/definitions.css b/starkingdoms-client/src/css/themes/catppuccin-common/definitions.css index c212df55d9ff15b7f8fc085b50010e9f58535a06..d98803c3f89e556715417a944307b9521d78d88f 100644 --- a/starkingdoms-client/src/css/themes/catppuccin-common/definitions.css +++ b/starkingdoms-client/src/css/themes/catppuccin-common/definitions.css @@ -31,4 +31,5 @@ --pill: rgb(var(--blue)); --sel-bg: rgba(var(--surface2), 0.4); --cursor: rgb(var(--rosewater)); + --dm: rgb(var(--teal)); } \ No newline at end of file diff --git a/starkingdoms-client/src/hub.ts b/starkingdoms-client/src/hub.ts index 1edc6a7cd2d2d1b43552950f8b73728dd7267cd1..7619e37738fa1b12ac12b57a785eec22f42086a4 100644 --- a/starkingdoms-client/src/hub.ts +++ b/starkingdoms-client/src/hub.ts @@ -1,7 +1,13 @@ import createDebug from "debug"; import { + MessagePacket, Packet, - PacketType, PartPositionsPacket, PlanetPositionsPacket, PlayerLeavePacket, PlayerListPacket, SpawnPlayerPacket, + PacketType, + PartPositionsPacket, + PlanetPositionsPacket, + PlayerLeavePacket, + PlayerListPacket, + SpawnPlayerPacket, } from "./protocol.ts"; import {appendPacket} from "./packet_ui.ts"; import {global} from "./main.ts"; @@ -81,11 +87,14 @@ export async function hub_connect(url: string, username: string): Promise packet.c; + let p = packet.c; let username = global.players_map.get(p.id)!; global.inverse_players_map.delete(username); global.players_map.delete(p.id); logger(`player removed (id=${p.id})`); + } else if (packet.t == PacketType.Message) { + let p = packet.c; + logger(`message type=${p.message_type} actor=${p.actor} content=${p.content}`); } else { logger(`unrecognized packet type ${packet.t}`); } diff --git a/starkingdoms-client/src/protocol.ts b/starkingdoms-client/src/protocol.ts index b5fb5994619b959f53b66322811189f9c7d771f4..0ada7e73c6bba7847f0058f1cdfd22c2bf793c2d 100644 --- a/starkingdoms-client/src/protocol.ts +++ b/starkingdoms-client/src/protocol.ts @@ -38,25 +38,42 @@ export interface PlayerListPacket { export interface PlayerLeavePacket { id: number } +export interface SendMessagePacket { + target: string | null, + content: string +} +export enum MessageType { + Server = "Server", + Error = "Error", + Chat = "Chat", + Direct = "Direct" +} +export interface MessagePacket { + message_type: MessageType, + actor: string, + content: string +} export enum PacketType { // serverbound ClientLogin = "ClientLogin", + SendMessage = "SendMessage", // clientbound SpawnPlayer = "SpawnPlayer", PlayerList = "PlayerList", PlanetPositions = "PlanetPositions", PartPositions = "PartPositions", - PlayerLeave = "PlayerLeave" + PlayerLeave = "PlayerLeave", + Message = "Message" } export interface Packet { t: PacketType, - c: ClientLoginPacket | SpawnPlayerPacket | PlayerListPacket | PlanetPositionsPacket | PartPositionsPacket | PlayerLeavePacket + c: ClientLoginPacket | SpawnPlayerPacket | PlayerListPacket | PlanetPositionsPacket | PartPositionsPacket | PlayerLeavePacket | SendMessagePacket | MessagePacket } -export const SERVERBOUND = [PacketType.ClientLogin]; -export const CLIENTBOUND = [PacketType.SpawnPlayer, PacketType.PlayerList, PacketType.PlanetPositions, PacketType.PartPositions, PacketType.PlayerLeave]; +export const SERVERBOUND = [PacketType.ClientLogin, PacketType.SendMessage]; +export const CLIENTBOUND = [PacketType.SpawnPlayer, PacketType.PlayerList, PacketType.PlanetPositions, PacketType.PartPositions, PacketType.PlayerLeave, PacketType.Message]; export enum Direction { Serverbound = "Serverbound", diff --git a/starkingdoms-client/src/rendering.ts b/starkingdoms-client/src/rendering.ts index 618eb3523e079683d3acb98497520ac00ab445ce..4995de1de917a3d09776af7ea086ba4badb510e4 100644 --- a/starkingdoms-client/src/rendering.ts +++ b/starkingdoms-client/src/rendering.ts @@ -10,6 +10,8 @@ export function startRender() { document.getElementById("server_selector")!.classList.add("hidden"); // show the HUD document.getElementById("hud")!.classList.remove("hidden"); + // and chat + document.getElementById("chat")!.classList.remove("hidden"); // create the canvas let canvas = document.createElement("canvas"); canvas.classList.add("game");