This commit is contained in:
ntr 2019-12-22 12:40:05 +10:00
parent 1cc29f73d1
commit 445e8084e0
9 changed files with 512 additions and 315 deletions

View File

@ -144,8 +144,8 @@ impl Instance {
.collect::<Vec<Uuid>>()
}
// time out lobbies that have been open too long
pub fn upkeep(mut self) -> (Instance, Option<Game>) {
// time out lobbies that have been open too long
if self.phase == InstancePhase::Lobby && self.phase_timed_out() {
self.finish();
return (self, None);

View File

@ -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();

View File

@ -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<postgres::rows::Row<'a>> for Account {

View File

@ -18,7 +18,7 @@ use warden::{GameEvent};
use mail::Mail;
pub type EventsTx = Sender<Event>;
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<Account>, Sender<RpcMessage>),
Connect(Id, Account, Sender<RpcMessage>),
Disconnect(Id),
Subscribe(Id, Uuid),
Unsubscribe(Id, Uuid),
@ -64,7 +64,6 @@ pub enum Event {
struct WsClient {
id: Id,
account: Option<Uuid>,
tx: Sender<RpcMessage>,
subs: HashSet<Uuid>,
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::<Vec<&str>>().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<Uuid, String> = 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<Uuid, String> = 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)));

View File

@ -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};

View File

@ -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<Inst
Ok(instance)
}
pub fn instance_demo(account: &Account) -> Result<Instance, Error> {
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<Instance, Error> {
let mut instance = Instance::new()
// TODO generate nice game names

View File

@ -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<u8>, db: &Db, begin: Instant, events: &CbSender<Event>, stripe: &StripeClient) -> Result<RpcMessage, Error>;
fn connected(&mut self, db: &Db, events: &CbSender<Event>, ws: &CbSender<RpcMessage>) -> Result<(), Error>;
fn send(&mut self, msg: RpcMessage, events: &CbSender<Event>, ws: &CbSender<RpcMessage>) -> Result<(), Error>;
}
struct Connection {
pub id: usize,
pub id: Uuid,
pub ws: CbSender<RpcMessage>,
pool: PgPool,
stripe: StripeClient,
account: Option<Account>,
// account: Option<Account>,
user: Box<dyn User>,
events: CbSender<Event>,
}
impl Connection {
fn receive(&self, data: Vec<u8>, db: &Db, begin: Instant) -> Result<RpcMessage, Error> {
// cast the msg to this type to receive method name
match from_slice::<RpcRequest>(&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<Event>, 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<Event>, stripe: StripeClient) {
}
});
let anon_account = Account::anonymous();
let id = anon_account.id;
DeflateHandler::new(
Connection {
id: rng.gen::<usize>(),
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 })
}
)
})

View File

@ -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<Instance>,
pub game: Option<Game>,
}
impl User for Anonymous {
fn send(&mut self, msg: RpcMessage, _events: &CbSender<Event>, ws: &CbSender<RpcMessage>) -> 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<Event>, ws: &CbSender<RpcMessage>) -> 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<u8>, _db: &Db, _begin: Instant, _events: &CbSender<Event>, _stripe: &StripeClient) -> Result<RpcMessage, Error> {
match from_slice::<RpcRequest>(&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"))
},
}
}
}

View File

@ -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<Event>, ws: &CbSender<RpcMessage>) -> 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<Event>, ws: &CbSender<RpcMessage>) -> 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<u8>, db: &Db, begin: Instant, events: &CbSender<Event>, stripe: &StripeClient) -> Result<RpcMessage, Error> {
// cast the msg to this type to receive method name
match from_slice::<RpcRequest>(&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"))
},
}
}
}