From 81cb9772e39219dfbe545251a2d038d3531e1d5f Mon Sep 17 00:00:00 2001 From: Joel Therrien Date: Wed, 25 Dec 2024 18:59:08 -0800 Subject: [PATCH] WIP - Add styling --- server/src/main.rs | 38 ++++++++-- ui/src/UI.tsx | 2 +- ui/src/multiplayer.tsx | 166 +++++++++++++++++++++-------------------- ui/src/style.less | 39 ++++++++-- 4 files changed, 151 insertions(+), 94 deletions(-) diff --git a/server/src/main.rs b/server/src/main.rs index b3d6d34..58239eb 100644 --- a/server/src/main.rs +++ b/server/src/main.rs @@ -4,14 +4,16 @@ extern crate rocket; use itertools::Itertools; use rand::prelude::SmallRng; use rand::SeedableRng; +use rocket::fs::FileServer; use rocket::futures::{SinkExt, StreamExt}; use rocket::tokio::sync::broadcast::Sender; use rocket::tokio::sync::{Mutex, RwLock}; +use rocket::tokio::time::interval; use rocket::{tokio, State}; use serde::{Deserialize, Serialize}; use std::collections::HashMap; use std::sync::{Arc, LazyLock, Weak}; -use rocket::fs::FileServer; +use std::time::Duration; use tokio::select; use word_grid::api::{APIGame, ApiState, Update}; use word_grid::dictionary::{Dictionary, DictionaryImpl}; @@ -24,6 +26,15 @@ use ws::Message; static DICTIONARY: LazyLock = LazyLock::new(|| DictionaryImpl::create_from_path("../resources/dictionary.csv")); +fn escape_characters(x: &str) -> String { + let x = x + .replace("&", "&") + .replace("<", "<") + .replace(">", ">"); + + x +} + #[derive(Clone, Debug, Serialize, PartialEq)] struct Player { name: String, @@ -132,13 +143,12 @@ async fn incoming_message_handler( Some(message) => { match message { Ok(message) => { - if let Message::Ping(_) = message { - println!("Received ping from player {player:#?}"); + if matches!(message, Message::Ping(_)) || matches!(message, Message::Pong(_)) { return false; } let message = message.to_text().unwrap(); if message.len() == 0 { - println!("Websocket closed"); + println!("Received message of length zero"); println!("Player {player:#?} is leaving"); let mut room = room.write().await; @@ -372,8 +382,11 @@ async fn room( ws: ws::WebSocket, rooms: &State>, ) -> ws::Channel<'static> { + let id = escape_characters(id); + let player_name = escape_characters(player_name); + let mut rooms = rooms.lock().await; - let room = rooms.get(id); + let room = rooms.get(&id); let player = Player { name: player_name.to_string(), @@ -465,23 +478,38 @@ async fn room( ws.channel(move |mut stream| { Box::pin(async move { + let mut interval = interval(Duration::from_secs(10)); loop { let incoming_message = stream.next(); let room_message = receiver.recv(); + let ping_tick = interval.tick(); select! { // Rust formatter can't reach into this macro, hence we broke out the logic // into sub-functions message = incoming_message => { if incoming_message_handler(message, &sender, &player, &room, &mut stream).await { + println!("incoming returned true"); return Ok(()) } }, message = room_message => { if outgoing_message_handler(message, &sender, &player, &room, &mut stream).await { + println!("outgoing returned true"); return Ok(()) } + }, + _ = ping_tick => { + // send keep-alive + let message = Message::Ping(Vec::new()); + let result = stream.send(message.into()).await; + // if it fails, disconnect + if let Err(e) = result { + println!("Failed to send ping for player {player:#?}; disconnecting"); + println!("Received error {e}"); + return Ok(()) + } } } } diff --git a/ui/src/UI.tsx b/ui/src/UI.tsx index be74107..36a282b 100644 --- a/ui/src/UI.tsx +++ b/ui/src/UI.tsx @@ -262,7 +262,7 @@ export function AISelection(props: { //const processedProportionDictionary = 1.0 - props.proportionDictionary / 100; return <> -
+
(7); const [game, setGame] = useState(null); - - let button_or_game = ; - if(game){ - button_or_game = game; - } + const validSettings = roomName.length > 0 && !roomName.includes("/") && playerName.length > 0 && !playerName.includes("?") && !playerName.includes("&"); // Can change log scale to control shape of curve using following equation: // aiRandomness = log(1 + x*(n-1))/log(n) when x, the user input, ranges between 0 and 1 - const processedAIRandomness = Math.log(1 + (LOGBASE - 1)*aiRandomness/100) / Math.log(LOGBASE); + const processedAIRandomness = Math.log(1 + (LOGBASE - 1) * aiRandomness / 100) / Math.log(LOGBASE); const processedProportionDictionary = 1.0 - proportionDictionary / 100; - if(socket != null && partyInfo == null) { + if (game) { + return game; + } else if (socket != null && partyInfo == null) { return

Connecting to {roomName}

} else if (partyInfo != null) { const players = partyInfo.players.map((x) => { return
  • {x.name}
  • ; }); const ais = partyInfo.ais.map((x, i) => { - return
  • + return
  • Proportion: {unprocessedAIProportion(x.proportion)} / Randomness: {unprocessAIRandomness(x.randomness)}
  • }); - return
    + return

    Connected to {roomName}

    Players:
      - {players} -
    + {players} + AIs:
      {ais}
    -
    - Add AI - - -
    - - {button_or_game} -
    +
    + Add AI + + +
    +
    + + +
    + + + } else { - return
    -
    + return +
    -
    -
    -
    - -
    ; + if (event.reason != null && event.reason.length > 0) { + alert(`Disconnected with reason "${event.reason} & code ${event.code}"`); + } + }); + setSocket(socket); + }}>Connect + +
    + ; } } diff --git a/ui/src/style.less b/ui/src/style.less index 03b6f46..a2c336c 100644 --- a/ui/src/style.less +++ b/ui/src/style.less @@ -218,22 +218,45 @@ } } +.multiplayer-inputs-grid { + display: grid; + //grid-template-columns: 3fr 2fr; + //grid-column-gap: 1em; + grid-row-gap: 0.5em; + + label { + display: grid; + grid-template-columns: 3fr 2fr; + } +} + +.ai-grid{ + display: grid; + grid-template-columns: 3fr 2fr; + grid-column-gap: 1em; + grid-row-gap: 0.5em; +} + +.side-grid { + display: grid; + grid-template-columns: 1fr 1fr; + grid-column-gap: 1em; +} + +.multiplayer-ai-details { + button { + margin: 1em; + } +} dialog { border-radius: 10px; z-index: 1; + width: 50em; .new-game { width: 50em; - .grid { - display: grid; - grid-template-columns: 3fr 2fr; - grid-column-gap: 1em; - grid-row-gap: 0.5em; - - } - .selection-buttons { display: grid; grid-template-columns: 1fr 1fr;