forked from DigitalExtinction/Game
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Relates to DigitalExtinction#255.
- Loading branch information
Showing
5 changed files
with
188 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
[package] | ||
name = "de_server" | ||
description = "Digital Extinction lobby server." | ||
|
||
version.workspace = true | ||
edition.workspace = true | ||
authors.workspace = true | ||
repository.workspace = true | ||
keywords.workspace = true | ||
homepage.workspace = true | ||
license.workspace = true | ||
categories.workspace = true | ||
|
||
[dependencies] | ||
actix-web.workspace = true | ||
jsonwebtoken.workspace = true | ||
serde.workspace = true |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
use std::{ | ||
collections::HashMap, | ||
sync::{Mutex, MutexGuard}, | ||
}; | ||
|
||
use serde::{Deserialize, Serialize}; | ||
|
||
#[derive(Default)] | ||
pub struct Games(Mutex<GamesInner>); | ||
|
||
impl Games { | ||
pub fn inner(&self) -> MutexGuard<'_, GamesInner> { | ||
self.0.lock().unwrap() | ||
} | ||
} | ||
|
||
#[derive(Default)] | ||
pub struct GamesInner { | ||
max_id: u16, | ||
games: HashMap<u16, Game>, | ||
} | ||
|
||
impl GamesInner { | ||
pub fn games(&self) -> &HashMap<u16, Game> { | ||
&self.games | ||
} | ||
|
||
pub fn new_game(&mut self, config: GameConfig) { | ||
self.games.insert(self.max_id, Game::empty(config)); | ||
self.max_id = self.max_id.wrapping_add(1); | ||
} | ||
} | ||
|
||
#[derive(Serialize)] | ||
#[serde(rename_all = "camelCase")] | ||
pub struct Game { | ||
config: GameConfig, | ||
players: Vec<String>, | ||
} | ||
|
||
impl Game { | ||
pub fn empty(config: GameConfig) -> Self { | ||
Self { | ||
config, | ||
players: Vec::new(), | ||
} | ||
} | ||
|
||
/// Adds the player into the game. | ||
/// | ||
/// # Panics | ||
/// | ||
/// May panic if the player is already part of the game. | ||
pub fn join(&mut self, player: &str) -> Result<(), JoinError> { | ||
if self.config.max_players as usize <= self.players.len() { | ||
return Err(JoinError::GameIsFull); | ||
} | ||
|
||
debug_assert!(self.players.iter().all(|p| p != player)); | ||
self.players.push(player.into()); | ||
Ok(()) | ||
} | ||
} | ||
|
||
pub enum JoinError { | ||
GameIsFull, | ||
} | ||
|
||
#[derive(Serialize, Deserialize)] | ||
#[serde(rename_all = "camelCase")] | ||
pub struct GameConfig { | ||
name: String, | ||
max_players: u8, | ||
} | ||
|
||
impl GameConfig { | ||
// TODO | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
use actix_web::{get, post, web, App, HttpResponse, HttpServer, Responder}; | ||
use game::{Game, GameConfig, Games}; | ||
use user::Users; | ||
|
||
mod game; | ||
mod user; | ||
|
||
#[actix_web::main] | ||
async fn main() -> std::io::Result<()> { | ||
let users_data = web::Data::new(Users::default()); | ||
let games_data = web::Data::new(Games::default()); | ||
|
||
HttpServer::new(move || { | ||
let json_cfg = web::JsonConfig::default() | ||
// TODO a constant | ||
.limit(10 * 1024) | ||
.content_type_required(true); | ||
// TODO | ||
//.content_type(|mime| mime == mime::APPLICATION_JSON); | ||
|
||
let users_scope = web::scope("/users"); | ||
let games_scope = web::scope("/games") | ||
.app_data(games_data.clone()) | ||
.service(list_games) | ||
.service(create_game); | ||
|
||
App::new() | ||
.app_data(users_data.clone()) | ||
.service(users_scope) | ||
.service(games_scope) | ||
}) | ||
.bind(("127.0.0.1", 8080))? | ||
.run() | ||
.await | ||
} | ||
|
||
// TODO sing-up / sign-in request | ||
// TODO validate JWT in every other request | ||
// TODO join game | ||
// TODO leave game game | ||
// TODO remove game if everybody abandons it | ||
// TODO start game (remove it and create rendezvous) | ||
|
||
#[get("/")] | ||
async fn list_games(games: web::Data<Games>) -> impl Responder { | ||
HttpResponse::Ok().json(games.inner().games()) | ||
} | ||
|
||
#[post("/")] | ||
async fn create_game(games: web::Data<Games>, game: web::Json<GameConfig>) -> impl Responder { | ||
// TODO require user ID header | ||
// TODO disconnect user from other games (including this one -> re-connect) | ||
// TODO place the user into the game | ||
|
||
// TODO validate game parameters | ||
// * 2 <= max_players <= 4 | ||
games.inner().new_game(game.0); | ||
HttpResponse::Ok().json(()) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
use std::{ | ||
collections::HashMap, | ||
sync::{Mutex, MutexGuard}, | ||
}; | ||
|
||
use serde::{Deserialize, Serialize}; | ||
|
||
#[derive(Default)] | ||
pub struct Users(Mutex<UsersInner>); | ||
|
||
impl Users { | ||
pub fn inner(&self) -> MutexGuard<'_, UsersInner> { | ||
self.0.lock().unwrap() | ||
} | ||
} | ||
|
||
#[derive(Default)] | ||
pub struct UsersInner { | ||
users: HashMap<String, User>, | ||
} | ||
|
||
impl UsersInner { | ||
pub fn get(&self, id: &str) -> Option<&User> { | ||
self.users.get(id) | ||
} | ||
} | ||
|
||
#[derive(Serialize, Deserialize)] | ||
#[serde(rename_all = "camelCase")] | ||
pub struct User { | ||
name: String, | ||
} |