state stuff
This commit is contained in:
parent
61ded634cc
commit
0a59ebe9ea
5 changed files with 89 additions and 46 deletions
|
@ -15,23 +15,17 @@ pub fn Websocket() -> impl IntoView {
|
|||
..
|
||||
} = use_websocket("ws://0.0.0.0:3030/websocket");
|
||||
|
||||
// Signals
|
||||
let (online_users, set_online_users) = create_signal(0);
|
||||
let (active_games, set_active_games) = create_signal(0);
|
||||
let (chat_history, set_chat_history) = create_signal::<Vec<String>>(vec![]);
|
||||
|
||||
// Websocket stuff
|
||||
// let send_message = move |_| {
|
||||
// send("Hello, world!");
|
||||
// };
|
||||
|
||||
let status = move || ready_state.get().to_string();
|
||||
|
||||
let connected = move || ready_state.get() == ConnectionReadyState::Open;
|
||||
|
||||
let open_connection = move |_| {
|
||||
open();
|
||||
};
|
||||
|
||||
let close_connection = move |_| {
|
||||
close();
|
||||
};
|
||||
|
||||
let fake_new_game_request = NewGameRequest {
|
||||
name: String::from("Ligma"),
|
||||
host: Player {
|
||||
|
@ -43,15 +37,20 @@ pub fn Websocket() -> impl IntoView {
|
|||
packs: vec![0],
|
||||
};
|
||||
|
||||
// Game stuff
|
||||
let new_game_test = move |_| {
|
||||
send(&to_string(&fake_new_game_request).unwrap());
|
||||
};
|
||||
|
||||
let close_connection = move |_| {
|
||||
close();
|
||||
set_online_users(0);
|
||||
set_active_games(0);
|
||||
update_chat_history(&set_chat_history, format!("Disconnected.\n"));
|
||||
};
|
||||
// Chat stuff
|
||||
let chat_history_ref = create_node_ref::<Textarea>();
|
||||
|
||||
let (chat_history, set_chat_history) = create_signal::<Vec<String>>(vec![]);
|
||||
|
||||
fn update_chat_history(&history: &WriteSignal<Vec<String>>, message: String) {
|
||||
let _ = &history.update(|history: &mut Vec<_>| history.push(message));
|
||||
}
|
||||
|
@ -64,15 +63,30 @@ pub fn Websocket() -> impl IntoView {
|
|||
|
||||
if let Some(message) = message_raw {
|
||||
if let Ok(game) = serde_json::from_str::<Game>(message) {
|
||||
logging::log!("{:#}", serde_json::json!(game));
|
||||
logging::log!("Game object received.");
|
||||
} else if let Ok(state_summary) =
|
||||
serde_json::from_str::<ServerStateSummary>(message)
|
||||
{
|
||||
logging::log!(
|
||||
"Users: {}\nGames: {}",
|
||||
state_summary.online_users,
|
||||
state_summary.active_games
|
||||
);
|
||||
set_online_users(state_summary.online_users);
|
||||
set_active_games(state_summary.active_games);
|
||||
} else {
|
||||
update_chat_history(&set_chat_history, format!("{}\n", message));
|
||||
}
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
create_effect(move |_| {
|
||||
chat_history.with(move |_| {
|
||||
// Scroll chat textarea to bottom
|
||||
if let Some(hist) = chat_history_ref.get() {
|
||||
hist.set_scroll_top(hist.scroll_height());
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
|
@ -83,7 +97,10 @@ pub fn Websocket() -> impl IntoView {
|
|||
<div class="w-auto bg-slate-500">
|
||||
<hr/>
|
||||
<h2 class="text-2xl">Connection</h2>
|
||||
<p class="p-1">"status: " {status}</p>
|
||||
|
||||
<p class="p-1">"Users Online: " {online_users}</p>
|
||||
<p class="p-1">"Active Games: " {active_games}</p>
|
||||
<p class="p-1">"Connection Status: " {status}</p>
|
||||
<div class="p-1">
|
||||
<button on:click=open_connection disabled=connected>
|
||||
"Connect"
|
||||
|
|
|
@ -1,7 +1,15 @@
|
|||
use serde::{Deserialize, Serialize};
|
||||
use std::net::SocketAddr;
|
||||
|
||||
/// Server state summary
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct ServerStateSummary {
|
||||
pub online_users: usize,
|
||||
pub active_games: usize,
|
||||
}
|
||||
|
||||
/// User
|
||||
#[derive(Clone, Eq, PartialEq, Hash)]
|
||||
pub struct User {
|
||||
pub name: String,
|
||||
pub addr: SocketAddr,
|
||||
|
@ -109,5 +117,3 @@ pub struct Game {
|
|||
/// Black card for the current round
|
||||
pub current_black: Option<CardBlack>,
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -13,29 +13,44 @@ use std::{net::SocketAddr, sync::Arc};
|
|||
pub mod message_handler;
|
||||
use crate::message_handler::*;
|
||||
|
||||
fn greeting(state: &Arc<AppState>) -> String {
|
||||
fn motd() -> String {
|
||||
format!(
|
||||
"{:#?} Card packs loaded\n\
|
||||
{:#?} Current active games",
|
||||
state.all_cards.lock().unwrap().len(),
|
||||
state.games.lock().unwrap().len(),
|
||||
"Welcome!"
|
||||
)
|
||||
}
|
||||
|
||||
pub async fn websocket(stream: WebSocket, state: Arc<AppState>, who: User) {
|
||||
fn server_sum_update(state: &Arc<AppState>) -> ServerStateSummary {
|
||||
ServerStateSummary {
|
||||
online_users: state.users.lock().unwrap().len(),
|
||||
active_games: state.games.lock().unwrap().len(),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn on_websocket_connection(stream: WebSocket, state: Arc<AppState>, who: User) {
|
||||
// Add user to users
|
||||
//if doesn't exist
|
||||
let _true_if_not_exist = &state.users.lock().unwrap().insert(who.clone());
|
||||
//etc
|
||||
|
||||
// By splitting, we can send and receive at the same time.
|
||||
let (mut sender, mut receiver) = stream.split();
|
||||
|
||||
// sup
|
||||
let _greeting = sender.send(Message::Text(greeting(&state))).await;
|
||||
|
||||
// subscribe to broadcast channel
|
||||
let mut rx = state.tx.subscribe();
|
||||
// hydrate user
|
||||
let _ = &sender.send(Message::Text(motd())).await;
|
||||
let _ = &sender.send(Message::Text(serde_json::to_string(&server_sum_update(&state)).unwrap())).await;
|
||||
|
||||
// ANNOUNCE THY PRESENCE
|
||||
let msg = format!("{} joined.", who.name);
|
||||
tracing::debug!("{msg}");
|
||||
let _ = state.tx.send(msg);
|
||||
let _ = &state.tx.send(msg);
|
||||
|
||||
// Broadcast server state summary update
|
||||
let _ = &state
|
||||
.tx
|
||||
.send(serde_json::to_string(&server_sum_update(&state)).unwrap());
|
||||
|
||||
// subscribe to broadcast channel
|
||||
let mut rx = state.tx.subscribe();
|
||||
|
||||
// handle broadcasting further awesome messages
|
||||
let mut send_task = tokio::spawn(async move {
|
||||
|
@ -60,7 +75,7 @@ pub async fn websocket(stream: WebSocket, state: Arc<AppState>, who: User) {
|
|||
};
|
||||
}
|
||||
|
||||
pub async fn websocket_handler(
|
||||
pub async fn websocket_connection_handler(
|
||||
ws: WebSocketUpgrade,
|
||||
// user_agent: Option<TypedHeader<headers::UserAgent>>,
|
||||
ConnectInfo(addr): ConnectInfo<SocketAddr>,
|
||||
|
@ -68,7 +83,7 @@ pub async fn websocket_handler(
|
|||
) -> impl IntoResponse {
|
||||
tracing::debug!("New connection from {addr}");
|
||||
ws.on_upgrade(move |socket| {
|
||||
websocket(
|
||||
on_websocket_connection(
|
||||
socket,
|
||||
state,
|
||||
User {
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
use crate::api::{greeting, Message, User};
|
||||
use crate::api::*;
|
||||
use crate::AppState;
|
||||
use crate::Arc;
|
||||
use lib::models::*;
|
||||
|
||||
pub async fn message_handler(message: Message, state: &Arc<AppState>, who: &User) {
|
||||
let tx = &state.tx;
|
||||
|
@ -20,7 +19,8 @@ pub async fn message_handler(message: Message, state: &Arc<AppState>, who: &User
|
|||
tracing::error!("Failed to convert Game object to JSON.")
|
||||
}
|
||||
state.games.lock().unwrap().push(new_game_object);
|
||||
let _update = tx.send(greeting(state));
|
||||
let _ = tx.send(serde_json::to_string(&server_sum_update(state)).unwrap());
|
||||
// let _update = tx.send(motd());
|
||||
} else {
|
||||
let _res = tx.send(String::from("error creating game"));
|
||||
}
|
||||
|
@ -50,6 +50,8 @@ pub async fn message_handler(message: Message, state: &Arc<AppState>, who: &User
|
|||
let msg = format!("{0} left.", who.name);
|
||||
tracing::debug!("{msg}");
|
||||
let _ = tx.send(msg);
|
||||
let _ = state.users.lock().unwrap().remove(who);
|
||||
let _ = tx.send(serde_json::to_string(&server_sum_update(state)).unwrap());
|
||||
}
|
||||
|
||||
Message::Pong(ping) => {
|
||||
|
|
|
@ -2,6 +2,7 @@ use anyhow::{Context, Result};
|
|||
use axum::{response::Html, routing::get, Router};
|
||||
use axum_extra::response::Css;
|
||||
use lib::models::*;
|
||||
use std::collections::HashSet;
|
||||
use std::{
|
||||
// collections::HashSet,
|
||||
fs,
|
||||
|
@ -25,7 +26,8 @@ fn load_json(path: &str) -> Result<Vec<CardSet>> {
|
|||
}
|
||||
|
||||
// this is still around just for reference
|
||||
fn _test() -> Result<()> {
|
||||
#[allow(dead_code)]
|
||||
fn test() -> Result<()> {
|
||||
// choose decks
|
||||
let cards_input_path: &str = "../data/cah-cards-full.json";
|
||||
|
||||
|
@ -78,11 +80,11 @@ fn _test() -> Result<()> {
|
|||
// Our shared state
|
||||
pub struct AppState {
|
||||
// We require unique usernames. This tracks which usernames have been taken.
|
||||
// user_set: Mutex<HashSet<String>>,
|
||||
users: Mutex<HashSet<User>>,
|
||||
// Channel used to send messages to all connected clients.
|
||||
tx: broadcast::Sender<String>,
|
||||
// Master card decks
|
||||
all_cards: Mutex<Vec<CardSet>>,
|
||||
// all_cards: Mutex<Vec<CardSet>>,
|
||||
// Games list
|
||||
games: Mutex<Vec<Game>>,
|
||||
}
|
||||
|
@ -115,13 +117,14 @@ async fn main() -> Result<()> {
|
|||
// Set up application state for use with with_state().
|
||||
// let user_set = Mutex::new(HashSet::new());
|
||||
let (tx, _rx) = broadcast::channel(100);
|
||||
let cards_input_path: &str = "data/cah-cards-full.json";
|
||||
let all_cards = Mutex::new(load_json(cards_input_path)?);
|
||||
// let cards_input_path: &str = "data/cah-cards-full.json";
|
||||
let users = Mutex::new(HashSet::<User>::new());
|
||||
// let all_cards = Mutex::new(load_json(cards_input_path)?);
|
||||
let games = Mutex::new(vec![]);
|
||||
let app_state = Arc::new(AppState {
|
||||
// user_set,
|
||||
users,
|
||||
tx,
|
||||
all_cards,
|
||||
// all_cards,
|
||||
games,
|
||||
});
|
||||
|
||||
|
@ -130,7 +133,7 @@ async fn main() -> Result<()> {
|
|||
.route("/spawn_clients", get(spawn_clients))
|
||||
.route("/test_client", get(test_client))
|
||||
.route("/reference_client", get(reference_client))
|
||||
.route("/websocket", get(websocket_handler))
|
||||
.route("/websocket", get(websocket_connection_handler))
|
||||
.route("/css", get(css))
|
||||
.nest_service("/", ServeDir::new("dist"))
|
||||
.with_state(app_state);
|
||||
|
|
Loading…
Add table
Reference in a new issue