]> git.lizzy.rs Git - LightOverlay.git/commitdiff
New module system
authorshedaniel <daniel@shedaniel.me>
Mon, 11 May 2020 18:40:02 +0000 (02:40 +0800)
committershedaniel <daniel@shedaniel.me>
Mon, 11 May 2020 18:40:02 +0000 (02:40 +0800)
Signed-off-by: shedaniel <daniel@shedaniel.me>
38 files changed:
.gitignore
README.md
build.gradle
fabric/.gradle/loom-cache/1.15.2-projectmapped-net.fabricmc.yarn-1.15.2+build.15-v2/minecraft-1.15.2-projectmapped-net.fabricmc.yarn-1.15.2+build.15-v2.jar [new file with mode: 0644]
fabric/build.gradle [new file with mode: 0755]
fabric/src/main/java/me/shedaniel/lightoverlay/fabric/LOModMenuEntry.java [new file with mode: 0644]
fabric/src/main/java/me/shedaniel/lightoverlay/fabric/LightOverlay.java [new file with mode: 0644]
fabric/src/main/java/me/shedaniel/lightoverlay/fabric/mixin/MixinClientConnection.java [new file with mode: 0644]
fabric/src/main/resources/assets/lightoverlay/lang/en_us.json [new file with mode: 0755]
fabric/src/main/resources/fabric.mod.json [new file with mode: 0755]
fabric/src/main/resources/icon.png [new file with mode: 0644]
fabric/src/main/resources/lightoverlay.accesswidener [new file with mode: 0644]
fabric/src/main/resources/lightoverlay.mixins.json [new file with mode: 0644]
forge/build.gradle [new file with mode: 0644]
forge/src/main/java/me/shedaniel/lightoverlay/forge/LightOverlay.java [new file with mode: 0644]
forge/src/main/java/me/shedaniel/lightoverlay/forge/LightOverlayClient.java [new file with mode: 0644]
forge/src/main/java/me/shedaniel/lightoverlay/forge/LightOverlayCloth.java [new file with mode: 0644]
forge/src/main/resources/META-INF/NetworkManager.js [new file with mode: 0644]
forge/src/main/resources/META-INF/WorldRenderer.js [new file with mode: 0644]
forge/src/main/resources/META-INF/coremods.json [new file with mode: 0644]
forge/src/main/resources/META-INF/mods.toml [new file with mode: 0755]
forge/src/main/resources/assets/lightoverlay/lang/en_us.json [new file with mode: 0755]
forge/src/main/resources/icon.png [new file with mode: 0644]
forge/src/main/resources/pack.mcmeta [new file with mode: 0755]
gradle.properties
gradle/wrapper/gradle-wrapper.properties
merging.policy [new file with mode: 0644]
settings.gradle [new file with mode: 0644]
src/main/java/me/shedaniel/lightoverlay/LightOverlay.java [deleted file]
src/main/java/me/shedaniel/lightoverlay/LightOverlayClient.java [deleted file]
src/main/java/me/shedaniel/lightoverlay/LightOverlayCloth.java [deleted file]
src/main/resources/META-INF/NetworkManager.js [deleted file]
src/main/resources/META-INF/WorldRenderer.js [deleted file]
src/main/resources/META-INF/coremods.json [deleted file]
src/main/resources/META-INF/mods.toml [deleted file]
src/main/resources/assets/lightoverlay-forge/lang/en_us.json [deleted file]
src/main/resources/lightoverlay_icon_lowres.png [deleted file]
src/main/resources/pack.mcmeta [deleted file]

index f9dae6bd4b07dc7c53210a7c8b40beeec29c4181..06b3b34eff7286696f567943348c3f9c03f601a2 100644 (file)
@@ -1,9 +1,11 @@
 # Compiled nonsense that does not belong in *source* control
+*/build
 /build
 /bin
 /.gradle
 /minecraft
 /out
+*/run
 /run
 /classes
 
index 4a9d71432ea48124dd276e28aa8715a4ba4d570b..381a798f0147e7e5f4c00b3cf34debd29a42a28f 100644 (file)
--- a/README.md
+++ b/README.md
@@ -1 +1,12 @@
-LightOverlay-Forge
+# LightOverlay
+
+Fabric and Forge are split into modules, and we merge it afterwards
+
+# Running
+Fabric: `gradlew :fabric:runClient`<br>
+Forge: `gradlew :forge:runClient`
+
+Or use the run configs if you are using IntelliJ IDEA.
+
+# Compiling
+Run `gradlew buildMerged`, and the merged jar will be in `build/libs/`.
\ No newline at end of file
index f1a0fce279e501f0e317900d4cda6530ca767969..718020b88494f6584397ed0f24924b54e6a9dc24 100644 (file)
 buildscript {
     repositories {
-        maven { url = 'https://files.minecraftforge.net/maven' }
-        jcenter()
         mavenCentral()
     }
     dependencies {
-        classpath group: 'net.minecraftforge.gradle', name: 'ForgeGradle', version: '3.+', changing: true
+        classpath("commons-io:commons-io:2.6")
     }
 }
 
+import org.jetbrains.gradle.ext.ActionDelegationConfig
+
+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 "com.wynprice.cursemaven" version "2.1.1"
+    id "org.jetbrains.gradle.plugin.idea-ext" version "0.7"
 }
 
-apply plugin: 'net.minecraftforge.gradle'
-apply plugin: 'eclipse'
+allprojects {
+    apply plugin: "java"
 
-version = "4.6.1"
-group = "me.shedaniel" // http://maven.apache.org/guides/mini/guide-naming-conventions.html
-archivesBaseName = "LightOverlay"
+    group "me.shedaniel"
+    archivesBaseName = rootProject.name
+    version = rootProject.mod_version
 
-sourceCompatibility = targetCompatibility = compileJava.sourceCompatibility = compileJava.targetCompatibility = '1.8'
+    sourceCompatibility = targetCompatibility = compileJava.sourceCompatibility = compileJava.targetCompatibility = JavaVersion.VERSION_1_8
+}
 
