JE DECEDE NOW
Some checks failed
Rust Checks / checks (push) Has been cancelled

Co-authored-by: CoCoSol <CoCoSol007@users.noreply.github.com>
This commit is contained in:
Tipragot 2024-04-10 23:29:53 +02:00
parent 63afb174cf
commit 01924cdcb8
8 changed files with 318 additions and 76 deletions

13
Cargo.lock generated
View file

@ -750,7 +750,9 @@ dependencies = [
"dashmap",
"futures",
"lazy_static",
"rand",
"serde",
"slotmap",
"tokio",
"uuid",
]
@ -775,6 +777,16 @@ dependencies = [
"autocfg",
]
[[package]]
name = "slotmap"
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dbff4acf519f630b3a3ddcfaea6c06b42174d9a44bc70c620e9ed1649d58b82a"
dependencies = [
"serde",
"version_check",
]
[[package]]
name = "smallvec"
version = "1.13.1"
@ -1007,6 +1019,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a183cf7feeba97b4dd1c0d46788634f6221d87fa961b305bed08c851829efcc0"
dependencies = [
"getrandom",
"serde",
]
[[package]]

View file

@ -13,9 +13,11 @@ bincode = "1.3.3"
dashmap = "5.5.3"
futures = "0.3.30"
lazy_static = "1.4.0"
rand = "0.8.5"
serde = { version = "1.0.197", features = ["derive"] }
slotmap = { version = "1.0.7", features = ["serde"] }
tokio = { version = "1.37.0", features = ["macros", "rt-multi-thread"] }
uuid = { version = "1.8.0", features = ["v4"] }
uuid = { version = "1.8.0", features = ["v4", "serde"] }
# [lints]
# workspace = true

25
crates/server/src/game.rs Normal file
View file

@ -0,0 +1,25 @@
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

@ -0,0 +1,68 @@
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,26 +1,62 @@
use std::collections::HashSet;
use std::marker::PhantomData;
use dashmap::mapref::entry::Entry;
use dashmap::DashMap;
use uuid::Uuid;
struct Action {
title: String,
description: String,
tile_id: Uuid,
image:
pub trait UuidKey: From<Uuid> {
fn to_uuid(&self) -> Uuid;
}
struct ActionType {}
pub struct SlotDashMap<K: UuidKey, V>(DashMap<Uuid, V>, PhantomData<K>);
struct Player {}
impl<K: UuidKey, V> SlotDashMap<K, V> {
pub fn new() -> Self {
Self(DashMap::new(), PhantomData)
}
impl Player {
async fn make_action(&mut self, actions: HashSet<Action>) -> Action {
todo!()
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);
}
}
}
async fn border_wars_classic(players: HashSet<Player>) {
let player1 = players.iter().next().unwrap();
let action = player1.make_action(HashSet::new()).await;
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

@ -0,0 +1,20 @@
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

@ -0,0 +1,127 @@
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,31 +1,22 @@
use axum::extract::ws::{Message, WebSocket};
use axum::extract::ws::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::Serialize;
use tokio::sync::mpsc::{channel, Sender};
use uuid::Uuid;
trait Request: DeserializeOwned + Serialize {
type Response: Response;
}
trait Response: DeserializeOwned + Serialize {}
lazy_static! {
static ref CLIENTS: DashMap<Uuid, Sender<Vec<u8>>> = DashMap::new();
}
mod game;
mod login;
#[tokio::main]
async fn main() {
let app = Router::new().route(
"/",
get(|ws: WebSocketUpgrade| async { ws.on_upgrade(|socket| handle(socket)) }),
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
@ -33,46 +24,6 @@ async fn main() {
axum::serve(listener, app).await.expect("failed to serve");
}
async fn handle(mut socket: WebSocket) {
let (mut writer, mut reader) = socket.split();
let (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(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;
};
if let Err(error) = message_received(client_id, data).await {
println!("Error: {}", error);
break;
};
}
CLIENTS.remove(&client_id);
}
async fn send_message(id: Uuid, message: Vec<u8>) -> anyhow::Result<()> {
if let Some(sender) = CLIENTS.get(&id) {
sender.send(message).await?;
}
Ok(())
}
async fn message_received(id: Uuid, message: Vec<u8>) -> anyhow::Result<()> {
async fn handle(mut socket: WebSocket) -> anyhow::Result<()> {
Ok(())
}