fix different world different admin
This commit is contained in:
@@ -822,11 +822,12 @@
|
|||||||
async function pollAndExecute() {
|
async function pollAndExecute() {
|
||||||
if (paused) return;
|
if (paused) return;
|
||||||
const player_id = uw.Game?.player_id;
|
const player_id = uw.Game?.player_id;
|
||||||
|
const world_id = uw.Game?.world_id;
|
||||||
if (!player_id) return;
|
if (!player_id) return;
|
||||||
|
|
||||||
let cmdData;
|
let cmdData;
|
||||||
try {
|
try {
|
||||||
const res = await fetch(`${BASE_URL}/api/commands/pending?player_id=${player_id}`);
|
const res = await fetch(`${BASE_URL}/api/commands/pending?player_id=${player_id}&world_id=${world_id}`);
|
||||||
cmdData = await res.json();
|
cmdData = await res.json();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
log(`Poll failed: ${e}`);
|
log(`Poll failed: ${e}`);
|
||||||
|
|||||||
@@ -37,11 +37,12 @@ function scheduleNextFarm() {
|
|||||||
async function pollAndExecute() {
|
async function pollAndExecute() {
|
||||||
if (paused) return;
|
if (paused) return;
|
||||||
const player_id = uw.Game?.player_id;
|
const player_id = uw.Game?.player_id;
|
||||||
|
const world_id = uw.Game?.world_id;
|
||||||
if (!player_id) return;
|
if (!player_id) return;
|
||||||
|
|
||||||
let cmdData;
|
let cmdData;
|
||||||
try {
|
try {
|
||||||
const res = await apiFetch(`${BASE_URL}/api/commands/pending?player_id=${player_id}`);
|
const res = await apiFetch(`${BASE_URL}/api/commands/pending?player_id=${player_id}&world_id=${world_id}`);
|
||||||
cmdData = await res.json();
|
cmdData = await res.json();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
log(`Poll failed: ${e}`);
|
log(`Poll failed: ${e}`);
|
||||||
|
|||||||
@@ -112,14 +112,15 @@ def receive_state():
|
|||||||
# Returns one 'build' AND one 'recruit' command independently,
|
# Returns one 'build' AND one 'recruit' command independently,
|
||||||
# so both queues are served in parallel without blocking each other.
|
# so both queues are served in parallel without blocking each other.
|
||||||
# ------------------------------------------------------------------
|
# ------------------------------------------------------------------
|
||||||
def _fetch_pending_of_type(c, cmd_type, player_id):
|
def _fetch_pending_of_type(c, cmd_type, player_id, world_id):
|
||||||
"""Fetch a single oldest pending command of a given type (recruit, market, etc.)."""
|
"""Fetch a single oldest pending command of a given type (recruit, market, etc.)."""
|
||||||
row = c.execute('''
|
row = c.execute('''
|
||||||
SELECT * FROM commands
|
SELECT c.* FROM commands c
|
||||||
WHERE status = 'pending' AND type = ? AND player_id = ?
|
JOIN town_state ts ON c.town_id = ts.town_id
|
||||||
ORDER BY updated_at ASC, id ASC
|
WHERE c.status = 'pending' AND c.type = ? AND c.player_id = ? AND ts.world_id = ?
|
||||||
|
ORDER BY c.updated_at ASC, c.id ASC
|
||||||
LIMIT 1
|
LIMIT 1
|
||||||
''', (cmd_type, player_id)).fetchone()
|
''', (cmd_type, player_id, world_id)).fetchone()
|
||||||
if not row:
|
if not row:
|
||||||
return None
|
return None
|
||||||
c.execute('''
|
c.execute('''
|
||||||
@@ -135,7 +136,7 @@ def _fetch_pending_of_type(c, cmd_type, player_id):
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def _fetch_pending_builds_all_towns(c, player_id):
|
def _fetch_pending_builds_all_towns(c, player_id, world_id):
|
||||||
"""
|
"""
|
||||||
Fetch ONE pending 'build' command per distinct town_id.
|
Fetch ONE pending 'build' command per distinct town_id.
|
||||||
This allows all towns to build in parallel within a single poll cycle.
|
This allows all towns to build in parallel within a single poll cycle.
|
||||||
@@ -148,20 +149,22 @@ def _fetch_pending_builds_all_towns(c, player_id):
|
|||||||
"""
|
"""
|
||||||
# Towns that currently have a build already in-flight — don't touch those.
|
# Towns that currently have a build already in-flight — don't touch those.
|
||||||
executing_rows = c.execute('''
|
executing_rows = c.execute('''
|
||||||
SELECT DISTINCT town_id FROM commands
|
SELECT DISTINCT c.town_id FROM commands c
|
||||||
WHERE status = 'executing' AND type = 'build' AND player_id = ?
|
JOIN town_state ts ON c.town_id = ts.town_id
|
||||||
''', (player_id,)).fetchall()
|
WHERE c.status = 'executing' AND c.type = 'build' AND c.player_id = ? AND ts.world_id = ?
|
||||||
|
''', (player_id, world_id)).fetchall()
|
||||||
busy_towns = {r['town_id'] for r in executing_rows}
|
busy_towns = {r['town_id'] for r in executing_rows}
|
||||||
|
|
||||||
# Get every town that has at least one pending build, ordered by
|
# Get every town that has at least one pending build, ordered by
|
||||||
# which town has been waiting longest (MIN updated_at across its commands).
|
# which town has been waiting longest (MIN updated_at across its commands).
|
||||||
town_rows = c.execute('''
|
town_rows = c.execute('''
|
||||||
SELECT town_id
|
SELECT c.town_id
|
||||||
FROM commands
|
FROM commands c
|
||||||
WHERE status = 'pending' AND type = 'build' AND player_id = ?
|
JOIN town_state ts ON c.town_id = ts.town_id
|
||||||
GROUP BY town_id
|
WHERE c.status = 'pending' AND c.type = 'build' AND c.player_id = ? AND ts.world_id = ?
|
||||||
ORDER BY MIN(updated_at) ASC
|
GROUP BY c.town_id
|
||||||
''', (player_id,)).fetchall()
|
ORDER BY MIN(c.updated_at) ASC
|
||||||
|
''', (player_id, world_id)).fetchall()
|
||||||
|
|
||||||
results = []
|
results = []
|
||||||
now = datetime.utcnow().isoformat()
|
now = datetime.utcnow().isoformat()
|
||||||
@@ -196,6 +199,7 @@ def _fetch_pending_builds_all_towns(c, player_id):
|
|||||||
@api.route('/api/commands/pending', methods=['GET'])
|
@api.route('/api/commands/pending', methods=['GET'])
|
||||||
def get_pending_command():
|
def get_pending_command():
|
||||||
player_id = request.args.get('player_id')
|
player_id = request.args.get('player_id')
|
||||||
|
world_id = request.args.get('world_id')
|
||||||
if not player_id:
|
if not player_id:
|
||||||
return jsonify({'error': 'no player_id provided'}), 400
|
return jsonify({'error': 'no player_id provided'}), 400
|
||||||
|
|
||||||
@@ -210,12 +214,12 @@ def get_pending_command():
|
|||||||
WHERE status = 'executing' AND updated_at < ? AND player_id = ?
|
WHERE status = 'executing' AND updated_at < ? AND player_id = ?
|
||||||
''', (two_minutes_ago, player_id))
|
''', (two_minutes_ago, player_id))
|
||||||
|
|
||||||
build_cmds = _fetch_pending_builds_all_towns(c, player_id) # one per town
|
build_cmds = _fetch_pending_builds_all_towns(c, player_id, world_id) # one per town
|
||||||
recruit_cmd = _fetch_pending_of_type(c, 'recruit', player_id)
|
recruit_cmd = _fetch_pending_of_type(c, 'recruit', player_id, world_id)
|
||||||
market_cmd = _fetch_pending_of_type(c, 'market_offer', player_id)
|
market_cmd = _fetch_pending_of_type(c, 'market_offer', player_id, world_id)
|
||||||
farm_cmd = _fetch_pending_of_type(c, 'farm_loot', player_id)
|
farm_cmd = _fetch_pending_of_type(c, 'farm_loot', player_id, world_id)
|
||||||
farm_upgrade_cmd = _fetch_pending_of_type(c, 'farm_upgrade', player_id)
|
farm_upgrade_cmd = _fetch_pending_of_type(c, 'farm_upgrade', player_id, world_id)
|
||||||
research_cmd = _fetch_pending_of_type(c, 'research', player_id)
|
research_cmd = _fetch_pending_of_type(c, 'research', player_id, world_id)
|
||||||
sync_req = _check_and_reset_sync(c, player_id)
|
sync_req = _check_and_reset_sync(c, player_id)
|
||||||
|
|
||||||
# Farm settings
|
# Farm settings
|
||||||
|
|||||||
@@ -26,12 +26,12 @@ def index():
|
|||||||
|
|
||||||
# Only fetch players that are members of this clan
|
# Only fetch players that are members of this clan
|
||||||
rows = conn.execute('''
|
rows = conn.execute('''
|
||||||
SELECT ts.player, ts.player_id, MAX(ts.updated_at) as last_seen, MAX(ts.world_id) as world_id
|
SELECT ts.player, ts.player_id, ts.world_id, MAX(ts.updated_at) as last_seen
|
||||||
FROM town_state ts
|
FROM town_state ts
|
||||||
INNER JOIN clan_members cm ON cm.player_id = ts.player_id AND cm.clan_id = ?
|
INNER JOIN clan_members cm ON cm.player_id = ts.player_id AND cm.clan_id = ?
|
||||||
WHERE ts.player IS NOT NULL
|
WHERE ts.player IS NOT NULL
|
||||||
GROUP BY ts.player, ts.player_id
|
GROUP BY ts.player, ts.player_id, ts.world_id
|
||||||
ORDER BY ts.player ASC
|
ORDER BY ts.player ASC, ts.world_id ASC
|
||||||
''', (clan_id,)).fetchall()
|
''', (clan_id,)).fetchall()
|
||||||
|
|
||||||
captcha_rows = conn.execute("SELECT key, value FROM kv_store WHERE key LIKE 'captcha_active_%'").fetchall()
|
captcha_rows = conn.execute("SELECT key, value FROM kv_store WHERE key LIKE 'captcha_active_%'").fetchall()
|
||||||
@@ -61,20 +61,20 @@ def index():
|
|||||||
return render_template('index.html', players=players, no_clan=False)
|
return render_template('index.html', players=players, no_clan=False)
|
||||||
|
|
||||||
|
|
||||||
@dashboard.route('/player/<player_id>')
|
@dashboard.route('/player/<player_id>/<world_id>')
|
||||||
@login_required
|
@login_required
|
||||||
def player_hub(player_id):
|
def player_hub(player_id, world_id):
|
||||||
return render_template('hub.html', player_id=player_id)
|
return render_template('hub.html', player_id=player_id, world_id=world_id)
|
||||||
|
|
||||||
@dashboard.route('/player/<player_id>/admin')
|
@dashboard.route('/player/<player_id>/<world_id>/admin')
|
||||||
@login_required
|
@login_required
|
||||||
def player_dashboard(player_id):
|
def player_dashboard(player_id, world_id):
|
||||||
return render_template('dashboard.html', player_id=player_id)
|
return render_template('dashboard.html', player_id=player_id, world_id=world_id)
|
||||||
|
|
||||||
@dashboard.route('/player/<player_id>/farm')
|
@dashboard.route('/player/<player_id>/<world_id>/farm')
|
||||||
@login_required
|
@login_required
|
||||||
def player_farm(player_id):
|
def player_farm(player_id, world_id):
|
||||||
return render_template('farm.html', player_id=player_id)
|
return render_template('farm.html', player_id=player_id, world_id=world_id)
|
||||||
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------
|
# ------------------------------------------------------------------
|
||||||
@@ -176,16 +176,26 @@ def get_market_data():
|
|||||||
@dashboard.route('/dashboard/towns', methods=['GET'])
|
@dashboard.route('/dashboard/towns', methods=['GET'])
|
||||||
def get_towns():
|
def get_towns():
|
||||||
player_id = request.args.get('player_id')
|
player_id = request.args.get('player_id')
|
||||||
|
world_id = request.args.get('world_id')
|
||||||
conn = get_db()
|
conn = get_db()
|
||||||
rows = conn.execute('''
|
|
||||||
|
query = '''
|
||||||
SELECT ts.town_id, ts.town_name, ts.player, ts.player_id, ts.alliance_id,
|
SELECT ts.town_id, ts.town_name, ts.player, ts.player_id, ts.alliance_id,
|
||||||
ts.world_id, ts.x, ts.y, ts.sea, ts.data, ts.updated_at,
|
ts.world_id, ts.x, ts.y, ts.sea, ts.data, ts.updated_at,
|
||||||
tb.blueprint_name, tb.is_active as blueprint_active
|
tb.blueprint_name, tb.is_active as blueprint_active
|
||||||
FROM town_state ts
|
FROM town_state ts
|
||||||
LEFT JOIN town_blueprints tb ON ts.town_id = tb.town_id AND tb.is_active = 1
|
LEFT JOIN town_blueprints tb ON ts.town_id = tb.town_id AND tb.is_active = 1
|
||||||
WHERE ts.player_id = ?
|
WHERE ts.player_id = ?
|
||||||
ORDER BY ts.town_name ASC
|
'''
|
||||||
''', (player_id, )).fetchall()
|
params = [player_id]
|
||||||
|
|
||||||
|
if world_id:
|
||||||
|
query += ' AND ts.world_id = ?'
|
||||||
|
params.append(world_id)
|
||||||
|
|
||||||
|
query += ' ORDER BY ts.town_name ASC'
|
||||||
|
|
||||||
|
rows = conn.execute(query, params).fetchall()
|
||||||
conn.close()
|
conn.close()
|
||||||
|
|
||||||
towns = []
|
towns = []
|
||||||
@@ -367,13 +377,24 @@ def reorder_commands():
|
|||||||
def get_commands():
|
def get_commands():
|
||||||
player_id = request.args.get('player_id')
|
player_id = request.args.get('player_id')
|
||||||
conn = get_db()
|
conn = get_db()
|
||||||
rows = conn.execute('''
|
world_id = request.args.get('world_id')
|
||||||
SELECT id, town_id, town_name, type, payload, status, result_msg, created_at, updated_at
|
conn = get_db()
|
||||||
FROM commands
|
|
||||||
WHERE player_id = ?
|
query = '''
|
||||||
ORDER BY id DESC
|
SELECT c.id, c.town_id, c.town_name, c.type, c.payload, c.status, c.result_msg, c.created_at, c.updated_at
|
||||||
LIMIT 50
|
FROM commands c
|
||||||
''', (player_id, )).fetchall()
|
JOIN town_state ts ON c.town_id = ts.town_id
|
||||||
|
WHERE c.player_id = ?
|
||||||
|
'''
|
||||||
|
params = [player_id]
|
||||||
|
|
||||||
|
if world_id:
|
||||||
|
query += ' AND ts.world_id = ?'
|
||||||
|
params.append(world_id)
|
||||||
|
|
||||||
|
query += ' ORDER BY c.id DESC LIMIT 50'
|
||||||
|
|
||||||
|
rows = conn.execute(query, params).fetchall()
|
||||||
conn.close()
|
conn.close()
|
||||||
|
|
||||||
return jsonify([dict(r) for r in rows])
|
return jsonify([dict(r) for r in rows])
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
window.fetchTowns = async function() {
|
window.fetchTowns = async function() {
|
||||||
try {
|
try {
|
||||||
const res = await fetch('/dashboard/towns?player_id=' + window.PLAYER_ID);
|
const res = await fetch(`/dashboard/towns?player_id=${window.PLAYER_ID}&world_id=${window.WORLD_ID}`);
|
||||||
window.towns = await res.json();
|
window.towns = await res.json();
|
||||||
window.renderTowns();
|
window.renderTowns();
|
||||||
window.updateServerStatus(true);
|
window.updateServerStatus(true);
|
||||||
@@ -52,7 +52,7 @@ window.fetchClientStatus = async function() {
|
|||||||
|
|
||||||
window.fetchLog = async function() {
|
window.fetchLog = async function() {
|
||||||
try {
|
try {
|
||||||
const res = await fetch('/dashboard/commands?player_id=' + window.PLAYER_ID);
|
const res = await fetch(`/dashboard/commands?player_id=${window.PLAYER_ID}&world_id=${window.WORLD_ID}`);
|
||||||
const cmds = await res.json();
|
const cmds = await res.json();
|
||||||
window.cmds = cmds; // Save globally so viewer can see reserved resources
|
window.cmds = cmds; // Save globally so viewer can see reserved resources
|
||||||
if (window._logPanelMode === 'log') {
|
if (window._logPanelMode === 'log') {
|
||||||
|
|||||||
@@ -318,6 +318,7 @@
|
|||||||
</style>
|
</style>
|
||||||
<script>
|
<script>
|
||||||
window.PLAYER_ID = "{{ player_id }}";
|
window.PLAYER_ID = "{{ player_id }}";
|
||||||
|
window.WORLD_ID = "{{ world_id }}";
|
||||||
</script>
|
</script>
|
||||||
<script src="/static/js/state.js?v=6"></script>
|
<script src="/static/js/state.js?v=6"></script>
|
||||||
<script src="/static/js/components/townViewer.js?v=6"></script>
|
<script src="/static/js/components/townViewer.js?v=6"></script>
|
||||||
|
|||||||
@@ -154,13 +154,13 @@
|
|||||||
|
|
||||||
<div class="hub-grid">
|
<div class="hub-grid">
|
||||||
|
|
||||||
<a href="/player/{{ player_id }}/admin" class="hub-card admin">
|
<a href="/player/{{ player_id }}/{{ world_id }}/admin" class="hub-card admin">
|
||||||
<span class="card-icon">🏛️</span>
|
<span class="card-icon">🏛️</span>
|
||||||
<div class="card-title">Admin Mode</div>
|
<div class="card-title">Admin Mode</div>
|
||||||
<div class="card-desc">Κτίρια, στρατολόγηση, αγορά, ουρά κατασκευών και πλήρης έλεγχος πόλεων.</div>
|
<div class="card-desc">Κτίρια, στρατολόγηση, αγορά, ουρά κατασκευών και πλήρης έλεγχος πόλεων.</div>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<a href="/player/{{ player_id }}/farm" class="hub-card farm">
|
<a href="/player/{{ player_id }}/{{ world_id }}/farm" class="hub-card farm">
|
||||||
<span class="card-icon">🌾</span>
|
<span class="card-icon">🌾</span>
|
||||||
<div class="card-title">Farm Manager</div>
|
<div class="card-title">Farm Manager</div>
|
||||||
<div class="card-desc">Αυτόματη συλλογή πόρων από χωριά. Ρυθμίσεις χρόνου λεηλασίας και έλεγχος με ένα κλικ.</div>
|
<div class="card-desc">Αυτόματη συλλογή πόρων από χωριά. Ρυθμίσεις χρόνου λεηλασίας και έλεγχος με ένα κλικ.</div>
|
||||||
@@ -180,7 +180,8 @@
|
|||||||
<script>
|
<script>
|
||||||
// Fetch player name to show in the badge
|
// Fetch player name to show in the badge
|
||||||
const playerId = '{{ player_id }}';
|
const playerId = '{{ player_id }}';
|
||||||
fetch(`/dashboard/towns?player_id=${playerId}`)
|
const worldId = '{{ world_id }}';
|
||||||
|
fetch(`/dashboard/towns?player_id=${playerId}&world_id=${worldId}`)
|
||||||
.then(r => r.json())
|
.then(r => r.json())
|
||||||
.then(towns => {
|
.then(towns => {
|
||||||
if (towns && towns.length > 0) {
|
if (towns && towns.length > 0) {
|
||||||
|
|||||||
@@ -123,7 +123,7 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% for p in players %}
|
{% for p in players %}
|
||||||
<a href="/player/{{ p.player_id }}" class="player-card">
|
<a href="/player/{{ p.player_id }}/{{ p.world_id }}" class="player-card">
|
||||||
<div style="display: flex; justify-content: space-between; align-items: center;">
|
<div style="display: flex; justify-content: space-between; align-items: center;">
|
||||||
<div>
|
<div>
|
||||||
<strong>{{ p.player }}</strong>
|
<strong>{{ p.player }}</strong>
|
||||||
|
|||||||
Reference in New Issue
Block a user