Compare commits

2 Commits

Author SHA1 Message Date
ee0b7b2b7a adds UpdateResult class of queries 2019-10-29 16:19:00 +01:00
089aaf9a6d fixes api 2019-10-29 15:12:56 +01:00
6 changed files with 77 additions and 62 deletions

View File

@@ -18,9 +18,9 @@ mod schema;
pub use models::{
claim::{Claim, Claims},
item::{Item, LootManager, Inventory},
player::{Player, Wealth, Players, AsPlayer},
history::{Event, UpdateList},
item::{Inventory, Item, LootManager},
player::{AsPlayer, Player, Players, Wealth},
};
/// The connection used
@@ -29,6 +29,7 @@ pub type DbConnection = SqliteConnection;
pub type Pool = r2d2::Pool<ConnectionManager<DbConnection>>;
/// The result of a query on DB
pub type QueryResult<T> = Result<T, diesel::result::Error>;
pub type UpdateResult = QueryResult<Update>;
/// Sets up a connection pool and returns it.
/// Uses the DATABASE_URL environment variable (must be set)
@@ -41,7 +42,6 @@ pub fn create_pool() -> Pool {
.expect("Failed to create pool.")
}
/// Every possible update which can happen during a query
#[derive(Serialize, Deserialize, Debug)]
pub enum Update {
@@ -54,24 +54,25 @@ pub enum Update {
impl Update {
/// Change back what has been updated
fn undo(&self, conn: &DbConnection, id: i32) -> QueryResult<Self> {
fn undo(&self, conn: &DbConnection, id: i32) -> UpdateResult {
Ok(match self {
Update::Wealth(diff) => {
Update::Wealth(AsPlayer(conn, id).update_wealth(-diff.to_gp())?)
},
Update::ItemAdded(item) => {
Update::ItemRemoved(LootManager(conn, id).remove(item.id)?)
},
Update::ItemRemoved(item) => {
Update::ItemAdded(LootManager(conn, id).add_from(&item)?)
},
Update::Wealth(diff) => AsPlayer(conn, id).update_wealth(-diff.to_gp())?,
Update::ItemAdded(item) => LootManager(conn, id).find(item.id)?.remove(conn)?,
Update::ItemRemoved(item) => LootManager(conn, id).add_from(&item)?,
// Unused for now
Update::ClaimAdded(claim) => { Update::ClaimRemoved(*claim) },
Update::ClaimRemoved(claim) => { Update::ClaimAdded(*claim) },
Update::ClaimAdded(claim) => Update::ClaimRemoved(*claim),
Update::ClaimRemoved(claim) => Update::ClaimAdded(*claim),
})
}
}
/// TODO: use this to wrap update in UpdateResult, allowing unified interface
/// whether a query makes multiple updates or just one.
enum OneOrMore {
One(Update),
More(Vec<Update>),
}
/// Every value which can be queried
#[derive(Debug)]
pub enum Value {
@@ -107,17 +108,19 @@ pub fn sell_item_transaction(
id: i32,
loot_id: i32,
price_mod: Option<f64>,
) -> QueryResult<(Item, Wealth)> {
) -> QueryResult<(Update, Wealth)> {
conn.transaction(|| {
let deleted = LootManager(conn, id)
.remove(loot_id)?;
let mut sell_value = deleted.sell_value() as f64;
let to_delete = LootManager(conn, id).find(loot_id)?;
let mut sell_value = to_delete.sell_value() as f64;
if let Some(modifier) = price_mod {
sell_value *= modifier;
}
let wealth = AsPlayer(conn, id)
.update_wealth(sell_value)?;
Ok((deleted, wealth))
let deleted = to_delete.remove(conn)?;
if let Update::Wealth(wealth) = AsPlayer(conn, id).update_wealth(sell_value)? {
Ok((deleted, wealth))
} else {
Err(diesel::result::Error::RollbackTransaction)
}
})
}
@@ -131,7 +134,7 @@ pub fn buy_item_from_inventory(
id: i32,
item_id: i32,
price_mod: Option<f64>,
) -> QueryResult<(Item, Wealth)> {
) -> QueryResult<(Update, Wealth)> {
conn.transaction(|| {
// Find item in inventory
let item = Inventory(conn).find(item_id)?;
@@ -140,9 +143,11 @@ pub fn buy_item_from_inventory(
Some(modifier) => item.value() as f64 * modifier,
None => item.value() as f64,
};
AsPlayer(conn, id)
.update_wealth(-sell_price)
.map(|diff| (new_item, diff))
if let Update::Wealth(diff) = AsPlayer(conn, id).update_wealth(-sell_price)? {
Ok((new_item, diff))
} else {
Err(diesel::result::Error::RollbackTransaction)
}
})
}
@@ -161,35 +166,39 @@ pub fn resolve_claims(conn: &DbConnection) -> QueryResult<()> {
for (item, mut claims) in data {
if claims.len() > 1 {
// TODO: better sorting mechanism :)
claims.sort_by(|a,b| a.resolve.cmp(&b.resolve));
claims.sort_by(|a, b| a.resolve.cmp(&b.resolve));
}
let winner = claims.get(0).expect("Claims should not be empty !");
let player_id = winner.player_id;
winner.resolve_claim(conn)?;
models::player::AsPlayer(conn, player_id)
.update_debt(item.sell_value())?;
winner.resolve_claim(conn)?;
models::player::AsPlayer(conn, player_id).update_debt(item.sell_value())?;
}
Ok(())
})
}
/// Split up and share group money among selected players
pub fn split_and_share(conn: &DbConnection, amount: i32, players: Vec<i32>) -> QueryResult<Wealth> {
pub fn split_and_share(
conn: &DbConnection,
amount: i32,
players: &Vec<i32>,
) -> QueryResult<Wealth> {
let share = (
amount / (players.len() + 1) as i32 // +1 share for the group
amount / (players.len() + 1) as i32
// +1 share for the group
) as f64;
conn.transaction(|| {
for p in players.into_iter() {
let player = Players(conn).find(p)?;
for p in players {
let player = Players(conn).find(*p)?;
// Take debt into account
match share - player.debt as f64 {
rest if rest > 0.0 => {
AsPlayer(conn, p).update_debt(-player.debt)?;
AsPlayer(conn, p).update_wealth(rest)?;
AsPlayer(conn, *p).update_debt(-player.debt)?;
AsPlayer(conn, *p).update_wealth(rest)?;
AsPlayer(conn, 0).update_wealth(-rest)?;
},
}
_ => {
AsPlayer(conn, p).update_debt(-share as i32)?;
AsPlayer(conn, *p).update_debt(-share as i32)?;
}
}
}

View File

@@ -3,7 +3,7 @@ use diesel::expression::exists::Exists;
use diesel::prelude::*;
use crate::schema::{items, looted};
use crate::{DbConnection, QueryResult};
use crate::{DbConnection, QueryResult, Update, UpdateResult };
type ItemColumns = (looted::id, looted::name, looted::base_price);
const ITEM_COLUMNS: ItemColumns = (looted::id, looted::name, looted::base_price);
type OwnedBy = Select<OwnedLoot, ItemColumns>;
@@ -27,6 +27,11 @@ impl Item {
self.base_price / 2
}
pub fn remove(self, conn: &DbConnection) -> UpdateResult {
diesel::delete(looted::table.find(self.id)).execute(conn)?;
Ok(Update::ItemRemoved(self))
}
fn owned_by(player: i32) -> OwnedBy {
Loot::owned_by(player).select(ITEM_COLUMNS)
}
@@ -124,7 +129,7 @@ impl<'q> LootManager<'q> {
.first(self.0)?)
}
pub(crate) fn add<S: Into<String>>(self, name: S, base_price: i32) -> QueryResult<Item> {
pub(crate) fn add<S: Into<String>>(self, name: S, base_price: i32) -> UpdateResult {
self.add_from(&Item {
id: 0,
name: name.into(),
@@ -133,7 +138,7 @@ impl<'q> LootManager<'q> {
}
/// Adds a copy of the given item inside player chest
pub fn add_from(self, item: &Item) -> QueryResult<Item> {
pub fn add_from(self, item: &Item) -> UpdateResult {
let new_item = NewLoot {
name: &item.name,
base_price: item.base_price,
@@ -142,13 +147,7 @@ impl<'q> LootManager<'q> {
diesel::insert_into(looted::table)
.values(&new_item)
.execute(self.0)?;
self.last()
}
pub fn remove(self, item_id: i32) -> QueryResult<Item> {
let deleted = self.find(item_id)?;
diesel::delete(looted::table.find(deleted.id)).execute(self.0)?;
Ok(deleted)
Ok(Update::ItemAdded(self.last()?))
}
}

View File

@@ -1,5 +1,5 @@
use crate::schema::players;
use crate::{DbConnection, QueryResult};
use crate::{DbConnection, QueryResult, Update, UpdateResult};
use diesel::prelude::*;
mod notification;
@@ -50,10 +50,7 @@ impl<'q> Players<'q> {
/// Notify all players of an event
pub fn notifiy_all(&self, text: &str) -> QueryResult<()> {
for id in self.all()?
.into_iter()
.map(|p| p.id)
{
for id in self.all()?.into_iter().map(|p| p.id) {
self.notify(id, text);
}
Ok(())
@@ -75,7 +72,7 @@ impl<'q> AsPlayer<'q> {
notification::pop_all_for(self.1, self.0)
}
/// Updates this player's wealth, returning the difference
pub fn update_wealth(&self, value_in_gp: f64) -> QueryResult<Wealth> {
pub fn update_wealth(&self, value_in_gp: f64) -> UpdateResult {
use crate::schema::players::dsl::*;
let current_wealth = players
.find(self.1)
@@ -86,7 +83,7 @@ impl<'q> AsPlayer<'q> {
.filter(id.eq(self.1))
.set(&updated_wealth)
.execute(self.0)?;
Ok(updated_wealth - current_wealth)
Ok(Update::Wealth(updated_wealth - current_wealth))
}
/// Updates this player's debt

View File

@@ -1,6 +1,7 @@
<template>
<PlayerView
:id="player_id"
:players="playersId"
v-slot="{ player, loot, notifications, actions, claims }"
>
<main id="app" class="container">
@@ -142,7 +143,7 @@ export default {
api.fetch("items", "GET", null),
])
.then(([players, loot, items]) => {
this.playerList = players.value;
this.$set(this, 'playerList', players.value);
this.groupLoot = loot.value;
this.itemsInventory = items.value;
})
@@ -177,6 +178,7 @@ export default {
showPlayerChest () { return this.activeView == 'player' },
isAdding () { return this.activeView == 'adding' },
playerIsGroup () { return this.player_id == 0 },
playersId () { return this.playerList.map(p => p.id).filter(i => i != 0); }
}
}
</script>

View File

@@ -1,7 +1,7 @@
import { api } from '../lootalot.js'
export default {
props: ["id"],
props: ["id", "players"],
data () { return {
player: {
name: "Loading",
@@ -76,7 +76,15 @@ export default {
putClaim (itemId) { this.call("claims", "PUT", itemId) },
withdrawClaim (itemId) { this.call("claims", "DELETE", itemId) },
buyItems(items) { this.call("loot", "PUT", items) },
sellItems (items) { this.call("loot", "DELETE", { items, global_mod: null, players: null }) },
sellItems (items) {
let players;
if (this.player.id == 0) {
players = this.players;
} else {
players = null;
}
this.call("loot", "DELETE", { items, global_mod: null, players })
},
undoLastAction () { this.call("events/last", "DELETE", null) },
},
watch: {

View File

@@ -113,9 +113,7 @@ pub fn execute(
None
}
ApiActions::UpdateWealth(id, amount) => {
response.push_update(Update::Wealth(
db::AsPlayer(conn, id).update_wealth(amount)?,
));
response.push_update(db::AsPlayer(conn, id).update_wealth(amount)?);
response.notify(format!("Mis à jour ({}po)!", amount));
Some((id, "Argent mis à jour"))
}
@@ -165,15 +163,17 @@ pub fn execute(
.fold(db::Wealth::from_gp(0.0), |acc, i| acc + i);
match id {
0 => {
let players = params.players.expect("The player list should be passed in !");
let share = db::split_and_share(
conn,
total_amount.to_gp() as i32,
params.players.expect("Should not be None"),
&players,
)?;
response.notify(format!(
"Les objets ont été vendus, chaque joueur a reçu {} po",
share.to_gp()
));
//response.push_update(Update::GroupShare(players, share));
response.push_update(Update::Wealth(share));
}
_ => {