Merge branch 'release/1.4.5'

This commit is contained in:
ntr 2019-09-15 17:26:37 +10:00
commit 1a41f95ed9
21 changed files with 311 additions and 163 deletions

View File

@ -1 +1 @@
1.4.4 1.4.5

View File

@ -1,6 +1,6 @@
{ {
"name": "mnml-client", "name": "mnml-client",
"version": "1.4.4", "version": "1.4.5",
"description": "", "description": "",
"main": "index.js", "main": "index.js",
"scripts": { "scripts": {

View File

@ -53,7 +53,7 @@ aside {
border-color: forestgreen; border-color: forestgreen;
} }
&:active, &:focus { &:active, &:focus, &.enabled {
background: forestgreen; background: forestgreen;
color: black; color: black;
border-color: forestgreen; border-color: forestgreen;

View File

@ -286,3 +286,9 @@ li {
background-repeat: no-repeat; background-repeat: no-repeat;
background-position: center; background-position: center;
} }
#clipboard {
width: 1px;
height: 1px;
padding: 0px;
}

View File

@ -1,6 +1,6 @@
{ {
"name": "mnml-client", "name": "mnml-client",
"version": "1.4.4", "version": "1.4.5",
"description": "", "description": "",
"main": "index.js", "main": "index.js",
"scripts": { "scripts": {
@ -29,6 +29,7 @@
"preact-compat": "^3.19.0", "preact-compat": "^3.19.0",
"preact-context": "^1.1.3", "preact-context": "^1.1.3",
"preact-redux": "^2.1.0", "preact-redux": "^2.1.0",
"query-string": "^6.8.3",
"react-string-replace": "^0.4.4", "react-string-replace": "^0.4.4",
"react-stripe-elements": "^3.0.0", "react-stripe-elements": "^3.0.0",
"redux": "^4.0.0" "redux": "^4.0.0"

View File

@ -19,6 +19,7 @@ export const setConstructRename = value => ({ type: 'SET_CONSTRUCT_RENAME', valu
export const setGame = value => ({ type: 'SET_GAME', value }); export const setGame = value => ({ type: 'SET_GAME', value });
export const setInfo = value => ({ type: 'SET_INFO', value }); export const setInfo = value => ({ type: 'SET_INFO', value });
export const setEmail = value => ({ type: 'SET_EMAIL', value }); export const setEmail = value => ({ type: 'SET_EMAIL', value });
export const setInvite = value => ({ type: 'SET_INVITE', value });
export const setInstance = value => ({ type: 'SET_INSTANCE', value }); export const setInstance = value => ({ type: 'SET_INSTANCE', value });
export const setInstances = value => ({ type: 'SET_INSTANCES', value }); export const setInstances = value => ({ type: 'SET_INSTANCES', value });
export const setItemEquip = value => ({ type: 'SET_ITEM_EQUIP', value }); export const setItemEquip = value => ({ type: 'SET_ITEM_EQUIP', value });

View File

@ -2,7 +2,7 @@ const anime = require('animejs').default;
const { TIMES } = require('../../constants'); const { TIMES } = require('../../constants');
function Banish(id) { function Banish(id, idle) {
return anime({ return anime({
targets: [document.getElementById(id)], targets: [document.getElementById(id)],
scaleY: 0, scaleY: 0,
@ -11,6 +11,8 @@ function Banish(id) {
delay: TIMES.TARGET_DELAY_MS, delay: TIMES.TARGET_DELAY_MS,
duration: TIMES.TARGET_DURATION_MS * 0.45, duration: TIMES.TARGET_DURATION_MS * 0.45,
direction: 'alternate', direction: 'alternate',
begin: idle.pause,
complete: idle.play,
}); });
} }

View File

@ -2,7 +2,7 @@ const anime = require('animejs').default;
const { TIMES } = require('../../constants'); const { TIMES } = require('../../constants');
function Invert(id) { function Invert(id, idle) {
return anime({ return anime({
targets: [document.getElementById(id)], targets: [document.getElementById(id)],
rotate: 180, rotate: 180,
@ -10,6 +10,8 @@ function Invert(id) {
duration: TIMES.TARGET_DURATION_MS * 0.45, duration: TIMES.TARGET_DURATION_MS * 0.45,
easing: 'easeInOutElastic', easing: 'easeInOutElastic',
direction: 'alternate', direction: 'alternate',
begin: idle.pause,
complete: idle.play,
}); });
} }

View File

@ -2,15 +2,17 @@ const anime = require('animejs').default;
const { TIMES } = require('../../constants'); const { TIMES } = require('../../constants');
function sourceCast(id, direction) { function sourceCast(id, direction, idle) {
const { x, y } = direction; const { x, y } = direction;
return anime({ return anime({
targets: [document.getElementById(id)], targets: [document.getElementById(id)],
translateX: [0, x * 200], translateX: x * window.screen.width * 0.15,
translateY: [0, y * 200], translateY: y * window.screen.height * 0.15,
easing: 'easeInOutElastic', easing: 'easeInOutElastic',
direction: 'alternate', direction: 'alternate',
duration: TIMES.SOURCE_DURATION_MS, duration: TIMES.SOURCE_DURATION_MS,
begin: idle.pause,
complete: idle.play,
}); });
} }

View File

@ -37,75 +37,10 @@ class ConstructAvatar extends Component {
} }
componentDidMount() { componentDidMount() {
const { animSource, animTarget, construct } = this.props;
// back to idle
if (!animTarget && !animSource) {
if (!this.idle) {
this.idle = idleAnimation(this.props.construct.id); this.idle = idleAnimation(this.props.construct.id);
return this.animations.push(this.idle); return this.animations.push(this.idle);
} }
return this.idle.play();
}
const isSource = animSource && animSource.constructId === construct.id;
const selectAnim = () => {
if (isSource) {
console.warn(construct.name, animSource);
return sourceCast(animSource.constructId, animSource.direction);
}
switch (animTarget.skill) {
case 'banish': return banish(construct.id);
case 'invert': return invert(construct.id);
default: return null;
}
};
const anim = selectAnim();
if (!anim) return false;
this.idle.pause();
this.animations.push(anim);
return true;
}
componentDidUpdate(prevProps) {
const { animSource, animTarget, construct } = this.props;
// back to idle
if (!animTarget && !animSource) {
if (!this.idle) {
this.idle = idleAnimation(this.props.construct.id);
return this.animations.push(this.idle);
}
return this.idle.play();
}
const isSource = animSource && animSource.constructId === construct.id;
const selectAnim = () => {
if (isSource) {
return sourceCast(animSource.constructId, animSource.direction);
}
switch (animTarget.skill) {
case 'Banish': return banish(construct.id);
case 'Invert': return invert(construct.id);
default: return null;
}
};
const anim = selectAnim();
if (!anim) return false;
this.idle.pause();
this.animations.push(anim);
return true;
}
resetAnimations() { resetAnimations() {
for (let i = this.animations.length - 1; i >= 0; i--) { for (let i = this.animations.length - 1; i >= 0; i--) {
this.animations[i].reset(); this.animations[i].reset();
@ -132,23 +67,18 @@ class ConstructAvatar extends Component {
// this is the source // this is the source
if (animSource && animSource.constructId === construct.id) { if (animSource && animSource.constructId === construct.id) {
// console.warn(construct.name, 'should update') // console.warn(construct.name, 'should update')
return true; return sourceCast(animSource.constructId, animSource.direction, this.idle);
} }
// this is the target // this is the target
if (animTarget && animTarget.constructId.includes(construct.id)) { if (animTarget && animTarget.constructId.includes(construct.id)) {
// console.warn(construct.name, 'should update') // console.warn(construct.name, 'should update')
return true; switch (animTarget.skill) {
case 'Banish': return banish(construct.id, this.idle);
case 'Invert': return invert(construct.id, this.idle);
default: return null;
}
} }
// we were previously doing src anim
const prevSrc = this.props.animSource && this.props.animSource.constructId === construct.id;
if (prevSrc && !animSource) return true;
const prevTarget = this.props.animTarget && this.props.animTarget.constructId.includes(construct.id);
if (prevTarget && !animTarget) return true;
// console.warn(construct.name, 'not updating');
return false; return false;
} }

View File

@ -76,7 +76,9 @@ function Controls(args) {
background: displayColour, background: displayColour,
}; };
const timer = ( const timer = instance.phase !== 'InProgress'
? null
: (
<div class="timer-container"> <div class="timer-container">
<div class="timer" style={timerStyles} >&nbsp;</div> <div class="timer" style={timerStyles} >&nbsp;</div>
</div> </div>

View File

@ -1,11 +1,14 @@
const preact = require('preact'); const preact = require('preact');
const { connect } = require('preact-redux'); const { connect } = require('preact-redux');
const { errorToast, infoToast } = require('../utils');
const addState = connect( const addState = connect(
function receiveState(state) { function receiveState(state) {
const { const {
ws, ws,
instances, instances,
invite,
} = state; } = state;
function sendInstanceState(id) { function sendInstanceState(id) {
@ -20,12 +23,18 @@ const addState = connect(
ws.sendInstanceQueue(); ws.sendInstanceQueue();
} }
function sendInstanceInvite() {
ws.sendInstanceInvite();
}
return { return {
instances, instances,
invite,
sendInstanceState, sendInstanceState,
sendInstanceQueue, sendInstanceQueue,
sendInstancePractice, sendInstancePractice,
sendInstanceInvite,
}; };
} }
); );
@ -33,10 +42,12 @@ const addState = connect(
function JoinButtons(args) { function JoinButtons(args) {
const { const {
instances, instances,
invite,
sendInstanceState, sendInstanceState,
sendInstanceQueue, sendInstanceQueue,
sendInstancePractice, sendInstancePractice,
sendInstanceInvite,
} = args; } = args;
if (instances.length) { if (instances.length) {
@ -55,6 +66,48 @@ function JoinButtons(args) {
); );
} }
const inviteBtn = () => {
if (!invite) {
return (
<button
class='pvp ready'
onClick={() => sendInstanceInvite()}
type="submit">
Invite
</button>
);
}
function copyClick(e) {
const link = `${document.location.origin}#join=${invite}`;
const textArea = document.createElement('textarea', { id: '#clipboard' });
textArea.value = link;
document.body.appendChild(textArea);
textArea.focus();
textArea.select();
try {
document.execCommand('copy');
infoToast('Invite link copied.');
} catch (err) {
console.error('link copy error', err);
errorToast('Invite link copy error.');
}
document.body.removeChild(textArea);
return true;
}
return (
<button
class='pvp ready enabled'
onClick={copyClick}
type="submit">
Copy Link
</button>
);
};
return ( return (
<aside class='play-ctrl'> <aside class='play-ctrl'>
<div class="timer-container"></div> <div class="timer-container"></div>
@ -65,6 +118,7 @@ function JoinButtons(args) {
type="submit"> type="submit">
PVP PVP
</button> </button>
{inviteBtn()}
<button <button
class='practice ready' class='practice ready'
onClick={() => sendInstancePractice()} onClick={() => sendInstancePractice()}

View File

@ -1,3 +1,5 @@
const querystring = require('query-string');
const eachSeries = require('async/eachSeries'); const eachSeries = require('async/eachSeries');
const sample = require('lodash/sample'); const sample = require('lodash/sample');
@ -168,9 +170,20 @@ function registerEvents(store) {
return store.dispatch(actions.setInstances(v)); return store.dispatch(actions.setInstances(v));
} }
function setInvite(code) {
if (!code) return store.dispatch(actions.setInvite(null));
navigator.clipboard.writeText(code).then(() => {
notify(`your invite code ${code} was copied to the clipboard.`);
}, () => {});
return store.dispatch(actions.setInvite(code));
}
function setInstance(v) { function setInstance(v) {
const { account, instance, ws } = store.getState(); const { account, instance, ws } = store.getState();
if (v) { if (v) {
setInvite(null);
const player = v.players.find(p => p.id === account.id); const player = v.players.find(p => p.id === account.id);
store.dispatch(actions.setPlayer(player)); store.dispatch(actions.setPlayer(player));
@ -272,6 +285,16 @@ function registerEvents(store) {
} */ } */
// setup / localstorage // setup / localstorage
function urlHashChange() {
const { ws } = store.getState();
const cmds = querystring.parse(location.hash);
if (cmds.join) ws.sendInstanceJoin(cmds.join);
return true;
}
window.addEventListener('hashchange', urlHashChange, false);
return { return {
clearCombiner, clearCombiner,
clearConstructRename, clearConstructRename,
@ -289,12 +312,15 @@ function registerEvents(store) {
setEmail, setEmail,
setInstance, setInstance,
setItemInfo, setItemInfo,
setInvite,
setPing, setPing,
setShop, setShop,
setTeam, setTeam,
setSubscription, setSubscription,
setWs, setWs,
urlHashChange,
notify, notify,
}; };
} }

View File

@ -31,6 +31,7 @@ module.exports = {
constructRename: createReducer(null, 'SET_CONSTRUCT_RENAME'), constructRename: createReducer(null, 'SET_CONSTRUCT_RENAME'),
game: createReducer(null, 'SET_GAME'), game: createReducer(null, 'SET_GAME'),
email: createReducer(null, 'SET_EMAIL'), email: createReducer(null, 'SET_EMAIL'),
invite: createReducer(null, 'SET_INVITE'),
info: createReducer(null, 'SET_INFO'), info: createReducer(null, 'SET_INFO'),
instance: createReducer(null, 'SET_INSTANCE'), instance: createReducer(null, 'SET_INSTANCE'),
instances: createReducer([], 'SET_INSTANCES'), instances: createReducer([], 'SET_INSTANCES'),

View File

@ -126,6 +126,14 @@ function createSocket(events) {
send(['InstanceQueue', {}]); send(['InstanceQueue', {}]);
} }
function sendInstanceInvite() {
send(['InstanceInvite', {}]);
}
function sendInstanceJoin(code) {
send(['InstanceJoin', { code }]);
}
function sendInstanceReady(instanceId) { function sendInstanceReady(instanceId) {
send(['InstanceReady', { instance_id: instanceId }]); send(['InstanceReady', { instance_id: instanceId }]);
} }
@ -239,6 +247,9 @@ function createSocket(events) {
QueueRequested: () => events.notify('pvp queue request received'), QueueRequested: () => events.notify('pvp queue request received'),
QueueJoined: () => events.notify('you have joined the pvp queue'), QueueJoined: () => events.notify('you have joined the pvp queue'),
InviteRequested: () => events.notify('pvp queue request received'),
Invite: code => events.setInvite(code),
Joining: () => events.notify('searching for instance...'),
Error: errHandler, Error: errHandler,
}; };
@ -284,6 +295,8 @@ function createSocket(events) {
sendPing(); sendPing();
sendItemInfo(); sendItemInfo();
events.urlHashChange();
return true; return true;
} }
@ -318,6 +331,7 @@ function createSocket(events) {
ws.addEventListener('message', onMessage); ws.addEventListener('message', onMessage);
ws.addEventListener('error', onError); ws.addEventListener('error', onError);
ws.addEventListener('close', onClose); ws.addEventListener('close', onClose);
return ws; return ws;
} }
@ -338,6 +352,8 @@ function createSocket(events) {
sendInstancePractice, sendInstancePractice,
sendInstanceQueue, sendInstanceQueue,
sendInstanceState, sendInstanceState,
sendInstanceInvite,
sendInstanceJoin,
sendVboxAccept, sendVboxAccept,
sendVboxApply, sendVboxApply,

View File

@ -1,6 +1,6 @@
{ {
"name": "mnml-ops", "name": "mnml-ops",
"version": "1.4.4", "version": "1.4.5",
"description": "", "description": "",
"main": "index.js", "main": "index.js",
"scripts": { "scripts": {

View File

@ -1,6 +1,6 @@
[package] [package]
name = "mnml" name = "mnml"
version = "1.4.4" version = "1.4.5"
authors = ["ntr <ntr@smokestack.io>"] authors = ["ntr <ntr@smokestack.io>"]
[dependencies] [dependencies]

View File

@ -12,6 +12,7 @@ use account;
use account::Account; use account::Account;
use game; use game;
use instance; use instance;
use names;
use pg::{Db, PgPool}; use pg::{Db, PgPool};
use rpc::RpcMessage; use rpc::RpcMessage;
@ -54,6 +55,9 @@ pub enum Event {
// client events // client events
Queue(Id), Queue(Id),
Invite(Id),
Join(Id, String),
Joined(Id),
} }
struct WsClient { struct WsClient {
@ -62,6 +66,7 @@ struct WsClient {
tx: Sender<RpcMessage>, tx: Sender<RpcMessage>,
subs: HashSet<Uuid>, subs: HashSet<Uuid>,
pvp: bool, pvp: bool,
invite: Option<String>,
} }
impl Events { impl Events {
@ -115,7 +120,7 @@ impl Events {
None => None, None => None,
}; };
let client = WsClient { id, tx, account: account_id, subs: HashSet::new(), pvp: false }; let client = WsClient { id, tx, account: account_id, subs: HashSet::new(), pvp: false, invite: None };
self.clients.insert(id, client); self.clients.insert(id, client);
info!("clients={:?}", self.clients.len()); info!("clients={:?}", self.clients.len());
@ -223,6 +228,59 @@ impl Events {
info!("joined game queue id={:?} account={:?}", requester.id, requester.account); info!("joined game queue id={:?} account={:?}", requester.id, requester.account);
return Ok(()); return Ok(());
}, },
Event::Invite(id) => {
// check whether request is valid
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);
c.invite = Some(code.clone());
c.tx.send(RpcMessage::Invite(code))?;
return Ok(());
},
Event::Join(id, code) => {
// check whether request is valid
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);
let inv = self.clients.iter()
.filter(|(_id, c)| c.invite.is_some())
.find(|(_id, c)| match c.invite {
Some(ref c) => *c == code,
None => false,
})
.map(|(_id, c)| PvpRequest { id: c.id, account: c.account.unwrap(), tx: c.tx.clone() })
.ok_or(format_err!("invite not found code={:?}", code))?;
let join = PvpRequest { id: c.id, account: c.account.unwrap(), tx: c.tx.clone() };
self.warden.send(GameEvent::Match((join, inv)))?;
return Ok(());
},
Event::Joined(id) => {
// check whether request is valid
let c = self.clients.get_mut(&id)
.ok_or(format_err!("connection not found id={:?}", id))?;
c.pvp = false;
c.invite = None;
return Ok(());
},
} }
} }
} }

View File

@ -117,7 +117,7 @@ pub fn shapes_write(id: Uuid) -> Result<Uuid, Error> {
(ConstructShapes::Circle, 10), (ConstructShapes::Circle, 10),
(ConstructShapes::Line, 10), (ConstructShapes::Line, 10),
(ConstructShapes::V, 10), (ConstructShapes::V, 10),
// (ConstructShapes::Tri, 3), (ConstructShapes::Tri, 10),
// (ConstructShapes::Plus, 5), // (ConstructShapes::Plus, 5),
(ConstructShapes::Blank, 1), (ConstructShapes::Blank, 1),
]; ];
@ -155,6 +155,9 @@ pub fn shapes_write(id: Uuid) -> Result<Uuid, Error> {
let size = rng.gen_range(20.0, 50.0); let size = rng.gen_range(20.0, 50.0);
write!(&mut svg, "<rect fill=\"{fill}\" x=\"-{x}\" y=\"-{y}\" width=\"{width}\" height=\"{height}\" transform=\"translate({x_t}, {y_t}) rotate({rotation})\" />", write!(&mut svg, "<rect fill=\"{fill}\" x=\"-{x}\" y=\"-{y}\" width=\"{width}\" height=\"{height}\" transform=\"translate({x_t}, {y_t}) rotate({rotation})\" />",
fill = colour, x = size / 2.0, y = size / 2.0, width = size, height = size, x_t = x_translate, y_t = y_translate, rotation = rotation)?; fill = colour, x = size / 2.0, y = size / 2.0, width = size, height = size, x_t = x_translate, y_t = y_translate, rotation = rotation)?;
if scalar == 0.0 && rng.gen_bool(0.5) {
continue;
}
write!(&mut svg, "<rect fill=\"{fill}\" x=\"-{x}\" y=\"-{y}\" width=\"{width}\" height=\"{height}\" transform=\"translate({x_t}, {y_t}) rotate({rotation})\" />", write!(&mut svg, "<rect fill=\"{fill}\" x=\"-{x}\" y=\"-{y}\" width=\"{width}\" height=\"{height}\" transform=\"translate({x_t}, {y_t}) rotate({rotation})\" />",
fill = colour, x = size / 2.0, y = size / 2.0, width = size, height = size, x_t = -x_translate, y_t = -y_translate, rotation = rotation)?; fill = colour, x = size / 2.0, y = size / 2.0, width = size, height = size, x_t = -x_translate, y_t = -y_translate, rotation = rotation)?;
}, },
@ -163,6 +166,9 @@ pub fn shapes_write(id: Uuid) -> Result<Uuid, Error> {
let b = rng.gen_range(20.0, 50.0); let b = rng.gen_range(20.0, 50.0);
write!(&mut svg, "<polygon fill=\"{fill}\" x=\"{x}\" y=\"{y}\" points=\"{x0} {y0}, {x1} {y1}, {x2} {y2}\" transform=\"translate({x_translate}, {y_translate}) rotate({rotation})\" />", write!(&mut svg, "<polygon fill=\"{fill}\" x=\"{x}\" y=\"{y}\" points=\"{x0} {y0}, {x1} {y1}, {x2} {y2}\" transform=\"translate({x_translate}, {y_translate}) rotate({rotation})\" />",
fill = colour, x = -b / 2.0, y = h / 2.0, x0 = -b / 2.0, y0 = -h / 2.0, x1 = 0, y1 = b / 2.0, x2 = b / 2.0, y2 = -h / 2.0, rotation = rotation, x_translate = x_translate, y_translate = y_translate)?; fill = colour, x = -b / 2.0, y = h / 2.0, x0 = -b / 2.0, y0 = -h / 2.0, x1 = 0, y1 = b / 2.0, x2 = b / 2.0, y2 = -h / 2.0, rotation = rotation, x_translate = x_translate, y_translate = y_translate)?;
if scalar == 0.0 && rng.gen_bool(0.5) {
continue;
}
write!(&mut svg, "<polygon fill=\"{fill}\" x=\"{x}\" y=\"{y}\" points=\"{x0} {y0}, {x1} {y1}, {x2} {y2}\" transform=\"translate({x_translate}, {y_translate}) rotate({rotation})\" />", write!(&mut svg, "<polygon fill=\"{fill}\" x=\"{x}\" y=\"{y}\" points=\"{x0} {y0}, {x1} {y1}, {x2} {y2}\" transform=\"translate({x_translate}, {y_translate}) rotate({rotation})\" />",
fill = colour, x = -b / 2.0, y = h / 2.0, x0 = -b / 2.0, y0 = -h / 2.0, x1 = 0, y1 = b / 2.0, x2 = b / 2.0, y2 = -h / 2.0, rotation = rotation + 180, x_translate = -x_translate, y_translate = -y_translate)?; fill = colour, x = -b / 2.0, y = h / 2.0, x0 = -b / 2.0, y0 = -h / 2.0, x1 = 0, y1 = b / 2.0, x2 = b / 2.0, y2 = -h / 2.0, rotation = rotation + 180, x_translate = -x_translate, y_translate = -y_translate)?;
}, },
@ -178,6 +184,9 @@ pub fn shapes_write(id: Uuid) -> Result<Uuid, Error> {
let height = rng.gen_range(20.0, 50.0); let height = rng.gen_range(20.0, 50.0);
write!(&mut svg, "<rect fill=\"{fill}\" x=\"-{x}\" y=\"-{y}\" width=\"{width}\" height=\"{height}\" transform=\"translate({x_t}, {y_t}) rotate({rotation})\" />", write!(&mut svg, "<rect fill=\"{fill}\" x=\"-{x}\" y=\"-{y}\" width=\"{width}\" height=\"{height}\" transform=\"translate({x_t}, {y_t}) rotate({rotation})\" />",
fill = colour, x = width / 2.0, y = height / 2.0, width = width, height = height, x_t = x_translate, y_t = y_translate, rotation = rotation)?; fill = colour, x = width / 2.0, y = height / 2.0, width = width, height = height, x_t = x_translate, y_t = y_translate, rotation = rotation)?;
if scalar == 0.0 && rng.gen_bool(0.5) {
continue;
}
write!(&mut svg, "<rect fill=\"{fill}\" x=\"-{x}\" y=\"-{y}\" width=\"{width}\" height=\"{height}\" transform=\"translate({x_t}, {y_t}) rotate({rotation})\" />", write!(&mut svg, "<rect fill=\"{fill}\" x=\"-{x}\" y=\"-{y}\" width=\"{width}\" height=\"{height}\" transform=\"translate({x_t}, {y_t}) rotate({rotation})\" />",
fill = colour, x = width / 2.0, y = height / 2.0, width = width, height = height, x_t = -x_translate, y_t = -y_translate, rotation = rotation)?; fill = colour, x = width / 2.0, y = height / 2.0, width = width, height = height, x_t = -x_translate, y_t = -y_translate, rotation = rotation)?;
}, },
@ -188,11 +197,30 @@ pub fn shapes_write(id: Uuid) -> Result<Uuid, Error> {
write!(&mut svg, "<polyline fill=\"none\" stroke=\"{fill}\" stroke-width=\"{width}\" x=\"{x}\" y=\"{y}\" points=\"{x0} {y0}, {x1} {y1}, {x2} {y2}\" transform=\"translate({x_translate}, {y_translate}) rotate({rotation})\" />", write!(&mut svg, "<polyline fill=\"none\" stroke=\"{fill}\" stroke-width=\"{width}\" x=\"{x}\" y=\"{y}\" points=\"{x0} {y0}, {x1} {y1}, {x2} {y2}\" transform=\"translate({x_translate}, {y_translate}) rotate({rotation})\" />",
fill = colour, width = width, x = -b / 2.0, y = h / 2.0, x0 = -b / 2.0, y0 = -h / 2.0, x1 = 0, y1 = b / 2.0, x2 = b / 2.0, y2 = -h / 2.0, rotation = rotation, x_translate = x_translate, y_translate = y_translate)?; fill = colour, width = width, x = -b / 2.0, y = h / 2.0, x0 = -b / 2.0, y0 = -h / 2.0, x1 = 0, y1 = b / 2.0, x2 = b / 2.0, y2 = -h / 2.0, rotation = rotation, x_translate = x_translate, y_translate = y_translate)?;
if scalar == 0.0 && rng.gen_bool(0.5) {
continue;
}
write!(&mut svg, "<polyline fill=\"none\" stroke=\"{fill}\" stroke-width=\"{width}\" x=\"{x}\" y=\"{y}\" points=\"{x0} {y0}, {x1} {y1}, {x2} {y2}\" transform=\"translate({x_translate}, {y_translate}) rotate({rotation})\" />", write!(&mut svg, "<polyline fill=\"none\" stroke=\"{fill}\" stroke-width=\"{width}\" x=\"{x}\" y=\"{y}\" points=\"{x0} {y0}, {x1} {y1}, {x2} {y2}\" transform=\"translate({x_translate}, {y_translate}) rotate({rotation})\" />",
fill = colour, width = width, x = -b / 2.0, y = h / 2.0, x0 = -b / 2.0, y0 = -h / 2.0, x1 = 0, y1 = b / 2.0, x2 = b / 2.0, y2 = -h / 2.0, rotation = rotation + 180, x_translate = -x_translate, y_translate = -y_translate)?; fill = colour, width = width, x = -b / 2.0, y = h / 2.0, x0 = -b / 2.0, y0 = -h / 2.0, x1 = 0, y1 = b / 2.0, x2 = b / 2.0, y2 = -h / 2.0, rotation = rotation + 180, x_translate = -x_translate, y_translate = -y_translate)?;
}, },
ConstructShapes::Tri => { ConstructShapes::Tri => {
let width = rng.gen_range(2.0, 4.0);
let length = rng.gen_range(12.5, 25.0);
let x0 = (0.0 as f64).cos() * length;
let y0 = (0.0 as f64).sin() * length;
let x1 = ((f64::consts::PI * 2.0) / 3.0).cos() * length;
let y1 = ((f64::consts::PI * 2.0) / 3.0).sin() * length;
let x2 = ((f64::consts::PI * 4.0) / 3.0).cos() * length;
let y2 = ((f64::consts::PI * 4.0) / 3.0).sin() * length;
write!(&mut svg, "<path stroke=\"{fill}\" stroke-width=\"{width}\" d=\"M{x0} {y0}L 0 0 M{x1} {y1}L 0 0 M{x2} {y2}L 0 0 \" transform=\"translate({x_translate}, {y_translate}) rotate({rotation})\" />",
fill = colour, width = width, x0 = x0, y0 = y0, x1 = x1, y1 = y1, x2 = x2, y2 = y2, rotation = rotation, x_translate = x_translate, y_translate = y_translate)?;
if scalar == 0.0 && rng.gen_bool(0.5) {
continue;
}
write!(&mut svg, "<path stroke=\"{fill}\" stroke-width=\"{width}\" d=\"M{x0} {y0}L 0 0 M{x1} {y1}L 0 0 M{x2} {y2}L 0 0 \" transform=\"translate({x_translate}, {y_translate}) rotate({rotation})\" />",
fill = colour, width = width, x0 = x0, y0 = y0, x1 = x1, y1 = y1, x2 = x2, y2 = y2, rotation = rotation, x_translate = -x_translate, y_translate = -y_translate)?;
}, },
ConstructShapes::Plus => { ConstructShapes::Plus => {

View File

@ -62,6 +62,10 @@ pub enum RpcMessage {
QueueJoined(()), QueueJoined(()),
QueueCancelled(()), QueueCancelled(()),
InviteRequested(()),
Invite(String),
Joining(()),
Error(String), Error(String),
} }
@ -91,6 +95,8 @@ pub enum RpcRequest {
SubscriptionState {}, SubscriptionState {},
EmailState {}, EmailState {},
InstanceInvite {},
InstanceJoin { code: String },
InstanceQueue {}, InstanceQueue {},
InstancePractice {}, InstancePractice {},
InstanceAbandon { instance_id: Uuid }, InstanceAbandon { instance_id: Uuid },
@ -136,21 +142,27 @@ impl Connection {
None => return Err(err_msg("auth required")), None => return Err(err_msg("auth required")),
}; };
// evented but authorization required
match v {
RpcRequest::InstanceQueue {} => {
self.events.send(Event::Queue(self.id))?;
return Ok(RpcMessage::QueueRequested(()));
},
_ => (),
};
// all good, let's make a tx and process
let mut tx = db.transaction()?;
let request = v.clone(); let request = v.clone();
let response = match v { 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(()))
},
_ => {
// all good, let's make a tx and process
let mut tx = db.transaction()?;
let res = match v {
RpcRequest::AccountState {} => RpcRequest::AccountState {} =>
Ok(RpcMessage::AccountState(account.clone())), Ok(RpcMessage::AccountState(account.clone())),
RpcRequest::AccountConstructs {} => RpcRequest::AccountConstructs {} =>
@ -228,6 +240,9 @@ impl Connection {
}; };
tx.commit()?; tx.commit()?;
res
}
};
info!("request={:?} account={:?} duration={:?}", request, account.name, begin.elapsed()); info!("request={:?} account={:?} duration={:?}", request, account.name, begin.elapsed());

View File

@ -90,6 +90,10 @@ impl Warden {
fn on_match(&mut self, pair: Pair) -> Result<(), Error> { fn on_match(&mut self, pair: Pair) -> Result<(), Error> {
info!("received pair={:?}", pair); info!("received pair={:?}", pair);
// clear pvp status
self.events.send(Event::Joined(pair.0.id))?;
self.events.send(Event::Joined(pair.1.id))?;
let db = self.pool.get()?; let db = self.pool.get()?;
let mut tx = db.transaction()?; let mut tx = db.transaction()?;