This commit is contained in:
2026-04-20 23:43:01 +03:00
parent 9ff4398b14
commit aa3a5463c7
2 changed files with 119 additions and 45 deletions

View File

@@ -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;
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}` };
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 type: ${cmd.type}` };
} catch (e) {
result = { ok: false, msg: `Exception: ${e.message}` };
}
} catch (e) {
result = { ok: false, msg: `Exception: ${e.message}` };
}
const finalStatus = result.requeue ? 'pending' : (result.ok ? 'done' : 'failed');
log(`Command #${cmd.id}: ${finalStatus === 'done' ? '✅' : finalStatus === 'pending' ? '⏳' : '❌'} ${result.msg}`);
reportResult(cmd.id, finalStatus, result.msg);
};
const finalStatus = result.requeue ? 'pending' : (result.ok ? 'done' : 'failed');
log(`Command #${cmd.id} result: ${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 4590 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 818 seconds (randomized jitter)
jitterLoop(pollAndExecute, 8000, 18000);
// Wire up game event observers after a short delay to ensure game is ready
setTimeout(setupObservers, 6000);
});
})();