Merge branch 'release/1.12.0'
This commit is contained in:
commit
d1606e9117
29
WORKLOG.md
29
WORKLOG.md
@ -3,32 +3,16 @@
|
|||||||
|
|
||||||
_ntr_
|
_ntr_
|
||||||
* can't reset password without knowing password =\
|
* can't reset password without knowing password =\
|
||||||
* change cooldowns to delay & recharge
|
|
||||||
- delay is cooldown before skill can first be used
|
|
||||||
- recharge is cooldown after using skill
|
|
||||||
- every x speed reduces delay of skills
|
|
||||||
* audio
|
* audio
|
||||||
* animation effects
|
* animation effects
|
||||||
* vbox combine / buy / equip etc
|
* vbox combine / buy / equip etc
|
||||||
* background music
|
* background music
|
||||||
* effects rework
|
|
||||||
|
|
||||||
Siphon =
|
|
||||||
[
|
|
||||||
DamageBlue(50%),
|
|
||||||
Apply(
|
|
||||||
Siphon(2T)
|
|
||||||
- Siphoning(2T)
|
|
||||||
),
|
|
||||||
]
|
|
||||||
|
|
||||||
Hexagon Set
|
|
||||||
- Pick Colour
|
|
||||||
- Random Walk
|
|
||||||
- Draw hex
|
|
||||||
- Increase intensity for each visit
|
|
||||||
|
|
||||||
_mashy_
|
_mashy_
|
||||||
|
* combat text
|
||||||
|
- last 3-4 text events (damage / heal / disable etc)
|
||||||
|
|
||||||
* rebalance
|
* rebalance
|
||||||
* speed specs
|
* speed specs
|
||||||
* life specs
|
* life specs
|
||||||
@ -111,6 +95,11 @@ _tba_
|
|||||||
* treats
|
* treats
|
||||||
* client animation bpm
|
* client animation bpm
|
||||||
* background colour changes depending on time of day
|
* background colour changes depending on time of day
|
||||||
|
Hexagon Set
|
||||||
|
- Pick Colour
|
||||||
|
- Random Walk
|
||||||
|
- Draw hex
|
||||||
|
- Increase intensity for each visit
|
||||||
|
|
||||||
# Mechanics
|
# Mechanics
|
||||||
* 10d chaos maths, not rock paper scissors
|
* 10d chaos maths, not rock paper scissors
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "mnml-client",
|
"name": "mnml-client",
|
||||||
"version": "1.11.2",
|
"version": "1.12.0",
|
||||||
"description": "",
|
"description": "",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
@ -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;
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "mnml-client",
|
"name": "mnml-client",
|
||||||
"version": "1.11.2",
|
"version": "1.12.0",
|
||||||
"description": "",
|
"description": "",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
@ -19,8 +19,8 @@ const addState = connect(
|
|||||||
} = state;
|
} = state;
|
||||||
|
|
||||||
|
|
||||||
function sendSetPassword(current, password) {
|
function sendSetPassword(password) {
|
||||||
postData('/account/password', { current, password })
|
postData('/account/password', { password })
|
||||||
.then(res => res.json())
|
.then(res => res.json())
|
||||||
.then(data => {
|
.then(data => {
|
||||||
if (data.error) return errorToast(data.error);
|
if (data.error) return errorToast(data.error);
|
||||||
@ -74,7 +74,7 @@ class AccountStatus extends Component {
|
|||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
passwordState: { current: '', password: '', confirm: ''},
|
passwordState: { password: '', confirm: ''},
|
||||||
emailState: null,
|
emailState: null,
|
||||||
unsubState: false,
|
unsubState: false,
|
||||||
};
|
};
|
||||||
@ -105,8 +105,8 @@ class AccountStatus extends Component {
|
|||||||
passwordState.password === passwordState.confirm;
|
passwordState.password === passwordState.confirm;
|
||||||
|
|
||||||
const setPasswordDisabled = () => {
|
const setPasswordDisabled = () => {
|
||||||
const { current, password, confirm } = passwordState;
|
const { password, confirm } = passwordState;
|
||||||
return !(passwordsEqual() && password && current && confirm);
|
return !(passwordsEqual() && password && confirm);
|
||||||
}
|
}
|
||||||
|
|
||||||
const tlClick = e => {
|
const tlClick = e => {
|
||||||
@ -173,15 +173,7 @@ class AccountStatus extends Component {
|
|||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<h3>Password</h3>
|
<h3>Password</h3>
|
||||||
<label for="current">Password:</label>
|
<label for="current">Set Password:</label>
|
||||||
<input
|
|
||||||
class="login-input"
|
|
||||||
type="password"
|
|
||||||
name="current"
|
|
||||||
value={passwordState.current}
|
|
||||||
onInput={linkState(this, 'passwordState.current')}
|
|
||||||
placeholder="current"
|
|
||||||
/>
|
|
||||||
<input
|
<input
|
||||||
class="login-input"
|
class="login-input"
|
||||||
type="password"
|
type="password"
|
||||||
@ -202,7 +194,7 @@ class AccountStatus extends Component {
|
|||||||
/>
|
/>
|
||||||
<button
|
<button
|
||||||
disabled={setPasswordDisabled()}
|
disabled={setPasswordDisabled()}
|
||||||
onClick={() => sendSetPassword(passwordState.current, passwordState.password)}>
|
onClick={() => sendSetPassword(passwordState.password)}>
|
||||||
Set Password
|
Set Password
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -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>
|
||||||
|
|||||||
@ -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() {
|
||||||
getInstanceState();
|
if (authenticated) {
|
||||||
|
getInstanceState();
|
||||||
|
}
|
||||||
quit();
|
quit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -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>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -232,7 +232,6 @@ function Play(args) {
|
|||||||
<div>
|
<div>
|
||||||
Join our Discord server to find opponents and talk to the devs. <br />
|
Join our Discord server to find opponents and talk to the devs. <br />
|
||||||
Message <b>@ntr</b> or <b>@mashy</b> for some credits to get started.<br />
|
Message <b>@ntr</b> or <b>@mashy</b> for some credits to get started.<br />
|
||||||
<a href='https://www.youtube.com/watch?v=VtZLlkpJuS8'>Tutorial Playthrough on YouTube</a>
|
|
||||||
</div>
|
</div>
|
||||||
<br />
|
<br />
|
||||||
<div>
|
<div>
|
||||||
|
|||||||
@ -96,7 +96,7 @@ function genItemInfo(item, itemInfo, player) {
|
|||||||
itemSourceInfo = reactStringReplace(itemSourceInfo, itemRegEx, match => shapes[match]());
|
itemSourceInfo = reactStringReplace(itemSourceInfo, itemRegEx, match => shapes[match]());
|
||||||
}
|
}
|
||||||
|
|
||||||
const cooldown = isSkill && fullInfo.cooldown ? <div>{fullInfo.cooldown} Turn delay</div> : null;
|
const cooldown = isSkill && fullInfo.cooldown ? <div>{fullInfo.delay} turn delay, {fullInfo.cooldown} turn cooldown</div> : null;
|
||||||
|
|
||||||
const speed = isSkill
|
const speed = isSkill
|
||||||
? <div> Speed {shapes.SpeedStat()} multiplier {fullInfo.speed * 4}% </div>
|
? <div> Speed {shapes.SpeedStat()} multiplier {fullInfo.speed * 4}% </div>
|
||||||
|
|||||||
@ -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);
|
||||||
|
|||||||
@ -71,7 +71,7 @@ module.exports = {
|
|||||||
},
|
},
|
||||||
speedStat: {
|
speedStat: {
|
||||||
item: 'SPEED',
|
item: 'SPEED',
|
||||||
description: 'Speed determines the order in which skills resolve.\nCombine SPEED specs to increase speed.',
|
description: 'Speed determines the order in which skills resolve.\nThe initial delay of skills is reduced by 1 turn for every 250 speed.\nCombine SPEED specs to increase speed.',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@ -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,
|
||||||
|
|
||||||
|
|||||||
@ -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,
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "mnml_core"
|
name = "mnml_core"
|
||||||
version = "1.11.2"
|
version = "1.12.0"
|
||||||
authors = ["ntr <ntr@smokestack.io>", "mashy <mashy@mnml.gg>"]
|
authors = ["ntr <ntr@smokestack.io>", "mashy <mashy@mnml.gg>"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
|||||||
@ -52,10 +52,14 @@ impl ConstructSkill {
|
|||||||
pub fn new(skill: Skill) -> ConstructSkill {
|
pub fn new(skill: Skill) -> ConstructSkill {
|
||||||
ConstructSkill {
|
ConstructSkill {
|
||||||
skill,
|
skill,
|
||||||
cd: skill.base_cd(),
|
cd: skill.delay(),
|
||||||
disabled: false,
|
disabled: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn set_cooldown(&mut self, cd: Cooldown) -> () {
|
||||||
|
self.cd = cd;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)]
|
#[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)]
|
||||||
@ -432,15 +436,34 @@ impl Construct {
|
|||||||
self.skills.iter().find(|s| s.skill == skill && s.cd.is_some())
|
self.skills.iter().find(|s| s.skill == skill && s.cd.is_some())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn set_construct_delays(&mut self) -> () {
|
||||||
|
// for every multiple of speed threshold delays are reduced by 1 at start of game
|
||||||
|
let speed_threshold = 250;
|
||||||
|
let delay_reduction = self.speed.value.wrapping_div(speed_threshold);
|
||||||
|
|
||||||
|
self.skills
|
||||||
|
.iter_mut()
|
||||||
|
.for_each(|s| match s.skill.delay() {
|
||||||
|
Some(cd) => match cd.saturating_sub(delay_reduction) {
|
||||||
|
0 => s.set_cooldown(None),
|
||||||
|
_ => s.set_cooldown(Some(cd.saturating_sub(delay_reduction)))
|
||||||
|
},
|
||||||
|
None => s.set_cooldown(None)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
pub fn skill_set_cd(&mut self, skill: Skill) -> &mut Construct {
|
pub fn skill_set_cd(&mut self, skill: Skill) -> &mut Construct {
|
||||||
// println!("{:?} {:?} skill cooldown set", self.name, skill);
|
// println!("{:?} {:?} skill cooldown set", self.name, skill);
|
||||||
|
|
||||||
// tests force resolve some skills
|
// tests force resolve some skills
|
||||||
// which cause the game to attempt to put them on cd
|
// which cause the game to attempt to put them on cd
|
||||||
// even though the construct doesn't know the skill
|
// even though the construct doesn't know the skill
|
||||||
if let Some(i) = self.skills.iter().position(|s| s.skill == skill) {
|
// if let Some(i) = self.skills.iter().position(|s| s.skill == skill) {
|
||||||
self.skills.remove(i);
|
// self.skills.remove(i);
|
||||||
self.skills.insert(i, ConstructSkill::new(skill));
|
// self.skills.insert(i, ConstructSkill::new(skill));
|
||||||
|
//}
|
||||||
|
if let Some(sk) = self.skills.iter_mut().find(|s| s.skill == skill) {
|
||||||
|
sk.set_cooldown(skill.base_cd());
|
||||||
}
|
}
|
||||||
|
|
||||||
self
|
self
|
||||||
@ -1218,7 +1241,7 @@ mod tests {
|
|||||||
i += 1;
|
i += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
assert_eq!(i, Skill::Sleep.base_cd().unwrap());
|
assert_eq!(i, Skill::Sleep.delay().unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -104,7 +104,6 @@ impl Game {
|
|||||||
// let player_description = player.constructs.iter().map(|c| c.name.clone()).collect::<Vec<String>>().join(", ");
|
// let player_description = player.constructs.iter().map(|c| c.name.clone()).collect::<Vec<String>>().join(", ");
|
||||||
// self.log.push(format!("{:} has joined the game. [{:}]", player.name, player_description));
|
// self.log.push(format!("{:} has joined the game. [{:}]", player.name, player_description));
|
||||||
|
|
||||||
player.constructs.sort_unstable_by_key(|c| c.id);
|
|
||||||
self.players.push(player);
|
self.players.push(player);
|
||||||
|
|
||||||
Ok(self)
|
Ok(self)
|
||||||
@ -140,12 +139,19 @@ impl Game {
|
|||||||
&& self.players.iter().all(|t| t.constructs.len() == self.player_constructs)
|
&& self.players.iter().all(|t| t.constructs.len() == self.player_constructs)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn start(self) -> Game {
|
pub fn start(mut self) -> Game {
|
||||||
// both forfeit ddue to no skills
|
// both forfeit ddue to no skills
|
||||||
if self.finished() {
|
if self.finished() {
|
||||||
return self.finish();
|
return self.finish();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.players
|
||||||
|
.iter_mut()
|
||||||
|
.for_each(|p| p.constructs
|
||||||
|
.iter_mut()
|
||||||
|
.for_each(|c| c.set_construct_delays())
|
||||||
|
);
|
||||||
|
|
||||||
self.skill_phase_start(0)
|
self.skill_phase_start(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1163,6 +1169,44 @@ mod tests {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn delay_test() {
|
||||||
|
let mut x = Construct::new()
|
||||||
|
.named(&"pronounced \"creeep\"".to_string())
|
||||||
|
.learn(Skill::Ruin);
|
||||||
|
|
||||||
|
let mut y = Construct::new()
|
||||||
|
.named(&"lemongrass tea".to_string())
|
||||||
|
.learn(Skill::Ruin);
|
||||||
|
|
||||||
|
// Ruin has 2 turn cd
|
||||||
|
// 250 speed = 1 cd delay reduction
|
||||||
|
x.speed.force(499);
|
||||||
|
y.speed.force(700);
|
||||||
|
|
||||||
|
let mut game = Game::new();
|
||||||
|
game.set_player_num(2).set_player_constructs(1);
|
||||||
|
|
||||||
|
let x_player_id = Uuid::new_v4();
|
||||||
|
x.account = x_player_id;
|
||||||
|
let x_player = Player::new(x_player_id, None, &"ntr".to_string(), vec![x]);
|
||||||
|
|
||||||
|
let y_player_id = Uuid::new_v4();
|
||||||
|
y.account = y_player_id;
|
||||||
|
let y_player = Player::new(y_player_id, None, &"mash".to_string(), vec![y]);
|
||||||
|
|
||||||
|
game
|
||||||
|
.player_add(x_player).unwrap()
|
||||||
|
.player_add(y_player).unwrap();
|
||||||
|
|
||||||
|
game = game.start();
|
||||||
|
|
||||||
|
assert!(game.players[0].constructs[0].skill_on_cd(Skill::Ruin).is_some());
|
||||||
|
assert!(game.players[1].constructs[0].skill_on_cd(Skill::Ruin).is_none());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn stun_test() {
|
fn stun_test() {
|
||||||
let mut game = create_test_game();
|
let mut game = create_test_game();
|
||||||
@ -1240,7 +1284,7 @@ mod tests {
|
|||||||
// should auto progress back to skill phase
|
// should auto progress back to skill phase
|
||||||
assert!(game.phase == Phase::Skill);
|
assert!(game.phase == Phase::Skill);
|
||||||
|
|
||||||
assert!(game.player_by_id(y_player.id).unwrap().constructs[0].skill_on_cd(Skill::Stun).is_some());
|
assert!(game.player_by_id(y_player.id).unwrap().constructs[0].skill_on_cd(Skill::Stun).is_none());
|
||||||
assert!(game.player_by_id(x_player.id).unwrap().constructs[0].skill_on_cd(Skill::Block).is_none());
|
assert!(game.player_by_id(x_player.id).unwrap().constructs[0].skill_on_cd(Skill::Block).is_none());
|
||||||
|
|
||||||
game.add_skill(x_player.id, x_construct.id, y_construct.id, Skill::Attack).unwrap();
|
game.add_skill(x_player.id, x_construct.id, y_construct.id, Skill::Attack).unwrap();
|
||||||
|
|||||||
@ -1362,6 +1362,7 @@ pub struct ItemInfo {
|
|||||||
pub values: Option<SpecValues>,
|
pub values: Option<SpecValues>,
|
||||||
pub skill: bool,
|
pub skill: bool,
|
||||||
pub speed: Option<usize>,
|
pub speed: Option<usize>,
|
||||||
|
pub delay: Cooldown,
|
||||||
pub cooldown: Cooldown,
|
pub cooldown: Cooldown,
|
||||||
pub description: String,
|
pub description: String,
|
||||||
}
|
}
|
||||||
@ -1406,6 +1407,10 @@ pub fn item_info() -> ItemInfoCtr {
|
|||||||
Some(s) => s.base_cd(),
|
Some(s) => s.base_cd(),
|
||||||
None => None
|
None => None
|
||||||
},
|
},
|
||||||
|
delay: match v.into_skill() {
|
||||||
|
Some(s) => s.delay(),
|
||||||
|
None => None
|
||||||
|
},
|
||||||
})
|
})
|
||||||
.collect::<Vec<ItemInfo>>();
|
.collect::<Vec<ItemInfo>>();
|
||||||
|
|
||||||
|
|||||||
@ -65,7 +65,9 @@ pub struct Player {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Player {
|
impl Player {
|
||||||
pub fn new(account: Uuid, img: Option<Uuid>, name: &String, constructs: Vec<Construct>) -> Player {
|
pub fn new(account: Uuid, img: Option<Uuid>, name: &String, mut constructs: Vec<Construct>) -> Player {
|
||||||
|
constructs.sort_unstable_by_key(|c| c.id);
|
||||||
|
|
||||||
Player {
|
Player {
|
||||||
id: account,
|
id: account,
|
||||||
img,
|
img,
|
||||||
|
|||||||
@ -522,6 +522,157 @@ impl Skill {
|
|||||||
Skill::SustainPlus |
|
Skill::SustainPlus |
|
||||||
Skill::SustainPlusPlus => Some(1),
|
Skill::SustainPlusPlus => Some(1),
|
||||||
|
|
||||||
|
Skill::Intercept => Some(2),
|
||||||
|
Skill::InterceptPlus => Some(2),
|
||||||
|
Skill::InterceptPlusPlus => Some(2),
|
||||||
|
|
||||||
|
Skill::Electrify |
|
||||||
|
Skill::ElectrifyPlus |
|
||||||
|
Skill::ElectrifyPlusPlus => None,
|
||||||
|
|
||||||
|
Skill::Absorb |
|
||||||
|
Skill::AbsorbPlus |
|
||||||
|
Skill::AbsorbPlusPlus => Some(1),
|
||||||
|
|
||||||
|
//-----------
|
||||||
|
// Never cast directly
|
||||||
|
//---------
|
||||||
|
// Trigger
|
||||||
|
Skill::HybridBlast |
|
||||||
|
Skill::HasteStrike |
|
||||||
|
Skill::CounterAttack|
|
||||||
|
Skill::CounterAttackPlus |
|
||||||
|
Skill::CounterAttackPlusPlus | // counter
|
||||||
|
Skill::Electrocute|
|
||||||
|
Skill::ElectrocutePlus |
|
||||||
|
Skill::ElectrocutePlusPlus |
|
||||||
|
Skill::Absorption|
|
||||||
|
Skill::AbsorptionPlus |
|
||||||
|
Skill::AbsorptionPlusPlus |
|
||||||
|
// Ticks
|
||||||
|
Skill::ElectrocuteTick|
|
||||||
|
Skill::DecayTick|
|
||||||
|
Skill::SiphonTick|
|
||||||
|
Skill::TriageTick => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn delay(&self) -> Cooldown {
|
||||||
|
match self {
|
||||||
|
Skill::Attack => None,
|
||||||
|
Skill::Block => None, // reduce damage
|
||||||
|
Skill::Buff => None,
|
||||||
|
Skill::Debuff => None,
|
||||||
|
Skill::Stun => Some(1),
|
||||||
|
|
||||||
|
Skill::Strike=> None,
|
||||||
|
Skill::StrikePlus => None,
|
||||||
|
Skill::StrikePlusPlus => None,
|
||||||
|
|
||||||
|
Skill::Counter|
|
||||||
|
Skill::CounterPlus |
|
||||||
|
Skill::CounterPlusPlus => None,
|
||||||
|
|
||||||
|
Skill::Restrict |
|
||||||
|
Skill::RestrictPlus |
|
||||||
|
Skill::RestrictPlusPlus => Some(1),
|
||||||
|
|
||||||
|
Skill::Bash |
|
||||||
|
Skill::BashPlus |
|
||||||
|
Skill::BashPlusPlus => Some(1),
|
||||||
|
|
||||||
|
Skill::Heal=> None,
|
||||||
|
Skill::HealPlus => None,
|
||||||
|
Skill::HealPlusPlus => None,
|
||||||
|
|
||||||
|
Skill::Triage=> None, // hot
|
||||||
|
Skill::TriagePlus => None, // hot
|
||||||
|
Skill::TriagePlusPlus => None, // hot
|
||||||
|
|
||||||
|
Skill::Break | // no damage stun, adds vulnerable
|
||||||
|
Skill::BreakPlus |
|
||||||
|
Skill::BreakPlusPlus => Some(1),
|
||||||
|
|
||||||
|
Skill::Blast |
|
||||||
|
Skill::BlastPlus |
|
||||||
|
Skill::BlastPlusPlus => None,
|
||||||
|
|
||||||
|
Skill::Chaos |
|
||||||
|
Skill::ChaosPlus |
|
||||||
|
Skill::ChaosPlusPlus => None,
|
||||||
|
|
||||||
|
Skill::Amplify |
|
||||||
|
Skill::AmplifyPlus |
|
||||||
|
Skill::AmplifyPlusPlus => Some(1),
|
||||||
|
|
||||||
|
Skill::Hybrid |
|
||||||
|
Skill::HybridPlus |
|
||||||
|
Skill::HybridPlusPlus => Some(1),
|
||||||
|
|
||||||
|
Skill::Invert |
|
||||||
|
Skill::InvertPlus |
|
||||||
|
Skill::InvertPlusPlus => Some(2),
|
||||||
|
|
||||||
|
Skill::Decay => None, // dot
|
||||||
|
Skill::DecayPlus => None,
|
||||||
|
Skill::DecayPlusPlus => None,
|
||||||
|
|
||||||
|
Skill::Siphon|
|
||||||
|
Skill::SiphonPlus |
|
||||||
|
Skill::SiphonPlusPlus => None,
|
||||||
|
|
||||||
|
Skill::Curse |
|
||||||
|
Skill::CursePlus |
|
||||||
|
Skill::CursePlusPlus => Some(1),
|
||||||
|
|
||||||
|
Skill::Link |
|
||||||
|
Skill::LinkPlus |
|
||||||
|
Skill::LinkPlusPlus => Some(1),
|
||||||
|
|
||||||
|
Skill::Silence |
|
||||||
|
Skill::SilencePlus |
|
||||||
|
Skill::SilencePlusPlus => Some(1),
|
||||||
|
|
||||||
|
Skill::Purify |
|
||||||
|
Skill::PurifyPlus |
|
||||||
|
Skill::PurifyPlusPlus => None,
|
||||||
|
|
||||||
|
Skill::Purge |
|
||||||
|
Skill::PurgePlus |
|
||||||
|
Skill::PurgePlusPlus => Some(1),
|
||||||
|
|
||||||
|
Skill::Banish |
|
||||||
|
Skill::BanishPlus |
|
||||||
|
Skill::BanishPlusPlus => Some(1),
|
||||||
|
|
||||||
|
Skill::Haste |
|
||||||
|
Skill::HastePlus |
|
||||||
|
Skill::HastePlusPlus => Some(1),
|
||||||
|
|
||||||
|
Skill::Reflect |
|
||||||
|
Skill::ReflectPlus |
|
||||||
|
Skill::ReflectPlusPlus => None,
|
||||||
|
|
||||||
|
Skill::Recharge |
|
||||||
|
Skill::RechargePlus |
|
||||||
|
Skill::RechargePlusPlus => None,
|
||||||
|
|
||||||
|
Skill::Ruin |
|
||||||
|
Skill::RuinPlus |
|
||||||
|
Skill::RuinPlusPlus => Some(2),
|
||||||
|
|
||||||
|
Skill::Slay=> None,
|
||||||
|
Skill::SlayPlus => None,
|
||||||
|
Skill::SlayPlusPlus => None,
|
||||||
|
|
||||||
|
Skill::Sleep |
|
||||||
|
Skill::SleepPlus |
|
||||||
|
Skill::SleepPlusPlus => Some(1),
|
||||||
|
|
||||||
|
Skill::Sustain |
|
||||||
|
Skill::SustainPlus |
|
||||||
|
Skill::SustainPlusPlus => Some(1),
|
||||||
|
|
||||||
Skill::Intercept => Some(1),
|
Skill::Intercept => Some(1),
|
||||||
Skill::InterceptPlus => Some(1),
|
Skill::InterceptPlus => Some(1),
|
||||||
Skill::InterceptPlusPlus => Some(1),
|
Skill::InterceptPlusPlus => Some(1),
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "mnml-ops",
|
"name": "mnml-ops",
|
||||||
"version": "1.11.2",
|
"version": "1.12.0",
|
||||||
"description": "",
|
"description": "",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "mnml"
|
name = "mnml"
|
||||||
version = "1.11.2"
|
version = "1.12.0"
|
||||||
authors = ["ntr <ntr@smokestack.io>"]
|
authors = ["ntr <ntr@smokestack.io>"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
|||||||
@ -213,42 +213,42 @@ pub fn new_img(tx: &mut Transaction, id: Uuid) -> Result<Account, Error> {
|
|||||||
Account::try_from(row)
|
Account::try_from(row)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_password(tx: &mut Transaction, id: Uuid, current: &String, password: &String) -> Result<String, MnmlHttpError> {
|
pub fn set_password(tx: &mut Transaction, id: Uuid, password: &String) -> Result<String, MnmlHttpError> {
|
||||||
if password.len() < PASSWORD_MIN_LEN || password.len() > 100 {
|
if password.len() < PASSWORD_MIN_LEN || password.len() > 100 {
|
||||||
return Err(MnmlHttpError::PasswordUnacceptable);
|
return Err(MnmlHttpError::PasswordUnacceptable);
|
||||||
}
|
}
|
||||||
|
|
||||||
let query = "
|
// let query = "
|
||||||
SELECT id, password
|
// SELECT id, password
|
||||||
FROM accounts
|
// FROM accounts
|
||||||
WHERE id = $1
|
// WHERE id = $1
|
||||||
";
|
// ";
|
||||||
|
|
||||||
let result = tx
|
// let result = tx
|
||||||
.query(query, &[&id])?;
|
// .query(query, &[&id])?;
|
||||||
|
|
||||||
let row = match result.iter().next() {
|
// let row = match result.iter().next() {
|
||||||
Some(row) => row,
|
// Some(row) => row,
|
||||||
None => {
|
// None => {
|
||||||
let mut rng = thread_rng();
|
// let mut rng = thread_rng();
|
||||||
let garbage: String = iter::repeat(())
|
// let garbage: String = iter::repeat(())
|
||||||
.map(|()| rng.sample(Alphanumeric))
|
// .map(|()| rng.sample(Alphanumeric))
|
||||||
.take(64)
|
// .take(64)
|
||||||
.collect();
|
// .collect();
|
||||||
|
|
||||||
// verify garbage to prevent timing attacks
|
// // verify garbage to prevent timing attacks
|
||||||
verify(garbage.clone(), &garbage).ok();
|
// verify(garbage.clone(), &garbage).ok();
|
||||||
return Err(MnmlHttpError::AccountNotFound);
|
// return Err(MnmlHttpError::AccountNotFound);
|
||||||
},
|
// },
|
||||||
};
|
// };
|
||||||
|
|
||||||
let id: Uuid = row.get(0);
|
// let id: Uuid = row.get(0);
|
||||||
let db_pw: String = row.get(1);
|
// let db_pw: String = row.get(1);
|
||||||
|
|
||||||
// return bad request to prevent being logged out
|
// // return bad request to prevent being logged out
|
||||||
if !verify(current, &db_pw)? {
|
// if !verify(current, &db_pw)? {
|
||||||
return Err(MnmlHttpError::BadRequest);
|
// return Err(MnmlHttpError::BadRequest);
|
||||||
}
|
// }
|
||||||
|
|
||||||
let password = hash(&password, PASSWORD_ROUNDS)?;
|
let password = hash(&password, PASSWORD_ROUNDS)?;
|
||||||
|
|
||||||
|
|||||||
@ -369,7 +369,7 @@ fn recover(req: &mut Request) -> IronResult<Response> {
|
|||||||
|
|
||||||
#[derive(Debug,Clone,Deserialize)]
|
#[derive(Debug,Clone,Deserialize)]
|
||||||
struct SetPassword {
|
struct SetPassword {
|
||||||
current: String,
|
// current: String,
|
||||||
password: String,
|
password: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -385,7 +385,7 @@ fn set_password(req: &mut Request) -> IronResult<Response> {
|
|||||||
let db = state.pool.get().or(Err(MnmlHttpError::DbError))?;
|
let db = state.pool.get().or(Err(MnmlHttpError::DbError))?;
|
||||||
let mut tx = db.transaction().or(Err(MnmlHttpError::DbError))?;
|
let mut tx = db.transaction().or(Err(MnmlHttpError::DbError))?;
|
||||||
|
|
||||||
let token = account::set_password(&mut tx, a.id, ¶ms.current, ¶ms.password)?;
|
let token = account::set_password(&mut tx, a.id, ¶ms.password)?;
|
||||||
|
|
||||||
tx.commit().or(Err(MnmlHttpError::ServerError))?;
|
tx.commit().or(Err(MnmlHttpError::ServerError))?;
|
||||||
|
|
||||||
|
|||||||
@ -42,10 +42,10 @@ pub enum Mail {
|
|||||||
fn recover(email: &String, name: &String, token: &String) -> SendableEmail {
|
fn recover(email: &String, name: &String, token: &String) -> SendableEmail {
|
||||||
let body = format!("{:},
|
let body = format!("{:},
|
||||||
the link below will recover your account.
|
the link below will recover your account.
|
||||||
please change your password immediately in the account page.
|
please change your password immediately in the account page
|
||||||
this link will expire in 48 hours or once used.
|
as this link will expire in 48 hours or once used.
|
||||||
|
|
||||||
http://mnml.gg/api/account/recover?recover_token={:}
|
https://mnml.gg/api/account/recover?recover_token={:}
|
||||||
|
|
||||||
glhf
|
glhf
|
||||||
--mnml", name, token);
|
--mnml", name, token);
|
||||||
@ -63,7 +63,7 @@ glhf
|
|||||||
fn confirm(email: &String, name: &String, token: &String) -> SendableEmail {
|
fn confirm(email: &String, name: &String, token: &String) -> SendableEmail {
|
||||||
let confirm_body = format!("{:},
|
let confirm_body = format!("{:},
|
||||||
please click the link below to confirm your email
|
please click the link below to confirm your email
|
||||||
http://mnml.gg/api/account/email/confirm?confirm_token={:}
|
https://mnml.gg/api/account/email/confirm?confirm_token={:}
|
||||||
|
|
||||||
glhf
|
glhf
|
||||||
--mnml", name, token);
|
--mnml", name, token);
|
||||||
|
|||||||
@ -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)?;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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))
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|||||||
@ -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))
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@ -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)?)),
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "mnml-studios",
|
"name": "mnml-studios",
|
||||||
"version": "1.11.2",
|
"version": "1.12.0",
|
||||||
"description": "",
|
"description": "",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user