diff --git a/WORKLOG.md b/WORKLOG.md index fc20200d..20cb28d1 100644 --- a/WORKLOG.md +++ b/WORKLOG.md @@ -29,6 +29,13 @@ *$$$* +clicks buy + creates stripe order / fill 0x order + +server notified of payment + txs <- payment + + buy supporter pack account credited with features char sets diff --git a/client/src/socket.jsx b/client/src/socket.jsx index ada2fbba..427e320c 100644 --- a/client/src/socket.jsx +++ b/client/src/socket.jsx @@ -1,7 +1,7 @@ const toast = require('izitoast'); const cbor = require('borc'); -const SOCKET_URL = process.env.NODE_ENV === 'production' ? 'wss://mnml.gg/ws' : 'ws://localhost:40000'; +const SOCKET_URL = process.env.NODE_ENV === 'production' ? 'wss://mnml.gg/ws' : 'ws://localhost:40000/ws/'; function errorToast(err) { console.error(err); diff --git a/server/Cargo.toml b/server/Cargo.toml index 619095e4..4cfd6849 100644 --- a/server/Cargo.toml +++ b/server/Cargo.toml @@ -25,3 +25,9 @@ failure = "0.1" log = "0.4" fern = "0.5" +actix = "0.8.2" +actix-web = "1.0.0" +actix-web-actors = "1.0.0" +futures = "0.1" +bytes = "0.4" + diff --git a/server/src/main.rs b/server/src/main.rs index 3daa167b..eaffe954 100644 --- a/server/src/main.rs +++ b/server/src/main.rs @@ -9,6 +9,10 @@ extern crate postgres; extern crate r2d2; extern crate r2d2_postgres; +extern crate actix; +extern crate actix_web; +extern crate actix_web_actors; + extern crate serde; extern crate serde_cbor; #[macro_use] extern crate serde_derive; diff --git a/server/src/net.rs b/server/src/net.rs index 7c131084..07cb03fc 100644 --- a/server/src/net.rs +++ b/server/src/net.rs @@ -1,49 +1,122 @@ -use failure::{Error, err_msg}; -use tungstenite::Message; -use tungstenite::protocol::WebSocket; -use tungstenite::server::accept; -use tungstenite::Message::Binary; -use std::net::{TcpListener, TcpStream}; +use std::time::{Instant, Duration}; +use std::env; + use serde_cbor::{to_vec}; -use std::env; -use std::thread::{spawn, sleep}; -use std::time::{Instant, Duration}; +use actix::prelude::*; +use actix_web::{middleware, web, App, Error, HttpRequest, HttpResponse, HttpServer}; +use actix_web_actors::ws; use r2d2::{Pool}; use r2d2::{PooledConnection}; use r2d2_postgres::{TlsMode, PostgresConnectionManager}; -static DB_POOL_SIZE: u32 = 20; - -pub type Db = PooledConnection; - -use rpc::{Rpc}; +use rpc::{receive, RpcErrorResponse}; use warden::{warden}; -#[derive(Debug,Clone,Serialize,Deserialize)] -struct RpcErrorResponse { - err: String +pub type Db = PooledConnection; +type PgPool = Pool; + +const DB_POOL_SIZE: u32 = 20; +const HEARTBEAT_INTERVAL: Duration = Duration::from_secs(5); +const CLIENT_TIMEOUT: Duration = Duration::from_secs(10); + +/// websocket connection is long running connection, it easier +/// to handle with an actor +pub struct MnmlSocket { + /// Client must send ping at least once per 10 seconds (CLIENT_TIMEOUT), + /// otherwise we drop connection. + hb: Instant, + pool: PgPool, } -fn receive(db: Db, begin: Instant, rpc: &Rpc, msg: Message, client: &mut WebSocket) -> Result { - match rpc.receive(msg, begin, &db, client) { - Ok(reply) => { - let response = to_vec(&reply) - .expect("failed to serialize response"); - client.write_message(Binary(response))?; - return Ok(reply.method); - }, - Err(e) => { - let response = to_vec(&RpcErrorResponse { err: e.to_string() }) - .expect("failed to serialize error response"); - client.write_message(Binary(response))?; - return Err(err_msg(e)); +impl Actor for MnmlSocket { + type Context = ws::WebsocketContext; + + // once the actor has been started this fn runs + // it starts the heartbeat interval and keepalive + fn started(&mut self, ctx: &mut Self::Context) { + self.hb(ctx); + } +} + +/// Handler for `ws::Message` +impl StreamHandler for MnmlSocket { + fn handle(&mut self, msg: ws::Message, ctx: &mut Self::Context) { + // process websocket messages + println!("msg: {:?}", msg); + match msg { + ws::Message::Ping(msg) => { + self.hb = Instant::now(); + ctx.pong(&msg); + } + ws::Message::Pong(_) => { + self.hb = Instant::now(); + } + ws::Message::Text(text) => (), + ws::Message::Close(_) => { + ctx.stop(); + } + ws::Message::Nop => (), + ws::Message::Binary(bin) => { + let db_connection = self.pool.get().expect("unable to get db connection"); + match receive(bin.to_vec(), &db_connection, ctx) { + Ok(reply) => { + let response = to_vec(&reply) + .expect("failed to serialize response"); + ctx.binary(response); + }, + Err(e) => { + let response = to_vec(&RpcErrorResponse { err: e.to_string() }) + .expect("failed to serialize error response"); + ctx.binary(response); + } + } + } } } } -pub fn db_connection(url: String) -> Pool { +impl MnmlSocket { + fn new(state: web::Data) -> MnmlSocket { + // idk why this has to be cloned again + // i guess because each socket is added as a new thread? + MnmlSocket { hb: Instant::now(), pool: state.pool.clone() } + } + + // starts the keepalive interval once actor started + fn hb(&self, ctx: &mut ::Context) { + ctx.run_interval(HEARTBEAT_INTERVAL, |act, ctx| { + if Instant::now().duration_since(act.hb) > CLIENT_TIMEOUT { + info!("Websocket Client heartbeat failed, disconnecting!"); + + // stop actor + ctx.stop(); + + // don't try to send a ping + return; + } + + ctx.ping(""); + }); + } +} + +// idk how this stuff works +// but the args extract what you need from the incoming requests +// this grabs +// the req obj itself which we need for cookies +// the application state +// and the websocket stream +fn ws_index(r: HttpRequest, state: web::Data, stream: web::Payload) -> Result { + let res = ws::start(MnmlSocket::new(state), &r, stream); + + // response of upgrade being sent back + // info!("res={:?}", res.as_ref().unwrap()); + res +} + +fn create_pool(url: String) -> Pool { let manager = PostgresConnectionManager::new(url, TlsMode::None) .expect("could not instantiate pg manager"); @@ -53,76 +126,114 @@ pub fn db_connection(url: String) -> Pool { .expect("Failed to create pool.") } -// fn print_panic_payload(ctx: &str, payload: &(Any + Send + 'static)) { -// let d = format!("{:?}", payload); -// let s = if let Some(s) = payload.downcast_ref::() { -// &s -// } else if let Some(s) = payload.downcast_ref::<&str>() { -// s -// } else { -// // "PAYLOAD IS NOT A STRING" -// d.as_str() -// }; -// info!("{}: PANIC OCCURRED: {}", ctx, s); -// } +// This struct represents state +struct State { + pool: PgPool, +} pub fn start() { - // panic::set_hook(Box::new(|panic_info| { - // print_panic_payload("set_hook", panic_info.payload()); - // if let Some(location) = panic_info.location() { - // info!("LOCATION: {}:{}", location.file(), location.line()); - // } else { - // info!("NO LOCATION INFORMATION"); - // } - // })); - let database_url = env::var("DATABASE_URL") .expect("DATABASE_URL must be set"); - let pool = db_connection(database_url); + let pool = create_pool(database_url); - // { - // let startup_connection = pool.get().expect("unable to get db connection"); - // startup(startup_connection).unwrap(); - // } - - let server = TcpListener::bind("0.0.0.0:40000").unwrap(); - - let warden_pool = pool.clone(); - spawn(move || { - loop { - let db_connection = warden_pool.get().expect("unable to get db connection"); - if let Err(e) = warden(db_connection) { - info!("{:?}", e); - } - sleep(Duration::new(1, 0)); - } - - }); - - for stream in server.incoming() { - let db = pool.clone(); - spawn(move || { - let mut websocket = accept(stream.unwrap()).unwrap(); - let rpc = Rpc {}; - - loop { - match websocket.read_message() { - Ok(msg) => { - let begin = Instant::now(); - let db_connection = db.get().expect("unable to get db connection"); - match receive(db_connection, begin, &rpc, msg, &mut websocket) { - Ok(_) => (), - Err(e) => warn!("{:?}", e), - } - }, - // connection is closed - Err(e) => { - debug!("{:?}", e); - return; - } - }; - } - }); - } + HttpServer::new(move || { + App::new() + .data(State { pool: pool.clone() }) + // enable logger + .wrap(middleware::Logger::default()) + // websocket route + .service(web::resource("/ws/").route(web::get().to(ws_index))) + }) + .bind("127.0.0.1:40000").expect("could not bind to port") + .run().expect("could not start http server"); } + +// #[derive(Debug,Clone,Serialize,Deserialize)] +// struct RpcErrorResponse { +// err: String +// } + +// pub fn db_connection(url: String) -> Pool { +// let manager = PostgresConnectionManager::new(url, TlsMode::None) +// .expect("could not instantiate pg manager"); + +// Pool::builder() +// .max_size(DB_POOL_SIZE) +// .build(manager) +// .expect("Failed to create pool.") +// } + +// // fn print_panic_payload(ctx: &str, payload: &(Any + Send + 'static)) { +// // let d = format!("{:?}", payload); +// // let s = if let Some(s) = payload.downcast_ref::() { +// // &s +// // } else if let Some(s) = payload.downcast_ref::<&str>() { +// // s +// // } else { +// // // "PAYLOAD IS NOT A STRING" +// // d.as_str() +// // }; +// // info!("{}: PANIC OCCURRED: {}", ctx, s); +// // } + +// pub fn start() { +// // panic::set_hook(Box::new(|panic_info| { +// // print_panic_payload("set_hook", panic_info.payload()); +// // if let Some(location) = panic_info.location() { +// // info!("LOCATION: {}:{}", location.file(), location.line()); +// // } else { +// // info!("NO LOCATION INFORMATION"); +// // } +// // })); + +// let database_url = env::var("DATABASE_URL") +// .expect("DATABASE_URL must be set"); + +// let pool = db_connection(database_url); + +// // { +// // let startup_connection = pool.get().expect("unable to get db connection"); +// // startup(startup_connection).unwrap(); +// // } + +// let server = TcpListener::bind("0.0.0.0:40000").unwrap(); + +// let warden_pool = pool.clone(); +// spawn(move || { +// loop { +// let db_connection = warden_pool.get().expect("unable to get db connection"); +// if let Err(e) = warden(db_connection) { +// info!("{:?}", e); +// } +// sleep(Duration::new(1, 0)); +// } + +// }); + +// for stream in server.incoming() { +// let db = pool.clone(); +// spawn(move || { +// let mut websocket = accept(stream.unwrap()).unwrap(); +// let rpc = Rpc {}; + +// loop { +// match websocket.read_message() { +// Ok(msg) => { +// let begin = Instant::now(); +// let db_connection = db.get().expect("unable to get db connection"); +// match receive(db_connection, begin, &rpc, msg, &mut websocket) { +// Ok(_) => (), +// Err(e) => warn!("{:?}", e), +// } +// }, +// // connection is closed +// Err(e) => { +// debug!("{:?}", e); +// return; +// } +// }; +// } +// }); +// } +// } diff --git a/server/src/rpc.rs b/server/src/rpc.rs index 05a812a0..210befe7 100644 --- a/server/src/rpc.rs +++ b/server/src/rpc.rs @@ -1,17 +1,12 @@ -use tungstenite::Message; -use tungstenite::protocol::WebSocket; -use tungstenite::Message::Binary; +use actix_web_actors::ws; + use postgres::transaction::Transaction; -use std::net::{TcpStream}; - -use std::time::Instant; - use serde_cbor::{from_slice, to_vec}; use uuid::Uuid; use failure::Error; use failure::err_msg; -use net::Db; +use net::{Db, MnmlSocket}; use construct::{Construct, construct_spawn, construct_delete}; use game::{Game, game_state, game_skill, game_ready}; use account::{Account, account_create, account_login, account_from_token, account_constructs, account_instances}; @@ -20,370 +15,356 @@ use instance::{Instance, instance_state, instance_list, instance_new, instance_r use vbox::{vbox_accept, vbox_apply, vbox_discard, vbox_combine, vbox_reclaim, vbox_unequip}; use item::{Item, ItemInfoCtr, item_info}; -pub struct Rpc; -impl Rpc { - pub fn receive(&self, msg: Message, begin: Instant, db: &Db, client: &mut WebSocket) -> Result { - // consume the ws data into bytes - let data = msg.into_data(); +type MnmlWs = ws::WebsocketContext; - // cast the msg to this type to receive method name - match from_slice::(&data) { - Ok(v) => { - if v.method == "ping" { - return Ok(RpcResponse { method: "pong".to_string(), params: RpcResult::Pong(()) }); - } +pub fn receive(data: Vec, db: &Db, client: &mut MnmlWs) -> Result { + // cast the data to this type to receive method name + match from_slice::(&data) { + Ok(v) => { + if v.method == "ping" { + return Ok(RpcResponse { method: "pong".to_string(), params: RpcResult::Pong(()) }); + } - let mut tx = db.transaction()?; + let mut tx = db.transaction()?; - let account: Option = match v.token { - Some(t) => Some(account_from_token(t, &mut tx)?), - None => None, - }; + let account: Option = match v.token { + Some(t) => Some(account_from_token(t, &mut tx)?), + None => None, + }; - let account_name = match &account { - Some(a) => a.name.clone(), - None => "none".to_string(), - }; + let account_name = match &account { + Some(a) => a.name.clone(), + None => "none".to_string(), + }; - // check the method - // if no auth required - match v.method.as_ref() { - "account_create" => (), - "account_login" => (), - "item_info" => (), - _ => match account { - Some(_) => (), - None => return Err(err_msg("auth required")), - }, - }; + // check the method + // if no auth required + match v.method.as_ref() { + "account_create" => (), + "account_login" => (), + "item_info" => (), + _ => match account { + Some(_) => (), + None => return Err(err_msg("auth required")), + }, + }; - // now we have the method name - // match on that to determine what fn to call - let response = match v.method.as_ref() { - // NO AUTH - "account_create" => Rpc::account_create(data, &mut tx, client), - "account_login" => Rpc::account_login(data, &mut tx, client), - "item_info" => Ok(RpcResponse { method: "item_info".to_string(), params: RpcResult::ItemInfo(item_info()) }), + // now we have the method name + // match on that to determine what fn to call + let response = match v.method.as_ref() { + // NO AUTH + "account_create" => handle_account_create(data, &mut tx, client), + "account_login" => handle_account_login(data, &mut tx, client), + "item_info" => Ok(RpcResponse { method: "item_info".to_string(), params: RpcResult::ItemInfo(item_info()) }), - // AUTH METHODS - "account_constructs" => Rpc::account_constructs(data, &mut tx, account.unwrap(), client), - "account_instances" => Rpc::account_instances(data, &mut tx, account.unwrap(), client), + // AUTH METHODS + "account_constructs" => handle_account_constructs(data, &mut tx, account.unwrap(), client), + "account_instances" => handle_account_instances(data, &mut tx, account.unwrap(), client), - "construct_spawn" => Rpc::construct_spawn(data, &mut tx, account.unwrap(), client), - "construct_delete" => Rpc::construct_delete(data, &mut tx, account.unwrap(), client), + "construct_spawn" => handle_construct_spawn(data, &mut tx, account.unwrap(), client), + "construct_delete" => handle_construct_delete(data, &mut tx, account.unwrap(), client), - "game_state" => Rpc::game_state(data, &mut tx, account.unwrap(), client), - "game_skill" => Rpc::game_skill(data, &mut tx, account.unwrap(), client), - "game_ready" => Rpc::game_ready(data, &mut tx, account.unwrap(), client), + "game_state" => handle_game_state(data, &mut tx, account.unwrap(), client), + "game_skill" => handle_game_skill(data, &mut tx, account.unwrap(), client), + "game_ready" => handle_game_ready(data, &mut tx, account.unwrap(), client), - "instance_list" => Rpc::instance_list(data, &mut tx, account.unwrap(), client), - "instance_join" => Rpc::instance_join(data, &mut tx, account.unwrap(), client), - "instance_ready" => Rpc::instance_ready(data, &mut tx, account.unwrap(), client), - "instance_new" => Rpc::instance_new(data, &mut tx, account.unwrap(), client), - "instance_state" => Rpc::instance_state(data, &mut tx, account.unwrap(), client), + "instance_list" => handle_instance_list(data, &mut tx, account.unwrap(), client), + "instance_join" => handle_instance_join(data, &mut tx, account.unwrap(), client), + "instance_ready" => handle_instance_ready(data, &mut tx, account.unwrap(), client), + "instance_new" => handle_instance_new(data, &mut tx, account.unwrap(), client), + "instance_state" => handle_instance_state(data, &mut tx, account.unwrap(), client), - "vbox_accept" => Rpc::vbox_accept(data, &mut tx, account.unwrap(), client), - "vbox_apply" => Rpc::vbox_apply(data, &mut tx, account.unwrap(), client), - "vbox_combine" => Rpc::vbox_combine(data, &mut tx, account.unwrap(), client), - "vbox_discard" => Rpc::vbox_discard(data, &mut tx, account.unwrap(), client), - "vbox_reclaim" => Rpc::vbox_reclaim(data, &mut tx, account.unwrap(), client), - "vbox_unequip" => Rpc::vbox_unequip(data, &mut tx, account.unwrap(), client), + "vbox_accept" => handle_vbox_accept(data, &mut tx, account.unwrap(), client), + "vbox_apply" => handle_vbox_apply(data, &mut tx, account.unwrap(), client), + "vbox_combine" => handle_vbox_combine(data, &mut tx, account.unwrap(), client), + "vbox_discard" => handle_vbox_discard(data, &mut tx, account.unwrap(), client), + "vbox_reclaim" => handle_vbox_reclaim(data, &mut tx, account.unwrap(), client), + "vbox_unequip" => handle_vbox_unequip(data, &mut tx, account.unwrap(), client), - _ => Err(format_err!("unknown method - {:?}", v.method)), - }; + _ => Err(format_err!("unknown method - {:?}", v.method)), + }; - tx.commit()?; + tx.commit()?; - info!("method={:?} account={:?} duration={:?}", v.method, account_name, begin.elapsed()); - - return response; - }, - Err(e) => { - info!("{:?}", e); - Err(err_msg("unknown error")) - }, - } + return response; + }, + Err(e) => { + info!("{:?}", e); + Err(err_msg("unknown error")) + }, } +} - fn send_msg(client: &mut WebSocket, msg: RpcResponse) -> Result<(), Error> { - let bytes = to_vec(&msg)?; - match client.write_message(Binary(bytes)) { - Ok(()) => Ok(()), - Err(e) => Err(err_msg(e)) - } +fn handle_game_state(data: Vec, tx: &mut Transaction, account: Account, _client: &mut MnmlWs) -> Result { + let msg = from_slice::(&data).or(Err(err_msg("invalid params")))?; + + let game_response = RpcResponse { + method: "game_state".to_string(), + params: RpcResult::GameState(game_state(msg.params, tx, &account)?) + }; + + return Ok(game_response); +} + +// fn handle_game_pve(data: Vec, tx: &mut Transaction, account: Account, _client: &mut MnmlWs) -> Result { +// let msg = from_slice::(&data).or(Err(err_msg("invalid params")))?; + +// let game_response = RpcResponse { +// method: "game_state".to_string(), +// params: RpcResult::GameState(game_pve(msg.params, tx, &account)?) +// }; + +// return Ok(game_response); +// } + +fn handle_game_skill(data: Vec, tx: &mut Transaction, account: Account, _client: &mut MnmlWs) -> Result { + let msg = from_slice::(&data).or(Err(err_msg("invalid params")))?; + + let game_response = RpcResponse { + method: "game_state".to_string(), + params: RpcResult::GameState(game_skill(msg.params, tx, &account)?) + }; + + return Ok(game_response); +} + +fn handle_game_ready(data: Vec, tx: &mut Transaction, account: Account, _client: &mut MnmlWs) -> Result { + let msg = from_slice::(&data).or(Err(err_msg("invalid params")))?; + + let game_response = RpcResponse { + method: "game_state".to_string(), + params: RpcResult::GameState(game_ready(msg.params, tx, &account)?) + }; + + return Ok(game_response); +} + + +fn handle_construct_spawn(data: Vec, tx: &mut Transaction, account: Account, client: &mut MnmlWs) -> Result { + let msg = from_slice::(&data).or(Err(err_msg("invalid params")))?; + + let construct_list = RpcResponse { + method: "account_constructs".to_string(), + params: RpcResult::ConstructList(account_constructs(tx, &account)?) + }; + + Ok(construct_list) +} + +fn handle_construct_delete(data: Vec, tx: &mut Transaction, account: Account, _client: &mut MnmlWs) -> Result { + let msg = from_slice::(&data).or(Err(err_msg("invalid params")))?; + + construct_delete(tx, msg.params.id, account.id)?; + + let construct_list = RpcResponse { + method: "account_constructs".to_string(), + params: RpcResult::ConstructList(account_constructs(tx, &account)?) + }; + + Ok(construct_list) +} + + +fn handle_account_create(data: Vec, tx: &mut Transaction, _client: &mut MnmlWs) -> Result { + let msg = from_slice::(&data).or(Err(err_msg("invalid params")))?; + + let account = account_create(msg.params, tx)?; + + Ok(RpcResponse { + method: "account_create".to_string(), + params: RpcResult::Account(account) + }) +} + +fn handle_account_login(data: Vec, tx: &mut Transaction, _client: &mut MnmlWs) -> Result { + match from_slice::(&data) { + Ok(v) => Ok(RpcResponse { + method: v.method, + params: RpcResult::Account(account_login(v.params, tx)?) + }), + Err(_e) => Err(err_msg("invalid params")), } +} - fn game_state(data: Vec, tx: &mut Transaction, account: Account, _client: &mut WebSocket) -> Result { - let msg = from_slice::(&data).or(Err(err_msg("invalid params")))?; +// fn handle_account_demo(_data: Vec, tx: &mut Transaction, _client: &mut MnmlWs) -> Result { +// let mut rng = thread_rng(); - let game_response = RpcResponse { +// let acc_name: String = iter::repeat(()).map(|()| rng.sample(Alphanumeric)).take(8).collect(); + +// let account = account_create(AccountCreateParams { name: acc_name, password: "grepgrepgrep".to_string() }, tx)?; + +// let name: String = iter::repeat(()).map(|()| rng.sample(Alphanumeric)).take(8).collect(); +// construct_spawn(ConstructSpawnParams { name }, tx, &account)?; + +// let name: String = iter::repeat(()).map(|()| rng.sample(Alphanumeric)).take(8).collect(); +// construct_spawn(ConstructSpawnParams { name }, tx, &account)?; + +// let name: String = iter::repeat(()).map(|()| rng.sample(Alphanumeric)).take(8).collect(); +// construct_spawn(ConstructSpawnParams { name }, tx, &account)?; + +// let res = RpcResponse { +// method: "account_create".to_string(), +// params: RpcResult::Account(account), +// }; + +// return Ok(res); +// } + + +fn handle_account_constructs(_data: Vec, tx: &mut Transaction, account: Account, _client: &mut MnmlWs) -> Result { + Ok(RpcResponse { + method: "account_constructs".to_string(), + params: RpcResult::ConstructList(account_constructs(tx, &account)?) + }) +} + +fn handle_account_instances(_data: Vec, tx: &mut Transaction, account: Account, _client: &mut MnmlWs) -> Result { + Ok(RpcResponse { + method: "account_instances".to_string(), + params: RpcResult::InstanceList(account_instances(tx, &account)?) + }) +} + +fn handle_instance_new(data: Vec, tx: &mut Transaction, account: Account, _client: &mut MnmlWs) -> Result { + let msg = from_slice::(&data).or(Err(err_msg("invalid params")))?; + + let response = RpcResponse { + method: "instance_state".to_string(), + params: RpcResult::InstanceState(instance_new(msg.params, tx, &account)?) + }; + + return Ok(response); +} + +fn handle_instance_ready(data: Vec, tx: &mut Transaction, account: Account, _client: &mut MnmlWs) -> Result { + let msg = from_slice::(&data).or(Err(err_msg("invalid params")))?; + + let response = RpcResponse { + method: "instance_state".to_string(), + params: RpcResult::InstanceState(instance_ready(msg.params, tx, &account)?) + }; + + return Ok(response); +} + +fn handle_instance_join(data: Vec, tx: &mut Transaction, account: Account, _client: &mut MnmlWs) -> Result { + let msg = from_slice::(&data).or(Err(err_msg("invalid params")))?; + + let response = RpcResponse { + method: "instance_state".to_string(), + params: RpcResult::InstanceState(instance_join(msg.params, tx, &account)?) + }; + + return Ok(response); +} + +fn handle_instance_list(_data: Vec, tx: &mut Transaction, _account: Account, _client: &mut MnmlWs) -> Result { + let response = RpcResponse { + method: "instance_list".to_string(), + params: RpcResult::InstanceList(instance_list(tx)?) + }; + + return Ok(response); +} + + +// fn handle_instance_ready(data: Vec, tx: &mut Transaction, account: Account, _client: &mut MnmlWs) -> Result { +// let msg = from_slice::(&data).or(Err(err_msg("invalid params")))?; + +// let response = RpcResponse { +// method: "game_state".to_string(), +// params: RpcResult::GameState(instance_ready(msg.params, tx, &account)?) +// }; + +// return Ok(response); +// } + +fn handle_instance_state(data: Vec, tx: &mut Transaction, account: Account, _client: &mut MnmlWs) -> Result { + let msg = from_slice::(&data).or(Err(err_msg("invalid params")))?; + match instance_state(msg.params, tx, &account)? { + RpcResult::GameState(p) => Ok(RpcResponse { method: "game_state".to_string(), - params: RpcResult::GameState(game_state(msg.params, tx, &account)?) - }; - - return Ok(game_response); - } - - // fn game_pve(data: Vec, tx: &mut Transaction, account: Account, _client: &mut WebSocket) -> Result { - // let msg = from_slice::(&data).or(Err(err_msg("invalid params")))?; - - // let game_response = RpcResponse { - // method: "game_state".to_string(), - // params: RpcResult::GameState(game_pve(msg.params, tx, &account)?) - // }; - - // return Ok(game_response); - // } - - fn game_skill(data: Vec, tx: &mut Transaction, account: Account, _client: &mut WebSocket) -> Result { - let msg = from_slice::(&data).or(Err(err_msg("invalid params")))?; - - let game_response = RpcResponse { - method: "game_state".to_string(), - params: RpcResult::GameState(game_skill(msg.params, tx, &account)?) - }; - - return Ok(game_response); - } - - fn game_ready(data: Vec, tx: &mut Transaction, account: Account, _client: &mut WebSocket) -> Result { - let msg = from_slice::(&data).or(Err(err_msg("invalid params")))?; - - let game_response = RpcResponse { - method: "game_state".to_string(), - params: RpcResult::GameState(game_ready(msg.params, tx, &account)?) - }; - - return Ok(game_response); - } - - - fn construct_spawn(data: Vec, tx: &mut Transaction, account: Account, client: &mut WebSocket) -> Result { - let msg = from_slice::(&data).or(Err(err_msg("invalid params")))?; - - Rpc::send_msg(client, RpcResponse { - method: "construct_spawn".to_string(), - params: RpcResult::ConstructSpawn(construct_spawn(msg.params, tx, &account)?) - })?; - - let construct_list = RpcResponse { - method: "account_constructs".to_string(), - params: RpcResult::ConstructList(account_constructs(tx, &account)?) - }; - - Ok(construct_list) - } - - fn construct_delete(data: Vec, tx: &mut Transaction, account: Account, _client: &mut WebSocket) -> Result { - let msg = from_slice::(&data).or(Err(err_msg("invalid params")))?; - - construct_delete(tx, msg.params.id, account.id)?; - - let construct_list = RpcResponse { - method: "account_constructs".to_string(), - params: RpcResult::ConstructList(account_constructs(tx, &account)?) - }; - - Ok(construct_list) - } - - - fn account_create(data: Vec, tx: &mut Transaction, _client: &mut WebSocket) -> Result { - let msg = from_slice::(&data).or(Err(err_msg("invalid params")))?; - - let account = account_create(msg.params, tx)?; - - Ok(RpcResponse { - method: "account_create".to_string(), - params: RpcResult::Account(account) - }) - } - - fn account_login(data: Vec, tx: &mut Transaction, _client: &mut WebSocket) -> Result { - match from_slice::(&data) { - Ok(v) => Ok(RpcResponse { - method: v.method, - params: RpcResult::Account(account_login(v.params, tx)?) - }), - Err(_e) => Err(err_msg("invalid params")), - } - } - - // fn account_demo(_data: Vec, tx: &mut Transaction, _client: &mut WebSocket) -> Result { - // let mut rng = thread_rng(); - - // let acc_name: String = iter::repeat(()).map(|()| rng.sample(Alphanumeric)).take(8).collect(); - - // let account = account_create(AccountCreateParams { name: acc_name, password: "grepgrepgrep".to_string() }, tx)?; - - // let name: String = iter::repeat(()).map(|()| rng.sample(Alphanumeric)).take(8).collect(); - // construct_spawn(ConstructSpawnParams { name }, tx, &account)?; - - // let name: String = iter::repeat(()).map(|()| rng.sample(Alphanumeric)).take(8).collect(); - // construct_spawn(ConstructSpawnParams { name }, tx, &account)?; - - // let name: String = iter::repeat(()).map(|()| rng.sample(Alphanumeric)).take(8).collect(); - // construct_spawn(ConstructSpawnParams { name }, tx, &account)?; - - // let res = RpcResponse { - // method: "account_create".to_string(), - // params: RpcResult::Account(account), - // }; - - // return Ok(res); - // } - - - fn account_constructs(_data: Vec, tx: &mut Transaction, account: Account, _client: &mut WebSocket) -> Result { - Ok(RpcResponse { - method: "account_constructs".to_string(), - params: RpcResult::ConstructList(account_constructs(tx, &account)?) - }) - } - - fn account_instances(_data: Vec, tx: &mut Transaction, account: Account, _client: &mut WebSocket) -> Result { - Ok(RpcResponse { - method: "account_instances".to_string(), - params: RpcResult::InstanceList(account_instances(tx, &account)?) - }) - } - - fn instance_new(data: Vec, tx: &mut Transaction, account: Account, _client: &mut WebSocket) -> Result { - let msg = from_slice::(&data).or(Err(err_msg("invalid params")))?; - - let response = RpcResponse { + params: RpcResult::GameState(p), + }), + RpcResult::InstanceState(p) => Ok(RpcResponse { method: "instance_state".to_string(), - params: RpcResult::InstanceState(instance_new(msg.params, tx, &account)?) - }; - - return Ok(response); + params: RpcResult::InstanceState(p), + }), + _ => Err(err_msg("unhandled instance state")) } +} - fn instance_ready(data: Vec, tx: &mut Transaction, account: Account, _client: &mut WebSocket) -> Result { - let msg = from_slice::(&data).or(Err(err_msg("invalid params")))?; +fn handle_vbox_accept(data: Vec, tx: &mut Transaction, account: Account, _client: &mut MnmlWs) -> Result { + let msg = from_slice::(&data).or(Err(err_msg("invalid params")))?; - let response = RpcResponse { - method: "instance_state".to_string(), - params: RpcResult::InstanceState(instance_ready(msg.params, tx, &account)?) - }; + let response = RpcResponse { + method: "instance_state".to_string(), + params: RpcResult::InstanceState(vbox_accept(msg.params, tx, &account)?) + }; - return Ok(response); - } + return Ok(response); +} - fn instance_join(data: Vec, tx: &mut Transaction, account: Account, _client: &mut WebSocket) -> Result { - let msg = from_slice::(&data).or(Err(err_msg("invalid params")))?; +fn handle_vbox_discard(data: Vec, tx: &mut Transaction, account: Account, _client: &mut MnmlWs) -> Result { + let msg = from_slice::(&data).or(Err(err_msg("invalid params")))?; - let response = RpcResponse { - method: "instance_state".to_string(), - params: RpcResult::InstanceState(instance_join(msg.params, tx, &account)?) - }; + let response = RpcResponse { + method: "instance_state".to_string(), + params: RpcResult::InstanceState(vbox_discard(msg.params, tx, &account)?) + }; - return Ok(response); - } - - fn instance_list(_data: Vec, tx: &mut Transaction, _account: Account, _client: &mut WebSocket) -> Result { - let response = RpcResponse { - method: "instance_list".to_string(), - params: RpcResult::InstanceList(instance_list(tx)?) - }; - - return Ok(response); - } + return Ok(response); +} - // fn instance_ready(data: Vec, tx: &mut Transaction, account: Account, _client: &mut WebSocket) -> Result { - // let msg = from_slice::(&data).or(Err(err_msg("invalid params")))?; +fn handle_vbox_combine(data: Vec, tx: &mut Transaction, account: Account, _client: &mut MnmlWs) -> Result { + let msg = from_slice::(&data).or(Err(err_msg("invalid params")))?; - // let response = RpcResponse { - // method: "game_state".to_string(), - // params: RpcResult::GameState(instance_ready(msg.params, tx, &account)?) - // }; + let response = RpcResponse { + method: "instance_state".to_string(), + params: RpcResult::InstanceState(vbox_combine(msg.params, tx, &account)?) + }; - // return Ok(response); - // } + return Ok(response); +} - fn instance_state(data: Vec, tx: &mut Transaction, account: Account, _client: &mut WebSocket) -> Result { - let msg = from_slice::(&data).or(Err(err_msg("invalid params")))?; - match instance_state(msg.params, tx, &account)? { - RpcResult::GameState(p) => Ok(RpcResponse { - method: "game_state".to_string(), - params: RpcResult::GameState(p), - }), - RpcResult::InstanceState(p) => Ok(RpcResponse { - method: "instance_state".to_string(), - params: RpcResult::InstanceState(p), - }), - _ => Err(err_msg("unhandled instance state")) - } - } +fn handle_vbox_apply(data: Vec, tx: &mut Transaction, account: Account, _client: &mut MnmlWs) -> Result { + let msg = from_slice::(&data).or(Err(err_msg("invalid params")))?; - fn vbox_accept(data: Vec, tx: &mut Transaction, account: Account, _client: &mut WebSocket) -> Result { - let msg = from_slice::(&data).or(Err(err_msg("invalid params")))?; + let response = RpcResponse { + method: "instance_state".to_string(), + params: RpcResult::InstanceState(vbox_apply(msg.params, tx, &account)?) + }; - let response = RpcResponse { - method: "instance_state".to_string(), - params: RpcResult::InstanceState(vbox_accept(msg.params, tx, &account)?) - }; + return Ok(response); +} - return Ok(response); - } +fn handle_vbox_reclaim(data: Vec, tx: &mut Transaction, account: Account, _client: &mut MnmlWs) -> Result { + let msg = from_slice::(&data).or(Err(err_msg("invalid params")))?; - fn vbox_discard(data: Vec, tx: &mut Transaction, account: Account, _client: &mut WebSocket) -> Result { - let msg = from_slice::(&data).or(Err(err_msg("invalid params")))?; + let response = RpcResponse { + method: "instance_state".to_string(), + params: RpcResult::InstanceState(vbox_reclaim(msg.params, tx, &account)?) + }; - let response = RpcResponse { - method: "instance_state".to_string(), - params: RpcResult::InstanceState(vbox_discard(msg.params, tx, &account)?) - }; + return Ok(response); +} - return Ok(response); - } +fn handle_vbox_unequip(data: Vec, tx: &mut Transaction, account: Account, _client: &mut MnmlWs) -> Result { + let msg = from_slice::(&data).or(Err(err_msg("invalid params")))?; + let response = RpcResponse { + method: "instance_state".to_string(), + params: RpcResult::InstanceState(vbox_unequip(msg.params, tx, &account)?) + }; - fn vbox_combine(data: Vec, tx: &mut Transaction, account: Account, _client: &mut WebSocket) -> Result { - let msg = from_slice::(&data).or(Err(err_msg("invalid params")))?; + return Ok(response); +} - let response = RpcResponse { - method: "instance_state".to_string(), - params: RpcResult::InstanceState(vbox_combine(msg.params, tx, &account)?) - }; - - return Ok(response); - } - - fn vbox_apply(data: Vec, tx: &mut Transaction, account: Account, _client: &mut WebSocket) -> Result { - let msg = from_slice::(&data).or(Err(err_msg("invalid params")))?; - - let response = RpcResponse { - method: "instance_state".to_string(), - params: RpcResult::InstanceState(vbox_apply(msg.params, tx, &account)?) - }; - - return Ok(response); - } - - fn vbox_reclaim(data: Vec, tx: &mut Transaction, account: Account, _client: &mut WebSocket) -> Result { - let msg = from_slice::(&data).or(Err(err_msg("invalid params")))?; - - let response = RpcResponse { - method: "instance_state".to_string(), - params: RpcResult::InstanceState(vbox_reclaim(msg.params, tx, &account)?) - }; - - return Ok(response); - } - - fn vbox_unequip(data: Vec, tx: &mut Transaction, account: Account, _client: &mut WebSocket) -> Result { - let msg = from_slice::(&data).or(Err(err_msg("invalid params")))?; - - let response = RpcResponse { - method: "instance_state".to_string(), - params: RpcResult::InstanceState(vbox_unequip(msg.params, tx, &account)?) - }; - - return Ok(response); - } +#[derive(Debug,Clone,Serialize,Deserialize)] +pub struct RpcErrorResponse { + pub err: String } #[derive(Debug,Clone,Serialize,Deserialize)]