shop bois

This commit is contained in:
ntr 2019-07-13 18:27:38 +10:00
parent 52b1e14541
commit 2fa7310aaa
16 changed files with 210 additions and 121 deletions

View File

@ -23,6 +23,7 @@ export const setResolution = value => ({ type: 'SET_RESOLUTION', value });
export const setShowLog = value => ({ type: 'SET_SHOW_LOG', value }); export const setShowLog = value => ({ type: 'SET_SHOW_LOG', value });
export const setShowNav = value => ({ type: 'SET_SHOW_NAV', value }); export const setShowNav = value => ({ type: 'SET_SHOW_NAV', value });
export const setSkip = value => ({ type: 'SET_SKIP', value }); export const setSkip = value => ({ type: 'SET_SKIP', value });
export const setShop = value => ({ type: 'SET_SHOP', value });
export const setTeam = value => ({ type: 'SET_SELECTED_CONSTRUCTS', value: Array.from(value) }); export const setTeam = value => ({ type: 'SET_SELECTED_CONSTRUCTS', value: Array.from(value) });
export const setVboxHighlight = value => ({ type: 'SET_VBOX_HIGHLIGHT', value }); export const setVboxHighlight = value => ({ type: 'SET_VBOX_HIGHLIGHT', value });
export const setWs = value => ({ type: 'SET_WS', value }); export const setWs = value => ({ type: 'SET_WS', value });

View File

