* Add an inbox/ outbox system to server components * Define a data flow from Request -> computation -> Update * Create simple handlers to call server or client code for requestswip/yesman
parent
5dab336049
commit
effbdeed66
@ -0,0 +1,61 @@ |
|||||||
|
//! Logging specifics
|
||||||
|
|
||||||
|
const BANNER: &'static str = " |
||||||
|
██████╗ ███████╗████████╗ ███╗ ██╗ ██████╗ ██████╗ ███████╗ |
||||||
|
██╔══██╗██╔════╝╚══██╔══╝ ████╗ ██║██╔═══██╗██╔══██╗██╔════╝ |
||||||
|
██████╔╝███████╗ ██║ ██╔██╗ ██║██║ ██║██║ ██║█████╗
|
||||||
|
██╔══██╗╚════██║ ██║ ██║╚██╗██║██║ ██║██║ ██║██╔══╝
|
||||||
|
██║ ██║███████║ ██║ ██║ ╚████║╚██████╔╝██████╔╝███████╗ |
||||||
|
╚═╝ ╚═╝╚══════╝ ╚═╝ ╚═╝ ╚═══╝ ╚═════╝ ╚═════╝ ╚══════╝"; |
||||||
|
|
||||||
|
use systemstat::{Platform, System}; |
||||||
|
use tracing_subscriber::{filter::LevelFilter, fmt, EnvFilter}; |
||||||
|
|
||||||
|
#[cfg(not(target_os = "windows"))] |
||||||
|
const PLATFORM_DISCLAIMER: &'static str = "Platform: Unspecified *nix-like"; |
||||||
|
#[cfg(target_os = "windows")] |
||||||
|
static PLATFORM_DISCLAIMER: &'static str = |
||||||
|
"WARNING: Windows server hosts are not officially supported!"; |
||||||
|
|
||||||
|
pub(crate) fn initialise() { |
||||||
|
let filter = EnvFilter::try_from_env("RST_LOG") |
||||||
|
.unwrap_or_default() |
||||||
|
.add_directive(LevelFilter::DEBUG.into()) |
||||||
|
.add_directive("async_std=error".parse().unwrap()) |
||||||
|
.add_directive("gfx_device_gl=error".parse().unwrap()) |
||||||
|
.add_directive("ggez=error".parse().unwrap()) |
||||||
|
.add_directive("selectors=error".parse().unwrap()) |
||||||
|
.add_directive("gilrs=error".parse().unwrap()) |
||||||
|
.add_directive("mio=error".parse().unwrap()); |
||||||
|
|
||||||
|
// Initialise the logger
|
||||||
|
fmt().with_env_filter(filter).init(); |
||||||
|
info!("Initialising..."); |
||||||
|
info!("{}", BANNER); |
||||||
|
info!("{}", PLATFORM_DISCLAIMER); |
||||||
|
info!("Available cores: {}", num_cpus::get()); |
||||||
|
info!("Available RAM: {}", mem()); |
||||||
|
info!("Version: {}", crate::constants::VERSION); |
||||||
|
} |
||||||
|
|
||||||
|
fn mem() -> String { |
||||||
|
let sys = System::new(); |
||||||
|
let mem = sys.memory().unwrap(); |
||||||
|
format!( |
||||||
|
"{} / {}", |
||||||
|
mem.free.to_string_as(true), |
||||||
|
mem.total.to_string_as(true) |
||||||
|
) |
||||||
|
} |
||||||
|
|
||||||
|
#[macro_export] |
||||||
|
macro_rules! fatal { |
||||||
|
() => { |
||||||
|
error!("Unknown failure!"); |
||||||
|
std::process::exit(2) |
||||||
|
}; |
||||||
|
($($arg:tt)*) => ({ |
||||||
|
error!($($arg)*); |
||||||
|
std::process::exit(2) |
||||||
|
}) |
||||||
|
} |
@ -0,0 +1 @@ |
|||||||
|
//! Client networking endpoint logic
|
@ -0,0 +1,70 @@ |
|||||||
|
#![allow(unused)] |
||||||
|
|
||||||
|
use super::ClientState; |
||||||
|
use async_trait::async_trait; |
||||||
|
use chrono::{DateTime, Utc}; |
||||||
|
use rst_core::{ |
||||||
|
wire::{ |
||||||
|
game::Action, AuthErr, Lobby, LobbyErr, LobbyId, LobbyUpdate, MatchErr, MatchId, RegErr, |
||||||
|
UpdateState, User, UserId, |
||||||
|
}, |
||||||
|
GameIf, Match, |
||||||
|
}; |
||||||
|
use std::sync::Arc; |
||||||
|
|
||||||
|
#[async_trait] |
||||||
|
impl GameIf for ClientState { |
||||||
|
async fn register(self: Arc<Self>, name: String, pw: String) -> Result<UserId, RegErr> { |
||||||
|
todo!() |
||||||
|
} |
||||||
|
|
||||||
|
async fn login(self: Arc<Self>, name: String, pw: String) -> Result<User, AuthErr> { |
||||||
|
todo!() |
||||||
|
} |
||||||
|
|
||||||
|
async fn logout(self: Arc<Self>, user: User) -> Result<(), AuthErr> { |
||||||
|
todo!() |
||||||
|
} |
||||||
|
|
||||||
|
async fn anonymous(self: Arc<Self>, name: String) -> Result<User, AuthErr> { |
||||||
|
todo!() |
||||||
|
} |
||||||
|
|
||||||
|
async fn join(self: Arc<Self>, user: User, lobby: LobbyId) -> Result<Lobby, LobbyErr> { |
||||||
|
todo!() |
||||||
|
} |
||||||
|
|
||||||
|
async fn leave(self: Arc<Self>, user: User, lobby: LobbyId) -> Result<(), LobbyErr> { |
||||||
|
todo!() |
||||||
|
} |
||||||
|
|
||||||
|
async fn ready( |
||||||
|
self: Arc<Self>, |
||||||
|
user: User, |
||||||
|
lobby: LobbyId, |
||||||
|
ready: bool, |
||||||
|
) -> Result<LobbyUpdate, LobbyErr> { |
||||||
|
todo!() |
||||||
|
} |
||||||
|
|
||||||
|
async fn start_req( |
||||||
|
self: Arc<Self>, |
||||||
|
user: UserId, |
||||||
|
lobby: LobbyId, |
||||||
|
) -> Result<DateTime<Utc>, LobbyErr> { |
||||||
|
todo!() |
||||||
|
} |
||||||
|
|
||||||
|
async fn perform_action( |
||||||
|
self: Arc<Self>, |
||||||
|
user: User, |
||||||
|
mtch: MatchId, |
||||||
|
act: Action, |
||||||
|
) -> UpdateState { |
||||||
|
todo!() |
||||||
|
} |
||||||
|
|
||||||
|
async fn leave_match(self: Arc<Self>, user: User, mtch: MatchId) -> Result<(), MatchErr> { |
||||||
|
todo!() |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,10 @@ |
|||||||
|
use rst_core::map::{Map, MapNode}; |
||||||
|
|
||||||
|
/// Client map state wrapper
|
||||||
|
///
|
||||||
|
/// The map state is calculated by the server and updates are streamed
|
||||||
|
/// to all clients in a [Match](rst_core::Match).
|
||||||
|
pub struct MapState { |
||||||
|
inner: Map, |
||||||
|
} |
||||||
|
|
@ -0,0 +1,51 @@ |
|||||||
|
pub use crate::wire::{AuthErr, LobbyErr, MatchErr, RegErr}; |
||||||
|
use serde::{Deserialize, Serialize}; |
||||||
|
use std::fmt::{self, Display, Formatter}; |
||||||
|
|
||||||
|
/// A wrapper around the different RST node errors that can occur
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)] |
||||||
|
pub enum Error { |
||||||
|
/// Failure in the registration process
|
||||||
|
Register(RegErr), |
||||||
|
/// Failure in the authentication process
|
||||||
|
Auth(AuthErr), |
||||||
|
/// Failure handling a lobby
|
||||||
|
Lobby(LobbyErr), |
||||||
|
/// Failure in a match setting
|
||||||
|
Match(MatchErr), |
||||||
|
} |
||||||
|
|
||||||
|
impl std::error::Error for Error {} |
||||||
|
|
||||||
|
impl Display for Error { |
||||||
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result { |
||||||
|
write!(f, "An RST error") |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// A RST Node specific error
|
||||||
|
pub type Result<T> = std::result::Result<T, Error>; |
||||||
|
|
||||||
|
impl From<RegErr> for Error { |
||||||
|
fn from(e: RegErr) -> Self { |
||||||
|
Error::Register(e) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl From<AuthErr> for Error { |
||||||
|
fn from(e: AuthErr) -> Self { |
||||||
|
Error::Auth(e) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl From<LobbyErr> for Error { |
||||||
|
fn from(e: LobbyErr) -> Self { |
||||||
|
Error::Lobby(e) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl From<MatchErr> for Error { |
||||||
|
fn from(e: MatchErr) -> Self { |
||||||
|
Error::Match(e) |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,78 @@ |
|||||||
|
use crate::wire::{ |
||||||
|
game::{Action, Update}, |
||||||
|
Lobby, |
||||||
|
}; |
||||||
|
|
||||||
|
use async_std::sync::RwLock; |
||||||
|
use std::collections::VecDeque; |
||||||
|
|
||||||
|
pub enum ClientUpdate { |
||||||
|
/// Change to the lobby state
|
||||||
|
Lobby(Lobby), |
||||||
|
/// A game simulation update
|
||||||
|
GameUpdate(Update), |
||||||
|
} |
||||||
|
|
||||||
|
impl<'l> From<&'l Lobby> for ClientUpdate { |
||||||
|
fn from(l: &'l Lobby) -> Self { |
||||||
|
ClientUpdate::Lobby(l.clone()) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl<'l> From<&'l Update> for ClientUpdate { |
||||||
|
fn from(u: &'l Update) -> Self { |
||||||
|
ClientUpdate::GameUpdate(u.clone()) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// A message out buffer that can be attached to any server entity
|
||||||
|
pub struct Outbox { |
||||||
|
queue: RwLock<VecDeque<ClientUpdate>>, |
||||||
|
} |
||||||
|
|
||||||
|
impl Outbox { |
||||||
|
pub fn new() -> Self { |
||||||
|
Self { |
||||||
|
queue: Default::default(), |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// Queue a new item to send out
|
||||||
|
pub async fn queue(&self, update: impl Into<ClientUpdate>) { |
||||||
|
let mut q = self.queue.write().await; |
||||||
|
q.push_back(update.into()); |
||||||
|
} |
||||||
|
|
||||||
|
/// Run a closure for all queued items
|
||||||
|
pub async fn run_for<F: Fn(&ClientUpdate)>(&self, handle: F) { |
||||||
|
let q = self.queue.read().await; |
||||||
|
q.iter().for_each(|item| handle(item)); |
||||||
|
} |
||||||
|
|
||||||
|
/// Clear the outbox for the next update interval
|
||||||
|
pub async fn clear(&self) { |
||||||
|
self.queue.write().await.clear(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
pub struct Inbox { |
||||||
|
queue: RwLock<VecDeque<Action>>, |
||||||
|
} |
||||||
|
|
||||||
|
impl Inbox { |
||||||
|
pub fn new() -> Self { |
||||||
|
Self { |
||||||
|
queue: Default::default(), |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// Queue a new item to send out
|
||||||
|
pub async fn queue(&self, update: impl Into<Action>) { |
||||||
|
let mut q = self.queue.write().await; |
||||||
|
q.push_back(update.into()); |
||||||
|
} |
||||||
|
|
||||||
|
pub async fn pop(&self) -> Option<Action> { |
||||||
|
self.queue.write().await.pop_front() |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,49 @@ |
|||||||
|
use crate::{server::Server, Id}; |
||||||
|
use async_std::{ |
||||||
|
net::UdpSocket, |
||||||
|
sync::{Arc, RwLock}, |
||||||
|
task, |
||||||
|
}; |
||||||
|
use std::{ |
||||||
|
collections::BTreeMap, |
||||||
|
net::SocketAddr, |
||||||
|
sync::atomic::{AtomicBool, Ordering}, |
||||||
|
}; |
||||||
|
|
||||||
|
pub struct Endpoint { |
||||||
|
running: AtomicBool, |
||||||
|
socket: UdpSocket, |
||||||
|
bind: String, |
||||||
|
clients: RwLock<BTreeMap<Id, Client>>, |
||||||
|
} |
||||||
|
|
||||||
|
pub struct Client {} |
||||||
|
|
||||||
|
impl Endpoint { |
||||||
|
pub async fn new(bind: &str) -> Arc<Self> { |
||||||
|
let socket = UdpSocket::bind(bind).await.unwrap(); |
||||||
|
Arc::new(Self { |
||||||
|
running: true.into(), |
||||||
|
socket, |
||||||
|
bind: bind.into(), |
||||||
|
clients: Default::default(), |
||||||
|
}) |
||||||
|
} |
||||||
|
|
||||||
|
/// Stop the endpoint
|
||||||
|
pub fn stop(self: &Arc<Self>) { |
||||||
|
self.running.store(false, Ordering::Relaxed); |
||||||
|
} |
||||||
|
|
||||||
|
pub async fn listen(self: &Arc<Self>, serv: Arc<Server>) { |
||||||
|
let mut buf = vec![0; 1024]; |
||||||
|
|
||||||
|
info!("Listening for connections on {}", self.bind); |
||||||
|
while self.running.load(Ordering::Relaxed) { |
||||||
|
let (int, peer) = self.socket.recv_from(&mut buf).await.unwrap(); |
||||||
|
if int > 1024 { |
||||||
|
warn!("Read a larger chunk than buffer?"); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,52 @@ |
|||||||
|
use crate::{ |
||||||
|
error::Error, |
||||||
|
wire::{ |
||||||
|
AuthErr, Lobby, LobbyErr, LobbyId, LobbyUpdate, MatchErr, RegErr, Response, UpdateState, |
||||||
|
User, UserId, |
||||||
|
}, |
||||||
|
}; |
||||||
|
use chrono::{DateTime, Utc}; |
||||||
|
|
||||||
|
pub fn register(r: Result<UserId, RegErr>) -> Response { |
||||||
|
Response::Register(r) |
||||||
|
} |
||||||
|
|
||||||
|
pub fn login(r: Result<User, AuthErr>) -> Response { |
||||||
|
Response::Login(r) |
||||||
|
} |
||||||
|
|
||||||
|
pub fn logout(r: Result<(), AuthErr>) -> Response { |
||||||
|
Response::Logout(r) |
||||||
|
} |
||||||
|
|
||||||
|
pub fn rooms(r: Vec<(String, LobbyId)>) -> Response { |
||||||
|
Response::Rooms(r) |
||||||
|
} |
||||||
|
|
||||||
|
pub fn join(r: Result<Lobby, LobbyErr>) -> Response { |
||||||
|
Response::Join(r) |
||||||
|
} |
||||||
|
|
||||||
|
pub fn leave(r: Result<(), LobbyErr>) -> Response { |
||||||
|
Response::Leave(r) |
||||||
|
} |
||||||
|
|
||||||
|
pub fn ready(r: LobbyUpdate) -> Response { |
||||||
|
Response::Ready(r) |
||||||
|
} |
||||||
|
|
||||||
|
pub fn start_req(r: DateTime<Utc>) -> Response { |
||||||
|
Response::StartReq(r) |
||||||
|
} |
||||||
|
|
||||||
|
pub fn game_update(r: UpdateState) -> Response { |
||||||
|
Response::GameUpdate(r) |
||||||
|
} |
||||||
|
|
||||||
|
pub fn leave_game(r: Result<(), MatchErr>) -> Response { |
||||||
|
Response::LeaveGame(r) |
||||||
|
} |
||||||
|
|
||||||
|
pub fn invalid() -> Response { |
||||||
|
Response::Invalid |
||||||
|
} |
@ -0,0 +1,17 @@ |
|||||||
|
use crate::{ |
||||||
|
net::Client, |
||||||
|
server::Server, |
||||||
|
wire::{Request, Response}, |
||||||
|
}; |
||||||
|
use async_std::sync::{Receiver, Sender}; |
||||||
|
|
||||||
|
pub struct Handler { |
||||||
|
tx: Sender<Response>, |
||||||
|
} |
||||||
|
|
||||||
|
impl Handler { |
||||||
|
/// Start a message handler with a handle to send a reply back to the clients
|
||||||
|
pub fn start(req: Request) { |
||||||
|
|
||||||
|
} |
||||||
|
} |
@ -0,0 +1,20 @@ |
|||||||
|
mod endpoint; |
||||||
|
pub use endpoint::*; |
||||||
|
|
||||||
|
mod gen; |
||||||
|
pub use gen::*; |
||||||
|
|
||||||
|
mod handler; |
||||||
|
pub use handler::*; |
||||||
|
|
||||||
|
mod parser; |
||||||
|
pub use parser::*; |
||||||
|
|
||||||
|
// #[async_std::test]
|
||||||
|
// async fn user_connection() {
|
||||||
|
// let serv = Server::new();
|
||||||
|
// let ep = Endpoint::new("localhost:9999").await;
|
||||||
|
// task::spawn(async move { ep.listen(serv).await });
|
||||||
|
|
||||||
|
// // Create a fake client here
|
||||||
|
// }
|
@ -0,0 +1,17 @@ |
|||||||
|
use crate::{ |
||||||
|
error::Error, |
||||||
|
wire::{Request, Response}, |
||||||
|
GameIf, |
||||||
|
}; |
||||||
|
use std::sync::Arc; |
||||||
|
|
||||||
|
/// Parse a request and call a game interface function for it
|
||||||
|
pub async fn request(req: Request, game: Arc<impl GameIf>) -> Response { |
||||||
|
use Request::*; |
||||||
|
match req { |
||||||
|
Register(name, pw) => super::register(game.register(name, pw).await), |
||||||
|
Login(name, pw_hash) => super::login(game.login(name, pw_hash).await), |
||||||
|
Logout(user) => super::logout(game.logout(user).await), |
||||||
|
_ => super::invalid(), |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,5 @@ |
|||||||
|
mod action; |
||||||
|
pub use action::*; |
||||||
|
|
||||||
|
mod update; |
||||||
|
pub use update::*; |
@ -0,0 +1,25 @@ |
|||||||
|
//! This module implements the client-server game protocol
|
||||||
|
|
||||||
|
use super::{Request, Response}; |
||||||
|
|
||||||
|
pub enum NetErr { |
||||||
|
Refused, |
||||||
|
Dropped, |
||||||
|
Timeout, |
||||||
|
BadData, |
||||||
|
} |
||||||
|
|
||||||
|
/// Use this function to send a request to a partical remote
|
||||||
|
///
|
||||||
|
/// The function makes sure that you get a valid response back, but
|
||||||
|
/// does not yet ensure that this response is correct for the request
|
||||||
|
/// in question.
|
||||||
|
pub fn request_to(r: Request, remote: String) -> Result<Response, NetErr> { |
||||||
|
todo!() |
||||||
|
} |
||||||
|
|
||||||
|
/// Use this function to send a response to a client
|
||||||
|
pub fn response_to(r: Response, client: String) -> Result<(), NetErr> { |
||||||
|
todo!() |
||||||
|
} |
||||||
|
|
@ -0,0 +1,41 @@ |
|||||||
|
use crate::wire::{Action, LobbyId, MatchId, Request, User}; |
||||||
|
|
||||||
|
pub fn register(name: String, pw: String) -> Request { |
||||||
|
Request::Register(name, pw) |
||||||
|
} |
||||||
|
c |
||||||
|
pub fn login(name: String, pw: String) -> Request { |
||||||
|
Request::Login(name, pw) |
||||||
|
} |
||||||
|
|
||||||
|
pub fn logout(user: User) -> Request { |
||||||
|
Request::Logout(user) |
||||||
|
} |
||||||
|
|
||||||
|
pub fn anonymous(name: String) -> Request { |
||||||
|
Request::Anonymous(name) |
||||||
|
} |
||||||
|
|
||||||
|
pub fn join(user: User, lid: LobbyId) -> Request { |
||||||
|
Request::Join(user, lid) |
||||||
|
} |
||||||
|
|
||||||
|
pub fn leave(user: User, lid: LobbyId) -> Request { |
||||||
|
Request::Leave(user, lid) |
||||||
|
} |
||||||
|
|
||||||
|
pub fn ready(user: User, lid: LobbyId, ready: bool) -> Request { |
||||||
|
Request::Ready(user, lid, ready) |
||||||
|
} |
||||||
|
|
||||||
|
pub fn start_req(user: User, lid: LobbyId) -> Request { |
||||||
|
Request::StartReq(user, lid) |
||||||
|
} |
||||||
|
|
||||||
|
pub fn game_action(user: User, mid: MatchId, act: Action) -> Request { |
||||||
|
Request::GameAction(user, mid, act) |
||||||
|
} |
||||||
|
|
||||||
|
pub fn leave_game(user: User, mid: MatchId) -> Request { |
||||||
|
Request::LeaveGame(user, mid) |
||||||
|
} |
@ -1,5 +1,6 @@ |
|||||||
|
|
||||||
|
|
||||||
pub const NAME: &'static str = "RST Node"; |
pub const NAME: &'static str = "RST Node"; |
||||||
pub const VERSION: &'static str = env!("CARGO_PKG_VERSION"); |
pub const VERSION: &'static str = env!("CARGO_PKG_VERSION"); |
||||||
pub const AUTHORS: &'static str = env!("CARGO_PKG_AUTHORS"); |
pub const AUTHORS: &'static str = env!("CARGO_PKG_AUTHORS"); |
||||||
|
|
||||||
|
pub const DEFAULT_BIND: &'static str = "0.0.0.0"; |
||||||
|
pub const DEFAULT_PORT: u16 = 10022; |
||||||
|
@ -1,15 +1,22 @@ |
|||||||
|
//! RST Node game server
|
||||||
|
|
||||||
|
// Remove the warning spam
|
||||||
|
#![allow(warnings)] |
||||||
|
|
||||||
#[macro_use] |
#[macro_use] |
||||||
extern crate tracing; |
extern crate tracing; |
||||||
|
|
||||||
mod constants; |
pub(crate) mod constants; |
||||||
mod log; |
pub(crate) mod log; |
||||||
mod net; |
|
||||||
use net::ServerEndpoint; |
use rst_core::net::Endpoint; |
||||||
|
|
||||||
#[async_std::main] |
#[async_std::main] |
||||||
async fn main() { |
async fn main() { |
||||||
log::initialise(); |
log::initialise(); |
||||||
|
|
||||||
let serv = ServerEndpoint::new("0.0.0.0:10022").await; |
let addr = format!("{}:{}", constants::DEFAULT_BIND, constants::DEFAULT_PORT); |
||||||
serv.listen().await; |
let serv = Endpoint::new(addr.as_str()).await; |
||||||
|
// serv.listen().await;
|
||||||
|
// net::run().await;
|
||||||
} |
} |
||||||
|
@ -1,43 +0,0 @@ |
|||||||
#![allow(unused)] |
|
||||||
|
|
||||||
mod parser; |
|
||||||
|
|
||||||
use async_std::{ |
|
||||||
net::UdpSocket, |
|
||||||
sync::{Arc, RwLock}, |
|
||||||
task, |
|
||||||
}; |
|
||||||
use rst_core::Id; |
|
||||||
use std::{collections::BTreeMap, net::SocketAddr}; |
|
||||||
|
|
||||||
pub struct ServerEndpoint { |
|
||||||
socket: UdpSocket, |
|
||||||
bind: String, |
|
||||||
clients: RwLock<BTreeMap<Id, Client>>, |
|
||||||
} |
|
||||||
|
|
||||||
impl ServerEndpoint { |
|
||||||
pub async fn new(bind: &str) -> Arc<Self> { |
|
||||||
let socket = UdpSocket::bind(bind).await.unwrap(); |
|
||||||
Arc::new(Self { |
|
||||||
socket, |
|
||||||
bind: bind.into(), |
|
||||||
clients: Default::default(), |
|
||||||
}) |
|
||||||
} |
|
||||||
|
|
||||||
pub async fn listen(self: &Arc<Self>) { |
|
||||||
let mut buf = vec![0; 1024]; |
|
||||||
|
|
||||||
info!("Listening for connections on {}", self.bind); |
|
||||||
|
|
||||||
loop { |
|
||||||
let (_, peer) = self.socket.recv_from(&mut buf).await.unwrap(); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
pub struct Client { |
|
||||||
addr: SocketAddr, |
|
||||||
} |
|
||||||
|
|
@ -1,9 +0,0 @@ |
|||||||
use rst_core::wire::Request; |
|
||||||
|
|
||||||
pub async fn request(req: Request) { |
|
||||||
use Request::*; |
|
||||||
match req { |
|
||||||
Register(name, pw) => {}, |
|
||||||
_ => todo!(), |
|
||||||
} |
|
||||||
} |
|
@ -0,0 +1,153 @@ |
|||||||
|
//! Game server state handler
|
||||||
|
//!
|
||||||
|
//! A server can host many lobbies at the same time. It listens for
|
||||||
|
//! connections according to a address given to the initialiser.
|
||||||
|
|
||||||
|
use async_std::sync::{Arc, Mutex}; |
||||||
|
use async_trait::async_trait; |
||||||
|
use chrono::{DateTime, Utc}; |
||||||
|
use rst_core::{ |
||||||
|
data::Player, |
||||||
|
lobby::LobbyList, |
||||||
|
map::Map, |
||||||
|
users::UserStore, |
||||||
|
wire::{ |
||||||
|
Action, AuthErr, Lobby, LobbyErr, LobbyId, LobbyUpdate, MatchErr, MatchId, RegErr, |
||||||
|
Response, UpdateState, User, UserId, |
||||||
|
}, |
||||||
|
GameIf, Match, |
||||||
|
}; |
||||||
|
use std::{collections::BTreeMap, path::Path}; |
||||||
|
|
||||||
|
/// A convenience result wrapper for server actions
|
||||||
|
pub type ServerResult<T> = Result<T, ServerErr>; |
||||||
|
pub enum ServerErr { |
||||||
|
/// The requested directory is corrupted
|
||||||
|
NoSuchDir, |
||||||
|
/// Corrupted game state
|
||||||
|
Corrupted, |
||||||
|
/// No such match found
|
||||||
|
NoSuchMatch, |
||||||
|
} |
||||||
|
|
||||||
|
/// The game's server backend
|
||||||
|
pub struct Server { |
||||||
|
matches: BTreeMap<MatchId, Mutex<Match>>, |
||||||
|
users: UserStore, |
||||||
|
lobbies: LobbyList, |
||||||
|
} |
||||||
|
|
||||||
|
impl Server { |
||||||
|
/// Create a new game server
|
||||||
|
pub(crate) fn new() -> Arc<Self> { |
||||||
|
Arc::new(Self { |
||||||
|
matches: Default::default(), |
||||||
|
users: UserStore::new(), |
||||||
|
lobbies: LobbyList::new(), |
||||||
|
}) |
||||||
|
} |
||||||
|
|
||||||
|
/// Open the state dir of a game server
|
||||||
|
pub async fn open(self: Arc<Self>, path: &Path) -> ServerResult<()> { |
||||||
|
Ok(()) |
||||||
|
} |
||||||
|
|
||||||
|
/// Stop accepting new game connections and shutdown gracefully
|
||||||
|
///
|
||||||
|
/// Returns the number of matches still going on.
|
||||||
|
pub async fn shutdown(self: Arc<Self>) -> ServerResult<u64> { |
||||||
|
Ok(0) |
||||||
|
} |
||||||
|
|
||||||
|
/// Save and close the statedir and kicking all players
|
||||||
|
///
|
||||||
|
/// Returns the number of players that were kicked off the server
|
||||||
|
/// prematurely.
|
||||||
|
pub async fn kill(self: Arc<Self>) -> ServerResult<u64> { |
||||||
|
Ok(0) |
||||||
|
} |
||||||
|
|
||||||
|
// pub async fn update_map<F>(self: Arc<Self>, id: MatchId, cb: F) -> ServerResult<Response>
|
||||||
|
// where
|
||||||
|
// F: Fn(&mut Map) -> ServerResult<Response>,
|
||||||
|
// {
|
||||||
|
// match self.matches.get(&id) {
|
||||||
|
// Some(ref m) => m.lock().await.map.update(cb),
|
||||||
|
// None => Err(ServerErr::NoSuchMatch),
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
pub async fn update_players<F>(self: Arc<Self>, id: MatchId, cb: F) -> ServerResult<Response> |
||||||
|
where |
||||||
|
F: Fn(&mut Vec<Player>) -> ServerResult<Response>, |
||||||
|
{ |
||||||
|
match self.matches.get(&id) { |
||||||
|
Some(ref mut m) => cb(&mut m.lock().await.players), |
||||||
|
None => Err(ServerErr::NoSuchMatch), |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
#[async_trait] |
||||||
|
impl GameIf for Server { |
||||||
|
async fn register(self: Arc<Self>, _name: String, _pw: String) -> Result<UserId, RegErr> { |
||||||
|
todo!() |
||||||
|
} |
||||||
|
|
||||||
|
async fn login(self: Arc<Self>, _name: String, _pw: String) -> Result<User, AuthErr> { |
||||||
|
todo!() |
||||||
|
} |
||||||
|
|
||||||
|
async fn logout(self: Arc<Self>, _user: User) -> Result<(), AuthErr> { |
||||||
|
todo!() |
||||||
|
} |
||||||
|
|
||||||
|
async fn anonymous(self: Arc<Self>, name: String) -> Result<User, AuthErr> { |
||||||
|
// let (_, auth) = self.users.add(name, None, true).await;
|
||||||
|
// Ok(auth)
|
||||||
|
todo!() |
||||||
|
} |
||||||
|
|
||||||
|
async fn join(self: Arc<Self>, user: User, lobby: LobbyId) -> Result<Lobby, LobbyErr> { |
||||||
|
let mu = self.users.get(&user).await?; |
||||||
|
self.lobbies.get_mut(lobby, |l| l.join(&mu)).await |
||||||
|
} |
||||||
|
|
||||||
|
async fn leave(self: Arc<Self>, user: User, lobby: LobbyId) -> Result<(), LobbyErr> { |
||||||
|
let mu = self.users.get(&user).await?; |
||||||
|
self.lobbies.get_mut(lobby, |l| l.leave(&mu)).await |
||||||
|
} |
||||||
|
|
||||||
|
async fn ready( |
||||||
|
self: Arc<Self>, |
||||||
|
user: User, |
||||||
|
lobby: LobbyId, |
||||||
|
ready: bool, |
||||||
|
) -> Result<LobbyUpdate, LobbyErr> { |
||||||
|
self.lobbies.get_mut(lobby, |l| l.ready(user, ready)).await |
||||||
|
} |
||||||
|
|
||||||
|
/// A start request was received
|
||||||
|
async fn start_req( |
||||||
|
self: Arc<Self>, |
||||||
|
user: UserId, |
||||||
|
lobby: LobbyId, |
||||||
|
) -> Result<DateTime<Utc>, LobbyErr> { |
||||||
|
self.lobbies.get_mut(lobby, |l| l.start(user)).await??; |
||||||
|
let _lob = self.lobbies.consume(lobby).await?; |
||||||
|
Ok(Utc::now()) |
||||||
|
} |
||||||
|
|
||||||
|
async fn perform_action( |
||||||
|
self: Arc<Self>, |
||||||
|
user: User, |
||||||
|
mtch: MatchId, |
||||||
|
act: Action, |
||||||
|
) -> UpdateState { |
||||||
|
unimplemented!() |
||||||
|
} |
||||||
|
|
||||||
|
async fn leave_match(self: Arc<Self>, _user: User, _mtch: MatchId) -> Result<(), MatchErr> { |
||||||
|
unimplemented!() |
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue