From 1cc9c2eefa085ddad81a7ac47929f2e25b926fdd Mon Sep 17 00:00:00 2001 From: Artus Date: Thu, 7 Nov 2019 15:55:58 +0100 Subject: [PATCH] fixes some misbehaviour --- lootalot_db/src/lib.rs | 16 ++++++-- lootalot_db/src/models/claim.rs | 29 +++++++++++---- lootalot_db/src/models/item.rs | 13 +++++-- lootalot_db/src/models/player/wealth.rs | 8 ++-- src/api.rs | 49 +++++++++++++++++++------ src/server.rs | 9 ++++- 6 files changed, 94 insertions(+), 30 deletions(-) diff --git a/lootalot_db/src/lib.rs b/lootalot_db/src/lib.rs index 3b917b7..c2a6349 100644 --- a/lootalot_db/src/lib.rs +++ b/lootalot_db/src/lib.rs @@ -177,17 +177,26 @@ pub fn resolve_claims(conn: &DbConnection) -> QueryResult<()> { }) } -/// Split up and share group money among selected players +/// Split up and share an certain amount of group money among selected players +/// +/// The group first solve players debts, +/// then give what's left. +/// +/// # Returns +/// +/// A Wealth update with the amount of money actually shared pub fn split_and_share( conn: &DbConnection, amount: i32, players: &Vec, -) -> QueryResult { +) -> UpdateResult { let share = ( amount / (players.len() + 1) as i32 // +1 share for the group ) as f64; conn.transaction(|| { + // What we actually give, negative value + let mut diff = 0.0; for p in players { let player = Players(conn).find(*p)?; // Take debt into account @@ -196,13 +205,14 @@ pub fn split_and_share( AsPlayer(conn, *p).update_debt(-player.debt)?; AsPlayer(conn, *p).update_wealth(rest)?; AsPlayer(conn, 0).update_wealth(-rest)?; + diff -= rest; } _ => { AsPlayer(conn, *p).update_debt(-share as i32)?; } } } - Ok(Wealth::from_gp(share)) + Ok(Update::Wealth(Wealth::from_gp(diff))) }) } diff --git a/lootalot_db/src/models/claim.rs b/lootalot_db/src/models/claim.rs index 76d7d81..6b0a10d 100644 --- a/lootalot_db/src/models/claim.rs +++ b/lootalot_db/src/models/claim.rs @@ -1,6 +1,6 @@ -use crate::{DbConnection, QueryResult}; use diesel::prelude::*; +use crate::{DbConnection, QueryResult, Update, UpdateResult}; use crate::models::{self, item::Loot}; use crate::schema::claims; @@ -54,7 +54,7 @@ impl<'q> Claims<'q> { /// Will validate that the claimed item exists and is /// actually owned by the group. /// Duplicates are also ignored. - pub fn add(self, player_id: i32, loot_id: i32) -> QueryResult { + pub fn add(self, player_id: i32, loot_id: i32) -> UpdateResult { // We need to validate that the claimed item exists // AND is actually owned by group (id 0) let _item = models::item::LootManager(self.0, 0).find(loot_id)?; @@ -68,16 +68,22 @@ impl<'q> Claims<'q> { .values(&claim) .execute(self.0)?; // Return the created claim - claims::table - .order(claims::dsl::id.desc()) - .first::(self.0) + Ok( + Update::ClaimAdded( + claims::table + .order(claims::dsl::id.desc()) + .first::(self.0)? + ) + ) } /// Removes a claim from database, returning it - pub fn remove(self, player_id: i32, loot_id: i32) -> QueryResult { + pub fn remove(self, player_id: i32, loot_id: i32) -> UpdateResult { let claim = self.find(player_id, loot_id)?; claim.remove(self.0)?; - Ok(claim) + Ok( + Update::ClaimRemoved(claim) + ) } pub fn filtered_by_loot(&self, loot_id: i32) -> QueryResult> { @@ -86,6 +92,15 @@ impl<'q> Claims<'q> { .load(self.0) } + pub(crate) fn delete_for_loot(&self, loot_id: i32) -> QueryResult { + diesel::delete( + claims::table + .filter(claims::dsl::loot_id.eq(loot_id)) + ) + .execute(self.0) + + } + pub(crate) fn grouped_by_item(&self) -> QueryResult)>> { let group_loot: Vec = Loot::owned_by(0).load(self.0)?; let claims = claims::table.load(self.0)?.grouped_by(&group_loot); diff --git a/lootalot_db/src/models/item.rs b/lootalot_db/src/models/item.rs index 171bb02..c0225dd 100644 --- a/lootalot_db/src/models/item.rs +++ b/lootalot_db/src/models/item.rs @@ -3,7 +3,7 @@ use diesel::expression::exists::Exists; use diesel::prelude::*; use crate::schema::{items, looted}; -use crate::{DbConnection, QueryResult, Update, UpdateResult }; +use crate::{DbConnection, QueryResult, Update, UpdateResult, Claims }; type ItemColumns = (looted::id, looted::name, looted::base_price); const ITEM_COLUMNS: ItemColumns = (looted::id, looted::name, looted::base_price); type OwnedBy = Select; @@ -28,8 +28,15 @@ impl Item { } pub fn remove(self, conn: &DbConnection) -> UpdateResult { - diesel::delete(looted::table.find(self.id)).execute(conn)?; - Ok(Update::ItemRemoved(self)) + conn.transaction( + || -> UpdateResult + { + Claims(conn).delete_for_loot(self.id)?; + diesel::delete(looted::table.find(self.id)).execute(conn)?; + + Ok(Update::ItemRemoved(self)) + } + ) } fn owned_by(player: i32) -> OwnedBy { diff --git a/lootalot_db/src/models/player/wealth.rs b/lootalot_db/src/models/player/wealth.rs index 1903c6a..3d0274f 100644 --- a/lootalot_db/src/models/player/wealth.rs +++ b/lootalot_db/src/models/player/wealth.rs @@ -140,8 +140,8 @@ mod tests { fn test_diff_adding() { use super::Wealth; - /// Let say we add 0.08 gp - /// 1.23 + 0.08 gold is 1.31, diff is cp: -2, sp: +1 + // Let say we add 0.08 gp + // 1.23 + 0.08 gold is 1.31, diff is cp: -2, sp: +1 let old = Wealth::from_gp(1.23); let new = Wealth::from_gp(1.31); let diff = new - old; @@ -154,8 +154,8 @@ mod tests { fn test_diff_subbing() { use super::Wealth; - /// Let say we sub 0.08 gp - /// 1.31 - 0.08 gold is 1.23, diff is cp: +2, sp: -1 + // Let say we sub 0.08 gp + // 1.31 - 0.08 gold is 1.23, diff is cp: +2, sp: -1 let old = Wealth::from_gp(1.31); let new = Wealth::from_gp(1.23); let diff = new - old; diff --git a/src/api.rs b/src/api.rs index e6d685f..4660791 100644 --- a/src/api.rs +++ b/src/api.rs @@ -1,11 +1,14 @@ +use diesel::connection::Connection; use lootalot_db::{self as db, DbConnection, Update, Value}; +use std::collections::HashSet; +pub type IdList = Vec; pub type ItemListWithMods = Vec<(i32, Option)>; #[derive(Serialize, Deserialize, Debug)] pub struct BuySellParams { pub items: ItemListWithMods, - players: Option>, + players: Option, global_mod: Option, } @@ -66,6 +69,7 @@ pub enum ApiActions { UpdateWealth(i32, f64), BuyItems(i32, BuySellParams), SellItems(i32, BuySellParams), + ClaimItems(i32, IdList), ClaimItem(i32, i32), UnclaimItem(i32, i32), UndoLastAction(i32), @@ -161,18 +165,23 @@ 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, - &players, - )?; + let players = params + .players + .unwrap_or(db::Players(conn).all()?.into_iter().map(|p| p.id).collect()); + let shared = db::split_and_share(conn, total_amount.to_gp() as i32, &players)?; + let shared_amount = { + if let Update::Wealth(amount) = shared { + amount.to_gp() + } else { + panic!("cannot happen") + } + }; response.notify(format!( - "Les objets ont été vendus, chaque joueur a reçu {} po", - share.to_gp() + "Les objets ont été vendus, les joueurs ont reçu (au total) {} po", + shared_amount )); //response.push_update(Update::GroupShare(players, share)); - response.push_update(Update::Wealth(share)); + response.push_update(shared); } _ => { response.notify(format!( @@ -186,15 +195,31 @@ pub fn execute( Some((id, "Vente d'objets")) } ApiActions::ClaimItem(id, item) => { - response.push_update(Update::ClaimAdded(db::Claims(conn).add(id, item)?)); + response.push_update(db::Claims(conn).add(id, item)?); response.notify("Pour moi !".to_string()); None } ApiActions::UnclaimItem(id, item) => { - response.push_update(Update::ClaimRemoved(db::Claims(conn).remove(id, item)?)); + response.push_update(db::Claims(conn).remove(id, item)?); response.notify("Bof! Finalement non.".to_string()); None } + ApiActions::ClaimItems(id, items) => { + conn.transaction(|| -> Result, diesel::result::Error> { + let current_claims: HashSet = + db::Claims(conn).all()?.iter().filter(|c| c.player_id == id).map(|c| c.loot_id).collect(); + let new_claims: HashSet = items.into_iter().collect(); + // Claims to delete + for item in current_claims.difference(&new_claims) { + response.push_update(db::Claims(conn).remove(id, *item)?); + } + // Claims to add + for item in new_claims.difference(¤t_claims) { + response.push_update(db::Claims(conn).add(id, *item)?); + } + Ok(Some((id, "Requête(s) mises(s) à jour"))) + })? + } ApiActions::UndoLastAction(id) => { if let Ok(event) = db::models::history::get_last_of_player(conn, id) { let name = String::from(event.name()); diff --git a/src/server.rs b/src/server.rs index dbe0c2e..4ff9d31 100644 --- a/src/server.rs +++ b/src/server.rs @@ -10,6 +10,7 @@ use crate::api; type AppPool = web::Data; type PlayerId = web::Path; type ItemId = web::Json; +type IdList = web::Json; #[derive(Serialize, Deserialize, Debug)] struct NewGroupLoot { @@ -66,6 +67,12 @@ fn configure_api(config: &mut web::ServiceConfig) { .service( web::resource("/claims") //.route(web::get().to_async(endpoints::player_claims)) + .route(web::post().to_async( + |pool, (player, data): (PlayerId, IdList)| + { + db_call(pool, Q::ClaimItems(*player, data.clone())) + } + )) .route(web::put().to_async( |pool, (player, data): (PlayerId, ItemId)| { db_call(pool, Q::ClaimItem(*player, *data)) @@ -142,7 +149,7 @@ pub fn serve() -> std::io::Result<()> { .configure(configure_api) .wrap( Cors::new() - .allowed_origin("http://localhost:8080") + .allowed_origin("http://localhost:8088") .allowed_methods(vec!["GET", "POST", "PUT", "DELETE", "OPTIONS"]) .max_age(3600), )