From 395bf640c99c7de21f05b668d2d3758722bd65c1 Mon Sep 17 00:00:00 2001 From: ntr Date: Tue, 24 Dec 2019 12:09:30 +1000 Subject: [PATCH] consolidate views for auth and anon --- client/assets/styles/styles.less | 1 + client/src/actions.jsx | 1 + client/src/components/guest.top.jsx | 138 ++++++++++++++++++++++++++++ client/src/components/header.jsx | 15 +++ client/src/components/main.top.jsx | 6 ++ client/src/components/welcome.jsx | 73 +++++---------- client/src/events.jsx | 19 ++-- client/src/reducers.jsx | 1 + client/src/socket.jsx | 3 + server/src/rpc.rs | 2 + server/src/user_anonymous.rs | 62 +++++++------ server/src/user_authenticated.rs | 1 + 12 files changed, 238 insertions(+), 84 deletions(-) create mode 100644 client/src/components/guest.top.jsx diff --git a/client/assets/styles/styles.less b/client/assets/styles/styles.less index b22ee13d..5e6c3982 100644 --- a/client/assets/styles/styles.less +++ b/client/assets/styles/styles.less @@ -283,6 +283,7 @@ header { } border: none; + border-radius: 0; } } diff --git a/client/src/actions.jsx b/client/src/actions.jsx index 311d444b..a49d8278 100644 --- a/client/src/actions.jsx +++ b/client/src/actions.jsx @@ -1,4 +1,5 @@ export const setAccount = value => ({ type: 'SET_ACCOUNT', value }); +export const setAuthenticated = value => ({ type: 'SET_AUTHENTICATED', value }); export const setAnimating = value => ({ type: 'SET_ANIMATING', value }); export const setAnimFocus = value => ({ type: 'SET_ANIM_FOCUS', value }); diff --git a/client/src/components/guest.top.jsx b/client/src/components/guest.top.jsx new file mode 100644 index 00000000..f356356c --- /dev/null +++ b/client/src/components/guest.top.jsx @@ -0,0 +1,138 @@ +// const { connect } = require('preact-redux'); +const preact = require('preact'); +const { connect } = require('preact-redux'); + +const { errorToast, infoToast } = require('../utils'); +const actions = require('./../actions'); + +const VERSION = process.env.npm_package_version; + +const Welcome = require('./welcome'); + +const addState = connect( + function receiveState(state) { + const { + ws, + account, + instances, + invite, + pvp, + } = state; + + function sendInstanceState(id) { + ws.sendInstanceState(id); + } + + function sendInstancePractice() { + ws.sendInstancePractice(); + } + + function sendInstanceQueue() { + ws.sendInstanceQueue(); + } + + function sendInstanceInvite() { + ws.sendInstanceInvite(); + } + + function sendInstanceLeave() { + ws.sendInstanceLeave(); + } + + return { + account, + instances, + invite, + pvp, + + sendInstanceState, + sendInstanceQueue, + sendInstancePractice, + sendInstanceInvite, + sendInstanceLeave, + }; + }, + + function receiveDispatch(dispatch) { + function setMtxActive(mtx) { + dispatch(actions.setConstructRename(null)); + dispatch(actions.setMtxActive(mtx)); + return true; + } + + function setNav(place) { + return dispatch(actions.setNav(place)); + } + + return { + setMtxActive, + setNav, + }; + } +); + +function Play(args) { + const { + account, + instances, + invite, + pvp, + + sendInstanceState, + sendInstanceQueue, + sendInstancePractice, + sendInstanceInvite, + sendInstanceLeave, + + setNav, + } = args; + + const news = ( +
+

MNML is a turn-based 1v1 strategy game in an abstract setting.

+

+ Build a unique team of 3 constructs from a range of skills and specialisations.
+ Outplay your opponent in multiple rounds by adapting to an always shifting meta.
+ Simple rules, complex interactions and unique mechanics.
+

