stop minting a new account on every login
if the client presents an id we don't know, create it under that id and bump the sequence past it. also chest offers match through BaseChest, so one silver row covers every arena.
This commit is contained in:
parent
8a745d634c
commit
a2bf58a627
5 changed files with 86 additions and 3 deletions
|
|
@ -69,7 +69,7 @@ impl AuthApi for AuthService {
|
||||||
message,
|
message,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
let stored = if account.is_zero() || pass_token.is_none() {
|
let stored = if account.is_zero() {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
self.store.get(account).await.map_err(unavailable)?
|
self.store.get(account).await.map_err(unavailable)?
|
||||||
|
|
@ -92,6 +92,15 @@ impl AuthApi for AuthService {
|
||||||
}
|
}
|
||||||
existing
|
existing
|
||||||
}
|
}
|
||||||
|
None if !account.is_zero() => {
|
||||||
|
let adopted = self
|
||||||
|
.store
|
||||||
|
.adopt(account, pass_token.as_deref())
|
||||||
|
.await
|
||||||
|
.map_err(unavailable)?;
|
||||||
|
tracing::info!(account = %adopted.account_ref(), "adopted the account the client asked for");
|
||||||
|
adopted
|
||||||
|
}
|
||||||
None => {
|
None => {
|
||||||
let created = self.store.create().await.map_err(unavailable)?;
|
let created = self.store.create().await.map_err(unavailable)?;
|
||||||
tracing::info!(account = %created.account_ref(), "created account");
|
tracing::info!(account = %created.account_ref(), "created account");
|
||||||
|
|
|
||||||
|
|
@ -41,6 +41,41 @@ impl AccountStore {
|
||||||
.await?;
|
.await?;
|
||||||
Ok(account_from_row(&row))
|
Ok(account_from_row(&row))
|
||||||
}
|
}
|
||||||
|
pub async fn adopt(&self, account: AccountRef, pass_token: Option<&str>) -> Result<Account> {
|
||||||
|
let now = unix_seconds();
|
||||||
|
let token = pass_token
|
||||||
|
.map(str::to_owned)
|
||||||
|
.filter(|token| !token.is_empty())
|
||||||
|
.unwrap_or_else(generate_pass_token);
|
||||||
|
let row = storage::sqlx::query(
|
||||||
|
"insert into accounts (
|
||||||
|
account_high, account_low, pass_token, created_at, last_seen_at,
|
||||||
|
session_count, play_time_seconds, banned
|
||||||
|
)
|
||||||
|
values ($1, $2, $3, $4, $4, 0, 0, false)
|
||||||
|
on conflict (account_high, account_low) do nothing
|
||||||
|
returning *",
|
||||||
|
)
|
||||||
|
.bind(account.high)
|
||||||
|
.bind(account.low)
|
||||||
|
.bind(token)
|
||||||
|
.bind(now)
|
||||||
|
.fetch_optional(self.database.pool())
|
||||||
|
.await?;
|
||||||
|
storage::sqlx::query(
|
||||||
|
"select setval('accounts_low_seq', greatest($1, (select last_value from accounts_low_seq)))",
|
||||||
|
)
|
||||||
|
.bind(account.low.max(1) as i64)
|
||||||
|
.execute(self.database.pool())
|
||||||
|
.await?;
|
||||||
|
match row {
|
||||||
|
Some(row) => Ok(account_from_row(&row)),
|
||||||
|
None => self
|
||||||
|
.get(account)
|
||||||
|
.await?
|
||||||
|
.ok_or_else(|| storage::StorageError::corrupt("the account vanished mid adoption")),
|
||||||
|
}
|
||||||
|
}
|
||||||
pub async fn get(&self, account: AccountRef) -> Result<Option<Account>> {
|
pub async fn get(&self, account: AccountRef) -> Result<Option<Account>> {
|
||||||
let row = storage::sqlx::query(
|
let row = storage::sqlx::query(
|
||||||
"select * from accounts where account_high = $1 and account_low = $2",
|
"select * from accounts where account_high = $1 and account_low = $2",
|
||||||
|
|
|
||||||
|
|
@ -66,7 +66,7 @@ impl HomeMode {
|
||||||
}
|
}
|
||||||
fn market_cost(&self, give: &ShopEntry) -> Result<ShopEntry, &'static str> {
|
fn market_cost(&self, give: &ShopEntry) -> Result<ShopEntry, &'static str> {
|
||||||
if let Some(chest) = give.data.as_treasure_chest() {
|
if let Some(chest) = give.data.as_treasure_chest() {
|
||||||
let price = chest.int("ShopPriceWithoutSpeedUp");
|
let price = chest.shop_price();
|
||||||
if price < 1 {
|
if price < 1 {
|
||||||
return Err("that chest is not sold in the shop");
|
return Err("that chest is not sold in the shop");
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,15 @@ pub struct ShopOffer {
|
||||||
}
|
}
|
||||||
impl ShopOffer {
|
impl ShopOffer {
|
||||||
pub fn sells(&self, data: &logic::LogicDataRef) -> bool {
|
pub fn sells(&self, data: &logic::LogicDataRef) -> bool {
|
||||||
&self.give.data == data
|
if &self.give.data == data {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
match (self.give.data.as_treasure_chest(), data.as_treasure_chest()) {
|
||||||
|
(Some(offered), Some(wanted)) => {
|
||||||
|
offered.root().global_id() == wanted.root().global_id()
|
||||||
|
}
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
|
|
|
||||||
|
|
@ -47,6 +47,37 @@ impl LogicArenaData {
|
||||||
self.int("Arena")
|
self.int("Arena")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
impl LogicTreasureChestData {
|
||||||
|
pub fn base_chest(&self) -> Option<LogicTreasureChestData> {
|
||||||
|
let base = self.string("BaseChest");
|
||||||
|
if base.is_empty() || base == self.name() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
LogicDataRef::by_name(crate::data::tables::table::TREASURE_CHESTS, base).as_treasure_chest()
|
||||||
|
}
|
||||||
|
pub fn root(&self) -> LogicTreasureChestData {
|
||||||
|
match self.base_chest() {
|
||||||
|
Some(base) => base.root(),
|
||||||
|
None => self.clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn arena_data(&self) -> LogicDataRef {
|
||||||
|
LogicDataRef::by_name(crate::data::tables::table::ARENAS, self.string("Arena"))
|
||||||
|
}
|
||||||
|
pub fn shop_price(&self) -> i32 {
|
||||||
|
self.root().int("ShopPriceWithoutSpeedUp")
|
||||||
|
}
|
||||||
|
pub fn speed_up_cost(&self) -> i32 {
|
||||||
|
self.root().int("SpeedUpCost")
|
||||||
|
}
|
||||||
|
pub fn unlock_seconds(&self) -> i32 {
|
||||||
|
let root = self.root();
|
||||||
|
root.int("TimeTakenDays").saturating_mul(86_400)
|
||||||
|
+ root.int("TimeTakenHours").saturating_mul(3_600)
|
||||||
|
+ root.int("TimeTakenMinutes").saturating_mul(60)
|
||||||
|
+ root.int("TimeTakenSeconds")
|
||||||
|
}
|
||||||
|
}
|
||||||
impl LogicResourcePackData {
|
impl LogicResourcePackData {
|
||||||
pub fn resource(&self) -> LogicDataRef {
|
pub fn resource(&self) -> LogicDataRef {
|
||||||
LogicDataRef::by_name(
|
LogicDataRef::by_name(
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue