Added trading and quests, checkpoint push

This commit is contained in:
Joan
2026-02-08 20:18:42 +01:00
parent 8820cd897e
commit 70dc35b4b2
36 changed files with 3583 additions and 279 deletions

View File

@@ -819,7 +819,83 @@ async def process_status_effects(manager=None):
logger.error(f"❌ Error in status effects task: {e}", exc_info=True)
await asyncio.sleep(10)
# ============================================================================
# BACKGROUND TASK: MERCHANT RESTOCK
# ============================================================================
async def restock_merchants(manager=None, npcs_data=None):
"""Periodically restocks merchant inventory."""
logger.info("💰 Merchant Restock task started")
# If no data provided, we can't restock effectively without doing I/O which we want to avoid.
if not npcs_data:
logger.warning("⚠️ No NPC data provided to restock task. Merchants will not restock.")
return
while True:
try:
# Use injected data
static_npcs = npcs_data
start_time = time.time()
restocked_count = 0
for npc_id, npc_def in static_npcs.items():
trade_cfg = npc_def.get('trade', {})
if not trade_cfg.get('enabled'):
continue
stock_config = trade_cfg.get('stock', [])
for item_cfg in stock_config:
if item_cfg.get('infinite'):
continue
item_id = item_cfg['item_id']
max_stock = item_cfg.get('max_stock', 10)
restock_rate = item_cfg.get('restock_rate', 1)
# Get current stock
current_item = await db.get_merchant_stock_item(npc_id, item_id)
now = time.time()
if not current_item:
# Initialize if missing
# If we assume 'restocked' means it should exist.
await db.update_merchant_stock(
npc_id=npc_id,
item_id=item_id,
quantity=restock_rate,
update_restock_time=True
)
restocked_count += 1
continue
# Check timer (1 hour default)
last_restock = current_item.get('last_restock_at', 0)
if now - last_restock > 3600: # 1 hour
current_qty = current_item['quantity']
if current_qty < max_stock:
new_qty = min(max_stock, current_qty + restock_rate)
await db.update_merchant_stock(
npc_id=npc_id,
item_id=item_id,
quantity=new_qty,
update_restock_time=True
)
restocked_count += 1
if restocked_count > 0:
elapsed = time.time() - start_time
logger.info(f"Restocked {restocked_count} items in {elapsed:.2f}s")
await asyncio.sleep(600) # Check every 10 minutes
except Exception as e:
logger.error(f"❌ Error in merchant restock task: {e}", exc_info=True)
await asyncio.sleep(60)
# ============================================================================
# TASK STARTUP FUNCTION
# ============================================================================
@@ -868,7 +944,7 @@ def release_background_tasks_lock():
_lock_file_handle = None
async def start_background_tasks(manager=None, world_locations=None):
async def start_background_tasks(manager=None, world_locations=None, npcs_data=None):
"""
Start all background tasks.
Called when the API starts up.
@@ -877,6 +953,7 @@ async def start_background_tasks(manager=None, world_locations=None):
Args:
manager: WebSocket ConnectionManager for broadcasting events
world_locations: Dict of Location objects for interactable mapping
npcs_data: Dict of static NPC definitions
"""
# Try to acquire lock - only one worker will succeed
if not acquire_background_tasks_lock():
@@ -894,6 +971,7 @@ async def start_background_tasks(manager=None, world_locations=None):
asyncio.create_task(check_pvp_combat_timers(manager)),
asyncio.create_task(decay_corpses(manager)),
asyncio.create_task(process_status_effects(manager)),
asyncio.create_task(restock_merchants(manager, npcs_data)),
# Note: Interactable cooldowns are handled client-side with server validation
]