Fixed mon reuf
Some checks failed
Rust Checks / checks (push) Has been cancelled

This commit is contained in:
Tipragot 2024-04-12 20:52:20 +02:00
parent 27e0aad29d
commit 95c1890a1e
13 changed files with 7 additions and 826 deletions

View file

@ -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(),
}
}
}

View file

@ -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)
}

View file

@ -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,
}

View file

@ -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),
}
}
}

View file

@ -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>,
}

View file

@ -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)*);
};
() => {}
}

View file

@ -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;

View file

@ -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!(),
}
}

View file

@ -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
}
}

View file

@ -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(())
}

View file

@ -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()
}
}

View file

@ -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,

View file

@ -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;
}