enhance responses from API, integrates with frontend
This commit is contained in:
Binary file not shown.
@@ -14,7 +14,30 @@ mod schema;
|
|||||||
pub type DbConnection = SqliteConnection;
|
pub type DbConnection = SqliteConnection;
|
||||||
pub type Pool = r2d2::Pool<ConnectionManager<DbConnection>>;
|
pub type Pool = r2d2::Pool<ConnectionManager<DbConnection>>;
|
||||||
pub type QueryResult<T> = Result<T, diesel::result::Error>;
|
pub type QueryResult<T> = Result<T, diesel::result::Error>;
|
||||||
pub type ActionResult = QueryResult<bool>;
|
pub type ActionResult<R> = QueryResult<ActionStatus<R>>;
|
||||||
|
|
||||||
|
#[derive(Serialize, Debug)]
|
||||||
|
pub struct ActionStatus<R: serde::Serialize> {
|
||||||
|
/// Has the action made changes ?
|
||||||
|
executed: bool,
|
||||||
|
/// Response payload
|
||||||
|
response: R,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ActionStatus<()> {
|
||||||
|
fn ok() -> ActionStatus<()> {
|
||||||
|
Self {
|
||||||
|
executed: true,
|
||||||
|
response: (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn nop() -> ActionStatus<()> {
|
||||||
|
Self {
|
||||||
|
executed: false,
|
||||||
|
response: (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// A wrapper providing an API over the database
|
/// A wrapper providing an API over the database
|
||||||
/// It offers a convenient way to deal with connection
|
/// It offers a convenient way to deal with connection
|
||||||
@@ -115,7 +138,8 @@ impl<'q> AsPlayer<'q> {
|
|||||||
/// Adds the value in gold to the player's wealth.
|
/// Adds the value in gold to the player's wealth.
|
||||||
///
|
///
|
||||||
/// Value can be negative to substract wealth.
|
/// Value can be negative to substract wealth.
|
||||||
pub fn update_wealth(self, value_in_gp: f32) -> ActionResult {
|
pub fn update_wealth(self, value_in_gp: f32)
|
||||||
|
-> ActionResult<Option<(i32, i32, i32, i32)>> {
|
||||||
use schema::players::dsl::*;
|
use schema::players::dsl::*;
|
||||||
let current_wealth = players
|
let current_wealth = players
|
||||||
.find(self.id)
|
.find(self.id)
|
||||||
@@ -126,30 +150,54 @@ impl<'q> AsPlayer<'q> {
|
|||||||
let update = models::WealthUpdate::from_gp(
|
let update = models::WealthUpdate::from_gp(
|
||||||
current_wealth.to_gp() + value_in_gp
|
current_wealth.to_gp() + value_in_gp
|
||||||
);
|
);
|
||||||
|
// Difference in coins that is sent back
|
||||||
|
let (old, new) =
|
||||||
|
(current_wealth.as_tuple(), update.as_tuple());
|
||||||
|
let diff = (
|
||||||
|
new.0 - old.0,
|
||||||
|
new.1 - old.1,
|
||||||
|
new.2 - old.2,
|
||||||
|
new.3 - old.3
|
||||||
|
);
|
||||||
diesel::update(players)
|
diesel::update(players)
|
||||||
.filter(id.eq(self.id))
|
.filter(id.eq(self.id))
|
||||||
.set(&update)
|
.set(&update)
|
||||||
.execute(self.conn)
|
.execute(self.conn)
|
||||||
// TODO: need to work out what this boolean REALLY means
|
// TODO: need to work out what this boolean REALLY means
|
||||||
.map(|r| match r { 1 => true, _ => false })
|
.map(|r| match r {
|
||||||
|
1 => ActionStatus {
|
||||||
|
executed: true,
|
||||||
|
response: Some(diff),
|
||||||
|
},
|
||||||
|
_ => ActionStatus {
|
||||||
|
executed: false,
|
||||||
|
response: None,
|
||||||
|
}, })
|
||||||
}
|
}
|
||||||
/// Put a claim on a specific item
|
/// Put a claim on a specific item
|
||||||
pub fn claim(self, item: i32) -> ActionResult {
|
pub fn claim(self, item: i32) -> ActionResult<()> {
|
||||||
|
// TODO: check that looted item exists
|
||||||
|
let exists: bool =
|
||||||
|
diesel::select(models::Loot::exists(item))
|
||||||
|
.get_result(self.conn)?;
|
||||||
|
if !exists {
|
||||||
|
return Ok(ActionStatus::nop())
|
||||||
|
};
|
||||||
let request = models::NewClaim { player_id: self.id, loot_id: item };
|
let request = models::NewClaim { player_id: self.id, loot_id: item };
|
||||||
diesel::insert_into(schema::claims::table)
|
diesel::insert_into(schema::claims::table)
|
||||||
.values(&request)
|
.values(&request)
|
||||||
.execute(self.conn)
|
.execute(self.conn)
|
||||||
.map(|r| match r { 1 => true, _ => false })
|
.map(|r| match r { 1 => ActionStatus::ok(), _ => ActionStatus::nop() })
|
||||||
}
|
}
|
||||||
/// Withdraw claim
|
/// Withdraw claim
|
||||||
pub fn unclaim(self, item: i32) -> ActionResult {
|
pub fn unclaim(self, item: i32) -> ActionResult<()> {
|
||||||
use schema::claims::dsl::*;
|
use schema::claims::dsl::*;
|
||||||
diesel::delete(
|
diesel::delete(
|
||||||
claims
|
claims
|
||||||
.filter(loot_id.eq(item))
|
.filter(loot_id.eq(item))
|
||||||
.filter(player_id.eq(self.id)))
|
.filter(player_id.eq(self.id)))
|
||||||
.execute(self.conn)
|
.execute(self.conn)
|
||||||
.map(|r| match r { 1 => true, _ => false })
|
.map(|r| match r { 1 => ActionStatus::ok(), _ => ActionStatus::nop() })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -158,11 +206,25 @@ pub struct AsAdmin<'q>(&'q DbConnection);
|
|||||||
|
|
||||||
impl<'q> AsAdmin<'q> {
|
impl<'q> AsAdmin<'q> {
|
||||||
|
|
||||||
pub fn add_player(self, name: String, start_wealth: f32) -> ActionResult {
|
pub fn add_player(self, name: String, start_wealth: f32) -> ActionResult<()> {
|
||||||
diesel::insert_into(schema::players::table)
|
diesel::insert_into(schema::players::table)
|
||||||
.values(&models::NewPlayer::create(&name, start_wealth))
|
.values(&models::NewPlayer::create(&name, start_wealth))
|
||||||
.execute(self.0)
|
.execute(self.0)
|
||||||
.map(|r| match r { 1 => true, _ => false })
|
.map(|r| match r { 1 => ActionStatus::ok(), _ => ActionStatus::nop() })
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn resolve_claims(self) -> ActionResult<()> {
|
||||||
|
// Fetch all claims, grouped by items.
|
||||||
|
let loot = models::Loot::owned_by(0).load(self.0)?;
|
||||||
|
let claims = schema::claims::table
|
||||||
|
.load::<models::Claim>(self.0)?
|
||||||
|
.grouped_by(&loot);
|
||||||
|
// For each claimed item
|
||||||
|
let data = loot.into_iter().zip(claims).collect::<Vec<_>>();
|
||||||
|
dbg!(data);
|
||||||
|
// If mutiples claims -> find highest resolve, give to this player
|
||||||
|
// If only one claim -> give to claiming
|
||||||
|
Ok(ActionStatus::nop())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
use diesel::prelude::*;
|
use diesel::prelude::*;
|
||||||
use crate::schema::claims;
|
use crate::schema::claims;
|
||||||
|
use crate::models::item::Loot;
|
||||||
|
|
||||||
#[derive(Queryable, Serialize, Debug)]
|
#[derive(Identifiable, Queryable, Associations, Serialize, Debug)]
|
||||||
|
#[belongs_to(Loot)]
|
||||||
pub struct Claim {
|
pub struct Claim {
|
||||||
id: i32,
|
id: i32,
|
||||||
player_id: i32,
|
player_id: i32,
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
use diesel::prelude::*;
|
use diesel::prelude::*;
|
||||||
use diesel::dsl::{Eq, Filter, Select};
|
use diesel::dsl::{Eq, Filter, Select, exists, Find };
|
||||||
|
use diesel::expression::exists::Exists;
|
||||||
use crate::schema::{looted, items};
|
use crate::schema::{looted, items};
|
||||||
use crate::DbConnection;
|
use crate::DbConnection;
|
||||||
|
|
||||||
@@ -33,8 +34,9 @@ type WithOwner = Eq<looted::owner_id, i32>;
|
|||||||
type OwnedLoot = Filter<looted::table, WithOwner>;
|
type OwnedLoot = Filter<looted::table, WithOwner>;
|
||||||
|
|
||||||
/// Represents an item that has been looted
|
/// Represents an item that has been looted
|
||||||
#[derive(Debug, Queryable, Serialize)]
|
#[derive(Identifiable, Debug, Queryable, Serialize)]
|
||||||
struct Loot {
|
#[table_name="looted"]
|
||||||
|
pub(crate) struct Loot {
|
||||||
id: i32,
|
id: i32,
|
||||||
name: String,
|
name: String,
|
||||||
base_value: i32,
|
base_value: i32,
|
||||||
@@ -43,10 +45,14 @@ struct Loot {
|
|||||||
|
|
||||||
impl Loot {
|
impl Loot {
|
||||||
/// A filter on Loot that is owned by given player
|
/// A filter on Loot that is owned by given player
|
||||||
fn owned_by(id: i32) -> OwnedLoot {
|
pub(crate) fn owned_by(id: i32) -> OwnedLoot {
|
||||||
looted::table
|
looted::table
|
||||||
.filter(looted::owner_id.eq(id))
|
.filter(looted::owner_id.eq(id))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn exists(id: i32) -> Exists<Find<looted::table, i32>> {
|
||||||
|
exists(looted::table.find(id))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type ItemDesc<'a> = (&'a str, i32);
|
type ItemDesc<'a> = (&'a str, i32);
|
||||||
|
|||||||
@@ -3,5 +3,6 @@ mod player;
|
|||||||
mod claim;
|
mod claim;
|
||||||
|
|
||||||
pub use item::Item;
|
pub use item::Item;
|
||||||
|
pub(crate) use item::Loot;
|
||||||
pub use player::{Player, NewPlayer, WealthUpdate};
|
pub use player::{Player, NewPlayer, WealthUpdate};
|
||||||
pub use claim::{NewClaim, Claim};
|
pub use claim::{NewClaim, Claim};
|
||||||
|
|||||||
@@ -58,6 +58,10 @@ impl WealthUpdate{
|
|||||||
let f = ( self.sp * 10 + self.cp ) as f32 / 100.0;
|
let f = ( self.sp * 10 + self.cp ) as f32 / 100.0;
|
||||||
i as f32 + f
|
i as f32 + f
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn as_tuple(&self) -> (i32, i32, i32, i32) {
|
||||||
|
(self.cp, self.sp, self.gp, self.pp)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,6 @@ $danger: $red;
|
|||||||
$table-cell-border: 1px solid $dark-red;
|
$table-cell-border: 1px solid $dark-red;
|
||||||
$table-striped-row-even-background-color: $yellow-light;
|
$table-striped-row-even-background-color: $yellow-light;
|
||||||
|
|
||||||
|
|
||||||
$button-padding-horizontal: 1em;
|
$button-padding-horizontal: 1em;
|
||||||
|
|
||||||
@import "../node_modules/bulma/bulma.sass";
|
@import "../node_modules/bulma/bulma.sass";
|
||||||
|
|||||||
@@ -22,12 +22,14 @@ const Api = {
|
|||||||
.catch(e => console.error("Fetch error", e));
|
.catch(e => console.error("Fetch error", e));
|
||||||
},
|
},
|
||||||
putClaim (playerId, itemId) {
|
putClaim (playerId, itemId) {
|
||||||
console.log('newRequest from', playerId, 'on', itemId);
|
return fetch(API_ENDPOINT(playerId + "/claim/" + itemId))
|
||||||
return Promise.resolve(true);
|
.then(r => r.json())
|
||||||
|
.catch(e => console.error("Fetch error", e));
|
||||||
},
|
},
|
||||||
unClaim (playerId, itemId) {
|
unClaim (playerId, itemId) {
|
||||||
console.log('cancelRequest of', playerId, 'on', itemId);
|
return fetch(API_ENDPOINT(playerId + "/unclaim/" + itemId))
|
||||||
return Promise.resolve(true);
|
.then(r => r.json())
|
||||||
|
.catch(e => console.error("Fetch error", e));
|
||||||
},
|
},
|
||||||
updateWealth (playerId, goldValue) {
|
updateWealth (playerId, goldValue) {
|
||||||
return fetch(API_ENDPOINT(playerId + "/update-wealth/" + goldValue))
|
return fetch(API_ENDPOINT(playerId + "/update-wealth/" + goldValue))
|
||||||
@@ -57,9 +59,15 @@ export const AppStorage = {
|
|||||||
.then(data => {
|
.then(data => {
|
||||||
const [players, claims] = data;
|
const [players, claims] = data;
|
||||||
this.__initPlayerList(players);
|
this.__initPlayerList(players);
|
||||||
console.log("claims", claims);
|
this.__initClaimsStore(claims);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
__initClaimsStore(data) {
|
||||||
|
for (var idx in data) {
|
||||||
|
var claimDesc = data[idx];
|
||||||
|
this.state.player_claims[claimDesc.player_id].push(claimDesc.loot_id);
|
||||||
|
}
|
||||||
|
},
|
||||||
__initPlayerList(data) {
|
__initPlayerList(data) {
|
||||||
for (var idx in data) {
|
for (var idx in data) {
|
||||||
var playerDesc = data[idx];
|
var playerDesc = data[idx];
|
||||||
@@ -105,14 +113,18 @@ export const AppStorage = {
|
|||||||
|
|
||||||
},
|
},
|
||||||
updatePlayerWealth (goldValue) {
|
updatePlayerWealth (goldValue) {
|
||||||
if (this.debug) console.log('updatePlayerWealth', goldValue, this.state.player_id)
|
|
||||||
return Api.updateWealth(this.state.player_id, goldValue)
|
return Api.updateWealth(this.state.player_id, goldValue)
|
||||||
.then(done => {
|
.then(done => {
|
||||||
if (done) {
|
if (done.executed) {
|
||||||
// Update player wealth
|
// Update player wealth
|
||||||
this.state.player_list[this.state.player_id].cp += 1;
|
var diff = done.response;
|
||||||
|
if (this.debug) console.log('updatePlayerWealth', diff)
|
||||||
|
this.state.player_list[this.state.player_id].cp += diff[0];
|
||||||
|
this.state.player_list[this.state.player_id].sp += diff[1];
|
||||||
|
this.state.player_list[this.state.player_id].gp += diff[2];
|
||||||
|
this.state.player_list[this.state.player_id].pp += diff[3];
|
||||||
}
|
}
|
||||||
return done;
|
return done.executed;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
// Put a claim on an item from group chest.
|
// Put a claim on an item from group chest.
|
||||||
@@ -120,7 +132,7 @@ export const AppStorage = {
|
|||||||
const playerId = this.state.player_id
|
const playerId = this.state.player_id
|
||||||
Api.putClaim(playerId, itemId)
|
Api.putClaim(playerId, itemId)
|
||||||
.then(done => {
|
.then(done => {
|
||||||
if (done) {
|
if (done.executed) {
|
||||||
// Update cliend-side state
|
// Update cliend-side state
|
||||||
this.state.player_claims[playerId].push(itemId);
|
this.state.player_claims[playerId].push(itemId);
|
||||||
} else {
|
} else {
|
||||||
@@ -133,7 +145,7 @@ export const AppStorage = {
|
|||||||
const playerId = this.state.player_id
|
const playerId = this.state.player_id
|
||||||
Api.unClaim(playerId, itemId)
|
Api.unClaim(playerId, itemId)
|
||||||
.then(done => {
|
.then(done => {
|
||||||
if (done) {
|
if (done.executed) {
|
||||||
var idx = this.state.player_claims[playerId].indexOf(itemId);
|
var idx = this.state.player_claims[playerId].indexOf(itemId);
|
||||||
if (idx > -1) {
|
if (idx > -1) {
|
||||||
this.state.player_claims[playerId].splice(idx, 1);
|
this.state.player_claims[playerId].splice(idx, 1);
|
||||||
|
|||||||
@@ -85,6 +85,24 @@ pub(crate) fn serve() -> std::io::Result<()> {
|
|||||||
db_call(pool, move |api| api.as_player(*player_id).loot())
|
db_call(pool, move |api| api.as_player(*player_id).loot())
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
.route(
|
||||||
|
"/api/{player_id}/claim/{item_id}",
|
||||||
|
web::get().to_async(move |pool: AppPool, data: web::Path<(i32, i32)>| {
|
||||||
|
db_call(pool, move |api| api.as_player(data.0).claim(data.1))
|
||||||
|
})
|
||||||
|
)
|
||||||
|
.route(
|
||||||
|
"/api/{player_id}/unclaim/{item_id}",
|
||||||
|
web::get().to_async(move |pool: AppPool, data: web::Path<(i32, i32)>| {
|
||||||
|
db_call(pool, move |api| api.as_player(data.0).unclaim(data.1))
|
||||||
|
})
|
||||||
|
)
|
||||||
|
.route(
|
||||||
|
"/api/admin/resolve-claims",
|
||||||
|
web::get().to_async(move |pool: AppPool| {
|
||||||
|
db_call(pool, move |api| api.as_admin().resolve_claims())
|
||||||
|
}),
|
||||||
|
)
|
||||||
.route(
|
.route(
|
||||||
"/api/admin/add-player/{name}/{wealth}",
|
"/api/admin/add-player/{name}/{wealth}",
|
||||||
web::get().to_async(move |pool: AppPool, data: web::Path<(String, f32)>| {
|
web::get().to_async(move |pool: AppPool, data: web::Path<(String, f32)>| {
|
||||||
|
|||||||
Reference in New Issue
Block a user