reserved usernames
This commit is contained in:
parent
959d300455
commit
d930583e12
6 changed files with 44 additions and 24 deletions
|
@ -31,7 +31,7 @@ pub fn Auth() -> impl IntoView {
|
||||||
})
|
})
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
);
|
);
|
||||||
set_username.set(input.value());
|
set_username.set("".to_string());
|
||||||
input.set_value("");
|
input.set_value("");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
42
readme.md
42
readme.md
|
@ -4,12 +4,13 @@ A game master server for the popular game [Cards Against Humanity](https://www.c
|
||||||
|
|
||||||
This started as a problem trying to play games with friends who are all on different platforms. This shall be as cross-platform as it gets. I want it to work on anything that can establish a connection and allow anyone to write any front-end they can dream up whether it be a web page, chat bot, VR, etc. Any clients who share a master server can play together across any platform.
|
This started as a problem trying to play games with friends who are all on different platforms. This shall be as cross-platform as it gets. I want it to work on anything that can establish a connection and allow anyone to write any front-end they can dream up whether it be a web page, chat bot, VR, etc. Any clients who share a master server can play together across any platform.
|
||||||
|
|
||||||
|
|
||||||
## Test/Dev:
|
## Test/Dev:
|
||||||
|
|
||||||
### Server
|
### Server
|
||||||
|
|
||||||
With auto-reload:
|
With auto-reload:
|
||||||
* [Install cargo-watch](https://github.com/watchexec/cargo-watch?tab=readme-ov-file#install)
|
|
||||||
|
- [Install cargo-watch](https://github.com/watchexec/cargo-watch?tab=readme-ov-file#install)
|
||||||
|
|
||||||
Then:
|
Then:
|
||||||
|
|
||||||
|
@ -19,14 +20,15 @@ watch -i client -cx run
|
||||||
|
|
||||||
or if you're lazy like me just run `./test`
|
or if you're lazy like me just run `./test`
|
||||||
|
|
||||||
|
|
||||||
Without auto-reload:
|
Without auto-reload:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
cargo run
|
cargo run
|
||||||
```
|
```
|
||||||
|
|
||||||
### Client
|
### Client
|
||||||
* [Install Trunk](https://trunkrs.dev/#install)
|
|
||||||
|
- [Install Trunk](https://trunkrs.dev/#install)
|
||||||
|
|
||||||
Then:
|
Then:
|
||||||
|
|
||||||
|
@ -37,31 +39,33 @@ trunk serve --open
|
||||||
## Build:
|
## Build:
|
||||||
|
|
||||||
### Client
|
### Client
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
trunk build --release
|
trunk build --release
|
||||||
```
|
```
|
||||||
|
|
||||||
### Server
|
### Server
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
cargo build
|
cargo build
|
||||||
```
|
```
|
||||||
|
|
||||||
----------
|
---
|
||||||
* The server automatically serves the built client from `/dist` at `0.0.0.0:3030`
|
|
||||||
|
|
||||||
* Configure any custom clients to connect to `ws://0.0.0.0:3030/websocket`
|
- The server automatically serves the built client from `/dist` at `0.0.0.0:3030`
|
||||||
|
|
||||||
|
- Configure any custom clients to connect to `ws://0.0.0.0:3030/websocket`
|
||||||
|
|
||||||
## TODO:
|
## TODO:
|
||||||
* finish game logic
|
|
||||||
* prevent multiple users claiming the same identity and then all losing it - may create a zombie
|
- finish game logic
|
||||||
* prevent zombie users after browser crash
|
- prevent zombie users after browser crash
|
||||||
* figure out proper auth - client's problem?
|
- figure out proper auth - client's problem?
|
||||||
* use db
|
- use db
|
||||||
* test bincode and probably move away from json
|
- test bincode and probably move away from json
|
||||||
* make typescript sdk
|
- make typescript sdk
|
||||||
* support card text editing
|
- support card text editing
|
||||||
* prevent import of cards that have been seen already and edited
|
- prevent import of cards that have been seen already and edited
|
||||||
* handle duplicates
|
- handle duplicates
|
||||||
* efficiency
|
- efficiency
|
||||||
* make demo clients for multiple platforms and screens
|
- make demo clients for multiple platforms and screens
|
||||||
|
|
|
@ -44,7 +44,7 @@ pub async fn on_websocket_connection(stream: WebSocket, state: Arc<AppState>, ad
|
||||||
// Subscribe to receive from global broadcast channel
|
// Subscribe to receive from global broadcast channel
|
||||||
let mut rx = state.tx.subscribe();
|
let mut rx = state.tx.subscribe();
|
||||||
|
|
||||||
// Submit new messages from this client to broadcast
|
// Send messages to this client
|
||||||
let mut send_task = tokio::spawn(async move {
|
let mut send_task = tokio::spawn(async move {
|
||||||
while let Ok(msg) = rx.recv().await {
|
while let Ok(msg) = rx.recv().await {
|
||||||
if sender.send(Message::Text(msg)).await.is_err() {
|
if sender.send(Message::Text(msg)).await.is_err() {
|
||||||
|
@ -53,7 +53,7 @@ pub async fn on_websocket_connection(stream: WebSocket, state: Arc<AppState>, ad
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Pass messages from broadcast down to this client
|
// Receive messages from this client
|
||||||
let mut recv_task = tokio::spawn(async move {
|
let mut recv_task = tokio::spawn(async move {
|
||||||
while let Some(Ok(message)) = receiver.next().await {
|
while let Some(Ok(message)) = receiver.next().await {
|
||||||
message_handler(state.clone(), addr, message)
|
message_handler(state.clone(), addr, message)
|
||||||
|
|
|
@ -132,6 +132,8 @@ fn handle_user_log_in(
|
||||||
};
|
};
|
||||||
|
|
||||||
tracing::debug!("{msg}");
|
tracing::debug!("{msg}");
|
||||||
|
} else if state.reserved_names.read().unwrap().contains(&new_name) {
|
||||||
|
tracing::debug!("name is taken");
|
||||||
} else {
|
} else {
|
||||||
state
|
state
|
||||||
.online_users
|
.online_users
|
||||||
|
@ -149,8 +151,16 @@ fn handle_user_log_in(
|
||||||
new_name
|
new_name
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Reserve name
|
||||||
|
state
|
||||||
|
.reserved_names
|
||||||
|
.write()
|
||||||
|
.unwrap()
|
||||||
|
.insert(new_name.clone());
|
||||||
|
|
||||||
tracing::debug!("{msg}");
|
tracing::debug!("{msg}");
|
||||||
tx.send(to_string::<ChatMessage>(&ChatMessage { text: msg })?)?;
|
tx.send(to_string::<ChatMessage>(&ChatMessage { text: msg })?)?;
|
||||||
|
tracing::debug!("Name {} reserved.", &new_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
tracing::debug!(
|
tracing::debug!(
|
||||||
|
@ -159,6 +169,9 @@ fn handle_user_log_in(
|
||||||
state.offline_users.read().unwrap().len()
|
state.offline_users.read().unwrap().len()
|
||||||
);
|
);
|
||||||
tx.send(games_update(state))?;
|
tx.send(games_update(state))?;
|
||||||
|
tx.send(chat_meta_update(state))?;
|
||||||
|
|
||||||
|
// send the user their new name
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@ use rand::prelude::IteratorRandom;
|
||||||
use rand::thread_rng;
|
use rand::thread_rng;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use std::{
|
use std::{
|
||||||
collections::HashMap,
|
collections::{HashMap, HashSet},
|
||||||
fs::{read_to_string, File},
|
fs::{read_to_string, File},
|
||||||
io::{BufRead, BufReader},
|
io::{BufRead, BufReader},
|
||||||
net::SocketAddr,
|
net::SocketAddr,
|
||||||
|
@ -339,6 +339,7 @@ pub 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.
|
||||||
|
pub reserved_names: RwLock<HashSet<String>>,
|
||||||
pub online_users: RwLock<HashMap<SocketAddr, Arc<RwLock<User>>>>,
|
pub online_users: RwLock<HashMap<SocketAddr, Arc<RwLock<User>>>>,
|
||||||
pub offline_users: RwLock<HashMap<String, Arc<RwLock<User>>>>,
|
pub offline_users: RwLock<HashMap<String, Arc<RwLock<User>>>>,
|
||||||
// Channel used to send messages to all connected clients.
|
// Channel used to send messages to all connected clients.
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
use anyhow::{Context, Result};
|
use anyhow::{Context, Result};
|
||||||
use axum::{routing::get, Router};
|
use axum::{routing::get, Router};
|
||||||
use std::{
|
use std::{
|
||||||
collections::HashMap,
|
collections::{HashMap, HashSet},
|
||||||
net::SocketAddr,
|
net::SocketAddr,
|
||||||
sync::{Arc, RwLock},
|
sync::{Arc, RwLock},
|
||||||
};
|
};
|
||||||
|
@ -28,6 +28,7 @@ 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 reserved_names = RwLock::new(HashSet::<String>::new());
|
||||||
let online_users = RwLock::new(HashMap::<SocketAddr, Arc<RwLock<User>>>::new());
|
let online_users = RwLock::new(HashMap::<SocketAddr, Arc<RwLock<User>>>::new());
|
||||||
let offline_users = RwLock::new(HashMap::<String, Arc<RwLock<User>>>::new());
|
let offline_users = RwLock::new(HashMap::<String, Arc<RwLock<User>>>::new());
|
||||||
let (packs, packs_meta) = load_cards_from_json("data/cah-cards-full.json")?;
|
let (packs, packs_meta) = load_cards_from_json("data/cah-cards-full.json")?;
|
||||||
|
@ -36,6 +37,7 @@ async fn main() -> Result<()> {
|
||||||
let last_names = load_names("data/last.txt")?;
|
let last_names = load_names("data/last.txt")?;
|
||||||
|
|
||||||
let app_state = Arc::new(AppState {
|
let app_state = Arc::new(AppState {
|
||||||
|
reserved_names,
|
||||||
online_users,
|
online_users,
|
||||||
offline_users,
|
offline_users,
|
||||||
tx,
|
tx,
|
||||||
|
|
Loading…
Add table
Reference in a new issue