-buildscript {
- repositories {
- mavenCentral()
- }
- dependencies {
- classpath("commons-io:commons-io:2.6")
- }
-}
-
-import java.nio.file.FileVisitResult
-import java.nio.file.Files
-import java.nio.file.Path
-import java.nio.file.SimpleFileVisitor
-import java.nio.file.attribute.BasicFileAttributes
-import java.util.stream.Stream
-import java.util.zip.ZipEntry
-import java.util.zip.ZipInputStream
-import java.util.zip.ZipOutputStream
-
plugins {
- id "architectury-plugin" version "3.0.76"
- id "forgified-fabric-loom" version "0.6.67" apply false
+ id "architectury-plugin" version "3.2-SNAPSHOT"
+ id "dev.architectury.loom" version "0.7.2-SNAPSHOT" apply false
}
architectury {
}
subprojects {
- apply plugin: "forgified-fabric-loom"
+ apply plugin: "dev.architectury.loom"
loom {
silentMojangMappingsLicense()
archivesBaseName = rootProject.name
version = rootProject.mod_version
- tasks.withType(JavaCompile) {
- options.encoding = "UTF-8"
- }
-}
-
-task buildMerged {
- allprojects {
- dependsOn it.tasks.getByName("build")
- }
- doLast {
- def folder = file(".gradle/.mergemods")
- folder.mkdirs()
- def fabricJar = file("fabric/build/libs/${rootProject.name}-${rootProject.mod_version}-fabric.jar")
- def forgeJar = file("forge/build/libs/${rootProject.name}-${rootProject.mod_version}-forge.jar")
- def fabricFolder = new File(folder, ".tempFabric")
- def forgeFolder = new File(folder, ".tempForge")
- def mergeFolder = new File(folder, ".tempMerge")
- def policyMap = new HashMap<String, String>()
- file("merging.policy").eachLine {
- if (it.isBlank() || it.startsWith("#")) return
- def env = it.substring(0, it.indexOf(' '))
- if (env == "FABRIC")
- policyMap.put(it.substring(env.length() + 1), "Fabric")
- else if (env == "FORGE")
- policyMap.put(it.substring(env.length() + 1), "Forge")
- else throw new IllegalStateException("Illegal env $env at $it")
- }
- forgeFolder.deleteDir()
- fabricFolder.deleteDir()
- mergeFolder.deleteDir()
- unzip(fabricJar, fabricFolder)
- unzip(forgeJar, forgeFolder)
- mergeFolder.mkdirs()
- Stream.of(forgeFolder, fabricFolder).each { useFolder ->
- try {
- Files.walkFileTree(useFolder.toPath(), new SimpleFileVisitor<Path>() {
- @Override
- FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
- try {
- File ogFile = file.toFile()
- File outFile = new File(mergeFolder, ogFile.getAbsolutePath().replace(useFolder.getAbsolutePath(), ""))
- outFile.getParentFile().mkdirs()
- if (outFile.exists()) {
- def env = useFolder.getName().substring(5)
- def fileName = outFile.getAbsolutePath().replace(mergeFolder.getAbsolutePath(), "")
- if (!ogFile.isFile() || !outFile.isFile() || !Arrays.equals(ogFile.readBytes(), outFile.readBytes())) {
- def policyEnv = policyMap.get(fileName)
- if (policyEnv == null) {
- throw new IllegalStateException("Unhandled duplicate file: $fileName")
- }
- println "Chose env ${policyEnv.toUpperCase(Locale.ROOT)} for duplicate file: $fileName"
- if (policyEnv != env)
- return FileVisitResult.CONTINUE
- }
- }
- if (!ogFile.isDirectory()) {
- org.apache.commons.io.FileUtils.copyFile(ogFile, outFile)
- } else {
- org.apache.commons.io.FileUtils.copyDirectory(ogFile, outFile)
- }
- } catch (IOException e) {
- e.printStackTrace()
- System.exit(0)
- }
- return FileVisitResult.CONTINUE
- }
- })
- } catch (IOException e) {
- e.printStackTrace()
- System.exit(0)
- }
- }
- File finalMerge = file("build/libs/${rootProject.name}-${rootProject.mod_version}.jar")
- finalMerge.parentFile.mkdirs()
- finalMerge.delete()
- compress(mergeFolder.toPath(), finalMerge)
- folder.deleteDir()
+ repositories {
+ maven { url "https://maven.terraformersmc.com/releases" }
}
-}
-
-rootProject.subprojects.forEach {
- buildMerged.mustRunAfter it.tasks.getByName("build")
-}
-static def compress(Path sourceDir, File zipFile) {
- try {
- final ZipOutputStream outputStream = new ZipOutputStream(new FileOutputStream(zipFile))
- Files.walkFileTree(sourceDir, new SimpleFileVisitor<Path>() {
- @Override
- FileVisitResult visitFile(Path file, BasicFileAttributes attributes) {
- try {
- Path targetFile = sourceDir.relativize(file)
- outputStream.putNextEntry(new ZipEntry(targetFile.toString()))
- byte[] bytes = Files.readAllBytes(file)
- outputStream.write(bytes, 0, bytes.length)
- outputStream.closeEntry()
- } catch (IOException e) {
- e.printStackTrace()
- }
- return FileVisitResult.CONTINUE
- }
- })
- outputStream.close()
- } catch (IOException e) {
- e.printStackTrace()
+ tasks.withType(JavaCompile) {
+ options.encoding = "UTF-8"
+ options.release = 16
}
}
-
-static def unzip(File zipFile, File destDir) {
- if (!destDir.exists())
- destDir.mkdirs()
- FileInputStream fis
- byte[] buffer = new byte[1024]
- try {
- fis = new FileInputStream(zipFile)
- ZipInputStream zis = new ZipInputStream(fis)
- ZipEntry zipEntry = zis.getNextEntry()
- while (zipEntry != null) {
- if (!zipEntry.isDirectory()) {
- File newFile = new File(destDir, zipEntry.getName())
- new File(newFile.getParent()).mkdirs()
- FileOutputStream fos = new FileOutputStream(newFile)
- int len
- while ((len = zis.read(buffer)) > 0) {
- fos.write(buffer, 0, len)
- }
- fos.close()
- }
- zis.closeEntry()
- zipEntry = zis.getNextEntry()
- }
- zis.closeEntry()
- zis.close()
- fis.close()
- } catch (IOException e) {
- e.printStackTrace()
- }
-}
\ No newline at end of file
dependencies {
minecraft "com.mojang:minecraft:${rootProject.architectury.minecraft}"
mappings minecraft.officialMojangMappings()
- modCompile "me.shedaniel:architectury:${rootProject.architectury_version}"
- modCompile "net.fabricmc:fabric-loader:${rootProject.fabric_loader_version}"
+ modImplementation "dev.architectury:architectury:${rootProject.architectury_version}"
+ modImplementation "net.fabricmc:fabric-loader:${rootProject.fabric_loader_version}"
modCompileOnly("me.shedaniel.cloth:cloth-config:${rootProject.cloth_config_version}") {
exclude(group: "net.fabricmc.fabric-api")
}
}
architectury {
- common()
+ common(false)
}
\ No newline at end of file
+++ /dev/null
-package me.shedaniel.lightoverlay.common;
-
-import it.unimi.dsi.fastutil.longs.Long2ByteMap;
-import it.unimi.dsi.fastutil.longs.Long2ByteOpenHashMap;
-import net.minecraft.core.BlockPos;
-import net.minecraft.core.Direction;
-import net.minecraft.world.level.Level;
-import net.minecraft.world.phys.shapes.CollisionContext;
-import net.minecraft.world.phys.shapes.VoxelShape;
-import org.lwjgl.opengl.GL11;
-
-import java.io.Closeable;
-
-import static me.shedaniel.lightoverlay.common.LightOverlay.*;
-
-public class ChunkData implements Closeable {
- private static final IllegalStateException WRONG_TYPE = new IllegalStateException("Wrong type accessed!");
- private Long2ByteMap data;
- private int glListIndex = 0;
- private boolean generatedList = false;
-
- public ChunkData() {
- this(new Long2ByteOpenHashMap());
- }
-
- public ChunkData(Long2ByteMap data) {
- this.data = data;
- }
-
- public Long2ByteMap data() {
- return data;
- }
-
- private void compileList(Level level, CollisionContext collisionContext) {
- generatedList = true;
-
- if (data().isEmpty()) {
- glListIndex = 0;
- return;
- }
-
- glListIndex = GL11.glGenLists(3);
- GL11.glNewList(glListIndex, GL11.GL_COMPILE);
- GL11.glBegin(GL11.GL_LINES);
- color(redColor);
-
- BlockPos.MutableBlockPos mutable = new BlockPos.MutableBlockPos();
- for (Long2ByteMap.Entry objectEntry : data().long2ByteEntrySet()) {
- byte crossType = objectEntry.getByteValue();
- mutable.set(BlockPos.getX(objectEntry.getLongKey()), BlockPos.getY(objectEntry.getLongKey()), BlockPos.getZ(objectEntry.getLongKey()));
- if (crossType == CROSS_RED) {
- renderCross(level, mutable, collisionContext);
- }
- }
-
- GL11.glEnd();
- GL11.glEndList();
-
- GL11.glNewList(glListIndex + 1, GL11.GL_COMPILE);
- GL11.glBegin(GL11.GL_LINES);
- color(yellowColor);
-
- for (Long2ByteMap.Entry objectEntry : data().long2ByteEntrySet()) {
- byte crossType = objectEntry.getByteValue();
- mutable.set(BlockPos.getX(objectEntry.getLongKey()), BlockPos.getY(objectEntry.getLongKey()), BlockPos.getZ(objectEntry.getLongKey()));
- if (crossType == CROSS_YELLOW) {
- renderCross(level, mutable, collisionContext);
- }
- }
-
- GL11.glEnd();
- GL11.glEndList();
-
- GL11.glNewList(glListIndex + 2, GL11.GL_COMPILE);
- GL11.glBegin(GL11.GL_LINES);
- color(secondaryColor);
-
- for (Long2ByteMap.Entry objectEntry : data().long2ByteEntrySet()) {
- byte crossType = objectEntry.getByteValue();
- mutable.set(BlockPos.getX(objectEntry.getLongKey()), BlockPos.getY(objectEntry.getLongKey()), BlockPos.getZ(objectEntry.getLongKey()));
- if (crossType == CROSS_SECONDARY) {
- renderCross(level, mutable, collisionContext);
- }
- }
-
- GL11.glEnd();
- GL11.glEndList();
- }
-
- public void renderList(Level level, CollisionContext collisionContext) {
- if (!generatedList) {
- compileList(level, collisionContext);
- }
-
- if (glListIndex != 0) {
- GL11.glCallList(glListIndex);
- GL11.glCallList(glListIndex + 1);
- GL11.glCallList(glListIndex + 2);
- }
- }
-
- private static void color(int color) {
- int red = (color >> 16) & 255;
- int green = (color >> 8) & 255;
- int blue = color & 255;
- GL11.glColor4f(red / 255f, green / 255f, blue / 255f, 1f);
- }
-
- public static void renderCross(Level level, BlockPos pos, CollisionContext collisionContext) {
- double blockOffset = 0;
- VoxelShape upperOutlineShape = level.getBlockState(pos).getShape(level, pos, collisionContext);
- if (!upperOutlineShape.isEmpty())
- blockOffset += upperOutlineShape.max(Direction.Axis.Y);
-
-
- int x = pos.getX();
- int y = pos.getY();
- int z = pos.getZ();
- GL11.glVertex3d(x + .01, y + blockOffset, z + .01);
- GL11.glVertex3d(x - .01 + 1, y + blockOffset, z - .01 + 1);
- GL11.glVertex3d(x - .01 + 1, y + blockOffset, z + .01);
- GL11.glVertex3d(x + .01, y + blockOffset, z - .01 + 1);
- }
-
- @Override
- public void close() {
- if (glListIndex != 0) {
- GL11.glDeleteLists(glListIndex, 3);
- }
- }
-}
return Optional.empty();
}).setDefaultValue(-1).setTextGetter(integer -> new TextComponent(integer < 0 ? "Off" : "Level: " + integer)).setSaveConsumer(integer -> LightOverlay.secondaryLevel = integer).build());
general.addEntry(eb.startBooleanToggle(new TranslatableComponent("config.lightoverlay.showNumber"), LightOverlay.showNumber).setDefaultValue(false).setSaveConsumer(bool -> LightOverlay.showNumber = bool).build());
- general.addEntry(eb.startBooleanToggle(new TranslatableComponent("config.lightoverlay.smoothLines"), LightOverlay.smoothLines).setDefaultValue(true).setSaveConsumer(bool -> LightOverlay.smoothLines = bool).build());
general.addEntry(eb.startBooleanToggle(new TranslatableComponent("config.lightoverlay.underwater"), LightOverlay.underwater).setDefaultValue(false).setSaveConsumer(bool -> LightOverlay.underwater = bool).build());
general.addEntry(eb.startBooleanToggle(new TranslatableComponent("config.lightoverlay.mushroom"), LightOverlay.mushroom).setDefaultValue(false).setSaveConsumer(bool -> LightOverlay.mushroom = bool).build());
- general.addEntry(eb.startBooleanToggle(new TranslatableComponent("config.lightoverlay.useListWhileCaching"), LightOverlay.useListWhileCaching).setDefaultValue(true).setSaveConsumer(bool -> LightOverlay.useListWhileCaching = bool).build());
general.addEntry(eb.startIntSlider(new TranslatableComponent("config.lightoverlay.lineWidth"), Mth.floor(LightOverlay.lineWidth * 100), 100, 700).setDefaultValue(100).setTextGetter(integer -> new TextComponent("Light Width: " + LightOverlay.FORMAT.format(integer / 100d))).setSaveConsumer(integer -> LightOverlay.lineWidth = integer / 100f).build());
general.addEntry(eb.startColorField(new TranslatableComponent("config.lightoverlay.yellowColor"), LightOverlay.yellowColor).setDefaultValue(0xFFFF00).setSaveConsumer(color -> LightOverlay.yellowColor = color).build());
general.addEntry(eb.startColorField(new TranslatableComponent("config.lightoverlay.redColor"), LightOverlay.redColor).setDefaultValue(0xFF0000).setSaveConsumer(color -> LightOverlay.redColor = color).build());
import net.minecraft.core.BlockPos;
-import java.util.Objects;
-
public class CubicChunkPos {
public final int x;
public final int y;
public CubicChunkPos(BlockPos blockPos) {
this.x = blockPos.getX() >> 4;
- this.y = blockPos.getY() >> 4;
+ this.y = blockPos.getY() >> 5;
this.z = blockPos.getZ() >> 4;
}
}
public int getMinBlockY() {
- return this.y << 4;
+ return this.y << 5;
}
public int getMinBlockZ() {
}
public int getMaxBlockY() {
- return (this.y << 4) + 15;
+ return (this.y << 5) + 31;
}
public int getMaxBlockZ() {
return x == that.x && y == that.y && z == that.z;
}
+ @Override
+ public String toString() {
+ return "CubicChunkPos{" +
+ "x=" + x +
+ ", y=" + y +
+ ", z=" + z +
+ '}';
+ }
+
@Override
public int hashCode() {
- return Objects.hash(x, y, z);
+ return Long.hashCode(toLong());
}
}
package me.shedaniel.lightoverlay.common;
-import com.google.common.collect.Maps;
-import com.mojang.blaze3d.platform.GlStateManager;
import com.mojang.blaze3d.platform.InputConstants;
-import com.mojang.blaze3d.systems.RenderSystem;
-import com.mojang.blaze3d.vertex.Tesselator;
-import com.mojang.math.Transformation;
-import it.unimi.dsi.fastutil.longs.Long2ByteMap;
-import me.shedaniel.architectury.event.events.GuiEvent;
-import me.shedaniel.architectury.event.events.client.ClientTickEvent;
-import me.shedaniel.architectury.platform.Platform;
-import me.shedaniel.architectury.registry.KeyBindings;
-import net.minecraft.client.Camera;
+import com.mojang.blaze3d.vertex.PoseStack;
+import dev.architectury.event.events.client.ClientGuiEvent;
+import dev.architectury.event.events.client.ClientTickEvent;
+import dev.architectury.injectables.targets.ArchitecturyTarget;
+import dev.architectury.platform.Platform;
+import dev.architectury.registry.client.keymappings.KeyMappingRegistry;
import net.minecraft.client.KeyMapping;
-import net.minecraft.client.Minecraft;
-import net.minecraft.client.gui.Font;
-import net.minecraft.client.multiplayer.ClientLevel;
-import net.minecraft.client.player.LocalPlayer;
-import net.minecraft.client.renderer.MultiBufferSource;
-import net.minecraft.client.renderer.culling.Frustum;
-import net.minecraft.core.BlockPos;
-import net.minecraft.core.Direction;
import net.minecraft.resources.ResourceLocation;
-import net.minecraft.tags.BlockTags;
-import net.minecraft.util.LazyLoadedValue;
import net.minecraft.util.Mth;
-import net.minecraft.world.entity.Entity;
-import net.minecraft.world.entity.EntityType;
-import net.minecraft.world.entity.MobCategory;
-import net.minecraft.world.level.BlockGetter;
-import net.minecraft.world.level.Level;
-import net.minecraft.world.level.LightLayer;
-import net.minecraft.world.level.biome.Biome;
-import net.minecraft.world.level.block.Block;
-import net.minecraft.world.level.block.state.BlockState;
-import net.minecraft.world.level.chunk.ChunkStatus;
-import net.minecraft.world.level.chunk.LevelChunk;
-import net.minecraft.world.level.lighting.LayerLightEventListener;
-import net.minecraft.world.phys.shapes.CollisionContext;
-import net.minecraft.world.phys.shapes.VoxelShape;
-import org.apache.logging.log4j.LogManager;
-import org.lwjgl.opengl.GL11;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
-import java.lang.invoke.MethodHandle;
-import java.lang.invoke.MethodHandles;
-import java.lang.invoke.MethodType;
import java.text.DecimalFormat;
-import java.util.*;
-import java.util.concurrent.Executors;
-import java.util.concurrent.ThreadPoolExecutor;
+import java.util.Properties;
+import java.util.function.Consumer;
public class LightOverlay {
public static final DecimalFormat FORMAT = new DecimalFormat("#.#");
- private static final String KEYBIND_CATEGORY = "key.lightoverlay.category";
- private static final ResourceLocation ENABLE_OVERLAY_KEYBIND = new ResourceLocation("lightoverlay", "enable_overlay");
public static int reach = 12;
public static int crossLevel = 7;
public static int secondaryLevel = -1;
public static int higherCrossLevel = -1;
public static boolean caching = false;
public static boolean showNumber = false;
- public static boolean smoothLines = true;
public static boolean underwater = false;
public static boolean mushroom = false;
- public static boolean useListWhileCaching = true;
public static float lineWidth = 1.0F;
public static int yellowColor = 0xFFFF00, redColor = 0xFF0000, secondaryColor = 0x0000FF;
public static File configFile;
- private static KeyMapping enableOverlay;
- private static boolean enabled = false;
- private static final LazyLoadedValue<EntityType<Entity>> TESTING_ENTITY_TYPE = new LazyLoadedValue<>(() ->
- EntityType.Builder.createNothing(MobCategory.MONSTER).sized(0f, 0f).noSave().build(null));
- private static int threadNumber = 0;
- public static Frustum frustum;
- private static final ThreadPoolExecutor EXECUTOR = (ThreadPoolExecutor) Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors(), r -> {
- Thread thread = new Thread(r, "light-overlay-" + threadNumber++);
- thread.setDaemon(true);
- return thread;
- });
- private static final Set<CubicChunkPos> POS = Collections.synchronizedSet(new HashSet<>());
- private static final Set<CubicChunkPos> CALCULATING_POS = Collections.synchronizedSet(new HashSet<>());
- private static final Map<CubicChunkPos, ChunkData> CHUNK_MAP = Maps.newConcurrentMap();
- private static final Minecraft CLIENT = Minecraft.getInstance();
- private static long ticks = 0;
+ public static KeyMapping enableOverlay;
+ public static boolean enabled = false;
+
+ public static LightOverlayTicker ticker = new LightOverlayTicker();
+ public static LightOverlayRenderer renderer = new LightOverlayRenderer(ticker);
public static void register() {
// Load Config
configFile = new File(Platform.getConfigFolder().toFile(), "lightoverlay.properties");
loadConfig(configFile);
- enableOverlay = createKeyBinding(ENABLE_OVERLAY_KEYBIND, InputConstants.Type.KEYSYM, 296, KEYBIND_CATEGORY);
- KeyBindings.registerKeyBinding(enableOverlay);
+ enableOverlay = createKeyBinding(new ResourceLocation("lightoverlay", "enable_overlay"), InputConstants.Type.KEYSYM, 296, "key.lightoverlay.category");
+ KeyMappingRegistry.register(enableOverlay);
- registerDebugRenderer(() -> {
- if (enabled) {
- LocalPlayer playerEntity = CLIENT.player;
- int playerPosX = ((int) playerEntity.getX()) >> 4;
- int playerPosZ = ((int) playerEntity.getZ()) >> 4;
- CollisionContext collisionContext = CollisionContext.of(playerEntity);
- Level world = CLIENT.level;
- BlockPos playerPos = new BlockPos(playerEntity.getX(), playerEntity.getY(), playerEntity.getZ());
- Camera camera = CLIENT.gameRenderer.getMainCamera();
-
- if (showNumber) {
- RenderSystem.enableTexture();
- RenderSystem.depthMask(true);
- BlockPos.MutableBlockPos mutable = new BlockPos.MutableBlockPos();
- BlockPos.MutableBlockPos downMutable = new BlockPos.MutableBlockPos();
- for (Map.Entry<CubicChunkPos, ChunkData> entry : CHUNK_MAP.entrySet()) {
- if (caching && (Mth.abs(entry.getKey().x - playerPosX) > getChunkRange() || Mth.abs(entry.getKey().z - playerPosZ) > getChunkRange())) {
- continue;
- }
- for (Long2ByteMap.Entry objectEntry : entry.getValue().data().long2ByteEntrySet()) {
- mutable.set(BlockPos.getX(objectEntry.getLongKey()), BlockPos.getY(objectEntry.getLongKey()), BlockPos.getZ(objectEntry.getLongKey()));
- if (mutable.closerThan(playerPos, reach)) {
- if (frustum == null || isFrustumVisible(frustum, mutable.getX(), mutable.getY(), mutable.getZ(), mutable.getX() + 1, mutable.getX() + 1, mutable.getX() + 1)) {
- downMutable.set(mutable.getX(), mutable.getY() - 1, mutable.getZ());
- renderLevel(CLIENT, camera, world, mutable, downMutable, objectEntry.getByteValue(), collisionContext);
- }
- }
- }
- }
- RenderSystem.enableDepthTest();
- } else {
- boolean useList = useListWhileCaching && caching;
- RenderSystem.enableDepthTest();
- RenderSystem.disableTexture();
- RenderSystem.enableBlend();
- RenderSystem.enableCull();
- RenderSystem.blendFunc(GlStateManager.SourceFactor.SRC_ALPHA, GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA);
- if (smoothLines) GL11.glEnable(GL11.GL_LINE_SMOOTH);
- GL11.glLineWidth(lineWidth);
- if (!useList) GL11.glBegin(GL11.GL_LINES);
-
- BlockPos.MutableBlockPos mutable = new BlockPos.MutableBlockPos();
-
- if (useList) GL11.glTranslated(-camera.getPosition().x, -camera.getPosition().y + .01D, -camera.getPosition().z);
- for (Map.Entry<CubicChunkPos, ChunkData> entry : CHUNK_MAP.entrySet()) {
- CubicChunkPos chunkPos = entry.getKey();
- if (caching && (Mth.abs(chunkPos.x - playerPosX) > getChunkRange() || Mth.abs(chunkPos.z - playerPosZ) > getChunkRange())) {
- continue;
- }
-
- if (useList) {
- if (frustum == null || isFrustumVisible(frustum, chunkPos.getMinBlockX(), chunkPos.getMinBlockY(), chunkPos.getMinBlockZ(), chunkPos.getMaxBlockX(), chunkPos.getMaxBlockY(), chunkPos.getMaxBlockZ())) {
- entry.getValue().renderList(world, collisionContext);
- }
- } else {
- for (Long2ByteMap.Entry objectEntry : entry.getValue().data().long2ByteEntrySet()) {
- byte crossType = objectEntry.getByteValue();
- mutable.set(BlockPos.getX(objectEntry.getLongKey()), BlockPos.getY(objectEntry.getLongKey()), BlockPos.getZ(objectEntry.getLongKey()));
- if (mutable.closerThan(playerPos, reach)) {
- if (frustum == null || isFrustumVisible(frustum, mutable.getX(), mutable.getY(), mutable.getZ(), mutable.getX() + 1, mutable.getX() + 1, mutable.getX() + 1)) {
- int color = crossType == CROSS_RED ? redColor : crossType == CROSS_YELLOW ? yellowColor : secondaryColor;
- renderCross(camera, world, mutable, color, collisionContext);
- }
- }
- }
- }
- }
-
- if (!useList) GL11.glEnd();
- RenderSystem.disableBlend();
- RenderSystem.enableTexture();
- if (smoothLines) GL11.glDisable(GL11.GL_LINE_SMOOTH);
- }
- }
- });
+ registerDebugRenderer(renderer);
- GuiEvent.DEBUG_TEXT_LEFT.register(list -> {
+ ClientGuiEvent.DEBUG_TEXT_LEFT.register(list -> {
if (enabled) {
if (caching) {
- list.add(String.format("[Light Overlay] Chunks to queue: %02d", POS.size()));
+ list.add(String.format("[Light Overlay] Chunks to queue: %02d", ticker.POS.size()));
} else {
list.add("[Light Overlay] Enabled");
}
list.add("[Light Overlay] Disabled");
}
});
- ClientTickEvent.CLIENT_POST.register(LightOverlay::tick);
+ ClientTickEvent.CLIENT_POST.register(ticker::tick);
}
- private static void processChunk(CubicChunkPos pos, int playerPosX, int playerPosY, int playerPosZ, CollisionContext context) {
- CALCULATING_POS.remove(pos);
- if (Mth.abs(pos.x - playerPosX) > getChunkRange() || Mth.abs(pos.y - playerPosY) > getChunkRange() || Mth.abs(pos.z - playerPosZ) > getChunkRange() || POS.contains(pos)) {
- return;
- }
- try {
- calculateChunk(CLIENT.level.getChunkSource().getChunk(pos.x, pos.z, ChunkStatus.FULL, false), CLIENT.level, pos, context);
- } catch (Throwable throwable) {
- LogManager.getLogger().throwing(throwable);
- }
- }
public static void queueChunkAndNear(CubicChunkPos pos) {
for (int xOffset = -1; xOffset <= 1; xOffset++) {
}
public static void queueChunk(CubicChunkPos pos) {
- if (enabled && caching && !CALCULATING_POS.contains(pos)) {
- POS.add(pos);
- }
+ ticker.queueChunk(pos);
}
public static int getChunkRange() {
return Math.max(Mth.ceil(reach / 16f), 1);
}
- private static void calculateChunk(LevelChunk chunk, Level world, CubicChunkPos chunkPos, CollisionContext collisionContext) {
- if (world != null && chunk != null) {
- ChunkData chunkData = new ChunkData();
- LayerLightEventListener block = world.getLightEngine().getLayerListener(LightLayer.BLOCK);
- LayerLightEventListener sky = showNumber ? null : world.getLightEngine().getLayerListener(LightLayer.SKY);
- for (BlockPos pos : BlockPos.betweenClosed(chunkPos.getMinBlockX(), chunkPos.getMinBlockY(), chunkPos.getMinBlockZ(), chunkPos.getMaxBlockX(), chunkPos.getMaxBlockY(), chunkPos.getMaxBlockZ())) {
- BlockPos down = pos.below();
- if (showNumber) {
- int level = getCrossLevel(pos, down, chunk, block, collisionContext);
- if (level >= 0) {
- chunkData.data().put(pos.asLong(), (byte) level);
- }
- } else {
- Biome biome = !mushroom ? world.getBiome(pos) : null;
- byte type = getCrossType(pos, biome, down, chunk, block, sky, collisionContext);
- if (type != CROSS_NONE) {
- chunkData.data().put(pos.asLong(), type);
- }
- }
- }
- CHUNK_MAP.put(chunkPos, chunkData);
- } else {
- ChunkData data = CHUNK_MAP.remove(chunkPos);
-
- if (data != null) {
- data.close();
- }
- }
- }
-
- public static byte getCrossType(BlockPos pos, Biome biome, BlockPos down, BlockGetter world, LayerLightEventListener block, LayerLightEventListener sky, CollisionContext entityContext) {
- BlockState blockBelowState = world.getBlockState(down);
- BlockState blockUpperState = world.getBlockState(pos);
- VoxelShape upperCollisionShape = blockUpperState.getCollisionShape(world, pos, entityContext);
- if (!underwater && !blockUpperState.getFluidState().isEmpty())
- return CROSS_NONE;
- // Check if the outline is full
- if (Block.isFaceFull(upperCollisionShape, Direction.UP))
- return CROSS_NONE;
- // TODO: Not to hard code no redstone
- if (blockUpperState.isSignalSource())
- return CROSS_NONE;
- // Check if the collision has a bump
- if (upperCollisionShape.max(Direction.Axis.Y) > 0)
- return CROSS_NONE;
- if (blockUpperState.getBlock().is(BlockTags.RAILS))
- return CROSS_NONE;
- // Check block state allow spawning (excludes bedrock and barriers automatically)
- if (!blockBelowState.isValidSpawn(world, down, TESTING_ENTITY_TYPE.get()))
- return CROSS_NONE;
- if (!mushroom && Biome.BiomeCategory.MUSHROOM == biome.getBiomeCategory())
- return CROSS_NONE;
- int blockLightLevel = block.getLightValue(pos);
- int skyLightLevel = sky.getLightValue(pos);
- if (blockLightLevel > higherCrossLevel)
- return CROSS_NONE;
- if (skyLightLevel > higherCrossLevel)
- return CROSS_YELLOW;
- return lowerCrossLevel >= 0 && blockLightLevel > lowerCrossLevel ? CROSS_SECONDARY : CROSS_RED;
- }
-
- public static int getCrossLevel(BlockPos pos, BlockPos down, BlockGetter world, LayerLightEventListener view, CollisionContext collisionContext) {
- BlockState blockBelowState = world.getBlockState(down);
- BlockState blockUpperState = world.getBlockState(pos);
- VoxelShape collisionShape = blockBelowState.getCollisionShape(world, down, collisionContext);
- VoxelShape upperCollisionShape = blockUpperState.getCollisionShape(world, pos, collisionContext);
- if (!underwater && !blockUpperState.getFluidState().isEmpty())
- return -1;
- if (!blockBelowState.getFluidState().isEmpty())
- return -1;
- if (blockBelowState.isAir())
- return -1;
- if (Block.isFaceFull(upperCollisionShape, Direction.DOWN))
- return -1;
- return view.getLightValue(pos);
- }
-
- public static void renderCross(Camera camera, Level world, BlockPos pos, int color, CollisionContext collisionContext) {
- double cameraX = camera.getPosition().x;
- double cameraY = camera.getPosition().y - .005D;
- double blockOffset = 0;
- VoxelShape upperOutlineShape = world.getBlockState(pos).getShape(world, pos, collisionContext);
- if (!upperOutlineShape.isEmpty())
- blockOffset += upperOutlineShape.max(Direction.Axis.Y);
- double cameraZ = camera.getPosition().z;
-
- int red = (color >> 16) & 255;
- int green = (color >> 8) & 255;
- int blue = color & 255;
- int x = pos.getX();
- int y = pos.getY();
- int z = pos.getZ();
- RenderSystem.color4f(red / 255f, green / 255f, blue / 255f, 1f);
- GL11.glVertex3d(x + .01 - cameraX, y - cameraY + blockOffset, z + .01 - cameraZ);
- GL11.glVertex3d(x - .01 + 1 - cameraX, y - cameraY + blockOffset, z - .01 + 1 - cameraZ);
- GL11.glVertex3d(x - .01 + 1 - cameraX, y - cameraY + blockOffset, z + .01 - cameraZ);
- GL11.glVertex3d(x + .01 - cameraX, y - cameraY + blockOffset, z - .01 + 1 - cameraZ);
- }
-
- @SuppressWarnings("deprecation")
- public static void renderLevel(Minecraft client, Camera camera, Level world, BlockPos pos, BlockPos down, byte level, CollisionContext collisionContext) {
- String text = String.valueOf(level);
- Font font = client.font;
- double cameraX = camera.getPosition().x;
- double cameraY = camera.getPosition().y;
- VoxelShape upperOutlineShape = world.getBlockState(down).getShape(world, down, collisionContext);
- if (!upperOutlineShape.isEmpty())
- cameraY += 1 - upperOutlineShape.max(Direction.Axis.Y);
- double cameraZ = camera.getPosition().z;
- RenderSystem.pushMatrix();
- RenderSystem.translatef((float) (pos.getX() + 0.5f - cameraX), (float) (pos.getY() - cameraY) + 0.005f, (float) (pos.getZ() + 0.5f - cameraZ));
- RenderSystem.rotatef(90, 1, 0, 0);
- RenderSystem.normal3f(0.0F, 1.0F, 0.0F);
- float size = 0.07F;
- RenderSystem.scalef(-size, -size, size);
- float float_3 = (float) (-font.width(text)) / 2.0F + 0.4f;
- RenderSystem.enableAlphaTest();
- MultiBufferSource.BufferSource source = MultiBufferSource.immediate(Tesselator.getInstance().getBuilder());
- font.drawInBatch(text, float_3, -3.5f, level > higherCrossLevel ? 0xff042404 : (lowerCrossLevel >= 0 && level > lowerCrossLevel ? 0xff0066ff : 0xff731111), false, Transformation.identity().getMatrix(), source, false, 0, 15728880);
- source.endBatch();
- RenderSystem.popMatrix();
- }
public static void loadConfig(File file) {
try {
secondaryLevel = Integer.parseInt((String) properties.computeIfAbsent("secondaryLevel", a -> "-1"));
caching = ((String) properties.computeIfAbsent("caching", a -> "false")).equalsIgnoreCase("true");
showNumber = ((String) properties.computeIfAbsent("showNumber", a -> "false")).equalsIgnoreCase("true");
- smoothLines = ((String) properties.computeIfAbsent("smoothLines", a -> "true")).equalsIgnoreCase("true");
underwater = ((String) properties.computeIfAbsent("underwater", a -> "false")).equalsIgnoreCase("true");
mushroom = ((String) properties.computeIfAbsent("mushroom", a -> "false")).equalsIgnoreCase("true");
- useListWhileCaching = ((String) properties.computeIfAbsent("useListWhileCaching", a -> "true")).equalsIgnoreCase("true");
lineWidth = Float.parseFloat((String) properties.computeIfAbsent("lineWidth", a -> "1"));
{
int r, g, b;
secondaryColor = 0x0000FF;
caching = false;
showNumber = false;
- smoothLines = true;
underwater = false;
mushroom = false;
- useListWhileCaching = true;
try {
saveConfig(file);
} catch (IOException ex) {
if (secondaryLevel >= crossLevel) System.err.println("[Light Overlay] Secondary Level is higher than Cross Level");
lowerCrossLevel = Math.min(crossLevel, secondaryLevel);
higherCrossLevel = Math.max(crossLevel, secondaryLevel);
- for (ChunkData data : CHUNK_MAP.values()) {
- data.close();
- }
- CHUNK_MAP.clear();
- POS.clear();
+ ticker.CHUNK_MAP.clear();
+ ticker.POS.clear();
}
public static void saveConfig(File file) throws IOException {
fos.write("\n".getBytes());
fos.write(("showNumber=" + showNumber).getBytes());
fos.write("\n".getBytes());
- fos.write(("smoothLines=" + smoothLines).getBytes());
- fos.write("\n".getBytes());
fos.write(("underwater=" + underwater).getBytes());
fos.write("\n".getBytes());
fos.write(("mushroom=" + mushroom).getBytes());
fos.write("\n".getBytes());
- fos.write(("useListWhileCaching=" + useListWhileCaching).getBytes());
- fos.write("\n".getBytes());
fos.write(("lineWidth=" + FORMAT.format(lineWidth)).getBytes());
fos.write("\n".getBytes());
fos.write(("yellowColorRed=" + ((yellowColor >> 16) & 255)).getBytes());
return new KeyMapping("key." + id.getNamespace() + "." + id.getPath(), type, code, category);
}
- private static final LazyLoadedValue<MethodHandle> IS_FRUSTUM_VISIBLE = new LazyLoadedValue<>(() -> {
- try {
- return MethodHandles.lookup().findStatic(Class.forName("me.shedaniel.lightoverlay." + Platform.getModLoader() + ".LightOverlayImpl"), "isFrustumVisible",
- MethodType.methodType(boolean.class, Frustum.class, double.class, double.class, double.class, double.class, double.class, double.class));
- } catch (NoSuchMethodException | IllegalAccessException | ClassNotFoundException e) {
- throw new RuntimeException(e);
- }
- });
-
- private static boolean isFrustumVisible(Frustum frustum, double minX, double minY, double minZ, double maxX, double maxY, double maxZ) {
- try {
- return (boolean) IS_FRUSTUM_VISIBLE.get().invokeExact(frustum, minX, minY, minZ, maxX, maxY, maxZ);
- } catch (Throwable throwable) {
- throw new RuntimeException(throwable);
- }
- }
- private static void registerDebugRenderer(Runnable runnable) {
+ private static void registerDebugRenderer(Consumer<PoseStack> runnable) {
try {
- Class.forName("me.shedaniel.lightoverlay." + Platform.getModLoader() + ".LightOverlayImpl").getDeclaredField("debugRenderer").set(null, runnable);
+ Class.forName("me.shedaniel.lightoverlay." + ArchitecturyTarget.getCurrentTarget() + ".LightOverlayImpl").getDeclaredField("debugRenderer").set(null, runnable);
} catch (Throwable throwable) {
throw new RuntimeException(throwable);
}
}
- private static void tick(Minecraft minecraft) {
- while (enableOverlay.consumeClick())
- enabled = !enabled;
-
- try {
- ticks++;
- if (CLIENT.player == null || !enabled) {
- POS.clear();
- CALCULATING_POS.clear();
- EXECUTOR.getQueue().clear();
- for (ChunkData data : CHUNK_MAP.values()) {
- data.close();
- }
- CHUNK_MAP.clear();
- } else {
- LocalPlayer player = CLIENT.player;
- ClientLevel world = CLIENT.level;
- CollisionContext collisionContext = CollisionContext.of(player);
-
- if (!caching) {
- CALCULATING_POS.clear();
- POS.clear();
- for (ChunkData data : CHUNK_MAP.values()) {
- data.close();
- }
- CHUNK_MAP.clear();
- BlockPos playerPos = player.blockPosition();
- LayerLightEventListener block = world.getLightEngine().getLayerListener(LightLayer.BLOCK);
- LayerLightEventListener sky = showNumber ? null : world.getLightEngine().getLayerListener(LightLayer.SKY);
- BlockPos.MutableBlockPos downPos = new BlockPos.MutableBlockPos();
- Iterable<BlockPos> iterate = BlockPos.betweenClosed(playerPos.getX() - reach, playerPos.getY() - reach, playerPos.getZ() - reach,
- playerPos.getX() + reach, playerPos.getY() + reach, playerPos.getZ() + reach);
- ChunkData chunkData = new ChunkData();
- CHUNK_MAP.put(new CubicChunkPos(0, 0, 0), chunkData);
- for (BlockPos blockPos : iterate) {
- downPos.set(blockPos.getX(), blockPos.getY() - 1, blockPos.getZ());
- if (showNumber) {
- int level = getCrossLevel(blockPos, downPos, world, block, collisionContext);
- if (level >= 0) {
- chunkData.data().put(blockPos.asLong(), (byte) level);
- }
- } else {
- Biome biome = !mushroom ? world.getBiome(blockPos) : null;
- byte type = getCrossType(blockPos, biome, downPos, world, block, sky, collisionContext);
- if (type != CROSS_NONE) {
- chunkData.data().put(blockPos.asLong(), type);
- }
- }
- }
- } else {
- int playerPosX = ((int) player.getX()) >> 4;
- int playerPosY = ((int) player.getY()) >> 4;
- int playerPosZ = ((int) player.getZ()) >> 4;
- for (int chunkX = playerPosX - getChunkRange(); chunkX <= playerPosX + getChunkRange(); chunkX++) {
- for (int chunkY = Math.max(playerPosY - getChunkRange(), 0); chunkY <= playerPosY + getChunkRange() && chunkY <= 15; chunkY++) {
- for (int chunkZ = playerPosZ - getChunkRange(); chunkZ <= playerPosZ + getChunkRange(); chunkZ++) {
- if (Mth.abs(chunkX - playerPosX) > getChunkRange() || Mth.abs(chunkY - playerPosY) > getChunkRange() || Mth.abs(chunkZ - playerPosZ) > getChunkRange())
- continue;
- CubicChunkPos chunkPos = new CubicChunkPos(chunkX, chunkY, chunkZ);
- if (!CHUNK_MAP.containsKey(chunkPos))
- queueChunk(chunkPos);
- }
- }
- }
- for (int p = 0; p < 3; p++) {
- if (EXECUTOR.getQueue().size() >= Runtime.getRuntime().availableProcessors()) break;
- double d1 = Double.MAX_VALUE, d2 = Double.MAX_VALUE, d3 = Double.MAX_VALUE;
- CubicChunkPos c1 = null, c2 = null, c3 = null;
- synchronized (POS) {
- Iterator<CubicChunkPos> iterator = POS.iterator();
- while (iterator.hasNext()) {
- CubicChunkPos pos = iterator.next();
- if (Mth.abs(pos.x - playerPosX) > getChunkRange() || Mth.abs(pos.y - playerPosY) > getChunkRange() || Mth.abs(pos.z - playerPosZ) > getChunkRange() || CALCULATING_POS.contains(pos)) {
- iterator.remove();
- } else {
- if (isFrustumVisible(frustum, pos.getMinBlockX(), pos.getMinBlockY(), pos.getMinBlockZ(), pos.getMaxBlockX(), pos.getMaxBlockY(), pos.getMaxBlockZ())) {
- int i = Math.abs(pos.x - playerPosX);
- int j = Math.abs(pos.y - playerPosY);
- int k = Math.abs(pos.z - playerPosZ);
- double distance = Math.sqrt(i * i + j * j + k * k);
- if (distance < d1) {
- d3 = d2;
- d2 = d1;
- d1 = distance;
- c3 = c2;
- c2 = c1;
- c1 = pos;
- iterator.remove();
- } else if (distance < d2) {
- d3 = d2;
- d2 = distance;
- c3 = c2;
- c2 = pos;
- iterator.remove();
- } else if (distance < d3) {
- d3 = distance;
- c3 = pos;
- iterator.remove();
- }
- }
- }
- }
- }
- CubicChunkPos finalC1 = c1;
- CubicChunkPos finalC2 = c2;
- CubicChunkPos finalC3 = c3;
- if (finalC1 != null) {
- CALCULATING_POS.add(finalC1);
- if (finalC2 != null) {
- CALCULATING_POS.add(finalC2);
- if (finalC3 != null) {
- CALCULATING_POS.add(finalC3);
- }
- }
- EXECUTOR.submit(() -> {
- int playerPosX1 = ((int) CLIENT.player.getX()) >> 4;
- int playerPosY1 = ((int) CLIENT.player.getY()) >> 4;
- int playerPosZ1 = ((int) CLIENT.player.getZ()) >> 4;
- if (finalC1 != null) processChunk(finalC1, playerPosX1, playerPosY1, playerPosZ1, collisionContext);
- if (finalC2 != null) processChunk(finalC2, playerPosX1, playerPosY1, playerPosZ1, collisionContext);
- if (finalC3 != null) processChunk(finalC3, playerPosX1, playerPosY1, playerPosZ1, collisionContext);
- });
- }
- }
- if (ticks % 50 == 0) {
- Iterator<Map.Entry<CubicChunkPos, ChunkData>> iterator = CHUNK_MAP.entrySet().iterator();
- while (iterator.hasNext()) {
- Map.Entry<CubicChunkPos, ChunkData> entry = iterator.next();
- if (Mth.abs(entry.getKey().x - playerPosX) > getChunkRange() * 2 || Mth.abs(entry.getKey().y - playerPosY) > getChunkRange() * 2 || Mth.abs(entry.getKey().z - playerPosZ) > getChunkRange() * 2) {
- entry.getValue().close();
- iterator.remove();
- }
- }
- }
- }
- }
- } catch (Throwable throwable) {
- LogManager.getLogger().throwing(throwable);
- }
- }
-
public static final byte CROSS_YELLOW = 0;
public static final byte CROSS_RED = 1;
public static final byte CROSS_SECONDARY = 2;
--- /dev/null
+package me.shedaniel.lightoverlay.common;
+
+import com.google.common.base.Suppliers;
+import com.mojang.blaze3d.systems.RenderSystem;
+import com.mojang.blaze3d.vertex.*;
+import com.mojang.math.Matrix4f;
+import com.mojang.math.Vector3f;
+import dev.architectury.injectables.targets.ArchitecturyTarget;
+import it.unimi.dsi.fastutil.longs.Long2ByteMap;
+import net.minecraft.client.Camera;
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.gui.Font;
+import net.minecraft.client.player.LocalPlayer;
+import net.minecraft.client.renderer.GameRenderer;
+import net.minecraft.client.renderer.MultiBufferSource;
+import net.minecraft.client.renderer.culling.Frustum;
+import net.minecraft.core.BlockPos;
+import net.minecraft.core.Direction;
+import net.minecraft.util.Mth;
+import net.minecraft.world.level.Level;
+import net.minecraft.world.phys.shapes.CollisionContext;
+import net.minecraft.world.phys.shapes.VoxelShape;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
+import java.util.Map;
+import java.util.function.Consumer;
+import java.util.function.Supplier;
+
+public class LightOverlayRenderer implements Consumer<PoseStack> {
+ private final Minecraft minecraft = Minecraft.getInstance();
+ public Frustum frustum;
+ public LightOverlayTicker ticker;
+
+ public LightOverlayRenderer(LightOverlayTicker ticker) {
+ this.ticker = ticker;
+ }
+
+ @Override
+ public void accept(PoseStack poses) {
+ if (LightOverlay.enabled) {
+ LocalPlayer playerEntity = minecraft.player;
+ BlockPos playerPos = new BlockPos(playerEntity.getX(), playerEntity.getY(), playerEntity.getZ());
+ int playerPosX = playerPos.getX() >> 4;
+ int playerPosY = playerPos.getY() >> 5;
+ int playerPosZ = playerPos.getZ() >> 4;
+ CollisionContext collisionContext = CollisionContext.of(playerEntity);
+ Camera camera = minecraft.gameRenderer.getMainCamera();
+ int chunkRange = LightOverlay.getChunkRange();
+
+ if (LightOverlay.showNumber) {
+ renderLevels(new PoseStack(), camera, playerPos, playerPosX, playerPosY, playerPosZ, chunkRange, collisionContext);
+ } else {
+ renderCrosses(poses, camera, playerPos, playerPosX, playerPosY, playerPosZ, chunkRange, collisionContext);
+ }
+ }
+ }
+
+ private void renderLevels(PoseStack poses, Camera camera, BlockPos playerPos, int playerPosX, int playerPosY, int playerPosZ, int chunkRange, CollisionContext collisionContext) {
+ RenderSystem.enableTexture();
+ RenderSystem.depthMask(true);
+ BlockPos.MutableBlockPos mutable = new BlockPos.MutableBlockPos();
+ BlockPos.MutableBlockPos downMutable = new BlockPos.MutableBlockPos();
+ MultiBufferSource.BufferSource source = MultiBufferSource.immediate(Tesselator.getInstance().getBuilder());
+ for (Map.Entry<CubicChunkPos, Long2ByteMap> entry : ticker.CHUNK_MAP.entrySet()) {
+ CubicChunkPos chunkPos = entry.getKey();
+ if (LightOverlay.caching && (Mth.abs(chunkPos.x - playerPosX) > chunkRange || Mth.abs(chunkPos.y - playerPosY) > Math.max(1, chunkRange >> 1) || Mth.abs(chunkPos.z - playerPosZ) > chunkRange)) {
+ continue;
+ }
+ for (Long2ByteMap.Entry objectEntry : entry.getValue().long2ByteEntrySet()) {
+ mutable.set(objectEntry.getLongKey());
+ if (mutable.closerThan(playerPos, LightOverlay.reach)) {
+ if (isFrustumVisible(mutable.getX(), mutable.getY(), mutable.getZ(), mutable.getX() + 1, mutable.getX() + 1, mutable.getX() + 1)) {
+ downMutable.set(mutable.getX(), mutable.getY() - 1, mutable.getZ());
+ renderLevel(poses, source, camera, minecraft.level, mutable, downMutable, objectEntry.getByteValue(), collisionContext);
+ }
+ }
+ }
+ }
+ RenderSystem.enableDepthTest();
+ source.endBatch();
+ }
+
+ public void renderLevel(PoseStack poses, MultiBufferSource.BufferSource source, Camera camera, Level world, BlockPos pos, BlockPos down, byte level, CollisionContext collisionContext) {
+ String text = String.valueOf(level);
+ Font font = minecraft.font;
+ double cameraX = camera.getPosition().x;
+ double cameraY = camera.getPosition().y;
+ VoxelShape upperOutlineShape = world.getBlockState(down).getShape(world, down, collisionContext);
+ if (!upperOutlineShape.isEmpty())
+ cameraY += 1 - upperOutlineShape.max(Direction.Axis.Y);
+ double cameraZ = camera.getPosition().z;
+ poses.pushPose();
+ poses.translate(pos.getX() + 0.5 - cameraX, pos.getY() - cameraY + 0.005, pos.getZ() + 0.5 - cameraZ);
+ poses.mulPose(Vector3f.XP.rotationDegrees(90));
+// poses.glNormal3f(0.0F, 1.0F, 0.0F);
+ float size = 0.07F;
+ poses.scale(-size, -size, size);
+ float float_3 = (float) (-font.width(text)) / 2.0F + 0.4f;
+ font.drawInBatch(text, float_3, -3.5f, level > LightOverlay.higherCrossLevel ? 0xff042404 : (LightOverlay.lowerCrossLevel >= 0 && level > LightOverlay.lowerCrossLevel ? 0xff0066ff : 0xff731111), false, poses.last().pose(), source, false, 0, 15728880);
+ poses.popPose();
+ }
+
+ private void renderCrosses(PoseStack poses, Camera camera, BlockPos playerPos, int playerPosX, int playerPosY, int playerPosZ, int chunkRange, CollisionContext collisionContext) {
+ RenderSystem.enableDepthTest();
+ RenderSystem.disableTexture();
+ RenderSystem.disableBlend();
+ RenderSystem.setShader(GameRenderer::getPositionColorShader);
+ RenderSystem.lineWidth(LightOverlay.lineWidth);
+ Tesselator tesselator = Tesselator.getInstance();
+ BufferBuilder builder = tesselator.getBuilder();
+ builder.begin(VertexFormat.Mode.DEBUG_LINES, DefaultVertexFormat.POSITION_COLOR);
+ BlockPos.MutableBlockPos mutable = new BlockPos.MutableBlockPos();
+
+ System.out.println(ticker.CHUNK_MAP.size());
+ for (Map.Entry<CubicChunkPos, Long2ByteMap> entry : ticker.CHUNK_MAP.entrySet()) {
+ CubicChunkPos chunkPos = entry.getKey();
+ if (LightOverlay.caching && (Mth.abs(chunkPos.x - playerPosX) > chunkRange || Mth.abs(chunkPos.y - playerPosY) > Math.max(1, chunkRange >> 1) || Mth.abs(chunkPos.z - playerPosZ) > chunkRange)) {
+ continue;
+ }
+
+ for (Long2ByteMap.Entry objectEntry : entry.getValue().long2ByteEntrySet()) {
+ byte crossType = objectEntry.getByteValue();
+ mutable.set(objectEntry.getLongKey());
+ if (mutable.closerThan(playerPos, LightOverlay.reach)) {
+ if (isFrustumVisible(mutable.getX(), mutable.getY(), mutable.getZ(), mutable.getX() + 1, mutable.getX() + 1, mutable.getX() + 1)) {
+ int color = switch (crossType) {
+ case LightOverlay.CROSS_RED -> LightOverlay.redColor;
+ case LightOverlay.CROSS_YELLOW -> LightOverlay.yellowColor;
+ default -> LightOverlay.secondaryColor;
+ };
+ renderCross(poses.last().pose(), builder, camera, minecraft.level, mutable, color, collisionContext);
+ }
+ }
+ }
+ }
+
+ tesselator.end();
+ RenderSystem.lineWidth(1.0F);
+ RenderSystem.enableBlend();
+ RenderSystem.enableTexture();
+ }
+
+ public void renderCross(Matrix4f pose, BufferBuilder builder, Camera camera, Level world, BlockPos pos, int color, CollisionContext collisionContext) {
+ double cameraX = camera.getPosition().x;
+ double cameraY = camera.getPosition().y - .005D;
+ double blockOffset = 0;
+ VoxelShape upperOutlineShape = world.getBlockState(pos).getShape(world, pos, collisionContext);
+ if (!upperOutlineShape.isEmpty()) {
+ blockOffset += upperOutlineShape.max(Direction.Axis.Y);
+ }
+ double cameraZ = camera.getPosition().z;
+
+ int red = (color >> 16) & 255;
+ int green = (color >> 8) & 255;
+ int blue = color & 255;
+ double x = pos.getX() - cameraX;
+ double y = pos.getY() - cameraY + blockOffset;
+ double z = pos.getZ() - cameraZ;
+ builder.vertex(x + .01, y, z + .01).color(red, green, blue, 255).endVertex();
+ builder.vertex(x + .99, y, z + .99).color(red, green, blue, 255).endVertex();
+ builder.vertex(x + .99, y, z + .01).color(red, green, blue, 255).endVertex();
+ builder.vertex(x + .01, y, z + .99).color(red, green, blue, 255).endVertex();
+ }
+
+ private static final Supplier<MethodHandle> IS_FRUSTUM_VISIBLE = Suppliers.memoize(() -> {
+ try {
+ return MethodHandles.lookup().findStatic(Class.forName("me.shedaniel.lightoverlay." + ArchitecturyTarget.getCurrentTarget() + ".LightOverlayImpl"), "isFrustumVisible",
+ MethodType.methodType(boolean.class, Frustum.class, double.class, double.class, double.class, double.class, double.class, double.class));
+ } catch (NoSuchMethodException | IllegalAccessException | ClassNotFoundException e) {
+ throw new RuntimeException(e);
+ }
+ });
+
+ public boolean isFrustumVisible(double minX, double minY, double minZ, double maxX, double maxY, double maxZ) {
+ try {
+ return frustum == null || (boolean) IS_FRUSTUM_VISIBLE.get().invokeExact(frustum, minX, minY, minZ, maxX, maxY, maxZ);
+ } catch (Throwable throwable) {
+ throw new RuntimeException(throwable);
+ }
+ }
+}
--- /dev/null
+package me.shedaniel.lightoverlay.common;
+
+import com.google.common.base.Suppliers;
+import com.google.common.collect.Maps;
+import it.unimi.dsi.fastutil.longs.Long2ByteMap;
+import it.unimi.dsi.fastutil.longs.Long2ByteOpenHashMap;
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.multiplayer.ClientLevel;
+import net.minecraft.client.player.LocalPlayer;
+import net.minecraft.core.BlockPos;
+import net.minecraft.core.Direction;
+import net.minecraft.tags.BlockTags;
+import net.minecraft.util.Mth;
+import net.minecraft.world.entity.Entity;
+import net.minecraft.world.entity.EntityType;
+import net.minecraft.world.entity.MobCategory;
+import net.minecraft.world.level.BlockGetter;
+import net.minecraft.world.level.Level;
+import net.minecraft.world.level.LightLayer;
+import net.minecraft.world.level.biome.Biome;
+import net.minecraft.world.level.block.Block;
+import net.minecraft.world.level.block.state.BlockState;
+import net.minecraft.world.level.chunk.ChunkStatus;
+import net.minecraft.world.level.chunk.LevelChunk;
+import net.minecraft.world.level.lighting.LayerLightEventListener;
+import net.minecraft.world.phys.shapes.CollisionContext;
+import net.minecraft.world.phys.shapes.VoxelShape;
+import org.apache.logging.log4j.LogManager;
+
+import java.util.*;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.function.Supplier;
+
+public class LightOverlayTicker {
+ private final Minecraft minecraft = Minecraft.getInstance();
+ private long ticks = 0;
+ private static int threadNumber = 0;
+ private static final ThreadPoolExecutor EXECUTOR = (ThreadPoolExecutor) Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors(), r -> {
+ Thread thread = new Thread(r, "light-overlay-" + threadNumber++);
+ thread.setDaemon(true);
+ return thread;
+ });
+ public final Set<CubicChunkPos> POS = Collections.synchronizedSet(new HashSet<>());
+ public final Set<CubicChunkPos> CALCULATING_POS = Collections.synchronizedSet(new HashSet<>());
+ public final Map<CubicChunkPos, Long2ByteMap> CHUNK_MAP = Maps.newConcurrentMap();
+ private static final Supplier<EntityType<Entity>> TESTING_ENTITY_TYPE = Suppliers.memoize(() ->
+ EntityType.Builder.createNothing(MobCategory.MONSTER).sized(0f, 0f).noSave().build(null));
+
+ public void queueChunk(CubicChunkPos pos) {
+ if (LightOverlay.enabled && LightOverlay.caching && !CALCULATING_POS.contains(pos)) {
+ POS.add(pos);
+ }
+ }
+
+ public void tick(Minecraft minecraft) {
+ while (LightOverlay.enableOverlay.consumeClick())
+ LightOverlay.enabled = !LightOverlay.enabled;
+
+ try {
+ ticks++;
+ if (minecraft.player == null || !LightOverlay.enabled) {
+ POS.clear();
+ CALCULATING_POS.clear();
+ EXECUTOR.getQueue().clear();
+ CHUNK_MAP.clear();
+ } else {
+ LocalPlayer player = minecraft.player;
+ ClientLevel world = minecraft.level;
+ CollisionContext collisionContext = CollisionContext.of(player);
+
+ if (!LightOverlay.caching) {
+ CALCULATING_POS.clear();
+ POS.clear();
+ CHUNK_MAP.clear();
+ BlockPos playerPos = player.blockPosition();
+ LayerLightEventListener block = world.getLightEngine().getLayerListener(LightLayer.BLOCK);
+ LayerLightEventListener sky = LightOverlay.showNumber ? null : world.getLightEngine().getLayerListener(LightLayer.SKY);
+ BlockPos.MutableBlockPos downPos = new BlockPos.MutableBlockPos();
+ Iterable<BlockPos> iterate = BlockPos.betweenClosed(playerPos.getX() - LightOverlay.reach, playerPos.getY() - LightOverlay.reach, playerPos.getZ() - LightOverlay.reach,
+ playerPos.getX() + LightOverlay.reach, playerPos.getY() + LightOverlay.reach, playerPos.getZ() + LightOverlay.reach);
+ Long2ByteMap chunkData = new Long2ByteOpenHashMap();
+ CHUNK_MAP.put(new CubicChunkPos(0, 0, 0), chunkData);
+ for (BlockPos blockPos : iterate) {
+ downPos.set(blockPos.getX(), blockPos.getY() - 1, blockPos.getZ());
+ if (LightOverlay.showNumber) {
+ int level = getCrossLevel(blockPos, downPos, world, block, collisionContext);
+ if (level >= 0) {
+ chunkData.put(blockPos.asLong(), (byte) level);
+ }
+ } else {
+ Biome biome = !LightOverlay.mushroom ? world.getBiome(blockPos) : null;
+ byte type = getCrossType(blockPos, biome, downPos, world, block, sky, collisionContext);
+ if (type != LightOverlay.CROSS_NONE) {
+ chunkData.put(blockPos.asLong(), type);
+ }
+ }
+ }
+ } else {
+ var height = Mth.ceil(Minecraft.getInstance().level.getHeight() / 32.0);
+ var start = Math.floorDiv(Minecraft.getInstance().level.getMinBuildHeight(), 32);
+ int playerPosX = ((int) player.getX()) >> 4;
+ int playerPosY = ((int) player.getY()) >> 5;
+ int playerPosZ = ((int) player.getZ()) >> 4;
+ var chunkRange = LightOverlay.getChunkRange();
+ for (int chunkX = playerPosX - chunkRange; chunkX <= playerPosX + chunkRange; chunkX++) {
+ for (int chunkY = Math.max(playerPosY - Math.max(1, chunkRange >> 1), start); chunkY <= playerPosY + Math.max(1, chunkRange >> 1) && chunkY <= start + height; chunkY++) {
+ for (int chunkZ = playerPosZ - chunkRange; chunkZ <= playerPosZ + chunkRange; chunkZ++) {
+ if (Mth.abs(chunkX - playerPosX) > chunkRange || Mth.abs(chunkY - playerPosY) > chunkRange || Mth.abs(chunkZ - playerPosZ) > chunkRange)
+ continue;
+ CubicChunkPos chunkPos = new CubicChunkPos(chunkX, chunkY, chunkZ);
+ if (!CHUNK_MAP.containsKey(chunkPos))
+ queueChunk(chunkPos);
+ }
+ }
+ }
+ for (int p = 0; p < 3; p++) {
+ if (EXECUTOR.getQueue().size() >= Runtime.getRuntime().availableProcessors()) break;
+ double d1 = Double.MAX_VALUE, d2 = Double.MAX_VALUE, d3 = Double.MAX_VALUE;
+ CubicChunkPos c1 = null, c2 = null, c3 = null;
+ synchronized (POS) {
+ Iterator<CubicChunkPos> iterator = POS.iterator();
+ while (iterator.hasNext()) {
+ CubicChunkPos pos = iterator.next();
+ if (Mth.abs(pos.x - playerPosX) > chunkRange || Mth.abs(pos.y - playerPosY) > Math.max(1, chunkRange >> 1) || Mth.abs(pos.z - playerPosZ) > chunkRange || CALCULATING_POS.contains(pos)) {
+ iterator.remove();
+ } else {
+ if (LightOverlay.renderer.isFrustumVisible(pos.getMinBlockX(), pos.getMinBlockY(), pos.getMinBlockZ(), pos.getMaxBlockX(), pos.getMaxBlockY(), pos.getMaxBlockZ())) {
+ int dx = Math.abs(pos.x - playerPosX);
+ int dy = Math.abs(pos.y - playerPosY) << 1;
+ int dz = Math.abs(pos.z - playerPosZ);
+ double distance = Math.sqrt(dx * dx + dy * dy + dz * dz);
+ if (distance < d1) {
+ d3 = d2;
+ d2 = d1;
+ d1 = distance;
+ c3 = c2;
+ c2 = c1;
+ c1 = pos;
+ } else if (distance < d2) {
+ d3 = d2;
+ d2 = distance;
+ c3 = c2;
+ c2 = pos;
+ } else if (distance < d3) {
+ d3 = distance;
+ c3 = pos;
+ }
+ }
+ }
+ }
+ }
+ CubicChunkPos finalC1 = c1;
+ CubicChunkPos finalC2 = c2;
+ CubicChunkPos finalC3 = c3;
+ if (finalC1 != null) {
+ CALCULATING_POS.add(finalC1);
+ POS.remove(finalC1);
+ if (finalC2 != null) {
+ CALCULATING_POS.add(finalC2);
+ POS.remove(finalC2);
+ if (finalC3 != null) {
+ CALCULATING_POS.add(finalC3);
+ POS.remove(finalC3);
+ }
+ }
+ EXECUTOR.submit(() -> {
+ int playerPosX1 = ((int) minecraft.player.getX()) >> 4;
+ int playerPosY1 = ((int) minecraft.player.getY()) >> 5;
+ int playerPosZ1 = ((int) minecraft.player.getZ()) >> 4;
+ if (finalC1 != null) processChunk(finalC1, playerPosX1, playerPosY1, playerPosZ1, collisionContext);
+ if (finalC2 != null) processChunk(finalC2, playerPosX1, playerPosY1, playerPosZ1, collisionContext);
+ if (finalC3 != null) processChunk(finalC3, playerPosX1, playerPosY1, playerPosZ1, collisionContext);
+ });
+ }
+ }
+ if (ticks % 50 == 0) {
+ CHUNK_MAP.entrySet().removeIf(entry -> Mth.abs(entry.getKey().x - playerPosX) > chunkRange * 2 || Mth.abs(entry.getKey().y - playerPosY) > chunkRange * 2 || Mth.abs(entry.getKey().z - playerPosZ) > chunkRange * 2);
+ }
+ }
+ }
+ } catch (Throwable throwable) {
+ LogManager.getLogger().throwing(throwable);
+ }
+ }
+
+ private void processChunk(CubicChunkPos pos, int playerPosX, int playerPosY, int playerPosZ, CollisionContext context) {
+ CALCULATING_POS.remove(pos);
+ int chunkRange = LightOverlay.getChunkRange();
+ if (Mth.abs(pos.x - playerPosX) > chunkRange || Mth.abs(pos.y - playerPosY) > Math.max(1, chunkRange >> 1) || Mth.abs(pos.z - playerPosZ) > chunkRange || POS.contains(pos)) {
+ return;
+ }
+ try {
+ calculateChunk(minecraft.level.getChunkSource().getChunk(pos.x, pos.z, ChunkStatus.FULL, false), minecraft.level, pos, context);
+ } catch (Throwable throwable) {
+ LogManager.getLogger().throwing(throwable);
+ }
+ }
+
+ private void calculateChunk(LevelChunk chunk, Level world, CubicChunkPos chunkPos, CollisionContext collisionContext) {
+ if (world != null && chunk != null) {
+ Long2ByteMap chunkData = new Long2ByteOpenHashMap();
+ LayerLightEventListener block = world.getLightEngine().getLayerListener(LightLayer.BLOCK);
+ LayerLightEventListener sky = LightOverlay.showNumber ? null : world.getLightEngine().getLayerListener(LightLayer.SKY);
+ for (BlockPos pos : BlockPos.betweenClosed(chunkPos.getMinBlockX(), chunkPos.getMinBlockY(), chunkPos.getMinBlockZ(), chunkPos.getMaxBlockX(), chunkPos.getMaxBlockY(), chunkPos.getMaxBlockZ())) {
+ BlockPos down = pos.below();
+ if (LightOverlay.showNumber) {
+ int level = getCrossLevel(pos, down, chunk, block, collisionContext);
+ if (level >= 0) {
+ chunkData.put(pos.asLong(), (byte) level);
+ }
+ } else {
+ Biome biome = !LightOverlay.mushroom ? world.getBiome(pos) : null;
+ byte type = getCrossType(pos, biome, down, chunk, block, sky, collisionContext);
+ if (type != LightOverlay.CROSS_NONE) {
+ chunkData.put(pos.asLong(), type);
+ }
+ }
+ }
+ CHUNK_MAP.put(chunkPos, chunkData);
+ } else {
+ CHUNK_MAP.remove(chunkPos);
+ }
+ }
+
+ public byte getCrossType(BlockPos pos, Biome biome, BlockPos down, BlockGetter world, LayerLightEventListener block, LayerLightEventListener sky, CollisionContext entityContext) {
+ BlockState blockBelowState = world.getBlockState(down);
+ BlockState blockUpperState = world.getBlockState(pos);
+ VoxelShape upperCollisionShape = blockUpperState.getCollisionShape(world, pos, entityContext);
+ if (!LightOverlay.underwater && !blockUpperState.getFluidState().isEmpty())
+ return LightOverlay.CROSS_NONE;
+ // Check if the outline is full
+ if (Block.isFaceFull(upperCollisionShape, Direction.UP))
+ return LightOverlay.CROSS_NONE;
+ // TODO: Not to hard code no redstone
+ if (blockUpperState.isSignalSource())
+ return LightOverlay.CROSS_NONE;
+ // Check if the collision has a bump
+ if (upperCollisionShape.max(Direction.Axis.Y) > 0)
+ return LightOverlay.CROSS_NONE;
+ if (blockUpperState.is(BlockTags.RAILS))
+ return LightOverlay.CROSS_NONE;
+ // Check block state allow spawning (excludes bedrock and barriers automatically)
+ if (!blockBelowState.isValidSpawn(world, down, TESTING_ENTITY_TYPE.get()))
+ return LightOverlay.CROSS_NONE;
+ if (!LightOverlay.mushroom && Biome.BiomeCategory.MUSHROOM == biome.getBiomeCategory())
+ return LightOverlay.CROSS_NONE;
+ int blockLightLevel = block.getLightValue(pos);
+ int skyLightLevel = sky.getLightValue(pos);
+ if (blockLightLevel > LightOverlay.higherCrossLevel)
+ return LightOverlay.CROSS_NONE;
+ if (skyLightLevel > LightOverlay.higherCrossLevel)
+ return LightOverlay.CROSS_YELLOW;
+ return LightOverlay.lowerCrossLevel >= 0 && blockLightLevel > LightOverlay.lowerCrossLevel ? LightOverlay.CROSS_SECONDARY : LightOverlay.CROSS_RED;
+ }
+
+ public static int getCrossLevel(BlockPos pos, BlockPos down, BlockGetter world, LayerLightEventListener view, CollisionContext collisionContext) {
+ BlockState blockBelowState = world.getBlockState(down);
+ BlockState blockUpperState = world.getBlockState(pos);
+ VoxelShape collisionShape = blockBelowState.getCollisionShape(world, down, collisionContext);
+ VoxelShape upperCollisionShape = blockUpperState.getCollisionShape(world, pos, collisionContext);
+ if (!LightOverlay.underwater && !blockUpperState.getFluidState().isEmpty())
+ return -1;
+ if (!blockBelowState.getFluidState().isEmpty())
+ return -1;
+ if (blockBelowState.isAir())
+ return -1;
+ if (Block.isFaceFull(upperCollisionShape, Direction.DOWN))
+ return -1;
+ return view.getLightValue(pos);
+ }
+}
plugins {
- id "com.github.johnrengelman.shadow" version "5.0.0"
+ id "com.github.johnrengelman.shadow" version "7.0.0"
}
minecraft {
}
architectury {
- transformerVersion = "2.0.9999"
platformSetupLoomIde()
fabric()
}
}
configurations {
- shadow
-}
-
-repositories {
- mavenLocal()
+ shadowCommon
}
dependencies {
minecraft "com.mojang:minecraft:${rootProject.architectury.minecraft}"
mappings minecraft.officialMojangMappings()
- modCompile "net.fabricmc:fabric-loader:${rootProject.fabric_loader_version}"
+ modImplementation "net.fabricmc:fabric-loader:${rootProject.fabric_loader_version}"
- modCompile "net.fabricmc.fabric-api:fabric-api:${rootProject.fabric_api_version}"
+ modImplementation "net.fabricmc.fabric-api:fabric-api:${rootProject.fabric_api_version}"
modImplementation("me.shedaniel.cloth:cloth-config-fabric:${rootProject.cloth_config_version}") {
exclude(group: "net.fabricmc.fabric-api")
}
- modImplementation("io.github.prospector:modmenu:${rootProject.modmenu_version}") {
+ modImplementation("com.terraformersmc:modmenu:${rootProject.modmenu_version}") {
transitive = false
}
- modCompile("me.shedaniel:architectury:${rootProject.architectury_version}:fabric")
+ modImplementation("dev.architectury:architectury-fabric:${rootProject.architectury_version}")
implementation(project(path: ":common")) {
transitive = false
developmentFabric(project(path: ":common")) {
transitive = false
}
- shadow(project(path: ":common", configuration: "transformProductionFabric")) {
+ shadowCommon(project(path: ":common", configuration: "transformProductionFabric")) {
transitive = false
}
}
shadowJar {
- relocate "me.shedaniel.lightoverlay.common", "me.shedaniel.lightoverlay.common.fabric"
-
- configurations = [project.configurations.shadow]
+ configurations = [project.configurations.shadowCommon]
classifier "shadow"
}
package me.shedaniel.lightoverlay.fabric;
+import com.mojang.blaze3d.vertex.PoseStack;
import me.shedaniel.lightoverlay.common.LightOverlay;
import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderEvents;
import net.minecraft.client.renderer.culling.Frustum;
+import java.util.function.Consumer;
+
public class LightOverlayImpl {
- public static Runnable debugRenderer = () -> {};
+ public static Consumer<PoseStack> debugRenderer = poses -> {};
public static void init() {
LightOverlay.register();
- WorldRenderEvents.AFTER_SETUP.register(context -> LightOverlay.frustum = context.frustum());
- WorldRenderEvents.BEFORE_DEBUG_RENDER.register(context -> debugRenderer.run());
+ WorldRenderEvents.AFTER_SETUP.register(context -> LightOverlay.renderer.frustum = context.frustum());
+ WorldRenderEvents.BEFORE_DEBUG_RENDER.register(context -> debugRenderer.accept(context.matrixStack()));
}
public static boolean isFrustumVisible(Frustum frustum, double minX, double minY, double minZ, double maxX, double maxY, double maxZ) {
import me.shedaniel.lightoverlay.common.CubicChunkPos;
import me.shedaniel.lightoverlay.common.LightOverlay;
+import net.minecraft.client.multiplayer.ClientPacketListener;
import net.minecraft.network.Connection;
import net.minecraft.network.PacketListener;
import net.minecraft.network.protocol.Packet;
import net.minecraft.network.protocol.game.ClientboundLightUpdatePacket;
import net.minecraft.network.protocol.game.ClientboundSectionBlocksUpdatePacket;
import net.minecraft.network.protocol.game.ClientboundSetChunkCacheCenterPacket;
+import net.minecraft.util.Mth;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
@Mixin(Connection.class)
public class MixinClientConnection {
@Inject(method = "genericsFtw", at = @At("HEAD"))
- private static void handlePacket(Packet packet, PacketListener listener, CallbackInfo ci) {
- if (packet instanceof ClientboundBlockUpdatePacket) {
- LightOverlay.queueChunkAndNear(new CubicChunkPos(((ClientboundBlockUpdatePacket) packet).getPos()));
- } else if (packet instanceof ClientboundSetChunkCacheCenterPacket) {
- for (int y = 0; y <= 15; y++) {
- LightOverlay.queueChunkAndNear(new CubicChunkPos(((ClientboundSetChunkCacheCenterPacket) packet).getX(), y, ((ClientboundSetChunkCacheCenterPacket) packet).getZ()));
- }
- } else if (packet instanceof ClientboundSectionBlocksUpdatePacket) {
- for (int y = 0; y <= 15; y++) {
- LightOverlay.queueChunkAndNear(new CubicChunkPos(((ClientboundSectionBlocksUpdatePacket) packet).sectionPos.getX(), y, ((ClientboundSectionBlocksUpdatePacket) packet).sectionPos.getZ()));
- }
- } else if (packet instanceof ClientboundLightUpdatePacket) {
- for (int y = 0; y <= 15; y++) {
- LightOverlay.queueChunk(new CubicChunkPos(((ClientboundLightUpdatePacket) packet).getX(), y, ((ClientboundLightUpdatePacket) packet).getZ()));
+ private static void handlePacket(Packet<?> packet, PacketListener listener, CallbackInfo ci) {
+ try {
+ if (!(listener instanceof ClientPacketListener packetListener)) return;
+ var level = packetListener.getLevel();
+ if (level == null) return;
+ if (packet instanceof ClientboundBlockUpdatePacket p) {
+ LightOverlay.queueChunkAndNear(new CubicChunkPos(p.getPos()));
+ } else if (packet instanceof ClientboundSetChunkCacheCenterPacket p) {
+ var height = Mth.ceil(level.getHeight() / 32.0);
+ var start = Math.floorDiv(level.getMinBuildHeight(), 32);
+ for (int y = start; y < start + height; y++) {
+ LightOverlay.queueChunkAndNear(new CubicChunkPos(p.getX(), y, p.getZ()));
+ }
+ } else if (packet instanceof ClientboundSectionBlocksUpdatePacket p) {
+ LightOverlay.queueChunkAndNear(new CubicChunkPos(p.sectionPos.getX(), p.sectionPos.getY() >> 1, p.sectionPos.getZ()));
+ } else if (packet instanceof ClientboundLightUpdatePacket p) {
+ var height = Mth.ceil(level.getHeight() / 32.0);
+ var start = Math.floorDiv(level.getMinBuildHeight(), 32);
+ for (int y = start; y < start + height; y++) {
+ LightOverlay.queueChunk(new CubicChunkPos(p.getX(), y, p.getZ()));
+ }
}
+ } catch (Throwable throwable) {
+ new RuntimeException("Light Overlay failed to process packet", throwable).printStackTrace();
}
}
}
},
"depends": {
"fabric": ">=0.29.1",
- "architectury": ">=1.5.112",
- "cloth-config2": ">=4.10.13"
+ "architectury": ">=2-",
+ "cloth-config2": ">=5-"
}
}
}
architectury {
- transformerVersion = "2.0.9999"
platformSetupLoomIde()
forge()
}
inputs.property "META-INF/mods.toml", project.version
}
-repositories {
- mavenLocal()
-}
-
dependencies {
minecraft("com.mojang:minecraft:${rootProject.architectury.minecraft}")
mappings(minecraft.officialMojangMappings())
org.gradle.jvmargs=-Xmx3G
org.gradle.daemon=false
-mod_version=5.8.1
-minecraft_version=1.16.4
+mod_version=6.0.0
+minecraft_version=1.17
-architectury_version=1.5.112
+architectury_version=2.0.9
# fabric
-fabric_loader_version=0.10.8
-fabric_api_version=0.29.1+1.16
-cloth_config_version=4.10.13
-modmenu_version=1.14.6+build.31
+fabric_loader_version=0.11.6
+fabric_api_version=0.35.1+1.17
+cloth_config_version=5.0.34
+modmenu_version=2.0.2
# forge
forge_version=35.1.7
\ No newline at end of file
-distributionUrl=https\://services.gradle.org/distributions/gradle-5.5.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-all.zip
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStorePath=wrapper/dists
include("common")
include("fabric")
-include("forge")
+//include("forge")