generated from tipragot/rust
This commit is contained in:
parent
27e0aad29d
commit
95c1890a1e
|
@ -1,42 +0,0 @@
|
|||
use std::hash::Hash;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
|
||||
use dashmap::mapref::multiple::RefMutMulti;
|
||||
use dashmap::mapref::one::RefMut;
|
||||
|
||||
pub enum GlobalRefMut<'a, K: Eq + Hash, V> {
|
||||
Single(RefMut<'a, K, V>),
|
||||
Multi(RefMutMulti<'a, K, V>),
|
||||
}
|
||||
|
||||
impl<'a, K: Eq + Hash, V> From<RefMut<'a, K, V>> for GlobalRefMut<'a, K, V> {
|
||||
fn from(v: RefMut<'a, K, V>) -> Self {
|
||||
Self::Single(v)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, K: Eq + Hash, V> From<RefMutMulti<'a, K, V>> for GlobalRefMut<'a, K, V> {
|
||||
fn from(v: RefMutMulti<'a, K, V>) -> Self {
|
||||
Self::Multi(v)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, K: Eq + Hash, V> Deref for GlobalRefMut<'a, K, V> {
|
||||
type Target = V;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
match self {
|
||||
Self::Single(v) => v.value(),
|
||||
Self::Multi(v) => v.value(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, K: Eq + Hash, V> DerefMut for GlobalRefMut<'a, K, V> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
match self {
|
||||
Self::Single(v) => v.value_mut(),
|
||||
Self::Multi(v) => v.value_mut(),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,182 +0,0 @@
|
|||
use std::collections::{HashMap, HashSet};
|
||||
use std::hash::RandomState;
|
||||
use std::sync::Arc;
|
||||
|
||||
use anyhow::{bail, Context};
|
||||
use axum::extract::ws::{Message, WebSocket};
|
||||
use axum::extract::WebSocketUpgrade;
|
||||
use axum::routing::get;
|
||||
use axum::Router;
|
||||
use dashmap::mapref::entry::Entry;
|
||||
use dashmap::mapref::multiple::RefMutMulti;
|
||||
use dashmap::mapref::one::RefMut;
|
||||
use dashmap::DashMap;
|
||||
use futures::stream::{SplitSink, SplitStream};
|
||||
use futures::{SinkExt, StreamExt};
|
||||
use lazy_static::lazy_static;
|
||||
use serde::de::DeserializeOwned;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use server::GlobalRefMut;
|
||||
use slotmap::SlotMap;
|
||||
use tokio::sync::mpsc::{channel, Receiver, Sender};
|
||||
use tokio::sync::RwLock;
|
||||
use uuid::Uuid;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ConnectionSender(Sender<Vec<u8>>);
|
||||
|
||||
impl ConnectionSender {
|
||||
pub async fn send<T: Serialize>(&mut self, message: T) -> anyhow::Result<()> {
|
||||
Ok(self.0.send(bincode::serialize(&message)?).await?)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ConnectionReader(SplitStream<WebSocket>);
|
||||
|
||||
impl ConnectionReader {
|
||||
pub async fn read<T: DeserializeOwned>(&mut self) -> anyhow::Result<T> {
|
||||
loop {
|
||||
let Message::Binary(message) = self.0.next().await.context("no message")?? else {
|
||||
continue;
|
||||
};
|
||||
return Ok(bincode::deserialize(&message)?);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
let app = Router::new().route(
|
||||
"/",
|
||||
get(|ws: WebSocketUpgrade| async {
|
||||
ws.on_upgrade(|socket| async {
|
||||
let (mut sender, receiver) = socket.split();
|
||||
let (send_tx, mut send_rx) = channel(16);
|
||||
tokio::spawn(async move {
|
||||
while let Some(message) = send_rx.recv().await {
|
||||
sender.send(Message::Binary(message)).await?;
|
||||
}
|
||||
Ok::<(), axum::Error>(())
|
||||
});
|
||||
if let Err(e) = handle(ConnectionSender(send_tx), ConnectionReader(receiver)).await
|
||||
{
|
||||
eprintln!("Error: {}", e);
|
||||
}
|
||||
})
|
||||
}),
|
||||
);
|
||||
let listener = tokio::net::TcpListener::bind("0.0.0.0:80")
|
||||
.await
|
||||
.expect("failed to bind");
|
||||
axum::serve(listener, app).await.expect("failed to serve");
|
||||
}
|
||||
|
||||
lazy_static! {
|
||||
static ref LOBBIES: DashMap<Uuid, Lobby> = DashMap::new();
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
enum LoginRequest {
|
||||
CreateLobby {
|
||||
username: String,
|
||||
public: bool,
|
||||
},
|
||||
JoinLobby {
|
||||
lobby_id: Option<Uuid>,
|
||||
username: String,
|
||||
},
|
||||
}
|
||||
|
||||
slotmap::new_key_type! {
|
||||
struct ConnectionId;}
|
||||
|
||||
struct Lobby {
|
||||
id: Uuid,
|
||||
public: bool,
|
||||
connections: SlotMap<ConnectionId, LobbyConnection>,
|
||||
}
|
||||
|
||||
struct LobbyConnection {
|
||||
sender: ConnectionSender,
|
||||
username: String,
|
||||
ready: bool,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
struct LobbyJoined(Uuid);
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
struct IAmReady;
|
||||
|
||||
async fn handle(
|
||||
mut sender: ConnectionSender,
|
||||
mut receiver: ConnectionReader,
|
||||
) -> anyhow::Result<()> {
|
||||
// Find or create a lobby
|
||||
let login_request: LoginRequest = receiver.read().await?;
|
||||
let (mut lobby, username) = match login_request {
|
||||
LoginRequest::CreateLobby { username, public } => (create_lobby(public).await, username),
|
||||
LoginRequest::JoinLobby { lobby_id, username } => (
|
||||
match lobby_id {
|
||||
Some(id) => LOBBIES.get_mut(&id).context("lobby not found")?.into(),
|
||||
None => match find_random_lobby().await {
|
||||
Some(lobby) => lobby,
|
||||
None => create_lobby(true).await,
|
||||
},
|
||||
},
|
||||
username,
|
||||
),
|
||||
};
|
||||
|
||||
// Add the user to the lobby
|
||||
let lobby_id = lobby.id;
|
||||
sender.send(LobbyJoined(lobby_id)).await?;
|
||||
let connection_id = lobby.connections.insert(LobbyConnection {
|
||||
sender,
|
||||
username,
|
||||
ready: false,
|
||||
});
|
||||
drop(lobby);
|
||||
|
||||
// Wait for the user to be ready
|
||||
let disconnected = receiver.read::<IAmReady>().await.is_err();
|
||||
|
||||
// Check to start the game
|
||||
let Entry::Occupied(mut lobby) = LOBBIES.entry(lobby_id) else {
|
||||
bail!("lobby not found");
|
||||
};
|
||||
if disconnected {
|
||||
lobby.get_mut().connections.remove(connection_id);
|
||||
}
|
||||
|
||||
if lobby.get().connections.is_empty() {
|
||||
LOBBIES.remove(&lobby_id);
|
||||
return Ok(());
|
||||
}
|
||||
let should_start = lobby.connections.iter().all(|(_, c)| c.ready);
|
||||
|
||||
todo!()
|
||||
}
|
||||
|
||||
async fn create_lobby(public: bool) -> GlobalRefMut<'static, Uuid, Lobby> {
|
||||
loop {
|
||||
let id = Uuid::new_v4();
|
||||
if let Entry::Vacant(e) = LOBBIES.entry(id) {
|
||||
break e
|
||||
.insert(Lobby {
|
||||
id,
|
||||
public,
|
||||
connections: SlotMap::with_key(),
|
||||
})
|
||||
.into();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn find_random_lobby() -> Option<GlobalRefMut<'static, Uuid, Lobby>> {
|
||||
LOBBIES
|
||||
.iter_mut()
|
||||
.filter(|lobby| lobby.public)
|
||||
.min_by_key(|lobby| lobby.connections.len())
|
||||
.map(GlobalRefMut::from)
|
||||
}
|
|
@ -1,25 +0,0 @@
|
|||
use dashmap::DashMap;
|
||||
use lazy_static::lazy_static;
|
||||
use server::SlotDashMap;
|
||||
use slotmap::SlotMap;
|
||||
use uuid::Uuid;
|
||||
|
||||
server::new_key_type! {
|
||||
struct GameId;
|
||||
}
|
||||
|
||||
lazy_static! {
|
||||
static ref GAMES: SlotDashMap<GameId, Game> = SlotDashMap::new();
|
||||
}
|
||||
|
||||
slotmap::new_key_type! {
|
||||
struct PlayerId;
|
||||
}
|
||||
|
||||
pub struct Game {
|
||||
players: SlotMap<PlayerId, Player>,
|
||||
}
|
||||
|
||||
pub struct Player {
|
||||
connection_secret: Uuid,
|
||||
}
|
|
@ -1,68 +0,0 @@
|
|||
use std::collections::{HashSet, LinkedList};
|
||||
use std::time::Duration;
|
||||
|
||||
use rand::Rng;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use slotmap::SlotMap;
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::{receive_message, send_message};
|
||||
|
||||
slotmap::new_key_type! {
|
||||
struct ActionId;
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
struct Action {
|
||||
title: String,
|
||||
description: String,
|
||||
next_actions: SlotMap<ActionId, Self>,
|
||||
tile_position: Option<(i32, i32)>,
|
||||
image_id: Uuid,
|
||||
}
|
||||
|
||||
struct Player {
|
||||
connection: Option<Uuid>,
|
||||
secret: Uuid,
|
||||
}
|
||||
|
||||
impl Player {
|
||||
fn get_random_action(actions: &SlotMap<ActionId, Action>) -> LinkedList<ActionId> {
|
||||
if actions.len() == 0 {
|
||||
return LinkedList::new();
|
||||
}
|
||||
let random_index = rand::thread_rng().gen_range(0..actions.len());
|
||||
let action = actions.iter().nth(random_index).unwrap();
|
||||
let mut result = LinkedList::new();
|
||||
result.push_back(action.0);
|
||||
result.append(&mut Self::get_random_action(&action.1.next_actions));
|
||||
result
|
||||
}
|
||||
|
||||
async fn make_action(
|
||||
&mut self,
|
||||
actions: &SlotMap<ActionId, Action>,
|
||||
timeout: Duration,
|
||||
) -> LinkedList<ActionId> {
|
||||
let Some(connection) = self.connection else {
|
||||
return Self::get_random_action(actions);
|
||||
};
|
||||
|
||||
let Ok(encoded_message) = bincode::serialize(actions) else {
|
||||
return Self::get_random_action(actions);
|
||||
};
|
||||
|
||||
if !send_message(connection, encoded_message).await {
|
||||
return Self::get_random_action(actions);
|
||||
}
|
||||
|
||||
// Get the client response
|
||||
match tokio::time::timeout(timeout, receive_message(connection)).await {
|
||||
Ok(Some(response)) => match bincode::deserialize(&response) {
|
||||
Ok(response) => response,
|
||||
Err(_) => Self::get_random_action(actions),
|
||||
},
|
||||
_ => Self::get_random_action(actions),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,97 +0,0 @@
|
|||
use std::collections::{HashMap, HashSet};
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use uuid::Uuid;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
|
||||
pub struct GameId(Uuid);
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
|
||||
pub struct PlayerId(Uuid);
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
|
||||
pub struct PlayerSecret(Uuid);
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct PlayerProfile {
|
||||
pub username: String,
|
||||
pub image_id: Uuid,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub enum LoginRequest {
|
||||
Create {
|
||||
username: String,
|
||||
},
|
||||
JoinRandom {
|
||||
username: String,
|
||||
},
|
||||
Join {
|
||||
game_id: GameId,
|
||||
username: String,
|
||||
},
|
||||
Rejoin {
|
||||
game_id: GameId,
|
||||
player_id: PlayerId,
|
||||
secret: PlayerSecret,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub enum LoginResponse<T> {
|
||||
Refused(String),
|
||||
Success(T),
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct CreateResponse {
|
||||
game_id: GameId,
|
||||
player_id: PlayerId,
|
||||
secret: PlayerSecret,
|
||||
options: Vec<GameSettingField>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct GameSettingField {
|
||||
pub name: String,
|
||||
pub description: String,
|
||||
pub field_type: GameSettingFieldType,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub enum GameSettingFieldType {
|
||||
Integer { min: i32, max: i32 },
|
||||
Decimal { min: f32, max: f32 },
|
||||
String { min_len: usize, max_len: usize },
|
||||
Choice { choices: HashSet<String> },
|
||||
Boolean,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub enum GameSettingFieldValue {
|
||||
Integer(i32),
|
||||
Decimal(f32),
|
||||
String(String),
|
||||
Choice(String),
|
||||
Boolean(bool),
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct JoinRandomResponse {
|
||||
game_id: GameId,
|
||||
player_id: PlayerId,
|
||||
secret: PlayerSecret,
|
||||
players: HashMap<PlayerId, PlayerProfile>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct JoinResponse {
|
||||
player_id: PlayerId,
|
||||
secret: PlayerSecret,
|
||||
players: HashMap<PlayerId, PlayerProfile>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct RejoinResponse {
|
||||
players: HashMap<PlayerId, PlayerProfile>,
|
||||
}
|
|
@ -1,62 +0,0 @@
|
|||
use std::marker::PhantomData;
|
||||
|
||||
use dashmap::mapref::entry::Entry;
|
||||
use dashmap::DashMap;
|
||||
use uuid::Uuid;
|
||||
|
||||
pub trait UuidKey: From<Uuid> {
|
||||
fn to_uuid(&self) -> Uuid;
|
||||
}
|
||||
|
||||
pub struct SlotDashMap<K: UuidKey, V>(DashMap<Uuid, V>, PhantomData<K>);
|
||||
|
||||
impl<K: UuidKey, V> SlotDashMap<K, V> {
|
||||
pub fn new() -> Self {
|
||||
Self(DashMap::new(), PhantomData)
|
||||
}
|
||||
|
||||
pub fn insert(&self, value: V) -> K {
|
||||
loop {
|
||||
let id = Uuid::new_v4();
|
||||
let Entry::Vacant(entry) = self.0.entry(id) else {
|
||||
continue;
|
||||
};
|
||||
entry.insert(value);
|
||||
return K::from(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<K: UuidKey, V> Default for SlotDashMap<K, V> {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! new_key_type {
|
||||
( $(#[$outer:meta])* $vis:vis struct $name:ident; $($rest:tt)* ) => {
|
||||
$(#[$outer])*
|
||||
#[derive(Copy, Clone, Default,
|
||||
Eq, PartialEq, Ord, PartialOrd,
|
||||
Hash, Debug)]
|
||||
#[repr(transparent)]
|
||||
$vis struct $name(::uuid::Uuid);
|
||||
|
||||
impl ::core::convert::From<::uuid::Uuid> for $name {
|
||||
fn from(k: ::uuid::Uuid) -> Self {
|
||||
$name(k)
|
||||
}
|
||||
}
|
||||
|
||||
impl $crate::UuidKey for $name {
|
||||
fn to_uuid(&self) -> ::uuid::Uuid {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
$crate::new_key_type!($($rest)*);
|
||||
};
|
||||
|
||||
() => {}
|
||||
}
|
|
@ -1,64 +0,0 @@
|
|||
use std::collections::HashMap;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use uuid::Uuid;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
|
||||
pub struct LobbyId(Uuid);
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct Lobby {
|
||||
id: LobbyId,
|
||||
public: bool,
|
||||
players: HashMap<PlayerId, LobbyPlayer>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct LobbyPlayer {
|
||||
username: String,
|
||||
ready: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
|
||||
pub struct PlayerId(Uuid);
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
|
||||
pub struct GameId(Uuid);
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
|
||||
pub struct RejoinToken(Uuid);
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub enum LoginRequest {
|
||||
CreateLobby {
|
||||
username: String,
|
||||
public: bool,
|
||||
},
|
||||
JoinLobby {
|
||||
lobby_id: Option<Uuid>,
|
||||
username: String,
|
||||
},
|
||||
RejoinGame {
|
||||
token: RejoinToken,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct LobbyJoined {
|
||||
player_id: PlayerId,
|
||||
lobby: Lobby,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub enum LobbyClientPacket {
|
||||
Ready(bool),
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub enum LobbyServerPacket {
|
||||
LobbyUpdated(Lobby),
|
||||
GameStarted(GameId, RejoinToken),
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct RejoinResponse;
|
|
@ -1,20 +0,0 @@
|
|||
use anyhow::{bail, Context};
|
||||
use axum::extract::ws::{Message, WebSocket};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use uuid::Uuid;
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub enum LoginRequest {
|
||||
Create { username: String },
|
||||
}
|
||||
|
||||
pub async fn handle_client_login(socket: &mut WebSocket) -> anyhow::Result<()> {
|
||||
let Message::Binary(login_data) = socket.recv().await.context("client disconnected")?? else {
|
||||
bail!("expected login request");
|
||||
};
|
||||
|
||||
let login_request = bincode::deserialize(&login_data)?;
|
||||
match login_request {
|
||||
LoginRequest::Create { username } => todo!(),
|
||||
}
|
||||
}
|
|
@ -1,127 +0,0 @@
|
|||
use std::collections::LinkedList;
|
||||
|
||||
use anyhow::{bail, Context};
|
||||
use axum::extract::ws::{Message, WebSocket};
|
||||
use axum::extract::WebSocketUpgrade;
|
||||
use axum::routing::get;
|
||||
use axum::Router;
|
||||
use dashmap::mapref::entry::Entry;
|
||||
use dashmap::DashMap;
|
||||
use futures::{SinkExt, StreamExt};
|
||||
use lazy_static::lazy_static;
|
||||
use serde::de::DeserializeOwned;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use slotmap::SlotMap;
|
||||
use tokio::sync::mpsc::{channel, Receiver, Sender};
|
||||
use uuid::Uuid;
|
||||
|
||||
slotmap::new_key_type! {
|
||||
struct PlayerId;
|
||||
}
|
||||
|
||||
enum WorldModification {
|
||||
SetTile { r: i32, s: i32, image_id: Uuid },
|
||||
}
|
||||
|
||||
struct Player {
|
||||
connection_secret: Uuid,
|
||||
unsent_modifications: LinkedList<WorldModification>,
|
||||
}
|
||||
|
||||
pub struct Game {
|
||||
players: SlotMap<PlayerId, Player>,
|
||||
}
|
||||
|
||||
trait Request: DeserializeOwned + Serialize {
|
||||
type Response: Response;
|
||||
}
|
||||
|
||||
trait Response: DeserializeOwned + Serialize {}
|
||||
|
||||
lazy_static! {
|
||||
static ref CLIENTS: DashMap<Uuid, Sender<Vec<u8>>> = DashMap::new();
|
||||
static ref GAMES: DashMap<Uuid, Game> = DashMap::new();
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub enum LoginRequest {
|
||||
Create { pseudo: String },
|
||||
JoinRandom { pseudo: String },
|
||||
Join { game_id: Uuid, pseudo: String },
|
||||
Rejoin { game_id: Uuid },
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
let app = Router::new().route(
|
||||
"/",
|
||||
get(|ws: WebSocketUpgrade| async {
|
||||
ws.on_upgrade(|socket| async {
|
||||
if let Err(e) = handle(socket).await {
|
||||
eprintln!("Error: {}", e);
|
||||
}
|
||||
})
|
||||
}),
|
||||
);
|
||||
let listener = tokio::net::TcpListener::bind("0.0.0.0:80")
|
||||
.await
|
||||
.expect("failed to bind");
|
||||
axum::serve(listener, app).await.expect("failed to serve");
|
||||
}
|
||||
|
||||
async fn handle(mut socket: WebSocket) -> anyhow::Result<()> {
|
||||
let Message::Binary(data) = socket
|
||||
.recv()
|
||||
.await
|
||||
.context("client disconnected before login")??
|
||||
else {
|
||||
bail!("expected login request");
|
||||
};
|
||||
|
||||
let login_request = bincode::deserialize(&data)?;
|
||||
match login_request {
|
||||
LoginRequest::Create { pseudo } => todo!(),
|
||||
LoginRequest::JoinRandom { pseudo } => todo!(),
|
||||
LoginRequest::Join { game_id, pseudo } => todo!(),
|
||||
LoginRequest::Rejoin { game_id } => todo!(),
|
||||
}
|
||||
|
||||
let (mut writer, mut reader) = socket.split();
|
||||
|
||||
let (global_sender, mut receiver) = channel(128);
|
||||
let client_id = loop {
|
||||
let id = Uuid::new_v4();
|
||||
let Entry::Vacant(entry) = CLIENTS.entry(id) else {
|
||||
continue;
|
||||
};
|
||||
entry.insert(global_sender);
|
||||
break id;
|
||||
};
|
||||
|
||||
tokio::spawn(async move {
|
||||
while let Some(message) = receiver.recv().await {
|
||||
writer.send(Message::Binary(message)).await?;
|
||||
}
|
||||
Ok::<(), axum::Error>(())
|
||||
});
|
||||
|
||||
while let Some(Ok(message)) = reader.next().await {
|
||||
let Message::Binary(data) = message else {
|
||||
continue;
|
||||
};
|
||||
|
||||
todo!()
|
||||
}
|
||||
|
||||
CLIENTS.remove(&client_id);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn send_message(id: Uuid, message: Vec<u8>) -> bool {
|
||||
if let Some(sender) = CLIENTS.get(&id) {
|
||||
sender.send(message).await.is_ok()
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
|
@ -1,62 +0,0 @@
|
|||
use std::collections::HashMap;
|
||||
|
||||
use anyhow::{bail, Context};
|
||||
use axum::extract::ws::{Message, WebSocket};
|
||||
use axum::extract::WebSocketUpgrade;
|
||||
use axum::routing::get;
|
||||
use axum::Router;
|
||||
use dashmap::mapref::entry::Entry;
|
||||
use dashmap::DashMap;
|
||||
use lazy_static::lazy_static;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use server::{GameId, Lobby, LobbyId, LoginRequest, PlayerId};
|
||||
use uuid::Uuid;
|
||||
|
||||
lazy_static! {
|
||||
static ref LOBBIES: DashMap<LobbyId, Lobby> = DashMap::new();
|
||||
// static ref GAMES: DashMap<GameId, Game> = DashMap::new();
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
let app = Router::new().route(
|
||||
"/",
|
||||
get(|ws: WebSocketUpgrade| async {
|
||||
ws.on_upgrade(|socket| async {
|
||||
if let Err(e) = handle(socket).await {
|
||||
eprintln!("Error: {}", e);
|
||||
}
|
||||
})
|
||||
}),
|
||||
);
|
||||
let listener = tokio::net::TcpListener::bind("0.0.0.0:80")
|
||||
.await
|
||||
.expect("failed to bind");
|
||||
axum::serve(listener, app).await.expect("failed to serve");
|
||||
}
|
||||
|
||||
async fn handle(mut socket: WebSocket) -> anyhow::Result<()> {
|
||||
let Message::Binary(login_data) = socket.recv().await.context("client disconnected")?? else {
|
||||
bail!("expected login request");
|
||||
};
|
||||
let login_request = bincode::deserialize(&login_data)?;
|
||||
match login_request {
|
||||
LoginRequest::CreateLobby { username, public } => {
|
||||
let lobby_id = loop {
|
||||
let id = Uuid::new_v4();
|
||||
let Entry::Vacant(entry) = LOBBIES.entry(LobbyId(id)) else {
|
||||
continue;
|
||||
};
|
||||
entry.insert()
|
||||
}
|
||||
}
|
||||
LoginRequest::JoinLobby { lobby_id, username } => todo!(),
|
||||
LoginRequest::RejoinGame { token } => todo!(),
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn handle_game_creation(mut socket: WebSocket, username: String) -> anyhow::Result<()> {
|
||||
Ok(())
|
||||
}
|
|
@ -1,73 +0,0 @@
|
|||
use std::marker::PhantomData;
|
||||
|
||||
use dashmap::mapref::entry::Entry;
|
||||
use dashmap::DashMap;
|
||||
use uuid::Uuid;
|
||||
|
||||
pub trait UuidKey: From<Uuid> + Copy {
|
||||
fn to_uuid(&self) -> Uuid;
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! new_id_type {
|
||||
( $($vis:vis struct $name:ident;)* ) => {
|
||||
$(
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, ::serde::Serialize, ::serde::Deserialize)]
|
||||
$vis struct $name(::uuid::Uuid);
|
||||
|
||||
impl ::core::convert::From<::uuid::Uuid> for $name {
|
||||
fn from(k: ::uuid::Uuid) -> Self {
|
||||
$name(k)
|
||||
}
|
||||
}
|
||||
|
||||
impl $crate::utils::UuidKey for $name {
|
||||
fn to_uuid(&self) -> ::uuid::Uuid {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
)*
|
||||
};
|
||||
}
|
||||
|
||||
pub struct IdDashMap<K: UuidKey, V>(DashMap<Uuid, V>, PhantomData<K>);
|
||||
|
||||
impl<K: UuidKey, V> IdDashMap<K, V> {
|
||||
pub fn new() -> Self {
|
||||
Self(DashMap::new(), PhantomData)
|
||||
}
|
||||
|
||||
pub fn insert(&self, value: V) -> K {
|
||||
loop {
|
||||
let id = Uuid::new_v4();
|
||||
let Entry::Vacant(entry) = self.0.entry(id) else {
|
||||
continue;
|
||||
};
|
||||
entry.insert(value);
|
||||
return K::from(id);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn insert_with_id(&self, create_value: impl FnOnce(K) -> V) -> K {
|
||||
loop {
|
||||
let id = Uuid::new_v4();
|
||||
let Entry::Vacant(entry) = self.0.entry(id) else {
|
||||
continue;
|
||||
};
|
||||
let id = K::from(id);
|
||||
let value = create_value(id);
|
||||
entry.insert(value);
|
||||
return id;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn remove(&self, id: K) {
|
||||
self.0.remove(&id.to_uuid());
|
||||
}
|
||||
}
|
||||
|
||||
impl<K: UuidKey, V> Default for IdDashMap<K, V> {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
|
@ -25,13 +25,13 @@ pub enum ServerPacket {
|
|||
LobbyUpdated(Lobby),
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
#[derive(Clone, Serialize, Deserialize)]
|
||||
pub struct Lobby {
|
||||
pub public: bool,
|
||||
pub players: HashMap<Uuid, LobbyPlayer>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
#[derive(Clone, Serialize, Deserialize)]
|
||||
pub struct LobbyPlayer {
|
||||
pub username: String,
|
||||
pub ready: bool,
|
||||
|
|
|
@ -5,7 +5,6 @@ use axum::extract::ws::{Message, WebSocket};
|
|||
use axum::extract::WebSocketUpgrade;
|
||||
use axum::routing::get;
|
||||
use axum::Router;
|
||||
use dashmap::DashMap;
|
||||
use futures::{SinkExt, StreamExt};
|
||||
use lazy_static::lazy_static;
|
||||
use log::warn;
|
||||
|
@ -117,6 +116,8 @@ async fn handle_unauthenticated(client_id: Uuid, packet: ClientPacket) {
|
|||
},
|
||||
)]),
|
||||
};
|
||||
let message = bincode::serialize(&ServerPacket::LobbyUpdated(lobby.clone()))
|
||||
.expect("failed to serialize lobby");
|
||||
let mut lobbies = LOBBIES.write().await;
|
||||
lobbies.insert(lobby_id, lobby);
|
||||
CLIENTS
|
||||
|
@ -126,6 +127,7 @@ async fn handle_unauthenticated(client_id: Uuid, packet: ClientPacket) {
|
|||
.expect("client not found")
|
||||
.status = ClientStatus::InLobby(lobby_id);
|
||||
send_packet(client_id, ServerPacket::LobbyJoined(lobby_id)).await;
|
||||
send_message(client_id, message).await;
|
||||
}
|
||||
ClientPacket::JoinLobby { lobby_id, username } => {
|
||||
let mut lobbies = LOBBIES.write().await;
|
||||
|
@ -178,7 +180,8 @@ async fn handle_unauthenticated(client_id: Uuid, packet: ClientPacket) {
|
|||
.status = ClientStatus::InLobby(lobby_id);
|
||||
send_packet(client_id, ServerPacket::LobbyJoined(lobby_id)).await;
|
||||
|
||||
let message = bincode::serialize(&lobby).expect("failed to serialize lobby");
|
||||
let message = bincode::serialize(&ServerPacket::LobbyUpdated(lobby.clone()))
|
||||
.expect("failed to serialize lobby"); // PAS BON
|
||||
for player_id in lobby.players.keys() {
|
||||
send_message(*player_id, &message).await;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue