From: shedaniel Date: Mon, 11 May 2020 18:40:02 +0000 (+0800) Subject: New module system X-Git-Url: https://git.lizzy.rs/?a=commitdiff_plain;h=355b4fb80227243d0cfcb53db14292b54db17bdf;p=LightOverlay.git New module system Signed-off-by: shedaniel --- diff --git a/.gitignore b/.gitignore index f9dae6b..06b3b34 100644 --- a/.gitignore +++ b/.gitignore @@ -1,9 +1,11 @@ # Compiled nonsense that does not belong in *source* control +*/build /build /bin /.gradle /minecraft /out +*/run /run /classes diff --git a/README.md b/README.md index 4a9d714..381a798 100644 --- 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`
+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 diff --git a/build.gradle b/build.gradle index f1a0fce..718020b 100644 --- a/build.gradle +++ b/build.gradle @@ -1,74 +1,187 @@ 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() + 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() { + @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() { + @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 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 index 0000000..7e6d021 --- /dev/null +++ b/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 + } +} 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 index 0000000..b381757 --- /dev/null +++ b/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 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 index 0000000..28883f8 --- /dev/null +++ b/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 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 POS = Lists.newCopyOnWriteArrayList(); + private static final Map> 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>> chunkMapIterator = CHUNK_MAP.entrySet().iterator(); + while (chunkMapIterator.hasNext()) { + Map.Entry> 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 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> entry : CHUNK_MAP.entrySet()) { + if (MathHelper.abs(entry.getKey().x - playerPosX) > getChunkRange() || MathHelper.abs(entry.getKey().z - playerPosZ) > getChunkRange()) { + continue; + } + for (Map.Entry 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> entry : CHUNK_MAP.entrySet()) { + if (MathHelper.abs(entry.getKey().x - playerPosX) > getChunkRange() || MathHelper.abs(entry.getKey().z - playerPosZ) > getChunkRange()) { + continue; + } + for (Map.Entry 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 index 0000000..013e3d1 --- /dev/null +++ b/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)); + } + } +} 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 index 0000000..4d55bbc --- /dev/null +++ b/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" +} \ 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 index 0000000..b763963 --- /dev/null +++ b/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 + } +} diff --git a/fabric/src/main/resources/icon.png b/fabric/src/main/resources/icon.png new file mode 100644 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 index 0000000..5270ef9 --- /dev/null +++ b/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; \ 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 index 0000000..cb0c3f1 --- /dev/null +++ b/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 + } +} \ No newline at end of file diff --git a/forge/build.gradle b/forge/build.gradle new file mode 100644 index 0000000..e6c2c65 --- /dev/null +++ b/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}")) +} \ 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 index 0000000..290017b --- /dev/null +++ b/forge/src/main/java/me/shedaniel/lightoverlay/forge/LightOverlay.java @@ -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 index 0000000..a9c36c8 --- /dev/null +++ b/forge/src/main/java/me/shedaniel/lightoverlay/forge/LightOverlayClient.java @@ -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 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 POS = Lists.newCopyOnWriteArrayList(); + private static final Map> 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>> chunkMapIterator = CHUNK_MAP.entrySet().iterator(); + while (chunkMapIterator.hasNext()) { + Map.Entry> 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 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> entry : CHUNK_MAP.entrySet()) { + if (MathHelper.abs(entry.getKey().x - playerPosX) > getChunkRange() || MathHelper.abs(entry.getKey().z - playerPosZ) > getChunkRange()) { + continue; + } + for (Map.Entry 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> entry : CHUNK_MAP.entrySet()) { + if (MathHelper.abs(entry.getKey().x - playerPosX) > getChunkRange() || MathHelper.abs(entry.getKey().z - playerPosZ) > getChunkRange()) { + continue; + } + for (Map.Entry 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 index 0000000..3c22c9e --- /dev/null +++ b/forge/src/main/java/me/shedaniel/lightoverlay/forge/LightOverlayCloth.java @@ -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 index 0000000..134c427 --- /dev/null +++ b/forge/src/main/resources/META-INF/NetworkManager.js @@ -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 index 0000000..0282268 --- /dev/null +++ b/forge/src/main/resources/META-INF/WorldRenderer.js @@ -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 index 0000000..ed2abed --- /dev/null +++ b/forge/src/main/resources/META-INF/coremods.json @@ -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 index 0000000..3fe4bd3 --- /dev/null +++ b/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" 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 index 0000000..4d55bbc --- /dev/null +++ b/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" +} \ 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 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 index 0000000..1c54690 --- /dev/null +++ b/forge/src/main/resources/pack.mcmeta @@ -0,0 +1,6 @@ +{ + "pack": { + "description": "Light Overlay Forge", + "pack_format": 4 + } +} diff --git a/gradle.properties b/gradle.properties index 784899a..32d1762 100755 --- a/gradle.properties +++ b/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.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 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 4e1e288..c4bb5b2 100755 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/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 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists diff --git a/merging.policy b/merging.policy new file mode 100644 index 0000000..f956d76 --- /dev/null +++ b/merging.policy @@ -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 index 0000000..baec76a --- /dev/null +++ b/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") diff --git a/src/main/java/me/shedaniel/lightoverlay/LightOverlay.java b/src/main/java/me/shedaniel/lightoverlay/LightOverlay.java deleted file mode 100644 index aa20830..0000000 --- a/src/main/java/me/shedaniel/lightoverlay/LightOverlay.java +++ /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 index acd8255..0000000 --- a/src/main/java/me/shedaniel/lightoverlay/LightOverlayClient.java +++ /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 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 POS = Lists.newCopyOnWriteArrayList(); - private static final Map> 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>> chunkMapIterator = CHUNK_MAP.entrySet().iterator(); - while (chunkMapIterator.hasNext()) { - Map.Entry> 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 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> entry : CHUNK_MAP.entrySet()) { - if (MathHelper.abs(entry.getKey().x - playerPosX) > getChunkRange() || MathHelper.abs(entry.getKey().z - playerPosZ) > getChunkRange()) { - continue; - } - for (Map.Entry 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> entry : CHUNK_MAP.entrySet()) { - if (MathHelper.abs(entry.getKey().x - playerPosX) > getChunkRange() || MathHelper.abs(entry.getKey().z - playerPosZ) > getChunkRange()) { - continue; - } - for (Map.Entry 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 index 1dd264e..0000000 --- a/src/main/java/me/shedaniel/lightoverlay/LightOverlayCloth.java +++ /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 index 134c427..0000000 --- a/src/main/resources/META-INF/NetworkManager.js +++ /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 index 0282268..0000000 --- a/src/main/resources/META-INF/WorldRenderer.js +++ /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 index ed2abed..0000000 --- a/src/main/resources/META-INF/coremods.json +++ /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 index a6d7f03..0000000 --- a/src/main/resources/META-INF/mods.toml +++ /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 -# 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 index f209db8..0000000 --- a/src/main/resources/assets/lightoverlay-forge/lang/en_us.json +++ /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 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 index 1c54690..0000000 --- a/src/main/resources/pack.mcmeta +++ /dev/null @@ -1,6 +0,0 @@ -{ - "pack": { - "description": "Light Overlay Forge", - "pack_format": 4 - } -}