Skip to content

Conversation

@sa2urami
Copy link

@sa2urami sa2urami commented May 15, 2024

WORKING PLAYGROUND DEMO - PERF CHUNKS
LATEST MAP DEMO (Future City)

tasks (branch is archived now):

  • Fog, chunk opacity, cube map stars &meter
  • true cull face (like normal dot product)
  • investigate reusing shaders pipeline
  • > 2gb buffers for 4090
  • static lighting with cache
  • ssao
  • slabs & stairs, custom models like torches
  • god rays vol lighting
  • linearize Z (make clip Z far)
  • pass vert shader - fix lag on old ipads 5 with slow rest
  • display current rendering tiles
  • check transparent no text
  • frustum culling chunks
  • always draw 8 block radius

@zardoy

  • redesign mobile control like ptg
  • fix ui rot bug
  • investigate alt orientation ui
  • optifine zoom button
  • print more info on loading chunks, debug minecraft-like ui
  • always fix conn rel
  • load chunks in spiral, no mesher recompute, all sides on edges
  • Singleplayer world worker
  • mem crash ui
  • mem buff limit

Summary by CodeRabbit

  • New Features

    • Introduced a new WebGPU rendering mode for enhanced visuals and performance.
    • Added improved touch controls for mobile devices.
    • Implemented a new Controls component for better touch interaction.
    • Introduced new scene variants for enriched 3D experiences.
    • Added a new TextureAnimation class for managing texture animations.
    • Introduced a MessageChannelReplacement class for improved message handling.
  • Enhancements

    • Updated the maps provider URL for better integration.
    • Refined camera controls and texture animations for a more immersive experience.
    • Enhanced state management for player attributes and interactions.
    • Improved error handling and state management in various components.
  • Bug Fixes

    • Resolved issues related to block visibility and rendering.
    • Fixed issues with touch event handling and button responsiveness.

These updates deliver improved graphics, responsiveness, and a modernized user interface for a superior viewing experience.

@codesandbox
Copy link

codesandbox bot commented May 15, 2024

Review or Edit in CodeSandbox

Open the branch in Web EditorVS CodeInsiders

Open Preview

@zardoy
Copy link
Owner

zardoy commented May 21, 2024

/deploy

@github-actions
Copy link

Deployed to Vercel Preview: https://prismarine-pho51nlw6-zaro.vercel.app
Playground
Storybook

@zardoy
Copy link
Owner

zardoy commented May 22, 2024

/deploy

@github-actions
Copy link

Deployed to Vercel Preview: https://prismarine-6lrmuc5cl-zaro.vercel.app
Playground
Storybook

@zardoy zardoy changed the title New realisation of renderer on WebGPU New realisation of renderer on WebGPU (webgpu.mcraft.fun) May 24, 2024
@zardoy
Copy link
Owner

zardoy commented May 25, 2024

/deploy

@github-actions
Copy link

Deployed to Vercel Preview: https://prismarine-bc2ncs3uy-zaro.vercel.app
Playground
Storybook

@zardoy
Copy link
Owner

zardoy commented Jun 25, 2024

/deploy

@github-actions
Copy link

Deployed to Vercel Preview: https://prismarine-1x8yri3lq-zaro.vercel.app
Playground
Storybook

@zardoy
Copy link
Owner

zardoy commented Jul 10, 2024

/deploy

@github-actions
Copy link

Deployed to Vercel Preview: https://prismarine-gpwdotih8-zaro.vercel.app
Playground
Storybook

@zardoy
Copy link
Owner

zardoy commented Jul 10, 2024

/deploy

@github-actions
Copy link

Deployed to Vercel Preview: https://prismarine-90t7s5wjn-zaro.vercel.app
Playground
Storybook

@zardoy
Copy link
Owner

zardoy commented Jul 18, 2024

/deploy

@github-actions
Copy link

Deployed to Vercel Preview: https://prismarine-700h6sw1a-zaro.vercel.app
Playground
Storybook

@zardoy
Copy link
Owner

zardoy commented Jul 31, 2024

/deploy

@github-actions
Copy link

Deployed to Vercel Preview: https://prismarine-rfw8k4jpj-zaro.vercel.app
Playground
Storybook

@zardoy
Copy link
Owner

zardoy commented Aug 2, 2024

/deploy

@github-actions
Copy link

github-actions bot commented Aug 2, 2024

Deployed to Vercel Preview: https://prismarine-n3cdd3dup-zaro.vercel.app
Playground
Storybook

@zardoy
Copy link
Owner

zardoy commented Aug 2, 2024

/deploy

@github-actions
Copy link

github-actions bot commented Aug 2, 2024

Deployed to Vercel Preview: https://prismarine-h8wzgbvk8-zaro.vercel.app
Playground
Storybook

@zardoy
Copy link
Owner

zardoy commented Aug 4, 2024

/deploy

@github-actions
Copy link

github-actions bot commented Aug 4, 2024

Deployed to Vercel Preview: https://prismarine-arqd0oaby-zaro.vercel.app
Playground
Storybook

@zardoy
Copy link
Owner

zardoy commented Aug 4, 2024

/deploy

@github-actions
Copy link

github-actions bot commented Aug 4, 2024

Deployed to Vercel Preview: https://prismarine-qyfyijkk9-zaro.vercel.app
Playground
Storybook

@zardoy
Copy link
Owner

zardoy commented Aug 4, 2024

/deploy

@github-actions
Copy link

github-actions bot commented Aug 4, 2024

Deployed to Vercel Preview: https://prismarine-5aftdhvlw-zaro.vercel.app
Playground
Storybook

@zardoy
Copy link
Owner

zardoy commented Aug 5, 2024

/deploy

@github-actions
Copy link

github-actions bot commented Aug 5, 2024

Deployed to Vercel Preview: https://prismarine-b28fg7kj6-zaro.vercel.app
Playground
Storybook

@github-actions
Copy link

github-actions bot commented Apr 5, 2025

Deployed to Vercel Preview: https://prismarine-5o52rrley-zaro.vercel.app
Playground
Storybook

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🔭 Outside diff range comments (1)
renderer/viewer/three/world/cursorBlock.ts (1)

