servername,captca,timestamp,reserve resources
This commit is contained in:
@@ -14,12 +14,16 @@ dashboard = Blueprint('dashboard', __name__)
|
||||
def index():
|
||||
conn = get_db()
|
||||
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
|
||||
WHERE player IS NOT NULL
|
||||
GROUP BY player, player_id
|
||||
ORDER BY player ASC
|
||||
''').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()
|
||||
|
||||
players = []
|
||||
@@ -37,7 +41,9 @@ def index():
|
||||
players.append({
|
||||
'player': r['player'],
|
||||
'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)
|
||||
|
||||
@@ -8,6 +8,11 @@ window.fetchTowns = async function() {
|
||||
window.towns = await res.json();
|
||||
window.renderTowns();
|
||||
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) {
|
||||
window.renderBuildQueuePreview();
|
||||
@@ -45,7 +50,9 @@ window.fetchLog = async function() {
|
||||
try {
|
||||
const res = await fetch('/dashboard/commands?player_id=' + window.PLAYER_ID);
|
||||
const cmds = await res.json();
|
||||
window.cmds = cmds; // Save globally so viewer can see reserved resources
|
||||
window.renderLog(cmds);
|
||||
if (window.selectedTownId) window.renderTownDetails();
|
||||
} catch (e) {}
|
||||
};
|
||||
|
||||
|
||||
@@ -22,8 +22,10 @@ window.renderLog = function(cmds) {
|
||||
const statusClass = `status-${cmd.status}`;
|
||||
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>
|
||||
<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>${desc}</td>
|
||||
<td><span class="status-badge ${statusClass}">${cmd.status}</span></td>
|
||||
|
||||
@@ -111,13 +111,48 @@ window.renderTownDetails = function() {
|
||||
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) : '?';
|
||||
|
||||
document.getElementById('td-resources').innerHTML = `
|
||||
<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.stone} Πέτρα: <strong style="${getCol(t.resources.stone)}">${window.fmt(t.resources.stone)}</strong></div>
|
||||
<div>${window.RES_ICONS.iron} Ασήμι: <strong style="${getCol(t.resources.iron)}">${window.fmt(t.resources.iron)}</strong></div>
|
||||
<div>${window.RES_ICONS.wood} Ξύλο: ${resHtml(t.resources.wood, reservedWood)}</div>
|
||||
<div>${window.RES_ICONS.stone} Πέτρα: ${resHtml(t.resources.stone, reservedStone)}</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>
|
||||
`;
|
||||
|
||||
|
||||
@@ -69,19 +69,27 @@
|
||||
<a href="/player/{{ p.player_id }}" class="player-card">
|
||||
<div style="display: flex; justify-content: space-between; align-items: center;">
|
||||
<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>
|
||||
{% 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>
|
||||
</a>
|
||||
{% endfor %}
|
||||
|
||||
Reference in New Issue
Block a user