diff --git a/core/src/instance.rs b/core/src/instance.rs index 27d09bc0..3c7f8b0f 100644 --- a/core/src/instance.rs +++ b/core/src/instance.rs @@ -144,8 +144,8 @@ impl Instance { .collect::>() } + // time out lobbies that have been open too long pub fn upkeep(mut self) -> (Instance, Option) { - // time out lobbies that have been open too long if self.phase == InstancePhase::Lobby && self.phase_timed_out() { self.finish(); return (self, None); diff --git a/core/src/mob.rs b/core/src/mob.rs index 479acb43..5d774c13 100644 --- a/core/src/mob.rs +++ b/core/src/mob.rs @@ -31,6 +31,11 @@ pub fn bot_player() -> Player { Player::new(bot_id, None, &name(), constructs).set_bot(true) } +pub fn anon_player(id: Uuid) -> Player { + let constructs = instance_mobs(id); + Player::new(id, None, &"player".to_string(), constructs) +} + pub fn anim_test_game(skill: Skill) -> Game { let mut rng = thread_rng(); let mut game = Game::new(); diff --git a/server/src/account.rs b/server/src/account.rs index 7373d0f6..a162a22c 100644 --- a/server/src/account.rs +++ b/server/src/account.rs @@ -44,6 +44,16 @@ impl Account { Ok(Player::new(self.id, Some(self.img), &self.name, constructs)) } + + pub fn anonymous() -> Account { + Account { + id: Uuid::new_v4(), + img: Uuid::new_v4(), + name: "you".to_string(), + balance: 0, + subscribed: false, + } + } } impl<'a> TryFrom> for Account { diff --git a/server/src/events.rs b/server/src/events.rs index 0a347868..fab2bfca 100644 --- a/server/src/events.rs +++ b/server/src/events.rs @@ -18,7 +18,7 @@ use warden::{GameEvent}; use mail::Mail; pub type EventsTx = Sender; -type Id = usize; +type Id = Uuid; // this is pretty heavyweight // but it makes the ergonomics easy @@ -43,7 +43,7 @@ pub struct Events { #[derive(Debug,Clone)] pub enum Event { // ws lifecycle - Connect(Id, Option, Sender), + Connect(Id, Account, Sender), Disconnect(Id), Subscribe(Id, Uuid), Unsubscribe(Id, Uuid), @@ -64,7 +64,6 @@ pub enum Event { struct WsClient { id: Id, - account: Option, tx: Sender, subs: HashSet, chat: Option<(Uuid, String)>, @@ -118,14 +117,8 @@ impl Events { Event::Connect(id, account, tx) => { info!("connect id={:?} account={:?}", id, account); - let account_id = match account { - Some(a) => Some(a.id), - None => None, - }; - let client = WsClient { id, tx, - account: account_id, subs: HashSet::new(), pvp: false, invite: None, @@ -183,13 +176,10 @@ impl Events { if client.subs.contains(&id) { subs += 1; - let redacted = match client.account { - Some(a) => match msg { - RpcMessage::InstanceState(ref i) => RpcMessage::InstanceState(i.clone().redact(a)), - RpcMessage::GameState(ref i) => RpcMessage::GameState(i.clone().redact(a)), - _ => msg.clone(), - } - None => msg.clone(), + let redacted = match msg { + RpcMessage::InstanceState(ref i) => RpcMessage::InstanceState(i.clone().redact(client.id)), + RpcMessage::GameState(ref i) => RpcMessage::GameState(i.clone().redact(client.id)), + _ => msg.clone(), }; match client.tx.send(redacted) { @@ -218,18 +208,14 @@ impl Events { let c = self.clients.get(&id) .ok_or(format_err!("connection not found id={:?}", id))?; - if let None = c.account { - return Err(err_msg("cannot join pvp queue anonymously")); - } - - info!("pvp queue request id={:?} account={:?}", c.id, c.account); + info!("pvp queue request id={:?} account={:?}", c.id, c.id); } // create the req for the already queued opponent if let Some(opp_req) = match self.clients.iter_mut().find(|(c_id, c)| c.pvp && **c_id != id) { Some((q_id, q)) => { q.pvp = false; - Some(PvpRequest { id: *q_id, account: q.account.unwrap(), tx: q.tx.clone() }) + Some(PvpRequest { id: *q_id, account: q.id, tx: q.tx.clone() }) }, None => None, } { @@ -237,7 +223,7 @@ impl Events { let c = self.clients.get_mut(&id) .ok_or(format_err!("connection not found id={:?}", id))?; - let player_req = PvpRequest { id: c.id, account: c.account.unwrap(), tx: c.tx.clone() }; + let player_req = PvpRequest { id: c.id, account: c.id, tx: c.tx.clone() }; self.warden.send(GameEvent::Match((opp_req, player_req)))?; return Ok(()) @@ -247,7 +233,7 @@ impl Events { let requester = self.clients.get_mut(&id).unwrap(); requester.pvp = true; requester.tx.send(RpcMessage::QueueJoined(()))?; - info!("joined game queue id={:?} account={:?}", requester.id, requester.account); + info!("joined game queue id={:?} account={:?}", requester.id, requester.id); return Ok(()); }, @@ -256,12 +242,8 @@ impl Events { let c = self.clients.get_mut(&id) .ok_or(format_err!("connection not found id={:?}", id))?; - if let None = c.account { - return Err(err_msg("cannot join pvp queue anonymously")); - } - let code = names::name().split_whitespace().collect::>().join("-"); - info!("pvp invite request id={:?} account={:?} code={:?}", c.id, c.account, code); + info!("pvp invite request id={:?} account={:?} code={:?}", c.id, c.id, code); c.invite = Some(code.clone()); c.tx.send(RpcMessage::Invite(code))?; return Ok(()); @@ -272,11 +254,7 @@ impl Events { let c = self.clients.get(&id) .ok_or(format_err!("connection not found id={:?}", id))?; - if let None = c.account { - return Err(err_msg("cannot join pvp queue anonymously")); - } - - info!("pvp join request id={:?} account={:?} code={:?}", c.id, c.account, code); + info!("pvp join request id={:?} account={:?} code={:?}", c.id, c.id, code); let inv = self.clients.iter() .filter(|(_id, c)| c.invite.is_some()) @@ -284,10 +262,10 @@ impl Events { Some(ref c) => *c == code, None => false, }) - .map(|(_id, c)| PvpRequest { id: c.id, account: c.account.unwrap(), tx: c.tx.clone() }) + .map(|(_id, c)| PvpRequest { id: c.id, account: c.id, tx: c.tx.clone() }) .ok_or(format_err!("invite expired code={:?}", code))?; - let join = PvpRequest { id: c.id, account: c.account.unwrap(), tx: c.tx.clone() }; + let join = PvpRequest { id: c.id, account: c.id, tx: c.tx.clone() }; self.warden.send(GameEvent::Match((join, inv)))?; return Ok(()); @@ -310,7 +288,7 @@ impl Events { c.pvp = false; c.tx.send(RpcMessage::QueueLeft(()))?; - info!("left game queue id={:?} account={:?}", c.id, c.account); + info!("left game queue id={:?} account={:?}", c.id, c.id); return Ok(()); }, @@ -337,12 +315,11 @@ impl Events { // now collect all listeners of this instance let chat_state: HashMap = self.clients.iter() - .filter(|(_id, c)| c.account.is_some()) .filter(|(_id, c)| match c.chat { Some(ref chat) => chat.0 == instance, None => false, }) - .map(|(_id, c)| (c.account.unwrap(), c.chat.clone().unwrap().1)) + .map(|(_id, c)| (c.id, c.chat.clone().unwrap().1)) .collect(); return self.event(Event::Push(instance, RpcMessage::InstanceChat(chat_state))); @@ -357,12 +334,11 @@ impl Events { } let chat_state: HashMap = self.clients.iter() - .filter(|(_id, c)| c.account.is_some()) .filter(|(_id, c)| match c.chat { Some(ref chat) => chat.0 == instance, None => false, }) - .map(|(_id, c)| (c.account.unwrap(), c.chat.clone().unwrap().1)) + .map(|(_id, c)| (c.id, c.chat.clone().unwrap().1)) .collect(); return self.event(Event::Push(instance, RpcMessage::InstanceChat(chat_state))); diff --git a/server/src/lib.rs b/server/src/lib.rs index 66efa49d..26fa9e31 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -48,6 +48,8 @@ mod pg; mod events; pub mod rpc; mod warden; +mod user_authenticated; +mod user_anonymous; use std::thread::{spawn}; use std::path::{Path}; diff --git a/server/src/pg.rs b/server/src/pg.rs index 0c815c9a..ec56a9be 100644 --- a/server/src/pg.rs +++ b/server/src/pg.rs @@ -22,7 +22,7 @@ use mnml_core::mob::instance_mobs; use mnml_core::vbox::{ItemType, VboxIndices}; use mnml_core::item::Item; use mnml_core::skill::Skill; -use mnml_core::mob::bot_player; +use mnml_core::mob::{bot_player, anon_player}; use mnml_core::instance::{Instance, TimeControl}; use events::{Event}; @@ -694,6 +694,35 @@ pub fn instance_practice(tx: &mut Transaction, account: &Account) -> Result Result { + let bot = bot_player(); + let bot_id = bot.id; + + // generate imgs for the client to see + for c in bot.constructs.iter() { + img::shapes_write(c.img)?; + } + + let mut instance = Instance::new() + .set_time_control(TimeControl::Practice) + .set_name(bot.name.clone())?; + + let player = anon_player(account.id); + + for c in player.constructs.iter() { + img::shapes_write(c.img)?; + } + + instance.add_player(player.clone())?; + instance.add_player(bot)?; + + instance.player_ready(bot_id)?; + // skip faceoff + instance.player_ready(player.id)?; + + Ok(instance) +} + pub fn pvp(tx: &mut Transaction, a: &Account, b: &Account) -> Result { let mut instance = Instance::new() // TODO generate nice game names diff --git a/server/src/rpc.rs b/server/src/rpc.rs index fea26385..ff755767 100644 --- a/server/src/rpc.rs +++ b/server/src/rpc.rs @@ -1,6 +1,6 @@ use mnml_core::item::ItemInfoCtr; use mnml_core::instance::ChatState; -use mnml_core::item::item_info; + use std::collections::HashMap; use std::time::{Instant}; use std::thread::{spawn}; @@ -8,12 +8,8 @@ use std::thread::{spawn}; use std::str; use uuid::Uuid; -use rand::prelude::*; - use failure::Error; -use failure::err_msg; - -use serde_cbor::{from_slice, to_vec}; +use serde_cbor::{to_vec}; use cookie::Cookie; use stripe::{Client as StripeClient, Subscription}; @@ -22,42 +18,24 @@ use crossbeam_channel::{unbounded, Sender as CbSender}; use ws::{Builder, CloseCode, Message, Handler, Request, Response, Settings, Sender as WsSender}; use ws::deflate::DeflateHandler; -use pg::{ - game_concede, - game_offer_draw, - game_ready, - game_skill, - game_skill_clear, - game_state, - instance_abandon, - instance_practice, - instance_ready, - instance_state, - vbox_apply, - vbox_buy, - vbox_combine, - vbox_refill, - vbox_refund, - vbox_unequip, -}; - use account::{Account}; use account; use events::{Event}; +use user_anonymous::{Anonymous}; +use user_authenticated::{Authorised}; + use mnml_core::construct::{Construct}; use mnml_core::game::{Game}; use mnml_core::vbox::{ItemType}; use mnml_core::item::Item; use mnml_core::skill::Skill; -use mnml_core::mob::{anim_test_game}; + use mnml_core::instance::{Instance}; use mtx; -use mail; -use payments; use mail::Email; use pg::{Db}; use pg::{PgPool}; @@ -145,194 +123,32 @@ pub enum RpcRequest { VboxRefund { instance_id: Uuid, index: String }, } +pub trait User { + fn receive(&mut self, data: Vec, db: &Db, begin: Instant, events: &CbSender, stripe: &StripeClient) -> Result; + fn connected(&mut self, db: &Db, events: &CbSender, ws: &CbSender) -> Result<(), Error>; + fn send(&mut self, msg: RpcMessage, events: &CbSender, ws: &CbSender) -> Result<(), Error>; +} + struct Connection { - pub id: usize, + pub id: Uuid, pub ws: CbSender, pool: PgPool, stripe: StripeClient, - account: Option, + // account: Option, + user: Box, events: CbSender, } impl Connection { - fn receive(&self, data: Vec, db: &Db, begin: Instant) -> Result { - // cast the msg to this type to receive method name - match from_slice::(&data) { - Ok(v) => { - - // non authenticated - // non transactional reqs - match v { - RpcRequest::Ping {} => return Ok(RpcMessage::Pong(())), - RpcRequest::ItemInfo {} => return Ok(RpcMessage::ItemInfo(item_info())), - RpcRequest::DevResolve { skill } => - return Ok(RpcMessage::GameState(anim_test_game(skill))), - _ => (), - }; - - // check for authorization now - let account = match self.account { - Some(ref account) => account, - None => return Err(err_msg("auth required")), - }; - - let request = v.clone(); - - let response = match v { - // evented but authorization required - RpcRequest::InstanceQueue {} => { - self.events.send(Event::Queue(self.id))?; - Ok(RpcMessage::QueueRequested(())) - }, - RpcRequest::InstanceInvite {} => { - self.events.send(Event::Invite(self.id))?; - Ok(RpcMessage::InviteRequested(())) - }, - RpcRequest::InstanceJoin { code } => { - self.events.send(Event::Join(self.id, code))?; - Ok(RpcMessage::Joining(())) - }, - RpcRequest::InstanceLeave {} => { - self.events.send(Event::Leave(self.id))?; - Ok(RpcMessage::Processing(())) - }, - - RpcRequest::InstanceChat { instance_id, index } => { - if !account.subscribed { - return Err(err_msg("subscribe to unlock chat")) - } - - let wheel = account::chat_wheel(&db, account.id)?; - - if let Some(c) = wheel.get(index) { - self.events.send(Event::Chat(self.id, instance_id, c.to_string()))?; - } else { - return Err(err_msg("invalid chat index")); - } - - Ok(RpcMessage::Processing(())) - }, - _ => { - // all good, let's make a tx and process - let mut tx = db.transaction()?; - - let res = match v { - RpcRequest::AccountState {} => - Ok(RpcMessage::AccountState(account.clone())), - RpcRequest::AccountConstructs {} => - Ok(RpcMessage::AccountConstructs(account::constructs(&mut tx, &account)?)), - RpcRequest::AccountInstances {} => - Ok(RpcMessage::AccountInstances(account::account_instances(&mut tx, account)?)), - RpcRequest::AccountSetTeam { ids } => - Ok(RpcMessage::AccountTeam(account::set_team(&mut tx, &account, ids)?)), - - RpcRequest::EmailState {} => - Ok(RpcMessage::EmailState(mail::select_account(&db, account.id)?)), - - RpcRequest::SubscriptionState {} => - Ok(RpcMessage::SubscriptionState(payments::account_subscription(&db, &self.stripe, &account)?)), - - // RpcRequest::AccountShop {} => - // Ok(RpcMessage::AccountShop(mtx::account_shop(&mut tx, &account)?)), - - // RpcRequest::ConstructDelete" => handle_construct_delete(data, &mut tx, account), - - RpcRequest::GameState { id } => - Ok(RpcMessage::GameState(game_state(&mut tx, account, id)?)), - - RpcRequest::GameSkill { game_id, construct_id, target_construct_id, skill } => - Ok(RpcMessage::GameState(game_skill(&mut tx, account, game_id, construct_id, target_construct_id, skill)?)), - - RpcRequest::GameSkillClear { game_id } => - Ok(RpcMessage::GameState(game_skill_clear(&mut tx, account, game_id)?)), - - RpcRequest::GameReady { id } => - Ok(RpcMessage::GameState(game_ready(&mut tx, account, id)?)), - - RpcRequest::GameConcede { game_id } => - Ok(RpcMessage::GameState(game_concede(&mut tx, account, game_id)?)), - - RpcRequest::GameOfferDraw { game_id } => - Ok(RpcMessage::GameState(game_offer_draw(&mut tx, account, game_id)?)), - - RpcRequest::InstancePractice {} => - Ok(RpcMessage::InstanceState(instance_practice(&mut tx, account)?)), - - // these two can return GameState or InstanceState - RpcRequest::InstanceReady { instance_id } => - Ok(instance_ready(&mut tx, account, instance_id)?), - RpcRequest::InstanceState { instance_id } => - Ok(instance_state(&mut tx, instance_id)?), - RpcRequest::InstanceAbandon { instance_id } => - Ok(instance_abandon(&mut tx, account, instance_id)?), - - RpcRequest::VboxBuy { instance_id, group, index, construct_id } => - Ok(RpcMessage::InstanceState(vbox_buy(&mut tx, account, instance_id, group, index, construct_id)?)), - - RpcRequest::VboxApply { instance_id, construct_id, index } => - Ok(RpcMessage::InstanceState(vbox_apply(&mut tx, account, instance_id, construct_id, index)?)), - - RpcRequest::VboxCombine { instance_id, inv_indices, vbox_indices } => - Ok(RpcMessage::InstanceState(vbox_combine(&mut tx, account, instance_id, inv_indices, vbox_indices)?)), - - RpcRequest::VboxRefill { instance_id } => - Ok(RpcMessage::InstanceState(vbox_refill(&mut tx, account, instance_id)?)), - - RpcRequest::VboxRefund { instance_id, index } => - Ok(RpcMessage::InstanceState(vbox_refund(&mut tx, account, instance_id, index)?)), - - RpcRequest::VboxUnequip { instance_id, construct_id, target } => - Ok(RpcMessage::InstanceState(vbox_unequip(&mut tx, account, instance_id, construct_id, target, None)?)), - - RpcRequest::VboxUnequipApply { instance_id, construct_id, target, target_construct_id } => - Ok(RpcMessage::InstanceState(vbox_unequip(&mut tx, account, instance_id, construct_id, target, Some(target_construct_id))?)), - - RpcRequest::MtxConstructSpawn {} => - Ok(RpcMessage::ConstructSpawn(mtx::new_construct(&mut tx, account)?)), - - RpcRequest::MtxConstructApply { mtx, construct_id, name } => - Ok(RpcMessage::AccountTeam(mtx::apply(&mut tx, account, mtx, construct_id, name)?)), - - RpcRequest::MtxAccountApply { mtx } => - Ok(RpcMessage::AccountState(mtx::account_apply(&mut tx, account, mtx)?)), - - RpcRequest::MtxBuy { mtx } => - Ok(RpcMessage::AccountShop(mtx::buy(&mut tx, account, mtx)?)), - - RpcRequest::SubscriptionEnding { ending } => - Ok(RpcMessage::SubscriptionState(payments::subscription_ending(&mut tx, &self.stripe, account, ending)?)), - - _ => Err(format_err!("unknown request request={:?}", request)), - }; - - tx.commit()?; - res - } - }; - - info!("request={:?} account={:?} duration={:?}", request, account.name, begin.elapsed()); - - return response; - }, - Err(e) => { - warn!("{:?}", e); - Err(err_msg("invalid message")) - }, - } - } - // this is where last minute processing happens // use it to modify outgoing messages, update subs, serialize in some way... fn send(&self, msg: RpcMessage) -> Result<(), Error> { - let msg = match self.account { - Some(ref a) => match msg { - RpcMessage::InstanceState(v) => RpcMessage::InstanceState(v.redact(a.id)), - RpcMessage::AccountInstances(v) => - RpcMessage::AccountInstances(v.into_iter().map(|i| i.redact(a.id)).collect()), - RpcMessage::GameState(v) => RpcMessage::GameState(v.redact(a.id)), - _ => msg, - }, - None => msg, + let msg = match msg { + RpcMessage::InstanceState(v) => RpcMessage::InstanceState(v.redact(self.id)), + RpcMessage::AccountInstances(v) => + RpcMessage::AccountInstances(v.into_iter().map(|i| i.redact(self.id)).collect()), + RpcMessage::GameState(v) => RpcMessage::GameState(v.redact(self.id)), + _ => msg, }; self.ws.send(msg).unwrap(); @@ -347,48 +163,8 @@ impl Connection { // when it encounters errors impl Handler for Connection { fn on_open(&mut self, _: ws::Handshake) -> ws::Result<()> { - info!("websocket connected account={:?}", self.account); - - // tell events we have connected - self.events.send(Event::Connect(self.id, self.account.clone(), self.ws.clone())).unwrap(); - - // if user logged in do some prep work - if let Some(ref a) = self.account { - self.send(RpcMessage::AccountState(a.clone())).unwrap(); - self.events.send(Event::Subscribe(self.id, a.id)).unwrap(); - - // check if they have an image that needs to be generated - account::img_check(&a).unwrap(); - - let db = self.pool.get().unwrap(); - let mut tx = db.transaction().unwrap(); - - // send account constructs - let account_constructs = account::constructs(&mut tx, a).unwrap(); - self.send(RpcMessage::AccountConstructs(account_constructs)).unwrap(); - - // get account instances - // and send them to the client - let account_instances = account::account_instances(&mut tx, a).unwrap(); - self.send(RpcMessage::AccountInstances(account_instances)).unwrap(); - - let shop = mtx::account_shop(&mut tx, &a).unwrap(); - self.send(RpcMessage::AccountShop(shop)).unwrap(); - - let team = account::team(&mut tx, &a).unwrap(); - self.send(RpcMessage::AccountTeam(team)).unwrap(); - - let wheel = account::chat_wheel(&db, a.id).unwrap(); - self.send(RpcMessage::ChatWheel(wheel)).unwrap(); - - if let Some(instance) = account::tutorial(&mut tx, &a).unwrap() { - self.send(RpcMessage::InstanceState(instance)).unwrap(); - } - - // tx should do nothing - tx.commit().unwrap(); - } - + let db = self.pool.get().unwrap(); + self.user.connected(&db, &self.events, &self.ws).unwrap(); Ok(()) } @@ -398,23 +174,9 @@ impl Handler for Connection { let begin = Instant::now(); let db_connection = self.pool.get().unwrap(); - match self.receive(msg, &db_connection, begin) { - Ok(reply) => { - // if the user queries the state of something - // we tell events to push updates to them - match reply { - RpcMessage::AccountState(ref v) => { - self.account = Some(v.clone()); - self.events.send(Event::Subscribe(self.id, v.id)).unwrap() - }, - RpcMessage::GameState(ref v) => - self.events.send(Event::Subscribe(self.id, v.id)).unwrap(), - RpcMessage::InstanceState(ref v) => - self.events.send(Event::Subscribe(self.id, v.id)).unwrap(), - _ => (), - }; - - self.send(reply).unwrap(); + match self.user.receive(msg, &db_connection, begin, &self.events, &self.stripe) { + Ok(msg) => { + self.user.send(msg, &self.events, &self.ws).unwrap(); }, Err(e) => { warn!("{:?}", e); @@ -428,7 +190,7 @@ impl Handler for Connection { } fn on_close(&mut self, _: CloseCode, _: &str) { - info!("websocket disconnected account={:?}", self.account); + info!("websocket disconnected id={:?}", self.id); self.events.send(Event::Disconnect(self.id)).unwrap(); } @@ -456,21 +218,19 @@ impl Handler for Connection { if cookie.name() == TOKEN_HEADER { let db = self.pool.get().unwrap(); match account::from_token(&db, &cookie.value().to_string()) { - Ok(a) => self.account = Some(a), + Ok(a) => self.user = Box::new(Authorised { id: a.id, account: a }), Err(_) => return unauth(), } } }; - }; + } Ok(res) } } pub fn start(pool: PgPool, events_tx: CbSender, stripe: StripeClient) { - let mut rng = thread_rng(); - - let ws = Builder::new() + let _ws = Builder::new() .with_settings(Settings { max_connections: 10_000, ..Settings::default() @@ -500,14 +260,17 @@ pub fn start(pool: PgPool, events_tx: CbSender, stripe: StripeClient) { } }); + let anon_account = Account::anonymous(); + let id = anon_account.id; + DeflateHandler::new( Connection { - id: rng.gen::(), - account: None, + id, ws: tx, pool: pool.clone(), stripe: stripe.clone(), events: events_tx.clone(), + user: Box::new(Anonymous { id, account: anon_account, game: None, instance: None }) } ) }) diff --git a/server/src/user_anonymous.rs b/server/src/user_anonymous.rs new file mode 100644 index 00000000..51bd4a4a --- /dev/null +++ b/server/src/user_anonymous.rs @@ -0,0 +1,148 @@ +use std::time::Instant; +use uuid::Uuid; + +use failure::Error; +use failure::err_msg; + +use crossbeam_channel::{Sender as CbSender}; + +use serde_cbor::{from_slice}; + +use stripe::{Client as StripeClient}; + +use account::{Account}; +use pg::{Db}; +use pg; +use events::{Event}; +use rpc::{RpcMessage, RpcRequest, User}; + +use mnml_core::game::Game; +use mnml_core::item::item_info; +use mnml_core::instance::Instance; + +#[derive(Debug,Clone)] +pub struct Anonymous { + pub account: Account, + pub id: Uuid, + pub instance: Option, + pub game: Option, +} + +impl User for Anonymous { + fn send(&mut self, msg: RpcMessage, _events: &CbSender, ws: &CbSender) -> Result<(), Error> { + // if the user queries the state of something + // we tell events to push updates to them + match msg { + RpcMessage::GameState(ref v) => + self.game = Some(v.clone()), + RpcMessage::InstanceState(ref v) => + self.instance = Some(v.clone()), + _ => (), + }; + + ws.send(msg)?; + + Ok(()) + } + + fn connected(&mut self, _db: &Db, events: &CbSender, ws: &CbSender) -> Result<(), Error> { + info!("anonymous connection"); + + self.send(RpcMessage::AccountState(self.account.clone()), events, ws)?; + self.send(RpcMessage::ItemInfo(item_info()), events, ws)?; + self.send(RpcMessage::InstanceState(pg::instance_demo(&self.account)?), events, ws)?; + + Ok(()) + } + + fn receive(&mut self, data: Vec, _db: &Db, _begin: Instant, _events: &CbSender, _stripe: &StripeClient) -> Result { + match from_slice::(&data) { + Ok(v) => { + let mut instance = match self.instance { + Some(ref i) => i.clone(), + None => return Err(err_msg("instance missing")), + }; + + let game = match self.game { + Some(ref i) => Some(i.clone()), + None => None + }; + + match v { + RpcRequest::Ping {} => return Ok(RpcMessage::Pong(())), + + RpcRequest::InstanceReady { instance_id: _ } => { + match instance.player_ready(self.account.id)? { + Some(g) => Ok(RpcMessage::GameState(g)), + None => Ok(RpcMessage::InstanceState(instance)), + } + }, + + RpcRequest::InstanceState { instance_id: _ } => + Ok(RpcMessage::InstanceState(instance)), + + RpcRequest::InstanceAbandon { instance_id: _ } => + Err(err_msg("don't give up!")), + + RpcRequest::VboxBuy { instance_id: _, group, index, construct_id } => + Ok(RpcMessage::InstanceState(instance.vbox_buy(self.account.id, group, index, construct_id)?)), + + RpcRequest::VboxApply { instance_id: _, construct_id, index } => + Ok(RpcMessage::InstanceState(instance.vbox_apply(self.account.id, index, construct_id)?)), + + RpcRequest::VboxCombine { instance_id: _, inv_indices, vbox_indices } => + Ok(RpcMessage::InstanceState(instance.vbox_combine(self.account.id, inv_indices, vbox_indices)?)), + + RpcRequest::VboxRefill { instance_id: _ } => + Ok(RpcMessage::InstanceState(instance.vbox_refill(self.account.id)?)), + + RpcRequest::VboxRefund { instance_id: _, index } => + Ok(RpcMessage::InstanceState(instance.vbox_refund(self.account.id, index)?)), + + RpcRequest::VboxUnequip { instance_id: _, construct_id, target } => + Ok(RpcMessage::InstanceState(instance.vbox_unequip(self.account.id, target, construct_id, None)?)), + + RpcRequest::VboxUnequipApply { instance_id: _, construct_id, target, target_construct_id } => + Ok(RpcMessage::InstanceState(instance.vbox_unequip(self.account.id, target, construct_id, Some(target_construct_id))?)), + + RpcRequest::GameState { id: _ } => + Ok(RpcMessage::GameState(game.unwrap())), + + RpcRequest::GameSkill { game_id: _, construct_id, target_construct_id, skill } => { + let mut game = game.unwrap(); + game.add_skill(self.account.id, construct_id, target_construct_id, skill)?; + Ok(RpcMessage::GameState(game)) + }, + + RpcRequest::GameSkillClear { game_id: _ } => { + let mut game = game.unwrap(); + game.clear_skill(self.account.id)?; + Ok(RpcMessage::GameState(game)) + }, + + RpcRequest::GameReady { id: _ } => { + let mut game = game.unwrap(); + game.player_ready(self.account.id)?; + if game.skill_phase_finished() { + game = game.resolve_phase_start(); + } + + Ok(RpcMessage::GameState(game)) + }, + + RpcRequest::GameConcede { game_id: _ } => + Ok(RpcMessage::GameState(game.unwrap().concede(self.account.id)?)), + + RpcRequest::GameOfferDraw { game_id: _ } => + Ok(RpcMessage::GameState(game.unwrap().offer_draw(self.account.id)?)), + + _ => Err(format_err!("unhandled anonymous request request={:?}", v)), + } + }, + Err(e) => { + warn!("{:?}", e); + Err(err_msg("invalid message")) + }, + } + } +} diff --git a/server/src/user_authenticated.rs b/server/src/user_authenticated.rs new file mode 100644 index 00000000..c92400a3 --- /dev/null +++ b/server/src/user_authenticated.rs @@ -0,0 +1,264 @@ +use mnml_core::mob::anim_test_game; +use mnml_core::item::item_info; +use std::time::Instant; +use uuid::Uuid; + +use failure::Error; +use failure::err_msg; + +use crossbeam_channel::{Sender as CbSender}; +use stripe::{Client as StripeClient}; + +use serde_cbor::{from_slice}; + +use pg::{ + game_concede, + game_offer_draw, + game_ready, + game_skill, + game_skill_clear, + game_state, + instance_abandon, + instance_practice, + instance_ready, + instance_state, + vbox_apply, + vbox_buy, + vbox_combine, + vbox_refill, + vbox_refund, + vbox_unequip, +}; + +use account::{Account}; +use account; +use events::{Event}; + +use mtx; +use mail; +use payments; +use pg::{Db}; +use rpc::{RpcMessage, RpcRequest, User}; + + +#[derive(Debug,Clone)] +pub struct Authorised { + pub account: Account, + pub id: Uuid +} + +impl User for Authorised { + fn send(&mut self, msg: RpcMessage, events: &CbSender, ws: &CbSender) -> Result<(), Error> { + // if the user queries the state of something + // we tell events to push updates to them + match msg { + RpcMessage::AccountState(ref v) => { + events.send(Event::Subscribe(self.id, v.id))? + }, + RpcMessage::GameState(ref v) => + events.send(Event::Subscribe(self.id, v.id))?, + RpcMessage::InstanceState(ref v) => + events.send(Event::Subscribe(self.id, v.id))?, + _ => (), + }; + + ws.send(msg)?; + + Ok(()) + } + + fn connected(&mut self, db: &Db, events: &CbSender, ws: &CbSender) -> Result<(), Error> { + info!("authenticated connection account={:?}", self.account); + let a = &self.account; + + // tell events we have connected + events.send(Event::Connect(self.id, a.clone(), ws.clone()))?; + + ws.send(RpcMessage::AccountState(a.clone()))?; + events.send(Event::Subscribe(self.id, a.id))?; + + // check if they have an image that needs to be generated + account::img_check(&a)?; + + let mut tx = db.transaction()?; + + // send account constructs + let account_constructs = account::constructs(&mut tx, &a)?; + ws.send(RpcMessage::AccountConstructs(account_constructs))?; + + // get account instances + // and send them to the client + let account_instances = account::account_instances(&mut tx, &a)?; + ws.send(RpcMessage::AccountInstances(account_instances))?; + + let shop = mtx::account_shop(&mut tx, &a)?; + ws.send(RpcMessage::AccountShop(shop))?; + + let team = account::team(&mut tx, &a)?; + ws.send(RpcMessage::AccountTeam(team))?; + + let wheel = account::chat_wheel(&db, a.id)?; + ws.send(RpcMessage::ChatWheel(wheel))?; + + if let Some(instance) = account::tutorial(&mut tx, &a)? { + ws.send(RpcMessage::InstanceState(instance))?; + } + + // tx should do nothing + tx.commit()?; + + Ok(()) + } + + fn receive(&mut self, data: Vec, db: &Db, begin: Instant, events: &CbSender, stripe: &StripeClient) -> Result { + // cast the msg to this type to receive method name + match from_slice::(&data) { + Ok(v) => { + let request = v.clone(); + let response = match v { + RpcRequest::Ping {} => return Ok(RpcMessage::Pong(())), + RpcRequest::ItemInfo {} => return Ok(RpcMessage::ItemInfo(item_info())), + RpcRequest::DevResolve { skill } => + return Ok(RpcMessage::GameState(anim_test_game(skill))), + + RpcRequest::InstanceQueue {} => { + events.send(Event::Queue(self.id))?; + Ok(RpcMessage::QueueRequested(())) + }, + RpcRequest::InstanceInvite {} => { + events.send(Event::Invite(self.id))?; + Ok(RpcMessage::InviteRequested(())) + }, + RpcRequest::InstanceJoin { code } => { + events.send(Event::Join(self.id, code))?; + Ok(RpcMessage::Joining(())) + }, + RpcRequest::InstanceLeave {} => { + events.send(Event::Leave(self.id))?; + Ok(RpcMessage::Processing(())) + }, + + RpcRequest::InstanceChat { instance_id, index } => { + if !self.account.subscribed { + return Err(err_msg("subscribe to unlock chat")) + } + + let wheel = account::chat_wheel(&db, self.account.id)?; + + if let Some(c) = wheel.get(index) { + events.send(Event::Chat(self.id, instance_id, c.to_string()))?; + } else { + return Err(err_msg("invalid chat index")); + } + + Ok(RpcMessage::Processing(())) + }, + _ => { + // all good, let's make a tx and process + let mut tx = db.transaction()?; + + let res = match v { + RpcRequest::AccountState {} => + Ok(RpcMessage::AccountState(self.account.clone())), + RpcRequest::AccountConstructs {} => + Ok(RpcMessage::AccountConstructs(account::constructs(&mut tx, &self.account)?)), + RpcRequest::AccountInstances {} => + Ok(RpcMessage::AccountInstances(account::account_instances(&mut tx, &self.account)?)), + RpcRequest::AccountSetTeam { ids } => + Ok(RpcMessage::AccountTeam(account::set_team(&mut tx, &self.account, ids)?)), + + RpcRequest::EmailState {} => + Ok(RpcMessage::EmailState(mail::select_account(&db, self.account.id)?)), + + RpcRequest::SubscriptionState {} => + Ok(RpcMessage::SubscriptionState(payments::account_subscription(db, stripe, &self.account)?)), + + // RpcRequest::AccountShop {} => + // Ok(RpcMessage::AccountShop(mtx::account_shop(&mut tx, &account)?)), + + // RpcRequest::ConstructDelete" => handle_construct_delete(data, &mut tx, account), + + RpcRequest::GameState { id } => + Ok(RpcMessage::GameState(game_state(&mut tx, &self.account, id)?)), + + RpcRequest::GameSkill { game_id, construct_id, target_construct_id, skill } => + Ok(RpcMessage::GameState(game_skill(&mut tx, &self.account, game_id, construct_id, target_construct_id, skill)?)), + + RpcRequest::GameSkillClear { game_id } => + Ok(RpcMessage::GameState(game_skill_clear(&mut tx, &self.account, game_id)?)), + + RpcRequest::GameReady { id } => + Ok(RpcMessage::GameState(game_ready(&mut tx, &self.account, id)?)), + + RpcRequest::GameConcede { game_id } => + Ok(RpcMessage::GameState(game_concede(&mut tx, &self.account, game_id)?)), + + RpcRequest::GameOfferDraw { game_id } => + Ok(RpcMessage::GameState(game_offer_draw(&mut tx, &self.account, game_id)?)), + + RpcRequest::InstancePractice {} => + Ok(RpcMessage::InstanceState(instance_practice(&mut tx, &self.account)?)), + + // these two can return GameState or InstanceState + RpcRequest::InstanceReady { instance_id } => + Ok(instance_ready(&mut tx, &self.account, instance_id)?), + RpcRequest::InstanceState { instance_id } => + Ok(instance_state(&mut tx, instance_id)?), + RpcRequest::InstanceAbandon { instance_id } => + Ok(instance_abandon(&mut tx, &self.account, instance_id)?), + + RpcRequest::VboxBuy { instance_id, group, index, construct_id } => + Ok(RpcMessage::InstanceState(vbox_buy(&mut tx, &self.account, instance_id, group, index, construct_id)?)), + + RpcRequest::VboxApply { instance_id, construct_id, index } => + Ok(RpcMessage::InstanceState(vbox_apply(&mut tx, &self.account, instance_id, construct_id, index)?)), + + RpcRequest::VboxCombine { instance_id, inv_indices, vbox_indices } => + Ok(RpcMessage::InstanceState(vbox_combine(&mut tx, &self.account, instance_id, inv_indices, vbox_indices)?)), + + RpcRequest::VboxRefill { instance_id } => + Ok(RpcMessage::InstanceState(vbox_refill(&mut tx, &self.account, instance_id)?)), + + RpcRequest::VboxRefund { instance_id, index } => + Ok(RpcMessage::InstanceState(vbox_refund(&mut tx, &self.account, instance_id, index)?)), + + RpcRequest::VboxUnequip { instance_id, construct_id, target } => + Ok(RpcMessage::InstanceState(vbox_unequip(&mut tx, &self.account, instance_id, construct_id, target, None)?)), + + RpcRequest::VboxUnequipApply { instance_id, construct_id, target, target_construct_id } => + Ok(RpcMessage::InstanceState(vbox_unequip(&mut tx, &self.account, instance_id, construct_id, target, Some(target_construct_id))?)), + + RpcRequest::MtxConstructSpawn {} => + Ok(RpcMessage::ConstructSpawn(mtx::new_construct(&mut tx, &self.account)?)), + + RpcRequest::MtxConstructApply { mtx, construct_id, name } => + Ok(RpcMessage::AccountTeam(mtx::apply(&mut tx, &self.account, mtx, construct_id, name)?)), + + RpcRequest::MtxAccountApply { mtx } => + Ok(RpcMessage::AccountState(mtx::account_apply(&mut tx, &self.account, mtx)?)), + + RpcRequest::MtxBuy { mtx } => + Ok(RpcMessage::AccountShop(mtx::buy(&mut tx, &self.account, mtx)?)), + + RpcRequest::SubscriptionEnding { ending } => + Ok(RpcMessage::SubscriptionState(payments::subscription_ending(&mut tx, stripe, &self.account, ending)?)), + + _ => Err(format_err!("unknown request request={:?}", request)), + }; + + tx.commit()?; + res + } + }; + + info!("request={:?} account={:?} duration={:?}", request, self.account.name, begin.elapsed()); + + return response; + }, + Err(e) => { + warn!("{:?}", e); + Err(err_msg("invalid message")) + }, + } + } +}