93-105: ⚠️ Potential issue

Method signature change may cause unintended mutation

The updateBreakAnimation method has been refactored to work with separated position and shape data instead of a complete Block object. While this decoupling is good for the new renderer architecture, there's a potential issue with line 99.

The line position.add(blockPosition) mutates the original position object from mergedShape. If mergedShape.position is used elsewhere after this call, it will contain an incorrect position (with blockPosition already added). Consider using a clone of the position to avoid this side effect:

- position.add(blockPosition)
- this.blockBreakMesh.position.set(position.x, position.y, position.z)
+ const worldPosition = position.clone().add(blockPosition)
+ this.blockBreakMesh.position.set(worldPosition.x, worldPosition.y, worldPosition.z)
🧹 Nitpick comments (5)
renderer/viewer/lib/basePlayerState.ts (3)

17-18: Consider defining a stronger type for block shape fields.
Using any for position and other fields could lead to runtime surprises. A dedicated interface or a more specific type would improve clarity and maintainability.


45-51: Consolidate coordinate properties into a dedicated type or interface.
Reusing (x, y, z) across multiple structures may lead to duplication. A shared Vec3Like type can improve consistency.


52-59: Apply the same coordinate-type approach for 'diggingBlock'.
Consider a shared interface or type to avoid repeating coordinate definitions.

renderer/viewer/three/worldrendererThree.ts (1)

122-139: Introduce descriptive constants for fog distance values.
Replacing hard-coded values (e.g., 100 vs 20) with well-named constants could improve clarity. Overall, the reactive updates for player state are implemented cleanly.

renderer/viewer/three/world/cursorBlock.ts (1)

95-95: Parameter validation inconsistency

The mergedShape parameter is marked as optional in the method signature, but the early return condition treats it as required. This could lead to unexpected behavior if callers assume they can omit it. Consider either:

  1. Making the parameter required in the signature, or
  2. Handling the undefined case differently if it's a valid scenario
