ez mode tutorial and updates to the auth and anon user structs

This commit is contained in:
ntr 2020-01-08 17:17:05 +10:00
parent d7931bc3bb
commit 549b9cdf65
12 changed files with 168 additions and 63 deletions

View File

@ -100,6 +100,12 @@ section {
} }
} }
.block-text {
letter-spacing: 0.25em;
text-transform: uppercase;
text-align: center;
}
.list { .list {
margin-bottom: 2em; margin-bottom: 2em;

View File

@ -14,6 +14,7 @@ const addState = connect(
const { const {
ws, ws,
account, account,
tutorial,
} = state; } = state;
function sendInstancePractice() { function sendInstancePractice() {
@ -21,6 +22,7 @@ const addState = connect(
} }
return { return {
promptRegister: tutorial === 99, // see events
account, account,
sendInstancePractice, sendInstancePractice,
}; };
@ -30,6 +32,7 @@ const addState = connect(
function Play(args) { function Play(args) {
const { const {
account, account,
promptRegister,
sendInstancePractice, sendInstancePractice,
} = args; } = args;
@ -47,6 +50,17 @@ function Play(args) {
); );
const list = () => { const list = () => {
if (promptRegister) {
return (
<div class='block-text'>
<p><b>You just won your first round of MNML.</b></p>
<p>Register below to play a real Bo5 against other players, play a practice round, customise your team & more...</p>
<p>glhf</p>
</div>
)
}
return ( return (
<div class='list play'> <div class='list play'>
<figure> <figure>

View File

@ -9,6 +9,7 @@ const addState = connect(
ws, ws,
game, game,
account, account,
authenticated,
chatShow, chatShow,
animating, animating,
} = state; } = state;
@ -33,6 +34,7 @@ const addState = connect(
return { return {
game, game,
account, account,
authenticated,
chatShow, chatShow,
sendAbandon, sendAbandon,
sendGameSkillClear, sendGameSkillClear,
@ -65,6 +67,7 @@ function GameCtrlBtns(args) {
animating, animating,
account, account,
chatShow, chatShow,
authenticated,
getInstanceState, getInstanceState,
sendGameSkillClear, sendGameSkillClear,
@ -77,7 +80,9 @@ function GameCtrlBtns(args) {
const finished = game.phase === 'Finished'; const finished = game.phase === 'Finished';
function quitClick() { function quitClick() {
if (authenticated) {
getInstanceState(); getInstanceState();
}
quit(); quit();
} }

View File

@ -9,6 +9,7 @@ const addState = connect(
ws, ws,
game, game,
animating, animating,
authenticated,
account, account,
} = state; } = state;
@ -27,6 +28,7 @@ const addState = connect(
return { return {
game, game,
account, account,
authenticated,
sendAbandon, sendAbandon,
sendDraw, sendDraw,
@ -50,6 +52,7 @@ function GameCtrlTopBtns(args) {
const { const {
game, game,
account, account,
authenticated,
leave, leave,
sendAbandon, sendAbandon,
@ -82,6 +85,11 @@ function GameCtrlTopBtns(args) {
setTimeout(() => this.setState({ concedeState: false }), 2000); setTimeout(() => this.setState({ concedeState: false }), 2000);
}; };
const authBtn = btn => {
if (authenticated) return btn;
return <button disabled>-</button>
}
const abandonClasses = `abandon ${abandonState ? 'confirming' : ''}`; const abandonClasses = `abandon ${abandonState ? 'confirming' : ''}`;
const abandonText = abandonState ? 'Confirm' : 'Abandon'; const abandonText = abandonState ? 'Confirm' : 'Abandon';
const abandonAction = abandonState ? sendAbandon : abandonStateTrue; const abandonAction = abandonState ? sendAbandon : abandonStateTrue;
@ -102,9 +110,9 @@ function GameCtrlTopBtns(args) {
return ( return (
<div class="instance-ctrl-btns"> <div class="instance-ctrl-btns">
{abandonBtn} {authBtn(abandonBtn)}
{concedeBtn} {authBtn(concedeBtn)}
{drawBtn} {authBtn(drawBtn)}
</div> </div>
); );
} }

View File

@ -7,6 +7,7 @@ const addState = connect(
function receiveState(state) { function receiveState(state) {
const { const {
ws, ws,
authenticated,
instance, instance,
tutorial, tutorial,
} = state; } = state;
@ -17,6 +18,7 @@ const addState = connect(
return { return {
instance, instance,
authenticated,
tutorial, tutorial,
sendAbandon, sendAbandon,
}; };
@ -39,6 +41,7 @@ const addState = connect(
function InstanceTopBtns(args) { function InstanceTopBtns(args) {
const { const {
instance, instance,
authenticated,
leave, leave,
sendAbandon, sendAbandon,
@ -61,9 +64,16 @@ function InstanceTopBtns(args) {
const abandonBtn = <button class={abandonClasses} disabled={finished} onClick={abandonAction}>{abandonText}</button>; const abandonBtn = <button class={abandonClasses} disabled={finished} onClick={abandonAction}>{abandonText}</button>;
const leaveBtn = <button class='abandon confirming' onClick={() => leave(tutorial)}>Leave</button>; const leaveBtn = <button class='abandon confirming' onClick={() => leave(tutorial)}>Leave</button>;
const finalBtn = () => {
// disable for tutorial mode
if (!authenticated) return <button disabled='true'>-</button>;
if (finished) return leaveBtn;
return abandonBtn;
}
return ( return (
<div class="instance-ctrl-btns"> <div class="instance-ctrl-btns">
{finished ? leaveBtn : abandonBtn} {finalBtn()}
</div> </div>
); );
} }

View File

@ -1,13 +1,31 @@
// eslint-disable-next-line // eslint-disable-next-line
const preact = require('preact'); const preact = require('preact');
const { connect } = require('preact-redux');
const Login = require('./welcome.login'); const Login = require('./welcome.login');
const Register = require('./welcome.register'); const Register = require('./welcome.register');
const Help = require('./welcome.help'); const Help = require('./welcome.help');
// const About = require('./welcome.about'); // const About = require('./welcome.about');
function Welcome() { const addState = connect(
const page = this.state.page || 'login'; 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 = () => { const pageEl = () => {
if (page === 'login') return <Login />; if (page === 'login') return <Login />;
@ -45,4 +63,4 @@ function Welcome() {
); );
} }
module.exports = Welcome; module.exports = addState(Welcome);

View File

@ -218,6 +218,10 @@ function registerEvents(store) {
store.dispatch(actions.setTutorial(1)); store.dispatch(actions.setTutorial(1));
} }
function promptRegister() {
store.dispatch(actions.setTutorial(99));
store.dispatch(actions.setInstance(null));
}
window.addEventListener('hashchange', urlHashChange, false); window.addEventListener('hashchange', urlHashChange, false);
@ -250,6 +254,7 @@ function registerEvents(store) {
setWs, setWs,
startTutorial, startTutorial,
promptRegister,
urlHashChange, urlHashChange,

View File

@ -301,6 +301,7 @@ function createSocket(events) {
// Joining: () => events.notify('Searching for instance...'), // Joining: () => events.notify('Searching for instance...'),
StartTutorial: () => events.startTutorial(), StartTutorial: () => events.startTutorial(),
PromptRegister: () => events.promptRegister(),
Processing: () => true, Processing: () => true,
Error: errHandler, Error: errHandler,

View File

@ -695,11 +695,17 @@ pub fn instance_practice(tx: &mut Transaction, account: &Account) -> Result<Inst
} }
pub fn instance_demo(account: &Account) -> Result<Instance, Error> { pub fn instance_demo(account: &Account) -> Result<Instance, Error> {
let bot = bot_player(); let mut bot = bot_player();
let bot_id = bot.id; let bot_id = bot.id;
// generate imgs for the client to see // 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)?; img::shapes_write(c.img)?;
} }
@ -709,6 +715,7 @@ pub fn instance_demo(account: &Account) -> Result<Instance, Error> {
let player = anon_player(account.id); let player = anon_player(account.id);
// smash these noobs
for c in player.constructs.iter() { for c in player.constructs.iter() {
img::shapes_write(c.img)?; img::shapes_write(c.img)?;
} }

View File

@ -2,7 +2,6 @@ use mnml_core::item::ItemInfoCtr;
use mnml_core::instance::ChatState; use mnml_core::instance::ChatState;
use std::collections::HashMap; use std::collections::HashMap;
use std::time::{Instant};
use std::thread::{spawn}; use std::thread::{spawn};
use std::str; use std::str;
@ -23,7 +22,7 @@ use account;
use events::{Event}; use events::{Event};
use user_anonymous::{Anonymous}; use user_anonymous::{Anonymous};
use user_authenticated::{Authorised}; use user_authenticated::{Authenticated};
use mnml_core::construct::{Construct}; use mnml_core::construct::{Construct};
use mnml_core::game::{Game}; use mnml_core::game::{Game};
@ -37,7 +36,7 @@ use mnml_core::instance::{Instance};
use mtx; use mtx;
use mail::Email; use mail::Email;
use pg::{Db};
use pg::{PgPool}; use pg::{PgPool};
use http::{AUTH_CLEAR, TOKEN_HEADER}; use http::{AUTH_CLEAR, TOKEN_HEADER};
@ -63,6 +62,7 @@ pub enum RpcMessage {
Pong(()), Pong(()),
StartTutorial(()), StartTutorial(()),
PromptRegister(()),
QueueRequested(()), QueueRequested(()),
QueueJoined(()), QueueJoined(()),
@ -126,9 +126,9 @@ pub enum RpcRequest {
} }
pub trait User { pub trait User {
fn receive(&mut self, data: Vec<u8>, db: &Db, begin: Instant, events: &CbSender<Event>, stripe: &StripeClient) -> Result<RpcMessage, Error>; fn receive(&mut self, data: Vec<u8>, stripe: &StripeClient) -> Result<RpcMessage, Error>;
fn connected(&mut self, db: &Db, events: &CbSender<Event>, ws: &CbSender<RpcMessage>) -> Result<(), Error>; fn connected(&mut self) -> Result<(), Error>;
fn send(&mut self, msg: RpcMessage, events: &CbSender<Event>, ws: &CbSender<RpcMessage>) -> Result<(), Error>; fn send(&mut self, msg: RpcMessage) -> Result<(), Error>;
} }
struct Connection { struct Connection {
@ -165,20 +165,16 @@ impl Connection {
// when it encounters errors // when it encounters errors
impl Handler for Connection { impl Handler for Connection {
fn on_open(&mut self, _: ws::Handshake) -> ws::Result<()> { fn on_open(&mut self, _: ws::Handshake) -> ws::Result<()> {
let db = self.pool.get().unwrap(); self.user.connected().unwrap();
self.user.connected(&db, &self.events, &self.ws).unwrap();
Ok(()) Ok(())
} }
fn on_message(&mut self, msg: Message) -> ws::Result<()> { fn on_message(&mut self, msg: Message) -> ws::Result<()> {
match msg { match msg {
Message::Binary(msg) => { Message::Binary(msg) => {
let begin = Instant::now(); match self.user.receive(msg, &self.stripe) {
let db_connection = self.pool.get().unwrap();
match self.user.receive(msg, &db_connection, begin, &self.events, &self.stripe) {
Ok(msg) => { Ok(msg) => {
self.user.send(msg, &self.events, &self.ws).unwrap(); self.user.send(msg).unwrap();
}, },
Err(e) => { Err(e) => {
warn!("{:?}", e); warn!("{:?}", e);
@ -220,7 +216,7 @@ impl Handler for Connection {
if cookie.name() == TOKEN_HEADER { if cookie.name() == TOKEN_HEADER {
let db = self.pool.get().unwrap(); let db = self.pool.get().unwrap();
match account::from_token(&db, &cookie.value().to_string()) { 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(), Err(_) => return unauth(),
} }
} }
@ -268,11 +264,11 @@ pub fn start(pool: PgPool, events_tx: CbSender<Event>, stripe: StripeClient) {
DeflateHandler::new( DeflateHandler::new(
Connection { Connection {
id, id,
ws: tx, ws: tx.clone(),
pool: pool.clone(), pool: pool.clone(),
stripe: stripe.clone(), stripe: stripe.clone(),
events: events_tx.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))
} }
) )
}) })

View File

@ -1,4 +1,3 @@
use std::time::Instant;
use uuid::Uuid; use uuid::Uuid;
use failure::Error; use failure::Error;
@ -9,11 +8,8 @@ use crossbeam_channel::{Sender as CbSender};
use serde_cbor::{from_slice}; use serde_cbor::{from_slice};
use stripe::{Client as StripeClient}; use stripe::{Client as StripeClient};
use account::{Account}; use account::{Account};
use pg::{Db};
use pg; use pg;
use events::{Event};
use rpc::{RpcMessage, RpcRequest, User}; use rpc::{RpcMessage, RpcRequest, User};
use mnml_core::game::Game; use mnml_core::game::Game;
@ -26,10 +22,24 @@ pub struct Anonymous {
pub id: Uuid, pub id: Uuid,
pub instance: Option<Instance>, pub instance: Option<Instance>,
pub game: Option<Game>, pub game: Option<Game>,
ws: CbSender<RpcMessage>,
}
impl Anonymous {
pub fn new(account: Account, ws: CbSender<RpcMessage>) -> Anonymous {
Anonymous {
id: account.id,
account,
ws,
instance: None,
game: None,
}
}
} }
impl User for Anonymous { impl User for Anonymous {
fn send(&mut self, msg: RpcMessage, _events: &CbSender<Event>, ws: &CbSender<RpcMessage>) -> Result<(), Error> { fn send(&mut self, msg: RpcMessage) -> Result<(), Error> {
// if the user queries the state of something // if the user queries the state of something
// we tell events to push updates to them // we tell events to push updates to them
match msg { match msg {
@ -40,21 +50,21 @@ impl User for Anonymous {
_ => (), _ => (),
}; };
ws.send(msg)?; self.ws.send(msg)?;
Ok(()) Ok(())
} }
fn connected(&mut self, _db: &Db, events: &CbSender<Event>, ws: &CbSender<RpcMessage>) -> Result<(), Error> { fn connected(&mut self) -> Result<(), Error> {
info!("anonymous connection"); info!("anonymous connection");
self.send(RpcMessage::AccountState(self.account.clone()), events, ws)?; self.ws.send(RpcMessage::AccountState(self.account.clone()))?;
self.send(RpcMessage::StartTutorial(()), events, ws)?; self.ws.send(RpcMessage::StartTutorial(()))?;
Ok(()) Ok(())
} }
fn receive(&mut self, data: Vec<u8>, _db: &Db, _begin: Instant, _events: &CbSender<Event>, _stripe: &StripeClient) -> Result<RpcMessage, Error> { fn receive(&mut self, data: Vec<u8>, _stripe: &StripeClient) -> Result<RpcMessage, Error> {
match from_slice::<RpcRequest>(&data) { match from_slice::<RpcRequest>(&data) {
Ok(v) => { Ok(v) => {
let get_instance = || { let get_instance = || {
@ -139,6 +149,10 @@ impl User for Anonymous {
game = game.resolve_phase_start(); game = game.resolve_phase_start();
} }
if game.finished() {
self.ws.send(RpcMessage::PromptRegister(()))?;
}
Ok(RpcMessage::GameState(game)) Ok(RpcMessage::GameState(game))
}, },

View File

@ -37,72 +37,90 @@ use events::{Event};
use mtx; use mtx;
use mail; use mail;
use payments; use payments;
use pg::{Db}; use pg::{PgPool};
use rpc::{RpcMessage, RpcRequest, User}; use rpc::{RpcMessage, RpcRequest, User};
#[derive(Debug,Clone)] #[derive(Debug,Clone)]
pub struct Authorised { pub struct Authenticated {
pub account: Account, pub account: Account,
pub id: Uuid pub id: Uuid,
events: CbSender<Event>,
ws: CbSender<RpcMessage>,
pool: PgPool,
} }
impl User for Authorised { impl Authenticated {
fn send(&mut self, msg: RpcMessage, events: &CbSender<Event>, ws: &CbSender<RpcMessage>) -> Result<(), Error> { pub fn new(account: Account, ws: CbSender<RpcMessage>, events: CbSender<Event>, 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 // if the user queries the state of something
// we tell events to push updates to them // we tell events to push updates to them
match msg { match msg {
RpcMessage::AccountState(ref v) => { 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) => 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) => 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(()) Ok(())
} }
fn connected(&mut self, db: &Db, events: &CbSender<Event>, ws: &CbSender<RpcMessage>) -> Result<(), Error> { fn connected(&mut self) -> Result<(), Error> {
info!("authenticated connection account={:?}", self.account); info!("authenticated connection account={:?}", self.account);
let a = &self.account; let a = &self.account;
ws.send(RpcMessage::AccountAuthenticated(a.clone()))?; self.ws.send(RpcMessage::AccountAuthenticated(a.clone()))?;
// tell events we have connected // 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()))?; self.ws.send(RpcMessage::AccountState(a.clone()))?;
events.send(Event::Subscribe(self.id, a.id))?; self.events.send(Event::Subscribe(self.id, a.id))?;
// check if they have an image that needs to be generated // check if they have an image that needs to be generated
account::img_check(&a)?; account::img_check(&a)?;
let db = self.pool.get()?;
let mut tx = db.transaction()?; let mut tx = db.transaction()?;
// send account constructs // send account constructs
let account_constructs = account::constructs(&mut tx, &a)?; let account_constructs = account::constructs(&mut tx, &a)?;
ws.send(RpcMessage::AccountConstructs(account_constructs))?; self.ws.send(RpcMessage::AccountConstructs(account_constructs))?;
// get account instances // get account instances
// and send them to the client // and send them to the client
let account_instances = account::account_instances(&mut tx, &a)?; 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)?; 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)?; 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)?; 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)? { if let Some(instance) = account::tutorial(&mut tx, &a)? {
ws.send(RpcMessage::InstanceState(instance))?; self.ws.send(RpcMessage::InstanceState(instance))?;
} }
// tx should do nothing // tx should do nothing
@ -111,8 +129,11 @@ impl User for Authorised {
Ok(()) Ok(())
} }
fn receive(&mut self, data: Vec<u8>, db: &Db, begin: Instant, events: &CbSender<Event>, stripe: &StripeClient) -> Result<RpcMessage, Error> { fn receive(&mut self, data: Vec<u8>, stripe: &StripeClient) -> Result<RpcMessage, Error> {
// cast the msg to this type to receive method name // cast the msg to this type to receive method name
let begin = Instant::now();
let db = self.pool.get()?;
match from_slice::<RpcRequest>(&data) { match from_slice::<RpcRequest>(&data) {
Ok(v) => { Ok(v) => {
let request = v.clone(); let request = v.clone();
@ -123,19 +144,19 @@ impl User for Authorised {
return Ok(RpcMessage::GameState(anim_test_game(skill))), return Ok(RpcMessage::GameState(anim_test_game(skill))),
RpcRequest::InstanceQueue {} => { RpcRequest::InstanceQueue {} => {
events.send(Event::Queue(self.id))?; self.events.send(Event::Queue(self.id))?;
Ok(RpcMessage::QueueRequested(())) Ok(RpcMessage::QueueRequested(()))
}, },
RpcRequest::InstanceInvite {} => { RpcRequest::InstanceInvite {} => {
events.send(Event::Invite(self.id))?; self.events.send(Event::Invite(self.id))?;
Ok(RpcMessage::InviteRequested(())) Ok(RpcMessage::InviteRequested(()))
}, },
RpcRequest::InstanceJoin { code } => { RpcRequest::InstanceJoin { code } => {
events.send(Event::Join(self.id, code))?; self.events.send(Event::Join(self.id, code))?;
Ok(RpcMessage::Joining(())) Ok(RpcMessage::Joining(()))
}, },
RpcRequest::InstanceLeave {} => { RpcRequest::InstanceLeave {} => {
events.send(Event::Leave(self.id))?; self.events.send(Event::Leave(self.id))?;
Ok(RpcMessage::Processing(())) Ok(RpcMessage::Processing(()))
}, },
@ -147,7 +168,7 @@ impl User for Authorised {
let wheel = account::chat_wheel(&db, self.account.id)?; let wheel = account::chat_wheel(&db, self.account.id)?;
if let Some(c) = wheel.get(index) { 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 { } else {
return Err(err_msg("invalid chat index")); 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)?)), Ok(RpcMessage::EmailState(mail::select_account(&db, self.account.id)?)),
RpcRequest::SubscriptionState {} => RpcRequest::SubscriptionState {} =>
Ok(RpcMessage::SubscriptionState(payments::account_subscription(db, stripe, &self.account)?)), Ok(RpcMessage::SubscriptionState(payments::account_subscription(&db, stripe, &self.account)?)),
// RpcRequest::AccountShop {} => // RpcRequest::AccountShop {} =>
// Ok(RpcMessage::AccountShop(mtx::account_shop(&mut tx, &account)?)), // Ok(RpcMessage::AccountShop(mtx::account_shop(&mut tx, &account)?)),