Initial Commit
This commit is contained in:
commit
a7a2d5e9c5
10 changed files with 1636 additions and 0 deletions
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
target
|
1426
Cargo.lock
generated
Normal file
1426
Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load diff
2
Cargo.toml
Normal file
2
Cargo.toml
Normal file
|
@ -0,0 +1,2 @@
|
|||
[workspace]
|
||||
members = ["lobby"]
|
18
lobby/Cargo.toml
Normal file
18
lobby/Cargo.toml
Normal file
|
@ -0,0 +1,18 @@
|
|||
[package]
|
||||
name = "mad-lobby-service"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
axum = { version = "0.8" }
|
||||
juniper = "0.16"
|
||||
tokio = { version = "1.20", features = ["macros", "net", "rt-multi-thread", "time"] }
|
||||
tokio-stream = "0.1"
|
||||
tokio-tungstenite = "0.26"
|
||||
anyhow = "1.0"
|
||||
juniper_axum = "0.2"
|
||||
rand = "0.9"
|
||||
jwt = "0.16"
|
||||
hmac = "0.12"
|
||||
sha2 = "0.10"
|
||||
uuid = { version = "1.16", features = ["v4"] }
|
0
lobby/LICENSE
Normal file
0
lobby/LICENSE
Normal file
0
lobby/src/client/mod.rs
Normal file
0
lobby/src/client/mod.rs
Normal file
61
lobby/src/main.rs
Normal file
61
lobby/src/main.rs
Normal file
|
@ -0,0 +1,61 @@
|
|||
pub mod client;
|
||||
pub mod server;
|
||||
|
||||
use std::{net::SocketAddr, sync::Arc};
|
||||
|
||||
use axum::{
|
||||
Extension, Router,
|
||||
response::Html,
|
||||
routing::{MethodFilter, get, on},
|
||||
};
|
||||
use juniper::{graphql_object, EmptyMutation, EmptySubscription, RootNode};
|
||||
use juniper_axum::{graphiql, graphql, playground};
|
||||
use tokio::net::TcpListener;
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct Query;
|
||||
|
||||
#[graphql_object]
|
||||
impl Query {
|
||||
/// Adds two `a` and `b` numbers.
|
||||
fn add(a: i32, b: i32) -> i32 {
|
||||
a + b
|
||||
}
|
||||
}
|
||||
|
||||
type Schema = RootNode<'static, Query, EmptyMutation, EmptySubscription>;
|
||||
|
||||
async fn homepage() -> Html<&'static str> {
|
||||
"<html><h1>juniper_axum/simple example</h1>\
|
||||
<div>visit <a href=\"/graphiql\">GraphiQL</a></div>\
|
||||
<div>visit <a href=\"/playground\">GraphQL Playground</a></div>\
|
||||
</html>"
|
||||
.into()
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
|
||||
let schema = Schema::new(Query, EmptyMutation::new(), EmptySubscription::new());
|
||||
|
||||
let app = Router::new()
|
||||
.route(
|
||||
"/graphql",
|
||||
on(
|
||||
MethodFilter::GET.or(MethodFilter::POST),
|
||||
graphql::<Arc<Schema>>,
|
||||
),
|
||||
)
|
||||
.route("/graphiql", get(graphiql("/graphql", "/subscriptions")))
|
||||
.route("/playground", get(playground("/graphql", "/subscriptions")))
|
||||
.route("/", get(homepage))
|
||||
.layer(Extension(Arc::new(schema)));
|
||||
|
||||
let addr = SocketAddr::from(([127, 0, 0, 1], 8080));
|
||||
let listener = TcpListener::bind(addr)
|
||||
.await
|
||||
.unwrap_or_else(|e| panic!("failed to listen on {addr}: {e}"));
|
||||
axum::serve(listener, app)
|
||||
.await
|
||||
.unwrap_or_else(|e| panic!("failed to run `axum::serve`: {e}"));
|
||||
}
|
31
lobby/src/server/gql_schema.rs
Normal file
31
lobby/src/server/gql_schema.rs
Normal file
|
@ -0,0 +1,31 @@
|
|||
use juniper::graphql_object;
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* Roots */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct Query;
|
||||
|
||||
#[graphql_object]
|
||||
impl Query {
|
||||
fn lobbies(&self) -> String {
|
||||
"".to_string() // TODO
|
||||
}
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* Objects */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct QueryLobby {
|
||||
id: String
|
||||
}
|
||||
|
||||
#[graphql_object]
|
||||
impl QueryLobby {
|
||||
fn id(&self) -> String {
|
||||
self.id.clone()
|
||||
}
|
||||
}
|
95
lobby/src/server/logic.rs
Normal file
95
lobby/src/server/logic.rs
Normal file
|
@ -0,0 +1,95 @@
|
|||
use std::{
|
||||
collections::BTreeMap,
|
||||
time::{SystemTime, UNIX_EPOCH},
|
||||
};
|
||||
|
||||
use hmac::{Hmac, Mac};
|
||||
use rand::RngCore;
|
||||
use sha2::Sha256;
|
||||
|
||||
fn unix_time() -> u64 {
|
||||
SystemTime::now()
|
||||
.duration_since(UNIX_EPOCH)
|
||||
.unwrap()
|
||||
.as_secs()
|
||||
}
|
||||
|
||||
pub enum LobbyStatus {
|
||||
Alive,
|
||||
Inactive,
|
||||
}
|
||||
|
||||
pub struct Lobby {
|
||||
last_heartbeat: u64,
|
||||
status: LobbyStatus,
|
||||
}
|
||||
|
||||
impl Lobby {
|
||||
fn send_heartbeat(&mut self) {
|
||||
self.last_heartbeat = unix_time();
|
||||
}
|
||||
}
|
||||
|
||||
pub struct LobbyManager {
|
||||
heartbeat_timeout: u64,
|
||||
server_key: Hmac<Sha256>,
|
||||
lobbies: BTreeMap<String, Lobby>,
|
||||
}
|
||||
|
||||
impl LobbyManager {
|
||||
fn new() -> Self {
|
||||
let mut server_key_token: [u8; 64] = [0; 64];
|
||||
{
|
||||
rand::rng().fill_bytes(&mut server_key_token);
|
||||
}
|
||||
|
||||
// TODO: Handle this expect better
|
||||
let server_key: Hmac<Sha256> = Hmac::new_from_slice(&server_key_token).expect("bad token?");
|
||||
|
||||
Self {
|
||||
heartbeat_timeout: 30,
|
||||
server_key,
|
||||
lobbies: BTreeMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
fn create_lobby(&mut self) {
|
||||
let lobby = Lobby {
|
||||
last_heartbeat: unix_time(),
|
||||
status: LobbyStatus::Alive,
|
||||
};
|
||||
|
||||
self.lobbies.insert(uuid::Uuid::new_v4().to_string(), lobby);
|
||||
}
|
||||
|
||||
fn tick(&mut self) {
|
||||
for lobby in &mut self.lobbies {
|
||||
if lobby.1.last_heartbeat > unix_time() + self.heartbeat_timeout {
|
||||
match lobby.1.status {
|
||||
LobbyStatus::Alive => lobby.1.status = LobbyStatus::Inactive,
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn cull_lobbies(&mut self) -> Result<usize, ()> {
|
||||
let mut key_list: Vec<String> = Vec::new();
|
||||
|
||||
{
|
||||
for lobby in &self.lobbies {
|
||||
match lobby.1.status {
|
||||
LobbyStatus::Inactive => key_list.push(lobby.0.clone()),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
{
|
||||
for key in &key_list {
|
||||
self.lobbies.remove(key);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(key_list.len())
|
||||
}
|
||||
}
|
2
lobby/src/server/mod.rs
Normal file
2
lobby/src/server/mod.rs
Normal file
|
@ -0,0 +1,2 @@
|
|||
mod gql_schema;
|
||||
mod logic;
|
Loading…
Add table
Reference in a new issue