ye
This commit is contained in:
parent
c04f9dd6df
commit
9f405d9580
4 changed files with 87 additions and 43 deletions
|
@ -53,7 +53,7 @@ pub enum PlayerRole {
|
|||
#[derive(Debug)]
|
||||
pub struct CAHPlayer {
|
||||
/// Player's username
|
||||
pub player_name: String,
|
||||
pub name: String,
|
||||
/// This player's role
|
||||
pub role: PlayerRole,
|
||||
/// The player's hand
|
||||
|
@ -72,7 +72,7 @@ pub struct CAHGame {
|
|||
/// Black draw pile
|
||||
pub black: Vec<CAHCardBlack>,
|
||||
/// White discard pile
|
||||
pub jwhite_discard: Vec<CAHCardWhite>,
|
||||
pub white_discard: Vec<CAHCardWhite>,
|
||||
/// Black discard pile
|
||||
pub black_discard: Vec<CAHCardBlack>,
|
||||
/// Indicates game active/game over
|
||||
|
@ -189,14 +189,14 @@ impl CAHGame {
|
|||
pub fn create_player(&mut self, mut player: CAHPlayer) -> Result<()> {
|
||||
println!(
|
||||
"Creating player {} as {:?}",
|
||||
&player.player_name, &player.role
|
||||
&player.name, &player.role
|
||||
);
|
||||
|
||||
let mut hand_buf = vec![];
|
||||
for _ in 0..10 {
|
||||
hand_buf.push(self.draw_one_white()?);
|
||||
}
|
||||
println!("Dealing hand for {}", &player.player_name);
|
||||
println!("Dealing hand for {}", &player.name);
|
||||
player.white.extend(hand_buf);
|
||||
|
||||
self.players.push(player);
|
||||
|
|
37
src/api.rs
37
src/api.rs
|
@ -1,3 +1,4 @@
|
|||
use crate::AppState;
|
||||
use axum::{
|
||||
extract::{
|
||||
ws::{Message, WebSocket, WebSocketUpgrade},
|
||||
|
@ -6,19 +7,7 @@ use axum::{
|
|||
response::IntoResponse,
|
||||
};
|
||||
use futures::{sink::SinkExt, stream::StreamExt};
|
||||
use std::{
|
||||
collections::HashSet,
|
||||
sync::{Arc, Mutex},
|
||||
};
|
||||
use tokio::sync::broadcast;
|
||||
|
||||
// Our shared state
|
||||
pub struct AppState {
|
||||
// We require unique usernames. This tracks which usernames have been taken.
|
||||
pub user_set: Mutex<HashSet<String>>,
|
||||
// Channel used to send messages to all connected clients.
|
||||
pub tx: broadcast::Sender<String>,
|
||||
}
|
||||
use std::sync::Arc;
|
||||
|
||||
pub async fn websocket_handler(
|
||||
ws: WebSocketUpgrade,
|
||||
|
@ -35,18 +24,18 @@ pub async fn websocket(stream: WebSocket, state: Arc<AppState>) {
|
|||
let (mut sender, mut receiver) = stream.split();
|
||||
|
||||
// Username gets set in the receive loop, if it's valid.
|
||||
let mut username = String::new();
|
||||
let mut newplayer = String::new();
|
||||
// Loop until a text message is found.
|
||||
while let Some(Ok(message)) = receiver.next().await {
|
||||
if let Message::Text(name) = message {
|
||||
// If username that is sent by client is not taken, fill username string.
|
||||
check_username(&state, &mut username, &name);
|
||||
// If newplayer that is sent by client is not taken, fill newplayer string.
|
||||
check_username(&state, &mut newplayer, &name);
|
||||
|
||||
// If not empty we want to quit the loop else we want to quit function.
|
||||
if !username.is_empty() {
|
||||
if !newplayer.is_empty() {
|
||||
break;
|
||||
} else {
|
||||
// Only send our client that username is taken.
|
||||
// Only send our client that newplayer is taken.
|
||||
let _ = sender
|
||||
.send(Message::Text(String::from("Username already taken.")))
|
||||
.await;
|
||||
|
@ -61,7 +50,7 @@ pub async fn websocket(stream: WebSocket, state: Arc<AppState>) {
|
|||
let mut rx = state.tx.subscribe();
|
||||
|
||||
// Now send the "joined" message to all subscribers.
|
||||
let msg = format!("{username} joined.");
|
||||
let msg = format!("{newplayer} joined.");
|
||||
tracing::debug!("{msg}");
|
||||
let _ = state.tx.send(msg);
|
||||
|
||||
|
@ -78,13 +67,13 @@ pub async fn websocket(stream: WebSocket, state: Arc<AppState>) {
|
|||
|
||||
// Clone things we want to pass (move) to the receiving task.
|
||||
let tx = state.tx.clone();
|
||||
let name = username.clone();
|
||||
let name = newplayer.clone();
|
||||
|
||||
// Spawn a task that takes messages from the websocket, prepends the user
|
||||
// name, and sends them to all broadcast subscribers.
|
||||
let mut recv_task = tokio::spawn(async move {
|
||||
while let Some(Ok(Message::Text(text))) = receiver.next().await {
|
||||
// Add username before message.
|
||||
// Add newplayer before message.
|
||||
let _ = tx.send(format!("{name}: {text}"));
|
||||
}
|
||||
});
|
||||
|
@ -96,12 +85,12 @@ pub async fn websocket(stream: WebSocket, state: Arc<AppState>) {
|
|||
};
|
||||
|
||||
// Send "user left" message (similar to "joined" above).
|
||||
let msg = format!("{username} left.");
|
||||
let msg = format!("{newplayer} left.");
|
||||
tracing::debug!("{msg}");
|
||||
let _ = state.tx.send(msg);
|
||||
|
||||
// Remove username from map so new clients can take it again.
|
||||
state.user_set.lock().unwrap().remove(&username);
|
||||
// Remove newplayer from map so new clients can take it again.
|
||||
state.user_set.lock().unwrap().remove(&newplayer);
|
||||
}
|
||||
|
||||
pub fn check_username(state: &AppState, string: &mut String, name: &str) {
|
||||
|
|
42
src/main.rs
42
src/main.rs
|
@ -22,6 +22,7 @@ fn load_json(path: &str) -> Result<Vec<CAHCardSet>, Box<dyn Error>> {
|
|||
Ok(jayson)
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn test() -> Result<(), Box<dyn Error>> {
|
||||
// choose decks
|
||||
let cards_input_path: &str = "data/cah-cards-full.json";
|
||||
|
@ -32,14 +33,14 @@ fn test() -> Result<(), Box<dyn Error>> {
|
|||
println!("{}", &chosen_packs.len());
|
||||
|
||||
let test_player0 = CAHPlayer {
|
||||
player_name: "Adam".to_string(),
|
||||
name: "Adam".to_string(),
|
||||
role: PlayerRole::Host,
|
||||
white: vec![],
|
||||
black: vec![],
|
||||
};
|
||||
|
||||
let test_player1 = CAHPlayer {
|
||||
player_name: "Ferris".to_string(),
|
||||
name: "Ferris".to_string(),
|
||||
role: PlayerRole::Player,
|
||||
white: vec![],
|
||||
black: vec![],
|
||||
|
@ -71,8 +72,20 @@ fn test() -> Result<(), Box<dyn Error>> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
// Our shared state
|
||||
pub struct AppState {
|
||||
// We require unique usernames. This tracks which usernames have been taken.
|
||||
user_set: Mutex<HashSet<String>>,
|
||||
// Channel used to send messages to all connected clients.
|
||||
tx: broadcast::Sender<String>,
|
||||
// Master card decks
|
||||
all_cards: Mutex<Vec<CAHCardSet>>,
|
||||
// Games list
|
||||
games: Mutex<Vec<CAHGame>>,
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
async fn main() -> Result<(), Box<dyn Error>> {
|
||||
tracing_subscriber::registry()
|
||||
.with(
|
||||
tracing_subscriber::EnvFilter::try_from_default_env()
|
||||
|
@ -81,13 +94,28 @@ async fn main() {
|
|||
.with(tracing_subscriber::fmt::layer())
|
||||
.init();
|
||||
|
||||
let _ = test();
|
||||
|
||||
// Set up application state for use with with_state().
|
||||
let user_set = Mutex::new(HashSet::new());
|
||||
let (tx, _rx) = broadcast::channel(100);
|
||||
|
||||
let app_state = Arc::new(AppState { user_set, tx });
|
||||
// choose decks
|
||||
let cards_input_path: &str = "data/cah-cards-full.json";
|
||||
|
||||
// TODO: this should be a master card database and pointers
|
||||
// to the cards should be passed to the game instead of actual cards
|
||||
let all_cards = Mutex::new(load_json(cards_input_path)?);
|
||||
let games = Mutex::new(vec![]);
|
||||
let app_state = Arc::new(AppState {
|
||||
user_set,
|
||||
tx,
|
||||
all_cards,
|
||||
games,
|
||||
});
|
||||
|
||||
tracing::debug!(
|
||||
"Loaded {} Card packs!",
|
||||
&app_state.all_cards.lock().unwrap().len()
|
||||
);
|
||||
|
||||
let app = Router::new()
|
||||
.route("/", get(index))
|
||||
|
@ -97,6 +125,8 @@ async fn main() {
|
|||
let listener = tokio::net::TcpListener::bind("0.0.0.0:3030").await.unwrap();
|
||||
tracing::debug!("listening on {}", listener.local_addr().unwrap());
|
||||
axum::serve(listener, app).await.unwrap();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Include utf-8 file at **compile** time.
|
||||
|
|
|
@ -11,7 +11,10 @@
|
|||
div {
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
@ -19,11 +22,19 @@
|
|||
<div id="status">
|
||||
<p><em>Disconnected...</em></p>
|
||||
</div>
|
||||
<div>
|
||||
<form id="login" onsubmit="loginSubmit();return false">
|
||||
<input id="username" style="display:block; width:100px; box-sizing: border-box" type="text" placeholder="username">
|
||||
<button id="join-chat" type="submit">Join Chat</button>
|
||||
</form>
|
||||
<div style="display: flex;">
|
||||
<span>
|
||||
<p>Username</p>
|
||||
<p>Game Name</p>
|
||||
</span>
|
||||
<span>
|
||||
<form id="new-game" onsubmit="createGame();return false">
|
||||
<input id="username" style="display:block; width:100px; box-sizing: border-box" type="text" placeholder="username">
|
||||
<input id="gamename" style="display:block; width:100px; box-sizing: border-box" type="text" placeholder="game name">
|
||||
<br />
|
||||
<button id="create-game" type="submit">Create Game</button>
|
||||
</form>
|
||||
</span>
|
||||
</div>
|
||||
<div>
|
||||
<form id="chat" onsubmit="chatSubmit();return false">
|
||||
|
@ -34,9 +45,23 @@
|
|||
<script type="text/javascript">
|
||||
websocket = new WebSocket("ws://localhost:3030/websocket");
|
||||
|
||||
function loginSubmit() {
|
||||
document.getElementById("join-chat").disabled = true;
|
||||
websocket.send(username.value);
|
||||
function createGame() {
|
||||
document.getElementById("create-game").disabled = true;
|
||||
|
||||
let CAHPlayer = {
|
||||
name: username.value,
|
||||
role: 'h',
|
||||
white: [],
|
||||
black: [],
|
||||
};
|
||||
|
||||
let NewGameRequest = {
|
||||
name: gamename.value,
|
||||
host: CAHPlayer,
|
||||
packs: [0],
|
||||
};
|
||||
|
||||
websocket.send(JSON.stringify(NewGameRequest));
|
||||
};
|
||||
|
||||
websocket.onopen = function() {
|
||||
|
|
Loading…
Add table
Reference in a new issue