Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
300dad5
Merge pull request #2082 from HiveGamesOSS/develop
clankstar Apr 1, 2026
e1e317e
Merge pull request #2162 from HiveGamesOSS/develop
clankstar Apr 22, 2026
a0dfcb2
Add PreviewTileKey value type
Fabrimat May 18, 2026
027bc9c
Add alpha-aware tile downsampler for preview LOD pyramid
Fabrimat May 18, 2026
4f4ce42
Add bounded LRU cache for preview tile pyramid
Fabrimat May 18, 2026
a1029ed
Add PreviewMapBin reader/writer with empty-tile lookups
Fabrimat May 18, 2026
2d3c32c
Add fast metadata-only scan for Java Edition worlds
Fabrimat May 18, 2026
7315afc
Add Bedrock key-only scan to preview metadata reader
Fabrimat May 18, 2026
6dbf437
Add lazy preview tile service with LOD 0 generation
Fabrimat May 18, 2026
e42f403
Support LOD aggregation in preview tile service
Fabrimat May 18, 2026
3e3770c
Add production region RGBA source backed by Chunker world reader
Fabrimat May 18, 2026
e35f04e
Add request/cancel preview tile messenger types
Fabrimat May 18, 2026
ed90fc1
Switch preview command to metadata pass and add tile service handlers
Fabrimat May 18, 2026
53ac7b0
Remove legacy upfront preview writers
Fabrimat May 18, 2026
3ad350a
Forward preview tile flow messages and add typed listener support
Fabrimat May 18, 2026
b2d9ec5
Parse full map.bin client-side and expose empty-tile oracle
Fabrimat May 18, 2026
8dc3c7f
Add auto-fit minZoom calculation with -12 floor
Fabrimat May 18, 2026
c4520d0
Add LRU client tile cache with explicit DOM unmount
Fabrimat May 18, 2026
9586a98
Add custom GridLayer with batched padded tile requests
Fabrimat May 18, 2026
c7a347b
Add editable center-coordinate control for preview map
Fabrimat May 18, 2026
ce44506
Switch preview to streaming tile layer with center coords control
Fabrimat May 18, 2026
5d6be23
Run preview metadata pass asynchronously with progress feedback
Fabrimat May 18, 2026
e23218b
Fetch preview map.bin via session protocol to avoid IPC blocking
Fabrimat May 18, 2026
8e4bace
Cap preview layer native zoom to LOD 0 and guard isTileEmpty against …
Fabrimat May 18, 2026
5867006
Add zoom indicator and refine center coords control
Fabrimat May 18, 2026
3583701
Expose preview layer at negative zoom levels and keep more tiles in DOM
Fabrimat May 18, 2026
86f1edc
Make zoom indicator interactive and add tile loading badge
Fabrimat May 18, 2026
66bc783
Polish preview controls: leaflet-bar contour, smaller coords inline w…
Fabrimat May 18, 2026
508c9b7
Refine coords control sizing and reinvalidate map size on container r…
Fabrimat May 18, 2026
f00c8b6
Recover orphan tiles via Leaflet registry and redraw layers after mount
Fabrimat May 18, 2026
41da377
Use additive _update instead of redraw to avoid tile flicker
Fabrimat May 18, 2026
da3098d
Prevent Leaflet from pruning preview tiles still awaiting their tile_…
Fabrimat May 18, 2026
bba88c0
Scope tile-prune protection to current zoom level only
Fabrimat May 18, 2026
910da9f
Write preview tiles atomically and set initial map view at construction
Fabrimat May 18, 2026
45abb35
Update tile placeholder src directly on tile_ready to avoid dual-imag…
Fabrimat May 18, 2026
485e6be
Serialize preview region loads to avoid LevelDB lock contention
Fabrimat May 19, 2026
398694c
Pre-load default world tiles into a shared cache before mounting the map
Fabrimat May 26, 2026
b04d2fd
Warm adjacent LOD tiles on moveend so zoom steps paint from cache
Fabrimat May 26, 2026
0fe4665
Unify preview loading wrapper and abort Java tile work on cancel
Fabrimat May 26, 2026
1442c69
Merge remote-tracking branch 'upstream/develop' into world-preview-st…
Fabrimat Jun 5, 2026
ffa70ad
Pick newest CLI jar when multiple builds exist
Fabrimat Jun 5, 2026
b59308a
Retry preview region reads when the LevelDB lock is briefly unavailable
Fabrimat Jun 5, 2026
7120eee
Fix Bedrock sub-chunk key parsing in the preview metadata scan
Fabrimat Jun 5, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 37 additions & 5 deletions app/electron/src/session.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,12 @@ export class Session {
if (files.length === 0) {
throw new Error("chunker-cli executable is missing!");
}

// The dev build directory can contain multiple versioned jars (e.g. an older
// chunker-cli-1.17.0.jar left next to chunker-cli-1.18.1.jar). Sort by name using
// numeric ordering so we always launch the newest build instead of whichever the OS
// happens to list first.
files.sort((a, b) => b.name.localeCompare(a.name, undefined, {numeric: true}));
executable = path.join(cliDirectory, files[0].name);
}

