fixes
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
// ==UserScript==
|
||||
// @name Grepolis Remote Control
|
||||
// @namespace http://tampermonkey.net/
|
||||
// @version 2.1
|
||||
// @version 2.3
|
||||
// @description Polls grepo.haunter-pets.top for remote commands and executes them in-game
|
||||
// @author Dimitrios
|
||||
// @match https://*.grepolis.com/game/*
|
||||
@@ -15,8 +15,22 @@
|
||||
|
||||
const uw = typeof unsafeWindow !== 'undefined' ? unsafeWindow : window;
|
||||
const BASE_URL = 'https://grepo.haunter-pets.top';
|
||||
const POLL_INTERVAL_MS = 5000; // poll for commands
|
||||
const STATE_INTERVAL_MS = 30000; // push town state
|
||||
|
||||
// ---- Jitter helpers -----------------------------------------------
|
||||
// Returns a random integer between min and max (inclusive)
|
||||
function randInt(min, max) {
|
||||
return Math.floor(Math.random() * (max - min + 1)) + min;
|
||||
}
|
||||
// Schedules fn to run after a random ms delay, then reschedules itself
|
||||
function jitterLoop(fn, minMs, maxMs) {
|
||||
function schedule() {
|
||||
setTimeout(async () => {
|
||||
await fn();
|
||||
schedule(); // reschedule with a NEW random delay every time
|
||||
}, randInt(minMs, maxMs));
|
||||
}
|
||||
schedule();
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// Toolbar indicator button
|
||||
@@ -303,7 +317,11 @@
|
||||
log(`Resource check skipped: ${e}`);
|
||||
}
|
||||
|
||||
// Fire the build request
|
||||
// Fire the build request — with a human-like reaction delay
|
||||
const reactionMs = randInt(800, 2500);
|
||||
log(`Waiting ${reactionMs}ms before firing buildUp (reaction time)...`);
|
||||
await sleep(reactionMs);
|
||||
|
||||
uw.gpAjax.ajaxPost('frontend_bridge', 'execute', {
|
||||
model_url: 'BuildingOrder',
|
||||
action_name: 'buildUp',
|
||||
@@ -334,6 +352,11 @@
|
||||
];
|
||||
const endpoint = navalUnits.includes(unit_id) ? 'building_docks' : 'building_barracks';
|
||||
|
||||
// Fire the recruit request — with a human-like reaction delay
|
||||
const reactionMs = randInt(800, 2500);
|
||||
log(`Waiting ${reactionMs}ms before firing recruit (reaction time)...`);
|
||||
await sleep(reactionMs);
|
||||
|
||||
uw.gpAjax.ajaxPost(endpoint, 'build', {
|
||||
unit_id,
|
||||
amount: parseInt(amount) || 1,
|
||||
@@ -345,7 +368,7 @@
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// Poll for and execute a pending command
|
||||
// Poll for and execute pending commands (build + recruit in parallel)
|
||||
// ----------------------------------------------------------------
|
||||
async function pollAndExecute() {
|
||||
if (paused) return;
|
||||
@@ -359,41 +382,86 @@
|
||||
return;
|
||||
}
|
||||
|
||||
const cmd = cmdData.command;
|
||||
if (!cmd) return; // nothing pending
|
||||
// Build queue and Recruit queue are now independent
|
||||
const buildCmd = cmdData.build;
|
||||
const recruitCmd = cmdData.recruit;
|
||||
|
||||
const execute = async (cmd) => {
|
||||
if (!cmd) return;
|
||||
log(`Executing command #${cmd.id} — type:${cmd.type} town:${cmd.town_id}`);
|
||||
|
||||
let result;
|
||||
try {
|
||||
if (cmd.type === 'build') {
|
||||
result = await executeBuild(cmd);
|
||||
} else if (cmd.type === 'recruit') {
|
||||
result = await executeRecruit(cmd);
|
||||
} else {
|
||||
result = { ok: false, msg: `Unknown command type: ${cmd.type}` };
|
||||
}
|
||||
if (cmd.type === 'build') result = await executeBuild(cmd);
|
||||
else if (cmd.type === 'recruit') result = await executeRecruit(cmd);
|
||||
else result = { ok: false, msg: `Unknown type: ${cmd.type}` };
|
||||
} catch (e) {
|
||||
result = { ok: false, msg: `Exception: ${e.message}` };
|
||||
}
|
||||
|
||||
const finalStatus = result.requeue ? 'pending' : (result.ok ? 'done' : 'failed');
|
||||
log(`Command #${cmd.id} result: ${finalStatus === 'done' ? '✅' : (finalStatus === 'pending' ? '⏳' : '❌')} ${result.msg}`);
|
||||
log(`Command #${cmd.id}: ${finalStatus === 'done' ? '✅' : finalStatus === 'pending' ? '⏳' : '❌'} ${result.msg}`);
|
||||
reportResult(cmd.id, finalStatus, result.msg);
|
||||
};
|
||||
|
||||
// Run both queues concurrently — they do NOT block each other
|
||||
await Promise.all([execute(buildCmd), execute(recruitCmd)]);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// Observers — instant triggers on meaningful game events
|
||||
// ----------------------------------------------------------------
|
||||
function setupObservers() {
|
||||
try {
|
||||
// 1. Town switch — instant dashboard sync when you click a different town
|
||||
uw.$.Observer(uw.GameEvents.town.town_switch).subscribe(() => {
|
||||
log('Observer: town switched — pushing state');
|
||||
pushState();
|
||||
});
|
||||
} catch(e) { log(`Observer town_switch failed: ${e}`); }
|
||||
|
||||
try {
|
||||
// 2. Building finished — push state AND immediately try the next build command
|
||||
uw.$.Observer(uw.GameEvents.town.building.order_completed).subscribe(() => {
|
||||
log('Observer: building completed — pushing state + polling');
|
||||
pushState();
|
||||
pollAndExecute();
|
||||
});
|
||||
} catch(e) { log(`Observer building.order_completed failed: ${e}`); }
|
||||
|
||||
try {
|
||||
// 3. Troops finished training — same logic for the recruit queue
|
||||
uw.$.Observer(uw.GameEvents.town.unit.order_completed).subscribe(() => {
|
||||
log('Observer: unit order completed — pushing state + polling');
|
||||
pushState();
|
||||
pollAndExecute();
|
||||
});
|
||||
} catch(e) { log(`Observer unit.order_completed failed: ${e}`); }
|
||||
|
||||
try {
|
||||
// 4. Points changed — fires when a building finishes (extra safety net)
|
||||
uw.$.Observer(uw.GameEvents.player.points.changed).subscribe(() => {
|
||||
log('Observer: player points changed — pushing state');
|
||||
pushState();
|
||||
});
|
||||
} catch(e) { log(`Observer player.points.changed failed: ${e}`); }
|
||||
|
||||
log('Observers registered successfully');
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// Boot
|
||||
// ----------------------------------------------------------------
|
||||
window.addEventListener('load', () => {
|
||||
log('Grepolis Remote Control loaded');
|
||||
log('Grepolis Remote Control v2.3 loaded');
|
||||
|
||||
// Push state immediately, then on interval
|
||||
// Push state once after load, then every 45–90 seconds (randomized)
|
||||
setTimeout(pushState, 5000);
|
||||
setInterval(pushState, STATE_INTERVAL_MS);
|
||||
jitterLoop(pushState, 45000, 90000);
|
||||
|
||||
// Poll for commands
|
||||
setInterval(pollAndExecute, POLL_INTERVAL_MS);
|
||||
// Poll for commands every 8–18 seconds (randomized jitter)
|
||||
jitterLoop(pollAndExecute, 8000, 18000);
|
||||
|
||||
// Wire up game event observers after a short delay to ensure game is ready
|
||||
setTimeout(setupObservers, 6000);
|
||||
});
|
||||
|
||||
})();
|
||||
|
||||
@@ -62,38 +62,44 @@ def receive_state():
|
||||
# ------------------------------------------------------------------
|
||||
# GET /api/commands/pending
|
||||
# Tampermonkey polls this to get the next command to execute.
|
||||
# Returns ONE command at a time, marks it as 'executing'.
|
||||
# Returns one 'build' AND one 'recruit' command independently,
|
||||
# so both queues are served in parallel without blocking each other.
|
||||
# ------------------------------------------------------------------
|
||||
@api.route('/api/commands/pending', methods=['GET'])
|
||||
def get_pending_command():
|
||||
conn = get_db()
|
||||
c = conn.cursor()
|
||||
def _fetch_pending_of_type(c, cmd_type):
|
||||
row = c.execute('''
|
||||
SELECT * FROM commands
|
||||
WHERE status = 'pending'
|
||||
WHERE status = 'pending' AND type = ?
|
||||
ORDER BY id ASC
|
||||
LIMIT 1
|
||||
''').fetchone()
|
||||
|
||||
''', (cmd_type,)).fetchone()
|
||||
if not row:
|
||||
conn.close()
|
||||
return jsonify({'command': None})
|
||||
|
||||
return None
|
||||
c.execute('''
|
||||
UPDATE commands
|
||||
SET status = 'executing', updated_at = ?
|
||||
WHERE id = ?
|
||||
''', (datetime.utcnow().isoformat(), row['id']))
|
||||
conn.commit()
|
||||
conn.close()
|
||||
|
||||
return jsonify({
|
||||
'command': {
|
||||
return {
|
||||
'id': row['id'],
|
||||
'town_id': row['town_id'],
|
||||
'type': row['type'],
|
||||
'payload': json.loads(row['payload'])
|
||||
}
|
||||
|
||||
@api.route('/api/commands/pending', methods=['GET'])
|
||||
def get_pending_command():
|
||||
conn = get_db()
|
||||
c = conn.cursor()
|
||||
|
||||
build_cmd = _fetch_pending_of_type(c, 'build')
|
||||
recruit_cmd = _fetch_pending_of_type(c, 'recruit')
|
||||
|
||||
conn.commit()
|
||||
conn.close()
|
||||
|
||||
return jsonify({
|
||||
'build': build_cmd,
|
||||
'recruit': recruit_cmd
|
||||
})
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user