Compare commits

...

2 commits

Author SHA1 Message Date
Adam
98dd4e85c2 expand game browser 2024-08-02 02:35:31 -04:00
Adam
96bef78efb make users arcs 2024-08-02 01:20:54 -04:00
7 changed files with 232 additions and 173 deletions

View file

@ -1,4 +1,5 @@
use crate::components::websocket::WebSocketContext; use crate::components::websocket::WebSocketContext;
use leptos::html::Input;
use leptos::*; use leptos::*;
use leptos_use::core::ConnectionReadyState; use leptos_use::core::ConnectionReadyState;
use lib::models::*; use lib::models::*;
@ -12,13 +13,17 @@ pub fn Browser() -> impl IntoView {
let game_update_context = expect_context::<ReadSignal<Option<GamesUpdate>>>(); let game_update_context = expect_context::<ReadSignal<Option<GamesUpdate>>>();
let (active_games, set_active_games) = create_signal::<Vec<String>>(vec![]); let (active_games, set_active_games) = create_signal::<Vec<String>>(vec![]);
let fake_new_game_request = NewGameRequest { let new_game_input_ref = create_node_ref::<Input>();
name: String::from("Ligma"),
};
// Game stuff // Game stuff
let new_game_test = move |_| { let new_game = move |_| {
(websocket.send)(&to_string(&fake_new_game_request).unwrap()); (websocket.send)(
&to_string(&NewGameRequest {
name: new_game_input_ref.get().unwrap().value(),
})
.unwrap(),
);
new_game_input_ref.get().unwrap().set_value("");
}; };
create_effect(move |_| { create_effect(move |_| {
@ -42,9 +47,20 @@ pub fn Browser() -> impl IntoView {
<h2 class="text-2xl">Game Browser</h2> <h2 class="text-2xl">Game Browser</h2>
{move || active_games().into_iter().map(|n| view! { <li>{n}</li> }).collect_view()} {move || active_games().into_iter().map(|n| view! { <li>{n}</li> }).collect_view()}
</ul> </ul>
<button on:click=new_game_test disabled=move || !connected()> <form onsubmit="return false" on:submit=new_game>
"Test New Game" <input
</button> class="w-80 h-11 font-mono rounded-sm bg-slate-900 text-slate-200"
placeholder="Name"
disabled=move || !connected()
node_ref=new_game_input_ref
/>
<input
class="py-2 px-4 pl-4 font-bold text-white rounded border-b-4 bg-slate-600 border-slate-800 hover:bg-slate-700 hover:border-slate-500"
type="submit"
disabled=move || !connected()
value="New Game"
/>
</form>
</div> </div>
} }
} }

View file

@ -69,13 +69,13 @@ pub fn Websocket() -> impl IntoView {
// Contexts for message handler // Contexts for message handler
let (state_summary, set_state_summary) = let (state_summary, set_state_summary) =
create_signal::<Option<ServerStateSummary>>(Option::None); create_signal::<Option<ServerStateSummary>>(Option::None);
let (game_object, set_game_object) = create_signal::<Option<Game>>(Option::None); // let (game_object, set_game_object) = create_signal::<Option<Game>>(Option::None);
let (user_update, set_user_update) = create_signal::<Option<UserUpdate>>(Option::None); let (user_update, set_user_update) = create_signal::<Option<UserUpdate>>(Option::None);
let (chat_update, set_chat_update) = create_signal::<Option<ChatUpdate>>(Option::None); let (chat_update, set_chat_update) = create_signal::<Option<ChatUpdate>>(Option::None);
let (chat_message, set_chat_message) = create_signal::<Option<ChatMessage>>(Option::None); let (chat_message, set_chat_message) = create_signal::<Option<ChatMessage>>(Option::None);
let (active_games, set_active_games) = create_signal::<Option<GamesUpdate>>(Option::None); let (active_games, set_active_games) = create_signal::<Option<GamesUpdate>>(Option::None);
provide_context::<ReadSignal<Option<Game>>>(game_object); // provide_context::<ReadSignal<Option<Game>>>(game_object);
provide_context::<ReadSignal<Option<UserUpdate>>>(user_update); provide_context::<ReadSignal<Option<UserUpdate>>>(user_update);
provide_context::<ReadSignal<Option<ChatUpdate>>>(chat_update); provide_context::<ReadSignal<Option<ChatUpdate>>>(chat_update);
provide_context::<ReadSignal<Option<ChatMessage>>>(chat_message); provide_context::<ReadSignal<Option<ChatMessage>>>(chat_message);
@ -88,9 +88,10 @@ pub fn Websocket() -> impl IntoView {
// Send all messages as strings into chat box // Send all messages as strings into chat box
if let Some(message) = message_raw { if let Some(message) = message_raw {
if let Ok(game) = from_str::<Game>(message) { // if let Ok(game) = from_str::<Game>(message) {
set_game_object(Some(game)); // set_game_object(Some(game));
} else if let Ok(state_summary) = from_str::<ServerStateSummary>(message) { // }
if let Ok(state_summary) = from_str::<ServerStateSummary>(message) {
set_state_summary(Some(state_summary)); set_state_summary(Some(state_summary));
} else if let Ok(chat_message) = from_str::<ChatMessage>(message) { } else if let Ok(chat_message) = from_str::<ChatMessage>(message) {
set_chat_message(Some(chat_message)); set_chat_message(Some(chat_message));

View file

@ -1,115 +1,129 @@
// use anyhow::Result; use anyhow::Result;
// use rand::prelude::IteratorRandom; use rand::prelude::IteratorRandom;
// use rand::thread_rng; use rand::thread_rng;
// use std::sync::{Arc, Mutex};
// use crate::models::*;
// use crate::models::*;
// impl Game {
// /// Build game decks from input data for game start. impl Game {
// /// This should only run once and at startup. /// Build game decks from input data for game start.
// fn _build_decks(&mut self, cards_json: Vec<CardSet>) -> Result<()> { /// This should only run once and at startup.
// for pack in cards_json { fn _build_decks(&mut self, cards_json: Vec<CardSet>) -> Result<()> {
// if let Some(white) = pack.white { for pack in cards_json {
// self.white.extend(white) if let Some(white) = pack.white {
// } self.white.extend(white)
// if let Some(black) = pack.black { }
// self.black.extend(black) if let Some(black) = pack.black {
// } self.black.extend(black)
// } }
// }
// Ok(())
// } Ok(())
// }
// pub fn new(request: NewGameRequest) -> Result<Self> {
// let mut game = Game { pub fn new(request: NewGameManifest) -> Result<Self> {
// ..Default::default() let mut game = Game {
// }; ..Default::default()
// tracing::debug!("Creating game {}", &request.name); };
// game.name = request.name; tracing::debug!(
// "Creating game {} with {} as host",
// // game.build_decks(request.packs)?; &request.name,
// game.create_player(request.host)?; request.host.lock().unwrap().name
// game.deal_black()?; );
// game.name = request.name;
// Ok(game) game.host = request.host.clone();
// }
// // game.build_decks(request.packs)?;
// // pub fn join(request:GameJoinRequest) game.create_player(request.host)?;
// game.deal_black()?;
// /// Log counts of current drawable cards
// /// For testing Ok(game)
// pub fn deck_counts(&self) { }
// tracing::debug!(
// "Deck Counts:\n {} White cards\n {} Black cards", // pub fn join(request:GameJoinRequest)
// self.white.len(),
// self.black.len() /// Log counts of current drawable cards
// ); /// For testing
// } pub fn deck_counts(&self) {
// tracing::debug!(
// /// Draw one white card at random from play deck. "Deck Counts:\n {} White cards\n {} Black cards",
// fn draw_one_white(&mut self) -> Result<CardWhite> { self.white.len(),
// let deck = &mut self.white; self.black.len()
// );
// // this feels sloppy }
// if let Some(index) = (0..deck.len()).choose(&mut thread_rng()) {
// Ok(deck.swap_remove(index)) /// Draw one white card at random from play deck.
// } else { fn draw_one_white(&mut self) -> Result<CardWhite> {
// Ok(CardWhite { let deck = &mut self.white;
// text: "Error.\n\nbtw if you see this tell me I'm lazy :)".to_string(),
// pack: 0, // this feels sloppy
// }) if let Some(index) = (0..deck.len()).choose(&mut thread_rng()) {
// } Ok(deck.swap_remove(index))
// } } else {
// Ok(CardWhite {
// /// Draw one black card at random from play deck. text: "Error.\n\nbtw if you see this tell me I'm lazy :)".to_string(),
// fn draw_one_black(&mut self) -> Result<CardBlack> { pack: 0,
// let deck = &mut self.black; })
// }
// // this feels sloppy }
// if let Some(index) = (0..deck.len()).choose(&mut thread_rng()) {
// Ok(deck.swap_remove(index)) /// Draw one black card at random from play deck.
// } else { fn draw_one_black(&mut self) -> Result<CardBlack> {
// Ok(CardBlack { let deck = &mut self.black;
// text: "Error.\n\nbtw if you see this tell me I'm lazy :)".to_string(),
// pick: 0, // this feels sloppy
// pack: 0, if let Some(index) = (0..deck.len()).choose(&mut thread_rng()) {
// }) Ok(deck.swap_remove(index))
// } } else {
// } Ok(CardBlack {
// text: "Error.\n\nbtw if you see this tell me I'm lazy :)".to_string(),
// /// Deal a black card and use it for the current round pick: 0,
// fn deal_black(&mut self) -> Result<()> { pack: 0,
// self.current_black = Some(self.draw_one_black()?); })
// }
// Ok(()) }
// }
// /// Deal a black card and use it for the current round
// /// Create a new player and add them to the game. fn deal_black(&mut self) -> Result<()> {
// pub fn create_player(&mut self, mut player: Player) -> Result<()> { self.current_black = Some(self.draw_one_black()?);
// tracing::debug!("Creating player {} as {:?}", &player.name, &player.role);
// Ok(())
// let mut hand_buf = vec![]; }
// for _ in 0..10 {
// hand_buf.push(self.draw_one_white()?); /// Create a new player and add them to the game.
// } pub fn create_player(&mut self, user: Arc<Mutex<User>>) -> Result<()> {
// tracing::debug!("Dealing hand to {}", &player.name); let mut new_player = Player {
// player.white.extend(hand_buf); user: user.clone(),
// white: vec![],
// self.players.push(player); black: vec![],
// };
// Ok(())
// } let new_player_name = user.lock().unwrap().name.clone();
// tracing::debug!("Creating player for {}", &new_player_name);
// pub fn game_start(&mut self) -> Result<()> {
// self.game_active = true; let mut hand_buf = vec![];
// tracing::debug!("Game Active!"); for _ in 0..10 {
// hand_buf.push(self.draw_one_white()?);
// if let Some(black) = &self.current_black { }
// tracing::debug!("{}", black.text); tracing::debug!("Dealing hand to {}", &new_player_name);
// } else {
// tracing::debug!("YOU DONE FUCKED UP (no current black card)"); new_player.white.extend(hand_buf);
// }
// self.players.push(new_player);
// Ok(())
// } Ok(())
// } }
pub fn game_start(&mut self) -> Result<()> {
self.game_active = true;
tracing::debug!("Game Active!");
if let Some(black) = &self.current_black {
tracing::debug!("{}", black.text);
} else {
tracing::debug!("YOU DONE FUCKED UP (no current black card)");
}
Ok(())
}
}

View file

@ -1,4 +1,5 @@
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::sync::{Arc, Mutex};
/// Games update /// Games update
#[derive(Serialize, Deserialize, Debug)] #[derive(Serialize, Deserialize, Debug)]
@ -39,7 +40,7 @@ pub struct ServerStateSummary {
} }
/// User /// User
#[derive(Debug, Eq, PartialEq, Hash)] #[derive(Default, Debug, Eq, PartialEq, Hash)]
pub struct User { pub struct User {
pub name: String, pub name: String,
} }
@ -58,13 +59,12 @@ pub struct NewGameRequest {
} }
/// New game request structure /// New game request structure
use std::pin::Pin;
#[derive(Debug)] #[derive(Debug)]
pub struct NewGameManifest<'user> { pub struct NewGameManifest {
/// Game name /// Game name
pub name: String, pub name: String,
/// Game host /// Game host
pub host: &'user Pin<Box<User>>, pub host: Arc<Mutex<User>>,
} }
/// Game join request structure /// Game join request structure
@ -124,12 +124,10 @@ pub enum PlayerRole {
} }
/// A struct that represents a player /// A struct that represents a player
#[derive(Debug, Serialize, Deserialize)] #[derive(Debug)]
pub struct Player { pub struct Player {
/// Player's username /// Player's username
pub name: String, pub user: Arc<Mutex<User>>,
/// This player's role
pub role: PlayerRole,
/// The player's hand /// The player's hand
pub white: Vec<CardWhite>, pub white: Vec<CardWhite>,
/// The player's wins /// The player's wins
@ -137,10 +135,12 @@ pub struct Player {
} }
/// The game master /// The game master
#[derive(Default, Debug, Serialize, Deserialize)] #[derive(Default, Debug)]
pub struct Game { pub struct Game {
/// The name of the game /// The name of the game
pub name: String, pub name: String,
/// The host user of the game
pub host: Arc<Mutex<User>>,
/// White draw pile /// White draw pile
pub white: Vec<CardWhite>, pub white: Vec<CardWhite>,
/// Black draw pile /// Black draw pile

View file

@ -12,6 +12,7 @@ use futures::{SinkExt, StreamExt};
use lib::models::*; use lib::models::*;
use rand::seq::SliceRandom; use rand::seq::SliceRandom;
use serde_json::to_string; use serde_json::to_string;
use std::sync::Mutex;
use std::{net::SocketAddr, sync::Arc}; use std::{net::SocketAddr, sync::Arc};
pub mod message_handler; pub mod message_handler;
use crate::message_handler::*; use crate::message_handler::*;
@ -72,7 +73,7 @@ async fn handle_new_user(
addr: &SocketAddr, addr: &SocketAddr,
) -> Result<()> { ) -> Result<()> {
// Create // Create
let new_user = Box::pin(generate_new_user(&state)); let new_user = Arc::new(Mutex::new(generate_new_user(&state)));
tracing::debug!("User created at ptr: {:p}", new_user); tracing::debug!("User created at ptr: {:p}", new_user);
tracing::debug!("User borrowed ptr: {:p}", *&new_user); tracing::debug!("User borrowed ptr: {:p}", *&new_user);
@ -122,9 +123,9 @@ fn generate_new_user(state: &Arc<AppState>) -> User {
} }
/// Generate message to notify client of user changes /// Generate message to notify client of user changes
fn client_self_user_update(new_user: &User) -> String { fn client_self_user_update(new_user: &Arc<Mutex<User>>) -> String {
to_string::<UserUpdate>(&UserUpdate { to_string::<UserUpdate>(&UserUpdate {
username: new_user.name.clone(), username: new_user.lock().unwrap().name.clone(),
}) })
.unwrap() .unwrap()
} }
@ -135,7 +136,7 @@ fn chat_meta_update(state: &Arc<AppState>) -> String {
let mut names = vec![]; let mut names = vec![];
for user in state.online_users.lock().unwrap().iter() { for user in state.online_users.lock().unwrap().iter() {
names.push(user.1.name.clone()); names.push(user.1.lock().unwrap().name.clone());
} }
to_string::<ChatUpdate>(&ChatUpdate { to_string::<ChatUpdate>(&ChatUpdate {
@ -155,9 +156,11 @@ fn motd() -> String {
/// Generate server summary update - mostly debug stuff /// Generate server summary update - mostly debug stuff
fn server_summary_update(state: &Arc<AppState>) -> String { fn server_summary_update(state: &Arc<AppState>) -> String {
let online_users = state.online_users.lock().unwrap().len();
let active_games = state.games.lock().unwrap().len();
to_string::<ServerStateSummary>(&ServerStateSummary { to_string::<ServerStateSummary>(&ServerStateSummary {
online_users: state.online_users.lock().unwrap().len(), online_users,
active_games: state.games.lock().unwrap().len(), active_games,
}) })
.unwrap() .unwrap()
} }
@ -168,7 +171,11 @@ fn games_update(state: &Arc<AppState>) -> String {
let mut names = vec![]; let mut names = vec![];
for game in state.games.lock().unwrap().iter() { for game in state.games.lock().unwrap().iter() {
names.push(game.name.clone()); names.push(format!(
"Name: {} Host: {}",
game.name,
game.host.lock().unwrap().name
));
} }
to_string::<GamesUpdate>(&GamesUpdate { games: names }).unwrap() to_string::<GamesUpdate>(&GamesUpdate { games: names }).unwrap()
@ -178,7 +185,15 @@ fn games_update(state: &Arc<AppState>) -> String {
fn announce_join(state: &Arc<AppState>, addr: &SocketAddr) -> String { fn announce_join(state: &Arc<AppState>, addr: &SocketAddr) -> String {
let msg = format!( let msg = format!(
"{} joined.", "{} joined.",
state.online_users.lock().unwrap().get(addr).unwrap().name state
.online_users
.lock()
.unwrap()
.get(addr)
.unwrap()
.lock()
.unwrap()
.name
); );
tracing::debug!("{}", &msg); tracing::debug!("{}", &msg);

View file

@ -54,31 +54,23 @@ fn handle_new_game(
tx: &Sender<String>, tx: &Sender<String>,
addr: SocketAddr, addr: SocketAddr,
) -> Result<()> { ) -> Result<()> {
let binding = state.online_users.lock().unwrap();
let user = binding.get(&addr).unwrap();
tracing::debug!("{:#?} from {}", new_game, user.name);
let manifest = NewGameManifest { let manifest = NewGameManifest {
name: new_game.name, name: new_game.name,
host: user, host: state
.online_users
.lock()
.unwrap()
.get(&addr)
.unwrap()
.clone(),
}; };
// create game // create game
// if let Ok(new_game_object) = Game::new(new_game) { if let Ok(new_game_object) = Game::new(manifest) {
// if let Ok(game_json) = to_string(&new_game_object) { state.games.lock().unwrap().push(new_game_object);
// tracing::debug!("Sent new game JSON."); tx.send(games_update(state))?;
// // this is a broadcast tx.send(server_summary_update(state))?;
// // change this }
// tx.send(game_json)?;
// } else {
// // change this
// tracing::error!("Failed to convert Game object to JSON.")
// }
//
// state.games.lock().unwrap().push(new_game_object);
// tx.send(games_update(state))?;
// tx.send(server_summary_update(state))?;
// }
Ok(()) Ok(())
} }
@ -90,7 +82,7 @@ fn handle_chat_message(
tx: &Sender<String>, tx: &Sender<String>,
addr: SocketAddr, addr: SocketAddr,
) -> Result<()> { ) -> Result<()> {
let msg = format! {"{0}: {1}", state.online_users.lock().unwrap().get(&addr).unwrap().name, chat_message.text}; let msg = format! {"{0}: {1}", state.online_users.lock().unwrap().get(&addr).unwrap().lock().unwrap().name, chat_message.text};
tracing::debug!("{msg}"); tracing::debug!("{msg}");
tx.send(to_string::<ChatMessage>(&ChatMessage { text: msg })?)?; tx.send(to_string::<ChatMessage>(&ChatMessage { text: msg })?)?;
@ -110,6 +102,8 @@ fn handle_user_log_in(
.unwrap() .unwrap()
.get(&addr) .get(&addr)
.unwrap() .unwrap()
.lock()
.unwrap()
.name .name
.clone(); .clone();
let new_name = user_log_in.username.clone(); let new_name = user_log_in.username.clone();
@ -144,6 +138,8 @@ fn handle_user_log_in(
.unwrap() .unwrap()
.get_mut(&addr) .get_mut(&addr)
.unwrap() .unwrap()
.lock()
.unwrap()
.change_name(user_log_in.username); .change_name(user_log_in.username);
let msg = format! { let msg = format! {
@ -183,7 +179,15 @@ fn handle_close(
if let Some(cf) = close_frame { if let Some(cf) = close_frame {
tracing::debug!( tracing::debug!(
"Close received from {0} with code: {1} and reason: {2}", "Close received from {0} with code: {1} and reason: {2}",
state.online_users.lock().unwrap().get(&addr).unwrap().name, state
.online_users
.lock()
.unwrap()
.get(&addr)
.unwrap()
.lock()
.unwrap()
.name,
cf.code, cf.code,
cf.reason cf.reason
) )
@ -194,7 +198,15 @@ fn handle_close(
let msg = ChatMessage { let msg = ChatMessage {
text: format!( text: format!(
"{0} left.", "{0} left.",
state.online_users.lock().unwrap().get(&addr).unwrap().name state
.online_users
.lock()
.unwrap()
.get(&addr)
.unwrap()
.lock()
.unwrap()
.name
), ),
}; };
@ -206,6 +218,8 @@ fn handle_close(
.unwrap() .unwrap()
.get(&addr) .get(&addr)
.unwrap() .unwrap()
.lock()
.unwrap()
.name .name
.clone(); .clone();

View file

@ -8,7 +8,6 @@ use std::{
fs::{read_to_string, File}, fs::{read_to_string, File},
io::{BufRead, BufReader}, io::{BufRead, BufReader},
net::SocketAddr, net::SocketAddr,
pin::Pin,
sync::{Arc, Mutex}, sync::{Arc, Mutex},
}; };
use tokio::sync::broadcast; use tokio::sync::broadcast;
@ -44,8 +43,8 @@ fn load_names(path: &str) -> Result<Vec<String>> {
// Our shared state // Our shared state
pub struct AppState { pub struct AppState {
// We require unique usernames. This tracks which usernames have been taken. // We require unique usernames. This tracks which usernames have been taken.
online_users: Mutex<HashMap<SocketAddr, Pin<Box<User>>>>, online_users: Mutex<HashMap<SocketAddr, Arc<Mutex<User>>>>,
offline_users: Mutex<HashMap<String, Pin<Box<User>>>>, offline_users: Mutex<HashMap<String, Arc<Mutex<User>>>>,
// Channel used to send messages to all connected clients. // Channel used to send messages to all connected clients.
tx: broadcast::Sender<String>, tx: broadcast::Sender<String>,
// Master card decks // Master card decks
@ -71,8 +70,8 @@ async fn main() -> Result<()> {
// Set up application state for use with with_state(). // Set up application state for use with with_state().
// Main Broadcast Channel // Main Broadcast Channel
let (tx, _rx) = broadcast::channel(100); let (tx, _rx) = broadcast::channel(100);
let online_users = Mutex::new(HashMap::<SocketAddr, Pin<Box<User>>>::new()); let online_users = Mutex::new(HashMap::<SocketAddr, Arc<Mutex<User>>>::new());
let offline_users = Mutex::new(HashMap::<String, Pin<Box<User>>>::new()); let offline_users = Mutex::new(HashMap::<String, Arc<Mutex<User>>>::new());
let all_cards = load_cards_from_json("data/cah-cards-full.json")?; let all_cards = load_cards_from_json("data/cah-cards-full.json")?;
let games = Mutex::new(vec![]); let games = Mutex::new(vec![]);
let first_names = load_names("data/first.txt")?; let first_names = load_names("data/first.txt")?;