merge shop and looted tables
This commit is contained in:
@@ -1,3 +1,2 @@
|
|||||||
DROP TABLE items;
|
DROP TABLE items;
|
||||||
DROP TABLE looted;
|
DROP TABLE loot;
|
||||||
DROP TABLE shop;
|
|
||||||
|
|||||||
@@ -5,8 +5,8 @@ CREATE TABLE items (
|
|||||||
base_price INTEGER NOT NULL
|
base_price INTEGER NOT NULL
|
||||||
);
|
);
|
||||||
|
|
||||||
-- The items that have been looted
|
-- The loot
|
||||||
CREATE TABLE looted (
|
CREATE TABLE loot (
|
||||||
id INTEGER PRIMARY KEY NOT NULL,
|
id INTEGER PRIMARY KEY NOT NULL,
|
||||||
name VARCHAR NOT NULL,
|
name VARCHAR NOT NULL,
|
||||||
base_price INTEGER NOT NULL,
|
base_price INTEGER NOT NULL,
|
||||||
@@ -14,9 +14,3 @@ CREATE TABLE looted (
|
|||||||
FOREIGN KEY (owner_id) REFERENCES players(id)
|
FOREIGN KEY (owner_id) REFERENCES players(id)
|
||||||
);
|
);
|
||||||
|
|
||||||
-- The items that are available in shop
|
|
||||||
CREATE TABLE shop (
|
|
||||||
id INTEGER PRIMARY KEY NOT NULL,
|
|
||||||
name VARCHAR NOT NULL,
|
|
||||||
base_price INTEGER NOT NULL
|
|
||||||
);
|
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ pub type DbConnection = SqliteConnection;
|
|||||||
pub type Pool = r2d2::Pool<ConnectionManager<DbConnection>>;
|
pub type Pool = r2d2::Pool<ConnectionManager<DbConnection>>;
|
||||||
/// The result of a query on DB
|
/// The result of a query on DB
|
||||||
pub type QueryResult<T> = Result<T, diesel::result::Error>;
|
pub type QueryResult<T> = Result<T, diesel::result::Error>;
|
||||||
pub type UpdateResult = QueryResult<Update>;
|
pub type UpdateResult = QueryResult<Vec<Update>>;
|
||||||
|
|
||||||
/// Sets up a connection pool and returns it.
|
/// Sets up a connection pool and returns it.
|
||||||
/// Uses the DATABASE_URL environment variable (must be set)
|
/// Uses the DATABASE_URL environment variable (must be set)
|
||||||
@@ -60,8 +60,8 @@ impl Update {
|
|||||||
Update::ItemAdded(item) => LootManager(conn, id).find(item.id)?.remove(conn)?,
|
Update::ItemAdded(item) => LootManager(conn, id).find(item.id)?.remove(conn)?,
|
||||||
Update::ItemRemoved(item) => LootManager(conn, id).add_from(&item)?,
|
Update::ItemRemoved(item) => LootManager(conn, id).add_from(&item)?,
|
||||||
// Unused for now
|
// Unused for now
|
||||||
Update::ClaimAdded(claim) => Update::ClaimRemoved(*claim),
|
Update::ClaimAdded(claim) => vec!(Update::ClaimRemoved(*claim)),
|
||||||
Update::ClaimRemoved(claim) => Update::ClaimAdded(*claim),
|
Update::ClaimRemoved(claim) => vec!(Update::ClaimAdded(*claim)),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -99,82 +99,6 @@ impl serde::Serialize for Value {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sells a single item inside a transaction
|
|
||||||
///
|
|
||||||
/// # Returns
|
|
||||||
/// The deleted entity and the updated Wealth (as a difference from previous value)
|
|
||||||
pub fn sell_item_transaction(
|
|
||||||
conn: &DbConnection,
|
|
||||||
id: i32,
|
|
||||||
loot_id: i32,
|
|
||||||
price_mod: Option<f64>,
|
|
||||||
) -> QueryResult<(Update, Wealth)> {
|
|
||||||
conn.transaction(|| {
|
|
||||||
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 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)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Buys a single item, copied from inventory.
|
|
||||||
/// Runs inside a transaction
|
|
||||||
///
|
|
||||||
/// # Returns
|
|
||||||
/// The created entity and the updated Wealth (as a difference from previous value)
|
|
||||||
pub fn buy_item_from_inventory(
|
|
||||||
conn: &DbConnection,
|
|
||||||
id: i32,
|
|
||||||
item_id: i32,
|
|
||||||
price_mod: Option<f64>,
|
|
||||||
) -> QueryResult<(Update, Wealth)> {
|
|
||||||
conn.transaction(|| {
|
|
||||||
// Find item in inventory
|
|
||||||
let item = Inventory(conn).find(item_id)?;
|
|
||||||
let new_item = LootManager(conn, id).add_from(&item)?;
|
|
||||||
let sell_price = match price_mod {
|
|
||||||
Some(modifier) => item.value() * modifier,
|
|
||||||
None => item.value(),
|
|
||||||
};
|
|
||||||
if let Update::Wealth(diff) = AsPlayer(conn, id).update_wealth(-sell_price)? {
|
|
||||||
Ok((new_item, diff))
|
|
||||||
} else {
|
|
||||||
Err(diesel::result::Error::RollbackTransaction)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn buy_item_from_shop(
|
|
||||||
conn: &DbConnection,
|
|
||||||
id: i32,
|
|
||||||
item_id: i32,
|
|
||||||
price_mod: Option<f64>,
|
|
||||||
) -> QueryResult<(Update, Wealth)> {
|
|
||||||
conn.transaction(|| {
|
|
||||||
let shop = Shop(conn);
|
|
||||||
// Find item in inventory
|
|
||||||
let item = shop.get(item_id)?;
|
|
||||||
let new_item = LootManager(conn, id).add_from(&item)?;
|
|
||||||
let _deleted = shop.remove(item_id)?;
|
|
||||||
let sell_price = match price_mod {
|
|
||||||
Some(modifier) => item.value() * modifier,
|
|
||||||
None => item.value(),
|
|
||||||
};
|
|
||||||
if let Update::Wealth(diff) = AsPlayer(conn, id).update_wealth(-sell_price)? {
|
|
||||||
Ok((new_item, diff))
|
|
||||||
} else {
|
|
||||||
Err(diesel::result::Error::RollbackTransaction)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// Resolve all pending claims and dispatch claimed items.
|
/// Resolve all pending claims and dispatch claimed items.
|
||||||
///
|
///
|
||||||
@@ -239,7 +163,7 @@ pub fn split_and_share(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(Update::Wealth(Wealth::from_gp(shared_total)))
|
Ok(vec!(Update::Wealth(Wealth::from_gp(shared_total))))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -73,22 +73,22 @@ impl<'q> Claims<'q> {
|
|||||||
.values(&claim)
|
.values(&claim)
|
||||||
.execute(self.0)?;
|
.execute(self.0)?;
|
||||||
// Return the created claim
|
// Return the created claim
|
||||||
Ok(
|
Ok(vec!(
|
||||||
Update::ClaimAdded(
|
Update::ClaimAdded(
|
||||||
claims::table
|
claims::table
|
||||||
.order(claims::dsl::id.desc())
|
.order(claims::dsl::id.desc())
|
||||||
.first::<Claim>(self.0)?
|
.first::<Claim>(self.0)?
|
||||||
)
|
)
|
||||||
)
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Removes a claim from database, returning it
|
/// Removes a claim from database, returning it
|
||||||
pub fn remove(self, player_id: i32, loot_id: i32) -> UpdateResult {
|
pub fn remove(self, player_id: i32, loot_id: i32) -> UpdateResult {
|
||||||
let claim = self.find(player_id, loot_id)?;
|
let claim = self.find(player_id, loot_id)?;
|
||||||
claim.remove(self.0)?;
|
claim.remove(self.0)?;
|
||||||
Ok(
|
Ok(vec!(
|
||||||
Update::ClaimRemoved(claim)
|
Update::ClaimRemoved(claim)
|
||||||
)
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn filtered_by_loot(&self, loot_id: i32) -> QueryResult<Vec<Claim>> {
|
pub fn filtered_by_loot(&self, loot_id: i32) -> QueryResult<Vec<Claim>> {
|
||||||
|
|||||||
@@ -2,10 +2,10 @@ use diesel::dsl::{exists, Eq, Filter, Find, Select};
|
|||||||
use diesel::expression::exists::Exists;
|
use diesel::expression::exists::Exists;
|
||||||
use diesel::prelude::*;
|
use diesel::prelude::*;
|
||||||
|
|
||||||
use crate::schema::{items, looted, shop};
|
use crate::schema::{items, loot};
|
||||||
use crate::{DbConnection, QueryResult, Update, UpdateResult, Claims };
|
use crate::{DbConnection, QueryResult, Update, UpdateResult, Claims };
|
||||||
type ItemColumns = (looted::id, looted::name, looted::base_price);
|
type ItemColumns = (loot::id, loot::name, loot::base_price);
|
||||||
const ITEM_COLUMNS: ItemColumns = (looted::id, looted::name, looted::base_price);
|
const ITEM_COLUMNS: ItemColumns = (loot::id, loot::name, loot::base_price);
|
||||||
type OwnedBy = Select<OwnedLoot, ItemColumns>;
|
type OwnedBy = Select<OwnedLoot, ItemColumns>;
|
||||||
|
|
||||||
/// Represents a basic item
|
/// Represents a basic item
|
||||||
@@ -27,57 +27,46 @@ impl Item {
|
|||||||
self.base_price as f64 / 2.0
|
self.base_price as f64 / 2.0
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn remove(self, conn: &DbConnection) -> UpdateResult {
|
|
||||||
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 {
|
fn owned_by(player: i32) -> OwnedBy {
|
||||||
Loot::owned_by(player).select(ITEM_COLUMNS)
|
Loot::owned_by(player).select(ITEM_COLUMNS)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const OF_GROUP = 0;
|
// Owner status
|
||||||
const OF_SHOP = -1;
|
const OF_GROUP: i32 = 0;
|
||||||
const SOLD = -2;
|
const OF_SHOP: i32 = -1;
|
||||||
|
const SOLD: i32 = -2;
|
||||||
|
|
||||||
type WithOwner = Eq<looted::owner_id, i32>;
|
type WithOwner = Eq<loot::owner_id, i32>;
|
||||||
type OwnedLoot = Filter<looted::table, WithOwner>;
|
type OwnedLoot = Filter<loot::table, WithOwner>;
|
||||||
|
|
||||||
/// An owned item
|
/// An owned item
|
||||||
///
|
///
|
||||||
/// The owner is a Player, the Group, the Merchant
|
/// The owner is a Player, the Group, the Merchant
|
||||||
/// OR the SOLD state.
|
/// OR the SOLD state.
|
||||||
#[derive(Identifiable, Debug, Queryable)]
|
#[derive(Identifiable, Debug, Queryable)]
|
||||||
#[table_name = "looted"]
|
#[table_name = "loot"]
|
||||||
pub(super) struct Loot {
|
pub(super) struct Loot {
|
||||||
id: i32,
|
id: i32,
|
||||||
name: String,
|
name: String,
|
||||||
base_price: i32,
|
base_price: i32,
|
||||||
owner: i32,
|
owner_id: i32,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
impl Loot {
|
impl Loot {
|
||||||
/// A filter on Loot that is owned by given player
|
/// A filter on Loot that is owned by given player
|
||||||
pub(super) fn owned_by(id: i32) -> OwnedLoot {
|
pub(super) fn owned_by(id: i32) -> OwnedLoot {
|
||||||
looted::table.filter(looted::owner_id.eq(id))
|
loot::table.filter(loot::owner_id.eq(id))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn exists(id: i32) -> Exists<Find<looted::table, i32>> {
|
fn exists(id: i32) -> Exists<Find<loot::table, i32>> {
|
||||||
exists(looted::table.find(id))
|
exists(loot::table.find(id))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn set_owner(&self, owner: i32, conn: &DbConnection) -> QueryResult<()> {
|
pub(super) fn set_owner(&self, owner: i32, conn: &DbConnection) -> QueryResult<()> {
|
||||||
diesel::update(looted::table.find(self.id))
|
diesel::update(loot::table.find(self.id))
|
||||||
.set(looted::dsl::owner_id.eq(owner))
|
.set(loot::dsl::owner_id.eq(owner))
|
||||||
.execute(conn)?;
|
.execute(conn)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -90,8 +79,8 @@ impl Loot {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn find(id: i32) -> Find<looted::table, i32> {
|
pub(super) fn find(id: i32) -> Find<loot::table, i32> {
|
||||||
looted::table.find(id)
|
loot::table.find(id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -107,6 +96,28 @@ impl<'q> Inventory<'q> {
|
|||||||
pub fn find(&self, item_id: i32) -> QueryResult<Item> {
|
pub fn find(&self, item_id: i32) -> QueryResult<Item> {
|
||||||
items::table.find(item_id).first::<Item>(self.0)
|
items::table.find(item_id).first::<Item>(self.0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn find_by_name(&self, item_name: &str) -> QueryResult<Item> {
|
||||||
|
Ok(items::table.filter(items::dsl::name.like(item_name)).first(self.0)?)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Check the inventory against a list of item names
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// A tuple of found items and errors
|
||||||
|
pub fn check_list(&self, item_names: Vec<String>) -> QueryResult<(Vec<Item>, String)> {
|
||||||
|
let all_items = self.all()?;
|
||||||
|
let mut found = Vec::new();
|
||||||
|
let mut errors = String::new();
|
||||||
|
for name in &item_names {
|
||||||
|
match self.find_by_name(name) {
|
||||||
|
Ok(item) => found.push(item),
|
||||||
|
Err(_) => errors.push_str(&format!("{},\n", name)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok((found, errors))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The shop resource
|
/// The shop resource
|
||||||
@@ -115,39 +126,62 @@ pub struct Shop<'q>(pub &'q DbConnection);
|
|||||||
impl<'q> Shop<'q> {
|
impl<'q> Shop<'q> {
|
||||||
// Rename to list
|
// Rename to list
|
||||||
pub fn all(&self) -> QueryResult<Vec<Item>> {
|
pub fn all(&self) -> QueryResult<Vec<Item>> {
|
||||||
// Loot::owned_by(OF_SHOP).load(self.0)
|
Item::owned_by(OF_SHOP).load(self.0)
|
||||||
shop::table.load(self.0)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// pub fn buy(&self, items: Vec<(i32, f64)>, buyer: i32) -> UpdateResult {}
|
fn buy_single(&self, buyer: i32, item_id: i32, price_mod: Option<f64>) -> UpdateResult {
|
||||||
|
use crate::AsPlayer;
|
||||||
pub fn get(&self, id: i32) -> QueryResult<Item> {
|
let item = self.get(item_id)?;
|
||||||
shop::table.find(&id).first::<Item>(self.0)
|
let sell_price = match price_mod {
|
||||||
|
Some(modifier) => item.base_price as f64 * modifier,
|
||||||
|
None => item.base_price as f64,
|
||||||
|
};
|
||||||
|
self.0.transaction(|| {
|
||||||
|
let mut updates = AsPlayer(self.0, buyer).update_wealth(-sell_price)?;
|
||||||
|
item.set_owner(buyer, self.0)?;
|
||||||
|
updates.push(Update::ItemAdded(item.into_item()));
|
||||||
|
Ok(updates)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn remove(&self, id: i32) -> QueryResult<()> {
|
pub fn buy(&self, items: Vec<(i32, Option<f64>)>, buyer: i32) -> UpdateResult {
|
||||||
diesel::delete(
|
// TODO: check that player has enough money !
|
||||||
shop::table.find(&id)
|
let has_enough_gold = true;
|
||||||
).execute(self.0)?;
|
|
||||||
Ok(())
|
if has_enough_gold {
|
||||||
|
let mut updates = Vec::new();
|
||||||
|
for (item_id, price_mod) in items.into_iter() {
|
||||||
|
updates.append(
|
||||||
|
&mut self.buy_single(buyer, item_id, price_mod)?
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Ok(updates)
|
||||||
|
} else {
|
||||||
|
unimplemented!();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get(&self, id: i32) -> QueryResult<Loot> {
|
||||||
|
Loot::owned_by(OF_SHOP).find(&id).first::<Loot>(self.0)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn replace_list(&self, items: Vec<Item>) -> QueryResult<()> {
|
pub fn replace_list(&self, items: Vec<Item>) -> QueryResult<()> {
|
||||||
|
use self::loot::dsl::*;
|
||||||
self.0.transaction(
|
self.0.transaction(
|
||||||
|| -> QueryResult<()>
|
|| -> QueryResult<()>
|
||||||
{
|
{
|
||||||
// Remove all content
|
// Remove all content
|
||||||
diesel::delete(shop::table).execute(self.0)?;
|
diesel::delete(Loot::owned_by(OF_SHOP)).execute(self.0)?;
|
||||||
// Adds new list
|
// Adds new list
|
||||||
for item in &items {
|
for item in &items {
|
||||||
let new_item = NewItem {
|
let new_item = NewLoot {
|
||||||
name: &item.name,
|
name: &item.name,
|
||||||
base_price: item.base_price,
|
base_price: item.base_price,
|
||||||
|
owner_id: OF_SHOP,
|
||||||
};
|
};
|
||||||
diesel::insert_into(shop::table)
|
diesel::insert_into(loot)
|
||||||
.values(&new_item)
|
.values(&new_item)
|
||||||
.execute(self.0)?;
|
.execute(self.0)?;
|
||||||
|
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -164,28 +198,50 @@ impl<'q> LootManager<'q> {
|
|||||||
Ok(Item::owned_by(self.1).load(self.0)?)
|
Ok(Item::owned_by(self.1).load(self.0)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get(&self, item_id: i32) -> QueryResult<Loot> {
|
||||||
|
Ok(Loot::owned_by(self.1).find(item_id).first(self.0)?)
|
||||||
|
}
|
||||||
|
|
||||||
/// Finds an item by id
|
/// Finds an item by id
|
||||||
///
|
///
|
||||||
/// Returns a NotFound error if an item is found by it
|
/// Returns a NotFound error if an item is found by it
|
||||||
/// does not belong to this player
|
/// does not belong to this player
|
||||||
pub fn find(&self, loot_id: i32) -> QueryResult<Item> {
|
pub fn find(&self, loot_id: i32) -> QueryResult<Item> {
|
||||||
Ok(Loot::find(loot_id).first(self.0).and_then(|loot: Loot| {
|
Ok(self.get(loot_id)?.into_item())
|
||||||
if loot.owner != self.1 {
|
|
||||||
Err(diesel::result::Error::NotFound)
|
|
||||||
} else {
|
|
||||||
Ok(Item {
|
|
||||||
id: loot.id,
|
|
||||||
name: loot.name,
|
|
||||||
base_price: loot.base_price,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})?)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn sell_single(&self, loot_id: i32, price_mod: Option<f64>) -> UpdateResult {
|
||||||
|
let to_sell = self.get(loot_id)?;
|
||||||
|
let mut sell_value = to_sell.base_price as f64 / 2.0;
|
||||||
|
if let Some(modifier) = price_mod {
|
||||||
|
sell_value *= modifier;
|
||||||
|
}
|
||||||
|
self.0.transaction(|| {
|
||||||
|
let mut updates =
|
||||||
|
crate::AsPlayer(self.0, self.1)
|
||||||
|
.update_wealth(sell_value)?;
|
||||||
|
to_sell.set_owner(SOLD, self.0)?;
|
||||||
|
updates.push(Update::ItemRemoved(to_sell.into_item()));
|
||||||
|
Ok(updates)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn sell(&self, items: Vec<(i32, Option<f64>)>) -> UpdateResult {
|
||||||
|
self.0.transaction(|| {
|
||||||
|
let mut updates = Vec::new();
|
||||||
|
for (loot_id, price_mod) in items.into_iter() {
|
||||||
|
updates.append(&mut self.sell_single(loot_id, price_mod)?);
|
||||||
|
}
|
||||||
|
Ok(updates)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/// The last item added to the chest
|
/// The last item added to the chest
|
||||||
pub fn last(&self) -> QueryResult<Item> {
|
pub fn last(&self) -> QueryResult<Item> {
|
||||||
Ok(Item::owned_by(self.1)
|
Ok(Item::owned_by(self.1)
|
||||||
.order(looted::dsl::id.desc())
|
.order(loot::dsl::id.desc())
|
||||||
.first(self.0)?)
|
.first(self.0)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -204,19 +260,19 @@ impl<'q> LootManager<'q> {
|
|||||||
base_price: item.base_price,
|
base_price: item.base_price,
|
||||||
owner_id: self.1,
|
owner_id: self.1,
|
||||||
};
|
};
|
||||||
diesel::insert_into(looted::table)
|
diesel::insert_into(loot::table)
|
||||||
.values(&new_item)
|
.values(&new_item)
|
||||||
.execute(self.0)?;
|
.execute(self.0)?;
|
||||||
Ok(Update::ItemAdded(self.last()?))
|
Ok(vec!(Update::ItemAdded(self.last()?)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An item being looted or bought.
|
/// An item being loot or bought.
|
||||||
///
|
///
|
||||||
/// The owner is set to 0 in case of looting,
|
/// The owner is set to 0 in case of looting,
|
||||||
/// to the id of buying player otherwise.
|
/// to the id of buying player otherwise.
|
||||||
#[derive(Insertable)]
|
#[derive(Insertable)]
|
||||||
#[table_name = "looted"]
|
#[table_name = "loot"]
|
||||||
struct NewLoot<'a> {
|
struct NewLoot<'a> {
|
||||||
name: &'a str,
|
name: &'a str,
|
||||||
base_price: i32,
|
base_price: i32,
|
||||||
@@ -224,7 +280,7 @@ struct NewLoot<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Insertable)]
|
#[derive(Insertable)]
|
||||||
#[table_name = "shop"]
|
#[table_name = "items"]
|
||||||
struct NewItem<'a> {
|
struct NewItem<'a> {
|
||||||
name: &'a str,
|
name: &'a str,
|
||||||
base_price: i32,
|
base_price: i32,
|
||||||
|
|||||||
@@ -89,7 +89,7 @@ impl<'q> AsPlayer<'q> {
|
|||||||
.filter(id.eq(self.1))
|
.filter(id.eq(self.1))
|
||||||
.set(&updated_wealth)
|
.set(&updated_wealth)
|
||||||
.execute(self.0)?;
|
.execute(self.0)?;
|
||||||
Ok(Update::Wealth(updated_wealth - current_wealth))
|
Ok(vec!(Update::Wealth(updated_wealth - current_wealth)))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Updates this player's debt
|
/// Updates this player's debt
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ table! {
|
|||||||
}
|
}
|
||||||
|
|
||||||
table! {
|
table! {
|
||||||
looted (id) {
|
loot (id) {
|
||||||
id -> Integer,
|
id -> Integer,
|
||||||
name -> Text,
|
name -> Text,
|
||||||
base_price -> Integer,
|
base_price -> Integer,
|
||||||
@@ -54,26 +54,16 @@ table! {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
table! {
|
|
||||||
shop (id) {
|
|
||||||
id -> Integer,
|
|
||||||
name -> Text,
|
|
||||||
base_price -> Integer,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
joinable!(claims -> looted (loot_id));
|
|
||||||
joinable!(claims -> players (player_id));
|
joinable!(claims -> players (player_id));
|
||||||
joinable!(history -> players (player_id));
|
joinable!(history -> players (player_id));
|
||||||
joinable!(looted -> players (owner_id));
|
joinable!(loot -> players (owner_id));
|
||||||
joinable!(notifications -> players (player_id));
|
joinable!(notifications -> players (player_id));
|
||||||
|
|
||||||
allow_tables_to_appear_in_same_query!(
|
allow_tables_to_appear_in_same_query!(
|
||||||
claims,
|
claims,
|
||||||
history,
|
history,
|
||||||
items,
|
items,
|
||||||
looted,
|
loot,
|
||||||
notifications,
|
notifications,
|
||||||
players,
|
players,
|
||||||
shop,
|
|
||||||
);
|
);
|
||||||
|
|||||||
179
src/api.rs
179
src/api.rs
@@ -40,11 +40,11 @@ pub struct ApiResponse {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ApiResponse {
|
impl ApiResponse {
|
||||||
fn push_update(&mut self, update: db::Update) {
|
fn push_updates(&mut self, updates: Vec<db::Update>) {
|
||||||
if let Some(v) = self.updates.as_mut() {
|
if let Some(v) = self.updates.as_mut() {
|
||||||
v.push(update);
|
v.append(&mut updates);
|
||||||
} else {
|
} else {
|
||||||
self.updates = Some(vec![update]);
|
self.updates = Some(updates);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -107,6 +107,8 @@ pub enum ApiEndpoints {
|
|||||||
BuyItems(i32, BuySellParams), // db::Shop(conn)::buy(params)
|
BuyItems(i32, BuySellParams), // db::Shop(conn)::buy(params)
|
||||||
RefreshShop(ItemList), // db::Shop(conn)::replace_list(items)
|
RefreshShop(ItemList), // db::Shop(conn)::replace_list(items)
|
||||||
|
|
||||||
|
ClaimsList,
|
||||||
|
|
||||||
// db::Players::get returns AsPlayer<'q>
|
// db::Players::get returns AsPlayer<'q>
|
||||||
PlayerList, //db::Players(conn)::list()
|
PlayerList, //db::Players(conn)::list()
|
||||||
PlayerAdd(NewPlayer), //db::Players(conn)::add(player)
|
PlayerAdd(NewPlayer), //db::Players(conn)::add(player)
|
||||||
@@ -122,150 +124,111 @@ pub enum ApiEndpoints {
|
|||||||
ResolveClaims, // db::Group(conn)::resolve_claims()
|
ResolveClaims, // db::Group(conn)::resolve_claims()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static ErrorMsg : &str = "Une erreur est survenue";
|
||||||
|
|
||||||
pub fn execute(
|
pub fn execute(
|
||||||
conn: &DbConnection,
|
conn: &DbConnection,
|
||||||
query: ApiActions,
|
query: ApiEndpoints,
|
||||||
) -> Result<ApiResponse, diesel::result::Error> {
|
) -> Result<ApiResponse, diesel::result::Error> {
|
||||||
let mut response = ApiResponse::default();
|
let mut response = ApiResponse::default();
|
||||||
// Return an Option<String> that describes what happened.
|
// Return an Option<String> that describes what happened.
|
||||||
// If there is some value, store the actions in db so that it can be reversed.
|
// If there is some value, store the actions in db so that it can be reversed.
|
||||||
let action_text: Option<(i32, &str)> = match query {
|
let action_text: Option<(i32, &str)> = match query {
|
||||||
ApiActions::CheckItemList(names) => {
|
// Inventory
|
||||||
let (items, errors) = {
|
ApiEndpoints::FetchInventory => {
|
||||||
let mut found = Vec::new();
|
match db::Inventory(conn).all() {
|
||||||
let mut errors = String::new();
|
Ok(items) => response.set_value(Value::ItemList(items)),
|
||||||
let items = db::Inventory(conn).all()?;
|
Err(_) => response.set_error(ErrorMsg),
|
||||||
for name in &names {
|
}
|
||||||
if let Some(item) = items.iter().filter(|i| &i.name == name).take(1).next() {
|
|
||||||
found.push(item.clone())
|
|
||||||
} else {
|
|
||||||
errors.push_str(&format!("{},\n", name));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
(found, errors)
|
|
||||||
};
|
|
||||||
|
|
||||||
response.set_value(Value::ItemList(items));
|
|
||||||
response.push_error(errors);
|
|
||||||
dbg!(&names, &response);
|
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
ApiActions::FetchPlayers => {
|
ApiEnpoints::InventoryCheck(names) => {
|
||||||
|
match db::Inventory(conn).check_list(names) {
|
||||||
|
Ok((items, errors)) => {
|
||||||
|
response.set_value(Value::ItemList(items));
|
||||||
|
response.push_error(errors);
|
||||||
|
}
|
||||||
|
Err(_) => response.set_error(ErrorMsg),
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
ApiEndpoints::InventoryAdd((name, value)) => {
|
||||||
|
response.set_error("Not implemented");
|
||||||
|
None
|
||||||
|
}
|
||||||
|
//Shop
|
||||||
|
ApiEnpoints::ShopList => {
|
||||||
|
match db::Shop(conn).all() {
|
||||||
|
Ok(items) => response.set_value(Value::ItemList(items)),
|
||||||
|
Err(_) => response.set_error(ErrorMsg),
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
ApiEndpoints::BuyItems(buyer, params) => {
|
||||||
|
match db::Shop(conn).buy(params, buyer) {
|
||||||
|
Ok(updates) => {
|
||||||
|
response.notifiy("Objets achetés !");
|
||||||
|
response.push_updates(updates);
|
||||||
|
Some((id, "Achat d'objets"))
|
||||||
|
},
|
||||||
|
Err(_) => {
|
||||||
|
response.set_error(ErrorMsg);
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Players
|
||||||
|
ApiEndpoints::PlayerList => {
|
||||||
response.set_value(Value::PlayerList(db::Players(conn).all_except_group()?));
|
response.set_value(Value::PlayerList(db::Players(conn).all_except_group()?));
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
ApiActions::FetchInventory => {
|
ApiEndpoint::ClaimList => {
|
||||||
response.set_value(Value::ItemList(db::Inventory(conn).all()?));
|
|
||||||
None
|
|
||||||
}
|
|
||||||
ApiActions::FetchShopInventory => {
|
|
||||||
response.set_value(Value::ItemList(db::Shop(conn).all()?));
|
|
||||||
None
|
|
||||||
}
|
|
||||||
ApiActions::FetchClaims => {
|
|
||||||
response.set_value(Value::ClaimList(db::Claims(conn).all()?));
|
response.set_value(Value::ClaimList(db::Claims(conn).all()?));
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
ApiActions::FetchPlayer(id) => {
|
ApiEndpoint::PlayerClaims(id) => {
|
||||||
response.set_value(Value::Players(conn)(db::Players(conn).find(id)?));
|
|
||||||
None
|
|
||||||
}
|
|
||||||
ApiActions::FetchPlayerClaims(id) => {
|
|
||||||
response.set_value(Value::ClaimList(db::Claims(conn).by_player(id)?));
|
response.set_value(Value::ClaimList(db::Claims(conn).by_player(id)?));
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
ApiActions::FetchNotifications(id) => {
|
ApiEndpoint::PlayerNotifications(id) => {
|
||||||
response.set_value(Value::Notifications(
|
response.set_value(Value::Notifications(
|
||||||
db::AsPlayer(conn, id).notifications()?,
|
db::AsPlayer(conn, id).notifications()?,
|
||||||
));
|
));
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
ApiActions::FetchLoot(id) => {
|
ApiEndpoint::PlayerLoot(id) => {
|
||||||
response.set_value(Value::ItemList(db::LootManager(conn, id).all()?));
|
response.set_value(Value::ItemList(db::LootManager(conn, id).all()?));
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
ApiActions::UpdateWealth(id, amount) => {
|
ApiEndpoint::PlayerUpdateWealth(id, amount) => {
|
||||||
response.push_update(db::AsPlayer(conn, id).update_wealth(amount)?);
|
response.push_updates(db::AsPlayer(conn, id).update_wealth(amount)?);
|
||||||
response.notify(format!("Mis à jour ({}po)!", amount));
|
response.notify(format!("Mis à jour ({}po)!", amount));
|
||||||
Some((id, "Argent mis à jour"))
|
Some((id, "Argent mis à jour"))
|
||||||
}
|
}
|
||||||
ApiActions::BuyItems(id, params) => {
|
|
||||||
// TODO: check that player has enough money !
|
|
||||||
let has_enough_gold = true;
|
|
||||||
|
|
||||||
if has_enough_gold {
|
|
||||||
let mut gains: Vec<db::Wealth> = Vec::with_capacity(params.items.len());
|
|
||||||
for (item_id, price_mod) in params.items.into_iter() {
|
|
||||||
if let Ok((item, diff)) = db::buy_item_from_shop(conn, id, item_id, price_mod) {
|
|
||||||
response.push_update(item);
|
|
||||||
gains.push(diff);
|
|
||||||
} else {
|
|
||||||
response.push_error(format!("Error adding {}", item_id));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let added_items = gains.len();
|
|
||||||
let total_amount = gains
|
|
||||||
.into_iter()
|
|
||||||
.fold(db::Wealth::from_gp(0.0), |acc, i| acc + i);
|
|
||||||
response.notify(format!(
|
|
||||||
"{} objets achetés pour {}po",
|
|
||||||
added_items,
|
|
||||||
total_amount.to_gp()
|
|
||||||
));
|
|
||||||
response.push_update(Update::Wealth(total_amount));
|
|
||||||
Some((id, "Achat d'objets"))
|
|
||||||
} else {
|
|
||||||
response.push_error("Vous n'avez pas assez d'argent !");
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Behavior differs if player is group or regular.
|
// Behavior differs if player is group or regular.
|
||||||
// Group sells item like players then split the total amount among players.
|
// Group sells item like players then split the total amount among players.
|
||||||
ApiActions::SellItems(id, params) => {
|
ApiEndpoint::SellItems(id, params) => {
|
||||||
conn.transaction(|| -> Result<Option<(i32, &str)>, diesel::result::Error> {
|
conn.transaction(|| -> Result<Option<(i32, &str)>, diesel::result::Error> {
|
||||||
let mut gains: Vec<db::Wealth> = Vec::with_capacity(params.items.len());
|
let sold = LootManager(conn, id).sell(params.items)?;
|
||||||
for (loot_id, price_mod) in params.items.iter() {
|
|
||||||
if let Ok((deleted, diff)) =
|
|
||||||
db::sell_item_transaction(conn, id, *loot_id, *price_mod)
|
|
||||||
{
|
|
||||||
response.push_update(deleted);
|
|
||||||
gains.push(diff);
|
|
||||||
} else {
|
|
||||||
response
|
|
||||||
.push_error(format!("Erreur lors de la vente (loot_id : {})", loot_id));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let sold_items = gains.len();
|
|
||||||
let total_amount = gains
|
|
||||||
.into_iter()
|
|
||||||
.fold(db::Wealth::from_gp(0.0), |acc, i| acc + i);
|
|
||||||
match id {
|
match id {
|
||||||
0 => {
|
0 => {
|
||||||
let players = params.players.unwrap_or_default();
|
let players = params.players.unwrap_or_default();
|
||||||
if let Update::Wealth(shared) =
|
updates.push(
|
||||||
db::split_and_share(conn, total_amount.to_gp() as i32, players)?
|
db::split_and_share(conn, total_amount.to_gp() as i32, players)?
|
||||||
{
|
);
|
||||||
response.notify(format!(
|
response.notify("Les objets ont été vendus");
|
||||||
"Les objets ont été vendus, les joueurs ont reçu (au total) {} po",
|
response.push_updates(updates);
|
||||||
shared.to_gp()
|
|
||||||
));
|
|
||||||
response.push_update(Update::Wealth(total_amount - shared));
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
response.notify(format!(
|
response.notify("Objets vendus !");
|
||||||
"{} objet(s) vendu(s) pour {} po",
|
response.push_updates(updates);
|
||||||
sold_items,
|
|
||||||
total_amount.to_gp()
|
|
||||||
));
|
|
||||||
response.push_update(Update::Wealth(total_amount));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(Some((id, "Vente d'objets")))
|
Ok(Some((id, "Vente d'objets")))
|
||||||
})?
|
})?
|
||||||
}
|
}
|
||||||
ApiActions::ClaimItems(id, items) => {
|
ApiEndpoint::ClaimItems(id, items) => {
|
||||||
conn.transaction(|| -> Result<Option<(i32, &str)>, diesel::result::Error> {
|
conn.transaction(|| -> Result<Option<(i32, &str)>, diesel::result::Error> {
|
||||||
let current_claims: HashSet<i32> = db::Claims(conn)
|
let current_claims: HashSet<i32> = db::Claims(conn)
|
||||||
.all()?
|
.all()?
|
||||||
@@ -285,7 +248,7 @@ pub fn execute(
|
|||||||
Ok(None)
|
Ok(None)
|
||||||
})?
|
})?
|
||||||
}
|
}
|
||||||
ApiActions::UndoLastAction(id) => {
|
ApiEndpoint::UndoLastAction(id) => {
|
||||||
if let Ok(event) = db::models::history::get_last_of_player(conn, id) {
|
if let Ok(event) = db::models::history::get_last_of_player(conn, id) {
|
||||||
let name = String::from(event.name());
|
let name = String::from(event.name());
|
||||||
for undone in event.undo(conn)?.into_inner().into_iter() {
|
for undone in event.undo(conn)?.into_inner().into_iter() {
|
||||||
@@ -298,7 +261,7 @@ pub fn execute(
|
|||||||
None
|
None
|
||||||
}
|
}
|
||||||
// Group actions
|
// Group actions
|
||||||
ApiActions::AddLoot(data) => {
|
ApiEndpoint::AddLoot(data) => {
|
||||||
let mut added_items = 0;
|
let mut added_items = 0;
|
||||||
for item in data.items.into_iter() {
|
for item in data.items.into_iter() {
|
||||||
if let Ok(added) = db::LootManager(conn, 0).add_from(&item) {
|
if let Ok(added) = db::LootManager(conn, 0).add_from(&item) {
|
||||||
@@ -317,13 +280,17 @@ pub fn execute(
|
|||||||
};
|
};
|
||||||
Some((0, "Nouveau loot"))
|
Some((0, "Nouveau loot"))
|
||||||
}
|
}
|
||||||
|
ApiEndpoints::ResolveClaims => {
|
||||||
|
response.push_error("Not implemented!");
|
||||||
|
None
|
||||||
|
}
|
||||||
// Admin actions
|
// Admin actions
|
||||||
ApiActions::RefreshShopInventory(items) => {
|
ApiEndpoints::RefreshShop(items) => {
|
||||||
db::Shop(conn).replace_list(items)?;
|
db::Shop(conn).replace_list(items)?;
|
||||||
response.notify("Inventaire du marchand renouvelé !");
|
response.notify("Inventaire du marchand renouvelé !");
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
ApiActions::AddPlayer(data) => {
|
ApiEnpoints::AddPlayer(data) => {
|
||||||
db::Players(conn).add(&data.name, data.wealth)?;
|
db::Players(conn).add(&data.name, data.wealth)?;
|
||||||
response.notify("Joueur ajouté !");
|
response.notify("Joueur ajouté !");
|
||||||
None
|
None
|
||||||
|
|||||||
Reference in New Issue
Block a user