-minecraft {
-    mappings channel: 'snapshot', version: '20200408-1.15.1'
-    runs {
-        client {
-            workingDirectory project.file('run')
-            // Recommended logging data for a userdev environment
-            property 'forge.logging.markers', 'SCAN,REGISTRIES,REGISTRYDUMP'
-            // Recommended logging level for the console
-            property 'forge.logging.console.level', 'debug'
-            mods {
-                examplemod {
-                    source sourceSets.main
-                }
-            }
+idea.project.settings {
+    delegateActions {
+        delegateBuildRunToGradle = false
+        testRunner = ActionDelegationConfig.TestRunner.PLATFORM
+    }
+    runConfigurations {
+        "Fabric: Minecraft Client"(org.jetbrains.gradle.ext.Gradle) {
+            project = rootProject.project(":fabric")
+            taskNames = Collections.singletonList("runClient")
         }
-        server {
-            workingDirectory project.file('run')
-            // Recommended logging data for a userdev environment
-            property 'forge.logging.markers', 'SCAN,REGISTRIES,REGISTRYDUMP'
-            // Recommended logging level for the console
-            property 'forge.logging.console.level', 'debug'
-            mods {
-                examplemod {
-                    source sourceSets.main
-                }
-            }
+        "Fabric: Minecraft Server"(org.jetbrains.gradle.ext.Gradle) {
+            project = rootProject.project(":fabric")
+            taskNames = Collections.singletonList("runServer")
+        }
+        "Forge: Minecraft Client"(org.jetbrains.gradle.ext.Gradle) {
+            project = rootProject.project(":forge")
+            taskNames = Collections.singletonList("runClient")
+        }
+        "Forge: Minecraft Server"(org.jetbrains.gradle.ext.Gradle) {
+            project = rootProject.project(":forge")
+            taskNames = Collections.singletonList("runServer")
         }
     }
 }
 
-repositories {
-    maven { url "https://files.minecraftforge.net/maven" }
+task buildMerged {
+    doLast {
+        def folder = file(".gradle/.mergemods")
+        folder.mkdirs()
+        def fabricJar = file("fabric/build/libs/${rootProject.name}-${rootProject.mod_version}.jar")
+        def forgeJar = file("forge/build/libs/${rootProject.name}-${rootProject.mod_version}.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(), "")
+                                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()
+    }
 }
 
-dependencies {
-    minecraft 'net.minecraftforge:forge:1.15.2-31.1.39'
-    implementation fg.deobf("curse.maven:cloth-config-forge:2938583")
-}
+buildMerged.mustRunAfter project(":fabric").tasks.getByName("build")
+buildMerged.mustRunAfter project(":forge").tasks.getByName("build")
 
-jar {
-    manifest {
-        attributes(["Specification-Title"     : "examplemod",
-                    "Specification-Vendor"    : "examplemodsareus",
-                    "Specification-Version"   : "1", // We are version 1 of the modlauncher specification
-                    "Implementation-Title"    : project.name,
-                    "Implementation-Version"  : "${version}",
-                    "Implementation-Vendor"   : "examplemodsareus",
-                    "Implementation-Timestamp": new Date().format("yyyy-MM-dd'T'HH:mm:ssZ")],)
+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()
     }
 }
+
+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
diff --git a/fabric/.gradle/loom-cache/1.15.2-projectmapped-net.fabricmc.yarn-1.15.2+build.15-v2/minecraft-1.15.2-projectmapped-net.fabricmc.yarn-1.15.2+build.15-v2.jar b/fabric/.gradle/loom-cache/1.15.2-projectmapped-net.fabricmc.yarn-1.15.2+build.15-v2/minecraft-1.15.2-projectmapped-net.fabricmc.yarn-1.15.2+build.15-v2.jar
new file mode 100644 (file)
index 0000000..48d7027
Binary files /dev/null and b/fabric/.gradle/loom-cache/1.15.2-projectmapped-net.fabricmc.yarn-1.15.2+build.15-v2/minecraft-1.15.2-projectmapped-net.fabricmc.yarn-1.15.2+build.15-v2.jar differ
diff --git a/fabric/build.gradle b/fabric/build.gradle
new file mode 100755 (executable)
index 0000000..7e6d021
--- /dev/null
@@ -0,0 +1,39 @@
+plugins {
+    id("fabric-loom") version "0.4.3"
+}
+
+minecraft {
+    accessWidener = file("src/main/resources/lightoverlay.accesswidener")
+}
+
+processResources {
+    filesMatching("fabric.mod.json") {
+        expand "version": project.version
+    }
+    inputs.property "version", project.version
+}
+
+repositories {
+    jcenter()
+}
+
+dependencies {
+    minecraft "com.mojang:minecraft:${rootProject.fabric_minecraft_version}"
+    mappings "net.fabricmc:yarn:${rootProject.fabric_minecraft_version}+build.${rootProject.yarn_build}:v2"
+    modCompile "net.fabricmc:fabric-loader:${rootProject.fabric_loader_version}"
+
+    modCompile "net.fabricmc.fabric-api:fabric-api:${rootProject.fabric_api_version}"
+    modCompile("me.shedaniel.cloth:cloth-events:${rootProject.cloth_events_version}") {
+        transitive = false
+    }
+    modImplementation("me.shedaniel.cloth:config-2:${rootProject.cloth_config_version}")
+    modImplementation("io.github.prospector:modmenu:${rootProject.modmenu_version}") {
+        transitive = false
+    }
+    include("me.shedaniel.cloth:cloth-events:${rootProject.cloth_events_version}") {
+        transitive = false
+    }
+    include("me.shedaniel.cloth:config-2:${rootProject.cloth_config_version}") {
+        transitive = false
+    }
+}
diff --git a/fabric/src/main/java/me/shedaniel/lightoverlay/fabric/LOModMenuEntry.java b/fabric/src/main/java/me/shedaniel/lightoverlay/fabric/LOModMenuEntry.java
new file mode 100644 (file)
index 0000000..b381757
--- /dev/null
@@ -0,0 +1,46 @@
+package me.shedaniel.lightoverlay.fabric;
+
+import io.github.prospector.modmenu.api.ModMenuApi;
+import me.shedaniel.clothconfig2.api.ConfigBuilder;
+import me.shedaniel.clothconfig2.api.ConfigCategory;
+import me.shedaniel.clothconfig2.api.ConfigEntryBuilder;
+import net.minecraft.client.gui.screen.Screen;
+import net.minecraft.util.math.MathHelper;
+
+import java.util.function.Function;
+
+public class LOModMenuEntry implements ModMenuApi {
+    @Override
+    public String getModId() {
+        return "lightoverlay";
+    }
+    
+    @Override
+    public Function<Screen, ? extends Screen> getConfigScreenFactory() {
+        return this::getConfigScreenByCloth;
+    }
+    
+    public Screen getConfigScreenByCloth(Screen parent) {
+        ConfigBuilder builder = ConfigBuilder.create().setParentScreen(parent).setTitle("key.lightoverlay.category");
+        
+        ConfigEntryBuilder eb = builder.entryBuilder();
+        ConfigCategory general = builder.getOrCreateCategory("config.lightoverlay.general");
+        general.addEntry(eb.startIntSlider("config.lightoverlay.reach", LightOverlay.reach, 1, 64).setDefaultValue(12).setTextGetter(integer -> "Reach: " + integer + " Blocks").setSaveConsumer(integer -> LightOverlay.reach = integer).build());
+        general.addEntry(eb.startIntSlider("config.lightoverlay.crossLevel", LightOverlay.crossLevel, 0, 15).setDefaultValue(7).setTextGetter(integer -> "Cross Level: " + integer).setSaveConsumer(integer -> LightOverlay.crossLevel = integer).build());
+        general.addEntry(eb.startBooleanToggle("config.lightoverlay.showNumber", LightOverlay.showNumber).setDefaultValue(false).setSaveConsumer(bool -> LightOverlay.showNumber = bool).build());
+        general.addEntry(eb.startBooleanToggle("config.lightoverlay.smoothLines", LightOverlay.smoothLines).setDefaultValue(true).setSaveConsumer(bool -> LightOverlay.smoothLines = bool).build());
+        general.addEntry(eb.startBooleanToggle("config.lightoverlay.underwater", LightOverlay.underwater).setDefaultValue(false).setSaveConsumer(bool -> LightOverlay.underwater = bool).build());
+        general.addEntry(eb.startIntSlider("config.lightoverlay.lineWidth", MathHelper.floor(LightOverlay.lineWidth * 100), 100, 700).setDefaultValue(100).setTextGetter(integer -> "Light Width: " + LightOverlay.FORMAT.format(integer / 100d)).setSaveConsumer(integer -> LightOverlay.lineWidth = integer / 100f).build());
+        general.addEntry(eb.startColorField("config.lightoverlay.yellowColor", LightOverlay.yellowColor).setDefaultValue(0xFFFF00).setSaveConsumer(color -> LightOverlay.yellowColor = color).build());
+        general.addEntry(eb.startColorField("config.lightoverlay.redColor", LightOverlay.redColor).setDefaultValue(0xFF0000).setSaveConsumer(color -> LightOverlay.redColor = color).build());
+        
+        return builder.setSavingRunnable(() -> {
+            try {
+                LightOverlay.saveConfig(LightOverlay.configFile);
+            } catch (Exception e) {
+                e.printStackTrace();
+            }
+            LightOverlay.loadConfig(LightOverlay.configFile);
+        }).build();
+    }
+}
diff --git a/fabric/src/main/java/me/shedaniel/lightoverlay/fabric/LightOverlay.java b/fabric/src/main/java/me/shedaniel/lightoverlay/fabric/LightOverlay.java
new file mode 100644 (file)
index 0000000..28883f8
--- /dev/null
@@ -0,0 +1,466 @@
+package me.shedaniel.lightoverlay.fabric;
+
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.mojang.blaze3d.platform.GlStateManager;
+import com.mojang.blaze3d.systems.RenderSystem;
+import me.shedaniel.cloth.hooks.ClothClientHooks;
+import net.fabricmc.api.ClientModInitializer;
+import net.fabricmc.fabric.api.client.keybinding.FabricKeyBinding;
+import net.fabricmc.fabric.api.client.keybinding.KeyBindingRegistry;
+import net.fabricmc.fabric.api.event.client.ClientTickCallback;
+import net.fabricmc.loader.api.FabricLoader;
+import net.minecraft.block.Block;
+import net.minecraft.block.BlockState;
+import net.minecraft.client.MinecraftClient;
+import net.minecraft.client.font.TextRenderer;
+import net.minecraft.client.network.ClientPlayerEntity;
+import net.minecraft.client.render.*;
+import net.minecraft.client.util.InputUtil;
+import net.minecraft.client.util.math.Rotation3;
+import net.minecraft.client.world.ClientWorld;
+import net.minecraft.entity.Entity;
+import net.minecraft.entity.EntityCategory;
+import net.minecraft.entity.EntityContext;
+import net.minecraft.entity.EntityType;
+import net.minecraft.entity.player.PlayerEntity;
+import net.minecraft.tag.BlockTags;
+import net.minecraft.text.TranslatableText;
+import net.minecraft.util.Identifier;
+import net.minecraft.util.math.*;
+import net.minecraft.util.shape.VoxelShape;
+import net.minecraft.world.BlockView;
+import net.minecraft.world.LightType;
+import net.minecraft.world.World;
+import net.minecraft.world.biome.Biome;
+import net.minecraft.world.chunk.ChunkStatus;
+import net.minecraft.world.chunk.WorldChunk;
+import net.minecraft.world.chunk.light.ChunkLightingView;
+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.text.DecimalFormat;
+import java.util.*;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+public class LightOverlay implements ClientModInitializer {
+    static final DecimalFormat FORMAT = new DecimalFormat("#.#");
+    private static final String KEYBIND_CATEGORY = "key.lightoverlay.category";
+    private static final Identifier ENABLE_OVERLAY_KEYBIND = new Identifier("lightoverlay", "enable_overlay");
+    private static final Identifier INCREASE_REACH_KEYBIND = new Identifier("lightoverlay", "increase_reach");
+    private static final Identifier DECREASE_REACH_KEYBIND = new Identifier("lightoverlay", "decrease_reach");
+    private static final Identifier INCREASE_LINE_WIDTH_KEYBIND = new Identifier("lightoverlay", "increase_line_width");
+    private static final Identifier DECREASE_LINE_WIDTH_KEYBIND = new Identifier("lightoverlay", "decrease_line_width");
+    static int reach = 12;
+    static int crossLevel = 7;
+    static boolean showNumber = false;
+    static boolean smoothLines = true;
+    static boolean underwater = false;
+    static float lineWidth = 1.0F;
+    static int yellowColor = 0xFFFF00, redColor = 0xFF0000;
+    static File configFile = new File(FabricLoader.getInstance().getConfigDirectory(), "lightoverlay.properties");
+    private static FabricKeyBinding enableOverlay, increaseReach, decreaseReach, increaseLineWidth, decreaseLineWidth;
+    private static boolean enabled = false;
+    private static EntityType<Entity> testingEntityType;
+    private static int threadNumber = 0;
+    private static final ExecutorService EXECUTOR = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors(), r -> {
+        Thread thread = new Thread(r, "light-overlay-" + threadNumber++);
+        thread.setDaemon(true);
+        return thread;
+    });
+    private static final List<ChunkPos> POS = Lists.newCopyOnWriteArrayList();
+    private static final Map<ChunkPos, Map<Long, Object>> CHUNK_MAP = Maps.newConcurrentMap();
+    private static long ticks = 0;
+    
+    static {
+        ClientTickCallback.EVENT.register(client -> {
+            try {
+                ticks++;
+                if (MinecraftClient.getInstance().player == null || !enabled) {
+                    POS.clear();
+                    CHUNK_MAP.clear();
+                } else {
+                    ClientPlayerEntity player = MinecraftClient.getInstance().player;
+                    ClientWorld world = MinecraftClient.getInstance().world;
+                    EntityContext entityContext = EntityContext.of(player);
+                    Vec3d[] playerPos = {null};
+                    int playerPosX = ((int) player.getX()) >> 4;
+                    int playerPosZ = ((int) player.getZ()) >> 4;
+                    if (ticks % 20 == 0) {
+                        for (int chunkX = playerPosX - getChunkRange(); chunkX <= playerPosX + getChunkRange(); chunkX++) {
+                            for (int chunkZ = playerPosZ - getChunkRange(); chunkZ <= playerPosZ + getChunkRange(); chunkZ++) {
+                                ChunkPos chunkPos = new ChunkPos(chunkX, chunkZ);
+                                if (!CHUNK_MAP.containsKey(chunkPos))
+                                    queueChunk(chunkPos);
+                            }
+                        }
+                    }
+                    if (!POS.isEmpty()) {
+                        if (playerPos[0] == null) {
+                            playerPos[0] = player.getPos();
+                        }
+                        ChunkPos pos = POS.stream().min(Comparator.comparingDouble(value -> value.toBlockPos(8, 0, 8).getSquaredDistance(playerPos[0].x, 0, playerPos[0].z, false))).get();
+                        EXECUTOR.submit(() -> {
+                            if (MathHelper.abs(pos.x - playerPosX) <= getChunkRange() && MathHelper.abs(pos.z - playerPosZ) <= getChunkRange()) {
+                                calculateChunk(world.getChunkManager().getChunk(pos.x, pos.z, ChunkStatus.FULL, false), world, pos, entityContext);
+                            } else {
+                                CHUNK_MAP.remove(pos);
+                            }
+                        });
+                        POS.remove(pos);
+                    }
+                    Iterator<Map.Entry<ChunkPos, Map<Long, Object>>> chunkMapIterator = CHUNK_MAP.entrySet().iterator();
+                    while (chunkMapIterator.hasNext()) {
+                        Map.Entry<ChunkPos, Map<Long, Object>> pos = chunkMapIterator.next();
+                        if (MathHelper.abs(pos.getKey().x - playerPosX) > getChunkRange() * 2 || MathHelper.abs(pos.getKey().z - playerPosZ) > getChunkRange() * 2) {
+                            chunkMapIterator.remove();
+                        }
+                    }
+                }
+            } catch (Exception e) {
+                LogManager.getLogger().throwing(e);
+            }
+        });
+    }
+    
+    public static void queueChunkAndNear(ChunkPos pos) {
+        for (int xOffset = -1; xOffset <= 1; xOffset++) {
+            for (int zOffset = -1; zOffset <= 1; zOffset++) {
+                queueChunk(new ChunkPos(pos.x + xOffset, pos.z + zOffset));
+            }
+        }
+    }
+    
+    public static void queueChunk(ChunkPos pos) {
+        if (!POS.contains(pos))
+            POS.add(0, pos);
+    }
+    
+    public static int getChunkRange() {
+        return Math.max(MathHelper.ceil(reach / 16f), 1);
+    }
+    
+    private static void calculateChunk(WorldChunk chunk, World world, ChunkPos chunkPos, EntityContext entityContext) {
+        Map<Long, Object> map = Maps.newHashMap();
+        if (chunk != null) {
+            ChunkLightingView block = chunk.getLightingProvider().get(LightType.BLOCK);
+            ChunkLightingView sky = showNumber ? null : chunk.getLightingProvider().get(LightType.SKY);
+            for (BlockPos pos : BlockPos.iterate(chunkPos.getStartX(), 0, chunkPos.getStartZ(), chunkPos.getEndX(), 256, chunkPos.getEndZ())) {
+                BlockPos down = pos.down();
+                if (showNumber) {
+                    int level = LightOverlay.getCrossLevel(pos, down, chunk, block, entityContext);
+                    if (level >= 0) {
+                        map.put(pos.asLong(), level);
+                    }
+                } else {
+                    Biome biome = world.getBiomeAccess().getBiome(pos);
+                    if (biome.getMaxSpawnLimit() > 0 && !biome.getEntitySpawnList(EntityCategory.MONSTER).isEmpty()) {
+                        CrossType type = LightOverlay.getCrossType(pos, down, chunk, block, sky, entityContext);
+                        if (type != CrossType.NONE) {
+                            map.put(pos.asLong(), type);
+                        }
+                    }
+                }
+            }
+        }
+        CHUNK_MAP.put(chunkPos, map);
+    }
+    
+    public static CrossType getCrossType(BlockPos pos, BlockPos down, BlockView world, ChunkLightingView block, ChunkLightingView sky, EntityContext entityContext) {
+        BlockState blockBelowState = world.getBlockState(down);
+        BlockState blockUpperState = world.getBlockState(pos);
+        VoxelShape upperCollisionShape = blockUpperState.getCollisionShape(world, pos, entityContext);
+        if (!underwater && !blockUpperState.getFluidState().isEmpty())
+            return CrossType.NONE;
+        // Check if the outline is full
+        if (Block.isFaceFullSquare(upperCollisionShape, Direction.UP))
+            return CrossType.NONE;
+        // TODO: Not to hard code no redstone
+        if (blockUpperState.emitsRedstonePower())
+            return CrossType.NONE;
+        // Check if the collision has a bump
+        if (upperCollisionShape.getMaximum(Direction.Axis.Y) > 0)
+            return CrossType.NONE;
+        if (blockUpperState.getBlock().matches(BlockTags.RAILS))
+            return CrossType.NONE;
+        // Check block state allow spawning (excludes bedrock and barriers automatically)
+        if (!blockBelowState.allowsSpawning(world, down, testingEntityType))
+            return CrossType.NONE;
+        if (block.getLightLevel(pos) > crossLevel)
+            return CrossType.NONE;
+        if (sky.getLightLevel(pos) > crossLevel)
+            return CrossType.YELLOW;
+        return CrossType.RED;
+    }
+    
+    public static int getCrossLevel(BlockPos pos, BlockPos down, BlockView world, ChunkLightingView view, EntityContext entityContext) {
+        BlockState blockBelowState = world.getBlockState(down);
+        BlockState blockUpperState = world.getBlockState(pos);
+        VoxelShape collisionShape = blockBelowState.getCollisionShape(world, down, entityContext);
+        VoxelShape upperCollisionShape = blockUpperState.getCollisionShape(world, pos, entityContext);
+        if (!underwater && !blockUpperState.getFluidState().isEmpty())
+            return -1;
+        if (!blockBelowState.getFluidState().isEmpty())
+            return -1;
+        if (blockBelowState.isAir())
+            return -1;
+        if (Block.isFaceFullSquare(upperCollisionShape, Direction.DOWN))
+            return -1;
+        return view.getLightLevel(pos);
+    }
+    
+    public static void renderCross(Tessellator tessellator, BufferBuilder buffer, Camera camera, World world, BlockPos pos, int color, EntityContext entityContext) {
+        double d0 = camera.getPos().x;
+        double d1 = camera.getPos().y - .005D;
+        VoxelShape upperOutlineShape = world.getBlockState(pos).getOutlineShape(world, pos, entityContext);
+        if (!upperOutlineShape.isEmpty())
+            d1 -= upperOutlineShape.getMaximum(Direction.Axis.Y);
+        double d2 = camera.getPos().z;
+        
+        buffer.begin(1, VertexFormats.POSITION_COLOR);
+        int red = (color >> 16) & 255;
+        int green = (color >> 8) & 255;
+        int blue = color & 255;
+        buffer.vertex(pos.getX() + .01 - d0, pos.getY() - d1, pos.getZ() + .01 - d2).color(red, green, blue, 255).next();
+        buffer.vertex(pos.getX() - .01 + 1 - d0, pos.getY() - d1, pos.getZ() - .01 + 1 - d2).color(red, green, blue, 255).next();
+        buffer.vertex(pos.getX() - .01 + 1 - d0, pos.getY() - d1, pos.getZ() + .01 - d2).color(red, green, blue, 255).next();
+        buffer.vertex(pos.getX() + .01 - d0, pos.getY() - d1, pos.getZ() - .01 + 1 - d2).color(red, green, blue, 255).next();
+        tessellator.draw();
+    }
+    
+    public static void renderLevel(MinecraftClient client, Camera camera, World world, BlockPos pos, BlockPos down, int level, EntityContext entityContext) {
+        String string_1 = String.valueOf(level);
+        TextRenderer textRenderer_1 = client.textRenderer;
+        double double_4 = camera.getPos().x;
+        double double_5 = camera.getPos().y;
+        VoxelShape upperOutlineShape = world.getBlockState(down).getOutlineShape(world, down, entityContext);
+        if (!upperOutlineShape.isEmpty())
+            double_5 += 1 - upperOutlineShape.getMaximum(Direction.Axis.Y);
+        double double_6 = camera.getPos().z;
+        RenderSystem.pushMatrix();
+        RenderSystem.translatef((float) (pos.getX() + 0.5f - double_4), (float) (pos.getY() - double_5) + 0.005f, (float) (pos.getZ() + 0.5f - double_6));
+        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) (-textRenderer_1.getStringWidth(string_1)) / 2.0F + 0.4f;
+        RenderSystem.enableAlphaTest();
+        VertexConsumerProvider.Immediate vertexConsumerProvider$Immediate_1 = VertexConsumerProvider.immediate(Tessellator.getInstance().getBuffer());
+        textRenderer_1.draw(string_1, float_3, -3.5f, level > crossLevel ? 0xff042404 : 0xff731111, false, Rotation3.identity().getMatrix(), vertexConsumerProvider$Immediate_1, false, 0, 15728880);
+        vertexConsumerProvider$Immediate_1.draw();
+        RenderSystem.popMatrix();
+    }
+    
+    static void loadConfig(File file) {
+        try {
+            redColor = 0xFF0000;
+            yellowColor = 0xFFFF00;
+            if (!file.exists() || !file.canRead())
+                saveConfig(file);
+            FileInputStream fis = new FileInputStream(file);
+            Properties properties = new Properties();
+            properties.load(fis);
+            fis.close();
+            reach = Integer.parseInt((String) properties.computeIfAbsent("reach", a -> "12"));
+            crossLevel = Integer.parseInt((String) properties.computeIfAbsent("crossLevel", a -> "7"));
+            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");
+            lineWidth = Float.parseFloat((String) properties.computeIfAbsent("lineWidth", a -> "1"));
+            {
+                int r, g, b;
+                r = Integer.parseInt((String) properties.computeIfAbsent("yellowColorRed", a -> "255"));
+                g = Integer.parseInt((String) properties.computeIfAbsent("yellowColorGreen", a -> "255"));
+                b = Integer.parseInt((String) properties.computeIfAbsent("yellowColorBlue", a -> "0"));
+                yellowColor = (r << 16) + (g << 8) + b;
+            }
+            {
+                int r, g, b;
+                r = Integer.parseInt((String) properties.computeIfAbsent("redColorRed", a -> "255"));
+                g = Integer.parseInt((String) properties.computeIfAbsent("redColorGreen", a -> "0"));
+                b = Integer.parseInt((String) properties.computeIfAbsent("redColorBlue", a -> "0"));
+                redColor = (r << 16) + (g << 8) + b;
+            }
+            saveConfig(file);
+        } catch (Exception e) {
+            e.printStackTrace();
+            reach = 12;
+            crossLevel = 7;
+            lineWidth = 1.0F;
+            redColor = 0xFF0000;
+            yellowColor = 0xFFFF00;
+            showNumber = false;
+            smoothLines = true;
+            underwater = false;
+            try {
+                saveConfig(file);
+            } catch (IOException ex) {
+                ex.printStackTrace();
+            }
+        }
+        CHUNK_MAP.clear();
+        POS.clear();
+    }
+    
+    static void saveConfig(File file) throws IOException {
+        FileOutputStream fos = new FileOutputStream(file, false);
+        fos.write("# Light Overlay Config".getBytes());
+        fos.write("\n".getBytes());
+        fos.write(("reach=" + reach).getBytes());
+        fos.write("\n".getBytes());
+        fos.write(("crossLevel=" + crossLevel).getBytes());
+        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(("lineWidth=" + FORMAT.format(lineWidth)).getBytes());
+        fos.write("\n".getBytes());
+        fos.write(("yellowColorRed=" + ((yellowColor >> 16) & 255)).getBytes());
+        fos.write("\n".getBytes());
+        fos.write(("yellowColorGreen=" + ((yellowColor >> 8) & 255)).getBytes());
+        fos.write("\n".getBytes());
+        fos.write(("yellowColorBlue=" + (yellowColor & 255)).getBytes());
+        fos.write("\n".getBytes());
+        fos.write(("redColorRed=" + ((redColor >> 16) & 255)).getBytes());
+        fos.write("\n".getBytes());
+        fos.write(("redColorGreen=" + ((redColor >> 8) & 255)).getBytes());
+        fos.write("\n".getBytes());
+        fos.write(("redColorBlue=" + (redColor & 255)).getBytes());
+        fos.close();
+    }
+    
+    @Override
+    public void onInitializeClient() {
+        // Load Config
+        loadConfig(configFile);
+        
+        // Setup
+        testingEntityType = EntityType.Builder.create(EntityCategory.MONSTER).setDimensions(0f, 0f).disableSaving().build(null);
+        MinecraftClient client = MinecraftClient.getInstance();
+        KeyBindingRegistry.INSTANCE.addCategory(KEYBIND_CATEGORY);
+        KeyBindingRegistry.INSTANCE.register(enableOverlay = FabricKeyBinding.Builder.create(ENABLE_OVERLAY_KEYBIND, InputUtil.Type.KEYSYM, 296, KEYBIND_CATEGORY).build());
+        KeyBindingRegistry.INSTANCE.register(increaseReach = FabricKeyBinding.Builder.create(INCREASE_REACH_KEYBIND, InputUtil.Type.KEYSYM, -1, KEYBIND_CATEGORY).build());
+        KeyBindingRegistry.INSTANCE.register(decreaseReach = FabricKeyBinding.Builder.create(DECREASE_REACH_KEYBIND, InputUtil.Type.KEYSYM, -1, KEYBIND_CATEGORY).build());
+        KeyBindingRegistry.INSTANCE.register(increaseLineWidth = FabricKeyBinding.Builder.create(INCREASE_LINE_WIDTH_KEYBIND, InputUtil.Type.KEYSYM, -1, KEYBIND_CATEGORY).build());
+        KeyBindingRegistry.INSTANCE.register(decreaseLineWidth = FabricKeyBinding.Builder.create(DECREASE_LINE_WIDTH_KEYBIND, InputUtil.Type.KEYSYM, -1, KEYBIND_CATEGORY).build());
+        ClothClientHooks.HANDLE_INPUT.register(minecraftClient -> {
+            while (enableOverlay.wasPressed())
+                enabled = !enabled;
+            while (increaseReach.wasPressed()) {
+                if (reach < 64)
+                    reach++;
+                try {
+                    saveConfig(configFile);
+                } catch (IOException e) {
+                    e.printStackTrace();
+                }
+                client.player.addChatMessage(new TranslatableText("text.lightoverlay.current_reach", reach), false);
+            }
+            while (decreaseReach.wasPressed()) {
+                if (reach > 1)
+                    reach--;
+                try {
+                    saveConfig(configFile);
+                } catch (IOException e) {
+                    e.printStackTrace();
+                }
+                client.player.addChatMessage(new TranslatableText("text.lightoverlay.current_reach", reach), false);
+            }
+            while (increaseLineWidth.wasPressed()) {
+                if (lineWidth < 7)
+                    lineWidth += 0.1f;
+                try {
+                    saveConfig(configFile);
+                } catch (IOException e) {
+                    e.printStackTrace();
+                }
+                client.player.addChatMessage(new TranslatableText("text.lightoverlay.current_line_width", FORMAT.format(lineWidth)), false);
+            }
+            while (decreaseLineWidth.wasPressed()) {
+                if (lineWidth > 1)
+                    lineWidth -= 0.1F;
+                try {
+                    saveConfig(configFile);
+                } catch (IOException e) {
+                    e.printStackTrace();
+                }
+                client.player.addChatMessage(new TranslatableText("text.lightoverlay.current_line_width", FORMAT.format(lineWidth)), false);
+            }
+        });
+        ClothClientHooks.DEBUG_RENDER_PRE.register(() -> {
+            if (LightOverlay.enabled) {
+                PlayerEntity playerEntity = client.player;
+                int playerPosX = ((int) playerEntity.getX()) >> 4;
+                int playerPosZ = ((int) playerEntity.getZ()) >> 4;
+                EntityContext entityContext = EntityContext.of(playerEntity);
+                World world = client.world;
+                BlockPos playerPos = new BlockPos(playerEntity.getX(), playerEntity.getY(), playerEntity.getZ());
+                Camera camera = MinecraftClient.getInstance().gameRenderer.getCamera();
+                if (showNumber) {
+                    RenderSystem.enableTexture();
+                    RenderSystem.depthMask(true);
+                    BlockPos.Mutable mutable = new BlockPos.Mutable();
+                    for (Map.Entry<ChunkPos, Map<Long, Object>> entry : CHUNK_MAP.entrySet()) {
+                        if (MathHelper.abs(entry.getKey().x - playerPosX) > getChunkRange() || MathHelper.abs(entry.getKey().z - playerPosZ) > getChunkRange()) {
+                            continue;
+                        }
+                        for (Map.Entry<Long, Object> objectEntry : entry.getValue().entrySet()) {
+                            if (objectEntry.getValue() instanceof Integer) {
+                                mutable.set(BlockPos.unpackLongX(objectEntry.getKey()), BlockPos.unpackLongY(objectEntry.getKey()), BlockPos.unpackLongZ(objectEntry.getKey()));
+                                if (mutable.isWithinDistance(playerPos, reach)) {
+                                    BlockPos down = mutable.down();
+                                    LightOverlay.renderLevel(client, camera, world, mutable, down, (Integer) objectEntry.getValue(), entityContext);
+                                }
+                            }
+                        }
+                    }
+                    RenderSystem.color4f(1.0F, 1.0F, 1.0F, 1.0F);
+                    RenderSystem.enableDepthTest();
+                } else {
+                    RenderSystem.enableDepthTest();
+                    RenderSystem.disableTexture();
+                    RenderSystem.enableBlend();
+                    RenderSystem.blendFunc(GlStateManager.SrcFactor.SRC_ALPHA, GlStateManager.DstFactor.ONE_MINUS_SRC_ALPHA);
+                    RenderSystem.disableLighting();
+                    if (smoothLines) GL11.glEnable(GL11.GL_LINE_SMOOTH);
+                    RenderSystem.lineWidth(lineWidth);
+                    Tessellator tessellator = Tessellator.getInstance();
+                    BufferBuilder buffer = tessellator.getBuffer();
+                    BlockPos.Mutable mutable = new BlockPos.Mutable();
+                    for (Map.Entry<ChunkPos, Map<Long, Object>> entry : CHUNK_MAP.entrySet()) {
+                        if (MathHelper.abs(entry.getKey().x - playerPosX) > getChunkRange() || MathHelper.abs(entry.getKey().z - playerPosZ) > getChunkRange()) {
+                            continue;
+                        }
+                        for (Map.Entry<Long, Object> objectEntry : entry.getValue().entrySet()) {
+                            if (objectEntry.getValue() instanceof CrossType) {
+                                mutable.set(BlockPos.unpackLongX(objectEntry.getKey()), BlockPos.unpackLongY(objectEntry.getKey()), BlockPos.unpackLongZ(objectEntry.getKey()));
+                                if (mutable.isWithinDistance(playerPos, reach)) {
+                                    BlockPos down = mutable.down();
+                                    int color = objectEntry.getValue() == CrossType.RED ? redColor : yellowColor;
+                                    LightOverlay.renderCross(tessellator, buffer, camera, world, mutable, color, entityContext);
+                                }
+                            }
+                        }
+                    }
+                    RenderSystem.disableBlend();
+                    RenderSystem.enableTexture();
+                    if (smoothLines) GL11.glDisable(GL11.GL_LINE_SMOOTH);
+                }
+            }
+        });
+    }
+    
+    private enum CrossType {
+        YELLOW,
+        RED,
+        NONE
+    }
+}
diff --git a/fabric/src/main/java/me/shedaniel/lightoverlay/fabric/mixin/MixinClientConnection.java b/fabric/src/main/java/me/shedaniel/lightoverlay/fabric/mixin/MixinClientConnection.java
new file mode 100644 (file)
index 0000000..013e3d1
--- /dev/null
@@ -0,0 +1,28 @@
+package me.shedaniel.lightoverlay.fabric.mixin;
+
+import me.shedaniel.lightoverlay.fabric.LightOverlay;
+import net.minecraft.network.ClientConnection;
+import net.minecraft.network.Packet;
+import net.minecraft.network.listener.PacketListener;
+import net.minecraft.network.packet.s2c.play.BlockUpdateS2CPacket;
+import net.minecraft.network.packet.s2c.play.ChunkDataS2CPacket;
+import net.minecraft.network.packet.s2c.play.ChunkDeltaUpdateS2CPacket;
+import net.minecraft.util.math.ChunkPos;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.injection.At;
+import org.spongepowered.asm.mixin.injection.Inject;
+import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
+
+@Mixin(ClientConnection.class)
+public class MixinClientConnection {
+    @Inject(method = "handlePacket", at = @At("HEAD"))
+    private static void handlePacket(Packet packet, PacketListener listener, CallbackInfo ci) {
+        if (packet instanceof BlockUpdateS2CPacket) {
+            LightOverlay.queueChunkAndNear(new ChunkPos(((BlockUpdateS2CPacket) packet).getPos()));
+        } else if (packet instanceof ChunkDataS2CPacket) {
+            LightOverlay.queueChunkAndNear(new ChunkPos(((ChunkDataS2CPacket) packet).getX(), ((ChunkDataS2CPacket) packet).getZ()));
+        } else if (packet instanceof ChunkDeltaUpdateS2CPacket) {
+            LightOverlay.queueChunkAndNear(new ChunkPos(((ChunkDeltaUpdateS2CPacket) packet).chunkPos.x, ((ChunkDeltaUpdateS2CPacket) packet).chunkPos.z));
+        }
+    }
+}
diff --git a/fabric/src/main/resources/assets/lightoverlay/lang/en_us.json b/fabric/src/main/resources/assets/lightoverlay/lang/en_us.json
new file mode 100755 (executable)
index 0000000..4d55bbc
--- /dev/null
@@ -0,0 +1,20 @@
+{
+  "key.lightoverlay.category": "Light Overlay",
+  "key.lightoverlay.enable_overlay": "Toggle Light Overlay",
+  "key.lightoverlay.decrease_reach": "Decrease Light Overlay's Reach",
+  "key.lightoverlay.increase_reach": "Increase Light Overlay's Reach",
+  "key.lightoverlay.increase_line_width": "Increase Light Overlay's Line Width",
+  "key.lightoverlay.decrease_line_width": "Decrease Light Overlay's Line Width",
+  "text.lightoverlay.current_reach": "The current reach is %d!",
+  "text.lightoverlay.current_line_width": "The current line width is %s!",
+  "config.lightoverlay.general": "General",
+  "config.lightoverlay.reach": "Reach:",
+  "config.lightoverlay.crossLevel": "Light Level:",
+  "config.lightoverlay.showNumber": "Show Number Instead:",
+  "config.lightoverlay.smoothLines": "Smooth Lines:",
+  "config.lightoverlay.underwater": "Display Underwater:",
+  "config.lightoverlay.lineWidth": "Line Width:",
+  "config.lightoverlay.yellowColor": "Yellow Color:",
+  "config.lightoverlay.redColor": "Red Color:",
+  "config.lightoverlay.invalidColor": "Invalid Color"
+}
\ No newline at end of file
diff --git a/fabric/src/main/resources/fabric.mod.json b/fabric/src/main/resources/fabric.mod.json
new file mode 100755 (executable)
index 0000000..b763963
--- /dev/null
@@ -0,0 +1,37 @@
+{
+  "schemaVersion": 1,
+  "id": "lightoverlay",
+  "name": "Light Overlay",
+  "description": "To provide users with NEI-like light overlay.",
+  "version": "${version}",
+  "environment": "client",
+  "authors": [
+    "Danielshe"
+  ],
+  "contact": {
+    "homepage": "https://minecraft.curseforge.com/projects/light-overlay",
+    "sources": "https://github.com/shedaniel/LightOverlay-Fabric",
+    "issues": "https://github.com/shedaniel/LightOverlay-Fabric/issues"
+  },
+  "entrypoints": {
+    "client": [
+      "me.shedaniel.lightoverlay.fabric.LightOverlay"
+    ],
+    "modmenu": [
+      "me.shedaniel.lightoverlay.fabric.LOModMenuEntry"
+    ]
+  },
+  "license": "Apache-2.0",
+  "icon": "icon.png",
+  "requires": {
+    "fabricloader": ">=0.4.0",
+    "cloth": "*"
+  },
+  "mixins": [
+    "lightoverlay.mixins.json"
+  ],
+  "accessWidener": "lightoverlay.accesswidener",
+  "custom": {
+    "modmenu:clientsideOnly": true
+  }
+}
diff --git a/fabric/src/main/resources/icon.png b/fabric/src/main/resources/icon.png
new file mode 100644 (file)
index 0000000..8bfd85a
Binary files /dev/null and b/fabric/src/main/resources/icon.png differ
diff --git a/fabric/src/main/resources/lightoverlay.accesswidener b/fabric/src/main/resources/lightoverlay.accesswidener
new file mode 100644 (file)
index 0000000..5270ef9
--- /dev/null
@@ -0,0 +1,2 @@
+accessWidener  v1  named
+accessible  field  net/minecraft/network/packet/s2c/play/ChunkDeltaUpdateS2CPacket chunkPos Lnet/minecraft/util/math/ChunkPos;
\ No newline at end of file
diff --git a/fabric/src/main/resources/lightoverlay.mixins.json b/fabric/src/main/resources/lightoverlay.mixins.json
new file mode 100644 (file)
index 0000000..cb0c3f1
--- /dev/null
@@ -0,0 +1,13 @@
+{
+  "required": true,
+  "package": "me.shedaniel.lightoverlay.fabric.mixin",
+  "minVersion": "0.7.11",
+  "compatibilityLevel": "JAVA_8",
+  "mixins": [],
+  "client": [
+    "MixinClientConnection"
+  ],
+  "injectors": {
+    "defaultRequire": 1
+  }
+}
\ No newline at end of file
diff --git a/forge/build.gradle b/forge/build.gradle
new file mode 100644 (file)
index 0000000..e6c2c65
--- /dev/null
@@ -0,0 +1,48 @@
+buildscript {
+    repositories {
+        maven { url "https://files.minecraftforge.net/maven" }
+        jcenter()
+        mavenCentral()
+    }
+    dependencies {
+        classpath(group: "net.minecraftforge.gradle", name: "ForgeGradle", version: "3.+", changing: true)
+    }
+}
+
+plugins {
+    id("com.wynprice.cursemaven") version("2.1.1")
+}
+
+apply plugin: "net.minecraftforge.gradle"
+apply plugin: "eclipse"
+
+minecraft {
+    mappings(channel: "snapshot", version: rootProject.mcp_snapshot)
+    runs {
+        client {
+            workingDirectory project.file("run")
+            mods {
+                examplemod {
+                    source sourceSets.main
+                }
+            }
+        }
+        server {
+            workingDirectory project.file("run")
+            mods {
+                examplemod {
+                    source sourceSets.main
+                }
+            }
+        }
+    }
+}
+
+repositories {
+    maven { url "https://files.minecraftforge.net/maven" }
+}
+
+dependencies {
+    minecraft("net.minecraftforge:forge:${rootProject.forge_minecraft_version}-31.1.39")
+    implementation(fg.deobf("curse.maven:cloth-config-forge:${rootProject.cloth_config_forge_commit}"))
+}
\ No newline at end of file
diff --git a/forge/src/main/java/me/shedaniel/lightoverlay/forge/LightOverlay.java b/forge/src/main/java/me/shedaniel/lightoverlay/forge/LightOverlay.java
new file mode 100644 (file)
index 0000000..290017b
--- /dev/null
@@ -0,0 +1,18 @@
+package me.shedaniel.lightoverlay.forge;
+
+import net.minecraft.network.IPacket;
+import net.minecraftforge.api.distmarker.Dist;
+import net.minecraftforge.fml.DistExecutor;
+import net.minecraftforge.fml.common.Mod;
+
+@Mod("lightoverlay-forge")
+public class LightOverlay {
+    public LightOverlay() {
+        //noinspection Convert2MethodRef
+        DistExecutor.runWhenOn(Dist.CLIENT, () -> () -> LightOverlayClient.register());
+    }
+    
+    public static void processPacket(IPacket<?> packet) {
+        DistExecutor.runWhenOn(Dist.CLIENT, () -> () -> LightOverlayClient.processPacket(packet));
+    }
+}
diff --git a/forge/src/main/java/me/shedaniel/lightoverlay/forge/LightOverlayClient.java b/forge/src/main/java/me/shedaniel/lightoverlay/forge/LightOverlayClient.java
new file mode 100644 (file)
index 0000000..a9c36c8
--- /dev/null
@@ -0,0 +1,506 @@
+package me.shedaniel.lightoverlay.forge;
+
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.mojang.blaze3d.systems.RenderSystem;
+import net.minecraft.block.Block;
+import net.minecraft.block.BlockState;
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.entity.player.ClientPlayerEntity;
+import net.minecraft.client.gui.FontRenderer;
+import net.minecraft.client.renderer.*;
+import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
+import net.minecraft.client.settings.KeyBinding;
+import net.minecraft.client.util.InputMappings;
+import net.minecraft.client.world.ClientWorld;
+import net.minecraft.entity.Entity;
+import net.minecraft.entity.EntityClassification;
+import net.minecraft.entity.EntityType;
+import net.minecraft.network.IPacket;
+import net.minecraft.network.play.server.SChangeBlockPacket;
+import net.minecraft.network.play.server.SChunkDataPacket;
+import net.minecraft.network.play.server.SMultiBlockChangePacket;
+import net.minecraft.tags.BlockTags;
+import net.minecraft.util.Direction;
+import net.minecraft.util.ResourceLocation;
+import net.minecraft.util.math.BlockPos;
+import net.minecraft.util.math.ChunkPos;
+import net.minecraft.util.math.MathHelper;
+import net.minecraft.util.math.Vec3d;
+import net.minecraft.util.math.shapes.ISelectionContext;
+import net.minecraft.util.math.shapes.VoxelShape;
+import net.minecraft.util.text.TranslationTextComponent;
+import net.minecraft.world.IBlockReader;
+import net.minecraft.world.LightType;
+import net.minecraft.world.World;
+import net.minecraft.world.biome.Biome;
+import net.minecraft.world.chunk.Chunk;
+import net.minecraft.world.chunk.ChunkStatus;
+import net.minecraft.world.lighting.IWorldLightListener;
+import net.minecraftforge.api.distmarker.Dist;
+import net.minecraftforge.client.event.InputEvent;
+import net.minecraftforge.client.settings.KeyConflictContext;
+import net.minecraftforge.client.settings.KeyModifier;
+import net.minecraftforge.common.MinecraftForge;
+import net.minecraftforge.event.TickEvent;
+import net.minecraftforge.eventbus.api.SubscribeEvent;
+import net.minecraftforge.fml.DistExecutor;
+import net.minecraftforge.fml.client.registry.ClientRegistry;
+import net.minecraftforge.fml.common.ObfuscationReflectionHelper;
+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.text.DecimalFormat;
+import java.util.*;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+public class LightOverlayClient {
+    static final DecimalFormat FORMAT = new DecimalFormat("#.#");
+    private static final String KEYBIND_CATEGORY = "key.lightoverlay-forge.category";
+    private static final ResourceLocation ENABLE_OVERLAY_KEYBIND = new ResourceLocation("lightoverlay-forge", "enable_overlay");
+    private static final ResourceLocation INCREASE_REACH_KEYBIND = new ResourceLocation("lightoverlay-forge", "increase_reach");
+    private static final ResourceLocation DECREASE_REACH_KEYBIND = new ResourceLocation("lightoverlay-forge", "decrease_reach");
+    private static final ResourceLocation INCREASE_LINE_WIDTH_KEYBIND = new ResourceLocation("lightoverlay-forge", "increase_line_width");
+    private static final ResourceLocation DECREASE_LINE_WIDTH_KEYBIND = new ResourceLocation("lightoverlay-forge", "decrease_line_width");
+    static int reach = 12;
+    static int crossLevel = 7;
+    static boolean showNumber = false;
+    static boolean smoothLines = true;
+    static boolean underwater = false;
+    static EntityType<Entity> testingEntityType;
+    static float lineWidth = 1.0F;
+    static int yellowColor = 0xFFFF00, redColor = 0xFF0000;
+    static File configFile = new File(new File(Minecraft.getInstance().gameDir, "config"), "lightoverlay.properties");
+    private static KeyBinding enableOverlay, increaseReach, decreaseReach, increaseLineWidth, decreaseLineWidth;
+    private static boolean enabled = false;
+    private static int threadNumber = 0;
+    private static final ExecutorService EXECUTOR = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors(), r -> {
+        Thread thread = new Thread(r, "light-overlay-" + threadNumber++);
+        thread.setDaemon(true);
+        return thread;
+    });
+    private static final List<ChunkPos> POS = Lists.newCopyOnWriteArrayList();
+    private static final Map<ChunkPos, Map<Long, Object>> CHUNK_MAP = Maps.newConcurrentMap();
+    private static long ticks = 0;
+    
+    public static void register() {
+        // Load Config
+        loadConfig(configFile);
+        
+        // Setup
+        testingEntityType = EntityType.Builder.create(EntityClassification.MONSTER).size(0f, 0f).disableSerialization().build(null);
+        enableOverlay = registerKeybind(ENABLE_OVERLAY_KEYBIND, InputMappings.Type.KEYSYM, 296, KEYBIND_CATEGORY);
+        increaseReach = registerKeybind(INCREASE_REACH_KEYBIND, InputMappings.Type.KEYSYM, -1, KEYBIND_CATEGORY);
+        decreaseReach = registerKeybind(DECREASE_REACH_KEYBIND, InputMappings.Type.KEYSYM, -1, KEYBIND_CATEGORY);
+        increaseLineWidth = registerKeybind(INCREASE_LINE_WIDTH_KEYBIND, InputMappings.Type.KEYSYM, -1, KEYBIND_CATEGORY);
+        decreaseLineWidth = registerKeybind(DECREASE_LINE_WIDTH_KEYBIND, InputMappings.Type.KEYSYM, -1, KEYBIND_CATEGORY);
+        MinecraftForge.EVENT_BUS.register(LightOverlayClient.class);
+        
+        try {
+            //noinspection Convert2MethodRef
+            DistExecutor.runWhenOn(Dist.CLIENT, () -> () -> LightOverlayCloth.register());
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+    
+    public static CrossType getCrossType(BlockPos pos, BlockPos down, IBlockReader reader, IWorldLightListener block, IWorldLightListener sky, ISelectionContext selectionContext) {
+        BlockState blockBelowState = reader.getBlockState(down);
+        BlockState blockUpperState = reader.getBlockState(pos);
+        VoxelShape upperCollisionShape = blockUpperState.getCollisionShape(reader, pos, selectionContext);
+        if (!underwater && !blockUpperState.getFluidState().isEmpty())
+            return CrossType.NONE;
+        /* WorldEntitySpawner.func_222266_a */
+        // Check if the outline is full
+        if (Block.doesSideFillSquare(upperCollisionShape, Direction.UP))
+            return CrossType.NONE;
+        // Check if there is power
+        if (blockUpperState.canProvidePower())
+            return CrossType.NONE;
+        // Check if the collision has a bump
+        if (upperCollisionShape.getEnd(Direction.Axis.Y) > 0)
+            return CrossType.NONE;
+        if (blockUpperState.getBlock().isIn(BlockTags.RAILS))
+            return CrossType.NONE;
+        // Check block state allow spawning (excludes bedrock and barriers automatically)
+        if (!blockBelowState.canEntitySpawn(reader, down, testingEntityType))
+            return CrossType.NONE;
+        if (block.getLightFor(pos) > crossLevel)
+            return CrossType.NONE;
+        if (sky.getLightFor(pos) > crossLevel)
+            return CrossType.YELLOW;
+        return CrossType.RED;
+    }
+    
+    public static int getCrossLevel(BlockPos pos, BlockPos down, IBlockReader reader, IWorldLightListener light, ISelectionContext context) {
+        BlockState blockBelowState = reader.getBlockState(down);
+        BlockState blockUpperState = reader.getBlockState(pos);
+        VoxelShape collisionShape = blockBelowState.getCollisionShape(reader, down, context);
+        VoxelShape upperCollisionShape = blockUpperState.getCollisionShape(reader, pos, context);
+        if (!underwater && !blockUpperState.getFluidState().isEmpty())
+            return -1;
+        if (!blockBelowState.getFluidState().isEmpty())
+            return -1;
+        if (blockBelowState.isAir(reader, down))
+            return -1;
+        if (Block.doesSideFillSquare(upperCollisionShape, Direction.DOWN))
+            return -1;
+        return light.getLightFor(pos);
+    }
+    
+    public static void renderCross(ActiveRenderInfo info, Tessellator tessellator, BufferBuilder buffer, World world, BlockPos pos, int color, ISelectionContext context) {
+        double d0 = info.getProjectedView().x;
+        double d1 = info.getProjectedView().y - .005D;
+        VoxelShape upperOutlineShape = world.getBlockState(pos).getShape(world, pos, context);
+        if (!upperOutlineShape.isEmpty())
+            d1 -= upperOutlineShape.getEnd(Direction.Axis.Y);
+        double d2 = info.getProjectedView().z;
+        buffer.begin(1, DefaultVertexFormats.POSITION_COLOR);
+        int red = (color >> 16) & 255;
+        int green = (color >> 8) & 255;
+        int blue = color & 255;
+        buffer.pos(pos.getX() + .01 - d0, pos.getY() - d1, pos.getZ() + .01 - d2).color(red, green, blue, 255).endVertex();
+        buffer.pos(pos.getX() - .01 + 1 - d0, pos.getY() - d1, pos.getZ() - .01 + 1 - d2).color(red, green, blue, 255).endVertex();
+        buffer.pos(pos.getX() - .01 + 1 - d0, pos.getY() - d1, pos.getZ() + .01 - d2).color(red, green, blue, 255).endVertex();
+        buffer.pos(pos.getX() + .01 - d0, pos.getY() - d1, pos.getZ() - .01 + 1 - d2).color(red, green, blue, 255).endVertex();
+        tessellator.draw();
+    }
+    
+    public static void renderLevel(Minecraft minecraft, ActiveRenderInfo info, World world, BlockPos pos, BlockPos down, int level, ISelectionContext context) {
+        String string_1 = String.valueOf(level);
+        FontRenderer fontRenderer = minecraft.fontRenderer;
+        double double_4 = info.getProjectedView().x;
+        double double_5 = info.getProjectedView().y;
+        VoxelShape upperOutlineShape = world.getBlockState(down).getShape(world, down, context);
+        if (!upperOutlineShape.isEmpty())
+            double_5 += 1 - upperOutlineShape.getEnd(Direction.Axis.Y);
+        double double_6 = info.getProjectedView().z;
+        RenderSystem.pushMatrix();
+        RenderSystem.translatef((float) (pos.getX() + 0.5f - double_4), (float) (pos.getY() - double_5) + 0.005f, (float) (pos.getZ() + 0.5f - double_6));
+        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) (-fontRenderer.getStringWidth(string_1)) / 2.0F + 0.4f;
+        RenderSystem.enableAlphaTest();
+        IRenderTypeBuffer.Impl vertexConsumerProvider$Immediate_1 = IRenderTypeBuffer.getImpl(Tessellator.getInstance().getBuffer());
+        fontRenderer.renderString(string_1, float_3, -3.5f, level > crossLevel ? 0xff042404 : 0xff731111, false, TransformationMatrix.identity().getMatrix(), vertexConsumerProvider$Immediate_1, false, 0, 15728880);
+        vertexConsumerProvider$Immediate_1.finish();
+        RenderSystem.popMatrix();
+    }
+    
+    @SubscribeEvent(receiveCanceled = true)
+    public static void handleInput(InputEvent.KeyInputEvent event) {
+        if (enableOverlay.isPressed())
+            enabled = !enabled;
+        if (increaseReach.isPressed()) {
+            if (reach < 64)
+                reach++;
+            try {
+                saveConfig(configFile);
+            } catch (IOException e) {
+                e.printStackTrace();
+            }
+            Minecraft.getInstance().player.sendStatusMessage(new TranslationTextComponent("text.lightoverlay-forge.current_reach", reach), false);
+        }
+        if (decreaseReach.isPressed()) {
+            if (reach > 1)
+                reach--;
+            try {
+                saveConfig(configFile);
+            } catch (IOException e) {
+                e.printStackTrace();
+            }
+            Minecraft.getInstance().player.sendStatusMessage(new TranslationTextComponent("text.lightoverlay-forge.current_reach", reach), false);
+        }
+        if (increaseLineWidth.isPressed()) {
+            if (lineWidth < 7)
+                lineWidth += 0.1f;
+            try {
+                saveConfig(configFile);
+            } catch (IOException e) {
+                e.printStackTrace();
+            }
+            Minecraft.getInstance().player.sendStatusMessage(new TranslationTextComponent("text.lightoverlay-forge.current_line_width", FORMAT.format(lineWidth)), false);
+        }
+        if (decreaseLineWidth.isPressed()) {
+            if (lineWidth > 1)
+                lineWidth -= 0.1F;
+            try {
+                saveConfig(configFile);
+            } catch (IOException e) {
+                e.printStackTrace();
+            }
+            Minecraft.getInstance().player.sendStatusMessage(new TranslationTextComponent("text.lightoverlay-forge.current_line_width", FORMAT.format(lineWidth)), false);
+        }
+    }
+    
+    public static void queueChunkAndNear(ChunkPos pos) {
+        for (int xOffset = -1; xOffset <= 1; xOffset++) {
+            for (int zOffset = -1; zOffset <= 1; zOffset++) {
+                queueChunk(new ChunkPos(pos.x + xOffset, pos.z + zOffset));
+            }
+        }
+    }
+    
+    public static void queueChunk(ChunkPos pos) {
+        if (!POS.contains(pos))
+            POS.add(0, pos);
+    }
+    
+    public static int getChunkRange() {
+        return Math.max(MathHelper.ceil(reach / 16f), 1);
+    }
+    
+    @SubscribeEvent
+    public static void tick(TickEvent.ClientTickEvent event) {
+        if (event.phase == TickEvent.Phase.END) {
+            try {
+                Minecraft minecraft = Minecraft.getInstance();
+                ticks++;
+                if (minecraft.player == null || !enabled) {
+                    POS.clear();
+                    CHUNK_MAP.clear();
+                } else {
+                    ClientPlayerEntity player = minecraft.player;
+                    ClientWorld world = minecraft.world;
+                    ISelectionContext selectionContext = ISelectionContext.forEntity(player);
+                    Vec3d[] playerPos = {null};
+                    int playerPosX = ((int) player.getPosX()) >> 4;
+                    int playerPosZ = ((int) player.getPosZ()) >> 4;
+                    if (ticks % 20 == 0) {
+                        for (int chunkX = playerPosX - getChunkRange(); chunkX <= playerPosX + getChunkRange(); chunkX++) {
+                            for (int chunkZ = playerPosZ - getChunkRange(); chunkZ <= playerPosZ + getChunkRange(); chunkZ++) {
+                                ChunkPos chunkPos = new ChunkPos(chunkX, chunkZ);
+                                if (!CHUNK_MAP.containsKey(chunkPos))
+                                    queueChunk(chunkPos);
+                            }
+                        }
+                    }
+                    if (!POS.isEmpty()) {
+                        if (playerPos[0] == null) {
+                            playerPos[0] = player.getPositionVec();
+                        }
+                        ChunkPos pos = POS.stream().min(Comparator.comparingDouble(value -> value.getBlock(8, 0, 8).distanceSq(playerPos[0].x, 0, playerPos[0].z, false))).get();
+                        EXECUTOR.submit(() -> {
+                            if (MathHelper.abs(pos.x - playerPosX) <= getChunkRange() && MathHelper.abs(pos.z - playerPosZ) <= getChunkRange()) {
+                                calculateChunk(world.getChunkProvider().getChunk(pos.x, pos.z, ChunkStatus.FULL, false), world, pos, selectionContext);
+                            } else {
+                                CHUNK_MAP.remove(pos);
+                            }
+                        });
+                        POS.remove(pos);
+                    }
+                    Iterator<Map.Entry<ChunkPos, Map<Long, Object>>> chunkMapIterator = CHUNK_MAP.entrySet().iterator();
+                    while (chunkMapIterator.hasNext()) {
+                        Map.Entry<ChunkPos, Map<Long, Object>> pos = chunkMapIterator.next();
+                        if (MathHelper.abs(pos.getKey().x - playerPosX) > getChunkRange() * 2 || MathHelper.abs(pos.getKey().z - playerPosZ) > getChunkRange() * 2) {
+                            chunkMapIterator.remove();
+                        }
+                    }
+                }
+            } catch (Exception e) {
+                LogManager.getLogger().throwing(e);
+            }
+        }
+    }
+    
+    private static void calculateChunk(Chunk chunk, World world, ChunkPos chunkPos, ISelectionContext selectionContext) {
+        Map<Long, Object> map = Maps.newHashMap();
+        if (chunk != null) {
+            IWorldLightListener block = chunk.getWorldLightManager().getLightEngine(LightType.BLOCK);
+            IWorldLightListener sky = showNumber ? null : chunk.getWorldLightManager().getLightEngine(LightType.SKY);
+            for (BlockPos pos : BlockPos.getAllInBoxMutable(chunkPos.getXStart(), 0, chunkPos.getZStart(), chunkPos.getXEnd(), 256, chunkPos.getZEnd())) {
+                BlockPos down = pos.down();
+                if (showNumber) {
+                    int level = LightOverlayClient.getCrossLevel(pos, down, chunk, block, selectionContext);
+                    if (level >= 0) {
+                        map.put(pos.toLong(), level);
+                    }
+                } else {
+                    Biome biome = world.getBiomeManager().getBiome(pos);
+                    if (biome.getSpawningChance() > 0 && !biome.getSpawns(EntityClassification.MONSTER).isEmpty()) {
+                        CrossType type = LightOverlayClient.getCrossType(pos, down, chunk, block, sky, selectionContext);
+                        if (type != CrossType.NONE) {
+                            map.put(pos.toLong(), type);
+                        }
+                    }
+                }
+            }
+        }
+        CHUNK_MAP.put(chunkPos, map);
+    }
+    
+    public static void renderWorldLast() {
+        if (LightOverlayClient.enabled) {
+            RenderSystem.pushMatrix();
+            Minecraft client = Minecraft.getInstance();
+            ClientPlayerEntity playerEntity = client.player;
+            int playerPosX = ((int) playerEntity.getPosX()) >> 4;
+            int playerPosZ = ((int) playerEntity.getPosZ()) >> 4;
+            ISelectionContext selectionContext = ISelectionContext.forEntity(playerEntity);
+            World world = client.world;
+            BlockPos playerPos = playerEntity.getPosition();
+            ActiveRenderInfo info = client.gameRenderer.getActiveRenderInfo();
+            if (showNumber) {
+                RenderSystem.enableTexture();
+                RenderSystem.depthMask(true);
+                BlockPos.Mutable mutable = new BlockPos.Mutable();
+                for (Map.Entry<ChunkPos, Map<Long, Object>> entry : CHUNK_MAP.entrySet()) {
+                    if (MathHelper.abs(entry.getKey().x - playerPosX) > getChunkRange() || MathHelper.abs(entry.getKey().z - playerPosZ) > getChunkRange()) {
+                        continue;
+                    }
+                    for (Map.Entry<Long, Object> objectEntry : entry.getValue().entrySet()) {
+                        if (objectEntry.getValue() instanceof Integer) {
+                            mutable.setPos(BlockPos.unpackX(objectEntry.getKey()), BlockPos.unpackY(objectEntry.getKey()), BlockPos.unpackZ(objectEntry.getKey()));
+                            if (mutable.withinDistance(playerPos, reach)) {
+                                BlockPos down = mutable.down();
+                                LightOverlayClient.renderLevel(client, info, world, mutable, down, (Integer) objectEntry.getValue(), selectionContext);
+                            }
+                        }
+                    }
+                }
+                RenderSystem.color4f(1.0F, 1.0F, 1.0F, 1.0F);
+                RenderSystem.enableDepthTest();
+            } else {
+                RenderSystem.enableDepthTest();
+                RenderSystem.shadeModel(7425);
+                RenderSystem.enableAlphaTest();
+                RenderSystem.defaultAlphaFunc();
+                RenderSystem.disableTexture();
+                RenderSystem.disableBlend();
+                if (smoothLines) GL11.glEnable(GL11.GL_LINE_SMOOTH);
+                RenderSystem.lineWidth(lineWidth);
+                Tessellator tessellator = Tessellator.getInstance();
+                BufferBuilder buffer = tessellator.getBuffer();
+                BlockPos.Mutable mutable = new BlockPos.Mutable();
+                for (Map.Entry<ChunkPos, Map<Long, Object>> entry : CHUNK_MAP.entrySet()) {
+                    if (MathHelper.abs(entry.getKey().x - playerPosX) > getChunkRange() || MathHelper.abs(entry.getKey().z - playerPosZ) > getChunkRange()) {
+                        continue;
+                    }
+                    for (Map.Entry<Long, Object> objectEntry : entry.getValue().entrySet()) {
+                        if (objectEntry.getValue() instanceof CrossType) {
+                            mutable.setPos(BlockPos.unpackX(objectEntry.getKey()), BlockPos.unpackY(objectEntry.getKey()), BlockPos.unpackZ(objectEntry.getKey()));
+                            if (mutable.withinDistance(playerPos, reach)) {
+                                BlockPos down = mutable.down();
+                                int color = objectEntry.getValue() == CrossType.RED ? redColor : yellowColor;
+                                LightOverlayClient.renderCross(info, tessellator, buffer, world, mutable, color, selectionContext);
+                            }
+                        }
+                    }
+                }
+                RenderSystem.enableBlend();
+                RenderSystem.enableTexture();
+                RenderSystem.shadeModel(7424);
+                if (smoothLines) GL11.glDisable(GL11.GL_LINE_SMOOTH);
+            }
+            RenderSystem.popMatrix();
+        }
+    }
+    
+    private static KeyBinding registerKeybind(ResourceLocation resourceLocation, InputMappings.Type type, int keyCode, String category) {
+        KeyBinding keyBinding = new KeyBinding("key." + resourceLocation.getNamespace() + "." + resourceLocation.getPath(), KeyConflictContext.IN_GAME, KeyModifier.NONE, type, keyCode, category);
+        ClientRegistry.registerKeyBinding(keyBinding);
+        return keyBinding;
+    }
+    
+    static void loadConfig(File file) {
+        try {
+            redColor = 0xFF0000;
+            yellowColor = 0xFFFF00;
+            if (!file.exists() || !file.canRead())
+                saveConfig(file);
+            FileInputStream fis = new FileInputStream(file);
+            Properties properties = new Properties();
+            properties.load(fis);
+            fis.close();
+            reach = Integer.parseInt((String) properties.computeIfAbsent("reach", a -> "12"));
+            crossLevel = Integer.parseInt((String) properties.computeIfAbsent("crossLevel", a -> "7"));
+            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");
+            lineWidth = Float.parseFloat((String) properties.computeIfAbsent("lineWidth", a -> "1"));
+            {
+                int r, g, b;
+                r = Integer.parseInt((String) properties.computeIfAbsent("yellowColorRed", a -> "255"));
+                g = Integer.parseInt((String) properties.computeIfAbsent("yellowColorGreen", a -> "255"));
+                b = Integer.parseInt((String) properties.computeIfAbsent("yellowColorBlue", a -> "0"));
+                yellowColor = (r << 16) + (g << 8) + b;
+            }
+            {
+                int r, g, b;
+                r = Integer.parseInt((String) properties.computeIfAbsent("redColorRed", a -> "255"));
+                g = Integer.parseInt((String) properties.computeIfAbsent("redColorGreen", a -> "0"));
+                b = Integer.parseInt((String) properties.computeIfAbsent("redColorBlue", a -> "0"));
+                redColor = (r << 16) + (g << 8) + b;
+            }
+            saveConfig(file);
+        } catch (Exception e) {
+            e.printStackTrace();
+            reach = 12;
+            lineWidth = 1.0F;
+            redColor = 0xFF0000;
+            yellowColor = 0xFFFF00;
+            showNumber = false;
+            smoothLines = true;
+            underwater = false;
+            try {
+                saveConfig(file);
+            } catch (IOException ex) {
+                ex.printStackTrace();
+            }
+        }
+    }
+    
+    static void saveConfig(File file) throws IOException {
+        FileOutputStream fos = new FileOutputStream(file, false);
+        fos.write("# Light Overlay Config".getBytes());
+        fos.write("\n".getBytes());
+        fos.write(("reach=" + reach).getBytes());
+        fos.write("\n".getBytes());
+        fos.write(("crossLevel=" + crossLevel).getBytes());
+        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(("lineWidth=" + FORMAT.format(lineWidth)).getBytes());
+        fos.write("\n".getBytes());
+        fos.write(("yellowColorRed=" + ((yellowColor >> 16) & 255)).getBytes());
+        fos.write("\n".getBytes());
+        fos.write(("yellowColorGreen=" + ((yellowColor >> 8) & 255)).getBytes());
+        fos.write("\n".getBytes());
+        fos.write(("yellowColorBlue=" + (yellowColor & 255)).getBytes());
+        fos.write("\n".getBytes());
+        fos.write(("redColorRed=" + ((redColor >> 16) & 255)).getBytes());
+        fos.write("\n".getBytes());
+        fos.write(("redColorGreen=" + ((redColor >> 8) & 255)).getBytes());
+        fos.write("\n".getBytes());
+        fos.write(("redColorBlue=" + (redColor & 255)).getBytes());
+        fos.close();
+    }
+    
+    public static void processPacket(IPacket<?> packet) {
+        if (packet instanceof SChangeBlockPacket) {
+            LightOverlayClient.queueChunkAndNear(new ChunkPos(((SChangeBlockPacket) packet).getPos()));
+        } else if (packet instanceof SChunkDataPacket) {
+            LightOverlayClient.queueChunkAndNear(new ChunkPos(((SChunkDataPacket) packet).getChunkX(), ((SChunkDataPacket) packet).getChunkZ()));
+        } else if (packet instanceof SMultiBlockChangePacket) {
+            ChunkPos chunkPos = ObfuscationReflectionHelper.getPrivateValue(SMultiBlockChangePacket.class, (SMultiBlockChangePacket) packet, "field_148925_b");
+            LightOverlayClient.queueChunkAndNear(new ChunkPos(chunkPos.x, chunkPos.z));
+        }
+    }
+    
+    private enum CrossType {
+        YELLOW,
+        RED,
+        NONE
+    }
+}
diff --git a/forge/src/main/java/me/shedaniel/lightoverlay/forge/LightOverlayCloth.java b/forge/src/main/java/me/shedaniel/lightoverlay/forge/LightOverlayCloth.java
new file mode 100644 (file)
index 0000000..3c22c9e
--- /dev/null
@@ -0,0 +1,36 @@
+package me.shedaniel.lightoverlay.forge;
+
+import me.shedaniel.forge.clothconfig2.api.ConfigBuilder;
+import me.shedaniel.forge.clothconfig2.api.ConfigCategory;
+import me.shedaniel.forge.clothconfig2.api.ConfigEntryBuilder;
+import net.minecraft.util.math.MathHelper;
+import net.minecraftforge.fml.ExtensionPoint;
+import net.minecraftforge.fml.ModLoadingContext;
+
+public class LightOverlayCloth {
+    public static void register() {
+        ModLoadingContext.get().registerExtensionPoint(ExtensionPoint.CONFIGGUIFACTORY, () -> (client, parent) -> {
+            ConfigBuilder builder = ConfigBuilder.create().setParentScreen(parent).setTitle("key.lightoverlay.category");
+            
+            ConfigEntryBuilder eb = builder.getEntryBuilder();
+            ConfigCategory general = builder.getOrCreateCategory("config.lightoverlay-forge.general");
+            general.addEntry(eb.startIntSlider("config.lightoverlay-forge.reach", LightOverlayClient.reach, 1, 64).setDefaultValue(12).setTextGetter(integer -> "Reach: " + integer + " Blocks").setSaveConsumer(integer -> LightOverlayClient.reach = integer).build());
+            general.addEntry(eb.startIntSlider("config.lightoverlay-forge.crossLevel", LightOverlayClient.crossLevel, 0, 15).setDefaultValue(7).setTextGetter(integer -> "Cross Level: " + integer).setSaveConsumer(integer -> LightOverlayClient.crossLevel = integer).build());
+            general.addEntry(eb.startBooleanToggle("config.lightoverlay-forge.showNumber", LightOverlayClient.showNumber).setDefaultValue(false).setSaveConsumer(bool -> LightOverlayClient.showNumber = bool).build());
+            general.addEntry(eb.startBooleanToggle("config.lightoverlay-forge.smoothLines", LightOverlayClient.smoothLines).setDefaultValue(true).setSaveConsumer(bool -> LightOverlayClient.smoothLines = bool).build());
+            general.addEntry(eb.startBooleanToggle("config.lightoverlay-forge.underwater", LightOverlayClient.underwater).setDefaultValue(false).setSaveConsumer(bool -> LightOverlayClient.underwater = bool).build());
+            general.addEntry(eb.startIntSlider("config.lightoverlay-forge.lineWidth", MathHelper.floor(LightOverlayClient.lineWidth * 100), 100, 700).setDefaultValue(100).setTextGetter(integer -> "Light Width: " + LightOverlayClient.FORMAT.format(integer / 100d)).setSaveConsumer(integer -> LightOverlayClient.lineWidth = integer / 100f).build());
+            general.addEntry(eb.startColorField("config.lightoverlay-forge.yellowColor", LightOverlayClient.yellowColor).setDefaultValue(0xFFFF00).setSaveConsumer(color -> LightOverlayClient.yellowColor = color).build());
+            general.addEntry(eb.startColorField("config.lightoverlay-forge.redColor", LightOverlayClient.redColor).setDefaultValue(0xFF0000).setSaveConsumer(color -> LightOverlayClient.redColor = color).build());
+            
+            return builder.setSavingRunnable(() -> {
+                try {
+                    LightOverlayClient.saveConfig(LightOverlayClient.configFile);
+                } catch (Exception e) {
+                    e.printStackTrace();
+                }
+                LightOverlayClient.loadConfig(LightOverlayClient.configFile);
+            }).build();
+        });
+    }
+}
diff --git a/forge/src/main/resources/META-INF/NetworkManager.js b/forge/src/main/resources/META-INF/NetworkManager.js
new file mode 100644 (file)
index 0000000..134c427
--- /dev/null
@@ -0,0 +1,37 @@
+var Opcodes = Java.type("org.objectweb.asm.Opcodes");
+var LabelNode = Java.type("org.objectweb.asm.tree.LabelNode");
+var VarInsnNode = Java.type("org.objectweb.asm.tree.VarInsnNode");
+var MethodInsnNode = Java.type("org.objectweb.asm.tree.MethodInsnNode");
+var ASMAPI = Java.type("net.minecraftforge.coremod.api.ASMAPI");
+
+function initializeCoreMod() {
+    return {
+        "light-overlay-forge": {
+            'target': {
+                'type': 'CLASS',
+                'name': 'net.minecraft.network.NetworkManager'
+            },
+            'transformer': function (classNode) {
+                var processPacket = ASMAPI.mapMethod("func_197664_a");
+                for (i in classNode.methods) {
+                    var method = classNode.methods[i];
+                    if (method.name === processPacket) {
+                        var instructions = method.instructions;
+                        var insnArray = instructions.toArray();
+                        for (j in insnArray) {
+                            var instruction = insnArray[j];
+                            if (instruction instanceof LabelNode) {
+                                instructions.insertBefore(instruction, new LabelNode());
+                                instructions.insertBefore(instruction, new VarInsnNode(Opcodes.ALOAD, 0));
+                                instructions.insertBefore(instruction, new MethodInsnNode(Opcodes.INVOKESTATIC, "me/shedaniel/lightoverlay/LightOverlay", "processPacket", "(Lnet/minecraft/network/IPacket;)V", false));
+                                break;
+                            }
+                        }
+                        break;
+                    }
+                }
+                return classNode;
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/forge/src/main/resources/META-INF/WorldRenderer.js b/forge/src/main/resources/META-INF/WorldRenderer.js
new file mode 100644 (file)
index 0000000..0282268
--- /dev/null
@@ -0,0 +1,37 @@
+var Opcodes = Java.type("org.objectweb.asm.Opcodes");
+var LabelNode = Java.type("org.objectweb.asm.tree.LabelNode");
+var VarInsnNode = Java.type("org.objectweb.asm.tree.VarInsnNode");
+var MethodInsnNode = Java.type("org.objectweb.asm.tree.MethodInsnNode");
+var ASMAPI = Java.type("net.minecraftforge.coremod.api.ASMAPI");
+
+function initializeCoreMod() {
+    return {
+        "light-overlay-forge": {
+            'target': {
+                'type': 'CLASS',
+                'name': 'net.minecraft.client.renderer.debug.DebugRenderer'
+            },
+            'transformer': function (classNode) {
+                var render = ASMAPI.mapMethod("func_229019_a_");
+                for (i in classNode.methods) {
+                    var method = classNode.methods[i];
+                    if (method.name === render) {
+                        var instructions = method.instructions;
+                        var insnArray = instructions.toArray();
+                        for (j in insnArray) {
+                            var instruction = insnArray[j];
+                            if (instruction instanceof LabelNode) {
+                                instructions.insertBefore(instruction, new LabelNode());
+                                instructions.insertBefore(instruction, new VarInsnNode(Opcodes.ALOAD, 0));
+                                instructions.insertBefore(instruction, new MethodInsnNode(Opcodes.INVOKESTATIC, "me/shedaniel/lightoverlay/LightOverlayClient", "renderWorldLast", "()V", false));
+                                break;
+                            }
+                        }
+                        break;
+                    }
+                }
+                return classNode;
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/forge/src/main/resources/META-INF/coremods.json b/forge/src/main/resources/META-INF/coremods.json
new file mode 100644 (file)
index 0000000..ed2abed
--- /dev/null
@@ -0,0 +1,4 @@
+{
+  "NetworkManager": "META-INF/NetworkManager.js",
+  "WorldRenderer": "META-INF/WorldRenderer.js"
+}
\ No newline at end of file
diff --git a/forge/src/main/resources/META-INF/mods.toml b/forge/src/main/resources/META-INF/mods.toml
new file mode 100755 (executable)
index 0000000..3fe4bd3
--- /dev/null
@@ -0,0 +1,19 @@
+modLoader="javafml"
+loaderVersion="[29,)"
+issueTrackerURL="https://github.com/shedaniel/LightOverlay/issues/" #optional
+logoFile="icon.png"
+authors="shedaniel"
+[[mods]]
+    modId="lightoverlay-forge"
+    version="${file.jarVersion}"
+    displayName="Light Overlay Forge"
+    description='''
+To provide users with NEI-like light overlay.
+'''
+
+[[dependencies.lightoverlay-forge]]
+    modId="cloth-config2"
+    mandatory=true
+    versionRange="[3.0,)"
+    ordering="NONE"
+    side="CLIENT"
diff --git a/forge/src/main/resources/assets/lightoverlay/lang/en_us.json b/forge/src/main/resources/assets/lightoverlay/lang/en_us.json
new file mode 100755 (executable)
index 0000000..4d55bbc
--- /dev/null
@@ -0,0 +1,20 @@
+{
+  "key.lightoverlay.category": "Light Overlay",
+  "key.lightoverlay.enable_overlay": "Toggle Light Overlay",
+  "key.lightoverlay.decrease_reach": "Decrease Light Overlay's Reach",
+  "key.lightoverlay.increase_reach": "Increase Light Overlay's Reach",
+  "key.lightoverlay.increase_line_width": "Increase Light Overlay's Line Width",
+  "key.lightoverlay.decrease_line_width": "Decrease Light Overlay's Line Width",
+  "text.lightoverlay.current_reach": "The current reach is %d!",
+  "text.lightoverlay.current_line_width": "The current line width is %s!",
+  "config.lightoverlay.general": "General",
+  "config.lightoverlay.reach": "Reach:",
+  "config.lightoverlay.crossLevel": "Light Level:",
+  "config.lightoverlay.showNumber": "Show Number Instead:",
+  "config.lightoverlay.smoothLines": "Smooth Lines:",
+  "config.lightoverlay.underwater": "Display Underwater:",
+  "config.lightoverlay.lineWidth": "Line Width:",
+  "config.lightoverlay.yellowColor": "Yellow Color:",
+  "config.lightoverlay.redColor": "Red Color:",
+  "config.lightoverlay.invalidColor": "Invalid Color"
+}
\ No newline at end of file
diff --git a/forge/src/main/resources/icon.png b/forge/src/main/resources/icon.png
new file mode 100644 (file)
index 0000000..8bfd85a
Binary files /dev/null and b/forge/src/main/resources/icon.png differ
diff --git a/forge/src/main/resources/pack.mcmeta b/forge/src/main/resources/pack.mcmeta
new file mode 100755 (executable)
index 0000000..1c54690
--- /dev/null
@@ -0,0 +1,6 @@
+{
+  "pack": {
+    "description": "Light Overlay Forge",
+    "pack_format": 4
+  }
+}
index 784899a6dbf133052aee5101899009e2d1bd9f54..32d1762eabefa958ebc7c473c095d0a11a3775ce 100755 (executable)
@@ -1,4 +1,18 @@
-# Sets default memory used for gradle commands. Can be overridden by user or command line properties.
-# This is required to provide enough memory for the Minecraft decompilation process.
 org.gradle.jvmargs=-Xmx3G
 org.gradle.daemon=false
+
+mod_version=4.6.2
+
+# fabric
+fabric_minecraft_version=1.15.2
+yarn_build=15
+fabric_loader_version=0.8.2+build.194
+fabric_api_version=0.5.1+build.294-1.15
+cloth_events_version=1.2.0
+cloth_config_version=2.14.0
+modmenu_version=1.10.2+build.32
+
+# forge
+mcp_snapshot=20200408-1.15.1
+forge_minecraft_version=1.15.2
+cloth_config_forge_commit=2938583
\ No newline at end of file
index 4e1e288150c12e450704e4825580c33d25f03e4c..c4bb5b22e57436f80c99e0382ba237e617146765 100755 (executable)
@@ -1,4 +1,3 @@
-#Thu Apr 16 21:43:39 HKT 2020
 distributionUrl=https\://services.gradle.org/distributions/gradle-5.5.1-all.zip
 distributionBase=GRADLE_USER_HOME
 distributionPath=wrapper/dists
diff --git a/merging.policy b/merging.policy
new file mode 100644 (file)
index 0000000..f956d76
--- /dev/null
@@ -0,0 +1,3 @@
+FABRIC /assets/lightoverlay/lang/en_us.json
+FORGE /META-INF/MANIFEST.MF
+FABRIC /icon.png
\ No newline at end of file
diff --git a/settings.gradle b/settings.gradle
new file mode 100644 (file)
index 0000000..baec76a
--- /dev/null
@@ -0,0 +1,15 @@
+pluginManagement {
+    repositories {
+        jcenter()
+        maven {
+            name = 'Fabric'
+            url = 'https://maven.fabricmc.net/'
+        }
+        gradlePluginPortal()
+    }
+}
+
+rootProject.name="light-overlay"
+
+include("fabric")
+include("forge")
diff --git a/src/main/java/me/shedaniel/lightoverlay/LightOverlay.java b/src/main/java/me/shedaniel/lightoverlay/LightOverlay.java
deleted file mode 100644 (file)
index aa20830..0000000
+++ /dev/null
@@ -1,20 +0,0 @@
-package me.shedaniel.lightoverlay;
-
-import net.minecraft.network.IPacket;
-import net.minecraftforge.api.distmarker.Dist;
-import net.minecraftforge.fml.DistExecutor;
-import net.minecraftforge.fml.common.Mod;
-
-@Mod("lightoverlay-forge")
-public class LightOverlay {
-    
-    public LightOverlay() {
-        //noinspection Convert2MethodRef
-        DistExecutor.runWhenOn(Dist.CLIENT, () -> () -> LightOverlayClient.register());
-    }
-    
-    public static void processPacket(IPacket<?> packet) {
-        DistExecutor.runWhenOn(Dist.CLIENT, () -> () -> LightOverlayClient.processPacket(packet));
-    }
-    
-}
diff --git a/src/main/java/me/shedaniel/lightoverlay/LightOverlayClient.java b/src/main/java/me/shedaniel/lightoverlay/LightOverlayClient.java
deleted file mode 100644 (file)
index acd8255..0000000
+++ /dev/null
@@ -1,510 +0,0 @@
-package me.shedaniel.lightoverlay;
-
-import com.google.common.collect.Lists;
-import com.google.common.collect.Maps;
-import com.mojang.blaze3d.platform.GlStateManager;
-import com.mojang.blaze3d.systems.RenderSystem;
-import net.minecraft.block.Block;
-import net.minecraft.block.BlockState;
-import net.minecraft.client.Minecraft;
-import net.minecraft.client.entity.player.ClientPlayerEntity;
-import net.minecraft.client.gui.FontRenderer;
-import net.minecraft.client.renderer.*;
-import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
-import net.minecraft.client.settings.KeyBinding;
-import net.minecraft.client.util.InputMappings;
-import net.minecraft.client.world.ClientWorld;
-import net.minecraft.entity.Entity;
-import net.minecraft.entity.EntityClassification;
-import net.minecraft.entity.EntityType;
-import net.minecraft.network.IPacket;
-import net.minecraft.network.play.server.SChangeBlockPacket;
-import net.minecraft.network.play.server.SChunkDataPacket;
-import net.minecraft.network.play.server.SMultiBlockChangePacket;
-import net.minecraft.tags.BlockTags;
-import net.minecraft.util.Direction;
-import net.minecraft.util.ResourceLocation;
-import net.minecraft.util.math.BlockPos;
-import net.minecraft.util.math.ChunkPos;
-import net.minecraft.util.math.MathHelper;
-import net.minecraft.util.math.Vec3d;
-import net.minecraft.util.math.shapes.ISelectionContext;
-import net.minecraft.util.math.shapes.VoxelShape;
-import net.minecraft.util.text.TranslationTextComponent;
-import net.minecraft.world.IBlockReader;
-import net.minecraft.world.LightType;
-import net.minecraft.world.World;
-import net.minecraft.world.biome.Biome;
-import net.minecraft.world.chunk.Chunk;
-import net.minecraft.world.chunk.ChunkStatus;
-import net.minecraft.world.lighting.IWorldLightListener;
-import net.minecraftforge.api.distmarker.Dist;
-import net.minecraftforge.client.event.InputEvent;
-import net.minecraftforge.client.event.RenderWorldLastEvent;
-import net.minecraftforge.client.settings.KeyConflictContext;
-import net.minecraftforge.client.settings.KeyModifier;
-import net.minecraftforge.common.MinecraftForge;
-import net.minecraftforge.event.TickEvent;
-import net.minecraftforge.eventbus.api.SubscribeEvent;
-import net.minecraftforge.fml.DistExecutor;
-import net.minecraftforge.fml.client.registry.ClientRegistry;
-import net.minecraftforge.fml.common.ObfuscationReflectionHelper;
-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.text.DecimalFormat;
-import java.util.*;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-
-public class LightOverlayClient {
-    
-    static final DecimalFormat FORMAT = new DecimalFormat("#.#");
-    private static final String KEYBIND_CATEGORY = "key.lightoverlay-forge.category";
-    private static final ResourceLocation ENABLE_OVERLAY_KEYBIND = new ResourceLocation("lightoverlay-forge", "enable_overlay");
-    private static final ResourceLocation INCREASE_REACH_KEYBIND = new ResourceLocation("lightoverlay-forge", "increase_reach");
-    private static final ResourceLocation DECREASE_REACH_KEYBIND = new ResourceLocation("lightoverlay-forge", "decrease_reach");
-    private static final ResourceLocation INCREASE_LINE_WIDTH_KEYBIND = new ResourceLocation("lightoverlay-forge", "increase_line_width");
-    private static final ResourceLocation DECREASE_LINE_WIDTH_KEYBIND = new ResourceLocation("lightoverlay-forge", "decrease_line_width");
-    static int reach = 12;
-    static int crossLevel = 7;
-    static boolean showNumber = false;
-    static boolean smoothLines = true;
-    static boolean underwater = false;
-    static EntityType<Entity> testingEntityType;
-    static float lineWidth = 1.0F;
-    static int yellowColor = 0xFFFF00, redColor = 0xFF0000;
-    static File configFile = new File(new File(Minecraft.getInstance().gameDir, "config"), "lightoverlay.properties");
-    private static KeyBinding enableOverlay, increaseReach, decreaseReach, increaseLineWidth, decreaseLineWidth;
-    private static boolean enabled = false;
-    private static int threadNumber = 0;
-    private static final ExecutorService EXECUTOR = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors(), r -> {
-        Thread thread = new Thread(r, "light-overlay-" + threadNumber++);
-        thread.setDaemon(true);
-        return thread;
-    });
-    private static final List<ChunkPos> POS = Lists.newCopyOnWriteArrayList();
-    private static final Map<ChunkPos, Map<Long, Object>> CHUNK_MAP = Maps.newConcurrentMap();
-    private static long ticks = 0;
-    
-    public static void register() {
-        // Load Config
-        loadConfig(configFile);
-        
-        // Setup
-        testingEntityType = EntityType.Builder.create(EntityClassification.MONSTER).size(0f, 0f).disableSerialization().build(null);
-        enableOverlay = registerKeybind(ENABLE_OVERLAY_KEYBIND, InputMappings.Type.KEYSYM, 296, KEYBIND_CATEGORY);
-        increaseReach = registerKeybind(INCREASE_REACH_KEYBIND, InputMappings.Type.KEYSYM, -1, KEYBIND_CATEGORY);
-        decreaseReach = registerKeybind(DECREASE_REACH_KEYBIND, InputMappings.Type.KEYSYM, -1, KEYBIND_CATEGORY);
-        increaseLineWidth = registerKeybind(INCREASE_LINE_WIDTH_KEYBIND, InputMappings.Type.KEYSYM, -1, KEYBIND_CATEGORY);
-        decreaseLineWidth = registerKeybind(DECREASE_LINE_WIDTH_KEYBIND, InputMappings.Type.KEYSYM, -1, KEYBIND_CATEGORY);
-        MinecraftForge.EVENT_BUS.register(LightOverlayClient.class);
-        
-        try {
-            //noinspection Convert2MethodRef
-            DistExecutor.runWhenOn(Dist.CLIENT, () -> () -> LightOverlayCloth.register());
-        } catch (Exception e) {
-            e.printStackTrace();
-        }
-    }
-    
-    public static CrossType getCrossType(BlockPos pos, BlockPos down, IBlockReader reader, IWorldLightListener block, IWorldLightListener sky, ISelectionContext selectionContext) {
-        BlockState blockBelowState = reader.getBlockState(down);
-        BlockState blockUpperState = reader.getBlockState(pos);
-        VoxelShape upperCollisionShape = blockUpperState.getCollisionShape(reader, pos, selectionContext);
-        if (!underwater && !blockUpperState.getFluidState().isEmpty())
-            return CrossType.NONE;
-        /* WorldEntitySpawner.func_222266_a */
-        // Check if the outline is full
-        if (Block.doesSideFillSquare(upperCollisionShape, Direction.UP))
-            return CrossType.NONE;
-        // Check if there is power
-        if (blockUpperState.canProvidePower())
-            return CrossType.NONE;
-        // Check if the collision has a bump
-        if (upperCollisionShape.getEnd(Direction.Axis.Y) > 0)
-            return CrossType.NONE;
-        if (blockUpperState.getBlock().isIn(BlockTags.RAILS))
-            return CrossType.NONE;
-        // Check block state allow spawning (excludes bedrock and barriers automatically)
-        if (!blockBelowState.canEntitySpawn(reader, down, testingEntityType))
-            return CrossType.NONE;
-        if (block.getLightFor(pos) > crossLevel)
-            return CrossType.NONE;
-        if (sky.getLightFor(pos) > crossLevel)
-            return CrossType.YELLOW;
-        return CrossType.RED;
-    }
-    
-    public static int getCrossLevel(BlockPos pos, BlockPos down, IBlockReader reader, IWorldLightListener light, ISelectionContext context) {
-        BlockState blockBelowState = reader.getBlockState(down);
-        BlockState blockUpperState = reader.getBlockState(pos);
-        VoxelShape collisionShape = blockBelowState.getCollisionShape(reader, down, context);
-        VoxelShape upperCollisionShape = blockUpperState.getCollisionShape(reader, pos, context);
-        if (!underwater && !blockUpperState.getFluidState().isEmpty())
-            return -1;
-        if (!blockBelowState.getFluidState().isEmpty())
-            return -1;
-        if (blockBelowState.isAir(reader, down))
-            return -1;
-        if (Block.doesSideFillSquare(upperCollisionShape, Direction.DOWN))
-            return -1;
-        return light.getLightFor(pos);
-    }
-    
-    public static void renderCross(ActiveRenderInfo info, Tessellator tessellator, BufferBuilder buffer, World world, BlockPos pos, int color, ISelectionContext context) {
-        double d0 = info.getProjectedView().x;
-        double d1 = info.getProjectedView().y - .005D;
-        VoxelShape upperOutlineShape = world.getBlockState(pos).getShape(world, pos, context);
-        if (!upperOutlineShape.isEmpty())
-            d1 -= upperOutlineShape.getEnd(Direction.Axis.Y);
-        double d2 = info.getProjectedView().z;
-        buffer.begin(1, DefaultVertexFormats.POSITION_COLOR);
-        int red = (color >> 16) & 255;
-        int green = (color >> 8) & 255;
-        int blue = color & 255;
-        buffer.pos(pos.getX() + .01 - d0, pos.getY() - d1, pos.getZ() + .01 - d2).color(red, green, blue, 255).endVertex();
-        buffer.pos(pos.getX() - .01 + 1 - d0, pos.getY() - d1, pos.getZ() - .01 + 1 - d2).color(red, green, blue, 255).endVertex();
-        buffer.pos(pos.getX() - .01 + 1 - d0, pos.getY() - d1, pos.getZ() + .01 - d2).color(red, green, blue, 255).endVertex();
-        buffer.pos(pos.getX() + .01 - d0, pos.getY() - d1, pos.getZ() - .01 + 1 - d2).color(red, green, blue, 255).endVertex();
-        tessellator.draw();
-    }
-    
-    public static void renderLevel(Minecraft minecraft, ActiveRenderInfo info, World world, BlockPos pos, BlockPos down, int level, ISelectionContext context) {
-        String string_1 = String.valueOf(level);
-        FontRenderer fontRenderer = minecraft.fontRenderer;
-        double double_4 = info.getProjectedView().x;
-        double double_5 = info.getProjectedView().y;
-        VoxelShape upperOutlineShape = world.getBlockState(down).getShape(world, down, context);
-        if (!upperOutlineShape.isEmpty())
-            double_5 += 1 - upperOutlineShape.getEnd(Direction.Axis.Y);
-        double double_6 = info.getProjectedView().z;
-        RenderSystem.pushMatrix();
-        RenderSystem.translatef((float) (pos.getX() + 0.5f - double_4), (float) (pos.getY() - double_5) + 0.005f, (float) (pos.getZ() + 0.5f - double_6));
-        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) (-fontRenderer.getStringWidth(string_1)) / 2.0F + 0.4f;
-        RenderSystem.enableAlphaTest();
-        IRenderTypeBuffer.Impl vertexConsumerProvider$Immediate_1 = IRenderTypeBuffer.getImpl(Tessellator.getInstance().getBuffer());
-        fontRenderer.renderString(string_1, float_3, -3.5f, level > crossLevel ? 0xff042404 : 0xff731111, false, TransformationMatrix.identity().getMatrix(), vertexConsumerProvider$Immediate_1, false, 0, 15728880);
-        vertexConsumerProvider$Immediate_1.finish();
-        RenderSystem.popMatrix();
-    }
-    
-    @SubscribeEvent(receiveCanceled = true)
-    public static void handleInput(InputEvent.KeyInputEvent event) {
-        if (enableOverlay.isPressed())
-            enabled = !enabled;
-        if (increaseReach.isPressed()) {
-            if (reach < 64)
-                reach++;
-            try {
-                saveConfig(configFile);
-            } catch (IOException e) {
-                e.printStackTrace();
-            }
-            Minecraft.getInstance().player.sendStatusMessage(new TranslationTextComponent("text.lightoverlay-forge.current_reach", reach), false);
-        }
-        if (decreaseReach.isPressed()) {
-            if (reach > 1)
-                reach--;
-            try {
-                saveConfig(configFile);
-            } catch (IOException e) {
-                e.printStackTrace();
-            }
-            Minecraft.getInstance().player.sendStatusMessage(new TranslationTextComponent("text.lightoverlay-forge.current_reach", reach), false);
-        }
-        if (increaseLineWidth.isPressed()) {
-            if (lineWidth < 7)
-                lineWidth += 0.1f;
-            try {
-                saveConfig(configFile);
-            } catch (IOException e) {
-                e.printStackTrace();
-            }
-            Minecraft.getInstance().player.sendStatusMessage(new TranslationTextComponent("text.lightoverlay-forge.current_line_width", FORMAT.format(lineWidth)), false);
-        }
-        if (decreaseLineWidth.isPressed()) {
-            if (lineWidth > 1)
-                lineWidth -= 0.1F;
-            try {
-                saveConfig(configFile);
-            } catch (IOException e) {
-                e.printStackTrace();
-            }
-            Minecraft.getInstance().player.sendStatusMessage(new TranslationTextComponent("text.lightoverlay-forge.current_line_width", FORMAT.format(lineWidth)), false);
-        }
-    }
-    
-    public static void queueChunkAndNear(ChunkPos pos) {
-        for (int xOffset = -1; xOffset <= 1; xOffset++) {
-            for (int zOffset = -1; zOffset <= 1; zOffset++) {
-                queueChunk(new ChunkPos(pos.x + xOffset, pos.z + zOffset));
-            }
-        }
-    }
-    
-    public static void queueChunk(ChunkPos pos) {
-        if (!POS.contains(pos))
-            POS.add(0, pos);
-    }
-    
-    public static int getChunkRange() {
-        return Math.max(MathHelper.ceil(reach / 16f), 1);
-    }
-    
-    @SubscribeEvent
-    public static void tick(TickEvent.ClientTickEvent event) {
-        if (event.phase == TickEvent.Phase.END) {
-            try {
-                Minecraft minecraft = Minecraft.getInstance();
-                ticks++;
-                if (minecraft.player == null || !enabled) {
-                    POS.clear();
-                    CHUNK_MAP.clear();
-                } else {
-                    ClientPlayerEntity player = minecraft.player;
-                    ClientWorld world = minecraft.world;
-                    ISelectionContext selectionContext = ISelectionContext.forEntity(player);
-                    Vec3d[] playerPos = {null};
-                    int playerPosX = ((int) player.getPosX()) >> 4;
-                    int playerPosZ = ((int) player.getPosZ()) >> 4;
-                    if (ticks % 20 == 0) {
-                        for (int chunkX = playerPosX - getChunkRange(); chunkX <= playerPosX + getChunkRange(); chunkX++) {
-                            for (int chunkZ = playerPosZ - getChunkRange(); chunkZ <= playerPosZ + getChunkRange(); chunkZ++) {
-                                ChunkPos chunkPos = new ChunkPos(chunkX, chunkZ);
-                                if (!CHUNK_MAP.containsKey(chunkPos))
-                                    queueChunk(chunkPos);
-                            }
-                        }
-                    }
-                    if (!POS.isEmpty()) {
-                        if (playerPos[0] == null) {
-                            playerPos[0] = player.getPositionVec();
-                        }
-                        ChunkPos pos = POS.stream().min(Comparator.comparingDouble(value -> value.getBlock(8, 0, 8).distanceSq(playerPos[0].x, 0, playerPos[0].z, false))).get();
-                        EXECUTOR.submit(() -> {
-                            if (MathHelper.abs(pos.x - playerPosX) <= getChunkRange() && MathHelper.abs(pos.z - playerPosZ) <= getChunkRange()) {
-                                calculateChunk(world.getChunkProvider().getChunk(pos.x, pos.z, ChunkStatus.FULL, false), world, pos, selectionContext);
-                            } else {
-                                CHUNK_MAP.remove(pos);
-                            }
-                        });
-                        POS.remove(pos);
-                    }
-                    Iterator<Map.Entry<ChunkPos, Map<Long, Object>>> chunkMapIterator = CHUNK_MAP.entrySet().iterator();
-                    while (chunkMapIterator.hasNext()) {
-                        Map.Entry<ChunkPos, Map<Long, Object>> pos = chunkMapIterator.next();
-                        if (MathHelper.abs(pos.getKey().x - playerPosX) > getChunkRange() * 2 || MathHelper.abs(pos.getKey().z - playerPosZ) > getChunkRange() * 2) {
-                            chunkMapIterator.remove();
-                        }
-                    }
-                }
-            } catch (Exception e) {
-                LogManager.getLogger().throwing(e);
-            }
-        }
-    }
-    
-    private static void calculateChunk(Chunk chunk, World world, ChunkPos chunkPos, ISelectionContext selectionContext) {
-        Map<Long, Object> map = Maps.newHashMap();
-        if (chunk != null) {
-            IWorldLightListener block = chunk.getWorldLightManager().getLightEngine(LightType.BLOCK);
-            IWorldLightListener sky = showNumber ? null : chunk.getWorldLightManager().getLightEngine(LightType.SKY);
-            for (BlockPos pos : BlockPos.getAllInBoxMutable(chunkPos.getXStart(), 0, chunkPos.getZStart(), chunkPos.getXEnd(), 256, chunkPos.getZEnd())) {
-                BlockPos down = pos.down();
-                if (showNumber) {
-                    int level = LightOverlayClient.getCrossLevel(pos, down, chunk, block, selectionContext);
-                    if (level >= 0) {
-                        map.put(pos.toLong(), level);
-                    }
-                } else {
-                    Biome biome = world.getBiomeManager().getBiome(pos);
-                    if (biome.getSpawningChance() > 0 && !biome.getSpawns(EntityClassification.MONSTER).isEmpty()) {
-                        CrossType type = LightOverlayClient.getCrossType(pos, down, chunk, block, sky, selectionContext);
-                        if (type != CrossType.NONE) {
-                            map.put(pos.toLong(), type);
-                        }
-                    }
-                }
-            }
-        }
-        CHUNK_MAP.put(chunkPos, map);
-    }
-    
-    public static void renderWorldLast() {
-        if (LightOverlayClient.enabled) {
-            RenderSystem.pushMatrix();
-            Minecraft client = Minecraft.getInstance();
-            ClientPlayerEntity playerEntity = client.player;
-            int playerPosX = ((int) playerEntity.getPosX()) >> 4;
-            int playerPosZ = ((int) playerEntity.getPosZ()) >> 4;
-            ISelectionContext selectionContext = ISelectionContext.forEntity(playerEntity);
-            World world = client.world;
-            BlockPos playerPos = playerEntity.getPosition();
-            ActiveRenderInfo info = client.gameRenderer.getActiveRenderInfo();
-            if (showNumber) {
-                RenderSystem.enableTexture();
-                RenderSystem.depthMask(true);
-                BlockPos.Mutable mutable = new BlockPos.Mutable();
-                for (Map.Entry<ChunkPos, Map<Long, Object>> entry : CHUNK_MAP.entrySet()) {
-                    if (MathHelper.abs(entry.getKey().x - playerPosX) > getChunkRange() || MathHelper.abs(entry.getKey().z - playerPosZ) > getChunkRange()) {
-                        continue;
-                    }
-                    for (Map.Entry<Long, Object> objectEntry : entry.getValue().entrySet()) {
-                        if (objectEntry.getValue() instanceof Integer) {
-                            mutable.setPos(BlockPos.unpackX(objectEntry.getKey()), BlockPos.unpackY(objectEntry.getKey()), BlockPos.unpackZ(objectEntry.getKey()));
-                            if (mutable.withinDistance(playerPos, reach)) {
-                                BlockPos down = mutable.down();
-                                LightOverlayClient.renderLevel(client, info, world, mutable, down, (Integer) objectEntry.getValue(), selectionContext);
-                            }
-                        }
-                    }
-                }
-                RenderSystem.color4f(1.0F, 1.0F, 1.0F, 1.0F);
-                RenderSystem.enableDepthTest();
-            } else {
-                RenderSystem.enableDepthTest();
-                RenderSystem.shadeModel(7425);
-                RenderSystem.enableAlphaTest();
-                RenderSystem.defaultAlphaFunc();
-                RenderSystem.disableTexture();
-                RenderSystem.disableBlend();
-                if (smoothLines) GL11.glEnable(GL11.GL_LINE_SMOOTH);
-                RenderSystem.lineWidth(lineWidth);
-                Tessellator tessellator = Tessellator.getInstance();
-                BufferBuilder buffer = tessellator.getBuffer();
-                BlockPos.Mutable mutable = new BlockPos.Mutable();
-                for (Map.Entry<ChunkPos, Map<Long, Object>> entry : CHUNK_MAP.entrySet()) {
-                    if (MathHelper.abs(entry.getKey().x - playerPosX) > getChunkRange() || MathHelper.abs(entry.getKey().z - playerPosZ) > getChunkRange()) {
-                        continue;
-                    }
-                    for (Map.Entry<Long, Object> objectEntry : entry.getValue().entrySet()) {
-                        if (objectEntry.getValue() instanceof CrossType) {
-                            mutable.setPos(BlockPos.unpackX(objectEntry.getKey()), BlockPos.unpackY(objectEntry.getKey()), BlockPos.unpackZ(objectEntry.getKey()));
-                            if (mutable.withinDistance(playerPos, reach)) {
-                                BlockPos down = mutable.down();
-                                int color = objectEntry.getValue() == CrossType.RED ? redColor : yellowColor;
-                                LightOverlayClient.renderCross(info, tessellator, buffer, world, mutable, color, selectionContext);
-                            }
-                        }
-                    }
-                }
-                RenderSystem.enableBlend();
-                RenderSystem.enableTexture();
-                RenderSystem.shadeModel(7424);
-                if (smoothLines) GL11.glDisable(GL11.GL_LINE_SMOOTH);
-            }
-            RenderSystem.popMatrix();
-        }
-    }
-    
-    private static KeyBinding registerKeybind(ResourceLocation resourceLocation, InputMappings.Type type, int keyCode, String category) {
-        KeyBinding keyBinding = new KeyBinding("key." + resourceLocation.getNamespace() + "." + resourceLocation.getPath(), KeyConflictContext.IN_GAME, KeyModifier.NONE, type, keyCode, category);
-        ClientRegistry.registerKeyBinding(keyBinding);
-        return keyBinding;
-    }
-    
-    static void loadConfig(File file) {
-        try {
-            redColor = 0xFF0000;
-            yellowColor = 0xFFFF00;
-            if (!file.exists() || !file.canRead())
-                saveConfig(file);
-            FileInputStream fis = new FileInputStream(file);
-            Properties properties = new Properties();
-            properties.load(fis);
-            fis.close();
-            reach = Integer.parseInt((String) properties.computeIfAbsent("reach", a -> "12"));
-            crossLevel = Integer.parseInt((String) properties.computeIfAbsent("crossLevel", a -> "7"));
-            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");
-            lineWidth = Float.parseFloat((String) properties.computeIfAbsent("lineWidth", a -> "1"));
-            {
-                int r, g, b;
-                r = Integer.parseInt((String) properties.computeIfAbsent("yellowColorRed", a -> "255"));
-                g = Integer.parseInt((String) properties.computeIfAbsent("yellowColorGreen", a -> "255"));
-                b = Integer.parseInt((String) properties.computeIfAbsent("yellowColorBlue", a -> "0"));
-                yellowColor = (r << 16) + (g << 8) + b;
-            }
-            {
-                int r, g, b;
-                r = Integer.parseInt((String) properties.computeIfAbsent("redColorRed", a -> "255"));
-                g = Integer.parseInt((String) properties.computeIfAbsent("redColorGreen", a -> "0"));
-                b = Integer.parseInt((String) properties.computeIfAbsent("redColorBlue", a -> "0"));
-                redColor = (r << 16) + (g << 8) + b;
-            }
-            saveConfig(file);
-        } catch (Exception e) {
-            e.printStackTrace();
-            reach = 12;
-            lineWidth = 1.0F;
-            redColor = 0xFF0000;
-            yellowColor = 0xFFFF00;
-            showNumber = false;
-            smoothLines = true;
-            underwater = false;
-            try {
-                saveConfig(file);
-            } catch (IOException ex) {
-                ex.printStackTrace();
-            }
-        }
-    }
-    
-    static void saveConfig(File file) throws IOException {
-        FileOutputStream fos = new FileOutputStream(file, false);
-        fos.write("# Light Overlay Config".getBytes());
-        fos.write("\n".getBytes());
-        fos.write(("reach=" + reach).getBytes());
-        fos.write("\n".getBytes());
-        fos.write(("crossLevel=" + crossLevel).getBytes());
-        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(("lineWidth=" + FORMAT.format(lineWidth)).getBytes());
-        fos.write("\n".getBytes());
-        fos.write(("yellowColorRed=" + ((yellowColor >> 16) & 255)).getBytes());
-        fos.write("\n".getBytes());
-        fos.write(("yellowColorGreen=" + ((yellowColor >> 8) & 255)).getBytes());
-        fos.write("\n".getBytes());
-        fos.write(("yellowColorBlue=" + (yellowColor & 255)).getBytes());
-        fos.write("\n".getBytes());
-        fos.write(("redColorRed=" + ((redColor >> 16) & 255)).getBytes());
-        fos.write("\n".getBytes());
-        fos.write(("redColorGreen=" + ((redColor >> 8) & 255)).getBytes());
-        fos.write("\n".getBytes());
-        fos.write(("redColorBlue=" + (redColor & 255)).getBytes());
-        fos.close();
-    }
-    
-    public static void processPacket(IPacket<?> packet) {
-        if (packet instanceof SChangeBlockPacket) {
-            LightOverlayClient.queueChunkAndNear(new ChunkPos(((SChangeBlockPacket) packet).getPos()));
-        } else if (packet instanceof SChunkDataPacket) {
-            LightOverlayClient.queueChunkAndNear(new ChunkPos(((SChunkDataPacket) packet).getChunkX(), ((SChunkDataPacket) packet).getChunkZ()));
-        } else if (packet instanceof SMultiBlockChangePacket) {
-            ChunkPos chunkPos = ObfuscationReflectionHelper.getPrivateValue(SMultiBlockChangePacket.class, (SMultiBlockChangePacket) packet, "field_148925_b");
-            LightOverlayClient.queueChunkAndNear(new ChunkPos(chunkPos.x, chunkPos.z));
-        }
-    }
-    
-    private enum CrossType {
-        YELLOW,
-        RED,
-        NONE
-    }
-    
-}
diff --git a/src/main/java/me/shedaniel/lightoverlay/LightOverlayCloth.java b/src/main/java/me/shedaniel/lightoverlay/LightOverlayCloth.java
deleted file mode 100644 (file)
index 1dd264e..0000000
+++ /dev/null
@@ -1,36 +0,0 @@
-package me.shedaniel.lightoverlay;
-
-import me.shedaniel.forge.clothconfig2.api.ConfigBuilder;
-import me.shedaniel.forge.clothconfig2.api.ConfigCategory;
-import me.shedaniel.forge.clothconfig2.api.ConfigEntryBuilder;
-import net.minecraft.util.math.MathHelper;
-import net.minecraftforge.fml.ExtensionPoint;
-import net.minecraftforge.fml.ModLoadingContext;
-
-public class LightOverlayCloth {
-    public static void register() {
-        ModLoadingContext.get().registerExtensionPoint(ExtensionPoint.CONFIGGUIFACTORY, () -> (client, parent) -> {
-            ConfigBuilder builder = ConfigBuilder.create().setParentScreen(parent).setTitle("key.lightoverlay.category");
-            
-            ConfigEntryBuilder eb = builder.getEntryBuilder();
-            ConfigCategory general = builder.getOrCreateCategory("config.lightoverlay-forge.general");
-            general.addEntry(eb.startIntSlider("config.lightoverlay-forge.reach", LightOverlayClient.reach, 1, 64).setDefaultValue(12).setTextGetter(integer -> "Reach: " + integer + " Blocks").setSaveConsumer(integer -> LightOverlayClient.reach = integer).build());
-            general.addEntry(eb.startIntSlider("config.lightoverlay-forge.crossLevel", LightOverlayClient.crossLevel, 0, 15).setDefaultValue(7).setTextGetter(integer -> "Cross Level: " + integer).setSaveConsumer(integer -> LightOverlayClient.crossLevel = integer).build());
-            general.addEntry(eb.startBooleanToggle("config.lightoverlay-forge.showNumber", LightOverlayClient.showNumber).setDefaultValue(false).setSaveConsumer(bool -> LightOverlayClient.showNumber = bool).build());
-            general.addEntry(eb.startBooleanToggle("config.lightoverlay-forge.smoothLines", LightOverlayClient.smoothLines).setDefaultValue(true).setSaveConsumer(bool -> LightOverlayClient.smoothLines = bool).build());
-            general.addEntry(eb.startBooleanToggle("config.lightoverlay-forge.underwater", LightOverlayClient.underwater).setDefaultValue(false).setSaveConsumer(bool -> LightOverlayClient.underwater = bool).build());
-            general.addEntry(eb.startIntSlider("config.lightoverlay-forge.lineWidth", MathHelper.floor(LightOverlayClient.lineWidth * 100), 100, 700).setDefaultValue(100).setTextGetter(integer -> "Light Width: " + LightOverlayClient.FORMAT.format(integer / 100d)).setSaveConsumer(integer -> LightOverlayClient.lineWidth = integer / 100f).build());
-            general.addEntry(eb.startColorField("config.lightoverlay-forge.yellowColor", LightOverlayClient.yellowColor).setDefaultValue(0xFFFF00).setSaveConsumer(color -> LightOverlayClient.yellowColor = color).build());
-            general.addEntry(eb.startColorField("config.lightoverlay-forge.redColor", LightOverlayClient.redColor).setDefaultValue(0xFF0000).setSaveConsumer(color -> LightOverlayClient.redColor = color).build());
-            
-            return builder.setSavingRunnable(() -> {
-                try {
-                    LightOverlayClient.saveConfig(LightOverlayClient.configFile);
-                } catch (Exception e) {
-                    e.printStackTrace();
-                }
-                LightOverlayClient.loadConfig(LightOverlayClient.configFile);
-            }).build();
-        });
-    }
-}
diff --git a/src/main/resources/META-INF/NetworkManager.js b/src/main/resources/META-INF/NetworkManager.js
deleted file mode 100644 (file)
index 134c427..0000000
+++ /dev/null
@@ -1,37 +0,0 @@
-var Opcodes = Java.type("org.objectweb.asm.Opcodes");
-var LabelNode = Java.type("org.objectweb.asm.tree.LabelNode");
-var VarInsnNode = Java.type("org.objectweb.asm.tree.VarInsnNode");
-var MethodInsnNode = Java.type("org.objectweb.asm.tree.MethodInsnNode");
-var ASMAPI = Java.type("net.minecraftforge.coremod.api.ASMAPI");
-
-function initializeCoreMod() {
-    return {
-        "light-overlay-forge": {
-            'target': {
-                'type': 'CLASS',
-                'name': 'net.minecraft.network.NetworkManager'
-            },
-            'transformer': function (classNode) {
-                var processPacket = ASMAPI.mapMethod("func_197664_a");
-                for (i in classNode.methods) {
-                    var method = classNode.methods[i];
-                    if (method.name === processPacket) {
-                        var instructions = method.instructions;
-                        var insnArray = instructions.toArray();
-                        for (j in insnArray) {
-                            var instruction = insnArray[j];
-                            if (instruction instanceof LabelNode) {
-                                instructions.insertBefore(instruction, new LabelNode());
-                                instructions.insertBefore(instruction, new VarInsnNode(Opcodes.ALOAD, 0));
-                                instructions.insertBefore(instruction, new MethodInsnNode(Opcodes.INVOKESTATIC, "me/shedaniel/lightoverlay/LightOverlay", "processPacket", "(Lnet/minecraft/network/IPacket;)V", false));
-                                break;
-                            }
-                        }
-                        break;
-                    }
-                }
-                return classNode;
-            }
-        }
-    }
-}
\ No newline at end of file
diff --git a/src/main/resources/META-INF/WorldRenderer.js b/src/main/resources/META-INF/WorldRenderer.js
deleted file mode 100644 (file)
index 0282268..0000000
+++ /dev/null
@@ -1,37 +0,0 @@
-var Opcodes = Java.type("org.objectweb.asm.Opcodes");
-var LabelNode = Java.type("org.objectweb.asm.tree.LabelNode");
-var VarInsnNode = Java.type("org.objectweb.asm.tree.VarInsnNode");
-var MethodInsnNode = Java.type("org.objectweb.asm.tree.MethodInsnNode");
-var ASMAPI = Java.type("net.minecraftforge.coremod.api.ASMAPI");
-
-function initializeCoreMod() {
-    return {
-        "light-overlay-forge": {
-            'target': {
-                'type': 'CLASS',
-                'name': 'net.minecraft.client.renderer.debug.DebugRenderer'
-            },
-            'transformer': function (classNode) {
-                var render = ASMAPI.mapMethod("func_229019_a_");
-                for (i in classNode.methods) {
-                    var method = classNode.methods[i];
-                    if (method.name === render) {
-                        var instructions = method.instructions;
-                        var insnArray = instructions.toArray();
-                        for (j in insnArray) {
-                            var instruction = insnArray[j];
-                            if (instruction instanceof LabelNode) {
-                                instructions.insertBefore(instruction, new LabelNode());
-                                instructions.insertBefore(instruction, new VarInsnNode(Opcodes.ALOAD, 0));
-                                instructions.insertBefore(instruction, new MethodInsnNode(Opcodes.INVOKESTATIC, "me/shedaniel/lightoverlay/LightOverlayClient", "renderWorldLast", "()V", false));
-                                break;
-                            }
-                        }
-                        break;
-                    }
-                }
-                return classNode;
-            }
-        }
-    }
-}
\ No newline at end of file
diff --git a/src/main/resources/META-INF/coremods.json b/src/main/resources/META-INF/coremods.json
deleted file mode 100644 (file)
index ed2abed..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-{
-  "NetworkManager": "META-INF/NetworkManager.js",
-  "WorldRenderer": "META-INF/WorldRenderer.js"
-}
\ No newline at end of file
diff --git a/src/main/resources/META-INF/mods.toml b/src/main/resources/META-INF/mods.toml
deleted file mode 100755 (executable)
index a6d7f03..0000000
+++ /dev/null
@@ -1,44 +0,0 @@
-# This is an example mods.toml file. It contains the data relating to the loading mods.
-# There are several mandatory fields (#mandatory), and many more that are optional (#optional).
-# The overall format is standard TOML format, v0.5.0.
-# Note that there are a couple of TOML lists in this file.
-# Find more information on toml format here:  https://github.com/toml-lang/toml
-# The name of the mod loader type to load - for regular FML @Mod mods it should be javafml
-modLoader="javafml" #mandatory
-# A version range to match for said mod loader - for regular FML @Mod it will be the forge version
-loaderVersion="[29,)" #mandatory (24 is current forge version)
-# A URL to refer people to when problems occur with this mod
-issueTrackerURL="https://github.com/shedaniel/LightOverlay-Forge/issues/" #optional
-# A URL for the "homepage" for this mod, displayed in the mod UI
-displayURL="http://shedaniel.me/" #optional
-# A file name (in the root of the mod JAR) containing a logo for display
-logoFile="lightoverlay_icon_lowres.png" #optional
-# A text field displayed in the mod UI
-credits="" #optional
-# A text field displayed in the mod UI
-authors="Danielshe" #optional
-# A list of mods - how many allowed here is determined by the individual mod loader
-[[mods]] #mandatory
-# The modid of the mod
-    modId="lightoverlay-forge" #mandatory
-# The version number of the mod - there's a few well known ${} variables useable here or just hardcode it
-    version="${file.jarVersion}" #mandatory
- # A display name for the mod
-    displayName="Light Overlay Forge" #mandatory
-# A URL to query for updates for this mod. See the JSON update specification <here>
-# updateJSONURL="http://myurl.me/" #optional
-# The description text for the mod (multi line!) (#mandatory)
-    description='''
-To provide users with NEI-like light overlay.
-'''
-
-[[dependencies.lightoverlay-forge]] #optional
-    # the modid of the dependency
-    modId="cloth-config2" #mandatory
-    # Does this dependency have to exist - if not, ordering below must be specified
-    mandatory=true #mandatory
-    # The version range of the dependency
-    versionRange="[3.0,)" #mandatory
-    ordering="NONE"
-    # Side this dependency is applied on - BOTH, CLIENT or SERVER
-    side="CLIENT"
diff --git a/src/main/resources/assets/lightoverlay-forge/lang/en_us.json b/src/main/resources/assets/lightoverlay-forge/lang/en_us.json
deleted file mode 100755 (executable)
index f209db8..0000000
+++ /dev/null
@@ -1,20 +0,0 @@
-{
-  "key.lightoverlay-forge.category": "Light Overlay",
-  "key.lightoverlay-forge.enable_overlay": "Toggle Light Overlay",
-  "key.lightoverlay-forge.decrease_reach": "Decrease Light Overlay's Reach",
-  "key.lightoverlay-forge.increase_reach": "Increase Light Overlay's Reach",
-  "key.lightoverlay-forge.increase_line_width": "Increase Light Overlay's Line Width",
-  "key.lightoverlay-forge.decrease_line_width": "Decrease Light Overlay's Line Width",
-  "text.lightoverlay-forge.current_reach": "The current reach is %d!",
-  "text.lightoverlay-forge.current_line_width": "The current line width is %s!",
-  "config.lightoverlay-forge.general": "General",
-  "config.lightoverlay-forge.reach": "Reach:",
-  "config.lightoverlay-forge.crossLevel": "Light Level:",
-  "config.lightoverlay-forge.showNumber": "Show Number Instead:",
-  "config.lightoverlay-forge.smoothLines": "Smooth Lines:",
-  "config.lightoverlay-forge.underwater": "Display Underwater:",
-  "config.lightoverlay-forge.lineWidth": "Line Width:",
-  "config.lightoverlay-forge.yellowColor": "Yellow Color:",
-  "config.lightoverlay-forge.redColor": "Red Color:",
-  "config.lightoverlay-forge.invalidColor": "Invalid Color"
-}
\ No newline at end of file
diff --git a/src/main/resources/lightoverlay_icon_lowres.png b/src/main/resources/lightoverlay_icon_lowres.png
deleted file mode 100644 (file)
index 8bfd85a..0000000
Binary files a/src/main/resources/lightoverlay_icon_lowres.png and /dev/null differ
diff --git a/src/main/resources/pack.mcmeta b/src/main/resources/pack.mcmeta
deleted file mode 100755 (executable)
index 1c54690..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-{
-  "pack": {
-    "description": "Light Overlay Forge",
-    "pack_format": 4
-  }
-}