Browse Source

New module system

Signed-off-by: shedaniel <daniel@shedaniel.me>
shedaniel 5 years ago
parent
commit
355b4fb802
30 changed files with 952 additions and 127 deletions
  1. 2 0
      .gitignore
  2. 12 1
      README.md
  3. 163 50
      build.gradle
  4. BIN
      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
  5. 39 0
      fabric/build.gradle
  6. 46 0
      fabric/src/main/java/me/shedaniel/lightoverlay/fabric/LOModMenuEntry.java
  7. 466 0
      fabric/src/main/java/me/shedaniel/lightoverlay/fabric/LightOverlay.java
  8. 28 0
      fabric/src/main/java/me/shedaniel/lightoverlay/fabric/mixin/MixinClientConnection.java
  9. 20 0
      fabric/src/main/resources/assets/lightoverlay/lang/en_us.json
  10. 37 0
      fabric/src/main/resources/fabric.mod.json
  11. 0 0
      fabric/src/main/resources/icon.png
  12. 2 0
      fabric/src/main/resources/lightoverlay.accesswidener
  13. 13 0
      fabric/src/main/resources/lightoverlay.mixins.json
  14. 48 0
      forge/build.gradle
  15. 1 3
      forge/src/main/java/me/shedaniel/lightoverlay/forge/LightOverlay.java
  16. 1 5
      forge/src/main/java/me/shedaniel/lightoverlay/forge/LightOverlayClient.java
  17. 1 1
      forge/src/main/java/me/shedaniel/lightoverlay/forge/LightOverlayCloth.java
  18. 0 0
      forge/src/main/resources/META-INF/NetworkManager.js
  19. 0 0
      forge/src/main/resources/META-INF/WorldRenderer.js
  20. 0 0
      forge/src/main/resources/META-INF/coremods.json
  21. 19 0
      forge/src/main/resources/META-INF/mods.toml
  22. 20 0
      forge/src/main/resources/assets/lightoverlay/lang/en_us.json
  23. BIN
      forge/src/main/resources/icon.png
  24. 0 0
      forge/src/main/resources/pack.mcmeta
  25. 16 2
      gradle.properties
  26. 0 1
      gradle/wrapper/gradle-wrapper.properties
  27. 3 0
      merging.policy
  28. 15 0
      settings.gradle
  29. 0 44
      src/main/resources/META-INF/mods.toml
  30. 0 20
      src/main/resources/assets/lightoverlay-forge/lang/en_us.json

+ 2 - 0
.gitignore

@@ -1,9 +1,11 @@
 # Compiled nonsense that does not belong in *source* control
 # Compiled nonsense that does not belong in *source* control
+*/build
 /build
 /build
 /bin
 /bin
 /.gradle
 /.gradle
 /minecraft
 /minecraft
 /out
 /out
+*/run
 /run
 /run
 /classes
 /classes
 
 

