cleanup
This commit is contained in:
parent
3d7a8aafdf
commit
c1892230b7
13 changed files with 1111 additions and 993 deletions
295
src/server/routes/debug.ts
Normal file
295
src/server/routes/debug.ts
Normal file
|
|
@ -0,0 +1,295 @@
|
|||
import { Router } from 'express';
|
||||
import { mkdir } from 'fs/promises';
|
||||
import { logger } from '../../util/logger.js';
|
||||
import { sleep } from '../../util/sleep.js';
|
||||
import { GRID_LAYOUTS } from '../../game/GridReader.js';
|
||||
import type { Bot } from '../../bot/Bot.js';
|
||||
import type { Server } from '../Server.js';
|
||||
import type { OcrEngine, OcrPreprocess } from '../../game/OcrDaemon.js';
|
||||
|
||||
export function debugRoutes(bot: Bot, server: Server): Router {
|
||||
const router = Router();
|
||||
|
||||
const notReady = (_req: any, res: any): boolean => {
|
||||
if (!bot.isReady) { res.status(503).json({ error: 'Not ready' }); return true; }
|
||||
return false;
|
||||
};
|
||||
|
||||
// --- Sync: OCR engine/preprocess selection ---
|
||||
|
||||
router.get('/ocr-engine', (req, res) => {
|
||||
if (notReady(req, res)) return;
|
||||
res.json({ ok: true, engine: bot.screenReader.debugOcrEngine });
|
||||
});
|
||||
|
||||
router.post('/ocr-engine', (req, res) => {
|
||||
if (notReady(req, res)) return;
|
||||
const { engine } = req.body as { engine: string };
|
||||
if (!['tesseract', 'easyocr', 'paddleocr'].includes(engine)) {
|
||||
res.status(400).json({ error: 'Invalid engine. Must be tesseract, easyocr, or paddleocr.' });
|
||||
return;
|
||||
}
|
||||
bot.screenReader.debugOcrEngine = engine as OcrEngine;
|
||||
server.broadcastLog('info', `OCR engine set to: ${engine}`);
|
||||
res.json({ ok: true });
|
||||
});
|
||||
|
||||
router.get('/ocr-preprocess', (req, res) => {
|
||||
if (notReady(req, res)) return;
|
||||
res.json({ ok: true, preprocess: bot.screenReader.debugPreprocess });
|
||||
});
|
||||
|
||||
router.post('/ocr-preprocess', (req, res) => {
|
||||
if (notReady(req, res)) return;
|
||||
const { preprocess } = req.body as { preprocess: string };
|
||||
if (!['none', 'bgsub', 'tophat'].includes(preprocess)) {
|
||||
res.status(400).json({ error: 'Invalid preprocess. Must be none, bgsub, or tophat.' });
|
||||
return;
|
||||
}
|
||||
bot.screenReader.debugPreprocess = preprocess as OcrPreprocess;
|
||||
server.broadcastLog('info', `OCR preprocess set to: ${preprocess}`);
|
||||
res.json({ ok: true });
|
||||
});
|
||||
|
||||
// --- Fire-and-forget: slow debug operations ---
|
||||
|
||||
router.post('/screenshot', (req, res) => {
|
||||
if (notReady(req, res)) return;
|
||||
res.json({ ok: true });
|
||||
bot.screenReader.saveDebugScreenshots('debug-screenshots').then(files => {
|
||||
server.broadcastLog('info', `Debug screenshots saved: ${files.map(f => f.split(/[\\/]/).pop()).join(', ')}`);
|
||||
server.broadcastDebug('screenshot', { files });
|
||||
}).catch(err => {
|
||||
logger.error({ err }, 'Debug screenshot failed');
|
||||
server.broadcastDebug('screenshot', { error: 'Screenshot failed' });
|
||||
});
|
||||
});
|
||||
|
||||
router.post('/ocr', (req, res) => {
|
||||
if (notReady(req, res)) return;
|
||||
res.json({ ok: true });
|
||||
bot.screenReader.debugReadFullScreen().then(text => {
|
||||
server.broadcastLog('info', `OCR [${bot.screenReader.debugOcrEngine}] (${text.length} chars): ${text.substring(0, 200)}`);
|
||||
server.broadcastDebug('ocr', { text });
|
||||
}).catch(err => {
|
||||
logger.error({ err }, 'Debug OCR failed');
|
||||
server.broadcastDebug('ocr', { error: 'OCR failed' });
|
||||
});
|
||||
});
|
||||
|
||||
router.post('/find-text', (req, res) => {
|
||||
if (notReady(req, res)) return;
|
||||
const { text } = req.body as { text: string };
|
||||
if (!text) { res.status(400).json({ error: 'Missing text parameter' }); return; }
|
||||
res.json({ ok: true });
|
||||
bot.screenReader.debugFindTextOnScreen(text).then(pos => {
|
||||
if (pos) {
|
||||
server.broadcastLog('info', `Found "${text}" at (${pos.x}, ${pos.y}) [${bot.screenReader.debugOcrEngine}]`);
|
||||
} else {
|
||||
server.broadcastLog('warn', `"${text}" not found on screen [${bot.screenReader.debugOcrEngine}]`);
|
||||
}
|
||||
server.broadcastDebug('find-text', { searchText: text, found: !!pos, position: pos });
|
||||
}).catch(err => {
|
||||
logger.error({ err }, 'Debug find-text failed');
|
||||
server.broadcastDebug('find-text', { searchText: text, error: 'Find text failed' });
|
||||
});
|
||||
});
|
||||
|
||||
router.post('/find-and-click', (req, res) => {
|
||||
if (notReady(req, res)) return;
|
||||
const { text, fuzzy } = req.body as { text: string; fuzzy?: boolean };
|
||||
if (!text) { res.status(400).json({ error: 'Missing text parameter' }); return; }
|
||||
res.json({ ok: true });
|
||||
(async () => {
|
||||
const pos = await bot.screenReader.debugFindTextOnScreen(text, !!fuzzy);
|
||||
if (pos) {
|
||||
await bot.gameController.focusGame();
|
||||
await bot.gameController.leftClickAt(pos.x, pos.y);
|
||||
server.broadcastLog('info', `Found "${text}" at (${pos.x}, ${pos.y}) and clicked [${bot.screenReader.debugOcrEngine}]`);
|
||||
server.broadcastDebug('find-and-click', { searchText: text, found: true, position: pos });
|
||||
} else {
|
||||
server.broadcastLog('warn', `"${text}" not found on screen [${bot.screenReader.debugOcrEngine}]`);
|
||||
server.broadcastDebug('find-and-click', { searchText: text, found: false, position: null });
|
||||
}
|
||||
})().catch(err => {
|
||||
logger.error({ err }, 'Debug find-and-click failed');
|
||||
server.broadcastDebug('find-and-click', { searchText: text, error: 'Find and click failed' });
|
||||
});
|
||||
});
|
||||
|
||||
router.post('/click', (req, res) => {
|
||||
if (notReady(req, res)) return;
|
||||
const { x, y } = req.body as { x: number; y: number };
|
||||
if (x == null || y == null) { res.status(400).json({ error: 'Missing x/y' }); return; }
|
||||
res.json({ ok: true });
|
||||
(async () => {
|
||||
await bot.gameController.focusGame();
|
||||
await bot.gameController.leftClickAt(x, y);
|
||||
server.broadcastLog('info', `Clicked at (${x}, ${y})`);
|
||||
server.broadcastDebug('click', { x, y });
|
||||
})().catch(err => {
|
||||
logger.error({ err }, 'Debug click failed');
|
||||
server.broadcastDebug('click', { x, y, error: 'Click failed' });
|
||||
});
|
||||
});
|
||||
|
||||
router.post('/hideout', (req, res) => {
|
||||
if (notReady(req, res)) return;
|
||||
res.json({ ok: true });
|
||||
(async () => {
|
||||
await bot.gameController.focusGame();
|
||||
await bot.gameController.goToHideout();
|
||||
server.broadcastLog('info', 'Sent /hideout command');
|
||||
server.broadcastDebug('hideout', {});
|
||||
})().catch(err => {
|
||||
logger.error({ err }, 'Debug hideout failed');
|
||||
server.broadcastDebug('hideout', { error: 'Hideout command failed' });
|
||||
});
|
||||
});
|
||||
|
||||
router.post('/click-then-click', (req, res) => {
|
||||
if (notReady(req, res)) return;
|
||||
const { first, second, timeout = 5000 } = req.body as { first: string; second: string; timeout?: number };
|
||||
if (!first || !second) { res.status(400).json({ error: 'Missing first/second' }); return; }
|
||||
res.json({ ok: true });
|
||||
(async () => {
|
||||
const pos1 = await bot.screenReader.findTextOnScreen(first);
|
||||
if (!pos1) {
|
||||
server.broadcastLog('warn', `"${first}" not found on screen`);
|
||||
server.broadcastDebug('click-then-click', { first, second, found: false, step: 'first' });
|
||||
return;
|
||||
}
|
||||
await bot.gameController.focusGame();
|
||||
await bot.gameController.leftClickAt(pos1.x, pos1.y);
|
||||
server.broadcastLog('info', `Clicked "${first}" at (${pos1.x}, ${pos1.y}), waiting for "${second}"...`);
|
||||
|
||||
const deadline = Date.now() + timeout;
|
||||
while (Date.now() < deadline) {
|
||||
const pos2 = await bot.screenReader.findTextOnScreen(second);
|
||||
if (pos2) {
|
||||
await bot.gameController.leftClickAt(pos2.x, pos2.y);
|
||||
server.broadcastLog('info', `Clicked "${second}" at (${pos2.x}, ${pos2.y})`);
|
||||
server.broadcastDebug('click-then-click', { first, second, found: true, position: pos2 });
|
||||
return;
|
||||
}
|
||||
}
|
||||
server.broadcastLog('warn', `"${second}" not found after clicking "${first}" (timed out)`);
|
||||
server.broadcastDebug('click-then-click', { first, second, found: false, step: 'second' });
|
||||
})().catch(err => {
|
||||
logger.error({ err }, 'Debug click-then-click failed');
|
||||
server.broadcastDebug('click-then-click', { first, second, error: 'Click-then-click failed' });
|
||||
});
|
||||
});
|
||||
|
||||
router.post('/grid-scan', (req, res) => {
|
||||
if (notReady(req, res)) return;
|
||||
const { layout: layoutName, targetRow, targetCol } = req.body as { layout: string; targetRow?: number; targetCol?: number };
|
||||
if (!GRID_LAYOUTS[layoutName]) { res.status(400).json({ error: `Unknown grid layout: ${layoutName}` }); return; }
|
||||
res.json({ ok: true });
|
||||
(async () => {
|
||||
const result = await bot.screenReader.grid.scan(layoutName, undefined, targetRow, targetCol);
|
||||
const imageBuffer = await bot.screenReader.captureRegion(result.layout.region);
|
||||
const imageBase64 = imageBuffer.toString('base64');
|
||||
const r = result.layout.region;
|
||||
const matchInfo = result.matches ? `, ${result.matches.length} matches` : '';
|
||||
server.broadcastLog('info',
|
||||
`Grid scan (${layoutName}): ${result.layout.cols}x${result.layout.rows} at (${r.x},${r.y}) ${r.width}x${r.height} — ${result.occupied.length} occupied cells${matchInfo}`);
|
||||
server.broadcastDebug('grid-scan', {
|
||||
layout: layoutName,
|
||||
occupied: result.occupied,
|
||||
items: result.items,
|
||||
matches: result.matches,
|
||||
cols: result.layout.cols,
|
||||
rows: result.layout.rows,
|
||||
image: imageBase64,
|
||||
region: result.layout.region,
|
||||
targetRow,
|
||||
targetCol,
|
||||
});
|
||||
})().catch(err => {
|
||||
logger.error({ err }, 'Debug grid-scan failed');
|
||||
server.broadcastDebug('grid-scan', { layout: layoutName, error: 'Grid scan failed' });
|
||||
});
|
||||
});
|
||||
|
||||
router.post('/test-match-hover', (req, res) => {
|
||||
if (notReady(req, res)) return;
|
||||
const { layout: layoutName, targetRow, targetCol } = req.body as { layout: string; targetRow: number; targetCol: number };
|
||||
if (!GRID_LAYOUTS[layoutName]) { res.status(400).json({ error: `Unknown layout: ${layoutName}` }); return; }
|
||||
if (targetRow == null || targetCol == null) { res.status(400).json({ error: 'Missing targetRow/targetCol' }); return; }
|
||||
res.json({ ok: true });
|
||||
(async () => {
|
||||
server.broadcastLog('info', `Scanning ${layoutName} with target (${targetRow},${targetCol})...`);
|
||||
const result = await bot.screenReader.grid.scan(layoutName, undefined, targetRow, targetCol);
|
||||
const matches = result.matches ?? [];
|
||||
const items = result.items ?? [];
|
||||
|
||||
const targetItem = items.find(i => targetRow >= i.row && targetRow < i.row + i.h && targetCol >= i.col && targetCol < i.col + i.w);
|
||||
const itemSize = targetItem ? `${targetItem.w}x${targetItem.h}` : '1x1';
|
||||
server.broadcastLog('info', `Target (${targetRow},${targetCol}) is ${itemSize}, found ${matches.length} matches`);
|
||||
|
||||
const hoverCells = [
|
||||
{ row: targetRow, col: targetCol, label: 'TARGET' },
|
||||
...matches.map(m => ({ row: m.row, col: m.col, label: `MATCH ${(m.similarity * 100).toFixed(0)}%` })),
|
||||
];
|
||||
|
||||
await bot.gameController.focusGame();
|
||||
await mkdir('items', { recursive: true });
|
||||
const tooltips: Array<{ row: number; col: number; label: string; text: string }> = [];
|
||||
const ts = Date.now();
|
||||
const reg = result.layout.region;
|
||||
const cellW = reg.width / result.layout.cols;
|
||||
const cellH = reg.height / result.layout.rows;
|
||||
|
||||
// Move mouse to empty space and take reference snapshot
|
||||
bot.gameController.moveMouseInstant(reg.x + reg.width + 50, reg.y + reg.height / 2);
|
||||
await sleep(50);
|
||||
await bot.screenReader.snapshot();
|
||||
await bot.screenReader.saveScreenshot(`items/${ts}_snapshot.png`);
|
||||
await sleep(200);
|
||||
|
||||
for (const cell of hoverCells) {
|
||||
const cellStart = performance.now();
|
||||
const x = Math.round(reg.x + cell.col * cellW + cellW / 2);
|
||||
const y = Math.round(reg.y + cell.row * cellH + cellH / 2);
|
||||
|
||||
await bot.gameController.moveMouseFast(x, y);
|
||||
await sleep(50);
|
||||
const afterMove = performance.now();
|
||||
|
||||
const imgPath = `items/${ts}_${cell.row}-${cell.col}.png`;
|
||||
const diff = await bot.screenReader.diffOcr(imgPath);
|
||||
const afterOcr = performance.now();
|
||||
const text = diff.text.trim();
|
||||
|
||||
const regionInfo = diff.region ? ` at (${diff.region.x},${diff.region.y}) ${diff.region.width}x${diff.region.height}` : '';
|
||||
tooltips.push({ row: cell.row, col: cell.col, label: cell.label, text });
|
||||
|
||||
server.broadcastLog('info',
|
||||
`${cell.label} (${cell.row},${cell.col}) [move: ${(afterMove - cellStart).toFixed(0)}ms, ocr: ${(afterOcr - afterMove).toFixed(0)}ms, total: ${(afterOcr - cellStart).toFixed(0)}ms]${regionInfo}:`);
|
||||
if (diff.lines.length > 0) {
|
||||
for (const line of diff.lines) {
|
||||
server.broadcastLog('info', ` ${line.text}`);
|
||||
}
|
||||
} else if (text) {
|
||||
for (const line of text.split('\n')) {
|
||||
if (line.trim()) server.broadcastLog('info', ` ${line.trim()}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
server.broadcastLog('info', `Done — hovered ${hoverCells.length} cells, read ${tooltips.filter(t => t.text).length} tooltips`);
|
||||
server.broadcastDebug('test-match-hover', {
|
||||
itemSize,
|
||||
matchCount: matches.length,
|
||||
hoveredCount: hoverCells.length,
|
||||
tooltips,
|
||||
});
|
||||
})().catch(err => {
|
||||
logger.error({ err }, 'Debug test-match-hover failed');
|
||||
server.broadcastDebug('test-match-hover', { error: 'Test match hover failed' });
|
||||
});
|
||||
});
|
||||
|
||||
return router;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue