mirror of
https://github.com/clusterio/clusterio.git
synced 2026-11-13 07:04:53 +09:00
refactor(minimap): deduplicate tile parsing and parallelize file cleanup
This commit is contained in:
1 parent
550ea22724
commit
387a9425ce
2 files changed
+113
-117
No files matched your search
@@ -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
|
||||
|
||||
@@ -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 };
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in new issue
Block a user