5.2 KiB
Future Ideas & Optimizations
This document tracks potential architectural improvements and features inspired by other Grepolis alliance coordination scripts (like GrepoData and Noct).
1. Timestamp-Based Polling Optimization (since parameter)
Inspired by: noct-api.grepo-soft.workers.dev
Current State: The Tampermonkey client polls the server every 8-18 seconds and receives the full state/command payload every time, even if nothing has changed.
Proposed Implementation:
- Add a
sincetimestamp parameter to the client's poll requests. - The server checks if any commands or state updates have occurred after the
sincetimestamp. - If no new data: The server returns an empty
HTTP 204 No Contentresponse. - If new data: The server returns
HTTP 200 OKwith only the data that changed.
Benefits:
- Drastically reduces server bandwidth and CPU load.
- Minimizes the size of network requests on the client side, making the script stealthier and less resource-intensive in the browser.
Concrete Code Example (How Noct does it):
// 1. Client-side polling logic
let lastFetchTime = Date.now();
async function pollCommands() {
const url = `https://noct-api.grepo-soft.workers.dev/api/alliance/commands` +
`?alliance=p0PmzsZMo4xZ2o29uvqggy5d` +
`&world=gr118` +
`&clientId=848938473` +
`&since=${lastFetchTime}`; // Ask only for things after this timestamp
const response = await fetch(url);
// 2. Server returns HTTP 204 (No Content) if nothing new happened
if (response.status === 204) {
return; // Empty payload, exit early
}
// 3. Server returns HTTP 200 (OK) only if there are NEW commands
if (response.status === 200) {
const data = await response.json();
// Process new commands...
executeCommands(data.commands);
// Update the timestamp so the next poll only asks for things after this moment
lastFetchTime = Date.now();
}
}
# Backend (Python/Flask Equivalent)
@app.route('/api/alliance/commands')
def get_commands():
# Get the timestamp from the URL query
since_ts = int(request.args.get('since', 0))
# Query DB for commands created AFTER the 'since' timestamp
new_commands = db.execute(
"SELECT * FROM commands WHERE created_at_ts > ?", (since_ts,)
).fetchall()
if not new_commands:
# Return empty body with 204 No Content
return '', 204
return jsonify({"commands": new_commands}), 200
2. WebSocket Architecture for Real-Time Synchronization
Inspired by: grepodata.com (ReactPHP WebSocket server)
Current State: Command delivery relies on HTTP polling. If an attack plan requires a launch in 30 seconds, but the client is on an 18-second polling interval, there is a high risk of missing the execution window or executing late.
Proposed Implementation:
- Integrate
Flask-SocketIO(or a standalone async WebSocket server) into the backend. - The client script establishes a persistent
wss://connection upon loading the game. - The client authenticates using its
clan_keyand subscribes to its alliance "topic/room". - When an admin arms an attack plan or a player updates their town state, the server instantly pushes the payload to all connected alliance members.
Benefits:
- Zero Polling Latency: Commands arrive in ~100ms instead of 8-18 seconds.
- Perfect Attack Timing: Ensures clients receive armed plans immediately, maximizing the margin for precise execution.
- Instant UI Updates: The dashboard and attack planner can update in real-time as members come online or troop counts change.
3. Server-Sent Events (SSE) Lag/Refresh Bug Fix
Issue:
When refreshing or hitting the "back" button on the Live Tracker (tracker.html), the page occasionally hangs, lags heavily, or completely fails to load.
Root Cause:
The Live Tracker uses SSE (EventSource) to receive real-time movement updates. Modern browsers strictly limit simultaneous HTTP/1.1 connections to the same server (usually 6 maximum). When the user navigates away or refreshes, the browser drops the frontend page, but the Python/Flask backend (tracker.py) does not immediately detect the broken pipe and keeps the socket open, waiting to send data.
If the user hits refresh multiple times, these "ghost" connections stack up. Upon reaching 6 ghost connections, the browser refuses to load any further requests until the old connections naturally time out (which can take 30+ seconds).
Proposed Fix:
- Client-side (
tracker.html): Ensure the browser explicitly tells the server the connection is closing exactly as the page unloads.
window.addEventListener('beforeunload', () => {
if (typeof es !== 'undefined' && es !== null) {
es.close();
}
});
- Server-side (
tracker.py): Ensure the generator handles client disconnects gracefully and immediately cleans up the subscriber queue without waiting for a timeout.
# Make sure the generator yields spaces/heartbeats actively so the OS
# throws an IOError/GeneratorExit the moment the client drops.