+ 12 - 1
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/`.

+ 163 - 50
build.gradle

@@ -1,74 +1,187 @@
 buildscript {
 buildscript {
     repositories {
     repositories {
-        maven { url = 'https://files.minecraftforge.net/maven' }
-        jcenter()
         mavenCentral()
         mavenCentral()
     }
     }
     dependencies {
     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 {
 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()
+    }
+}

BIN
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


+ 39 - 0
fabric/build.gradle

@@ -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
+    }
+}

+ 46 - 0
fabric/src/main/java/me/shedaniel/lightoverlay/fabric/LOModMenuEntry.java

@@ -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();
+    }
+}

+ 466 - 0
fabric/src/main/java/me/shedaniel/lightoverlay/fabric/LightOverlay.java

@@ -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
+    }
+}

+ 28 - 0
fabric/src/main/java/me/shedaniel/lightoverlay/fabric/mixin/MixinClientConnection.java

@@ -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));
+        }
+    }
+}

+ 20 - 0
fabric/src/main/resources/assets/lightoverlay/lang/en_us.json

@@ -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"
+}

+ 37 - 0
fabric/src/main/resources/fabric.mod.json

@@ -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
+  }
+}

+ 0 - 0
src/main/resources/lightoverlay_icon_lowres.png → fabric/src/main/resources/icon.png


+ 2 - 0
fabric/src/main/resources/lightoverlay.accesswidener

@@ -0,0 +1,2 @@
+accessWidener	v1  named
+accessible  field  net/minecraft/network/packet/s2c/play/ChunkDeltaUpdateS2CPacket chunkPos Lnet/minecraft/util/math/ChunkPos;

+ 13 - 0
fabric/src/main/resources/lightoverlay.mixins.json

@@ -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
+  }
+}

+ 48 - 0
forge/build.gradle

@@ -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}"))
+}

+ 1 - 3
src/main/java/me/shedaniel/lightoverlay/LightOverlay.java → forge/src/main/java/me/shedaniel/lightoverlay/forge/LightOverlay.java

@@ -1,4 +1,4 @@
-package me.shedaniel.lightoverlay;
+package me.shedaniel.lightoverlay.forge;
 
 
 import net.minecraft.network.IPacket;
 import net.minecraft.network.IPacket;
 import net.minecraftforge.api.distmarker.Dist;
 import net.minecraftforge.api.distmarker.Dist;
@@ -7,7 +7,6 @@ import net.minecraftforge.fml.common.Mod;
 
 
 @Mod("lightoverlay-forge")
 @Mod("lightoverlay-forge")
 public class LightOverlay {
 public class LightOverlay {
-    
     public LightOverlay() {
     public LightOverlay() {
         //noinspection Convert2MethodRef
         //noinspection Convert2MethodRef
         DistExecutor.runWhenOn(Dist.CLIENT, () -> () -> LightOverlayClient.register());
         DistExecutor.runWhenOn(Dist.CLIENT, () -> () -> LightOverlayClient.register());
@@ -16,5 +15,4 @@ public class LightOverlay {
     public static void processPacket(IPacket<?> packet) {
     public static void processPacket(IPacket<?> packet) {
         DistExecutor.runWhenOn(Dist.CLIENT, () -> () -> LightOverlayClient.processPacket(packet));
         DistExecutor.runWhenOn(Dist.CLIENT, () -> () -> LightOverlayClient.processPacket(packet));
     }
     }
-    
 }
 }

+ 1 - 5
src/main/java/me/shedaniel/lightoverlay/LightOverlayClient.java → forge/src/main/java/me/shedaniel/lightoverlay/forge/LightOverlayClient.java

@@ -1,8 +1,7 @@
-package me.shedaniel.lightoverlay;
+package me.shedaniel.lightoverlay.forge;
 
 
 import com.google.common.collect.Lists;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
 import com.google.common.collect.Maps;
-import com.mojang.blaze3d.platform.GlStateManager;
 import com.mojang.blaze3d.systems.RenderSystem;
 import com.mojang.blaze3d.systems.RenderSystem;
 import net.minecraft.block.Block;
 import net.minecraft.block.Block;
 import net.minecraft.block.BlockState;
 import net.minecraft.block.BlockState;
@@ -40,7 +39,6 @@ import net.minecraft.world.chunk.ChunkStatus;
 import net.minecraft.world.lighting.IWorldLightListener;
 import net.minecraft.world.lighting.IWorldLightListener;
 import net.minecraftforge.api.distmarker.Dist;
 import net.minecraftforge.api.distmarker.Dist;
 import net.minecraftforge.client.event.InputEvent;
 import net.minecraftforge.client.event.InputEvent;
-import net.minecraftforge.client.event.RenderWorldLastEvent;
 import net.minecraftforge.client.settings.KeyConflictContext;
 import net.minecraftforge.client.settings.KeyConflictContext;
 import net.minecraftforge.client.settings.KeyModifier;
 import net.minecraftforge.client.settings.KeyModifier;
 import net.minecraftforge.common.MinecraftForge;
 import net.minecraftforge.common.MinecraftForge;
@@ -62,7 +60,6 @@ import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 import java.util.concurrent.Executors;
 
 
 public class LightOverlayClient {
 public class LightOverlayClient {
-    
     static final DecimalFormat FORMAT = new DecimalFormat("#.#");
     static final DecimalFormat FORMAT = new DecimalFormat("#.#");
     private static final String KEYBIND_CATEGORY = "key.lightoverlay-forge.category";
     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 ENABLE_OVERLAY_KEYBIND = new ResourceLocation("lightoverlay-forge", "enable_overlay");
@@ -506,5 +503,4 @@ public class LightOverlayClient {
         RED,
         RED,
         NONE
         NONE
     }
     }
-    
 }
 }

+ 1 - 1
src/main/java/me/shedaniel/lightoverlay/LightOverlayCloth.java → forge/src/main/java/me/shedaniel/lightoverlay/forge/LightOverlayCloth.java

@@ -1,4 +1,4 @@
-package me.shedaniel.lightoverlay;
+package me.shedaniel.lightoverlay.forge;
 
 
 import me.shedaniel.forge.clothconfig2.api.ConfigBuilder;
 import me.shedaniel.forge.clothconfig2.api.ConfigBuilder;
 import me.shedaniel.forge.clothconfig2.api.ConfigCategory;
 import me.shedaniel.forge.clothconfig2.api.ConfigCategory;

+ 0 - 0
src/main/resources/META-INF/NetworkManager.js → forge/src/main/resources/META-INF/NetworkManager.js


+ 0 - 0
src/main/resources/META-INF/WorldRenderer.js → forge/src/main/resources/META-INF/WorldRenderer.js


+ 0 - 0
src/main/resources/META-INF/coremods.json → forge/src/main/resources/META-INF/coremods.json


+ 19 - 0
forge/src/main/resources/META-INF/mods.toml

@@ -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"

+ 20 - 0
forge/src/main/resources/assets/lightoverlay/lang/en_us.json

@@ -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"
+}

BIN
forge/src/main/resources/icon.png


+ 0 - 0
src/main/resources/pack.mcmeta → forge/src/main/resources/pack.mcmeta


+ 16 - 2
gradle.properties

@@ -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.jvmargs=-Xmx3G
 org.gradle.daemon=false
 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

+ 0 - 1
gradle/wrapper/gradle-wrapper.properties

@@ -1,4 +1,3 @@
-#Thu Apr 16 21:43:39 HKT 2020
 distributionUrl=https\://services.gradle.org/distributions/gradle-5.5.1-all.zip
 distributionUrl=https\://services.gradle.org/distributions/gradle-5.5.1-all.zip
 distributionBase=GRADLE_USER_HOME
 distributionBase=GRADLE_USER_HOME
 distributionPath=wrapper/dists
 distributionPath=wrapper/dists

+ 3 - 0
merging.policy

@@ -0,0 +1,3 @@
+FABRIC /assets/lightoverlay/lang/en_us.json
+FORGE /META-INF/MANIFEST.MF
+FABRIC /icon.png

+ 15 - 0
settings.gradle

@@ -0,0 +1,15 @@
+pluginManagement {
+    repositories {
+        jcenter()
+        maven {
+            name = 'Fabric'
+            url = 'https://maven.fabricmc.net/'
+        }
+        gradlePluginPortal()
+    }
+}
+
+rootProject.name="light-overlay"
+
+include("fabric")
+include("forge")

+ 0 - 44
src/main/resources/META-INF/mods.toml

@@ -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"

+ 0 - 20
src/main/resources/assets/lightoverlay-forge/lang/en_us.json

@@ -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"
-}