+
+ ); + + const list = () => { + return ( +
+
+
+ +
Learn MNML
+
+
+ +
Join the Community
+
+
+
+ ); + }; + + return ( +
+
+

Welcome to MNML

+ {news} + {list()} +
+ +
+ ); +} + +module.exports = addState(Play); diff --git a/client/src/components/header.jsx b/client/src/components/header.jsx index 734b1761..9dc7b6df 100644 --- a/client/src/components/header.jsx +++ b/client/src/components/header.jsx @@ -8,6 +8,7 @@ const addState = connect( const { ws, account, + authenticated, nav, } = state; @@ -22,6 +23,7 @@ const addState = connect( return { account, + authenticated, nav, sendInstanceState, @@ -48,6 +50,7 @@ const addState = connect( function Header(args) { const { account, + authenticated, nav, sendAccountStates, @@ -56,6 +59,18 @@ function Header(args) { if (!account) return false; + if (!authenticated) return ( +
+
+ +
+
+ ) + function navTo(p) { return setNav(p); } diff --git a/client/src/components/main.top.jsx b/client/src/components/main.top.jsx index b6395305..f56fe8c6 100644 --- a/client/src/components/main.top.jsx +++ b/client/src/components/main.top.jsx @@ -4,6 +4,7 @@ const preact = require('preact'); const actions = require('./../actions'); const AccountTop = require('./account.top'); +const GuestTop = require('./guest.top'); const Play = require('./play'); const Shop = require('./shop'); const Reshape = require('./reshape'); @@ -12,10 +13,12 @@ const addState = connect( function receiveState(state) { const { nav, + authenticated, } = state; return { nav, + authenticated, }; }, ); @@ -23,8 +26,11 @@ const addState = connect( function Top(args) { const { nav, + authenticated, } = args; + if (!authenticated) return + if (nav === 'account') return ; if (nav === 'play') return if (nav === 'shop') return diff --git a/client/src/components/welcome.jsx b/client/src/components/welcome.jsx index 3cb0824d..e04e95b7 100644 --- a/client/src/components/welcome.jsx +++ b/client/src/components/welcome.jsx @@ -7,7 +7,7 @@ const Help = require('./welcome.help'); // const About = require('./welcome.about'); function Welcome() { - const page = this.state.page || 'register'; + const page = this.state.page || 'login'; const pageEl = () => { if (page === 'login') return ; @@ -16,57 +16,32 @@ function Welcome() { return false; }; - const news = ( -
-

Welcome to mnml.

- -

MNML is a turn-based 1v1 strategy game in an abstract setting.

-

- Build a unique team of 3 constructs from a range of skills and specialisations.
- Outplay your opponent in multiple rounds by adapting to an always shifting meta.
- Simple rules, complex interactions and unique mechanics.
-

-

Free to play, no pay to win. Register to start playing.

- - Tutorial Playthrough on YouTube -
- ); - - const main =
{news}{pageEl()}
; + const form =
{pageEl()}
; return ( -
-
-
- - - - -
-
-
- {main} +
+
+ + +
-
+ {form} + ); } diff --git a/client/src/events.jsx b/client/src/events.jsx index fa048f29..c0b935b1 100644 --- a/client/src/events.jsx +++ b/client/src/events.jsx @@ -27,7 +27,6 @@ function registerEvents(store) { function clearTutorial() { store.dispatch(actions.setTutorial(null)); - localStorage.setItem('tutorial-complete', true); } @@ -35,7 +34,6 @@ function registerEvents(store) { store.dispatch(actions.setTutorialGame(null)); } - function setPing(ping) { store.dispatch(actions.setPing(ping)); } @@ -179,18 +177,14 @@ function registerEvents(store) { const player = v.players.find(p => p.id === account.id); store.dispatch(actions.setPlayer(player)); + if (tutorial) tutorialVbox(player, store, tutorial); + if (v.phase === 'Finished') { ws.sendAccountInstances(); } - - // instance.mobile.less hides info at @media 1000 - if (localStorage.getItem('tutorial-complete') || window.innerWidth <= 1100) { - store.dispatch(actions.setTutorial(null)); - } else if (v.time_control === 'Practice' && v.rounds.length === 1 && tutorial) { - tutorialVbox(player, store, tutorial); - } } + return store.dispatch(actions.setInstance(v)); } @@ -214,6 +208,11 @@ function registerEvents(store) { return true; } + function startTutorial() { + store.dispatch(actions.setTutorial(1)); + } + + window.addEventListener('hashchange', urlHashChange, false); return { @@ -243,6 +242,8 @@ function registerEvents(store) { setSubscription, setWs, + startTutorial, + urlHashChange, notify, diff --git a/client/src/reducers.jsx b/client/src/reducers.jsx index 9edeeae6..c30dec0a 100644 --- a/client/src/reducers.jsx +++ b/client/src/reducers.jsx @@ -10,6 +10,7 @@ function createReducer(defaultState, actionType) { /* eslint-disable key-spacing */ module.exports = { account: createReducer(null, 'SET_ACCOUNT'), + authenticated: createReducer(null, 'SET_AUTHENTICATED'), activeItem: createReducer(null, 'SET_ACTIVE_VAR'), activeSkill: createReducer(null, 'SET_ACTIVE_SKILL'), diff --git a/client/src/socket.jsx b/client/src/socket.jsx index bacf0870..443235ae 100644 --- a/client/src/socket.jsx +++ b/client/src/socket.jsx @@ -270,6 +270,7 @@ function createSocket(events) { // this object wraps the reply types to a function const handlers = { AccountState: onAccount, + AccountAuthenticated: events.setAuthenticated, AccountConstructs: onAccountConstructs, AccountTeam: onAccountTeam, AccountInstances: onAccountInstances, @@ -299,6 +300,8 @@ function createSocket(events) { ChatWheel: wheel => events.setChatWheel(wheel), // Joining: () => events.notify('Searching for instance...'), + StartTutorial: () => events.startTutorial(), + Processing: () => true, Error: errHandler, }; diff --git a/server/src/rpc.rs b/server/src/rpc.rs index ff755767..ff61cd01 100644 --- a/server/src/rpc.rs +++ b/server/src/rpc.rs @@ -44,6 +44,7 @@ use http::{AUTH_CLEAR, TOKEN_HEADER}; #[derive(Debug,Clone,Serialize)] pub enum RpcMessage { AccountState(Account), + AccountAuthenticated(()), AccountConstructs(Vec), AccountTeam(Vec), AccountInstances(Vec), @@ -61,6 +62,7 @@ pub enum RpcMessage { SubscriptionState(Option), Pong(()), + StartTutorial(()), QueueRequested(()), QueueJoined(()), diff --git a/server/src/user_anonymous.rs b/server/src/user_anonymous.rs index 0e30a495..40359e58 100644 --- a/server/src/user_anonymous.rs +++ b/server/src/user_anonymous.rs @@ -49,9 +49,8 @@ impl User for Anonymous { info!("anonymous connection"); self.send(RpcMessage::AccountState(self.account.clone()), events, ws)?; + self.send(RpcMessage::StartTutorial(()), events, ws)?; self.send(RpcMessage::ItemInfo(item_info()), events, ws)?; - // self.send(RpcMessage::StartTutorial(()), events, ws)?; - self.send(RpcMessage::InstanceState(pg::instance_demo(&self.account)?), events, ws)?; Ok(()) } @@ -59,70 +58,81 @@ impl User for Anonymous { 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 get_instance = || { + match self.instance { + Some(ref i) => Ok(i.clone()), + None => return Err(err_msg("instance missing")), + } }; - let game = match self.game { - Some(ref i) => Some(i.clone()), - None => None + let get_game = || { + match self.game { + Some(ref i) => Ok(i.clone()), + None => return Err(err_msg("game missing")), + } }; match v { RpcRequest::Ping {} => return Ok(RpcMessage::Pong(())), + + RpcRequest::InstancePractice {} => + Ok(RpcMessage::InstanceState(pg::instance_demo(&self.account)?)), + RpcRequest::InstanceReady { instance_id: _ } => { - match instance.player_ready(self.account.id)? { + match get_instance()?.player_ready(self.account.id)? { Some(g) => Ok(RpcMessage::GameState(g)), - None => Ok(RpcMessage::InstanceState(instance)), + None => Ok(RpcMessage::InstanceState(get_instance()?)), } }, RpcRequest::InstanceState { instance_id: _ } => - Ok(RpcMessage::InstanceState(instance)), + Ok(RpcMessage::InstanceState(get_instance()?)), - RpcRequest::InstanceAbandon { instance_id: _ } => - Err(err_msg("don't give up!")), + RpcRequest::InstanceAbandon { instance_id: _ } => { + let mut instance = get_instance()?; + instance.finish(); + Ok(RpcMessage::InstanceState(instance)) + }, RpcRequest::VboxBuy { instance_id: _, group, index, construct_id } => - Ok(RpcMessage::InstanceState(instance.vbox_buy(self.account.id, group, index, construct_id)?)), + Ok(RpcMessage::InstanceState(get_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)?)), + Ok(RpcMessage::InstanceState(get_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)?)), + Ok(RpcMessage::InstanceState(get_instance()?.vbox_combine(self.account.id, inv_indices, vbox_indices)?)), RpcRequest::VboxRefill { instance_id: _ } => - Ok(RpcMessage::InstanceState(instance.vbox_refill(self.account.id)?)), + Ok(RpcMessage::InstanceState(get_instance()?.vbox_refill(self.account.id)?)), RpcRequest::VboxRefund { instance_id: _, index } => - Ok(RpcMessage::InstanceState(instance.vbox_refund(self.account.id, index)?)), + Ok(RpcMessage::InstanceState(get_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)?)), + Ok(RpcMessage::InstanceState(get_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))?)), + Ok(RpcMessage::InstanceState(get_instance()?.vbox_unequip(self.account.id, target, construct_id, Some(target_construct_id))?)), RpcRequest::GameState { id: _ } => - Ok(RpcMessage::GameState(game.unwrap())), + Ok(RpcMessage::GameState(get_game()?)), RpcRequest::GameSkill { game_id: _, construct_id, target_construct_id, skill } => { - let mut game = game.unwrap(); + let mut game = get_game()?; 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(); + let mut game = get_game()?; game.clear_skill(self.account.id)?; Ok(RpcMessage::GameState(game)) }, RpcRequest::GameReady { id: _ } => { - let mut game = game.unwrap(); + let mut game = get_game()?; game.player_ready(self.account.id)?; if game.skill_phase_finished() { game = game.resolve_phase_start(); @@ -132,10 +142,10 @@ impl User for Anonymous { }, RpcRequest::GameConcede { game_id: _ } => - Ok(RpcMessage::GameState(game.unwrap().concede(self.account.id)?)), + Ok(RpcMessage::GameState(get_game()?.concede(self.account.id)?)), RpcRequest::GameOfferDraw { game_id: _ } => - Ok(RpcMessage::GameState(game.unwrap().offer_draw(self.account.id)?)), + Ok(RpcMessage::GameState(get_game()?.offer_draw(self.account.id)?)), _ => Err(format_err!("unhandled anonymous request request={:?}", v)), } diff --git a/server/src/user_authenticated.rs b/server/src/user_authenticated.rs index c92400a3..252b516d 100644 --- a/server/src/user_authenticated.rs +++ b/server/src/user_authenticated.rs @@ -71,6 +71,7 @@ impl User for Authorised { info!("authenticated connection account={:?}", self.account); let a = &self.account; + ws.send(RpcMessage::AccountAuthenticated(()))?; // tell events we have connected events.send(Event::Connect(self.id, a.clone(), ws.clone()))?;