refactor(minimap): deduplicate tile parsing and parallelize file cleanup

This commit is contained in:
Danielv123 committed 2026-02-21 17:11:33 +01:00
1 parent 550ea22724
commit 387a9425ce
2 files changed
+113 -117

No files matched your search

+13 -77
View File
@@ -26,6 +26,8 @@ import {
extractChunkFromTile,
renderTileToPixels,
pixelsToRGBA,
parseChunkDataRecord,
parsePixelDataRecord,
parseRecipeTileBinary,
type ParsedRecipeTile,
} from "./utils/tile-utils";
@@ -277,11 +279,8 @@ export class ControllerPlugin extends BaseControllerPlugin {
private async deleteFilesMatching(dir: string, predicate: (file: string) => boolean) {
const files = await fs.readdir(dir);
for (const file of files) {
if (predicate(file)) {
await fs.remove(path.join(dir, file));
}
}
const matches = files.filter(file => predicate(file));
await Promise.all(matches.map(file => fs.remove(path.join(dir, file))));
}
private removeSurfaceQueueEntries(instanceId: number, surface: string, force?: string) {
@@ -659,7 +658,7 @@ export class ControllerPlugin extends BaseControllerPlugin {
existingTile: Buffer,
newChunks: Map<string, { data: ChartData, tick: number }>
): Promise<Buffer | undefined> {
const existingChunks = new Map<string, { tick: number, data: Buffer }>();
const existingChunks = new Map<string, { tick: number, data: Buffer | Uint8Array }>();
// Parse existing tile data to find existing chunks
let offset = 0;
@@ -674,14 +673,20 @@ export class ControllerPlugin extends BaseControllerPlugin {
offset += 1;
if (type === 1) { // Chunk
const parseResult = this.parseChunkData(existingTile, offset, existingChunks);
const parseResult = parseChunkDataRecord(existingTile, offset);
if (!parseResult.success) {
this.logger.error(`Invalid tile data: insufficient chunk data at offset ${parseResult.newOffset}`);
break;
}
existingChunks.set(
`${parseResult.record.chunkX}_${parseResult.record.chunkY}`,
{ tick: parseResult.record.tick, data: parseResult.record.data }
);
offset = parseResult.newOffset;
} else if (type === 2) { // Pixels
const parseResult = this.parsePixelData(existingTile, offset);
const parseResult = parsePixelDataRecord(existingTile, offset);
if (!parseResult.success) {
this.logger.error(`Invalid tile data: insufficient pixel data at offset ${parseResult.newOffset}`);
break;
}
offset = parseResult.newOffset;
@@ -748,75 +753,6 @@ export class ControllerPlugin extends BaseControllerPlugin {
return Buffer.concat([existingTile, ...appendBuffers]);
}
private parseChunkData(
existingTile: Buffer,
offset: number,
existingChunks: Map<string, { tick: number, data: Buffer }>
): { success: boolean, newOffset: number } {
if (offset + 7 > existingTile.length) {
this.logger.error(`Invalid tile data: insufficient chunk header data at offset ${offset}`);
return { success: false, newOffset: offset };
}
// tick_sec (4 bytes): game second (uint32)
const tick = existingTile.readUInt32BE(offset);
offset += 4;
// chunk_coords (1 byte): packed nibble coords (x<<4 | y)
const chunkCoordsByte = existingTile.readUInt8(offset);
offset += 1;
// length (2 bytes): number of chunk bytes that follow (uint16)
const length = existingTile.readUInt16BE(offset);
offset += 2;
if (offset + length > existingTile.length) {
this.logger.error(`Invalid tile data: insufficient chunk data at offset ${offset}`);
return { success: false, newOffset: offset };
}
const chunkX = chunkCoordsByte >> 4;
const chunkY = chunkCoordsByte & 0x0F;
const chunkName = `${chunkX}_${chunkY}`;
// data (length bytes): compressed chunk bytes
const data = existingTile.slice(offset, offset + length);
offset += length;
existingChunks.set(chunkName, { tick, data });
return { success: true, newOffset: offset };
}
private parsePixelData(
existingTile: Buffer,
offset: number
): { success: boolean, newOffset: number } {
if (offset + 10 > existingTile.length) {
this.logger.error(`Invalid tile data: insufficient pixel header data at offset ${offset}`);
return { success: false, newOffset: offset };
}
// tick_sec (4 bytes): game second (uint32)
const tick = existingTile.readUInt32BE(offset);
offset += 4;
// pixel_count (2 bytes): number of pixel coords that follow (uint16)
const pixelCount = existingTile.readUInt16BE(offset);
offset += 2;
// new_color (2 bytes): RGB565 (uint16)
// old_color (2 bytes): RGB565 (uint16)
offset += 2 + 2;
void tick; // parsed for validation/advancing offset; not needed here
const pixelDataLength = pixelCount * 2;
if (offset + pixelDataLength > existingTile.length) {
this.logger.error(`Invalid tile data: insufficient pixel data at offset ${offset}`);
return { success: false, newOffset: offset };
}
// pixel_data (pixelCount * 2 bytes): x (1 byte), y (1 byte) pairs
// Skip payload since we only need offsets for chunk discovery.
offset += pixelDataLength;
return { success: true, newOffset: offset };
}
async loadExistingTagContent() {
try {
// Use existsSync for synchronous directory existence check
+100 -40
View File
@@ -433,6 +433,90 @@ export function parseTileData(tileData: BinaryData): ParsedTileData {
};
}
export interface ParsedChunkDataRecord {
tick: number;
chunkX: number;
chunkY: number;
data: BinaryData;
}
export interface ParsedPixelDataRecord {
tick: number;
pixelCount: number;
newColor: number;
oldColor: number;
pixelDataOffset: number;
}
export type ParseRecordResult<T> =
| { success: false; newOffset: number }
| { success: true; newOffset: number; record: T };
export function parseChunkDataRecord(
tileData: BinaryData,
offset: number
): ParseRecordResult<ParsedChunkDataRecord> {
if (offset + 7 > tileData.length) {
return { success: false, newOffset: offset };
}
const tick = readUInt32BE(tileData, offset);
offset += 4;
const chunkCoordsByte = readUInt8(tileData, offset);
offset += 1;
const length = readUInt16BE(tileData, offset);
offset += 2;
if (offset + length > tileData.length) {
return { success: false, newOffset: offset };
}
const chunkX = chunkCoordsByte >> 4;
const chunkY = chunkCoordsByte & 0x0F;
const data = sliceData(tileData, offset, offset + length);
return {
success: true,
newOffset: offset + length,
record: { tick, chunkX, chunkY, data },
};
}
export function parsePixelDataRecord(
tileData: BinaryData,
offset: number
): ParseRecordResult<ParsedPixelDataRecord> {
if (offset + 10 > tileData.length) {
return { success: false, newOffset: offset };
}
const tick = readUInt32BE(tileData, offset);
offset += 4;
const pixelCount = readUInt16BE(tileData, offset);
offset += 2;
const newColor = readUInt16BE(tileData, offset);
offset += 2;
const oldColor = readUInt16BE(tileData, offset);
offset += 2;
const pixelDataLength = pixelCount * 2;
if (offset + pixelDataLength > tileData.length) {
return { success: false, newOffset: offset };
}
return {
success: true,
newOffset: offset + pixelDataLength,
record: {
tick,
pixelCount,
newColor,
oldColor,
pixelDataOffset: offset,
},
};
}
/**
* Parse chunk record from tile data
* @param tileData The binary tile data being processed
@@ -447,35 +531,22 @@ function parseChunkRecord(
chunks: ChunkRecord[],
ticks: Set<number>
): { success: boolean; newOffset: number } {
if (offset + 7 > tileData.length) {
return { success: false, newOffset: offset };
const parsed = parseChunkDataRecord(tileData, offset);
if (!parsed.success) {
return parsed;
}
const tick = readUInt32BE(tileData, offset) * 60; // Convert back to actual tick
const tick = parsed.record.tick * 60; // Convert back to actual tick
ticks.add(tick);
offset += 4;
const chunkCoordsByte = readUInt8(tileData, offset);
offset += 1;
const length = readUInt16BE(tileData, offset);
offset += 2;
if (offset + length > tileData.length) {
return { success: false, newOffset: offset };
}
const chunkX = chunkCoordsByte >> 4;
const chunkY = chunkCoordsByte & 0x0F;
const data = sliceData(tileData, offset, offset + length);
chunks.push({
tick,
chunkX,
chunkY,
data,
chunkX: parsed.record.chunkX,
chunkY: parsed.record.chunkY,
data: parsed.record.data,
});
return { success: true, newOffset: offset + length };
return { success: true, newOffset: parsed.newOffset };
}
/**
@@ -492,35 +563,24 @@ function parsePixelChangeRecord(
pixelChanges: PixelChangeRecord[],
ticks: Set<number>
): { success: boolean; newOffset: number } {
if (offset + 10 > tileData.length) {
return { success: false, newOffset: offset };
const parsed = parsePixelDataRecord(tileData, offset);
if (!parsed.success) {
return parsed;
}
const tick = readUInt32BE(tileData, offset) * 60; // Convert back to actual tick
const tick = parsed.record.tick * 60; // Convert back to actual tick
ticks.add(tick);
offset += 4;
const pixelCount = readUInt16BE(tileData, offset);
offset += 2;
const newColor = readUInt16BE(tileData, offset);
offset += 2;
const oldColor = readUInt16BE(tileData, offset);
offset += 2;
const expectedDataLength = pixelCount * 2;
if (offset + expectedDataLength > tileData.length) {
return { success: false, newOffset: offset };
}
const changes: PixelChange[] = [];
for (let i = 0; i < pixelCount; i++) {
const pixelOffset = offset + i * 2;
for (let i = 0; i < parsed.record.pixelCount; i++) {
const pixelOffset = parsed.record.pixelDataOffset + i * 2;
const x = readUInt8(tileData, pixelOffset);
const y = readUInt8(tileData, pixelOffset + 1);
changes.push({ x, y, newColor, oldColor });
changes.push({ x, y, newColor: parsed.record.newColor, oldColor: parsed.record.oldColor });
}
pixelChanges.push({ tick, changes });
return { success: true, newOffset: offset + expectedDataLength };
return { success: true, newOffset: parsed.newOffset };
}
/**