server test is working :)
This commit is contained in:
parent
9189d9cd88
commit
dd34317d14
9 changed files with 249 additions and 166 deletions
|
@ -6,7 +6,7 @@ pub struct LoginRequest {
|
|||
pub password: String,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
#[derive(Serialize, Clone)]
|
||||
pub struct LoginResult {
|
||||
pub success: bool,
|
||||
// TODO: Figure out what the user needs on successful login to reduce traffic
|
||||
|
|
|
@ -4,21 +4,30 @@ pub mod map_actions;
|
|||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::game::chat_message::ChatMessage;
|
||||
|
||||
#[derive(Serialize, Deserialize, Default, Debug)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum Request {
|
||||
#[default]
|
||||
Error,
|
||||
Login(login::LoginRequest)
|
||||
Login(login::LoginRequest),
|
||||
Message(ChatMessage),
|
||||
Quit,
|
||||
Shutdown
|
||||
}
|
||||
#[derive(Serialize)]
|
||||
#[derive(Serialize, Clone)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum Response {
|
||||
Error(RequestError),
|
||||
Login(login::LoginResult)
|
||||
Login(login::LoginResult),
|
||||
Message(ChatMessage),
|
||||
Quit { id: String },
|
||||
Shutdown,
|
||||
|
||||
}
|
||||
|
||||
#[derive(Serialize, Debug)]
|
||||
#[derive(Serialize, Debug, Clone)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum RequestError {
|
||||
InvalidRequest,
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use serde::{Deserialize, Serialize};
|
||||
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
pub struct ChatMessage {
|
||||
/// message text, `{item}` can be used to refer to items and such, where item is of the path such as `items/sword` or `spells/fireball`
|
||||
pub text: String,
|
||||
|
@ -17,7 +17,7 @@ pub struct ChatMessage {
|
|||
pub targets: Option<Vec<String>>
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
pub struct RollDialogOption {
|
||||
/// Field name
|
||||
pub name: String,
|
||||
|
|
31
src/lib.rs
31
src/lib.rs
|
@ -1,5 +1,34 @@
|
|||
use game::{Game, GameImpl};
|
||||
use tokio::sync::{broadcast, mpsc};
|
||||
|
||||
pub mod user;
|
||||
pub mod table;
|
||||
pub mod api;
|
||||
pub mod game;
|
||||
pub mod pathfinder2r_impl;
|
||||
pub mod pathfinder2r_impl;
|
||||
|
||||
pub struct GameServer {
|
||||
game: Game<pathfinder2r_impl::Pathfinder2rCharacterSheet, pathfinder2r_impl::entry::PEntry>
|
||||
}
|
||||
impl GameServer {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
game: Game::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn server_loop(mut self, mut msgs: mpsc::Receiver<(String, api::Request)>, mut broadcast: broadcast::Sender<api::Response>) {
|
||||
while let Some(req) = msgs.recv().await {
|
||||
// TODO: do stuff yo!
|
||||
let (id, req) = req;
|
||||
match req {
|
||||
api::Request::Error => {},
|
||||
api::Request::Login(_) => {},
|
||||
api::Request::Message(msg) => { _ = broadcast.send(api::Response::Message(msg)); },
|
||||
api::Request::Quit => { _ = broadcast.send(api::Response::Quit { id })},
|
||||
api::Request::Shutdown => todo!(),
|
||||
}
|
||||
}
|
||||
_ = broadcast.send(api::Response::Shutdown);
|
||||
}
|
||||
}
|
93
src/main.rs
93
src/main.rs
|
@ -1,61 +1,110 @@
|
|||
use axum::{
|
||||
extract::ws::{self,Message}, response, routing, Router
|
||||
};
|
||||
use futures_util::{stream::{SplitSink, SplitStream}, SinkExt, StreamExt};
|
||||
use open_tavern::api::{Request, RequestError, Response};
|
||||
use tokio::sync::{broadcast, mpsc};
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
let (bsend, _) = broadcast::channel(10);
|
||||
let (msend, mrecv) = mpsc::channel(50);
|
||||
let bsend2 = bsend.clone();
|
||||
let app = Router::new()
|
||||
.route("/", routing::get(root))
|
||||
.route("/socket.js", routing::get(socket))
|
||||
.route("/ws", routing::get(ws_handler))
|
||||
;
|
||||
.route("/ws", routing::get(move |w| ws_handler(w, msend, bsend2.clone().subscribe())));
|
||||
|
||||
let listener = tokio::net::TcpListener::bind("0.0.0.0:3001").await.unwrap();
|
||||
axum::serve(listener, app).await.unwrap();
|
||||
let game = open_tavern::GameServer::new();
|
||||
tokio::spawn(game.server_loop(mrecv, bsend));
|
||||
|
||||
axum::serve(listener, app).await.expect("axum server crashed, yaaaaay (unless i crashed him that yay)");
|
||||
}
|
||||
|
||||
async fn ws_handler(ws: ws::WebSocketUpgrade) -> impl axum::response::IntoResponse {
|
||||
ws.on_upgrade(handle_socket)
|
||||
async fn ws_handler(ws: ws::WebSocketUpgrade, msend: mpsc::Sender<(String, Request)>, brecv: broadcast::Receiver<Response>) -> impl axum::response::IntoResponse {
|
||||
ws.on_upgrade(|w| handle_socket(w, msend, brecv))
|
||||
}
|
||||
|
||||
async fn handle_socket(mut socket: ws::WebSocket) {
|
||||
let mut logged_in = false;
|
||||
println!("Got a new socket");
|
||||
async fn socket_receiver(mut recv: SplitStream<ws::WebSocket>, msend: mpsc::Sender<(String, Request)>, id: String) {
|
||||
while let Some(msg) = recv.next().await {
|
||||
if let Ok(msg) = msg {
|
||||
match msg {
|
||||
Message::Text(t) => {
|
||||
let req = serde_json::from_str::<Request>(&t).unwrap_or_default();
|
||||
println!("Got message: {:?}", t);
|
||||
let erred = msend.send((id.clone(), req)).await.is_err();
|
||||
if erred {
|
||||
break;
|
||||
}
|
||||
}
|
||||
ws::Message::Binary(_) => todo!(),
|
||||
ws::Message::Ping(_) => todo!(),
|
||||
ws::Message::Pong(_) => todo!(),
|
||||
ws::Message::Close(_) => {
|
||||
// dont care if we fail the send as we are quitting regardless
|
||||
_ = msend.send((id.clone(), open_tavern::api::Request::Quit)).await;
|
||||
break;
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn socket_sender(mut send: SplitSink<ws::WebSocket, ws::Message>, mut brecv: broadcast::Receiver<Response>) {
|
||||
while let Ok(msg) = brecv.recv().await {
|
||||
let err = send.send(ws::Message::Text(serde_json::to_string(&msg).unwrap())).await.is_err();
|
||||
if err {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn handle_socket(mut socket: ws::WebSocket, msend: mpsc::Sender<(String, Request)>, brecv: broadcast::Receiver<Response>) {
|
||||
let mut id: Option<String> = None;
|
||||
loop {
|
||||
if let Some(msg) = socket.recv().await {
|
||||
if let Ok(msg) = msg {
|
||||
let response: Message = match msg {
|
||||
match msg {
|
||||
Message::Text(t) => {
|
||||
let req = serde_json::from_str::<Request>(&t).unwrap_or_default();
|
||||
println!("Got message: {:?}", t);
|
||||
match req {
|
||||
Request::Error => Message::Text(serde_json::to_string(&Response::Error(RequestError::InvalidRequest)).unwrap()),
|
||||
Request::Login(r) => if !logged_in {
|
||||
if r.username == "rusty" {
|
||||
logged_in = true;
|
||||
Message::Text(serde_json::to_string(&Response::Login(open_tavern::api::login::LoginResult { success: true })).unwrap())
|
||||
}
|
||||
else {
|
||||
Message::Text(serde_json::to_string(&Response::Login(open_tavern::api::login::LoginResult { success: false })).unwrap())
|
||||
}
|
||||
} else {
|
||||
Message::Text(serde_json::to_string(&Response::Error(open_tavern::api::RequestError::AlreadyLoggedIn)).unwrap())
|
||||
// TODO: Actual signing in mechanism with multiple ids :)
|
||||
Request::Login(r) => if r.username == "rusty" {
|
||||
_ = socket.send(Message::Text(
|
||||
serde_json::to_string(&Response::Login(open_tavern::api::login::LoginResult { success: true })).unwrap()
|
||||
)).await;
|
||||
id = Some(String::from("rusty"));
|
||||
break;
|
||||
}
|
||||
else {
|
||||
_ = socket.send(Message::Text(
|
||||
serde_json::to_string(&Response::Login(open_tavern::api::login::LoginResult { success: false })).unwrap()
|
||||
)).await;
|
||||
},
|
||||
}
|
||||
_ => {
|
||||
_ = socket.send(Message::Text(serde_json::to_string(&Response::Error(RequestError::InvalidRequest)).unwrap())).await;
|
||||
},
|
||||
}
|
||||
}
|
||||
ws::Message::Binary(_) => todo!(),
|
||||
ws::Message::Ping(_) => todo!(),
|
||||
ws::Message::Pong(_) => todo!(),
|
||||
ws::Message::Close(_) => break,
|
||||
};
|
||||
socket.send(response).await.expect("failed sending to socket");
|
||||
}
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if let Some(id) = id {
|
||||
println!("Got id for socket: {}", &id);
|
||||
let (send, recv) = socket.split();
|
||||
tokio::spawn(socket_receiver(recv, msend, id));
|
||||
tokio::spawn(socket_sender(send, brecv));
|
||||
}
|
||||
println!("Done with so-cat");
|
||||
}
|
||||
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
use crate::game::{action::{ActionDefinition, ActionResult, GameEntry}, character_sheet::*, chat_message::{ChatMessage, RollDialogOption}};
|
||||
use serde::Serialize;
|
||||
use tavern_macros::CharacterSheet;
|
||||
|
||||
pub mod entry;
|
||||
use entry::{PEntry, Weapon};
|
||||
|
||||
#[derive(Default, CharacterSheet)]
|
||||
#[derive(Default, CharacterSheet, Serialize)]
|
||||
pub struct Pathfinder2rCharacterSheet {
|
||||
// Genral stuff
|
||||
#[Input("Name")]
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue