Every purchase/upgrade path (new and pre-existing) is now race-safe against concurrent requests via atomic conditional SQL updates. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
98 lines
3.0 KiB
Python
98 lines
3.0 KiB
Python
# retoor <retoor@molodetz.nl>
|
|
|
|
from __future__ import annotations
|
|
|
|
from devplacepy.utils import generate_uid
|
|
|
|
from .. import economy
|
|
from .common import _iso, _iso_week, _lvl, _now, _quests, _today
|
|
from .farm import get_farm
|
|
|
|
|
|
WEEKLY_SLOT_INDEX = 3
|
|
|
|
|
|
def ensure_quests(farm: dict, day: str) -> None:
|
|
if _quests().find_one(farm_uid=farm["uid"], day=day, scope="daily"):
|
|
return
|
|
now = _iso(_now())
|
|
for index, quest in enumerate(economy.daily_quests(farm["user_uid"], day)):
|
|
_quests().insert(
|
|
{
|
|
"uid": generate_uid(),
|
|
"farm_uid": farm["uid"],
|
|
"user_uid": farm["user_uid"],
|
|
"day": day,
|
|
"scope": "daily",
|
|
"slot_index": index,
|
|
"kind": quest["kind"],
|
|
"label": quest["label"],
|
|
"goal": quest["goal"],
|
|
"progress": 0,
|
|
"reward_coins": quest["reward_coins"],
|
|
"reward_xp": quest["reward_xp"],
|
|
"reward_stars": 0,
|
|
"claimed": 0,
|
|
"created_at": now,
|
|
"updated_at": now,
|
|
}
|
|
)
|
|
|
|
|
|
def ensure_weekly_contract(farm: dict, iso_week: str) -> None:
|
|
if not _lvl(farm, "mastery_contracts"):
|
|
return
|
|
if _quests().find_one(farm_uid=farm["uid"], day=iso_week, scope="weekly"):
|
|
return
|
|
now = _iso(_now())
|
|
contract = economy.weekly_contract(farm["user_uid"], iso_week)
|
|
_quests().insert(
|
|
{
|
|
"uid": generate_uid(),
|
|
"farm_uid": farm["uid"],
|
|
"user_uid": farm["user_uid"],
|
|
"day": iso_week,
|
|
"scope": "weekly",
|
|
"slot_index": WEEKLY_SLOT_INDEX,
|
|
"kind": contract["kind"],
|
|
"label": contract["label"],
|
|
"goal": contract["goal"],
|
|
"progress": 0,
|
|
"reward_coins": 0,
|
|
"reward_xp": contract["reward_xp"],
|
|
"reward_stars": contract["reward_stars"],
|
|
"claimed": 0,
|
|
"created_at": now,
|
|
"updated_at": now,
|
|
}
|
|
)
|
|
|
|
|
|
def advance_quests(user_uid: str, kind: str, amount: int) -> None:
|
|
if amount <= 0:
|
|
return
|
|
try:
|
|
farm = get_farm(user_uid)
|
|
if not farm:
|
|
return
|
|
now = _now()
|
|
day = _today()
|
|
ensure_quests(farm, day)
|
|
iso_week = _iso_week(now)
|
|
ensure_weekly_contract(farm, iso_week)
|
|
rows = list(_quests().find(farm_uid=farm["uid"], day=day, kind=kind, scope="daily"))
|
|
rows += list(
|
|
_quests().find(farm_uid=farm["uid"], day=iso_week, kind=kind, scope="weekly")
|
|
)
|
|
for row in rows:
|
|
if row.get("claimed"):
|
|
continue
|
|
goal = int(row.get("goal") or 0)
|
|
progress = min(goal, int(row.get("progress") or 0) + amount)
|
|
_quests().update(
|
|
{"uid": row["uid"], "progress": progress, "updated_at": _iso(now)},
|
|
["uid"],
|
|
)
|
|
except Exception:
|
|
return
|