Expand Down Expand Up @@ -277,6 +283,12 @@ export class Session {
case "generate_preview":
await this.generatePreview(data.requestId);
break;
case "request_preview_tiles":
await this.requestPreviewTiles(data);
break;
case "cancel_preview_tiles":
await this.cancelPreviewTiles(data);
break;
case "convert":
await this.convertWorld(data.outputType, data.requestId, data);
break;
Expand Down Expand Up @@ -665,12 +677,32 @@ export class Session {
outputPath: previewOutputPath
}

// Send the preview request
this.sendToProcess(request, async (response) => {
// Use the base64 of the map.bin for output
response.output = (await fs.readFile(path.join(previewOutputPath, "map.bin"))).toString("base64");
// Send the preview request. The renderer fetches map.bin via session:// URL after
// receiving the success response — no base64 payload on the wire.
this.sendToProcess(request);
}

return response;
requestPreviewTiles(data) {
this.sendToProcess({
type: "request_preview_tiles",
requestId: data.requestId,
anonymousId: this._sessionID,
world: data.world,
lod: data.lod,
minTx: data.minTx,
minTz: data.minTz,
maxTx: data.maxTx,
maxTz: data.maxTz
});
}

cancelPreviewTiles(data) {
this.sendToProcess({
type: "cancel_preview_tiles",
requestId: data.requestId,
anonymousId: this._sessionID,
world: data.world,
lod: data.lod
});
}

Expand Down
42 changes: 23 additions & 19 deletions app/ui/src/api.js
Original file line number Diff line number Diff line change
@@ -1,58 +1,62 @@
let api = {
connection: undefined,
replyHandlers: {},
listeners: {},
connect: function (connectHandler) {
let handlers = {};

// Connection open handler
handlers.onopen = function () {
connectHandler();
};

// Connection close handler
handlers.onclose = function (e) {
api.connection = undefined;
if (e.code === 1000) return; // Don't show error, as it was completed cleanly (and successful!)
if (e.code === 1000) return;
if (e.code === 200) {
// Connection closed intentionally (eg. inactive)
} else {
// Connection closed for other reason, should reconnect?
// intentional close
}
connectHandler(e.code);
};

// Connection message handler
handlers.onmessage = function (e) {
let msg = JSON.parse(e.data);
let requestId = msg.requestId;
if (api.replyHandlers[requestId] === undefined) {
console.warn("No reply handler found: ", msg);
} else {
let handler = api.replyHandlers[requestId];

// Check if handler needs removing
let handler = api.replyHandlers[requestId];
if (handler !== undefined) {
if (msg.continue === undefined || msg.continue === false) {
// Remove as it's done
delete api.replyHandlers[requestId];
}

// Call handler
handler(msg);
return;
}

let typed = api.listeners[msg.type];
if (typed && typed.length > 0) {
for (let cb of typed) cb(msg);
return;
}

console.warn("No reply handler or listener found: ", msg);
};

// Set connection
this.connection = window.chunker.connect(handlers);
},
send: function (obj, replyHandler) {
obj.requestId = crypto.randomUUID();// Generate random id
obj.requestId = crypto.randomUUID();
if (this.connection !== undefined) {
this.replyHandlers[obj.requestId] = replyHandler;
this.connection.send(JSON.stringify(obj));
} else {
throw Error("Not connected!");
}
},
addListener: function (type, callback) {
if (!this.listeners[type]) this.listeners[type] = [];
this.listeners[type].push(callback);
return () => {
this.listeners[type] = (this.listeners[type] || []).filter(cb => cb !== callback);
};
},
isConnected: function () {
return this.connection !== undefined;
},
Expand All @@ -63,4 +67,4 @@ let api = {
}
};

export default api;
export default api;
Loading