- updateBreakAnimation (blockPosition: { x: number, y: number, z: number } | undefined, stage: number | null, mergedShape?: BlockShape) {
+ updateBreakAnimation (blockPosition: { x: number, y: number, z: number } | undefined, stage: number | null, mergedShape: BlockShape) {
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6b642d0 and 746f90d.

📒 Files selected for processing (9)
  • renderer/viewer/lib/basePlayerState.ts (3 hunks)
  • renderer/viewer/lib/mesher/mesher.ts (4 hunks)
  • renderer/viewer/lib/mesher/models.ts (10 hunks)
  • renderer/viewer/three/graphicsBackend.ts (1 hunks)
  • renderer/viewer/three/world/cursorBlock.ts (3 hunks)
  • renderer/viewer/three/worldrendererThree.ts (1 hunks)
  • renderer/viewer/webgpu/worldrendererWebgpu.ts (1 hunks)
  • src/controls.ts (3 hunks)
  • src/mineflayer/plugins/mouse.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (4)
  • renderer/viewer/lib/mesher/mesher.ts
  • renderer/viewer/three/graphicsBackend.ts
  • renderer/viewer/webgpu/worldrendererWebgpu.ts
  • renderer/viewer/lib/mesher/models.ts
🧰 Additional context used
🧬 Code Definitions (1)
src/mineflayer/plugins/mouse.ts (1)
src/mineflayer/playerState.ts (1)
  • playerState (206-206)
🪛 Biome (1.9.4)
src/controls.ts

[error] 950-950: This is an unexpected use of the debugger statement.

Unsafe fix: Remove debugger statement

(lint/suspicious/noDebugger)

🔇 Additional comments (11)
src/controls.ts (3)

420-422: Improved state check for URL locking.

Good enhancement by adding the fsState.inMemorySave condition check. This prevents potential errors when attempting to access inMemorySavePath when there is no in-memory save available.


948-951: Avoid leaving debugger statements in production code.

According to static analysis, the debugger statement is flagged. Removing or wrapping it behind a dev build check can prevent accidental triggers in production.

  if (e.code === 'KeyK' && e.altKey && !e.shiftKey && !e.ctrlKey && !e.metaKey) {
-    // eslint-disable-next-line no-debugger
-    debugger
+    // TODO: Use console.log or dev-tools breakpoints in development only
  }
🧰 Tools
🪛 Biome (1.9.4)

[error] 950-950: This is an unexpected use of the debugger statement.

Unsafe fix: Remove debugger statement

(lint/suspicious/noDebugger)


952-954:

Details

❓ Verification inconclusive

New keyboard shortcut to toggle renderer engines.

You've implemented a useful Alt+J keyboard shortcut to toggle between the WebGPU and ThreeJS renderers, which aligns with the main objective of this PR to add WebGPU as a rendering option.


🏁 Script executed:

#!/bin/bash
# Check for the renderer options definition in optionsGuiScheme.tsx

echo "Checking for renderer options definition..."
rg -A 10 "activeRenderer" --include="*.tsx"

Length of output: 582


Renderer Toggle Shortcut Functionality – Please Verify Active Renderer Definition

The Alt+J shortcut implementation in src/controls.ts (lines 952–954) cleanly toggles between the WebGPU and ThreeJS renderers as intended. The logic correctly checks that only Alt (without Shift, Ctrl, or Meta) is pressed and then swaps the activeRenderer value.

Before finalizing this change, please manually verify that the activeRenderer property is properly defined and used in the corresponding UI/options file (for example, in optionsGuiScheme.tsx or the relevant component), as our initial search using rg encountered flag issues.

  • File: src/controls.ts (lines 952–954) – toggling logic is correctly implemented.
  • Action Needed: Confirm that the activeRenderer property exists and is managed consistently across the codebase.
renderer/viewer/lib/basePlayerState.ts (3)

5-5: Use of 'ref' from 'valtio' is clear and valid.
No issues noted.


40-40: Addition of 'waterBreathing' property looks consistent.
This property aligns well with existing booleans like inWater and improves readability.


67-68: Reactive array reference for 'backgroundColor' is appropriate.
Good usage of ref to optimize reactivity.

src/mineflayer/plugins/mouse.ts (3)

14-14: Clearing 'lookingAtBlock' on missing block data is straightforward.
This prevents stale or incorrect references.


19-26: Reactive assignment of 'lookingAtBlock' data.
Storing block details in playerState is a clean and maintainable approach.


32-39: Managing 'diggingBlock' reactively is well-structured.
Conditionally clearing data when the break stage is absent ensures correct state transitions.

renderer/viewer/three/world/cursorBlock.ts (2)

6-6: Import updated to use new block shape types

The import statement has been updated to use the new BlockShape and BlocksShapes types from the basePlayerState module. This change aligns with the refactoring of the block rendering system to support the new WebGPU renderer.


121-121: Type signature improved for block shape handling

The parameter type for shapePositions has been updated to use the more specific BlocksShapes type, which improves type safety and makes the expected data structure clearer.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (1)
renderer/viewer/lib/worldrendererCommon.ts (1)

163-167: ⚠️ Potential issue

Potential memory leak from interval.

A setInterval is created but no reference is stored to clear it later. If multiple renderer instances are created or the renderer is destroyed and recreated, this may lead to memory leaks.

Consider storing the interval ID as a class property and clearing it in the destroy() method:

+private geometryStatsInterval?: NodeJS.Timeout

 constructor(public readonly resourcesManager: ResourcesManager, public displayOptions: DisplayWorldOptions, public version: string) {
   // ...
   
-  setInterval(() => {
+  this.geometryStatsInterval = setInterval(() => {
     this.geometryReceiveCountPerSec = Object.values(this.geometryReceiveCount).reduce((acc, curr) => acc + curr, 0)
     this.geometryReceiveCount = {}
     this.updateChunksStats()
   }, 500)
 }

 destroy() {
   // ...
+  if (this.geometryStatsInterval) clearInterval(this.geometryStatsInterval)
   
   this.active = false
   // ...
 }
🧹 Nitpick comments (1)
src/loadSave.ts (1)

19-20: Well-structured fsState initialization with global exposure

The new initialization approach using structuredClone(initialFsState) is a good practice as it:

  1. Creates a deep copy of the initial state to prevent reference issues
  2. Centralizes the state definition in one place
  3. Makes the state structure more predictable and maintainable

Making the state available globally with globalThis.fsState = fsState improves accessibility across modules but consider documenting this global access point for other developers.

Consider adding a comment explaining the purpose of making fsState global:

export const fsState = proxy(structuredClone(initialFsState))
+// Make fsState globally accessible for debugging and cross-module access
globalThis.fsState = fsState
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 746f90d and 96b1a81.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (9)
  • renderer/viewer/lib/mesher/mesher.ts (4 hunks)
  • renderer/viewer/lib/worldrendererCommon.ts (4 hunks)
  • renderer/viewer/webgpu/worldrendererWebgpu.ts (1 hunks)
  • src/controls.ts (2 hunks)
  • src/loadSave.ts (3 hunks)
  • src/optionsGuiScheme.tsx (2 hunks)
  • src/optionsStorage.ts (3 hunks)
  • src/utils.ts (1 hunks)
  • src/watchOptions.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (6)
  • src/watchOptions.ts
  • src/optionsGuiScheme.tsx
  • src/optionsStorage.ts
  • src/utils.ts
  • renderer/viewer/webgpu/worldrendererWebgpu.ts
  • renderer/viewer/lib/mesher/mesher.ts
🧰 Additional context used
🧬 Code Definitions (2)
renderer/viewer/lib/worldrendererCommon.ts (1)
renderer/viewer/lib/ui/newStats.ts (2)
  • addNewStat (7-36)
  • updateStatText (82-85)
src/loadSave.ts (1)
src/integratedServer/browserfsShared.ts (1)
  • initialFsState (31-43)
🔇 Additional comments (9)
src/controls.ts (2)

950-950: Avoid leaving debugger statements in production code.

According to static analysis, the debugger statement is flagged. Removing or wrapping it behind a dev build check can prevent accidental triggers in production.

  if (e.code === 'KeyK' && e.altKey && !e.shiftKey && !e.ctrlKey && !e.metaKey) {
-    debugger
+    // TODO: Use console.log or dev-tools breakpoints in development only
  }
🧰 Tools
🪛 Biome (1.9.4)

[error] 950-950: This is an unexpected use of the debugger statement.

Unsafe fix: Remove debugger statement

(lint/suspicious/noDebugger)


952-954: Good implementation of renderer toggle feature

The new Alt+J keyboard shortcut for toggling between WebGPU and ThreeJS renderers is a useful addition for testing and comparing the renderers. This aligns well with the PR objective of implementing a new WebGPU renderer.

src/loadSave.ts (3)

13-13: Good refactoring of imports from centralized module

The import statement has been updated to bring in utility functions and state from the browserfsShared module, which helps centralize file system related functionality. This is a good practice for maintainability.


107-107: Consistent use of encapsulated state properties

Using fsState.forceCachedDataPaths instead of a standalone variable improves code organization by properly encapsulating related state within the fsState object.


146-146: Consistent state property access pattern

Using fsState.forceRedirectPaths follows the same pattern of accessing state properties through the fsState object, maintaining consistency throughout the codebase.

renderer/viewer/lib/worldrendererCommon.ts (4)

159-160: Good addition of chunk download statistics UI element.

Adding a dedicated stat for downloaded chunks improves visibility of the rendering process, which aligns well with the WebGPU rendering implementation focus of this PR.


167-167: Increased stats refresh rate from 1000ms to 500ms.

Doubling the update frequency for geometry reception statistics will provide more responsive feedback during rendering. This is a good change for the WebGPU renderer where performance monitoring is critical.


528-528: Enhanced statistics display with queue information.

The addition of message queue length information (Q: ${this.messageQueue.length}) provides valuable insight into the rendering pipeline's current workload, which is helpful for debugging and performance monitoring.


579-582: Improved condition for resetting chunk loading metrics.

The revised condition for resetting metrics when there are no finished chunks is more precise than the previous implementation that checked if all chunks were not finished.

Comment on lines +601 to +608

// viewer.world.chunksReset() // todo

// TODO!
// if (localServer) {
// //@ts-expect-error not sure why it is private... maybe revisit api?
// localServer.players[0].world.columns = {}
// }
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Consider implementing the TODO for viewer.world.chunksReset()

The previous code that cleared localServer.players[0].world.columns has been commented out, with a TODO note for implementing viewer.world.chunksReset(). This change appears to be part of the refactoring for the new WebGPU renderer. Make sure to implement this method to properly handle chunk resets in the new rendering system.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

🧹 Nitpick comments (9)
renderer/viewer/webgpu/worldrendererWebgpu.ts (8)

157-159: Fix missing setTimeout delay parameter.

The setTimeout call is missing a delay parameter, which may cause inconsistent behavior across browsers.

 await new Promise(resolve => {
-  setTimeout(resolve,)
+  setTimeout(resolve, 0)
 })

180-180: Remove or explain commented code.

There's commented-out code that might indicate an unresolved issue or incomplete implementation.

Either remove the commented code or add an explanatory comment about why it's commented out and if it needs to be addressed in the future.

-  if (/* !this.loadedChunks[chunkCoords[0] + ',' + chunkCoords[2]] ||  */ !this.active) return
+  if (!this.active) return

230-232: Implement the updateShowChunksBorder method.

The method contains only a TODO comment without any implementation, which means this functionality is not working.

 updateShowChunksBorder (value: boolean) {
-  // todo
+  this.webgpuChannel.updateShowChunksBorder(value)
 }

Would you like me to help implement this method or create an issue to track this task?


429-435: Optimize mainLoop in debug UI to use requestAnimationFrame's callback parameter.

The current implementation creates a new variable to track updates, but requestAnimationFrame already provides a timestamp parameter that could be used to calculate FPS more accurately.

-  let updates = 0
+  let lastTime = performance.now();
+  let frames = 0;
   const mainLoop = () => {
-    requestAnimationFrame(mainLoop)
-    updates++
+    const currentTime = performance.now();
+    frames++;
+    
+    // Update FPS counter once per second
+    if (currentTime - lastTime >= 1000) {
+      updateText2(`Main Loop: ${frames}`);
+      frames = 0;
+      lastTime = currentTime;
+    }
+    
+    requestAnimationFrame(mainLoop);
   }
   mainLoop()
-  setInterval(() => {
-    updateText2(`Main Loop: ${updates}`)
-    updates = 0
-  }, 1000)

36-36: Add type safety to webgpuChannel placeholder.

The current implementation uses any type which reduces type safety. Consider using a more specific type to ensure type checking during development.

-  webgpuChannel: typeof workerProxyType['__workerProxy'] = this.getPlaceholderChannel()
+  webgpuChannel: typeof workerProxyType['__workerProxy'] = this.getPlaceholderChannel<typeof workerProxyType['__workerProxy']>()

Then update the getPlaceholderChannel method:

-  getPlaceholderChannel () {
+  getPlaceholderChannel<T extends Record<string, (...args: any[]) => any>> (): T {
     return new Proxy({}, {
       get: (target, p) => (...args) => {
         void this.readyWorkerPromise.then(() => {
-          this.webgpuChannel[p](...args)
+          (this.webgpuChannel as any)[p as string](...args)
         })
       }
-    }) as any // placeholder to avoid crashes
+    }) as T // placeholder to avoid crashes
   }

80-88: Improve type safety in getPlaceholderChannel method.

The method uses any type which reduces type safety. Using generics can provide better type checking while maintaining the flexibility needed for this placeholder implementation.

-  getPlaceholderChannel () {
-    return new Proxy({}, {
-      get: (target, p) => (...args) => {
+  getPlaceholderChannel<T extends Record<string, (...args: any[]) => any>>(): T {
+    return new Proxy<Record<string, any>>({}, {
+      get: (target, p: string | symbol) => (...args: any[]) => {
         void this.readyWorkerPromise.then(() => {
-          this.webgpuChannel[p](...args)
+          (this.webgpuChannel as any)[p](...args)
         })
       }
-    }) as any // placeholder to avoid crashes
+    }) as T // placeholder to avoid crashes
   }

101-113: Improve type safety in sendCameraToWorker method.

The method uses any type which reduces type safety. Using proper type annotations would improve code quality and prevent potential type-related bugs.

+  interface CameraVectors {
+    rotation: { x: number; y: number; z: number };
+    position: { x: number; y: number; z: number };
+  }

   sendCameraToWorker () {
-    const cameraVectors = ['rotation', 'position'].reduce((acc, key) => {
-      acc[key] = ['x', 'y', 'z'].reduce((acc2, key2) => {
-        acc2[key2] = this.camera[key][key2]
+    const cameraVectors = ['rotation', 'position'].reduce<Partial<CameraVectors>>((acc, key) => {
+      acc[key as keyof CameraVectors] = ['x', 'y', 'z'].reduce<{ x?: number; y?: number; z?: number }>((acc2, key2) => {
+        acc2[key2 as 'x' | 'y' | 'z'] = this.camera[key as 'rotation' | 'position'][key2 as 'x' | 'y' | 'z']
         return acc2
-      }, {})
+      }, {}) as { x: number; y: number; z: number }
       return acc
-    }, {}) as any
+    }, {}) as CameraVectors
     this.webgpuChannel.camera({
       ...cameraVectors,
       fov: this.displayOptions.inWorldRenderingConfig.fov
     })
   }

117-121: Optimize event listener in addWebgpuListener method.

The current implementation adds a new listener for every type, which is inefficient if multiple listeners for different types are added. Instead, a single listener could be used with type filtering.

 export class WorldRendererWebgpu extends WorldRendererCommon {
+  private typeListeners: Map<string, Array<(data: any) => void>> = new Map();
+  private messageHandler: ((e: MessageEvent) => void) | null = null;
 
   // ...
 
   addWebgpuListener (type: string, listener: (data: any) => void) {
     void this.readyWorkerPromise.then(() => {
-      this.worker!.addEventListener('message', (e: any) => {
-        if (e.data.type === type) {
-          listener(e.data)
-        }
-      })
+      if (!this.typeListeners.has(type)) {
+        this.typeListeners.set(type, []);
+      }
+      this.typeListeners.get(type)!.push(listener);
+      
+      // Add the global handler only once
+      if (!this.messageHandler) {
+        this.messageHandler = (e: MessageEvent) => {
+          const messageType = e.data?.type;
+          if (messageType && this.typeListeners.has(messageType)) {
+            for (const typeListener of this.typeListeners.get(messageType)!) {
+              typeListener(e.data);
+            }
+          }
+        };
+        this.worker!.addEventListener('message', this.messageHandler);
+      }
     })
   }
   
   // Don't forget to clean up in destroy method
   override destroy () {
     // ...
+    if (this.worker && this.messageHandler) {
+      this.worker.removeEventListener('message', this.messageHandler);
+      this.messageHandler = null;
+      this.typeListeners.clear();
+    }
     // ...
   }
 }
src/index.ts (1)

347-367: Large block of commented-out code should be removed

This extensive block of commented-out code suggests incomplete refactoring. If this functionality has been replaced, the commented code should be removed to improve readability.

If the functionality has been completely replaced by the new implementation, consider removing this commented-out code. If not, add a comment explaining why it's being preserved.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 96b1a81 and fe590bf.

📒 Files selected for processing (5)
  • renderer/viewer/webgpu/graphicsBackendWebgpu.ts (1 hunks)
  • renderer/viewer/webgpu/worldrendererWebgpu.ts (1 hunks)
  • src/appViewer.ts (4 hunks)
  • src/appViewerLoad.ts (3 hunks)
  • src/index.ts (10 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • renderer/viewer/webgpu/graphicsBackendWebgpu.ts
🧰 Additional context used
🧬 Code Definitions (3)
src/index.ts (4)
src/integratedServer/main.ts (2)
  • destroyLocalServerMain (115-137)
  • startLocalServerMain (55-97)
src/globalState.ts (1)
  • miscUiState (109-129)
src/optionsStorage.ts (1)
  • options (218-223)
src/appViewer.ts (1)
  • appViewer (256-256)
renderer/viewer/webgpu/worldrendererWebgpu.ts (5)
renderer/playground/webgpuRendererShared.ts (2)
  • defaultWebgpuRendererParams (4-13)
  • rendererParamsGui (15-25)
renderer/playground/webgpuRendererWorker.ts (2)
  • workerProxyType (95-281)
  • canvas (97-113)
renderer/viewer/webgpu/graphicsBackendWebgpu.ts (1)
  • WebgpuInitOptions (5-7)
src/appViewer.ts (1)
  • DisplayWorldOptions (61-68)
renderer/playground/webgpuBlockModels.ts (1)
  • prepareCreateWebgpuBlocksModelsData (9-157)
src/appViewerLoad.ts (2)
src/appViewer.ts (2)
  • appViewer (256-256)
  • loadBackend (117-156)
src/optionsStorage.ts (1)
  • options (218-223)
🔇 Additional comments (15)
renderer/viewer/webgpu/worldrendererWebgpu.ts (5)

58-63: Add MessagePort cleanup in destroy method.

The destroy method terminates the worker if it's a Worker instance but doesn't close the port if it's a MessagePort, which could lead to resource leaks.

Apply this diff to fix the issue:

 if (this.worker instanceof Worker) {
   this.worker.terminate()
+} else if (this.worker instanceof MessagePort) {
+  this.worker.close()
 }

273-274: Add error handling for fetch operation.

The fetch operation could fail for various reasons (network issues, invalid URL, etc.), but there's no error handling.

-  const imageBlob = await fetch(image).then(async (res) => res.blob())
+  let imageBlob: Blob;
+  try {
+    const response = await fetch(image);
+    if (!response.ok) {
+      throw new Error(`Failed to fetch image: ${response.status} ${response.statusText}`);
+    }
+    imageBlob = await response.blob();
+  } catch (error) {
+    console.error('Failed to fetch texture image:', error);
+    this.issueReporter.reportProblem(false, `Failed to load textures: ${error.message}`);
+    throw error;
+  }

402-404: Implement UI cleanup in addWebgpuDebugUi.

The TODO comment indicates that cleanup for the debug UI is not implemented, which could lead to memory leaks.

The debug UI should be properly cleaned up when no longer needed. Consider implementing a cleanup function that:

  1. Removes event listeners
  2. Clears timers
  3. Removes DOM elements

This function should be called when the renderer is destroyed.


407-419: Add cleanup for event listener in addWebgpuDebugUi.

The event listener is added but never removed, which could lead to memory leaks if the component is destroyed and recreated.

+  const messageListener = (e: any) => {
+    if (e.data.type === 'fps') {
+      updateText(`FPS: ${e.data.fps}`)
+      if (prevTimeout) clearTimeout(prevTimeout)
+      prevTimeout = setTimeout(() => {
+        updateText('<hanging>')
+      }, 1002)
+    }
+    if (e.data.type === 'stats') {
+      updateTextGpuStats(e.data.stats)
+      // worldRenderer.rendererDevice = `${e.data.device} WebGL data: ${WorldRendererThree.getRendererInfo(new THREE.WebGLRenderer())}`
+    }
+  }
+  worker.addEventListener('message', messageListener)
+  
+  // Clean up on abort
+  worldRenderer.abortController.signal.addEventListener('abort', () => {
+    worker.removeEventListener('message', messageListener)
+    if (prevTimeout) clearTimeout(prevTimeout)
+  })
-  worker.addEventListener('message', (e: any) => {
-    if (e.data.type === 'fps') {
-      updateText(`FPS: ${e.data.fps}`)
-      if (prevTimeout) clearTimeout(prevTimeout)
-      prevTimeout = setTimeout(() => {
-        updateText('<hanging>')
-      }, 1002)
-    }
-    if (e.data.type === 'stats') {
-      updateTextGpuStats(e.data.stats)
-      // worldRenderer.rendererDevice = `${e.data.device} WebGL data: ${WorldRendererThree.getRendererInfo(new THREE.WebGLRenderer())}`
-    }
-  })

454-458: Fix URL search parameter update.

The code sets the search parameter to the old value value instead of the new value newVal.

 if (rendererParamsGui[key]?.qsReload) {
   const searchParams = new URLSearchParams(window.location.search)
-  searchParams.set(key, String(value))
+  searchParams.set(key, String(newVal))
   window.location.search = searchParams.toString()
 }
src/appViewerLoad.ts (3)

2-2: Appropriate import for new WebGPU renderer

The import of the WebGPU backend matches the PR objective to implement a new rendering engine using WebGPU.


11-11: Correctly enables WebGPU backend loading

Previously commented code is now active, properly loading the WebGPU renderer when selected in options.


37-45:

Details

❓ Verification inconclusive

Potential undefined variable in renderer change handler

The subscription handler references a global bot variable that isn't defined or imported in this file. This could lead to runtime errors if the variable isn't available when the handler executes.


🏁 Script executed:

#!/bin/bash
# Check if 'bot' is defined as a global variable elsewhere
rg -A 2 -B 2 "window.bot\s*=|global.bot\s*=" --glob="*.{js,ts,jsx,tsx}"

Length of output: 794


Attention: Global 'bot' Initialization Clarification Needed

The subscription handler in src/appViewerLoad.ts references bot without a local declaration. Our search shows that bot is defined as a global variable in both src/index.ts and src/globals.js (via assignments to window.bot). Although this confirms that a global bot is present, relying on an implicit global can be fragile if the initialization order isn’t strictly guaranteed.

To strengthen the code:

  • Explicit Global Reference: Consider referencing window.bot instead of bot directly to make the dependency clearer.
  • Documentation/Declaration: Add a global declaration or an inline comment that documents the source and expected initialization order of bot.
  • Initialization Order Review: Verify that the code initializing bot always runs before the subscription handler in src/appViewerLoad.ts fires.
src/index.ts (4)

94-94: Appropriate imports for server management

The imports correctly include the new server management functions that replace the previous implementation.


200-200: Improved server termination with proper cleanup

Using destroyLocalServerMain(false) is more robust than simply setting variables to undefined as it properly terminates the worker and cleans up resources.


346-346: Improved server initialization with better encapsulation

Obtaining the CustomClient from startLocalServerMain promotes better separation of concerns compared to the previous approach.


706-706: Improved encapsulation with new startWithBot method

Using the new appViewer.startWithBot() method encapsulates the renderer initialization logic, which is a good practice for maintainability.

src/appViewer.ts (3)

194-194: Improved code organization in resetBackend

The change simplifies the method by delegating the cleanup logic to disconnectBackend, which is good for code maintainability.


221-226: Good consolidation of cleanup logic

Updating the disconnectBackend method to handle the state cleanup based on the cleanState parameter consolidates related functionality in one place, which is a good practice.


235-235: Simplified renderer state initialization

The direct assignment using proxy(getDefaultRendererState()) is cleaner than the previous approach and ensures a fresh renderer state.

Comment on lines +430 to +438
const mainLoop = () => {
requestAnimationFrame(mainLoop)
updates++
}
mainLoop()
setInterval(() => {
updateText2(`Main Loop: ${updates}`)
updates = 0
}, 1000)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Clean up interval timer in addWebgpuDebugUi.

The setInterval is created but never cleared, which could lead to memory leaks if the component is destroyed and recreated.

-  setInterval(() => {
+  const statsInterval = setInterval(() => {
     updateText2(`Main Loop: ${updates}`)
     updates = 0
   }, 1000)
+  
+  // Clean up on abort
+  worldRenderer.abortController.signal.addEventListener('abort', () => {
+    clearInterval(statsInterval);
+  })
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const mainLoop = () => {
requestAnimationFrame(mainLoop)
updates++
}
mainLoop()
setInterval(() => {
updateText2(`Main Loop: ${updates}`)
updates = 0
}, 1000)
const mainLoop = () => {
requestAnimationFrame(mainLoop)
updates++
}
mainLoop()
const statsInterval = setInterval(() => {
updateText2(`Main Loop: ${updates}`)
updates = 0
}, 1000)
// Clean up on abort
worldRenderer.abortController.signal.addEventListener('abort', () => {
clearInterval(statsInterval);
})

Comment on lines +115 to +123
addWebgpuListener (type: string, listener: (data: any) => void) {
void this.readyWorkerPromise.then(() => {
this.worker!.addEventListener('message', (e: any) => {
if (e.data.type === type) {
listener(e.data)
}
})
})
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Improve event listener management to prevent memory leaks.

The addWebgpuListener method adds event listeners without providing a way to remove them when the renderer is destroyed, which could lead to memory leaks if many listeners are added.

Create a storage for the listeners and clean them up in the destroy method:

 export class WorldRendererWebgpu extends WorldRendererCommon {
   // Add a new property to store listeners
+  private messageListeners: Array<{ type: string, handler: (e: any) => void }> = [];
   
   // Modify the addWebgpuListener method
   addWebgpuListener (type: string, listener: (data: any) => void) {
     void this.readyWorkerPromise.then(() => {
-      this.worker!.addEventListener('message', (e: any) => {
+      const handler = (e: any) => {
         if (e.data.type === type) {
           listener(e.data)
         }
-      })
+      };
+      this.worker!.addEventListener('message', handler);
+      this.messageListeners.push({ type, handler });
     })
   }
   
   // Modify the destroy method to clean up listeners
   override destroy () {
     // Existing code...
+    // Clean up message listeners
+    if (this.worker) {
+      for (const { handler } of this.messageListeners) {
+        this.worker.removeEventListener('message', handler);
+      }
+      this.messageListeners = [];
+    }
     // Rest of destroy method...
   }
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
addWebgpuListener (type: string, listener: (data: any) => void) {
void this.readyWorkerPromise.then(() => {
this.worker!.addEventListener('message', (e: any) => {
if (e.data.type === type) {
listener(e.data)
}
})
})
}
export class WorldRendererWebgpu extends WorldRendererCommon {
// Existing properties...
// Add a new property to store listeners
private messageListeners: Array<{ type: string, handler: (e: any) => void }> = [];
// Other class members...
addWebgpuListener(type: string, listener: (data: any) => void) {
void this.readyWorkerPromise.then(() => {
const handler = (e: any) => {
if (e.data.type === type) {
listener(e.data);
}
};
this.worker!.addEventListener('message', handler);
this.messageListeners.push({ type, handler });
});
}
override destroy() {
// Existing cleanup code...
// Clean up message listeners
if (this.worker) {
for (const { handler } of this.messageListeners) {
this.worker.removeEventListener('message', handler);
}
this.messageListeners = [];
}
// Rest of destroy method...
}
// Other class methods...
}

Comment on lines +221 to +223
const blob = await fetch(this.resourcesManager.currentResources!.blocksAtlasParser.latestImage).then(async (res) => res.blob())
this.webgpuChannel.updateTexture(blob)
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add error handling for texture update fetch operation.

Similar to the issue at line 273, this fetch operation lacks error handling which could lead to unhandled promise rejections.

-  const blob = await fetch(this.resourcesManager.currentResources!.blocksAtlasParser.latestImage).then(async (res) => res.blob())
-  this.webgpuChannel.updateTexture(blob)
+  try {
+    const response = await fetch(this.resourcesManager.currentResources!.blocksAtlasParser.latestImage);
+    if (!response.ok) {
+      throw new Error(`Failed to fetch texture: ${response.status} ${response.statusText}`);
+    }
+    const blob = await response.blob();
+    this.webgpuChannel.updateTexture(blob);
+  } catch (error) {
+    console.error('Failed to update texture:', error);
+    this.issueReporter.reportProblem(false, `Failed to update textures: ${error.message}`);
+  }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const blob = await fetch(this.resourcesManager.currentResources!.blocksAtlasParser.latestImage).then(async (res) => res.blob())
this.webgpuChannel.updateTexture(blob)
}
try {
const response = await fetch(this.resourcesManager.currentResources!.blocksAtlasParser.latestImage);
if (!response.ok) {
throw new Error(`Failed to fetch texture: ${response.status} ${response.statusText}`);
}
const blob = await response.blob();
this.webgpuChannel.updateTexture(blob);
} catch (error) {
console.error('Failed to update texture:', error);
this.issueReporter.reportProblem(false, `Failed to update textures: ${error.message}`);
}
}

Comment on lines +39 to +44
subscribeKey(options, 'activeRenderer', () => {
if (appViewer.currentDisplay === 'world' && bot) {
appViewer.resetBackend(true)
loadBackend()
void appViewer.startWithBot()
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add error handling for asynchronous operation

The code calls appViewer.startWithBot() with void which swallows any errors that might occur during this asynchronous operation.

Consider adding proper error handling:

- void appViewer.startWithBot()
+ try {
+   void appViewer.startWithBot()
+ } catch (error) {
+   console.error('Error restarting with new renderer:', error)
+ }

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines 217 to +220
if (singleplayer && !fsState.inMemorySave) {
possiblyCleanHandle(() => {
// todo: this is not enough, we need to wait for all async operations to finish
})
// possiblyCleanHandle(() => {
// // todo: this is not enough, we need to wait for all async operations to finish
// })
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Commented out cleanup code may lead to resource leaks

The previous cleanup logic using possiblyCleanHandle has been commented out without a clear replacement. This could potentially lead to resource leaks.


🏁 Script executed:

#!/bin/bash
# Check what possiblyCleanHandle was doing and if its functionality is replaced
rg -A 10 "possiblyCleanHandle\s*=" --glob="*.{js,ts,jsx,tsx}"

Length of output: 788


Critical: Address the removal of cleanup logic in src/index.ts
The previously active cleanup via possiblyCleanHandle is now commented out (lines 217–220 in src/index.ts), and there’s no clear replacement. Since possiblyCleanHandle (defined in src/integratedServer/browserfsShared.ts) handles asynchronous cleanup via browserfs.configure, eliminating its invocation risks leaving async operations unfinished and may create resource leaks.

  • Action items:
    • Reinstate and adjust cleanup: Re-enable possiblyCleanHandle with proper synchronization to ensure all async operations complete.
    • Or remove safely: If the cleanup logic is deemed unnecessary, remove the commented-out code entirely and update documentation accordingly to reflect the change.

Comment on lines +158 to +162
async startWithBot () {
const renderDistance = miscUiState.singleplayer ? options.renderDistance : options.multiplayerRenderDistance
await this.startWorld(bot.world, renderDistance)
this.worldView!.listenToBot(bot)
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

New method lacks error handling and uses undeclared global variable

The new startWithBot method references a global bot variable that isn't imported in this file. Additionally, it lacks error handling for potential issues during world initialization.

Consider adding proper variable declaration and error handling:

async startWithBot () {
+  if (!window.bot) {
+    throw new Error('Bot is not initialized')
+  }
  const renderDistance = miscUiState.singleplayer ? options.renderDistance : options.multiplayerRenderDistance
-  await this.startWorld(bot.world, renderDistance)
-  this.worldView!.listenToBot(bot)
+  try {
+    await this.startWorld(window.bot.world, renderDistance)
+    this.worldView!.listenToBot(window.bot)
+  } catch (error) {
+    console.error('Error starting world with bot:', error)
+    throw error
+  }
}

🏁 Script executed:

#!/bin/bash
# Check how bot is defined and accessed elsewhere in the codebase
rg -A 2 -B 2 "window.bot\s*=|global.bot\s*=" --glob="*.{js,ts,jsx,tsx}"

Length of output: 794


Attention: Global Variable Reference and Error Handling in startWithBot

After verifying the codebase, it’s clear that the bot instance is defined on the global scope as window.bot (see references in src/index.ts and src/globals.js), so direct usage of bot in this method is incorrect. Additionally, the lack of error handling around the asynchronous call to startWorld may lead to unhandled exceptions if issues occur during initialization.

Please update the startWithBot method to:

  • Explicitly reference window.bot instead of the undeclared bot.
  • Add an initialization check (e.g., verify that window.bot exists).
  • Wrap the asynchronous calls in a try/catch block to capture and log any errors.

Below is a suggested diff:

async startWithBot () {
+  if (!window.bot) {
+    throw new Error('Bot is not initialized.');
+  }
  const renderDistance = miscUiState.singleplayer ? options.renderDistance : options.multiplayerRenderDistance
-  await this.startWorld(bot.world, renderDistance)
-  this.worldView!.listenToBot(bot)
+  try {
+    await this.startWorld(window.bot.world, renderDistance)
+    this.worldView!.listenToBot(window.bot)
+  } catch (error) {
+    console.error('Error starting world with bot:', error)
+    throw error
+  }
}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
async startWithBot () {
const renderDistance = miscUiState.singleplayer ? options.renderDistance : options.multiplayerRenderDistance
await this.startWorld(bot.world, renderDistance)
this.worldView!.listenToBot(bot)
}
async startWithBot () {
if (!window.bot) {
throw new Error('Bot is not initialized.');
}
const renderDistance = miscUiState.singleplayer ? options.renderDistance : options.multiplayerRenderDistance
try {
await this.startWorld(window.bot.world, renderDistance)
this.worldView!.listenToBot(window.bot)
} catch (error) {
console.error('Error starting world with bot:', error)
throw error
}
}

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (3)
renderer/viewer/lib/worldrendererCommon.ts (3)

469-485: Unify out-of-range time checks with error throwing logic.
Here, the code gracefully handles timeOfDay < 0 || timeOfDay > 24000, while line 720 explicitly throws an error for the same condition. Consider consolidating into a single approach (error or fallback) to prevent confusion.


542-542: Minor formatting nitpick in stat text.
String formatting includes both “ms” and “ss” units, which might be slightly unclear. Consider clarifying or unifying the display for better readability.


593-593: Consolidate repeated logic for setting allChunksFinished.
This line reassigns this.allChunksFinished, while checkAllFinished() and other lines (e.g. line 703) also manage it. Centralizing the logic in checkAllFinished() (or a single function) can reduce confusion and maintain consistency.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between fe590bf and 809da7c.

📒 Files selected for processing (3)
  • renderer/viewer/lib/mesher/mesher.ts (5 hunks)
  • renderer/viewer/lib/worldrendererCommon.ts (7 hunks)
  • renderer/viewer/webgpu/graphicsBackendWebgpu.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • renderer/viewer/lib/mesher/mesher.ts
  • renderer/viewer/webgpu/graphicsBackendWebgpu.ts
🧰 Additional context used
🧬 Code Definitions (1)
renderer/viewer/lib/worldrendererCommon.ts (1)
renderer/viewer/lib/ui/newStats.ts (2)
  • addNewStat (7-36)
  • updateStatText (82-85)
🔇 Additional comments (5)
renderer/viewer/lib/worldrendererCommon.ts (5)

19-19: Adopting new UI stat utilities looks good.
No issues found with the expanded import of addNewStat, removeAllStats, removeStat, and updateStatText. This aligns with the overall stats-tracking approach in the codebase.


56-56: Initialization of day time indicator.
Defining timeOfTheDay = 0 is a straightforward and clear default.


160-161: Stat creation for downloaded chunks.
Using addNewStat to visualize the "downloaded-chunks" metric is consistent with your existing stat-based UI.


164-168: Potential memory leak from setInterval.
A setInterval is created but never cleared, which may cause a memory leak if multiple renderer instances are constructed. This issue was previously flagged in an older review. Consider storing the interval ID as a class property and calling clearInterval in destroy() to avoid leaks.

Example fix:

+ private geometryStatsInterval?: number

 constructor(...) {
   ...
-  setInterval(() => {
+  this.geometryStatsInterval = setInterval(() => {
     this.geometryReceiveCountPerSec = Object.values(this.geometryReceiveCount).reduce((acc, curr) => acc + curr, 0)
     this.geometryReceiveCount = {}
     this.updateChunksStats()
   }, 500)
 }

 destroy() {
   ...
+  if (this.geometryStatsInterval) clearInterval(this.geometryStatsInterval)
 }

725-725: Verify redundant out-of-range checks.
Because line 720 already throws an error if timeOfDay is out of range, ensure you truly need this additional assignment logic for negative or beyond 24000 values.

Would you like to confirm whether any code path sets timeOfTheDay to an invalid range, or should we remove the no-op block in getMesherConfig()?

@zardoy zardoy changed the title New realisation of renderer on WebGPU (webgpu.mcraft.fun) New FAST renderer on clean WebGPU try: -> webgpu.mcraft.fun <- Apr 6, 2025
@zardoy
Copy link
Owner

zardoy commented Apr 6, 2025

@coderabbitai pause

@coderabbitai
Copy link

coderabbitai bot commented Apr 6, 2025

✅ Actions performed

Reviews paused.

@github-actions
Copy link

github-actions bot commented Apr 7, 2025

Deployed to Vercel Preview: https://prismarine-axi4apbgu-zaro.vercel.app
Playground
Storybook

@github-actions
Copy link

github-actions bot commented Apr 7, 2025

Deployed to Vercel Preview: https://prismarine-p1admyy31-zaro.vercel.app
Playground
Storybook

@Alexander-Peterson
Copy link

Is their a download fo the modified minecraft web client of this site: https://prismarine-pho51nlw6-zaro.vercel.app/

@zardoy
Copy link
Owner

zardoy commented Apr 15, 2025

Is their a download fo the modified minecraft web client of this site: https://prismarine-pho51nlw6-zaro.vercel.app/

You can do it yourself by checking out the branch and running build-single-file npm script. I might add it deploy workflow as well so you can just download a single html file

@github-actions
Copy link

Deployed to Vercel Preview: https://prismarine-7s5ojgyk2-zaro.vercel.app
Playground
Storybook

@zardoy zardoy changed the title New FAST renderer on clean WebGPU try: -> webgpu.mcraft.fun <- New FAST renderer on clean WebGPU try: (webgpu.mcraft.fun) Jun 16, 2025
@zardoy zardoy changed the title New FAST renderer on clean WebGPU try: (webgpu.mcraft.fun) New FAST renderer on pure WebGPU Dec 28, 2025
@zardoy
Copy link
Owner

zardoy commented Dec 28, 2025

/deploy

@github-actions
Copy link

Deployed to Vercel Preview: https://prismarine-mq41ttsbv-zaro.vercel.app
Playground
Storybook

@zardoy zardoy changed the title New FAST renderer on pure WebGPU New FAST renderer on pure WebGPU try: webgpu.mcraft.fun Dec 28, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants