From cf23f38a6e1e6464e3fe81e491d5ae9f5de991dd Mon Sep 17 00:00:00 2001 From: haunter Date: Sun, 3 May 2026 14:19:34 +0300 Subject: [PATCH] fix world --- db.py | 43 +++++++++++++++++++++++++++++++++++++----- routes/api.py | 11 ++++++----- routes/auth.py | 29 ++++++++++++++-------------- templates/options.html | 6 +++--- 4 files changed, 62 insertions(+), 27 deletions(-) diff --git a/db.py b/db.py index 71a2f50..fe5dd33 100644 --- a/db.py +++ b/db.py @@ -218,25 +218,58 @@ def init_db(): ) ''') - # Clan members — links Grepolis player_ids to a clan + # Clan members — links Grepolis player_ids to a clan. + # UNIQUE on (clan_id, player_id, world_id) so the same player + # appearing in multiple worlds creates separate rows. c.execute(''' CREATE TABLE IF NOT EXISTS clan_members ( id INTEGER PRIMARY KEY AUTOINCREMENT, clan_id INTEGER NOT NULL REFERENCES clans(id), player_id TEXT NOT NULL, player_name TEXT, - world_id TEXT, + world_id TEXT NOT NULL DEFAULT '', features TEXT NOT NULL DEFAULT 'farm,admin', joined_at TEXT NOT NULL DEFAULT (datetime('now')), - UNIQUE(clan_id, player_id) + UNIQUE(clan_id, player_id, world_id) ) ''') + # Migration: if clan_members still has the old UNIQUE(clan_id, player_id) constraint + # (without world_id), recreate the table with the correct 3-column constraint. + try: + tbl_sql = c.execute( + "SELECT sql FROM sqlite_master WHERE type='table' AND name='clan_members'" + ).fetchone() + if tbl_sql and 'player_id, world_id' not in (tbl_sql['sql'] or ''): + c.execute(''' + CREATE TABLE IF NOT EXISTS _clan_members_new ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + clan_id INTEGER NOT NULL REFERENCES clans(id), + player_id TEXT NOT NULL, + player_name TEXT, + world_id TEXT NOT NULL DEFAULT '', + features TEXT NOT NULL DEFAULT 'farm,admin', + joined_at TEXT NOT NULL DEFAULT (datetime('now')), + UNIQUE(clan_id, player_id, world_id) + ) + ''') + c.execute(''' + INSERT OR IGNORE INTO _clan_members_new + (id, clan_id, player_id, player_name, world_id, features, joined_at) + SELECT id, clan_id, player_id, player_name, + COALESCE(world_id, ''), features, joined_at + FROM clan_members + ''') + c.execute('DROP TABLE clan_members') + c.execute('ALTER TABLE _clan_members_new RENAME TO clan_members') + except Exception as _e: + print(f'clan_members migration skipped: {_e}') + # Migration: Auto-assign existing users to their clan_id if they are the owner try: c.execute(''' - UPDATE users - SET clan_id = (SELECT id FROM clans WHERE owner_id = users.id) + UPDATE users + SET clan_id = (SELECT id FROM clans WHERE owner_id = users.id) WHERE clan_id IS NULL AND EXISTS (SELECT 1 FROM clans WHERE owner_id = users.id) ''') except Exception: diff --git a/routes/api.py b/routes/api.py index aeec8f6..180246e 100644 --- a/routes/api.py +++ b/routes/api.py @@ -30,16 +30,17 @@ def _get_clan_from_request(): # Helper — auto-register a player_id under a clan on first push. # ------------------------------------------------------------------ def _auto_register_member(clan_id, player_id, player_name, world_id=''): + world_id = world_id or '' conn = get_db() conn.execute(''' INSERT OR IGNORE INTO clan_members (clan_id, player_id, player_name, world_id) VALUES (?, ?, ?, ?) - ''', (clan_id, str(player_id), player_name or '', world_id or '')) - # Update name and world on every push (they can change) + ''', (clan_id, str(player_id), player_name or '', world_id)) + # Update name on every push (it can change); world_id is part of the key so no overwrite risk conn.execute(''' - UPDATE clan_members SET player_name = ?, world_id = ? - WHERE clan_id = ? AND player_id = ? - ''', (player_name or '', world_id or '', clan_id, str(player_id))) + UPDATE clan_members SET player_name = ? + WHERE clan_id = ? AND player_id = ? AND world_id = ? + ''', (player_name or '', clan_id, str(player_id), world_id)) conn.commit() conn.close() diff --git a/routes/auth.py b/routes/auth.py index b574ff4..e317c77 100644 --- a/routes/auth.py +++ b/routes/auth.py @@ -127,12 +127,13 @@ def options(): if clan: rows = conn.execute( '''SELECT cm.id, cm.player_id, cm.player_name, cm.world_id, cm.joined_at, cm.features, - ts.updated_at + MAX(ts.updated_at) as updated_at FROM clan_members cm LEFT JOIN town_state ts ON ts.player_id = cm.player_id + AND ts.world_id = cm.world_id WHERE cm.clan_id = ? - GROUP BY cm.player_id - ORDER BY cm.joined_at DESC''', + GROUP BY cm.player_id, cm.world_id + ORDER BY cm.player_name ASC, cm.world_id ASC''', (clan['id'],) ).fetchall() @@ -212,17 +213,17 @@ def regenerate_key(): return redirect(url_for('auth.options')) -@auth.route('/auth/clan/remove-member/', methods=['POST']) +@auth.route('/auth/clan/remove-member//', methods=['POST']) @login_required -def remove_member(player_id): +def remove_member(player_id, world_id): conn = get_db() clan = conn.execute( 'SELECT id FROM clans WHERE owner_id = ?', (current_user.id,) ).fetchone() if clan: conn.execute( - 'DELETE FROM clan_members WHERE clan_id = ? AND player_id = ?', - (clan['id'], player_id) + 'DELETE FROM clan_members WHERE clan_id = ? AND player_id = ? AND world_id = ?', + (clan['id'], player_id, world_id) ) conn.commit() conn.close() @@ -232,12 +233,12 @@ def remove_member(player_id): # ------------------------------------------------------------------ # POST /auth/clan/update-features/ # ------------------------------------------------------------------ -@auth.route('/auth/clan/update-features/', methods=['POST']) +@auth.route('/auth/clan/update-features//', methods=['POST']) @login_required -def update_member_features(player_id): - farm = 'farm' if request.form.get('farm') else None - admin = 'admin' if request.form.get('admin') else None - atk_planner = 'attack_planner' if request.form.get('attack_planner') else None +def update_member_features(player_id, world_id): + farm = 'farm' if request.form.get('farm') else None + admin = 'admin' if request.form.get('admin') else None + atk_planner = 'attack_planner' if request.form.get('attack_planner') else None atk_planner_admin = 'attack_planner_admin' if request.form.get('attack_planner_admin') else None features = ','.join(f for f in [farm, admin, atk_planner, atk_planner_admin] if f) or '' @@ -248,8 +249,8 @@ def update_member_features(player_id): ).fetchone() if clan: conn.execute( - 'UPDATE clan_members SET features = ? WHERE clan_id = ? AND player_id = ?', - (features, clan['id'], player_id) + 'UPDATE clan_members SET features = ? WHERE clan_id = ? AND player_id = ? AND world_id = ?', + (features, clan['id'], player_id, world_id) ) conn.commit() conn.close() diff --git a/templates/options.html b/templates/options.html index d446a70..c467714 100644 --- a/templates/options.html +++ b/templates/options.html @@ -287,7 +287,7 @@ {% if clan.owner_id == current_user.id %} -
+