servername,captca,timestamp,reserve resources
This commit is contained in:
@@ -14,12 +14,16 @@ dashboard = Blueprint('dashboard', __name__)
|
|||||||
def index():
|
def index():
|
||||||
conn = get_db()
|
conn = get_db()
|
||||||
rows = conn.execute('''
|
rows = conn.execute('''
|
||||||
SELECT player, player_id, MAX(updated_at) as last_seen
|
SELECT player, player_id, MAX(updated_at) as last_seen, MAX(world_id) as world_id
|
||||||
FROM town_state
|
FROM town_state
|
||||||
WHERE player IS NOT NULL
|
WHERE player IS NOT NULL
|
||||||
GROUP BY player, player_id
|
GROUP BY player, player_id
|
||||||
ORDER BY player ASC
|
ORDER BY player ASC
|
||||||
''').fetchall()
|
''').fetchall()
|
||||||
|
|
||||||
|
# Pre-fetch all active captchas
|
||||||
|
captcha_rows = conn.execute("SELECT key, value FROM kv_store WHERE key LIKE 'captcha_active_%'").fetchall()
|
||||||
|
active_captchas = {r['key'].replace('captcha_active_', ''): True for r in captcha_rows if r['value'] == '1'}
|
||||||
conn.close()
|
conn.close()
|
||||||
|
|
||||||
players = []
|
players = []
|
||||||
@@ -37,7 +41,9 @@ def index():
|
|||||||
players.append({
|
players.append({
|
||||||
'player': r['player'],
|
'player': r['player'],
|
||||||
'player_id': r['player_id'],
|
'player_id': r['player_id'],
|
||||||
'is_online': is_online
|
'world_id': r['world_id'] or 'Unknown',
|
||||||
|
'is_online': is_online,
|
||||||
|
'captcha_active': active_captchas.get(r['player_id'], False)
|
||||||
})
|
})
|
||||||
|
|
||||||
return render_template('index.html', players=players)
|
return render_template('index.html', players=players)
|
||||||
|
|||||||
@@ -9,6 +9,11 @@ window.fetchTowns = async function() {
|
|||||||
window.renderTowns();
|
window.renderTowns();
|
||||||
window.updateServerStatus(true);
|
window.updateServerStatus(true);
|
||||||
|
|
||||||
|
// Automatically select the first town if none is selected
|
||||||
|
if (!window.selectedTownId && window.towns && window.towns.length > 0) {
|
||||||
|
window.selectTown(window.towns[0].town_id);
|
||||||
|
}
|
||||||
|
|
||||||
if (window.selectedTownId) {
|
if (window.selectedTownId) {
|
||||||
window.renderBuildQueuePreview();
|
window.renderBuildQueuePreview();
|
||||||
window.renderBuildingDropdown();
|
window.renderBuildingDropdown();
|
||||||
@@ -45,7 +50,9 @@ 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);
|
||||||
const cmds = await res.json();
|
const cmds = await res.json();
|
||||||
|
window.cmds = cmds; // Save globally so viewer can see reserved resources
|
||||||
window.renderLog(cmds);
|
window.renderLog(cmds);
|
||||||
|
if (window.selectedTownId) window.renderTownDetails();
|
||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -22,8 +22,10 @@ window.renderLog = function(cmds) {
|
|||||||
const statusClass = `status-${cmd.status}`;
|
const statusClass = `status-${cmd.status}`;
|
||||||
const cancelBtn = `<button class="btn btn-danger btn-sm" onclick="cancelCommand(${cmd.id})">✕</button>`;
|
const cancelBtn = `<button class="btn btn-danger btn-sm" onclick="cancelCommand(${cmd.id})">✕</button>`;
|
||||||
|
|
||||||
|
const timeStr = new Date(cmd.created_at + 'Z').toLocaleTimeString([], { hour: '2-digit', minute: '2-digit', second: '2-digit' });
|
||||||
|
|
||||||
return `<tr>
|
return `<tr>
|
||||||
<td style="color:#888;font-size:0.7rem">#${cmd.id}</td>
|
<td style="color:#888;font-size:0.75rem">#${cmd.id}<br><span style="font-size:0.65rem;color:#555;">${timeStr}</span></td>
|
||||||
<td>${cmd.town_name || cmd.town_id}</td>
|
<td>${cmd.town_name || cmd.town_id}</td>
|
||||||
<td>${desc}</td>
|
<td>${desc}</td>
|
||||||
<td><span class="status-badge ${statusClass}">${cmd.status}</span></td>
|
<td><span class="status-badge ${statusClass}">${cmd.status}</span></td>
|
||||||
|
|||||||
@@ -111,13 +111,48 @@ window.renderTownDetails = function() {
|
|||||||
return 'color: #fff;';
|
return 'color: #fff;';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Calculate resources reserved in the bot's pending queues
|
||||||
|
let reservedWood = 0, reservedStone = 0, reservedIron = 0;
|
||||||
|
if (window.cmds) {
|
||||||
|
window.cmds.forEach(cmd => {
|
||||||
|
if ((cmd.status === 'pending' || cmd.status === 'executing') && cmd.town_id == t.town_id) {
|
||||||
|
if (cmd.type === 'build' && t.build_data) {
|
||||||
|
const p = typeof cmd.payload === 'string' ? JSON.parse(cmd.payload) : cmd.payload;
|
||||||
|
const cost = t.build_data[p.building_id];
|
||||||
|
if (cost) {
|
||||||
|
reservedWood += cost.wood || 0;
|
||||||
|
reservedStone += cost.stone || 0;
|
||||||
|
reservedIron += cost.iron || 0;
|
||||||
|
}
|
||||||
|
} else if (cmd.type === 'recruit' && t.unit_data) {
|
||||||
|
const p = typeof cmd.payload === 'string' ? JSON.parse(cmd.payload) : cmd.payload;
|
||||||
|
const amt = parseInt(p.amount) || 1;
|
||||||
|
const cost = t.unit_data[p.unit_id];
|
||||||
|
if (cost) {
|
||||||
|
reservedWood += (cost.wood || 0) * amt;
|
||||||
|
reservedStone += (cost.stone || 0) * amt;
|
||||||
|
reservedIron += (cost.iron || 0) * amt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const resHtml = (actual, reserved) => {
|
||||||
|
let base = `<strong style="${getCol(actual)}">${window.fmt(actual)}</strong>`;
|
||||||
|
if (reserved > 0) {
|
||||||
|
base += ` <span style="color:#ff6666; font-size:0.75rem;">(-${window.fmt(reserved)})</span>`;
|
||||||
|
}
|
||||||
|
return base;
|
||||||
|
};
|
||||||
|
|
||||||
const capFmt = (t.resources.storage != null && t.resources.storage !== '') ? window.fmt(t.resources.storage) : '?';
|
const capFmt = (t.resources.storage != null && t.resources.storage !== '') ? window.fmt(t.resources.storage) : '?';
|
||||||
|
|
||||||
document.getElementById('td-resources').innerHTML = `
|
document.getElementById('td-resources').innerHTML = `
|
||||||
<div style="font-size:0.75rem; color:#aaa; margin-bottom: 4px;">Χωρητικότητα: <strong style="color:#eee">${capFmt}</strong></div>
|
<div style="font-size:0.75rem; color:#aaa; margin-bottom: 4px;">Χωρητικότητα: <strong style="color:#eee">${capFmt}</strong></div>
|
||||||
<div>${window.RES_ICONS.wood} Ξύλο: <strong style="${getCol(t.resources.wood)}">${window.fmt(t.resources.wood)}</strong></div>
|
<div>${window.RES_ICONS.wood} Ξύλο: ${resHtml(t.resources.wood, reservedWood)}</div>
|
||||||
<div>${window.RES_ICONS.stone} Πέτρα: <strong style="${getCol(t.resources.stone)}">${window.fmt(t.resources.stone)}</strong></div>
|
<div>${window.RES_ICONS.stone} Πέτρα: ${resHtml(t.resources.stone, reservedStone)}</div>
|
||||||
<div>${window.RES_ICONS.iron} Ασήμι: <strong style="${getCol(t.resources.iron)}">${window.fmt(t.resources.iron)}</strong></div>
|
<div>${window.RES_ICONS.iron} Ασήμι: ${resHtml(t.resources.iron, reservedIron)}</div>
|
||||||
<div style="margin-top: 6px;">${window.RES_ICONS.pop} Πληθυσμός: <strong style="color:#fff">${t.resources.population}</strong></div>
|
<div style="margin-top: 6px;">${window.RES_ICONS.pop} Πληθυσμός: <strong style="color:#fff">${t.resources.population}</strong></div>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
|||||||
@@ -69,19 +69,27 @@
|
|||||||
<a href="/player/{{ p.player_id }}" class="player-card">
|
<a href="/player/{{ p.player_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> <span>(ID: {{ p.player_id }})</span>
|
<strong>{{ p.player }}</strong> <span style="color: #6fcfcf;">[{{ p.world_id }}]</span> <span>(ID: {{ p.player_id }})</span>
|
||||||
|
</div>
|
||||||
|
<div style="display: flex; gap: 8px;">
|
||||||
|
{% if p.captcha_active %}
|
||||||
|
<span style="display: flex; align-items: center; gap: 6px; background: rgba(255, 100, 100, 0.2); padding: 5px 12px; border-radius: 20px; font-size: 0.8rem; color: #ff6464; font-weight: bold; border: 1px solid rgba(255, 100, 100, 0.5); box-shadow: 0 0 8px rgba(255, 100, 100, 0.4);">
|
||||||
|
⚠️ Captcha
|
||||||
|
</span>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if p.is_online %}
|
||||||
|
<span style="display: flex; align-items: center; gap: 6px; background: rgba(50, 150, 50, 0.2); padding: 5px 12px; border-radius: 20px; font-size: 0.8rem; color: #7bcc7b; font-weight: bold; border: 1px solid rgba(123, 204, 123, 0.3);">
|
||||||
|
<span style="display: inline-block; width: 8px; height: 8px; background: #7bcc7b; border-radius: 50%; box-shadow: 0 0 6px #7bcc7b;"></span>
|
||||||
|
Online
|
||||||
|
</span>
|
||||||
|
{% else %}
|
||||||
|
<span style="display: flex; align-items: center; gap: 6px; background: rgba(150, 50, 50, 0.2); padding: 5px 12px; border-radius: 20px; font-size: 0.8rem; color: #cc7b7b; font-weight: bold; border: 1px solid rgba(204, 123, 123, 0.3);">
|
||||||
|
<span style="display: inline-block; width: 8px; height: 8px; background: #cc7b7b; border-radius: 50%;"></span>
|
||||||
|
Offline
|
||||||
|
</span>
|
||||||
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
{% if p.is_online %}
|
|
||||||
<span style="display: flex; align-items: center; gap: 6px; background: rgba(50, 150, 50, 0.2); padding: 5px 12px; border-radius: 20px; font-size: 0.8rem; color: #7bcc7b; font-weight: bold; border: 1px solid rgba(123, 204, 123, 0.3);">
|
|
||||||
<span style="display: inline-block; width: 8px; height: 8px; background: #7bcc7b; border-radius: 50%; box-shadow: 0 0 6px #7bcc7b;"></span>
|
|
||||||
Online
|
|
||||||
</span>
|
|
||||||
{% else %}
|
|
||||||
<span style="display: flex; align-items: center; gap: 6px; background: rgba(150, 50, 50, 0.2); padding: 5px 12px; border-radius: 20px; font-size: 0.8rem; color: #cc7b7b; font-weight: bold; border: 1px solid rgba(204, 123, 123, 0.3);">
|
|
||||||
<span style="display: inline-block; width: 8px; height: 8px; background: #cc7b7b; border-radius: 50%;"></span>
|
|
||||||
Offline
|
|
||||||
</span>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
</div>
|
||||||
</a>
|
</a>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|||||||
Reference in New Issue
Block a user