mirror of
https://github.com/tiennm99/WPlace-AutoBOT.git
synced 2026-10-13 18:15:12 +00:00
Implement off-main-thread coordinate generation using Web Workers with async fallback handling.
This commit is contained in:
1 parent
e4c9fd3f3a
commit
a9e4316e07
1 file changed
+249
-14
+249
-14
@@ -8736,21 +8736,9 @@ function getText(key, params) {
|
||||
|
||||
// Paint pixels until we run out of charges or complete the image
|
||||
try {
|
||||
// First generate coordinates without filtering to count skipped ones
|
||||
const allCoords = generateCoordinates(
|
||||
width,
|
||||
height,
|
||||
state.coordinateMode,
|
||||
state.coordinateDirection,
|
||||
state.coordinateSnake,
|
||||
state.blockWidth,
|
||||
state.blockHeight,
|
||||
0, // Don't filter here
|
||||
0 // Don't filter here
|
||||
);
|
||||
|
||||
// Generate the actual filtered coordinates for painting (resuming from last position)
|
||||
const coords = generateCoordinates(
|
||||
const coords = await generateCoordinatesAsync(
|
||||
width,
|
||||
height,
|
||||
state.coordinateMode,
|
||||
@@ -10611,4 +10599,251 @@ function getText(key, params) {
|
||||
Utils.cleanupTurnstile();
|
||||
});
|
||||
});
|
||||
})();
|
||||
})();
|
||||
|
||||
// === Async coordinate generation (off-main-thread) ===
|
||||
async function generateCoordinatesAsync(
|
||||
width,
|
||||
height,
|
||||
mode,
|
||||
direction,
|
||||
snake,
|
||||
blockWidth,
|
||||
blockHeight,
|
||||
startFromX = 0,
|
||||
startFromY = 0
|
||||
) {
|
||||
try {
|
||||
if (typeof Worker === 'undefined') {
|
||||
// Environment does not support Web Workers – fall back
|
||||
return generateCoordinates(
|
||||
width,
|
||||
height,
|
||||
mode,
|
||||
direction,
|
||||
snake,
|
||||
blockWidth,
|
||||
blockHeight,
|
||||
startFromX,
|
||||
startFromY
|
||||
);
|
||||
}
|
||||
|
||||
const workerCode = `self.onmessage = function(e) {
|
||||
const { width, height, mode, direction, snake, blockWidth, blockHeight, startFromX, startFromY } = e.data || {};
|
||||
|
||||
function generate() {
|
||||
let coords = [];
|
||||
|
||||
// Determine traversal direction
|
||||
let xStart, xEnd, xStep;
|
||||
let yStart, yEnd, yStep;
|
||||
switch (direction) {
|
||||
case 'top-left':
|
||||
xStart = 0; xEnd = width; xStep = 1;
|
||||
yStart = 0; yEnd = height; yStep = 1;
|
||||
break;
|
||||
case 'top-right':
|
||||
xStart = width - 1; xEnd = -1; xStep = -1;
|
||||
yStart = 0; yEnd = height; yStep = 1;
|
||||
break;
|
||||
case 'bottom-left':
|
||||
xStart = 0; xEnd = width; xStep = 1;
|
||||
yStart = height - 1; yEnd = -1; yStep = -1;
|
||||
break;
|
||||
case 'bottom-right':
|
||||
xStart = width - 1; xEnd = -1; xStep = -1;
|
||||
yStart = height - 1; yEnd = -1; yStep = -1;
|
||||
break;
|
||||
default:
|
||||
throw new Error('Unknown direction: ' + direction);
|
||||
}
|
||||
|
||||
if (mode === 'rows') {
|
||||
let rowIndex = 0;
|
||||
for (let y = yStart; y !== yEnd; y += yStep) {
|
||||
if (snake && rowIndex % 2 !== 0) {
|
||||
for (let x = xEnd - xStep; x !== xStart - xStep; x -= xStep) {
|
||||
coords.push([x, y]);
|
||||
}
|
||||
} else {
|
||||
for (let x = xStart; x !== xEnd; x += xStep) {
|
||||
coords.push([x, y]);
|
||||
}
|
||||
}
|
||||
rowIndex++;
|
||||
}
|
||||
} else if (mode === 'columns') {
|
||||
let colIndex = 0;
|
||||
for (let x = xStart; x !== xEnd; x += xStep) {
|
||||
if (snake && colIndex % 2 !== 0) {
|
||||
for (let y = yEnd - yStep; y !== yStart - yStep; y -= yStep) {
|
||||
coords.push([x, y]);
|
||||
}
|
||||
} else {
|
||||
for (let y = yStart; y !== yEnd; y += yStep) {
|
||||
coords.push([x, y]);
|
||||
}
|
||||
}
|
||||
colIndex++;
|
||||
}
|
||||
} else if (mode === 'circle-out') {
|
||||
const cx = Math.floor(width / 2);
|
||||
const cy = Math.floor(height / 2);
|
||||
const maxRadius = Math.ceil(Math.sqrt(cx * cx + cy * cy));
|
||||
for (let r = 0; r <= maxRadius; r++) {
|
||||
for (let y = cy - r; y <= cy + r; y++) {
|
||||
for (let x = cx - r; x <= cx + r; x++) {
|
||||
if (x >= 0 && x < width && y >= 0 && y < height) {
|
||||
const dist = Math.max(Math.abs(x - cx), Math.abs(y - cy));
|
||||
if (dist === r) coords.push([x, y]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (mode === 'circle-in') {
|
||||
const cx = Math.floor(width / 2);
|
||||
const cy = Math.floor(height / 2);
|
||||
const maxRadius = Math.ceil(Math.sqrt(cx * cx + cy * cy));
|
||||
for (let r = maxRadius; r >= 0; r--) {
|
||||
for (let y = cy - r; y <= cy + r; y++) {
|
||||
for (let x = cx - r; x <= cx + r; x++) {
|
||||
if (x >= 0 && x < width && y >= 0 && y < height) {
|
||||
const dist = Math.max(Math.abs(x - cx), Math.abs(y - cy));
|
||||
if (dist === r) coords.push([x, y]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (mode === 'blocks' || mode === 'shuffle-blocks') {
|
||||
const blocks = [];
|
||||
for (let by = 0; by < height; by += blockHeight) {
|
||||
for (let bx = 0; bx < width; bx += blockWidth) {
|
||||
const block = [];
|
||||
for (let y = by; y < Math.min(by + blockHeight, height); y++) {
|
||||
for (let x = bx; x < Math.min(bx + blockWidth, width); x++) {
|
||||
block.push([x, y]);
|
||||
}
|
||||
}
|
||||
blocks.push(block);
|
||||
}
|
||||
}
|
||||
if (mode === 'shuffle-blocks') {
|
||||
for (let i = blocks.length - 1; i > 0; i--) {
|
||||
const j = Math.floor(Math.random() * (i + 1));
|
||||
const tmp = blocks[i];
|
||||
blocks[i] = blocks[j];
|
||||
blocks[j] = tmp;
|
||||
}
|
||||
}
|
||||
for (const block of blocks) {
|
||||
coords = coords.concat(block); // avoid spread on huge arrays
|
||||
}
|
||||
} else {
|
||||
throw new Error('Unknown mode: ' + mode);
|
||||
}
|
||||
|
||||
// Resume from specified position if provided
|
||||
if (startFromX > 0 || startFromY > 0) {
|
||||
let startIndex = -1;
|
||||
for (let i = 0; i < coords.length; i++) {
|
||||
const c = coords[i];
|
||||
if (c[0] === startFromX && c[1] === startFromY) {
|
||||
startIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (startIndex >= 0) {
|
||||
coords = coords.slice(startIndex);
|
||||
}
|
||||
}
|
||||
|
||||
return coords;
|
||||
}
|
||||
|
||||
try {
|
||||
const coords = generate();
|
||||
self.postMessage({ ok: true, coords });
|
||||
} catch (err) {
|
||||
self.postMessage({ ok: false, error: err && (err.message || String(err)) });
|
||||
}
|
||||
};`;
|
||||
|
||||
const blob = new Blob([workerCode], { type: 'application/javascript' });
|
||||
const url = URL.createObjectURL(blob);
|
||||
|
||||
return await new Promise((resolve) => {
|
||||
const worker = new Worker(url);
|
||||
const cleanup = () => {
|
||||
URL.revokeObjectURL(url);
|
||||
try { worker.terminate(); } catch (e) { }
|
||||
};
|
||||
|
||||
worker.onmessage = (e) => {
|
||||
const { ok, coords, error } = e.data || {};
|
||||
cleanup();
|
||||
if (ok && Array.isArray(coords)) {
|
||||
resolve(coords);
|
||||
} else {
|
||||
console.warn('Coordinate worker failed, falling back to sync:', error);
|
||||
resolve(
|
||||
generateCoordinates(
|
||||
width,
|
||||
height,
|
||||
mode,
|
||||
direction,
|
||||
snake,
|
||||
blockWidth,
|
||||
blockHeight,
|
||||
startFromX,
|
||||
startFromY
|
||||
)
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
worker.onerror = (err) => {
|
||||
console.warn('Coordinate worker error, falling back to sync:', err && (err.message || err));
|
||||
cleanup();
|
||||
resolve(
|
||||
generateCoordinates(
|
||||
width,
|
||||
height,
|
||||
mode,
|
||||
direction,
|
||||
snake,
|
||||
blockWidth,
|
||||
blockHeight,
|
||||
startFromX,
|
||||
startFromY
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
worker.postMessage({
|
||||
width,
|
||||
height,
|
||||
mode,
|
||||
direction,
|
||||
snake,
|
||||
blockWidth,
|
||||
blockHeight,
|
||||
startFromX,
|
||||
startFromY,
|
||||
});
|
||||
});
|
||||
} catch (e) {
|
||||
console.warn('Failed to start coordinate worker, using sync generation:', e);
|
||||
return generateCoordinates(
|
||||
width,
|
||||
height,
|
||||
mode,
|
||||
direction,
|
||||
snake,
|
||||
blockWidth,
|
||||
blockHeight,
|
||||
startFromX,
|
||||
startFromY
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user