diff --git a/client/assets/styles/menu.less b/client/assets/styles/menu.less index 670a86be..ff035799 100644 --- a/client/assets/styles/menu.less +++ b/client/assets/styles/menu.less @@ -100,6 +100,12 @@ section { } } +.block-text { + letter-spacing: 0.25em; + text-transform: uppercase; + text-align: center; +} + .list { margin-bottom: 2em; diff --git a/client/src/components/front.page.jsx b/client/src/components/front.page.jsx index 32cf3e16..defc62e1 100644 --- a/client/src/components/front.page.jsx +++ b/client/src/components/front.page.jsx @@ -14,6 +14,7 @@ const addState = connect( const { ws, account, + tutorial, } = state; function sendInstancePractice() { @@ -21,6 +22,7 @@ const addState = connect( } return { + promptRegister: tutorial === 99, // see events account, sendInstancePractice, }; @@ -30,6 +32,7 @@ const addState = connect( function Play(args) { const { account, + promptRegister, sendInstancePractice, } = args; @@ -47,6 +50,17 @@ function Play(args) { ); const list = () => { + + if (promptRegister) { + return ( +
+

You just won your first round of MNML.

+

Register below to play a real Bo5 against other players, play a practice round, customise your team & more...

+

glhf

+
+ ) + } + return (
diff --git a/client/src/components/game.ctrl.btns.jsx b/client/src/components/game.ctrl.btns.jsx index 557e0915..9b898345 100644 --- a/client/src/components/game.ctrl.btns.jsx +++ b/client/src/components/game.ctrl.btns.jsx @@ -9,6 +9,7 @@ const addState = connect( ws, game, account, + authenticated, chatShow, animating, } = state; @@ -33,6 +34,7 @@ const addState = connect( return { game, account, + authenticated, chatShow, sendAbandon, sendGameSkillClear, @@ -65,6 +67,7 @@ function GameCtrlBtns(args) { animating, account, chatShow, + authenticated, getInstanceState, sendGameSkillClear, @@ -77,7 +80,9 @@ function GameCtrlBtns(args) { const finished = game.phase === 'Finished'; function quitClick() { - getInstanceState(); + if (authenticated) { + getInstanceState(); + } quit(); } diff --git a/client/src/components/game.ctrl.btns.top.jsx b/client/src/components/game.ctrl.btns.top.jsx index 3128d40d..f5e67fa2 100644 --- a/client/src/components/game.ctrl.btns.top.jsx +++ b/client/src/components/game.ctrl.btns.top.jsx @@ -9,6 +9,7 @@ const addState = connect( ws, game, animating, + authenticated, account, } = state; @@ -27,6 +28,7 @@ const addState = connect( return { game, account, + authenticated, sendAbandon, sendDraw, @@ -50,6 +52,7 @@ function GameCtrlTopBtns(args) { const { game, account, + authenticated, leave, sendAbandon, @@ -82,6 +85,11 @@ function GameCtrlTopBtns(args) { setTimeout(() => this.setState({ concedeState: false }), 2000); }; + const authBtn = btn => { + if (authenticated) return btn; + return + } + const abandonClasses = `abandon ${abandonState ? 'confirming' : ''}`; const abandonText = abandonState ? 'Confirm' : 'Abandon'; const abandonAction = abandonState ? sendAbandon : abandonStateTrue; @@ -102,9 +110,9 @@ function GameCtrlTopBtns(args) { return (
- {abandonBtn} - {concedeBtn} - {drawBtn} + {authBtn(abandonBtn)} + {authBtn(concedeBtn)} + {authBtn(drawBtn)}
); } diff --git a/client/src/components/instance.ctrl.top.btns.jsx b/client/src/components/instance.ctrl.top.btns.jsx index 7fc4f018..319298d7 100644 --- a/client/src/components/instance.ctrl.top.btns.jsx +++ b/client/src/components/instance.ctrl.top.btns.jsx @@ -7,6 +7,7 @@ const addState = connect( function receiveState(state) { const { ws, + authenticated, instance, tutorial, } = state; @@ -17,6 +18,7 @@ const addState = connect( return { instance, + authenticated, tutorial, sendAbandon, }; @@ -39,6 +41,7 @@ const addState = connect( function InstanceTopBtns(args) { const { instance, + authenticated, leave, sendAbandon, @@ -61,9 +64,16 @@ function InstanceTopBtns(args) { const abandonBtn = ; const leaveBtn = ; + const finalBtn = () => { + // disable for tutorial mode + if (!authenticated) return ; + if (finished) return leaveBtn; + return abandonBtn; + } + return (
- {finished ? leaveBtn : abandonBtn} + {finalBtn()}
); } diff --git a/client/src/components/welcome.jsx b/client/src/components/welcome.jsx index 8270cdb2..d32c5997 100644 --- a/client/src/components/welcome.jsx +++ b/client/src/components/welcome.jsx @@ -1,13 +1,31 @@ // eslint-disable-next-line const preact = require('preact'); +const { connect } = require('preact-redux'); const Login = require('./welcome.login'); const Register = require('./welcome.register'); const Help = require('./welcome.help'); // const About = require('./welcome.about'); -function Welcome() { - const page = this.state.page || 'login'; +const addState = connect( + function receiveState(state) { + const { + tutorial, + } = state; + + return { + promptRegister: tutorial === 99, // see events + }; + }, +); + +function Welcome(args) { + + const { + promptRegister, + } = args; + + const page = this.state.page || promptRegister && 'register' || 'login'; const pageEl = () => { if (page === 'login') return ; @@ -45,4 +63,4 @@ function Welcome() { ); } -module.exports = Welcome; +module.exports = addState(Welcome); diff --git a/client/src/events.jsx b/client/src/events.jsx index 11e791ea..a0e6a307 100644 --- a/client/src/events.jsx +++ b/client/src/events.jsx @@ -218,6 +218,10 @@ function registerEvents(store) { store.dispatch(actions.setTutorial(1)); } + function promptRegister() { + store.dispatch(actions.setTutorial(99)); + store.dispatch(actions.setInstance(null)); + } window.addEventListener('hashchange', urlHashChange, false); @@ -250,6 +254,7 @@ function registerEvents(store) { setWs, startTutorial, + promptRegister, urlHashChange, diff --git a/client/src/socket.jsx b/client/src/socket.jsx index 30e16b59..a7e2916c 100644 --- a/client/src/socket.jsx +++ b/client/src/socket.jsx @@ -301,6 +301,7 @@ function createSocket(events) { // Joining: () => events.notify('Searching for instance...'), StartTutorial: () => events.startTutorial(), + PromptRegister: () => events.promptRegister(), Processing: () => true, Error: errHandler, diff --git a/server/src/pg.rs b/server/src/pg.rs index 97444249..995c727b 100644 --- a/server/src/pg.rs +++ b/server/src/pg.rs @@ -695,11 +695,17 @@ pub fn instance_practice(tx: &mut Transaction, account: &Account) -> Result Result { - let bot = bot_player(); + let mut bot = bot_player(); let bot_id = bot.id; // generate imgs for the client to see - for c in bot.constructs.iter() { + for c in bot.constructs.iter_mut() { + + // smash these nubs + c.green_life.force(64); + c.red_life.force(0); + c.blue_life.force(0); + img::shapes_write(c.img)?; } @@ -709,6 +715,7 @@ pub fn instance_demo(account: &Account) -> Result { let player = anon_player(account.id); + // smash these noobs for c in player.constructs.iter() { img::shapes_write(c.img)?; } diff --git a/server/src/rpc.rs b/server/src/rpc.rs index 7ab789f1..9ffe3bdc 100644 --- a/server/src/rpc.rs +++ b/server/src/rpc.rs @@ -2,7 +2,6 @@ use mnml_core::item::ItemInfoCtr; use mnml_core::instance::ChatState; use std::collections::HashMap; -use std::time::{Instant}; use std::thread::{spawn}; use std::str; @@ -23,7 +22,7 @@ use account; use events::{Event}; use user_anonymous::{Anonymous}; -use user_authenticated::{Authorised}; +use user_authenticated::{Authenticated}; use mnml_core::construct::{Construct}; use mnml_core::game::{Game}; @@ -37,7 +36,7 @@ use mnml_core::instance::{Instance}; use mtx; use mail::Email; -use pg::{Db}; + use pg::{PgPool}; use http::{AUTH_CLEAR, TOKEN_HEADER}; @@ -63,6 +62,7 @@ pub enum RpcMessage { Pong(()), StartTutorial(()), + PromptRegister(()), QueueRequested(()), QueueJoined(()), @@ -126,9 +126,9 @@ pub enum RpcRequest { } 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>; + fn receive(&mut self, data: Vec, stripe: &StripeClient) -> Result; + fn connected(&mut self) -> Result<(), Error>; + fn send(&mut self, msg: RpcMessage) -> Result<(), Error>; } struct Connection { @@ -165,20 +165,16 @@ impl Connection { // when it encounters errors impl Handler for Connection { fn on_open(&mut self, _: ws::Handshake) -> ws::Result<()> { - let db = self.pool.get().unwrap(); - self.user.connected(&db, &self.events, &self.ws).unwrap(); + self.user.connected().unwrap(); Ok(()) } fn on_message(&mut self, msg: Message) -> ws::Result<()> { match msg { Message::Binary(msg) => { - let begin = Instant::now(); - let db_connection = self.pool.get().unwrap(); - - match self.user.receive(msg, &db_connection, begin, &self.events, &self.stripe) { + match self.user.receive(msg, &self.stripe) { Ok(msg) => { - self.user.send(msg, &self.events, &self.ws).unwrap(); + self.user.send(msg).unwrap(); }, Err(e) => { warn!("{:?}", e); @@ -220,7 +216,7 @@ 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.user = Box::new(Authorised { id: a.id, account: a }), + Ok(a) => self.user = Box::new(Authenticated::new(a, self.ws.clone(), self.events.clone(), self.pool.clone())), Err(_) => return unauth(), } } @@ -268,11 +264,11 @@ pub fn start(pool: PgPool, events_tx: CbSender, stripe: StripeClient) { DeflateHandler::new( Connection { id, - ws: tx, + ws: tx.clone(), pool: pool.clone(), stripe: stripe.clone(), events: events_tx.clone(), - user: Box::new(Anonymous { id, account: anon_account, game: None, instance: None }) + user: Box::new(Anonymous::new(anon_account, tx)) } ) }) diff --git a/server/src/user_anonymous.rs b/server/src/user_anonymous.rs index a24d9911..213d22a5 100644 --- a/server/src/user_anonymous.rs +++ b/server/src/user_anonymous.rs @@ -1,4 +1,3 @@ -use std::time::Instant; use uuid::Uuid; use failure::Error; @@ -9,11 +8,8 @@ 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; @@ -26,10 +22,24 @@ pub struct Anonymous { pub id: Uuid, pub instance: Option, pub game: Option, + + ws: CbSender, +} + +impl Anonymous { + pub fn new(account: Account, ws: CbSender) -> Anonymous { + Anonymous { + id: account.id, + account, + ws, + instance: None, + game: None, + } + } } impl User for Anonymous { - fn send(&mut self, msg: RpcMessage, _events: &CbSender, ws: &CbSender) -> Result<(), Error> { + fn send(&mut self, msg: RpcMessage) -> Result<(), Error> { // if the user queries the state of something // we tell events to push updates to them match msg { @@ -40,21 +50,21 @@ impl User for Anonymous { _ => (), }; - ws.send(msg)?; + self.ws.send(msg)?; Ok(()) } - fn connected(&mut self, _db: &Db, events: &CbSender, ws: &CbSender) -> Result<(), Error> { + fn connected(&mut self) -> Result<(), Error> { info!("anonymous connection"); - self.send(RpcMessage::AccountState(self.account.clone()), events, ws)?; - self.send(RpcMessage::StartTutorial(()), events, ws)?; + self.ws.send(RpcMessage::AccountState(self.account.clone()))?; + self.ws.send(RpcMessage::StartTutorial(()))?; Ok(()) } - fn receive(&mut self, data: Vec, _db: &Db, _begin: Instant, _events: &CbSender, _stripe: &StripeClient) -> Result { + fn receive(&mut self, data: Vec, _stripe: &StripeClient) -> Result { match from_slice::(&data) { Ok(v) => { let get_instance = || { @@ -139,6 +149,10 @@ impl User for Anonymous { game = game.resolve_phase_start(); } + if game.finished() { + self.ws.send(RpcMessage::PromptRegister(()))?; + } + Ok(RpcMessage::GameState(game)) }, diff --git a/server/src/user_authenticated.rs b/server/src/user_authenticated.rs index 3cf25be8..23d0c69d 100644 --- a/server/src/user_authenticated.rs +++ b/server/src/user_authenticated.rs @@ -37,72 +37,90 @@ use events::{Event}; use mtx; use mail; use payments; -use pg::{Db}; +use pg::{PgPool}; use rpc::{RpcMessage, RpcRequest, User}; #[derive(Debug,Clone)] -pub struct Authorised { +pub struct Authenticated { pub account: Account, - pub id: Uuid + pub id: Uuid, + + events: CbSender, + ws: CbSender, + pool: PgPool, } -impl User for Authorised { - fn send(&mut self, msg: RpcMessage, events: &CbSender, ws: &CbSender) -> Result<(), Error> { +impl Authenticated { + pub fn new(account: Account, ws: CbSender, events: CbSender, pool: PgPool) -> Authenticated { + Authenticated { + id: account.id, + account, + ws, + events, + pool, + } + } +} + + +impl User for Authenticated { + fn send(&mut self, msg: 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))? + self.events.send(Event::Subscribe(self.id, v.id))? }, RpcMessage::GameState(ref v) => - events.send(Event::Subscribe(self.id, v.id))?, + self.events.send(Event::Subscribe(self.id, v.id))?, RpcMessage::InstanceState(ref v) => - events.send(Event::Subscribe(self.id, v.id))?, + self.events.send(Event::Subscribe(self.id, v.id))?, _ => (), }; - ws.send(msg)?; + self.ws.send(msg)?; Ok(()) } - fn connected(&mut self, db: &Db, events: &CbSender, ws: &CbSender) -> Result<(), Error> { + fn connected(&mut self) -> Result<(), Error> { info!("authenticated connection account={:?}", self.account); let a = &self.account; - ws.send(RpcMessage::AccountAuthenticated(a.clone()))?; + self.ws.send(RpcMessage::AccountAuthenticated(a.clone()))?; // tell events we have connected - events.send(Event::Connect(self.id, a.clone(), ws.clone()))?; + self.events.send(Event::Connect(self.id, a.clone(), self.ws.clone()))?; - ws.send(RpcMessage::AccountState(a.clone()))?; - events.send(Event::Subscribe(self.id, a.id))?; + self.ws.send(RpcMessage::AccountState(a.clone()))?; + self.events.send(Event::Subscribe(self.id, a.id))?; // check if they have an image that needs to be generated account::img_check(&a)?; + let db = self.pool.get()?; let mut tx = db.transaction()?; // send account constructs let account_constructs = account::constructs(&mut tx, &a)?; - ws.send(RpcMessage::AccountConstructs(account_constructs))?; + self.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))?; + self.ws.send(RpcMessage::AccountInstances(account_instances))?; let shop = mtx::account_shop(&mut tx, &a)?; - ws.send(RpcMessage::AccountShop(shop))?; + self.ws.send(RpcMessage::AccountShop(shop))?; let team = account::team(&mut tx, &a)?; - ws.send(RpcMessage::AccountTeam(team))?; + self.ws.send(RpcMessage::AccountTeam(team))?; let wheel = account::chat_wheel(&db, a.id)?; - ws.send(RpcMessage::ChatWheel(wheel))?; + self.ws.send(RpcMessage::ChatWheel(wheel))?; if let Some(instance) = account::tutorial(&mut tx, &a)? { - ws.send(RpcMessage::InstanceState(instance))?; + self.ws.send(RpcMessage::InstanceState(instance))?; } // tx should do nothing @@ -111,8 +129,11 @@ impl User for Authorised { Ok(()) } - fn receive(&mut self, data: Vec, db: &Db, begin: Instant, events: &CbSender, stripe: &StripeClient) -> Result { + fn receive(&mut self, data: Vec, stripe: &StripeClient) -> Result { // cast the msg to this type to receive method name + let begin = Instant::now(); + let db = self.pool.get()?; + match from_slice::(&data) { Ok(v) => { let request = v.clone(); @@ -123,19 +144,19 @@ impl User for Authorised { return Ok(RpcMessage::GameState(anim_test_game(skill))), RpcRequest::InstanceQueue {} => { - events.send(Event::Queue(self.id))?; + self.events.send(Event::Queue(self.id))?; Ok(RpcMessage::QueueRequested(())) }, RpcRequest::InstanceInvite {} => { - events.send(Event::Invite(self.id))?; + self.events.send(Event::Invite(self.id))?; Ok(RpcMessage::InviteRequested(())) }, RpcRequest::InstanceJoin { code } => { - events.send(Event::Join(self.id, code))?; + self.events.send(Event::Join(self.id, code))?; Ok(RpcMessage::Joining(())) }, RpcRequest::InstanceLeave {} => { - events.send(Event::Leave(self.id))?; + self.events.send(Event::Leave(self.id))?; Ok(RpcMessage::Processing(())) }, @@ -147,7 +168,7 @@ impl User for Authorised { 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()))?; + self.events.send(Event::Chat(self.id, instance_id, c.to_string()))?; } else { return Err(err_msg("invalid chat index")); } @@ -172,7 +193,7 @@ impl User for Authorised { Ok(RpcMessage::EmailState(mail::select_account(&db, self.account.id)?)), RpcRequest::SubscriptionState {} => - Ok(RpcMessage::SubscriptionState(payments::account_subscription(db, stripe, &self.account)?)), + Ok(RpcMessage::SubscriptionState(payments::account_subscription(&db, stripe, &self.account)?)), // RpcRequest::AccountShop {} => // Ok(RpcMessage::AccountShop(mtx::account_shop(&mut tx, &account)?)),