@ -96,7 +96,7 @@ function AccountStatus(args) {
{saw(pingColour(ping))} {saw(pingColour(ping))}
<div class="ping-text">{ping}ms</div> <div class="ping-text">{ping}ms</div>
</div> </div>
<h3 class="account-header">{`¤${account.credits}`}</h3> <h3 class="account-header">{`¤${account.balance}`}</h3>
<Elements> <Elements>
<StripeBitsBtn account={account} /> <StripeBitsBtn account={account} />
</Elements> </Elements>

View File

@ -1,7 +1,6 @@
const anime = require('animejs').default; const anime = require('animejs').default;
function idle(id) { function idle(id) {
return false;
const duration = anime.random(2000, 18000); const duration = anime.random(2000, 18000);
const target = document.getElementById(id); const target = document.getElementById(id);
return anime({ return anime({

View File

@ -31,7 +31,7 @@ class ConstructAvatar extends Component {
<div <div
class="avatar" class="avatar"
id={this.props.construct.id} id={this.props.construct.id}
style={{ 'background-image': `url(/imgs/32809d3b-60e6-4d37-a183-e5d33374613b.svg)` }} style={{ 'background-image': `url(/imgs/${this.props.construct.img}.svg)` }}
/> />
); );
} }

View File

@ -6,12 +6,19 @@ const actions = require('./../actions');
const addState = connect( const addState = connect(
function receiveState(state) { function receiveState(state) {
const { const {
// ws, ws,
account, account,
shop,
} = state; } = state;
function mtxBuy(mtx) {
return ws.sendMtxBuy(mtx.variant);
}
return { return {
account, account,
shop,
mtxBuy,
}; };
}, },
@ -29,29 +36,37 @@ const addState = connect(
function Inventory(args) { function Inventory(args) {
const { const {
account, account,
shop,
setMtxActive, setMtxActive,
mtxBuy,
} = args; } = args;
if (!shop) return false;
const useMtx = (item, i) => (
<figure key={i} onClick={() => setMtxActive(item)} >
<figcaption>{item}</figcaption>
<button>¤1</button>
</figure>
);
const availableMtx = (item, i) => (
<figure key={i} onClick={() => mtxBuy(item)} >
<figcaption>{item.variant}</figcaption>
<button>¤{item.credits}</button>
</figure>
);
return ( return (
<div class="inventory"> <div class="inventory">
<h1>¤ {account.credits}</h1> <h1>¤ {account.balance}</h1>
<div class='list'> <div class='list'>
<figure onClick={() => setMtxActive('Reimage')} > {shop.owned.map(useMtx)}
<figcaption>Reimage</figcaption> </div>
<button>¤1</button> <h1>Shop</h1>
</figure> <div class='list'>
<figure onClick={() => setMtxActive('Rename')} > {shop.available.map(availableMtx)}
<figcaption>Rename</figcaption>
<button>¤1</button>
</figure>
<figure>
<figcaption>Invader Architecture</figcaption>
<button></button>
</figure>
<figure>
<figcaption>Molecular Architecture</figcaption>
<button></button>
</figure>
</div> </div>
</div> </div>
); );

View File

@ -111,6 +111,10 @@ function registerEvents(store) {
store.dispatch(actions.setAccount(account)); store.dispatch(actions.setAccount(account));
} }
function setShop(v) {
store.dispatch(actions.setShop(v));
}
function clearCombiner() { function clearCombiner() {
store.dispatch(actions.setInfo([])); store.dispatch(actions.setInfo([]));
store.dispatch(actions.setCombiner([null, null, null])); store.dispatch(actions.setCombiner([null, null, null]));
@ -233,6 +237,7 @@ function registerEvents(store) {
setInstanceList, setInstanceList,
setItemInfo, setItemInfo,
setPing, setPing,
setShop,
setWs, setWs,
}; };
} }

View File

@ -35,6 +35,7 @@ module.exports = {
reclaiming: createReducer(false, 'SET_RECLAIMING'), reclaiming: createReducer(false, 'SET_RECLAIMING'),
resolution: createReducer(null, 'SET_RESOLUTION'), resolution: createReducer(null, 'SET_RESOLUTION'),
skip: createReducer(false, 'SET_SKIP'), skip: createReducer(false, 'SET_SKIP'),
shop: createReducer(false, 'SET_SHOP'),
team: createReducer([null, null, null], 'SET_SELECTED_CONSTRUCTS'), team: createReducer([null, null, null], 'SET_SELECTED_CONSTRUCTS'),
vboxHighlight: createReducer([], 'SET_VBOX_HIGHLIGHT'), vboxHighlight: createReducer([], 'SET_VBOX_HIGHLIGHT'),
ws: createReducer(null, 'SET_WS'), ws: createReducer(null, 'SET_WS'),

View File

@ -124,6 +124,10 @@ function createSocket(events) {
// events.clearMtxActive(); // events.clearMtxActive();
} }
function sendMtxBuy(mtx) {
send(['MtxBuy', { mtx }]);
}
// ------------- // -------------
// Incoming // Incoming
// ------------- // -------------
@ -133,6 +137,10 @@ function createSocket(events) {
sendAccountInstances(); sendAccountInstances();
} }
function onAccountShop(shop) {
events.setShop(shop);
}
function onAccountInstances(list) { function onAccountInstances(list) {
events.setAccountInstances(list); events.setAccountInstances(list);
setTimeout(sendAccountInstances, 5000); setTimeout(sendAccountInstances, 5000);
@ -198,6 +206,7 @@ function createSocket(events) {
AccountState: onAccount, AccountState: onAccount,
AccountConstructs: onAccountConstructs, AccountConstructs: onAccountConstructs,
AccountInstances: onAccountInstances, AccountInstances: onAccountInstances,
AccountShop: onAccountShop,
GameState: onGameState, GameState: onGameState,
InstanceState: onInstanceState, InstanceState: onInstanceState,
ItemInfo: onItemInfo, ItemInfo: onItemInfo,
@ -308,6 +317,7 @@ function createSocket(events) {
sendVboxUnequip, sendVboxUnequip,
sendItemInfo, sendItemInfo,
sendMtxApply, sendMtxApply,
sendMtxBuy,
startInstanceStateTimeout, startInstanceStateTimeout,
startGameStateTimeout, startGameStateTimeout,
connect, connect,

View File

@ -9,7 +9,7 @@ exports.up = async knex => {
table.string('token', 64).notNullable(); table.string('token', 64).notNullable();
table.timestamp('token_expiry').notNullable(); table.timestamp('token_expiry').notNullable();
table.bigInteger('credits') table.bigInteger('balance')
.defaultTo(0) .defaultTo(0)
.notNullable(); .notNullable();
@ -23,7 +23,7 @@ exports.up = async knex => {
await knex.schema.raw(` await knex.schema.raw(`
ALTER TABLE accounts ALTER TABLE accounts
ADD CHECK (credits > 0); ADD CHECK (balance > 0);
`); `);
}; };

View File

@ -14,6 +14,7 @@ use instance::{Instance, instance_delete};
use mtx::{Mtx, FREE_MTX}; use mtx::{Mtx, FREE_MTX};
use pg::Db; use pg::Db;
use failure::Error; use failure::Error;
use failure::{err_msg, format_err}; use failure::{err_msg, format_err};
@ -23,13 +24,13 @@ static PASSWORD_MIN_LEN: usize = 11;
pub struct Account { pub struct Account {
pub id: Uuid, pub id: Uuid,
pub name: String, pub name: String,
pub credits: u32, pub balance: u32,
pub subscribed: bool, pub subscribed: bool,
} }
pub fn select(tx: &mut Transaction, id: Uuid) -> Result<Account, Error> { pub fn select(tx: &mut Transaction, id: Uuid) -> Result<Account, Error> {
let query = " let query = "
SELECT id, name, credits, subscribed SELECT id, name, balance, subscribed
FROM accounts FROM accounts
WHERE id = $1; WHERE id = $1;
"; ";
@ -40,18 +41,18 @@ pub fn select(tx: &mut Transaction, id: Uuid) -> Result<Account, Error> {
let row = result.iter().next() let row = result.iter().next()
.ok_or(format_err!("account not found {:?}", id))?; .ok_or(format_err!("account not found {:?}", id))?;
let db_credits: i64 = row.get(2); let db_balance: i64 = row.get(2);
let credits = u32::try_from(db_credits) let balance = u32::try_from(db_balance)
.or(Err(format_err!("user {:?} has unparsable balance {:?}", id, db_credits)))?; .or(Err(format_err!("user {:?} has unparsable balance {:?}", id, db_balance)))?;
let subscribed: bool = row.get(3); let subscribed: bool = row.get(3);
Ok(Account { id, name: row.get(1), credits, subscribed }) Ok(Account { id, name: row.get(1), balance, subscribed })
} }
pub fn from_token(db: &Db, token: String) -> Result<Account, Error> { pub fn from_token(db: &Db, token: String) -> Result<Account, Error> {
let query = " let query = "
SELECT id, name, subscribed, credits SELECT id, name, subscribed, balance
FROM accounts FROM accounts
WHERE token = $1 WHERE token = $1
AND token_expiry > now(); AND token_expiry > now();
@ -66,17 +67,17 @@ pub fn from_token(db: &Db, token: String) -> Result<Account, Error> {
let id: Uuid = row.get(0); let id: Uuid = row.get(0);
let name: String = row.get(1); let name: String = row.get(1);
let subscribed: bool = row.get(2); let subscribed: bool = row.get(2);
let db_credits: i64 = row.get(3); let db_balance: i64 = row.get(3);
let credits = u32::try_from(db_credits) let balance = u32::try_from(db_balance)
.or(Err(format_err!("user {:?} has unparsable balance {:?}", id, db_credits)))?; .or(Err(format_err!("user {:?} has unparsable balance {:?}", id, db_balance)))?;
Ok(Account { id, name, credits, subscribed }) Ok(Account { id, name, balance, subscribed })
} }
pub fn login(tx: &mut Transaction, name: &String, password: &String) -> Result<Account, Error> { pub fn login(tx: &mut Transaction, name: &String, password: &String) -> Result<Account, Error> {
let query = " let query = "
SELECT id, password, name, credits, subscribed SELECT id, password, name, balance, subscribed
FROM accounts FROM accounts
WHERE name = $1 WHERE name = $1
"; ";
@ -102,17 +103,17 @@ pub fn login(tx: &mut Transaction, name: &String, password: &String) -> Result<A
let id: Uuid = row.get(0); let id: Uuid = row.get(0);
let hash: String = row.get(1); let hash: String = row.get(1);
let name: String = row.get(2); let name: String = row.get(2);
let db_credits: i64 = row.get(3); let db_balance: i64 = row.get(3);
let subscribed: bool = row.get(4); let subscribed: bool = row.get(4);
if !verify(password, &hash)? { if !verify(password, &hash)? {
return Err(err_msg("password does not match")); return Err(err_msg("password does not match"));
} }
let credits = u32::try_from(db_credits) let balance = u32::try_from(db_balance)
.or(Err(format_err!("user {:?} has unparsable balance {:?}", id, db_credits)))?; .or(Err(format_err!("user {:?} has unparsable balance {:?}", id, db_balance)))?;
Ok(Account { id, name, credits, subscribed }) Ok(Account { id, name, balance, subscribed })
} }
pub fn new_token(tx: &mut Transaction, id: Uuid) -> Result<String, Error> { pub fn new_token(tx: &mut Transaction, id: Uuid) -> Result<String, Error> {
@ -133,7 +134,7 @@ pub fn new_token(tx: &mut Transaction, id: Uuid) -> Result<String, Error> {
let result = tx let result = tx
.query(query, &[&token, &id])?; .query(query, &[&token, &id])?;
let row = result.iter().next() result.iter().next()
.ok_or(format_err!("account not updated {:?}", id))?; .ok_or(format_err!("account not updated {:?}", id))?;
Ok(token) Ok(token)
@ -142,9 +143,9 @@ pub fn new_token(tx: &mut Transaction, id: Uuid) -> Result<String, Error> {
pub fn credit(tx: &mut Transaction, id: Uuid, credits: i64) -> Result<String, Error> { pub fn credit(tx: &mut Transaction, id: Uuid, credits: i64) -> Result<String, Error> {
let query = " let query = "
UPDATE accounts UPDATE accounts
SET credits = credits + $1 SET balance = balance + $1
WHERE id = $2 WHERE id = $2
RETURNING credits, name; RETURNING balance, name;
"; ";
let result = tx let result = tx
@ -153,38 +154,41 @@ pub fn credit(tx: &mut Transaction, id: Uuid, credits: i64) -> Result<String, Er
let row = result.iter().next() let row = result.iter().next()
.ok_or(format_err!("account not updated {:?}", id))?; .ok_or(format_err!("account not updated {:?}", id))?;
let db_credits: i64 = row.get(0); let db_balance: i64 = row.get(0);
let total = u32::try_from(db_credits) let balance = u32::try_from(db_balance)
.or(Err(format_err!("user {:?} has unparsable balance {:?}", id, db_credits)))?; .or(Err(format_err!("user {:?} has unparsable balance {:?}", id, db_balance)))?;
let name: String = row.get(1); let name: String = row.get(1);
info!("account credited name={:?} credited={:?} total={:?}", name, credits, total); info!("account credited name={:?} credited={:?} balance={:?}", name, credits, balance);
Ok(name) Ok(name)
} }
pub fn debit(tx: &mut Transaction, id: Uuid, credits: i64) -> Result<Account, Error> { pub fn debit(tx: &mut Transaction, id: Uuid, debit: i64) -> Result<Account, Error> {
let query = " let query = "
UPDATE accounts UPDATE accounts
SET credits = credits - $1 SET balance = balance - $1
WHERE id = $2 WHERE id = $2
RETURNING id, name, credits, subscribed RETURNING id, name, balance, subscribed
"; ";
let result = tx let result = tx
.query(query, &[&credits, &id])?; .query(query, &[&debit, &id])?;
let row = result.iter().next() let row = result.iter().next()
.ok_or(format_err!("account not found {:?}", id))?; .ok_or(format_err!("account not found {:?}", id))?;
let db_credits: i64 = row.get(2);
let credits = u32::try_from(db_credits) let name: String = row.get(1);
.or(Err(format_err!("user {:?} has unparsable balance {:?}", id, db_credits)))?; let db_balance: i64 = row.get(2);
let balance = u32::try_from(db_balance)
.or(Err(format_err!("user {:?} has unparsable balance {:?}", id, db_balance)))?;
let subscribed: bool = row.get(3); let subscribed: bool = row.get(3);
Ok(Account { id, name: row.get(1), credits, subscribed }) info!("account debited name={:?} debited={:?} balance={:?}", name, debit, balance);
Ok(Account { id, name: row.get(1), balance, subscribed })
} }
pub fn set_subscribed(tx: &mut Transaction, id: Uuid, subscribed: bool) -> Result<String, Error> { pub fn set_subscribed(tx: &mut Transaction, id: Uuid, subscribed: bool) -> Result<String, Error> {

View File

@ -11,7 +11,7 @@ use skill::{Skill, Cast, Immunity, Disable, Event};
use effect::{Cooldown, Effect, Colour}; use effect::{Cooldown, Effect, Colour};
use spec::{Spec}; use spec::{Spec};
use item::{Item}; use item::{Item};
use img::{img_molecular_write}; use img;
#[derive(Debug,Clone,Serialize,Deserialize)] #[derive(Debug,Clone,Serialize,Deserialize)]
pub struct Colours { pub struct Colours {
@ -883,7 +883,7 @@ pub fn construct_spawn(tx: &mut Transaction, account: Uuid, name: String) -> Res
let _returned = result.iter().next().ok_or(err_msg("no row returned"))?; let _returned = result.iter().next().ok_or(err_msg("no row returned"))?;
img_molecular_write(construct.img)?; img::molecular_write(construct.img)?;
info!("spawned construct account={:} construct={:?}", account, construct); info!("spawned construct account={:} construct={:?}", account, construct);
return Ok(construct); return Ok(construct);

View File

@ -9,7 +9,7 @@ use std::io::prelude::*;
use failure::Error; use failure::Error;
use failure::err_msg; use failure::err_msg;
pub fn img_molecular_write(id: Uuid) -> Result<Uuid, Error> { pub fn molecular_write(id: Uuid) -> Result<Uuid, Error> {
let mut rng = thread_rng(); let mut rng = thread_rng();
for _i in 0..100 { for _i in 0..100 {
@ -27,9 +27,7 @@ pub fn img_molecular_write(id: Uuid) -> Result<Uuid, Error> {
return Err(err_msg("too many missing molecules. wrong directory?")) return Err(err_msg("too many missing molecules. wrong directory?"))
} }
pub fn invader_write(id: Uuid) -> Result<Uuid, Error> {
fn invader_img_write(id: Uuid) -> Result<Uuid, Error> {
let mut rng = thread_rng(); let mut rng = thread_rng();
let mut svg = Vec::new(); let mut svg = Vec::new();
@ -86,17 +84,17 @@ fn invader_img_write(id: Uuid) -> Result<Uuid, Error> {
} }
#[cfg(test)] // #[cfg(test)]
mod tests { // mod tests {
use super::*; // use super::*;
#[test] // #[test]
fn invader_img_test() { // fn invader_img_test() {
for i in 0..100 { // for i in 0..100 {
invader_img_write(Uuid::new_v4()).unwrap(); // invader_img_write(Uuid::new_v4()).unwrap();
} // }
} // }
} // }

View File

@ -18,8 +18,7 @@ use mob::{bot_player, instance_mobs};
use game::{Game, Phase, game_get, game_write}; use game::{Game, Phase, game_get, game_write};
use item::{Item}; use item::{Item};
use rpc::{RpcResult}; use rpc::{RpcResult};
use img;
use img::{img_molecular_write};
#[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)] #[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)]
enum InstancePhase { enum InstancePhase {
@ -706,7 +705,7 @@ pub fn instance_new(tx: &mut Transaction, account: &Account, construct_ids: Vec<
// generate bot imgs only in the real world // generate bot imgs only in the real world
for c in bot.constructs.iter() { for c in bot.constructs.iter() {
img_molecular_write(c.img)?; img::molecular_write(c.img)?;
} }
let mut instance = Instance::new() let mut instance = Instance::new()

View File

@ -2,7 +2,6 @@ use std::convert::TryFrom;
use uuid::Uuid; use uuid::Uuid;
// use rand::prelude::*; // use rand::prelude::*;
use serde_cbor::{from_slice};
use postgres::transaction::Transaction; use postgres::transaction::Transaction;
use failure::Error; use failure::Error;
@ -12,17 +11,32 @@ use account;
use account::Account; use account::Account;
use construct::{Construct, construct_select, construct_write}; use construct::{Construct, construct_select, construct_write};
use img::{img_molecular_write}; use img;
pub const FREE_MTX: [MtxVariant; 2] = [ pub const FREE_MTX: [MtxVariant; 1] = [
MtxVariant::Rename, MtxVariant::Rename,
MtxVariant::Reimage,
]; ];
#[derive(Debug,Copy,Clone,Serialize,Deserialize)] pub const SHOP_LISTINGS: [Listing; 2] = [
Listing { variant: MtxVariant::ArchitectureMolecular, credits: 10 },
Listing { variant: MtxVariant::ArchitectureInvader, credits: 10 },
];
#[derive(Debug,Clone,PartialEq,Serialize,Deserialize)]
pub struct Shop {
owned: Vec<MtxVariant>,
available: Vec<Listing>,
}
#[derive(Debug,Copy,Clone,PartialEq,Serialize,Deserialize)]
pub struct Listing {
variant: MtxVariant,
credits: u16,
}
#[derive(Debug,Copy,Clone,PartialEq,Serialize,Deserialize)]
pub enum MtxVariant { pub enum MtxVariant {
Rename, Rename,
Reimage,
ArchitectureMolecular, ArchitectureMolecular,
ArchitectureInvader, ArchitectureInvader,
} }
@ -40,7 +54,6 @@ impl TryFrom<String> for MtxVariant {
fn try_from(v: String) -> Result<MtxVariant, Error> { fn try_from(v: String) -> Result<MtxVariant, Error> {
match v.as_ref() { match v.as_ref() {
"Rename" => Ok(MtxVariant::Rename), "Rename" => Ok(MtxVariant::Rename),
"Reimage" => Ok(MtxVariant::Reimage),
"ArchitectureMolecular" => Ok(MtxVariant::ArchitectureMolecular), "ArchitectureMolecular" => Ok(MtxVariant::ArchitectureMolecular),
"ArchitectureInvader" => Ok(MtxVariant::ArchitectureInvader), "ArchitectureInvader" => Ok(MtxVariant::ArchitectureInvader),
_ => Err(format_err!("mtx variant not found variant={:?}", v)), _ => Err(format_err!("mtx variant not found variant={:?}", v)),
@ -57,25 +70,20 @@ pub struct Mtx {
pub fn apply(tx: &mut Transaction, account: &Account, variant: MtxVariant, construct_id: Uuid) -> Result<Vec<Construct>, Error> { pub fn apply(tx: &mut Transaction, account: &Account, variant: MtxVariant, construct_id: Uuid) -> Result<Vec<Construct>, Error> {
let mtx = select(tx, variant, account.id)?; let mtx = select(tx, variant, account.id)?;
let construct = construct_select(tx, construct_id, account.id)?; let mut construct = construct_select(tx, construct_id, account.id)?;
match mtx.variant {
MtxVariant::Reimage => reimage(tx, construct)?,
_ => unimplemented!(),
};
account::debit(tx, account.id, 1)?; account::debit(tx, account.id, 1)?;
account::account_constructs(tx, account)
}
pub fn reimage(tx: &mut Transaction, mut construct: Construct) -> Result<Construct, Error> {
construct = construct.new_img(); construct = construct.new_img();
match mtx.variant {
MtxVariant::ArchitectureInvader => img::invader_write(construct.img)?,
MtxVariant::ArchitectureMolecular => img::molecular_write(construct.img)?,
MtxVariant::Rename => unimplemented!(),
// _ => unimplemented!(),
};
img_molecular_write(construct.img)?; construct_write(tx, construct)?;
account::account_constructs(tx, account)
construct = construct_write(tx, construct)?;
Ok(construct)
} }
pub fn select(tx: &mut Transaction, variant: MtxVariant, account: Uuid) -> Result<Mtx, Error> { pub fn select(tx: &mut Transaction, variant: MtxVariant, account: Uuid) -> Result<Mtx, Error> {
@ -111,32 +119,6 @@ impl Mtx {
} }
} }
pub fn account_list(tx: &mut Transaction, account: Uuid) -> Result<Vec<Mtx>, Error> {
let query = "
SELECT data, id
FROM mtx
WHERE account = $1;
";
let result = tx
.query(query, &[&account])?;
let values = result.into_iter().filter_map(|row| {
let bytes: Vec<u8> = row.get(0);
// let id: Uuid = row.get(1);
match from_slice::<Mtx>(&bytes) {
Ok(i) => Some(i),
Err(e) => {
warn!("{:?}", e);
None
}
}
}).collect::<Vec<Mtx>>();
return Ok(values);
}
pub fn delete(tx: &mut Transaction, id: Uuid) -> Result<(), Error> { pub fn delete(tx: &mut Transaction, id: Uuid) -> Result<(), Error> {
let query = " let query = "
DELETE DELETE
@ -193,3 +175,54 @@ impl Mtx {
// return Ok(self); // return Ok(self);
// } // }
} }
pub fn account_shop(tx: &mut Transaction, account: &Account) -> Result<Shop, Error> {
let query = "
SELECT variant
FROM mtx
WHERE account = $1;
";
let result = tx
.query(query, &[&account.id])?;
let mut owned = vec![];
for row in result.iter() {
let v_str: String = row.get(0);
let variant = match MtxVariant::try_from(v_str) {
Ok(v) => v,
Err(e) => {
warn!("{:?}", e);
continue;
},
};
owned.push(variant);
};
let available = SHOP_LISTINGS.iter()
.filter(|l| !owned.contains(&l.variant))
.map(|l| *l)
.collect::<Vec<Listing>>();
debug!("account shop acount={:?} owned={:?} available={:?}",
account.name, owned, available);
return Ok(Shop { owned, available });
}
pub fn buy(tx: &mut Transaction, account: &Account, mtx: MtxVariant) -> Result<Shop, Error> {
let listing = SHOP_LISTINGS
.iter()
.find(|l| l.variant == mtx)
.ok_or(format_err!("mtx variant not found variant={:?}", mtx))?;
let account = account::debit(tx, account.id, listing.credits as i64)?;
Mtx::new(mtx, account.id)
.insert(tx)?;
info!("account purchased mtx account={:?} mtx={:?} cost={:?} balance={:?}",
account.name, mtx, listing.credits, account.balance);
account_shop(tx, &account)
}

View File

@ -24,6 +24,7 @@ pub enum RpcResult {
AccountState(Account), AccountState(Account),
AccountConstructs(Vec<Construct>), AccountConstructs(Vec<Construct>),
AccountInstances(Vec<Instance>), AccountInstances(Vec<Instance>),
AccountShop(mtx::Shop),
GameState(Game), GameState(Game),
ItemInfo(ItemInfoCtr), ItemInfo(ItemInfoCtr),
@ -43,12 +44,14 @@ enum RpcRequest {
MtxConstructApply { mtx: mtx::MtxVariant, construct_id: Uuid }, MtxConstructApply { mtx: mtx::MtxVariant, construct_id: Uuid },
MtxAccountApply { mtx: mtx::MtxVariant }, MtxAccountApply { mtx: mtx::MtxVariant },
MtxBuy { mtx: mtx::MtxVariant },
GameState { id: Uuid }, GameState { id: Uuid },
GameReady { id: Uuid }, GameReady { id: Uuid },
GameSkill { game_id: Uuid, construct_id: Uuid, target_construct_id: Option<Uuid>, skill: Skill }, GameSkill { game_id: Uuid, construct_id: Uuid, target_construct_id: Option<Uuid>, skill: Skill },
AccountState {}, AccountState {},
AccountShop {},
AccountConstructs {}, AccountConstructs {},
AccountInstances {}, AccountInstances {},
@ -100,6 +103,10 @@ pub fn receive(data: Vec<u8>, db: &Db, _client: &mut WebSocket<TcpStream>, begin
RpcRequest::AccountInstances {} => RpcRequest::AccountInstances {} =>
Ok(RpcResult::AccountInstances(account_instances(&mut tx, &account)?)), Ok(RpcResult::AccountInstances(account_instances(&mut tx, &account)?)),
// RpcRequest::AccountShop {} =>
// Ok(RpcResult::AccountShop(mtx::account_shop(&mut tx, &account)?)),
// RpcRequest::ConstructDelete" => handle_construct_delete(data, &mut tx, account), // RpcRequest::ConstructDelete" => handle_construct_delete(data, &mut tx, account),
@ -145,6 +152,9 @@ pub fn receive(data: Vec<u8>, db: &Db, _client: &mut WebSocket<TcpStream>, begin
RpcRequest::MtxConstructApply { mtx, construct_id } => RpcRequest::MtxConstructApply { mtx, construct_id } =>
Ok(RpcResult::AccountConstructs(mtx::apply(&mut tx, account, mtx, construct_id)?)), Ok(RpcResult::AccountConstructs(mtx::apply(&mut tx, account, mtx, construct_id)?)),
RpcRequest::MtxBuy { mtx } =>
Ok(RpcResult::AccountShop(mtx::buy(&mut tx, account, mtx)?)),
_ => Err(format_err!("unknown request request={:?}", request)), _ => Err(format_err!("unknown request request={:?}", request)),
}; };
@ -155,7 +165,8 @@ pub fn receive(data: Vec<u8>, db: &Db, _client: &mut WebSocket<TcpStream>, begin
return response; return response;
}, },
Err(e) => { Err(e) => {
Err(format_err!("invalid message data={:?}", data)) warn!("{:?}", e);
Err(err_msg("invalid message"))
}, },
} }
} }

View File

@ -19,6 +19,7 @@ use serde_cbor::{to_vec};
use net::TOKEN_HEADER; use net::TOKEN_HEADER;
use rpc; use rpc;
use mtx;
use pg::PgPool; use pg::PgPool;
use account; use account;
@ -66,10 +67,22 @@ pub fn start(pool: PgPool) {
Some(t) => { Some(t) => {
let db = ws_pool.get() let db = ws_pool.get()
.expect("unable to get db connection"); .expect("unable to get db connection");
match account::from_token(&db, t) { match account::from_token(&db, t) {
Ok(a) => { Ok(a) => {
let state = to_vec(&rpc::RpcResult::AccountState(a.clone())).unwrap(); let state = to_vec(&rpc::RpcResult::AccountState(a.clone())).unwrap();
websocket.write_message(Binary(state)).unwrap(); websocket.write_message(Binary(state)).unwrap();
let mut tx = db.transaction().unwrap();
let shop = mtx::account_shop(&mut tx, &a).unwrap();
let shop = to_vec(&rpc::RpcResult::AccountShop(shop)).unwrap();
websocket.write_message(Binary(shop)).unwrap();
// tx doesn't change anything
tx.commit().unwrap();
Some(a) Some(a)
}, },
Err(e) => { Err(e) => {