diff --git a/blueprint_engine.py b/blueprint_engine.py index 1d18b67..f4d36b4 100644 --- a/blueprint_engine.py +++ b/blueprint_engine.py @@ -1,5 +1,6 @@ import json import logging +from datetime import datetime log = logging.getLogger(__name__) @@ -78,15 +79,30 @@ def evaluate_blueprints(conn): log.warning(f"[blueprint] buildData keys: {list(build_data.keys())}") log.warning(f"[blueprint] buildingOrder length: {len(build_queue)}") - # Don't queue anything if there's already a pending/executing command in DB + # Don't queue anything if there's already a valid pending/executing command in DB db_pending = conn.execute( - "SELECT id, type, status FROM commands WHERE town_id = ? AND type IN ('build', 'research') AND status IN ('pending', 'executing')", + "SELECT id, type, status, updated_at FROM commands WHERE town_id = ? AND type IN ('build', 'research') AND status IN ('pending', 'executing')", (town_id,) ).fetchall() + if db_pending: - details = [(r['id'], r['type'], r['status']) for r in db_pending] - log.warning(f"[blueprint] Already has {len(db_pending)} pending/executing commands — skipping. Commands: {details}") - continue + # Check if any of these are malformed (NULL updated_at = created by old broken blueprint engine) + # Auto-delete those so we can re-insert them correctly + broken_ids = [r['id'] for r in db_pending if r['updated_at'] is None] + if broken_ids: + log.warning(f"[blueprint] Deleting {len(broken_ids)} malformed stuck commands {broken_ids} for town {town_id}") + conn.execute(f"DELETE FROM commands WHERE id IN ({','.join('?' for _ in broken_ids)})", broken_ids) + conn.commit() + # Re-check if there are still real pending commands + db_pending = conn.execute( + "SELECT id, type, status, updated_at FROM commands WHERE town_id = ? AND type IN ('build', 'research') AND status IN ('pending', 'executing')", + (town_id,) + ).fetchall() + + if db_pending: + details = [(r['id'], r['type'], r['status']) for r in db_pending] + log.warning(f"[blueprint] Already has {len(db_pending)} pending/executing commands — skipping. Commands: {details}") + continue # Calculate Future Levels based on current + game queue future_levels = {k: v for k, v in buildings.items()} @@ -161,18 +177,28 @@ def evaluate_blueprints(conn): log.warning(f"[blueprint] Final: target_building={target_building}, target_research={target_research}") if target_building: + now = datetime.utcnow().isoformat() + # Get next position for this town's build queue + pos_row = conn.execute( + "SELECT MAX(position) as max_pos FROM commands" + " WHERE player_id = ? AND town_id = ? AND type = 'build'" + " AND status IN ('pending', 'executing')", + (str(player_id), str(town_id)) + ).fetchone() + position = (pos_row['max_pos'] or 0) + 1 payload_str = json.dumps({"building_id": target_building}) conn.execute(''' - INSERT INTO commands (town_id, town_name, type, payload, status, player_id) - VALUES (?, ?, ?, ?, ?, ?) - ''', (town_id, town_name_db, 'build', payload_str, 'pending', player_id)) + INSERT INTO commands (town_id, town_name, type, payload, status, position, created_at, updated_at, player_id) + VALUES (?, ?, ?, ?, 'pending', ?, ?, ?, ?) + ''', (str(town_id), town_name_db, 'build', payload_str, position, now, now, str(player_id))) log.warning(f"[blueprint] Inserted build command: {target_building} for {town_name_db}") elif target_research: + now = datetime.utcnow().isoformat() payload_str = json.dumps({"research_id": target_research}) conn.execute(''' - INSERT INTO commands (town_id, town_name, type, payload, status, player_id) - VALUES (?, ?, ?, ?, ?, ?) - ''', (town_id, town_name_db, 'research', payload_str, 'pending', player_id)) + INSERT INTO commands (town_id, town_name, type, payload, status, created_at, updated_at, player_id) + VALUES (?, ?, ?, ?, 'pending', ?, ?, ?) + ''', (str(town_id), town_name_db, 'research', payload_str, now, now, str(player_id))) log.warning(f"[blueprint] Inserted research command: {target_research} for {town_name_db}") else: log.warning(f"[blueprint] Nothing to do for {town_name_db}")