From 31e8de0b9f0e2651f95221b59cb61c9a6af0fadf Mon Sep 17 00:00:00 2001 From: ilammers Date: Wed, 24 Jun 2026 20:26:12 -0400 Subject: [PATCH 1/2] Port to MC 26.2 Bring over the MC 26.2 port changes developed in the standalone advanced-xray-26.2-fabric repo: updated gradle wrapper/properties, reworked ScanController and OutlineRender, and related screen/platform fixes. --- common/src/main/java/pro/mikey/xray/XRay.java | 4 +- .../pro/mikey/xray/core/OutlineRender.java | 223 +++++--- .../pro/mikey/xray/core/ScanController.java | 514 +++++++++--------- .../mikey/xray/screens/FindBlockScreen.java | 4 +- .../pro/mikey/xray/screens/HelpScreen.java | 2 +- .../xray/screens/ScanConfigureScreen.java | 8 +- .../mikey/xray/screens/ScanManageScreen.java | 10 +- common/src/main/resources/xray.accesswidener | 3 + .../pro/mikey/xray/fabric/XRayFabric.java | 4 +- gradle.properties | 8 +- gradle/wrapper/gradle-wrapper.properties | 2 +- gradlew | 0 gradlew.bat | 178 +++--- .../pro/mikey/xray/neoforge/XRayNeoForge.java | 4 +- 14 files changed, 532 insertions(+), 432 deletions(-) mode change 100755 => 100644 gradlew diff --git a/common/src/main/java/pro/mikey/xray/XRay.java b/common/src/main/java/pro/mikey/xray/XRay.java index 429237d2..71c8cfe6 100644 --- a/common/src/main/java/pro/mikey/xray/XRay.java +++ b/common/src/main/java/pro/mikey/xray/XRay.java @@ -43,13 +43,13 @@ public void onOpenGuiKeyPressed() { return; } - Minecraft.getInstance().setScreen(new ScanManageScreen()); + Minecraft.getInstance().gui.setScreen(new ScanManageScreen()); } private boolean minecraftNotReady() { Minecraft mc = Minecraft.getInstance(); - return mc.player == null || Minecraft.getInstance().screen != null || Minecraft.getInstance().level == null; + return mc.player == null || mc.gui.screen != null || mc.level == null; } public static Identifier id(String path) { diff --git a/common/src/main/java/pro/mikey/xray/core/OutlineRender.java b/common/src/main/java/pro/mikey/xray/core/OutlineRender.java index 7060cc0c..7e1245d3 100644 --- a/common/src/main/java/pro/mikey/xray/core/OutlineRender.java +++ b/common/src/main/java/pro/mikey/xray/core/OutlineRender.java @@ -1,91 +1,130 @@ package pro.mikey.xray.core; +import com.mojang.blaze3d.PrimitiveTopology; import com.mojang.blaze3d.buffers.GpuBuffer; import com.mojang.blaze3d.buffers.GpuBufferSlice; -import com.mojang.blaze3d.pipeline.BlendFunction; +import com.mojang.blaze3d.pipeline.DepthStencilState; import com.mojang.blaze3d.pipeline.RenderPipeline; +import com.mojang.blaze3d.pipeline.RenderTarget; +import com.mojang.blaze3d.platform.CompareOp; import com.mojang.blaze3d.systems.RenderPass; import com.mojang.blaze3d.systems.RenderSystem; import com.mojang.blaze3d.textures.GpuTextureView; import com.mojang.blaze3d.vertex.*; import net.minecraft.client.Minecraft; -import net.minecraft.client.renderer.DynamicUniforms; import net.minecraft.client.renderer.RenderPipelines; -import net.minecraft.client.renderer.ShapeRenderer; -import net.minecraft.resources.Identifier; import net.minecraft.world.level.ChunkPos; import net.minecraft.world.phys.Vec3; -import net.minecraft.world.phys.shapes.Shapes; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.joml.Matrix4f; import org.joml.Matrix4fStack; -import org.joml.Vector3f; -import org.joml.Vector4f; -import org.lwjgl.opengl.GL11; import java.io.Closeable; import java.util.*; public class OutlineRender { - private static final RenderSystem.AutoStorageIndexBuffer indices = RenderSystem.getSequentialBuffer(VertexFormat.Mode.LINES); + private static final Logger LOGGER = LogManager.getLogger(); + + // 26.2: Use PrimitiveTopology.LINES for the index buffer + private static final RenderSystem.AutoStorageIndexBuffer indices = RenderSystem.getSequentialBuffer(PrimitiveTopology.LINES); private static final Map vertexBuffers = new HashMap<>(); private static final Set chunksToRefresh = Collections.synchronizedSet(new HashSet<>()); - public static void renderBlocks(PoseStack poseStack) { - if (!ScanController.INSTANCE.isXRayActive() || Minecraft.getInstance().player == null) { - return; + // Lazy-initialized pipeline to ensure access widener is applied first + private static RenderPipeline xrayLinesPipeline = null; + + private static RenderPipeline getXrayPipeline() { + if (xrayLinesPipeline == null) { + // 26.2 port: depth test is now a property of the pipeline, not a global GL state. + // The old code globally disabled GL_DEPTH_TEST around the draw to get the see-through-walls + // effect; raw GL calls no longer function under the new Vulkan backend, so we bake + // CompareOp.ALWAYS into a derived LINES pipeline to always pass depth test. + xrayLinesPipeline = RenderPipelines.register( + RenderPipeline.builder(RenderPipelines.LINES_SNIPPET) + .withLocation("xray/pipeline/xray_lines") + .withDepthStencilState(Optional.of(new DepthStencilState(CompareOp.ALWAYS_PASS, false))) + .build() + ); } + return xrayLinesPipeline; + } - if (ScanController.INSTANCE.syncRenderList.isEmpty()) { - return; - } + // Vertex format for lines: position, color, normal, line width + private static final int VERTICES_PER_BLOCK = 24; // 12 edges * 2 vertices each + private static final float LINE_WIDTH = 2.0f; - if (!chunksToRefresh.isEmpty()) { - // Clear the vertex buffers for the chunks that need to be refreshed - for (ChunkPos pos : chunksToRefresh) { - VBOHolder holder = vertexBuffers.remove(pos); - if (holder != null) { - holder.close(); + public static void renderBlocks(Vec3 cameraPos) { + try { + boolean isActive = ScanController.INSTANCE.isXRayActive(); + boolean hasPlayer = Minecraft.getInstance().player != null; + + if (!isActive || !hasPlayer) { + return; + } + + // Take a snapshot of the render list to avoid concurrent modification + Map> renderListSnapshot; + synchronized (ScanController.INSTANCE.syncRenderList) { + if (ScanController.INSTANCE.syncRenderList.isEmpty()) { + return; } + renderListSnapshot = new HashMap<>(ScanController.INSTANCE.syncRenderList); } - chunksToRefresh.clear(); - } + + if (!chunksToRefresh.isEmpty()) { + // Clear the vertex buffers for the chunks that need to be refreshed + List toRefresh = new ArrayList<>(chunksToRefresh); + chunksToRefresh.clear(); + for (ChunkPos pos : toRefresh) { + VBOHolder holder = vertexBuffers.remove(pos); + if (holder != null) { + holder.close(); + } + } + } - // Clone the entrySet to avoid concurrent modification exceptions - var entries = new ArrayList<>(ScanController.INSTANCE.syncRenderList.entrySet()); + for (var chunkWithBlockData : renderListSnapshot.entrySet()) { + var chunkPos = chunkWithBlockData.getKey(); + var blocksWithProps = chunkWithBlockData.getValue(); - for (var chunkWithBlockData : entries) { - var chunkPos = chunkWithBlockData.getKey(); - var blocksWithProps = chunkWithBlockData.getValue(); + if (blocksWithProps == null || blocksWithProps.isEmpty()) { + continue; + } - if (blocksWithProps.isEmpty()) { - continue; - } + VBOHolder holder = vertexBuffers.get(chunkPos); + if (holder == null) { + // Take a snapshot of the blocks to avoid concurrent modification + var blockPropsClone = new ArrayList<>(blocksWithProps); - VBOHolder holder = vertexBuffers.get(chunkPos); - if (holder == null) { - BufferBuilder bufferBuilder = Tesselator.getInstance().begin(RenderPipelines.LINES.getVertexFormatMode(), RenderPipelines.LINES.getVertexFormat()); + // Calculate buffer size: each block needs 24 vertices, each vertex has POSITION_COLOR_NORMAL_LINE_WIDTH format + // Use 2x buffer size to account for any overhead/alignment requirements + int vertexSize = DefaultVertexFormat.POSITION_COLOR_NORMAL_LINE_WIDTH.getVertexSize(); + int bufferSize = blockPropsClone.size() * VERTICES_PER_BLOCK * vertexSize * 2; - // More concurrent modification exceptions can happen here, so we clone the list - var blockPropsClone = new ArrayList<>(blocksWithProps); + try (ByteBufferBuilder byteBufferBuilder = ByteBufferBuilder.exactlySized(bufferSize)) { + BufferBuilder bufferBuilder = new BufferBuilder(byteBufferBuilder, PrimitiveTopology.LINES, DefaultVertexFormat.POSITION_COLOR_NORMAL_LINE_WIDTH); - for (var blockProps : blockPropsClone) { - if (blockProps == null) { - continue; - } + for (var blockProps : blockPropsClone) { + if (blockProps == null) { + continue; + } - final int x = blockProps.x(), y = blockProps.y(), z = blockProps.z(); + final int x = blockProps.x(), y = blockProps.y(), z = blockProps.z(); - ShapeRenderer.renderShape(poseStack, bufferBuilder, Shapes.block(), x, y, z, blockProps.color(), 1f); - } + // Render block outline - replaces ShapeRenderer.renderShape which was removed in 26.2 + renderBlockOutline(bufferBuilder, x, y, z, blockProps.color()); + } - try (MeshData meshData = bufferBuilder.buildOrThrow()) { - int indexCount = meshData.drawState().indexCount(); - GpuBuffer vertexBuffer = RenderSystem.getDevice() - .createBuffer(() -> "Xray vertex buffer", GpuBuffer.USAGE_VERTEX, meshData.vertexBuffer()); + try (MeshData meshData = bufferBuilder.buildOrThrow()) { + int indexCount = meshData.drawState().indexCount(); + GpuBuffer vertexBuffer = RenderSystem.getDevice() + .createBuffer(() -> "Xray vertex buffer", GpuBuffer.USAGE_VERTEX, meshData.vertexBuffer()); - vertexBuffers.put(chunkPos, new VBOHolder(vertexBuffer, indexCount)); + vertexBuffers.put(chunkPos, new VBOHolder(vertexBuffer, indexCount)); + } } } @@ -94,34 +133,36 @@ public static void renderBlocks(PoseStack poseStack) { continue; } - Vec3 playerPos = Minecraft.getInstance().gameRenderer.getMainCamera().position().reverse(); + // Camera position is passed from the render context Matrix4fStack matrix4fStack = RenderSystem.getModelViewStack(); - GpuTextureView colorTextureView = Minecraft.getInstance().getMainRenderTarget().getColorTextureView(); - GpuTextureView depthTextureView = Minecraft.getInstance().getMainRenderTarget().getDepthTextureView(); + RenderTarget mainRenderTarget = Minecraft.getInstance().gameRenderer.mainRenderTarget(); + GpuTextureView colorTextureView = mainRenderTarget.getColorTextureView(); + GpuTextureView depthTextureView = mainRenderTarget.getDepthTextureView(); matrix4fStack.pushMatrix(); - matrix4fStack.translate((float) playerPos.x(), (float) playerPos.y(), (float) playerPos.z()); - GpuBufferSlice[] gpubufferslice = RenderSystem.getDynamicUniforms().writeTransforms(new DynamicUniforms.Transform(new Matrix4f(matrix4fStack), new Vector4f(1.0F, 1.0F, 1.0F, 1.0F), new Vector3f(), new Matrix4f())); - - GL11.glDisable(GL11.GL_DEPTH_TEST); - RenderSystem.setShaderFog(gpubufferslice[0]); + // Translate by negative camera position to render blocks in world space + matrix4fStack.translate((float) -cameraPos.x, (float) -cameraPos.y, (float) -cameraPos.z); + GpuBufferSlice dynamicTransform = RenderSystem.getDynamicUniforms().writeTransform(new Matrix4f(matrix4fStack)); GpuBuffer gpuBuffer = indices.getBuffer(holder.indexCount); + // Clear depth to 0.0 (far plane in reversed depth) so all fragments pass GREATER_THAN_OR_EQUAL test try (RenderPass renderPass = RenderSystem.getDevice() .createCommandEncoder() - .createRenderPass(() -> "xray", colorTextureView, OptionalInt.empty(), depthTextureView, OptionalDouble.empty())) { + .createRenderPass(() -> "xray", colorTextureView, Optional.empty(), depthTextureView, OptionalDouble.of(0.0))) { + renderPass.setPipeline(RenderPipelines.LINES); RenderSystem.bindDefaultUniforms(renderPass); - renderPass.setVertexBuffer(0, holder.vertexBuffer); + renderPass.setVertexBuffer(0, holder.vertexBuffer.slice()); renderPass.setIndexBuffer(gpuBuffer, indices.type()); - renderPass.setUniform("DynamicTransforms", gpubufferslice[0]); - renderPass.setPipeline(RenderPipelines.LINES); - renderPass.drawIndexed(0, 0, holder.indexCount, 1); + renderPass.setUniform("DynamicTransforms", dynamicTransform); + renderPass.drawIndexed(holder.indexCount, 1, 0, 0, 0); } - GL11.glEnable(GL11.GL_DEPTH_TEST); - matrix4fStack.popMatrix(); + matrix4fStack.popMatrix(); + } + } catch (Exception e) { + LOGGER.error("Error rendering XRay blocks", e); } } @@ -146,6 +187,62 @@ public static void refreshVBOForChunk(ChunkPos pos) { chunksToRefresh.add(pos); } + /** + * Renders a block outline as lines. This replaces ShapeRenderer.renderShape which was removed in MC 26.2. + * A block has 12 edges, and for the LINES pipeline, each edge requires 2 vertices with position, color, normal, and line width. + */ + private static void renderBlockOutline(BufferBuilder buffer, float x, float y, float z, int color) { + float minX = x; + float minY = y; + float minZ = z; + float maxX = x + 1.0f; + float maxY = y + 1.0f; + float maxZ = z + 1.0f; + + // Bottom face edges (Y = minY) + addLineVertex(buffer, minX, minY, minZ, color, 1, 0, 0); + addLineVertex(buffer, maxX, minY, minZ, color, 1, 0, 0); + + addLineVertex(buffer, maxX, minY, minZ, color, 0, 0, 1); + addLineVertex(buffer, maxX, minY, maxZ, color, 0, 0, 1); + + addLineVertex(buffer, maxX, minY, maxZ, color, -1, 0, 0); + addLineVertex(buffer, minX, minY, maxZ, color, -1, 0, 0); + + addLineVertex(buffer, minX, minY, maxZ, color, 0, 0, -1); + addLineVertex(buffer, minX, minY, minZ, color, 0, 0, -1); + + // Top face edges (Y = maxY) + addLineVertex(buffer, minX, maxY, minZ, color, 1, 0, 0); + addLineVertex(buffer, maxX, maxY, minZ, color, 1, 0, 0); + + addLineVertex(buffer, maxX, maxY, minZ, color, 0, 0, 1); + addLineVertex(buffer, maxX, maxY, maxZ, color, 0, 0, 1); + + addLineVertex(buffer, maxX, maxY, maxZ, color, -1, 0, 0); + addLineVertex(buffer, minX, maxY, maxZ, color, -1, 0, 0); + + addLineVertex(buffer, minX, maxY, maxZ, color, 0, 0, -1); + addLineVertex(buffer, minX, maxY, minZ, color, 0, 0, -1); + + // Vertical edges connecting top and bottom + addLineVertex(buffer, minX, minY, minZ, color, 0, 1, 0); + addLineVertex(buffer, minX, maxY, minZ, color, 0, 1, 0); + + addLineVertex(buffer, maxX, minY, minZ, color, 0, 1, 0); + addLineVertex(buffer, maxX, maxY, minZ, color, 0, 1, 0); + + addLineVertex(buffer, maxX, minY, maxZ, color, 0, 1, 0); + addLineVertex(buffer, maxX, maxY, maxZ, color, 0, 1, 0); + + addLineVertex(buffer, minX, minY, maxZ, color, 0, 1, 0); + addLineVertex(buffer, minX, maxY, maxZ, color, 0, 1, 0); + } + + private static void addLineVertex(BufferBuilder buffer, float x, float y, float z, int color, float nx, float ny, float nz) { + buffer.addVertex(x, y, z).setColor(color).setNormal(nx, ny, nz).setLineWidth(LINE_WIDTH); + } + private record VBOHolder(GpuBuffer vertexBuffer, int indexCount) implements Closeable { @Override diff --git a/common/src/main/java/pro/mikey/xray/core/ScanController.java b/common/src/main/java/pro/mikey/xray/core/ScanController.java index 1f5dcaee..8c099355 100644 --- a/common/src/main/java/pro/mikey/xray/core/ScanController.java +++ b/common/src/main/java/pro/mikey/xray/core/ScanController.java @@ -1,257 +1,257 @@ -package pro.mikey.xray.core; - -import net.minecraft.client.Minecraft; -import net.minecraft.core.BlockPos; -import net.minecraft.network.chat.Component; -import net.minecraft.util.Mth; -import net.minecraft.world.level.ChunkPos; -import net.minecraft.world.level.Level; -import net.minecraft.world.level.block.Block; -import net.minecraft.world.level.block.Blocks; -import net.minecraft.world.level.block.state.BlockState; -import org.jetbrains.annotations.NotNull; -import pro.mikey.xray.XRay; -import pro.mikey.xray.core.scanner.ScanStore; -import pro.mikey.xray.core.scanner.ScanType; - -import java.util.*; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.ThreadFactory; - -public enum ScanController { - INSTANCE; - - // Ensure this thread is shutdown when the game exists. - private final ExecutorService SCANNER = Executors.newFixedThreadPool(4, new ThreadFactory() { - @Override - public Thread newThread(@NotNull Runnable r) { - Thread thread = new Thread(r, "XRay-Scanner"); - thread.setDaemon(true); // Daemon threads do not prevent the JVM from exiting - return thread; - } - }); - - private final int maxStepsToScan = 5; - - // Block blackList - // Todo: move this to a configurable thing - public static final Set blackList = new HashSet<>() {{ - add(Blocks.AIR); - add(Blocks.BEDROCK); - add(Blocks.STONE); - add(Blocks.GRASS_BLOCK); - add(Blocks.DIRT); - }}; - - - public final Map> syncRenderList = Collections.synchronizedMap(new HashMap<>()); // this is accessed by threads - private ChunkPos lastChunkPos = null; - - public final ScanStore scanStore = new ScanStore(); - -// private BlockStore blockStore = new BlockStore(); - - // Thread management - - // Draw states - private boolean xrayActive = false; // Off by default - - public void init() { - this.scanStore.load(); - } - -// public static BlockStore getBlockStore() { -// return blockStore; -// } - - // Public accessors - public boolean isXRayActive() { - return this.xrayActive && Minecraft.getInstance().level != null && Minecraft.getInstance().player != null; - } - - public void toggleXRay() { - if (!xrayActive) // enable drawing - { - syncRenderList.clear(); // first, clear the buffer - xrayActive = true; // then, enable drawing - requestBlockFinder(true); // finally, force a refresh - - if (!XRay.config().showOverlay.get() && Minecraft.getInstance().player != null) - Minecraft.getInstance().player.sendSystemMessage(Component.translatable("xray.toggle.activated")); - } else // disable drawing - { - if (!XRay.config().showOverlay.get() && Minecraft.getInstance().player != null) - Minecraft.getInstance().player.sendSystemMessage(Component.translatable("xray.toggle.deactivated")); - - xrayActive = false; - } - } - - public boolean isLavaActive() { - return XRay.config().lavaActive.get(); - } - - public void toggleLava() { - XRay.config().lavaActive.set(!XRay.config().lavaActive.get()); - } - - public int getRadius() { - return Mth.clamp(XRay.config().radius.get(), 0, maxStepsToScan) * 3; - } - - public int getHalfRange() { - return Math.max(0, getRadius() / 2); - } - - public int getVisualRadius() { - return Math.max(1, getRadius()); - } - - public void incrementCurrentDist() { - if (XRay.config().radius.get() < maxStepsToScan) - XRay.config().radius.set(XRay.config().radius.get() + 1); - else - XRay.config().radius.set(0); - } - - public void decrementCurrentDist() { - if (XRay.config().radius.get() > 0) - XRay.config().radius.set(XRay.config().radius.get() - 1); - else - XRay.config().radius.set(maxStepsToScan); - } - - private boolean playerHasMoved() { - if (Minecraft.getInstance().player == null) - return false; - - ChunkPos plyChunkPos = Minecraft.getInstance().player.chunkPosition(); - - return lastChunkPos == null || !lastChunkPos.equals(plyChunkPos); - } - - private void updatePlayerPosition() { - lastChunkPos = Minecraft.getInstance().player.chunkPosition(); - } - - public synchronized void requestBlockFinder(boolean force) { - var player = Minecraft.getInstance().player; - if (player == null) { - return; - } - - if (isXRayActive() && (force || playerHasMoved())) { - updatePlayerPosition(); // since we're about to run, update the last known position - - if (force) { - // Clear the render list if we are forcing a scan - syncRenderList.clear(); - OutlineRender.clearVBOs(); // Clear the VBOs as well - } - - if (this.scanStore.activeScanTargets().isEmpty() && !isLavaActive()) { - return; - } - - int range = this.getHalfRange(); - - List chunksToScan = new ArrayList<>(); - var playerChunkPos = player.chunkPosition(); - for (int i = playerChunkPos.x() - range; i <= playerChunkPos.x() + range; i++) { - for (int j = playerChunkPos.z() - range; j <= playerChunkPos.z() + range; j++) { - chunksToScan.add(new ChunkPos(i, j)); - } - } - - // Sort the chunks by distance to the player - chunksToScan.sort(Comparator.comparingDouble(chunk -> chunk.distanceSquared(playerChunkPos))); - - var knownChunks = syncRenderList.keySet(); - - // New chunks - var newChunks = chunksToScan.stream().filter(chunk -> !knownChunks.contains(chunk)).toList(); - var removedChunks = knownChunks.stream().filter(chunk -> !chunksToScan.contains(chunk)).toList(); - - if (!removedChunks.isEmpty()) { - OutlineRender.clearVBOsFor(removedChunks); - } - - // Push the new chunks to the scanner, remove the old ones from the render list - for (ChunkPos chunk : removedChunks) { - syncRenderList.remove(chunk); - } - - for (ChunkPos chunk : newChunks) { - SCANNER.submit(new ChunkScanTask(player.level(), chunk)); - } - } - } - - public static void onBlockChange(Level level, BlockPos pos, BlockState state) { - if (!ScanController.INSTANCE.isXRayActive()) { - return; - } - - var chunkPos = new ChunkPos(pos.getX(), pos.getZ()); - Set outlineRenderTargets = ScanController.INSTANCE.syncRenderList.get(chunkPos); - if (outlineRenderTargets == null) { - // It's not being rendered, so we don't care - return; - } - - // We're now air baby! - if (state.isAir()) { - // Remove the block from the render list - var removed = outlineRenderTargets.removeIf(target -> target.x() == pos.getX() && target.y() == pos.getY() && target.z() == pos.getZ()); - if (removed) { - // We need to tell the outline render to refresh the VBO for this chunk - OutlineRender.refreshVBOForChunk(chunkPos); - } - - return; - } - - if (ScanController.INSTANCE.isLavaActive() && state.is(Blocks.LAVA)) { - // We're actively looking at this chunk so let's inject this block - outlineRenderTargets.add(new OutlineRenderTarget(pos.getX(), pos.getY(), pos.getZ(), 0xff0000)); - - // Tell the VBO to refresh for this chunk - OutlineRender.refreshVBOForChunk(chunkPos); - } - - // Otherwise, do we have scantarget in the active list of things to find? - var noMatchesFound = true; - - Set activeScanTargets = ScanController.INSTANCE.scanStore.activeScanTargets(); - for (var scanType : activeScanTargets) { - if (scanType.matches(level, pos, state, state.getFluidState())) { - // We need to tell the render system to refresh. We should manually add this black to the renderlist - // We're actively looking at this chunk so let's inject this block - outlineRenderTargets.add(new OutlineRenderTarget(pos.getX(), pos.getY(), pos.getZ(), scanType.colorInt())); - - // Tell the VBO to refresh for this chunk - OutlineRender.refreshVBOForChunk(chunkPos); - noMatchesFound = false; // We found a match, so we can stop checking - break; // We found a match, so we can stop checking - } - } - - // If no matches are found AND the block pos is currently in the render list, we need to remove it and ask the chunk to refresh - var blockFromRenderList = outlineRenderTargets.stream() - .filter(target -> target.x() == pos.getX() && target.y() == pos.getY() && target.z() == pos.getZ()) - .findFirst(); - - if (blockFromRenderList.isEmpty()) { - return; - } - - if (noMatchesFound) { - // We didn't find any matches, so we need to remove the block from the render list - outlineRenderTargets.remove(blockFromRenderList.get()); - - // Tell the VBO to refresh for this chunk - OutlineRender.refreshVBOForChunk(chunkPos); - } - } -} +package pro.mikey.xray.core; + +import net.minecraft.client.Minecraft; +import net.minecraft.core.BlockPos; +import net.minecraft.network.chat.Component; +import net.minecraft.util.Mth; +import net.minecraft.world.level.ChunkPos; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.state.BlockState; +import org.jetbrains.annotations.NotNull; +import pro.mikey.xray.XRay; +import pro.mikey.xray.core.scanner.ScanStore; +import pro.mikey.xray.core.scanner.ScanType; + +import java.util.*; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.ThreadFactory; + +public enum ScanController { + INSTANCE; + + // Ensure this thread is shutdown when the game exists. + private final ExecutorService SCANNER = Executors.newFixedThreadPool(4, new ThreadFactory() { + @Override + public Thread newThread(@NotNull Runnable r) { + Thread thread = new Thread(r, "XRay-Scanner"); + thread.setDaemon(true); // Daemon threads do not prevent the JVM from exiting + return thread; + } + }); + + private final int maxStepsToScan = 5; + + // Block blackList + // Todo: move this to a configurable thing + public static final Set blackList = new HashSet<>() {{ + add(Blocks.AIR); + add(Blocks.BEDROCK); + add(Blocks.STONE); + add(Blocks.GRASS_BLOCK); + add(Blocks.DIRT); + }}; + + + public final Map> syncRenderList = Collections.synchronizedMap(new HashMap<>()); // this is accessed by threads + private ChunkPos lastChunkPos = null; + + public final ScanStore scanStore = new ScanStore(); + +// private BlockStore blockStore = new BlockStore(); + + // Thread management + + // Draw states + private boolean xrayActive = false; // Off by default + + public void init() { + this.scanStore.load(); + } + +// public static BlockStore getBlockStore() { +// return blockStore; +// } + + // Public accessors + public boolean isXRayActive() { + return this.xrayActive && Minecraft.getInstance().level != null && Minecraft.getInstance().player != null; + } + + public void toggleXRay() { + if (!xrayActive) // enable drawing + { + syncRenderList.clear(); // first, clear the buffer + xrayActive = true; // then, enable drawing + requestBlockFinder(true); // finally, force a refresh + + if (!XRay.config().showOverlay.get() && Minecraft.getInstance().player != null) + Minecraft.getInstance().player.sendSystemMessage(Component.translatable("xray.toggle.activated")); + } else // disable drawing + { + if (!XRay.config().showOverlay.get() && Minecraft.getInstance().player != null) + Minecraft.getInstance().player.sendSystemMessage(Component.translatable("xray.toggle.deactivated")); + + xrayActive = false; + } + } + + public boolean isLavaActive() { + return XRay.config().lavaActive.get(); + } + + public void toggleLava() { + XRay.config().lavaActive.set(!XRay.config().lavaActive.get()); + } + + public int getRadius() { + return Mth.clamp(XRay.config().radius.get(), 0, maxStepsToScan) * 3; + } + + public int getHalfRange() { + return Math.max(0, getRadius() / 2); + } + + public int getVisualRadius() { + return Math.max(1, getRadius()); + } + + public void incrementCurrentDist() { + if (XRay.config().radius.get() < maxStepsToScan) + XRay.config().radius.set(XRay.config().radius.get() + 1); + else + XRay.config().radius.set(0); + } + + public void decrementCurrentDist() { + if (XRay.config().radius.get() > 0) + XRay.config().radius.set(XRay.config().radius.get() - 1); + else + XRay.config().radius.set(maxStepsToScan); + } + + private boolean playerHasMoved() { + if (Minecraft.getInstance().player == null) + return false; + + ChunkPos plyChunkPos = Minecraft.getInstance().player.chunkPosition(); + + return lastChunkPos == null || !lastChunkPos.equals(plyChunkPos); + } + + private void updatePlayerPosition() { + lastChunkPos = Minecraft.getInstance().player.chunkPosition(); + } + + public synchronized void requestBlockFinder(boolean force) { + var player = Minecraft.getInstance().player; + if (player == null) { + return; + } + + if (isXRayActive() && (force || playerHasMoved())) { + updatePlayerPosition(); // since we're about to run, update the last known position + + if (force) { + // Clear the render list if we are forcing a scan + syncRenderList.clear(); + OutlineRender.clearVBOs(); // Clear the VBOs as well + } + + if (this.scanStore.activeScanTargets().isEmpty() && !isLavaActive()) { + return; + } + + int range = this.getHalfRange(); + + List chunksToScan = new ArrayList<>(); + var playerChunkPos = player.chunkPosition(); + for (int i = playerChunkPos.x() - range; i <= playerChunkPos.x() + range; i++) { + for (int j = playerChunkPos.z() - range; j <= playerChunkPos.z() + range; j++) { + chunksToScan.add(new ChunkPos(i, j)); + } + } + + // Sort the chunks by distance to the player + chunksToScan.sort(Comparator.comparingDouble(chunk -> chunk.distanceSquared(playerChunkPos))); + + var knownChunks = syncRenderList.keySet(); + + // New chunks + var newChunks = chunksToScan.stream().filter(chunk -> !knownChunks.contains(chunk)).toList(); + var removedChunks = knownChunks.stream().filter(chunk -> !chunksToScan.contains(chunk)).toList(); + + if (!removedChunks.isEmpty()) { + OutlineRender.clearVBOsFor(removedChunks); + } + + // Push the new chunks to the scanner, remove the old ones from the render list + for (ChunkPos chunk : removedChunks) { + syncRenderList.remove(chunk); + } + + for (ChunkPos chunk : newChunks) { + SCANNER.submit(new ChunkScanTask(player.level(), chunk)); + } + } + } + + public static void onBlockChange(Level level, BlockPos pos, BlockState state) { + if (!ScanController.INSTANCE.isXRayActive()) { + return; + } + + var chunkPos = new ChunkPos(pos.getX(), pos.getZ()); + Set outlineRenderTargets = ScanController.INSTANCE.syncRenderList.get(chunkPos); + if (outlineRenderTargets == null) { + // It's not being rendered, so we don't care + return; + } + + // We're now air baby! + if (state.isAir()) { + // Remove the block from the render list + var removed = outlineRenderTargets.removeIf(target -> target.x() == pos.getX() && target.y() == pos.getY() && target.z() == pos.getZ()); + if (removed) { + // We need to tell the outline render to refresh the VBO for this chunk + OutlineRender.refreshVBOForChunk(chunkPos); + } + + return; + } + + if (ScanController.INSTANCE.isLavaActive() && state.is(Blocks.LAVA)) { + // We're actively looking at this chunk so let's inject this block + outlineRenderTargets.add(new OutlineRenderTarget(pos.getX(), pos.getY(), pos.getZ(), 0xff0000)); + + // Tell the VBO to refresh for this chunk + OutlineRender.refreshVBOForChunk(chunkPos); + } + + // Otherwise, do we have scantarget in the active list of things to find? + var noMatchesFound = true; + + Set activeScanTargets = ScanController.INSTANCE.scanStore.activeScanTargets(); + for (var scanType : activeScanTargets) { + if (scanType.matches(level, pos, state, state.getFluidState())) { + // We need to tell the render system to refresh. We should manually add this black to the renderlist + // We're actively looking at this chunk so let's inject this block + outlineRenderTargets.add(new OutlineRenderTarget(pos.getX(), pos.getY(), pos.getZ(), scanType.colorInt())); + + // Tell the VBO to refresh for this chunk + OutlineRender.refreshVBOForChunk(chunkPos); + noMatchesFound = false; // We found a match, so we can stop checking + break; // We found a match, so we can stop checking + } + } + + // If no matches are found AND the block pos is currently in the render list, we need to remove it and ask the chunk to refresh + var blockFromRenderList = outlineRenderTargets.stream() + .filter(target -> target.x() == pos.getX() && target.y() == pos.getY() && target.z() == pos.getZ()) + .findFirst(); + + if (blockFromRenderList.isEmpty()) { + return; + } + + if (noMatchesFound) { + // We didn't find any matches, so we need to remove the block from the render list + outlineRenderTargets.remove(blockFromRenderList.get()); + + // Tell the VBO to refresh for this chunk + OutlineRender.refreshVBOForChunk(chunkPos); + } + } +} diff --git a/common/src/main/java/pro/mikey/xray/screens/FindBlockScreen.java b/common/src/main/java/pro/mikey/xray/screens/FindBlockScreen.java index 8112973c..7d47840e 100644 --- a/common/src/main/java/pro/mikey/xray/screens/FindBlockScreen.java +++ b/common/src/main/java/pro/mikey/xray/screens/FindBlockScreen.java @@ -37,7 +37,7 @@ public void init() { search.setFocused(true); this.setFocused(search); - addRenderableWidget(Button.builder(Component.translatable("xray.single.cancel"), b -> Minecraft.getInstance().setScreen(new ScanManageScreen())) + addRenderableWidget(Button.builder(Component.translatable("xray.single.cancel"), b -> Minecraft.getInstance().gui.setScreen(new ScanManageScreen())) .pos(getWidth() / 2 + 43, getHeight() / 2 + 84) .size(60, 20) .build()); @@ -118,7 +118,7 @@ public void setSelected(@Nullable BlockSlot entry) { return; } - Minecraft.getInstance().setScreen(new ScanConfigureScreen(entry.getBlock(), FindBlockScreen::new)); + Minecraft.getInstance().gui.setScreen(new ScanConfigureScreen(entry.getBlock(), FindBlockScreen::new)); } void updateEntries(List blocks) { diff --git a/common/src/main/java/pro/mikey/xray/screens/HelpScreen.java b/common/src/main/java/pro/mikey/xray/screens/HelpScreen.java index 13e69d81..2a9166f8 100644 --- a/common/src/main/java/pro/mikey/xray/screens/HelpScreen.java +++ b/common/src/main/java/pro/mikey/xray/screens/HelpScreen.java @@ -28,7 +28,7 @@ public void init() { areas.add(new LinedText("xray.message.help.gui")); areas.add(new LinedText("xray.message.help.warning")); - this.addRenderableWidget(Button.builder(Component.translatable("xray.single.close"), btn -> Minecraft.getInstance().setScreen(new ScanManageScreen())) + this.addRenderableWidget(Button.builder(Component.translatable("xray.single.close"), btn -> Minecraft.getInstance().gui.setScreen(new ScanManageScreen())) .pos((getWidth() / 2) - 100, (getHeight() / 2) + 80) .size(200, 20) .build() diff --git a/common/src/main/java/pro/mikey/xray/screens/ScanConfigureScreen.java b/common/src/main/java/pro/mikey/xray/screens/ScanConfigureScreen.java index f9c37055..d8668ea4 100644 --- a/common/src/main/java/pro/mikey/xray/screens/ScanConfigureScreen.java +++ b/common/src/main/java/pro/mikey/xray/screens/ScanConfigureScreen.java @@ -84,7 +84,7 @@ public void init() { .build()); } - rowHelper.addChild(Button.builder(Component.translatable("xray.single.cancel"), b -> Minecraft.getInstance().setScreen(this.previousScreenCallback.get())) + rowHelper.addChild(Button.builder(Component.translatable("xray.single.cancel"), b -> Minecraft.getInstance().gui.setScreen(this.previousScreenCallback.get())) .size(60, 20) .build()); @@ -145,7 +145,7 @@ private void editBlock() { editingType.name = oreName.getValue(); ScanController.INSTANCE.scanStore.save(); ScanController.INSTANCE.requestBlockFinder(true); - minecraft.setScreen(this.previousScreenCallback.get()); + minecraft.gui.setScreen(this.previousScreenCallback.get()); } private void removeBlock() { @@ -156,7 +156,7 @@ private void removeBlock() { ScanStore scanStore = ScanController.INSTANCE.scanStore; scanStore.removeEntry(editingType); ScanController.INSTANCE.requestBlockFinder(true); - minecraft.setScreen(this.previousScreenCallback.get()); + minecraft.gui.setScreen(this.previousScreenCallback.get()); } private void addBlock() { @@ -174,7 +174,7 @@ private void addBlock() { )); ScanController.INSTANCE.requestBlockFinder(true); - minecraft.setScreen(new ScanManageScreen()); + minecraft.gui.setScreen(new ScanManageScreen()); } @Override diff --git a/common/src/main/java/pro/mikey/xray/screens/ScanManageScreen.java b/common/src/main/java/pro/mikey/xray/screens/ScanManageScreen.java index 11ccf906..c7107c90 100644 --- a/common/src/main/java/pro/mikey/xray/screens/ScanManageScreen.java +++ b/common/src/main/java/pro/mikey/xray/screens/ScanManageScreen.java @@ -78,7 +78,7 @@ public void init() { addRenderableWidget( Button.builder(Component.translatable("xray.input.add"), (btn) -> { - minecraft.setScreen(new FindBlockScreen()); + minecraft.gui.setScreen(new FindBlockScreen()); }) .pos((getWidth() / 2) + 79, getHeight() / 2 - 60) .size(120, 20) @@ -96,7 +96,7 @@ public void init() { return; } - minecraft.setScreen(new ScanConfigureScreen(((BlockItem) handItem.getItem()).getBlock(), ScanManageScreen::new)); + minecraft.gui.setScreen(new ScanConfigureScreen(((BlockItem) handItem.getItem()).getBlock(), ScanManageScreen::new)); }) .pos(getWidth() / 2 + 79, getHeight() / 2 - 38) .size(120, 20) @@ -121,7 +121,7 @@ public void init() { if (result.getType() == HitResult.Type.BLOCK) { Block lookingAt = minecraft.level.getBlockState(result.getBlockPos()).getBlock(); - minecraft.setScreen(new ScanConfigureScreen(lookingAt, ScanManageScreen::new)); + minecraft.gui.setScreen(new ScanConfigureScreen(lookingAt, ScanManageScreen::new)); } else { player.sendSystemMessage(Component.literal("[XRay] " + I18n.get("xray.message.nothing_infront"))); this.onClose(); @@ -160,7 +160,7 @@ public void init() { addRenderableWidget( Button.builder(Component.translatable("xray.single.help"), button -> { - minecraft.setScreen(new HelpScreen()); + minecraft.gui.setScreen(new HelpScreen()); }) .pos(getWidth() / 2 + 79, getHeight() / 2 + 58) .size(60, 20) @@ -272,7 +272,7 @@ public void setSelected(@Nullable ScanManageScreen.ScanEntryScroller.ScanSlot en return; if (mouse.hasShiftDown()) { - Minecraft.getInstance().setScreen(new ScanConfigureScreen(entry.entry, ScanManageScreen::new)); + Minecraft.getInstance().gui.setScreen(new ScanConfigureScreen(entry.entry, ScanManageScreen::new)); return; } diff --git a/common/src/main/resources/xray.accesswidener b/common/src/main/resources/xray.accesswidener index 032ed0a1..2f86a5ac 100644 --- a/common/src/main/resources/xray.accesswidener +++ b/common/src/main/resources/xray.accesswidener @@ -1,5 +1,8 @@ accessWidener v1 official accessible field net/minecraft/client/renderer/RenderPipelines MATRICES_FOG_SNIPPET Lcom/mojang/blaze3d/pipeline/RenderPipeline$Snippet; accessible field net/minecraft/client/renderer/RenderPipelines GLOBALS_SNIPPET Lcom/mojang/blaze3d/pipeline/RenderPipeline$Snippet; +accessible field net/minecraft/client/renderer/RenderPipelines LINES_SNIPPET Lcom/mojang/blaze3d/pipeline/RenderPipeline$Snippet; +accessible method net/minecraft/client/renderer/RenderPipelines register (Lcom/mojang/blaze3d/pipeline/RenderPipeline;)Lcom/mojang/blaze3d/pipeline/RenderPipeline; accessible field net/minecraft/client/KeyMapping key Lcom/mojang/blaze3d/platform/InputConstants$Key; accessible field net/minecraft/client/renderer/LevelRenderer targets Lnet/minecraft/client/renderer/LevelTargetBundle; +accessible field net/minecraft/client/gui/Gui screen Lnet/minecraft/client/gui/screens/Screen; diff --git a/fabric/src/main/java/pro/mikey/xray/fabric/XRayFabric.java b/fabric/src/main/java/pro/mikey/xray/fabric/XRayFabric.java index 2e3808b9..cc3a3795 100644 --- a/fabric/src/main/java/pro/mikey/xray/fabric/XRayFabric.java +++ b/fabric/src/main/java/pro/mikey/xray/fabric/XRayFabric.java @@ -33,12 +33,12 @@ public void onInitializeClient() { } private void renderOverlay(LevelRenderContext levelRenderContext) { - OutlineRender.renderBlocks(levelRenderContext.poseStack()); + OutlineRender.renderBlocks(levelRenderContext.levelState().cameraRenderState.pos); } private void clientTickEvent(Minecraft mc) { - if (mc.player == null || mc.level == null || mc.screen != null) { + if (mc.player == null || mc.level == null || mc.gui.screen != null) { return; } diff --git a/gradle.properties b/gradle.properties index be80199f..2c635e15 100644 --- a/gradle.properties +++ b/gradle.properties @@ -3,8 +3,8 @@ org.gradle.daemon=true org.gradle.parallel=true mod_id=xray -mod_version=26.1.0.1 -minecraft_version=26.1 +mod_version=26.2.0.0 +minecraft_version=26.2 minecraft_version_range=26.1,26.2 # NeoForge @@ -12,7 +12,7 @@ neoforge_version=26.1.0.17-beta neoforge_version_range=26.1.0.0-beta # Fabric -fabric_loader_version=0.18.5 -fabric_api_version=0.144.4+26.1 +fabric_loader_version=0.19.3 +fabric_api_version=0.153.0+26.2 iris_version=7088024 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index dbc3ce4a..5dd3c012 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-9.4.0-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.5.1-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/gradlew b/gradlew old mode 100755 new mode 100644 diff --git a/gradlew.bat b/gradlew.bat index ac1b06f9..107acd32 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -1,89 +1,89 @@ -@rem -@rem Copyright 2015 the original author or authors. -@rem -@rem Licensed under the Apache License, Version 2.0 (the "License"); -@rem you may not use this file except in compliance with the License. -@rem You may obtain a copy of the License at -@rem -@rem https://www.apache.org/licenses/LICENSE-2.0 -@rem -@rem Unless required by applicable law or agreed to in writing, software -@rem distributed under the License is distributed on an "AS IS" BASIS, -@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -@rem See the License for the specific language governing permissions and -@rem limitations under the License. -@rem - -@if "%DEBUG%" == "" @echo off -@rem ########################################################################## -@rem -@rem Gradle startup script for Windows -@rem -@rem ########################################################################## - -@rem Set local scope for the variables with windows NT shell -if "%OS%"=="Windows_NT" setlocal - -set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. -set APP_BASE_NAME=%~n0 -set APP_HOME=%DIRNAME% - -@rem Resolve any "." and ".." in APP_HOME to make it shorter. -for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi - -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" - -@rem Find java.exe -if defined JAVA_HOME goto findJavaFromJavaHome - -set JAVA_EXE=java.exe -%JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto execute - -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:findJavaFromJavaHome -set JAVA_HOME=%JAVA_HOME:"=% -set JAVA_EXE=%JAVA_HOME%/bin/java.exe - -if exist "%JAVA_EXE%" goto execute - -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:execute -@rem Setup the command line - -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar - - -@rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* - -:end -@rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd - -:fail -rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of -rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 - -:mainEnd -if "%OS%"=="Windows_NT" endlocal - -:omega +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/neoforge/src/main/java/pro/mikey/xray/neoforge/XRayNeoForge.java b/neoforge/src/main/java/pro/mikey/xray/neoforge/XRayNeoForge.java index 36f49739..88acfcc9 100644 --- a/neoforge/src/main/java/pro/mikey/xray/neoforge/XRayNeoForge.java +++ b/neoforge/src/main/java/pro/mikey/xray/neoforge/XRayNeoForge.java @@ -44,7 +44,7 @@ private void registerGuiLayer(RegisterGuiLayersEvent event) { } private void onWorldRenderLast(RenderLevelStageEvent.AfterWeather event) { - OutlineRender.renderBlocks(event.getPoseStack()); + OutlineRender.renderBlocks(Minecraft.getInstance().gameRenderer.mainCamera().position()); } public void onClientSetup(FMLClientSetupEvent event) { @@ -61,7 +61,7 @@ public void registerPipeline(RegisterRenderPipelinesEvent event) { public void eventInput(InputEvent.Key event) { Minecraft mc = Minecraft.getInstance(); - if (mc.player == null || Minecraft.getInstance().screen != null || Minecraft.getInstance().level == null) + if (mc.player == null || mc.gui.screen != null || mc.level == null) return; if (XRay.TOGGLE_KEY.consumeClick()) { From 89720aa8067e3c7cd36d15a299d333ec708559ed Mon Sep 17 00:00:00 2001 From: ilammers Date: Wed, 24 Jun 2026 20:26:38 -0400 Subject: [PATCH 2/2] fix: restore executable bit on gradlew --- gradlew | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 gradlew diff --git a/gradlew b/gradlew old mode 100644 new mode 100755