# Compiled nonsense that does not belong in *source* control
+*/build
/build
/bin
/.gradle
/minecraft
/out
+*/run
/run
/classes
-LightOverlay-Forge
+# LightOverlay
+
+Fabric and Forge are split into modules, and we merge it afterwards
+
+# Running
+Fabric: `gradlew :fabric:runClient`<br>
+Forge: `gradlew :forge:runClient`
+
+Or use the run configs if you are using IntelliJ IDEA.
+
+# Compiling
+Run `gradlew buildMerged`, and the merged jar will be in `build/libs/`.
\ No newline at end of file
buildscript {
repositories {
- maven { url = 'https://files.minecraftforge.net/maven' }
- jcenter()
mavenCentral()
}
dependencies {
- classpath group: 'net.minecraftforge.gradle', name: 'ForgeGradle', version: '3.+', changing: true
+ classpath("commons-io:commons-io:2.6")
}
}
+import org.jetbrains.gradle.ext.ActionDelegationConfig
+
+import java.nio.file.FileVisitResult
+import java.nio.file.Files
+import java.nio.file.Path
+import java.nio.file.SimpleFileVisitor
+import java.nio.file.attribute.BasicFileAttributes
+import java.util.stream.Stream
+import java.util.zip.ZipEntry
+import java.util.zip.ZipInputStream
+import java.util.zip.ZipOutputStream
+
plugins {
- id "com.wynprice.cursemaven" version "2.1.1"
+ id "org.jetbrains.gradle.plugin.idea-ext" version "0.7"
}
-apply plugin: 'net.minecraftforge.gradle'
-apply plugin: 'eclipse'
+allprojects {
+ apply plugin: "java"
-version = "4.6.1"
-group = "me.shedaniel" // http://maven.apache.org/guides/mini/guide-naming-conventions.html
-archivesBaseName = "LightOverlay"
+ group "me.shedaniel"
+ archivesBaseName = rootProject.name
+ version = rootProject.mod_version
-sourceCompatibility = targetCompatibility = compileJava.sourceCompatibility = compileJava.targetCompatibility = '1.8'
+ sourceCompatibility = targetCompatibility = compileJava.sourceCompatibility = compileJava.targetCompatibility = JavaVersion.VERSION_1_8
+}
-minecraft {
- mappings channel: 'snapshot', version: '20200408-1.15.1'
- runs {
- client {
- workingDirectory project.file('run')
- // Recommended logging data for a userdev environment
- property 'forge.logging.markers', 'SCAN,REGISTRIES,REGISTRYDUMP'
- // Recommended logging level for the console
- property 'forge.logging.console.level', 'debug'
- mods {
- examplemod {
- source sourceSets.main
- }
- }
+idea.project.settings {
+ delegateActions {
+ delegateBuildRunToGradle = false
+ testRunner = ActionDelegationConfig.TestRunner.PLATFORM
+ }
+ runConfigurations {
+ "Fabric: Minecraft Client"(org.jetbrains.gradle.ext.Gradle) {
+ project = rootProject.project(":fabric")
+ taskNames = Collections.singletonList("runClient")
}
- server {
- workingDirectory project.file('run')
- // Recommended logging data for a userdev environment
- property 'forge.logging.markers', 'SCAN,REGISTRIES,REGISTRYDUMP'
- // Recommended logging level for the console
- property 'forge.logging.console.level', 'debug'
- mods {
- examplemod {
- source sourceSets.main
- }
- }
+ "Fabric: Minecraft Server"(org.jetbrains.gradle.ext.Gradle) {
+ project = rootProject.project(":fabric")
+ taskNames = Collections.singletonList("runServer")
+ }
+ "Forge: Minecraft Client"(org.jetbrains.gradle.ext.Gradle) {
+ project = rootProject.project(":forge")
+ taskNames = Collections.singletonList("runClient")
+ }
+ "Forge: Minecraft Server"(org.jetbrains.gradle.ext.Gradle) {
+ project = rootProject.project(":forge")
+ taskNames = Collections.singletonList("runServer")
}
}
}
-repositories {
- maven { url "https://files.minecraftforge.net/maven" }
+task buildMerged {
+ doLast {
+ def folder = file(".gradle/.mergemods")
+ folder.mkdirs()
+ def fabricJar = file("fabric/build/libs/${rootProject.name}-${rootProject.mod_version}.jar")
+ def forgeJar = file("forge/build/libs/${rootProject.name}-${rootProject.mod_version}.jar")
+ def fabricFolder = new File(folder, ".tempFabric")
+ def forgeFolder = new File(folder, ".tempForge")
+ def mergeFolder = new File(folder, ".tempMerge")
+ def policyMap = new HashMap<String, String>()
+ file("merging.policy").eachLine {
+ if (it.isBlank() || it.startsWith("#")) return
+ def env = it.substring(0, it.indexOf(' '))
+ if (env == "FABRIC")
+ policyMap.put(it.substring(env.length() + 1), "Fabric")
+ else if (env == "FORGE")
+ policyMap.put(it.substring(env.length() + 1), "Forge")
+ else throw new IllegalStateException("Illegal env $env at $it")
+ }
+ forgeFolder.deleteDir()
+ fabricFolder.deleteDir()
+ mergeFolder.deleteDir()
+ unzip(fabricJar, fabricFolder)
+ unzip(forgeJar, forgeFolder)
+ mergeFolder.mkdirs()
+ Stream.of(forgeFolder, fabricFolder).each { useFolder ->
+ try {
+ Files.walkFileTree(useFolder.toPath(), new SimpleFileVisitor<Path>() {
+ @Override
+ FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
+ try {
+ File ogFile = file.toFile()
+ File outFile = new File(mergeFolder, ogFile.getAbsolutePath().replace(useFolder.getAbsolutePath(), ""))
+ outFile.getParentFile().mkdirs()
+ if (outFile.exists()) {
+ def env = useFolder.getName().substring(5)
+ def fileName = outFile.getAbsolutePath().replace(mergeFolder.getAbsolutePath(), "")
+ def policyEnv = policyMap.get(fileName)
+ if (policyEnv == null) {
+ throw new IllegalStateException("Unhandled duplicate file: $fileName")
+ }
+ println "Chose env ${policyEnv.toUpperCase(Locale.ROOT)} for duplicate file: $fileName"
+ if (policyEnv != env)
+ return FileVisitResult.CONTINUE
+ }
+ if (!ogFile.isDirectory()) {
+ org.apache.commons.io.FileUtils.copyFile(ogFile, outFile)
+ } else {
+ org.apache.commons.io.FileUtils.copyDirectory(ogFile, outFile)
+ }
+ } catch (IOException e) {
+ e.printStackTrace()
+ System.exit(0)
+ }
+ return FileVisitResult.CONTINUE
+ }
+ })
+ } catch (IOException e) {
+ e.printStackTrace()
+ System.exit(0)
+ }
+ }
+ File finalMerge = file("build/libs/${rootProject.name}-${rootProject.mod_version}.jar")
+ finalMerge.parentFile.mkdirs()
+ finalMerge.delete()
+ compress(mergeFolder.toPath(), finalMerge)
+ folder.deleteDir()
+ }
}
-dependencies {
- minecraft 'net.minecraftforge:forge:1.15.2-31.1.39'
- implementation fg.deobf("curse.maven:cloth-config-forge:2938583")
-}
+buildMerged.mustRunAfter project(":fabric").tasks.getByName("build")
+buildMerged.mustRunAfter project(":forge").tasks.getByName("build")
-jar {
- manifest {
- attributes(["Specification-Title" : "examplemod",
- "Specification-Vendor" : "examplemodsareus",
- "Specification-Version" : "1", // We are version 1 of the modlauncher specification
- "Implementation-Title" : project.name,
- "Implementation-Version" : "${version}",
- "Implementation-Vendor" : "examplemodsareus",
- "Implementation-Timestamp": new Date().format("yyyy-MM-dd'T'HH:mm:ssZ")],)
+static def compress(Path sourceDir, File zipFile) {
+ try {
+ final ZipOutputStream outputStream = new ZipOutputStream(new FileOutputStream(zipFile))
+ Files.walkFileTree(sourceDir, new SimpleFileVisitor<Path>() {
+ @Override
+ FileVisitResult visitFile(Path file, BasicFileAttributes attributes) {
+ try {
+ Path targetFile = sourceDir.relativize(file)
+ outputStream.putNextEntry(new ZipEntry(targetFile.toString()))
+ byte[] bytes = Files.readAllBytes(file)
+ outputStream.write(bytes, 0, bytes.length)
+ outputStream.closeEntry()
+ } catch (IOException e) {
+ e.printStackTrace()
+ }
+ return FileVisitResult.CONTINUE
+ }
+ })
+ outputStream.close()
+ } catch (IOException e) {
+ e.printStackTrace()
}
}
+
+static def unzip(File zipFile, File destDir) {
+ if (!destDir.exists())
+ destDir.mkdirs()
+ FileInputStream fis
+ byte[] buffer = new byte[1024]
+ try {
+ fis = new FileInputStream(zipFile)
+ ZipInputStream zis = new ZipInputStream(fis)
+ ZipEntry zipEntry = zis.getNextEntry()
+ while (zipEntry != null) {
+ if (!zipEntry.isDirectory()) {
+ File newFile = new File(destDir, zipEntry.getName())
+ new File(newFile.getParent()).mkdirs()
+ FileOutputStream fos = new FileOutputStream(newFile)
+ int len
+ while ((len = zis.read(buffer)) > 0) {
+ fos.write(buffer, 0, len)
+ }
+ fos.close()
+ }
+ zis.closeEntry()
+ zipEntry = zis.getNextEntry()
+ }
+ zis.closeEntry()
+ zis.close()
+ fis.close()
+ } catch (IOException e) {
+ e.printStackTrace()
+ }
+}
\ No newline at end of file
--- /dev/null
+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
+ }
+}
--- /dev/null
+package me.shedaniel.lightoverlay.fabric;
+
+import io.github.prospector.modmenu.api.ModMenuApi;
+import me.shedaniel.clothconfig2.api.ConfigBuilder;
+import me.shedaniel.clothconfig2.api.ConfigCategory;
+import me.shedaniel.clothconfig2.api.ConfigEntryBuilder;
+import net.minecraft.client.gui.screen.Screen;
+import net.minecraft.util.math.MathHelper;
+
+import java.util.function.Function;
+
+public class LOModMenuEntry implements ModMenuApi {
+ @Override
+ public String getModId() {
+ return "lightoverlay";
+ }
+
+ @Override
+ public Function<Screen, ? extends Screen> getConfigScreenFactory() {
+ return this::getConfigScreenByCloth;
+ }
+
+ public Screen getConfigScreenByCloth(Screen parent) {
+ ConfigBuilder builder = ConfigBuilder.create().setParentScreen(parent).setTitle("key.lightoverlay.category");
+
+ ConfigEntryBuilder eb = builder.entryBuilder();
+ ConfigCategory general = builder.getOrCreateCategory("config.lightoverlay.general");
+ general.addEntry(eb.startIntSlider("config.lightoverlay.reach", LightOverlay.reach, 1, 64).setDefaultValue(12).setTextGetter(integer -> "Reach: " + integer + " Blocks").setSaveConsumer(integer -> LightOverlay.reach = integer).build());
+ general.addEntry(eb.startIntSlider("config.lightoverlay.crossLevel", LightOverlay.crossLevel, 0, 15).setDefaultValue(7).setTextGetter(integer -> "Cross Level: " + integer).setSaveConsumer(integer -> LightOverlay.crossLevel = integer).build());
+ general.addEntry(eb.startBooleanToggle("config.lightoverlay.showNumber", LightOverlay.showNumber).setDefaultValue(false).setSaveConsumer(bool -> LightOverlay.showNumber = bool).build());
+ general.addEntry(eb.startBooleanToggle("config.lightoverlay.smoothLines", LightOverlay.smoothLines).setDefaultValue(true).setSaveConsumer(bool -> LightOverlay.smoothLines = bool).build());
+ general.addEntry(eb.startBooleanToggle("config.lightoverlay.underwater", LightOverlay.underwater).setDefaultValue(false).setSaveConsumer(bool -> LightOverlay.underwater = bool).build());
+ general.addEntry(eb.startIntSlider("config.lightoverlay.lineWidth", MathHelper.floor(LightOverlay.lineWidth * 100), 100, 700).setDefaultValue(100).setTextGetter(integer -> "Light Width: " + LightOverlay.FORMAT.format(integer / 100d)).setSaveConsumer(integer -> LightOverlay.lineWidth = integer / 100f).build());
+ general.addEntry(eb.startColorField("config.lightoverlay.yellowColor", LightOverlay.yellowColor).setDefaultValue(0xFFFF00).setSaveConsumer(color -> LightOverlay.yellowColor = color).build());
+ general.addEntry(eb.startColorField("config.lightoverlay.redColor", LightOverlay.redColor).setDefaultValue(0xFF0000).setSaveConsumer(color -> LightOverlay.redColor = color).build());
+
+ return builder.setSavingRunnable(() -> {
+ try {
+ LightOverlay.saveConfig(LightOverlay.configFile);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ LightOverlay.loadConfig(LightOverlay.configFile);
+ }).build();
+ }
+}
--- /dev/null
+package me.shedaniel.lightoverlay.fabric;
+
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.mojang.blaze3d.platform.GlStateManager;
+import com.mojang.blaze3d.systems.RenderSystem;
+import me.shedaniel.cloth.hooks.ClothClientHooks;
+import net.fabricmc.api.ClientModInitializer;
+import net.fabricmc.fabric.api.client.keybinding.FabricKeyBinding;
+import net.fabricmc.fabric.api.client.keybinding.KeyBindingRegistry;
+import net.fabricmc.fabric.api.event.client.ClientTickCallback;
+import net.fabricmc.loader.api.FabricLoader;
+import net.minecraft.block.Block;
+import net.minecraft.block.BlockState;
+import net.minecraft.client.MinecraftClient;
+import net.minecraft.client.font.TextRenderer;
+import net.minecraft.client.network.ClientPlayerEntity;
+import net.minecraft.client.render.*;
+import net.minecraft.client.util.InputUtil;
+import net.minecraft.client.util.math.Rotation3;
+import net.minecraft.client.world.ClientWorld;
+import net.minecraft.entity.Entity;
+import net.minecraft.entity.EntityCategory;
+import net.minecraft.entity.EntityContext;
+import net.minecraft.entity.EntityType;
+import net.minecraft.entity.player.PlayerEntity;
+import net.minecraft.tag.BlockTags;
+import net.minecraft.text.TranslatableText;
+import net.minecraft.util.Identifier;
+import net.minecraft.util.math.*;
+import net.minecraft.util.shape.VoxelShape;
+import net.minecraft.world.BlockView;
+import net.minecraft.world.LightType;
+import net.minecraft.world.World;
+import net.minecraft.world.biome.Biome;
+import net.minecraft.world.chunk.ChunkStatus;
+import net.minecraft.world.chunk.WorldChunk;
+import net.minecraft.world.chunk.light.ChunkLightingView;
+import org.apache.logging.log4j.LogManager;
+import org.lwjgl.opengl.GL11;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.text.DecimalFormat;
+import java.util.*;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+public class LightOverlay implements ClientModInitializer {
+ static final DecimalFormat FORMAT = new DecimalFormat("#.#");
+ private static final String KEYBIND_CATEGORY = "key.lightoverlay.category";
+ private static final Identifier ENABLE_OVERLAY_KEYBIND = new Identifier("lightoverlay", "enable_overlay");
+ private static final Identifier INCREASE_REACH_KEYBIND = new Identifier("lightoverlay", "increase_reach");
+ private static final Identifier DECREASE_REACH_KEYBIND = new Identifier("lightoverlay", "decrease_reach");
+ private static final Identifier INCREASE_LINE_WIDTH_KEYBIND = new Identifier("lightoverlay", "increase_line_width");
+ private static final Identifier DECREASE_LINE_WIDTH_KEYBIND = new Identifier("lightoverlay", "decrease_line_width");
+ static int reach = 12;
+ static int crossLevel = 7;
+ static boolean showNumber = false;
+ static boolean smoothLines = true;
+ static boolean underwater = false;
+ static float lineWidth = 1.0F;
+ static int yellowColor = 0xFFFF00, redColor = 0xFF0000;
+ static File configFile = new File(FabricLoader.getInstance().getConfigDirectory(), "lightoverlay.properties");
+ private static FabricKeyBinding enableOverlay, increaseReach, decreaseReach, increaseLineWidth, decreaseLineWidth;
+ private static boolean enabled = false;
+ private static EntityType<Entity> testingEntityType;
+ private static int threadNumber = 0;
+ private static final ExecutorService EXECUTOR = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors(), r -> {
+ Thread thread = new Thread(r, "light-overlay-" + threadNumber++);
+ thread.setDaemon(true);
+ return thread;
+ });
+ private static final List<ChunkPos> POS = Lists.newCopyOnWriteArrayList();
+ private static final Map<ChunkPos, Map<Long, Object>> CHUNK_MAP = Maps.newConcurrentMap();
+ private static long ticks = 0;
+
+ static {
+ ClientTickCallback.EVENT.register(client -> {
+ try {
+ ticks++;
+ if (MinecraftClient.getInstance().player == null || !enabled) {
+ POS.clear();
+ CHUNK_MAP.clear();
+ } else {
+ ClientPlayerEntity player = MinecraftClient.getInstance().player;
+ ClientWorld world = MinecraftClient.getInstance().world;
+ EntityContext entityContext = EntityContext.of(player);
+ Vec3d[] playerPos = {null};
+ int playerPosX = ((int) player.getX()) >> 4;
+ int playerPosZ = ((int) player.getZ()) >> 4;
+ if (ticks % 20 == 0) {
+ for (int chunkX = playerPosX - getChunkRange(); chunkX <= playerPosX + getChunkRange(); chunkX++) {
+ for (int chunkZ = playerPosZ - getChunkRange(); chunkZ <= playerPosZ + getChunkRange(); chunkZ++) {
+ ChunkPos chunkPos = new ChunkPos(chunkX, chunkZ);
+ if (!CHUNK_MAP.containsKey(chunkPos))
+ queueChunk(chunkPos);
+ }
+ }
+ }
+ if (!POS.isEmpty()) {
+ if (playerPos[0] == null) {
+ playerPos[0] = player.getPos();
+ }
+ ChunkPos pos = POS.stream().min(Comparator.comparingDouble(value -> value.toBlockPos(8, 0, 8).getSquaredDistance(playerPos[0].x, 0, playerPos[0].z, false))).get();
+ EXECUTOR.submit(() -> {
+ if (MathHelper.abs(pos.x - playerPosX) <= getChunkRange() && MathHelper.abs(pos.z - playerPosZ) <= getChunkRange()) {
+ calculateChunk(world.getChunkManager().getChunk(pos.x, pos.z, ChunkStatus.FULL, false), world, pos, entityContext);
+ } else {
+ CHUNK_MAP.remove(pos);
+ }
+ });
+ POS.remove(pos);
+ }
+ Iterator<Map.Entry<ChunkPos, Map<Long, Object>>> chunkMapIterator = CHUNK_MAP.entrySet().iterator();
+ while (chunkMapIterator.hasNext()) {
+ Map.Entry<ChunkPos, Map<Long, Object>> pos = chunkMapIterator.next();
+ if (MathHelper.abs(pos.getKey().x - playerPosX) > getChunkRange() * 2 || MathHelper.abs(pos.getKey().z - playerPosZ) > getChunkRange() * 2) {
+ chunkMapIterator.remove();
+ }
+ }
+ }
+ } catch (Exception e) {
+ LogManager.getLogger().throwing(e);
+ }
+ });
+ }
+
+ public static void queueChunkAndNear(ChunkPos pos) {
+ for (int xOffset = -1; xOffset <= 1; xOffset++) {
+ for (int zOffset = -1; zOffset <= 1; zOffset++) {
+ queueChunk(new ChunkPos(pos.x + xOffset, pos.z + zOffset));
+ }
+ }
+ }
+
+ public static void queueChunk(ChunkPos pos) {
+ if (!POS.contains(pos))
+ POS.add(0, pos);
+ }
+
+ public static int getChunkRange() {
+ return Math.max(MathHelper.ceil(reach / 16f), 1);
+ }
+
+ private static void calculateChunk(WorldChunk chunk, World world, ChunkPos chunkPos, EntityContext entityContext) {
+ Map<Long, Object> map = Maps.newHashMap();
+ if (chunk != null) {
+ ChunkLightingView block = chunk.getLightingProvider().get(LightType.BLOCK);
+ ChunkLightingView sky = showNumber ? null : chunk.getLightingProvider().get(LightType.SKY);
+ for (BlockPos pos : BlockPos.iterate(chunkPos.getStartX(), 0, chunkPos.getStartZ(), chunkPos.getEndX(), 256, chunkPos.getEndZ())) {
+ BlockPos down = pos.down();
+ if (showNumber) {
+ int level = LightOverlay.getCrossLevel(pos, down, chunk, block, entityContext);
+ if (level >= 0) {
+ map.put(pos.asLong(), level);
+ }
+ } else {
+ Biome biome = world.getBiomeAccess().getBiome(pos);
+ if (biome.getMaxSpawnLimit() > 0 && !biome.getEntitySpawnList(EntityCategory.MONSTER).isEmpty()) {
+ CrossType type = LightOverlay.getCrossType(pos, down, chunk, block, sky, entityContext);
+ if (type != CrossType.NONE) {
+ map.put(pos.asLong(), type);
+ }
+ }
+ }
+ }
+ }
+ CHUNK_MAP.put(chunkPos, map);
+ }
+
+ public static CrossType getCrossType(BlockPos pos, BlockPos down, BlockView world, ChunkLightingView block, ChunkLightingView sky, EntityContext entityContext) {
+ BlockState blockBelowState = world.getBlockState(down);
+ BlockState blockUpperState = world.getBlockState(pos);
+ VoxelShape upperCollisionShape = blockUpperState.getCollisionShape(world, pos, entityContext);
+ if (!underwater && !blockUpperState.getFluidState().isEmpty())
+ return CrossType.NONE;
+ // Check if the outline is full
+ if (Block.isFaceFullSquare(upperCollisionShape, Direction.UP))
+ return CrossType.NONE;
+ // TODO: Not to hard code no redstone
+ if (blockUpperState.emitsRedstonePower())
+ return CrossType.NONE;
+ // Check if the collision has a bump
+ if (upperCollisionShape.getMaximum(Direction.Axis.Y) > 0)
+ return CrossType.NONE;
+ if (blockUpperState.getBlock().matches(BlockTags.RAILS))
+ return CrossType.NONE;
+ // Check block state allow spawning (excludes bedrock and barriers automatically)
+ if (!blockBelowState.allowsSpawning(world, down, testingEntityType))
+ return CrossType.NONE;
+ if (block.getLightLevel(pos) > crossLevel)
+ return CrossType.NONE;
+ if (sky.getLightLevel(pos) > crossLevel)
+ return CrossType.YELLOW;
+ return CrossType.RED;
+ }
+
+ public static int getCrossLevel(BlockPos pos, BlockPos down, BlockView world, ChunkLightingView view, EntityContext entityContext) {
+ BlockState blockBelowState = world.getBlockState(down);
+ BlockState blockUpperState = world.getBlockState(pos);
+ VoxelShape collisionShape = blockBelowState.getCollisionShape(world, down, entityContext);
+ VoxelShape upperCollisionShape = blockUpperState.getCollisionShape(world, pos, entityContext);
+ if (!underwater && !blockUpperState.getFluidState().isEmpty())
+ return -1;
+ if (!blockBelowState.getFluidState().isEmpty())
+ return -1;
+ if (blockBelowState.isAir())
+ return -1;
+ if (Block.isFaceFullSquare(upperCollisionShape, Direction.DOWN))
+ return -1;
+ return view.getLightLevel(pos);
+ }
+
+ public static void renderCross(Tessellator tessellator, BufferBuilder buffer, Camera camera, World world, BlockPos pos, int color, EntityContext entityContext) {
+ double d0 = camera.getPos().x;
+ double d1 = camera.getPos().y - .005D;
+ VoxelShape upperOutlineShape = world.getBlockState(pos).getOutlineShape(world, pos, entityContext);
+ if (!upperOutlineShape.isEmpty())
+ d1 -= upperOutlineShape.getMaximum(Direction.Axis.Y);
+ double d2 = camera.getPos().z;
+
+ buffer.begin(1, VertexFormats.POSITION_COLOR);
+ int red = (color >> 16) & 255;
+ int green = (color >> 8) & 255;
+ int blue = color & 255;
+ buffer.vertex(pos.getX() + .01 - d0, pos.getY() - d1, pos.getZ() + .01 - d2).color(red, green, blue, 255).next();
+ buffer.vertex(pos.getX() - .01 + 1 - d0, pos.getY() - d1, pos.getZ() - .01 + 1 - d2).color(red, green, blue, 255).next();
+ buffer.vertex(pos.getX() - .01 + 1 - d0, pos.getY() - d1, pos.getZ() + .01 - d2).color(red, green, blue, 255).next();
+ buffer.vertex(pos.getX() + .01 - d0, pos.getY() - d1, pos.getZ() - .01 + 1 - d2).color(red, green, blue, 255).next();
+ tessellator.draw();
+ }
+
+ public static void renderLevel(MinecraftClient client, Camera camera, World world, BlockPos pos, BlockPos down, int level, EntityContext entityContext) {
+ String string_1 = String.valueOf(level);
+ TextRenderer textRenderer_1 = client.textRenderer;
+ double double_4 = camera.getPos().x;
+ double double_5 = camera.getPos().y;
+ VoxelShape upperOutlineShape = world.getBlockState(down).getOutlineShape(world, down, entityContext);
+ if (!upperOutlineShape.isEmpty())
+ double_5 += 1 - upperOutlineShape.getMaximum(Direction.Axis.Y);
+ double double_6 = camera.getPos().z;
+ RenderSystem.pushMatrix();
+ RenderSystem.translatef((float) (pos.getX() + 0.5f - double_4), (float) (pos.getY() - double_5) + 0.005f, (float) (pos.getZ() + 0.5f - double_6));
+ RenderSystem.rotatef(90, 1, 0, 0);
+ RenderSystem.normal3f(0.0F, 1.0F, 0.0F);
+ float size = 0.07F;
+ RenderSystem.scalef(-size, -size, size);
+ float float_3 = (float) (-textRenderer_1.getStringWidth(string_1)) / 2.0F + 0.4f;
+ RenderSystem.enableAlphaTest();
+ VertexConsumerProvider.Immediate vertexConsumerProvider$Immediate_1 = VertexConsumerProvider.immediate(Tessellator.getInstance().getBuffer());
+ textRenderer_1.draw(string_1, float_3, -3.5f, level > crossLevel ? 0xff042404 : 0xff731111, false, Rotation3.identity().getMatrix(), vertexConsumerProvider$Immediate_1, false, 0, 15728880);
+ vertexConsumerProvider$Immediate_1.draw();
+ RenderSystem.popMatrix();
+ }
+
+ static void loadConfig(File file) {
+ try {
+ redColor = 0xFF0000;
+ yellowColor = 0xFFFF00;
+ if (!file.exists() || !file.canRead())
+ saveConfig(file);
+ FileInputStream fis = new FileInputStream(file);
+ Properties properties = new Properties();
+ properties.load(fis);
+ fis.close();
+ reach = Integer.parseInt((String) properties.computeIfAbsent("reach", a -> "12"));
+ crossLevel = Integer.parseInt((String) properties.computeIfAbsent("crossLevel", a -> "7"));
+ showNumber = ((String) properties.computeIfAbsent("showNumber", a -> "false")).equalsIgnoreCase("true");
+ smoothLines = ((String) properties.computeIfAbsent("smoothLines", a -> "true")).equalsIgnoreCase("true");
+ underwater = ((String) properties.computeIfAbsent("underwater", a -> "false")).equalsIgnoreCase("true");
+ lineWidth = Float.parseFloat((String) properties.computeIfAbsent("lineWidth", a -> "1"));
+ {
+ int r, g, b;
+ r = Integer.parseInt((String) properties.computeIfAbsent("yellowColorRed", a -> "255"));
+ g = Integer.parseInt((String) properties.computeIfAbsent("yellowColorGreen", a -> "255"));
+ b = Integer.parseInt((String) properties.computeIfAbsent("yellowColorBlue", a -> "0"));
+ yellowColor = (r << 16) + (g << 8) + b;
+ }
+ {
+ int r, g, b;
+ r = Integer.parseInt((String) properties.computeIfAbsent("redColorRed", a -> "255"));
+ g = Integer.parseInt((String) properties.computeIfAbsent("redColorGreen", a -> "0"));
+ b = Integer.parseInt((String) properties.computeIfAbsent("redColorBlue", a -> "0"));
+ redColor = (r << 16) + (g << 8) + b;
+ }
+ saveConfig(file);
+ } catch (Exception e) {
+ e.printStackTrace();
+ reach = 12;
+ crossLevel = 7;
+ lineWidth = 1.0F;
+ redColor = 0xFF0000;
+ yellowColor = 0xFFFF00;
+ showNumber = false;
+ smoothLines = true;
+ underwater = false;
+ try {
+ saveConfig(file);
+ } catch (IOException ex) {
+ ex.printStackTrace();
+ }
+ }
+ CHUNK_MAP.clear();
+ POS.clear();
+ }
+
+ static void saveConfig(File file) throws IOException {
+ FileOutputStream fos = new FileOutputStream(file, false);
+ fos.write("# Light Overlay Config".getBytes());
+ fos.write("\n".getBytes());
+ fos.write(("reach=" + reach).getBytes());
+ fos.write("\n".getBytes());
+ fos.write(("crossLevel=" + crossLevel).getBytes());
+ fos.write("\n".getBytes());
+ fos.write(("showNumber=" + showNumber).getBytes());
+ fos.write("\n".getBytes());
+ fos.write(("smoothLines=" + smoothLines).getBytes());
+ fos.write("\n".getBytes());
+ fos.write(("underwater=" + underwater).getBytes());
+ fos.write("\n".getBytes());
+ fos.write(("lineWidth=" + FORMAT.format(lineWidth)).getBytes());
+ fos.write("\n".getBytes());
+ fos.write(("yellowColorRed=" + ((yellowColor >> 16) & 255)).getBytes());
+ fos.write("\n".getBytes());
+ fos.write(("yellowColorGreen=" + ((yellowColor >> 8) & 255)).getBytes());
+ fos.write("\n".getBytes());
+ fos.write(("yellowColorBlue=" + (yellowColor & 255)).getBytes());
+ fos.write("\n".getBytes());
+ fos.write(("redColorRed=" + ((redColor >> 16) & 255)).getBytes());
+ fos.write("\n".getBytes());
+ fos.write(("redColorGreen=" + ((redColor >> 8) & 255)).getBytes());
+ fos.write("\n".getBytes());
+ fos.write(("redColorBlue=" + (redColor & 255)).getBytes());
+ fos.close();
+ }
+
+ @Override
+ public void onInitializeClient() {
+ // Load Config
+ loadConfig(configFile);
+
+ // Setup
+ testingEntityType = EntityType.Builder.create(EntityCategory.MONSTER).setDimensions(0f, 0f).disableSaving().build(null);
+ MinecraftClient client = MinecraftClient.getInstance();
+ KeyBindingRegistry.INSTANCE.addCategory(KEYBIND_CATEGORY);
+ KeyBindingRegistry.INSTANCE.register(enableOverlay = FabricKeyBinding.Builder.create(ENABLE_OVERLAY_KEYBIND, InputUtil.Type.KEYSYM, 296, KEYBIND_CATEGORY).build());
+ KeyBindingRegistry.INSTANCE.register(increaseReach = FabricKeyBinding.Builder.create(INCREASE_REACH_KEYBIND, InputUtil.Type.KEYSYM, -1, KEYBIND_CATEGORY).build());
+ KeyBindingRegistry.INSTANCE.register(decreaseReach = FabricKeyBinding.Builder.create(DECREASE_REACH_KEYBIND, InputUtil.Type.KEYSYM, -1, KEYBIND_CATEGORY).build());
+ KeyBindingRegistry.INSTANCE.register(increaseLineWidth = FabricKeyBinding.Builder.create(INCREASE_LINE_WIDTH_KEYBIND, InputUtil.Type.KEYSYM, -1, KEYBIND_CATEGORY).build());
+ KeyBindingRegistry.INSTANCE.register(decreaseLineWidth = FabricKeyBinding.Builder.create(DECREASE_LINE_WIDTH_KEYBIND, InputUtil.Type.KEYSYM, -1, KEYBIND_CATEGORY).build());
+ ClothClientHooks.HANDLE_INPUT.register(minecraftClient -> {
+ while (enableOverlay.wasPressed())
+ enabled = !enabled;
+ while (increaseReach.wasPressed()) {
+ if (reach < 64)
+ reach++;
+ try {
+ saveConfig(configFile);
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ client.player.addChatMessage(new TranslatableText("text.lightoverlay.current_reach", reach), false);
+ }
+ while (decreaseReach.wasPressed()) {
+ if (reach > 1)
+ reach--;
+ try {
+ saveConfig(configFile);
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ client.player.addChatMessage(new TranslatableText("text.lightoverlay.current_reach", reach), false);
+ }
+ while (increaseLineWidth.wasPressed()) {
+ if (lineWidth < 7)
+ lineWidth += 0.1f;
+ try {
+ saveConfig(configFile);
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ client.player.addChatMessage(new TranslatableText("text.lightoverlay.current_line_width", FORMAT.format(lineWidth)), false);
+ }
+ while (decreaseLineWidth.wasPressed()) {
+ if (lineWidth > 1)
+ lineWidth -= 0.1F;
+ try {
+ saveConfig(configFile);
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ client.player.addChatMessage(new TranslatableText("text.lightoverlay.current_line_width", FORMAT.format(lineWidth)), false);
+ }
+ });
+ ClothClientHooks.DEBUG_RENDER_PRE.register(() -> {
+ if (LightOverlay.enabled) {
+ PlayerEntity playerEntity = client.player;
+ int playerPosX = ((int) playerEntity.getX()) >> 4;
+ int playerPosZ = ((int) playerEntity.getZ()) >> 4;
+ EntityContext entityContext = EntityContext.of(playerEntity);
+ World world = client.world;
+ BlockPos playerPos = new BlockPos(playerEntity.getX(), playerEntity.getY(), playerEntity.getZ());
+ Camera camera = MinecraftClient.getInstance().gameRenderer.getCamera();
+ if (showNumber) {
+ RenderSystem.enableTexture();
+ RenderSystem.depthMask(true);
+ BlockPos.Mutable mutable = new BlockPos.Mutable();
+ for (Map.Entry<ChunkPos, Map<Long, Object>> entry : CHUNK_MAP.entrySet()) {
+ if (MathHelper.abs(entry.getKey().x - playerPosX) > getChunkRange() || MathHelper.abs(entry.getKey().z - playerPosZ) > getChunkRange()) {
+ continue;
+ }
+ for (Map.Entry<Long, Object> objectEntry : entry.getValue().entrySet()) {
+ if (objectEntry.getValue() instanceof Integer) {
+ mutable.set(BlockPos.unpackLongX(objectEntry.getKey()), BlockPos.unpackLongY(objectEntry.getKey()), BlockPos.unpackLongZ(objectEntry.getKey()));
+ if (mutable.isWithinDistance(playerPos, reach)) {
+ BlockPos down = mutable.down();
+ LightOverlay.renderLevel(client, camera, world, mutable, down, (Integer) objectEntry.getValue(), entityContext);
+ }
+ }
+ }
+ }
+ RenderSystem.color4f(1.0F, 1.0F, 1.0F, 1.0F);
+ RenderSystem.enableDepthTest();
+ } else {
+ RenderSystem.enableDepthTest();
+ RenderSystem.disableTexture();
+ RenderSystem.enableBlend();
+ RenderSystem.blendFunc(GlStateManager.SrcFactor.SRC_ALPHA, GlStateManager.DstFactor.ONE_MINUS_SRC_ALPHA);
+ RenderSystem.disableLighting();
+ if (smoothLines) GL11.glEnable(GL11.GL_LINE_SMOOTH);
+ RenderSystem.lineWidth(lineWidth);
+ Tessellator tessellator = Tessellator.getInstance();
+ BufferBuilder buffer = tessellator.getBuffer();
+ BlockPos.Mutable mutable = new BlockPos.Mutable();
+ for (Map.Entry<ChunkPos, Map<Long, Object>> entry : CHUNK_MAP.entrySet()) {
+ if (MathHelper.abs(entry.getKey().x - playerPosX) > getChunkRange() || MathHelper.abs(entry.getKey().z - playerPosZ) > getChunkRange()) {
+ continue;
+ }
+ for (Map.Entry<Long, Object> objectEntry : entry.getValue().entrySet()) {
+ if (objectEntry.getValue() instanceof CrossType) {
+ mutable.set(BlockPos.unpackLongX(objectEntry.getKey()), BlockPos.unpackLongY(objectEntry.getKey()), BlockPos.unpackLongZ(objectEntry.getKey()));
+ if (mutable.isWithinDistance(playerPos, reach)) {
+ BlockPos down = mutable.down();
+ int color = objectEntry.getValue() == CrossType.RED ? redColor : yellowColor;
+ LightOverlay.renderCross(tessellator, buffer, camera, world, mutable, color, entityContext);
+ }
+ }
+ }
+ }
+ RenderSystem.disableBlend();
+ RenderSystem.enableTexture();
+ if (smoothLines) GL11.glDisable(GL11.GL_LINE_SMOOTH);
+ }
+ }
+ });
+ }
+
+ private enum CrossType {
+ YELLOW,
+ RED,
+ NONE
+ }
+}
--- /dev/null
+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));
+ }
+ }
+}
--- /dev/null
+{
+ "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
--- /dev/null
+{
+ "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
+ }
+}
--- /dev/null
+accessWidener v1 named
+accessible field net/minecraft/network/packet/s2c/play/ChunkDeltaUpdateS2CPacket chunkPos Lnet/minecraft/util/math/ChunkPos;
\ No newline at end of file
--- /dev/null
+{
+ "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
--- /dev/null
+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
--- /dev/null
+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));
+ }
+}
--- /dev/null
+package me.shedaniel.lightoverlay.forge;
+
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.mojang.blaze3d.systems.RenderSystem;
+import net.minecraft.block.Block;
+import net.minecraft.block.BlockState;
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.entity.player.ClientPlayerEntity;
+import net.minecraft.client.gui.FontRenderer;
+import net.minecraft.client.renderer.*;
+import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
+import net.minecraft.client.settings.KeyBinding;
+import net.minecraft.client.util.InputMappings;
+import net.minecraft.client.world.ClientWorld;
+import net.minecraft.entity.Entity;
+import net.minecraft.entity.EntityClassification;
+import net.minecraft.entity.EntityType;
+import net.minecraft.network.IPacket;
+import net.minecraft.network.play.server.SChangeBlockPacket;
+import net.minecraft.network.play.server.SChunkDataPacket;
+import net.minecraft.network.play.server.SMultiBlockChangePacket;
+import net.minecraft.tags.BlockTags;
+import net.minecraft.util.Direction;
+import net.minecraft.util.ResourceLocation;
+import net.minecraft.util.math.BlockPos;
+import net.minecraft.util.math.ChunkPos;
+import net.minecraft.util.math.MathHelper;
+import net.minecraft.util.math.Vec3d;
+import net.minecraft.util.math.shapes.ISelectionContext;
+import net.minecraft.util.math.shapes.VoxelShape;
+import net.minecraft.util.text.TranslationTextComponent;
+import net.minecraft.world.IBlockReader;
+import net.minecraft.world.LightType;
+import net.minecraft.world.World;
+import net.minecraft.world.biome.Biome;
+import net.minecraft.world.chunk.Chunk;
+import net.minecraft.world.chunk.ChunkStatus;
+import net.minecraft.world.lighting.IWorldLightListener;
+import net.minecraftforge.api.distmarker.Dist;
+import net.minecraftforge.client.event.InputEvent;
+import net.minecraftforge.client.settings.KeyConflictContext;
+import net.minecraftforge.client.settings.KeyModifier;
+import net.minecraftforge.common.MinecraftForge;
+import net.minecraftforge.event.TickEvent;
+import net.minecraftforge.eventbus.api.SubscribeEvent;
+import net.minecraftforge.fml.DistExecutor;
+import net.minecraftforge.fml.client.registry.ClientRegistry;
+import net.minecraftforge.fml.common.ObfuscationReflectionHelper;
+import org.apache.logging.log4j.LogManager;
+import org.lwjgl.opengl.GL11;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.text.DecimalFormat;
+import java.util.*;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+public class LightOverlayClient {
+ static final DecimalFormat FORMAT = new DecimalFormat("#.#");
+ private static final String KEYBIND_CATEGORY = "key.lightoverlay-forge.category";
+ private static final ResourceLocation ENABLE_OVERLAY_KEYBIND = new ResourceLocation("lightoverlay-forge", "enable_overlay");
+ private static final ResourceLocation INCREASE_REACH_KEYBIND = new ResourceLocation("lightoverlay-forge", "increase_reach");
+ private static final ResourceLocation DECREASE_REACH_KEYBIND = new ResourceLocation("lightoverlay-forge", "decrease_reach");
+ private static final ResourceLocation INCREASE_LINE_WIDTH_KEYBIND = new ResourceLocation("lightoverlay-forge", "increase_line_width");
+ private static final ResourceLocation DECREASE_LINE_WIDTH_KEYBIND = new ResourceLocation("lightoverlay-forge", "decrease_line_width");
+ static int reach = 12;
+ static int crossLevel = 7;
+ static boolean showNumber = false;
+ static boolean smoothLines = true;
+ static boolean underwater = false;
+ static EntityType<Entity> testingEntityType;
+ static float lineWidth = 1.0F;
+ static int yellowColor = 0xFFFF00, redColor = 0xFF0000;
+ static File configFile = new File(new File(Minecraft.getInstance().gameDir, "config"), "lightoverlay.properties");
+ private static KeyBinding enableOverlay, increaseReach, decreaseReach, increaseLineWidth, decreaseLineWidth;
+ private static boolean enabled = false;
+ private static int threadNumber = 0;
+ private static final ExecutorService EXECUTOR = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors(), r -> {
+ Thread thread = new Thread(r, "light-overlay-" + threadNumber++);
+ thread.setDaemon(true);
+ return thread;
+ });
+ private static final List<ChunkPos> POS = Lists.newCopyOnWriteArrayList();
+ private static final Map<ChunkPos, Map<Long, Object>> CHUNK_MAP = Maps.newConcurrentMap();
+ private static long ticks = 0;
+
+ public static void register() {
+ // Load Config
+ loadConfig(configFile);
+
+ // Setup
+ testingEntityType = EntityType.Builder.create(EntityClassification.MONSTER).size(0f, 0f).disableSerialization().build(null);
+ enableOverlay = registerKeybind(ENABLE_OVERLAY_KEYBIND, InputMappings.Type.KEYSYM, 296, KEYBIND_CATEGORY);
+ increaseReach = registerKeybind(INCREASE_REACH_KEYBIND, InputMappings.Type.KEYSYM, -1, KEYBIND_CATEGORY);
+ decreaseReach = registerKeybind(DECREASE_REACH_KEYBIND, InputMappings.Type.KEYSYM, -1, KEYBIND_CATEGORY);
+ increaseLineWidth = registerKeybind(INCREASE_LINE_WIDTH_KEYBIND, InputMappings.Type.KEYSYM, -1, KEYBIND_CATEGORY);
+ decreaseLineWidth = registerKeybind(DECREASE_LINE_WIDTH_KEYBIND, InputMappings.Type.KEYSYM, -1, KEYBIND_CATEGORY);
+ MinecraftForge.EVENT_BUS.register(LightOverlayClient.class);
+
+ try {
+ //noinspection Convert2MethodRef
+ DistExecutor.runWhenOn(Dist.CLIENT, () -> () -> LightOverlayCloth.register());
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ public static CrossType getCrossType(BlockPos pos, BlockPos down, IBlockReader reader, IWorldLightListener block, IWorldLightListener sky, ISelectionContext selectionContext) {
+ BlockState blockBelowState = reader.getBlockState(down);
+ BlockState blockUpperState = reader.getBlockState(pos);
+ VoxelShape upperCollisionShape = blockUpperState.getCollisionShape(reader, pos, selectionContext);
+ if (!underwater && !blockUpperState.getFluidState().isEmpty())
+ return CrossType.NONE;
+ /* WorldEntitySpawner.func_222266_a */
+ // Check if the outline is full
+ if (Block.doesSideFillSquare(upperCollisionShape, Direction.UP))
+ return CrossType.NONE;
+ // Check if there is power
+ if (blockUpperState.canProvidePower())
+ return CrossType.NONE;
+ // Check if the collision has a bump
+ if (upperCollisionShape.getEnd(Direction.Axis.Y) > 0)
+ return CrossType.NONE;
+ if (blockUpperState.getBlock().isIn(BlockTags.RAILS))
+ return CrossType.NONE;
+ // Check block state allow spawning (excludes bedrock and barriers automatically)
+ if (!blockBelowState.canEntitySpawn(reader, down, testingEntityType))
+ return CrossType.NONE;
+ if (block.getLightFor(pos) > crossLevel)
+ return CrossType.NONE;
+ if (sky.getLightFor(pos) > crossLevel)
+ return CrossType.YELLOW;
+ return CrossType.RED;
+ }
+
+ public static int getCrossLevel(BlockPos pos, BlockPos down, IBlockReader reader, IWorldLightListener light, ISelectionContext context) {
+ BlockState blockBelowState = reader.getBlockState(down);
+ BlockState blockUpperState = reader.getBlockState(pos);
+ VoxelShape collisionShape = blockBelowState.getCollisionShape(reader, down, context);
+ VoxelShape upperCollisionShape = blockUpperState.getCollisionShape(reader, pos, context);
+ if (!underwater && !blockUpperState.getFluidState().isEmpty())
+ return -1;
+ if (!blockBelowState.getFluidState().isEmpty())
+ return -1;
+ if (blockBelowState.isAir(reader, down))
+ return -1;
+ if (Block.doesSideFillSquare(upperCollisionShape, Direction.DOWN))
+ return -1;
+ return light.getLightFor(pos);
+ }
+
+ public static void renderCross(ActiveRenderInfo info, Tessellator tessellator, BufferBuilder buffer, World world, BlockPos pos, int color, ISelectionContext context) {
+ double d0 = info.getProjectedView().x;
+ double d1 = info.getProjectedView().y - .005D;
+ VoxelShape upperOutlineShape = world.getBlockState(pos).getShape(world, pos, context);
+ if (!upperOutlineShape.isEmpty())
+ d1 -= upperOutlineShape.getEnd(Direction.Axis.Y);
+ double d2 = info.getProjectedView().z;
+ buffer.begin(1, DefaultVertexFormats.POSITION_COLOR);
+ int red = (color >> 16) & 255;
+ int green = (color >> 8) & 255;
+ int blue = color & 255;
+ buffer.pos(pos.getX() + .01 - d0, pos.getY() - d1, pos.getZ() + .01 - d2).color(red, green, blue, 255).endVertex();
+ buffer.pos(pos.getX() - .01 + 1 - d0, pos.getY() - d1, pos.getZ() - .01 + 1 - d2).color(red, green, blue, 255).endVertex();
+ buffer.pos(pos.getX() - .01 + 1 - d0, pos.getY() - d1, pos.getZ() + .01 - d2).color(red, green, blue, 255).endVertex();
+ buffer.pos(pos.getX() + .01 - d0, pos.getY() - d1, pos.getZ() - .01 + 1 - d2).color(red, green, blue, 255).endVertex();
+ tessellator.draw();
+ }
+
+ public static void renderLevel(Minecraft minecraft, ActiveRenderInfo info, World world, BlockPos pos, BlockPos down, int level, ISelectionContext context) {
+ String string_1 = String.valueOf(level);
+ FontRenderer fontRenderer = minecraft.fontRenderer;
+ double double_4 = info.getProjectedView().x;
+ double double_5 = info.getProjectedView().y;
+ VoxelShape upperOutlineShape = world.getBlockState(down).getShape(world, down, context);
+ if (!upperOutlineShape.isEmpty())
+ double_5 += 1 - upperOutlineShape.getEnd(Direction.Axis.Y);
+ double double_6 = info.getProjectedView().z;
+ RenderSystem.pushMatrix();
+ RenderSystem.translatef((float) (pos.getX() + 0.5f - double_4), (float) (pos.getY() - double_5) + 0.005f, (float) (pos.getZ() + 0.5f - double_6));
+ RenderSystem.rotatef(90, 1, 0, 0);
+ RenderSystem.normal3f(0.0F, 1.0F, 0.0F);
+ float size = 0.07F;
+ RenderSystem.scalef(-size, -size, size);
+ float float_3 = (float) (-fontRenderer.getStringWidth(string_1)) / 2.0F + 0.4f;
+ RenderSystem.enableAlphaTest();
+ IRenderTypeBuffer.Impl vertexConsumerProvider$Immediate_1 = IRenderTypeBuffer.getImpl(Tessellator.getInstance().getBuffer());
+ fontRenderer.renderString(string_1, float_3, -3.5f, level > crossLevel ? 0xff042404 : 0xff731111, false, TransformationMatrix.identity().getMatrix(), vertexConsumerProvider$Immediate_1, false, 0, 15728880);
+ vertexConsumerProvider$Immediate_1.finish();
+ RenderSystem.popMatrix();
+ }
+
+ @SubscribeEvent(receiveCanceled = true)
+ public static void handleInput(InputEvent.KeyInputEvent event) {
+ if (enableOverlay.isPressed())
+ enabled = !enabled;
+ if (increaseReach.isPressed()) {
+ if (reach < 64)
+ reach++;
+ try {
+ saveConfig(configFile);
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ Minecraft.getInstance().player.sendStatusMessage(new TranslationTextComponent("text.lightoverlay-forge.current_reach", reach), false);
+ }
+ if (decreaseReach.isPressed()) {
+ if (reach > 1)
+ reach--;
+ try {
+ saveConfig(configFile);
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ Minecraft.getInstance().player.sendStatusMessage(new TranslationTextComponent("text.lightoverlay-forge.current_reach", reach), false);
+ }
+ if (increaseLineWidth.isPressed()) {
+ if (lineWidth < 7)
+ lineWidth += 0.1f;
+ try {
+ saveConfig(configFile);
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ Minecraft.getInstance().player.sendStatusMessage(new TranslationTextComponent("text.lightoverlay-forge.current_line_width", FORMAT.format(lineWidth)), false);
+ }
+ if (decreaseLineWidth.isPressed()) {
+ if (lineWidth > 1)
+ lineWidth -= 0.1F;
+ try {
+ saveConfig(configFile);
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ Minecraft.getInstance().player.sendStatusMessage(new TranslationTextComponent("text.lightoverlay-forge.current_line_width", FORMAT.format(lineWidth)), false);
+ }
+ }
+
+ public static void queueChunkAndNear(ChunkPos pos) {
+ for (int xOffset = -1; xOffset <= 1; xOffset++) {
+ for (int zOffset = -1; zOffset <= 1; zOffset++) {
+ queueChunk(new ChunkPos(pos.x + xOffset, pos.z + zOffset));
+ }
+ }
+ }
+
+ public static void queueChunk(ChunkPos pos) {
+ if (!POS.contains(pos))
+ POS.add(0, pos);
+ }
+
+ public static int getChunkRange() {
+ return Math.max(MathHelper.ceil(reach / 16f), 1);
+ }
+
+ @SubscribeEvent
+ public static void tick(TickEvent.ClientTickEvent event) {
+ if (event.phase == TickEvent.Phase.END) {
+ try {
+ Minecraft minecraft = Minecraft.getInstance();
+ ticks++;
+ if (minecraft.player == null || !enabled) {
+ POS.clear();
+ CHUNK_MAP.clear();
+ } else {
+ ClientPlayerEntity player = minecraft.player;
+ ClientWorld world = minecraft.world;
+ ISelectionContext selectionContext = ISelectionContext.forEntity(player);
+ Vec3d[] playerPos = {null};
+ int playerPosX = ((int) player.getPosX()) >> 4;
+ int playerPosZ = ((int) player.getPosZ()) >> 4;
+ if (ticks % 20 == 0) {
+ for (int chunkX = playerPosX - getChunkRange(); chunkX <= playerPosX + getChunkRange(); chunkX++) {
+ for (int chunkZ = playerPosZ - getChunkRange(); chunkZ <= playerPosZ + getChunkRange(); chunkZ++) {
+ ChunkPos chunkPos = new ChunkPos(chunkX, chunkZ);
+ if (!CHUNK_MAP.containsKey(chunkPos))
+ queueChunk(chunkPos);
+ }
+ }
+ }
+ if (!POS.isEmpty()) {
+ if (playerPos[0] == null) {
+ playerPos[0] = player.getPositionVec();
+ }
+ ChunkPos pos = POS.stream().min(Comparator.comparingDouble(value -> value.getBlock(8, 0, 8).distanceSq(playerPos[0].x, 0, playerPos[0].z, false))).get();
+ EXECUTOR.submit(() -> {
+ if (MathHelper.abs(pos.x - playerPosX) <= getChunkRange() && MathHelper.abs(pos.z - playerPosZ) <= getChunkRange()) {
+ calculateChunk(world.getChunkProvider().getChunk(pos.x, pos.z, ChunkStatus.FULL, false), world, pos, selectionContext);
+ } else {
+ CHUNK_MAP.remove(pos);
+ }
+ });
+ POS.remove(pos);
+ }
+ Iterator<Map.Entry<ChunkPos, Map<Long, Object>>> chunkMapIterator = CHUNK_MAP.entrySet().iterator();
+ while (chunkMapIterator.hasNext()) {
+ Map.Entry<ChunkPos, Map<Long, Object>> pos = chunkMapIterator.next();
+ if (MathHelper.abs(pos.getKey().x - playerPosX) > getChunkRange() * 2 || MathHelper.abs(pos.getKey().z - playerPosZ) > getChunkRange() * 2) {
+ chunkMapIterator.remove();
+ }
+ }
+ }
+ } catch (Exception e) {
+ LogManager.getLogger().throwing(e);
+ }
+ }
+ }
+
+ private static void calculateChunk(Chunk chunk, World world, ChunkPos chunkPos, ISelectionContext selectionContext) {
+ Map<Long, Object> map = Maps.newHashMap();
+ if (chunk != null) {
+ IWorldLightListener block = chunk.getWorldLightManager().getLightEngine(LightType.BLOCK);
+ IWorldLightListener sky = showNumber ? null : chunk.getWorldLightManager().getLightEngine(LightType.SKY);
+ for (BlockPos pos : BlockPos.getAllInBoxMutable(chunkPos.getXStart(), 0, chunkPos.getZStart(), chunkPos.getXEnd(), 256, chunkPos.getZEnd())) {
+ BlockPos down = pos.down();
+ if (showNumber) {
+ int level = LightOverlayClient.getCrossLevel(pos, down, chunk, block, selectionContext);
+ if (level >= 0) {
+ map.put(pos.toLong(), level);
+ }
+ } else {
+ Biome biome = world.getBiomeManager().getBiome(pos);
+ if (biome.getSpawningChance() > 0 && !biome.getSpawns(EntityClassification.MONSTER).isEmpty()) {
+ CrossType type = LightOverlayClient.getCrossType(pos, down, chunk, block, sky, selectionContext);
+ if (type != CrossType.NONE) {
+ map.put(pos.toLong(), type);
+ }
+ }
+ }
+ }
+ }
+ CHUNK_MAP.put(chunkPos, map);
+ }
+
+ public static void renderWorldLast() {
+ if (LightOverlayClient.enabled) {
+ RenderSystem.pushMatrix();
+ Minecraft client = Minecraft.getInstance();
+ ClientPlayerEntity playerEntity = client.player;
+ int playerPosX = ((int) playerEntity.getPosX()) >> 4;
+ int playerPosZ = ((int) playerEntity.getPosZ()) >> 4;
+ ISelectionContext selectionContext = ISelectionContext.forEntity(playerEntity);
+ World world = client.world;
+ BlockPos playerPos = playerEntity.getPosition();
+ ActiveRenderInfo info = client.gameRenderer.getActiveRenderInfo();
+ if (showNumber) {
+ RenderSystem.enableTexture();
+ RenderSystem.depthMask(true);
+ BlockPos.Mutable mutable = new BlockPos.Mutable();
+ for (Map.Entry<ChunkPos, Map<Long, Object>> entry : CHUNK_MAP.entrySet()) {
+ if (MathHelper.abs(entry.getKey().x - playerPosX) > getChunkRange() || MathHelper.abs(entry.getKey().z - playerPosZ) > getChunkRange()) {
+ continue;
+ }
+ for (Map.Entry<Long, Object> objectEntry : entry.getValue().entrySet()) {
+ if (objectEntry.getValue() instanceof Integer) {
+ mutable.setPos(BlockPos.unpackX(objectEntry.getKey()), BlockPos.unpackY(objectEntry.getKey()), BlockPos.unpackZ(objectEntry.getKey()));
+ if (mutable.withinDistance(playerPos, reach)) {
+ BlockPos down = mutable.down();
+ LightOverlayClient.renderLevel(client, info, world, mutable, down, (Integer) objectEntry.getValue(), selectionContext);
+ }
+ }
+ }
+ }
+ RenderSystem.color4f(1.0F, 1.0F, 1.0F, 1.0F);
+ RenderSystem.enableDepthTest();
+ } else {
+ RenderSystem.enableDepthTest();
+ RenderSystem.shadeModel(7425);
+ RenderSystem.enableAlphaTest();
+ RenderSystem.defaultAlphaFunc();
+ RenderSystem.disableTexture();
+ RenderSystem.disableBlend();
+ if (smoothLines) GL11.glEnable(GL11.GL_LINE_SMOOTH);
+ RenderSystem.lineWidth(lineWidth);
+ Tessellator tessellator = Tessellator.getInstance();
+ BufferBuilder buffer = tessellator.getBuffer();
+ BlockPos.Mutable mutable = new BlockPos.Mutable();
+ for (Map.Entry<ChunkPos, Map<Long, Object>> entry : CHUNK_MAP.entrySet()) {
+ if (MathHelper.abs(entry.getKey().x - playerPosX) > getChunkRange() || MathHelper.abs(entry.getKey().z - playerPosZ) > getChunkRange()) {
+ continue;
+ }
+ for (Map.Entry<Long, Object> objectEntry : entry.getValue().entrySet()) {
+ if (objectEntry.getValue() instanceof CrossType) {
+ mutable.setPos(BlockPos.unpackX(objectEntry.getKey()), BlockPos.unpackY(objectEntry.getKey()), BlockPos.unpackZ(objectEntry.getKey()));
+ if (mutable.withinDistance(playerPos, reach)) {
+ BlockPos down = mutable.down();
+ int color = objectEntry.getValue() == CrossType.RED ? redColor : yellowColor;
+ LightOverlayClient.renderCross(info, tessellator, buffer, world, mutable, color, selectionContext);
+ }
+ }
+ }
+ }
+ RenderSystem.enableBlend();
+ RenderSystem.enableTexture();
+ RenderSystem.shadeModel(7424);
+ if (smoothLines) GL11.glDisable(GL11.GL_LINE_SMOOTH);
+ }
+ RenderSystem.popMatrix();
+ }
+ }
+
+ private static KeyBinding registerKeybind(ResourceLocation resourceLocation, InputMappings.Type type, int keyCode, String category) {
+ KeyBinding keyBinding = new KeyBinding("key." + resourceLocation.getNamespace() + "." + resourceLocation.getPath(), KeyConflictContext.IN_GAME, KeyModifier.NONE, type, keyCode, category);
+ ClientRegistry.registerKeyBinding(keyBinding);
+ return keyBinding;
+ }
+
+ static void loadConfig(File file) {
+ try {
+ redColor = 0xFF0000;
+ yellowColor = 0xFFFF00;
+ if (!file.exists() || !file.canRead())
+ saveConfig(file);
+ FileInputStream fis = new FileInputStream(file);
+ Properties properties = new Properties();
+ properties.load(fis);
+ fis.close();
+ reach = Integer.parseInt((String) properties.computeIfAbsent("reach", a -> "12"));
+ crossLevel = Integer.parseInt((String) properties.computeIfAbsent("crossLevel", a -> "7"));
+ showNumber = ((String) properties.computeIfAbsent("showNumber", a -> "false")).equalsIgnoreCase("true");
+ smoothLines = ((String) properties.computeIfAbsent("smoothLines", a -> "true")).equalsIgnoreCase("true");
+ underwater = ((String) properties.computeIfAbsent("underwater", a -> "false")).equalsIgnoreCase("true");
+ lineWidth = Float.parseFloat((String) properties.computeIfAbsent("lineWidth", a -> "1"));
+ {
+ int r, g, b;
+ r = Integer.parseInt((String) properties.computeIfAbsent("yellowColorRed", a -> "255"));
+ g = Integer.parseInt((String) properties.computeIfAbsent("yellowColorGreen", a -> "255"));
+ b = Integer.parseInt((String) properties.computeIfAbsent("yellowColorBlue", a -> "0"));
+ yellowColor = (r << 16) + (g << 8) + b;
+ }
+ {
+ int r, g, b;
+ r = Integer.parseInt((String) properties.computeIfAbsent("redColorRed", a -> "255"));
+ g = Integer.parseInt((String) properties.computeIfAbsent("redColorGreen", a -> "0"));
+ b = Integer.parseInt((String) properties.computeIfAbsent("redColorBlue", a -> "0"));
+ redColor = (r << 16) + (g << 8) + b;
+ }
+ saveConfig(file);
+ } catch (Exception e) {
+ e.printStackTrace();
+ reach = 12;
+ lineWidth = 1.0F;
+ redColor = 0xFF0000;
+ yellowColor = 0xFFFF00;
+ showNumber = false;
+ smoothLines = true;
+ underwater = false;
+ try {
+ saveConfig(file);
+ } catch (IOException ex) {
+ ex.printStackTrace();
+ }
+ }
+ }
+
+ static void saveConfig(File file) throws IOException {
+ FileOutputStream fos = new FileOutputStream(file, false);
+ fos.write("# Light Overlay Config".getBytes());
+ fos.write("\n".getBytes());
+ fos.write(("reach=" + reach).getBytes());
+ fos.write("\n".getBytes());
+ fos.write(("crossLevel=" + crossLevel).getBytes());
+ fos.write("\n".getBytes());
+ fos.write(("showNumber=" + showNumber).getBytes());
+ fos.write("\n".getBytes());
+ fos.write(("smoothLines=" + smoothLines).getBytes());
+ fos.write("\n".getBytes());
+ fos.write(("underwater=" + underwater).getBytes());
+ fos.write("\n".getBytes());
+ fos.write(("lineWidth=" + FORMAT.format(lineWidth)).getBytes());
+ fos.write("\n".getBytes());
+ fos.write(("yellowColorRed=" + ((yellowColor >> 16) & 255)).getBytes());
+ fos.write("\n".getBytes());
+ fos.write(("yellowColorGreen=" + ((yellowColor >> 8) & 255)).getBytes());
+ fos.write("\n".getBytes());
+ fos.write(("yellowColorBlue=" + (yellowColor & 255)).getBytes());
+ fos.write("\n".getBytes());
+ fos.write(("redColorRed=" + ((redColor >> 16) & 255)).getBytes());
+ fos.write("\n".getBytes());
+ fos.write(("redColorGreen=" + ((redColor >> 8) & 255)).getBytes());
+ fos.write("\n".getBytes());
+ fos.write(("redColorBlue=" + (redColor & 255)).getBytes());
+ fos.close();
+ }
+
+ public static void processPacket(IPacket<?> packet) {
+ if (packet instanceof SChangeBlockPacket) {
+ LightOverlayClient.queueChunkAndNear(new ChunkPos(((SChangeBlockPacket) packet).getPos()));
+ } else if (packet instanceof SChunkDataPacket) {
+ LightOverlayClient.queueChunkAndNear(new ChunkPos(((SChunkDataPacket) packet).getChunkX(), ((SChunkDataPacket) packet).getChunkZ()));
+ } else if (packet instanceof SMultiBlockChangePacket) {
+ ChunkPos chunkPos = ObfuscationReflectionHelper.getPrivateValue(SMultiBlockChangePacket.class, (SMultiBlockChangePacket) packet, "field_148925_b");
+ LightOverlayClient.queueChunkAndNear(new ChunkPos(chunkPos.x, chunkPos.z));
+ }
+ }
+
+ private enum CrossType {
+ YELLOW,
+ RED,
+ NONE
+ }
+}
--- /dev/null
+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();
+ });
+ }
+}
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+{
+ "NetworkManager": "META-INF/NetworkManager.js",
+ "WorldRenderer": "META-INF/WorldRenderer.js"
+}
\ No newline at end of file
--- /dev/null
+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"
--- /dev/null
+{
+ "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
--- /dev/null
+{
+ "pack": {
+ "description": "Light Overlay Forge",
+ "pack_format": 4
+ }
+}
-# 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
-#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
--- /dev/null
+FABRIC /assets/lightoverlay/lang/en_us.json
+FORGE /META-INF/MANIFEST.MF
+FABRIC /icon.png
\ No newline at end of file
--- /dev/null
+pluginManagement {
+ repositories {
+ jcenter()
+ maven {
+ name = 'Fabric'
+ url = 'https://maven.fabricmc.net/'
+ }
+ gradlePluginPortal()
+ }
+}
+
+rootProject.name="light-overlay"
+
+include("fabric")
+include("forge")
+++ /dev/null
-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));
- }
-
-}
+++ /dev/null
-package me.shedaniel.lightoverlay;
-
-import com.google.common.collect.Lists;
-import com.google.common.collect.Maps;
-import com.mojang.blaze3d.platform.GlStateManager;
-import com.mojang.blaze3d.systems.RenderSystem;
-import net.minecraft.block.Block;
-import net.minecraft.block.BlockState;
-import net.minecraft.client.Minecraft;
-import net.minecraft.client.entity.player.ClientPlayerEntity;
-import net.minecraft.client.gui.FontRenderer;
-import net.minecraft.client.renderer.*;
-import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
-import net.minecraft.client.settings.KeyBinding;
-import net.minecraft.client.util.InputMappings;
-import net.minecraft.client.world.ClientWorld;
-import net.minecraft.entity.Entity;
-import net.minecraft.entity.EntityClassification;
-import net.minecraft.entity.EntityType;
-import net.minecraft.network.IPacket;
-import net.minecraft.network.play.server.SChangeBlockPacket;
-import net.minecraft.network.play.server.SChunkDataPacket;
-import net.minecraft.network.play.server.SMultiBlockChangePacket;
-import net.minecraft.tags.BlockTags;
-import net.minecraft.util.Direction;
-import net.minecraft.util.ResourceLocation;
-import net.minecraft.util.math.BlockPos;
-import net.minecraft.util.math.ChunkPos;
-import net.minecraft.util.math.MathHelper;
-import net.minecraft.util.math.Vec3d;
-import net.minecraft.util.math.shapes.ISelectionContext;
-import net.minecraft.util.math.shapes.VoxelShape;
-import net.minecraft.util.text.TranslationTextComponent;
-import net.minecraft.world.IBlockReader;
-import net.minecraft.world.LightType;
-import net.minecraft.world.World;
-import net.minecraft.world.biome.Biome;
-import net.minecraft.world.chunk.Chunk;
-import net.minecraft.world.chunk.ChunkStatus;
-import net.minecraft.world.lighting.IWorldLightListener;
-import net.minecraftforge.api.distmarker.Dist;
-import net.minecraftforge.client.event.InputEvent;
-import net.minecraftforge.client.event.RenderWorldLastEvent;
-import net.minecraftforge.client.settings.KeyConflictContext;
-import net.minecraftforge.client.settings.KeyModifier;
-import net.minecraftforge.common.MinecraftForge;
-import net.minecraftforge.event.TickEvent;
-import net.minecraftforge.eventbus.api.SubscribeEvent;
-import net.minecraftforge.fml.DistExecutor;
-import net.minecraftforge.fml.client.registry.ClientRegistry;
-import net.minecraftforge.fml.common.ObfuscationReflectionHelper;
-import org.apache.logging.log4j.LogManager;
-import org.lwjgl.opengl.GL11;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.text.DecimalFormat;
-import java.util.*;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-
-public class LightOverlayClient {
-
- static final DecimalFormat FORMAT = new DecimalFormat("#.#");
- private static final String KEYBIND_CATEGORY = "key.lightoverlay-forge.category";
- private static final ResourceLocation ENABLE_OVERLAY_KEYBIND = new ResourceLocation("lightoverlay-forge", "enable_overlay");
- private static final ResourceLocation INCREASE_REACH_KEYBIND = new ResourceLocation("lightoverlay-forge", "increase_reach");
- private static final ResourceLocation DECREASE_REACH_KEYBIND = new ResourceLocation("lightoverlay-forge", "decrease_reach");
- private static final ResourceLocation INCREASE_LINE_WIDTH_KEYBIND = new ResourceLocation("lightoverlay-forge", "increase_line_width");
- private static final ResourceLocation DECREASE_LINE_WIDTH_KEYBIND = new ResourceLocation("lightoverlay-forge", "decrease_line_width");
- static int reach = 12;
- static int crossLevel = 7;
- static boolean showNumber = false;
- static boolean smoothLines = true;
- static boolean underwater = false;
- static EntityType<Entity> testingEntityType;
- static float lineWidth = 1.0F;
- static int yellowColor = 0xFFFF00, redColor = 0xFF0000;
- static File configFile = new File(new File(Minecraft.getInstance().gameDir, "config"), "lightoverlay.properties");
- private static KeyBinding enableOverlay, increaseReach, decreaseReach, increaseLineWidth, decreaseLineWidth;
- private static boolean enabled = false;
- private static int threadNumber = 0;
- private static final ExecutorService EXECUTOR = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors(), r -> {
- Thread thread = new Thread(r, "light-overlay-" + threadNumber++);
- thread.setDaemon(true);
- return thread;
- });
- private static final List<ChunkPos> POS = Lists.newCopyOnWriteArrayList();
- private static final Map<ChunkPos, Map<Long, Object>> CHUNK_MAP = Maps.newConcurrentMap();
- private static long ticks = 0;
-
- public static void register() {
- // Load Config
- loadConfig(configFile);
-
- // Setup
- testingEntityType = EntityType.Builder.create(EntityClassification.MONSTER).size(0f, 0f).disableSerialization().build(null);
- enableOverlay = registerKeybind(ENABLE_OVERLAY_KEYBIND, InputMappings.Type.KEYSYM, 296, KEYBIND_CATEGORY);
- increaseReach = registerKeybind(INCREASE_REACH_KEYBIND, InputMappings.Type.KEYSYM, -1, KEYBIND_CATEGORY);
- decreaseReach = registerKeybind(DECREASE_REACH_KEYBIND, InputMappings.Type.KEYSYM, -1, KEYBIND_CATEGORY);
- increaseLineWidth = registerKeybind(INCREASE_LINE_WIDTH_KEYBIND, InputMappings.Type.KEYSYM, -1, KEYBIND_CATEGORY);
- decreaseLineWidth = registerKeybind(DECREASE_LINE_WIDTH_KEYBIND, InputMappings.Type.KEYSYM, -1, KEYBIND_CATEGORY);
- MinecraftForge.EVENT_BUS.register(LightOverlayClient.class);
-
- try {
- //noinspection Convert2MethodRef
- DistExecutor.runWhenOn(Dist.CLIENT, () -> () -> LightOverlayCloth.register());
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
-
- public static CrossType getCrossType(BlockPos pos, BlockPos down, IBlockReader reader, IWorldLightListener block, IWorldLightListener sky, ISelectionContext selectionContext) {
- BlockState blockBelowState = reader.getBlockState(down);
- BlockState blockUpperState = reader.getBlockState(pos);
- VoxelShape upperCollisionShape = blockUpperState.getCollisionShape(reader, pos, selectionContext);
- if (!underwater && !blockUpperState.getFluidState().isEmpty())
- return CrossType.NONE;
- /* WorldEntitySpawner.func_222266_a */
- // Check if the outline is full
- if (Block.doesSideFillSquare(upperCollisionShape, Direction.UP))
- return CrossType.NONE;
- // Check if there is power
- if (blockUpperState.canProvidePower())
- return CrossType.NONE;
- // Check if the collision has a bump
- if (upperCollisionShape.getEnd(Direction.Axis.Y) > 0)
- return CrossType.NONE;
- if (blockUpperState.getBlock().isIn(BlockTags.RAILS))
- return CrossType.NONE;
- // Check block state allow spawning (excludes bedrock and barriers automatically)
- if (!blockBelowState.canEntitySpawn(reader, down, testingEntityType))
- return CrossType.NONE;
- if (block.getLightFor(pos) > crossLevel)
- return CrossType.NONE;
- if (sky.getLightFor(pos) > crossLevel)
- return CrossType.YELLOW;
- return CrossType.RED;
- }
-
- public static int getCrossLevel(BlockPos pos, BlockPos down, IBlockReader reader, IWorldLightListener light, ISelectionContext context) {
- BlockState blockBelowState = reader.getBlockState(down);
- BlockState blockUpperState = reader.getBlockState(pos);
- VoxelShape collisionShape = blockBelowState.getCollisionShape(reader, down, context);
- VoxelShape upperCollisionShape = blockUpperState.getCollisionShape(reader, pos, context);
- if (!underwater && !blockUpperState.getFluidState().isEmpty())
- return -1;
- if (!blockBelowState.getFluidState().isEmpty())
- return -1;
- if (blockBelowState.isAir(reader, down))
- return -1;
- if (Block.doesSideFillSquare(upperCollisionShape, Direction.DOWN))
- return -1;
- return light.getLightFor(pos);
- }
-
- public static void renderCross(ActiveRenderInfo info, Tessellator tessellator, BufferBuilder buffer, World world, BlockPos pos, int color, ISelectionContext context) {
- double d0 = info.getProjectedView().x;
- double d1 = info.getProjectedView().y - .005D;
- VoxelShape upperOutlineShape = world.getBlockState(pos).getShape(world, pos, context);
- if (!upperOutlineShape.isEmpty())
- d1 -= upperOutlineShape.getEnd(Direction.Axis.Y);
- double d2 = info.getProjectedView().z;
- buffer.begin(1, DefaultVertexFormats.POSITION_COLOR);
- int red = (color >> 16) & 255;
- int green = (color >> 8) & 255;
- int blue = color & 255;
- buffer.pos(pos.getX() + .01 - d0, pos.getY() - d1, pos.getZ() + .01 - d2).color(red, green, blue, 255).endVertex();
- buffer.pos(pos.getX() - .01 + 1 - d0, pos.getY() - d1, pos.getZ() - .01 + 1 - d2).color(red, green, blue, 255).endVertex();
- buffer.pos(pos.getX() - .01 + 1 - d0, pos.getY() - d1, pos.getZ() + .01 - d2).color(red, green, blue, 255).endVertex();
- buffer.pos(pos.getX() + .01 - d0, pos.getY() - d1, pos.getZ() - .01 + 1 - d2).color(red, green, blue, 255).endVertex();
- tessellator.draw();
- }
-
- public static void renderLevel(Minecraft minecraft, ActiveRenderInfo info, World world, BlockPos pos, BlockPos down, int level, ISelectionContext context) {
- String string_1 = String.valueOf(level);
- FontRenderer fontRenderer = minecraft.fontRenderer;
- double double_4 = info.getProjectedView().x;
- double double_5 = info.getProjectedView().y;
- VoxelShape upperOutlineShape = world.getBlockState(down).getShape(world, down, context);
- if (!upperOutlineShape.isEmpty())
- double_5 += 1 - upperOutlineShape.getEnd(Direction.Axis.Y);
- double double_6 = info.getProjectedView().z;
- RenderSystem.pushMatrix();
- RenderSystem.translatef((float) (pos.getX() + 0.5f - double_4), (float) (pos.getY() - double_5) + 0.005f, (float) (pos.getZ() + 0.5f - double_6));
- RenderSystem.rotatef(90, 1, 0, 0);
- RenderSystem.normal3f(0.0F, 1.0F, 0.0F);
- float size = 0.07F;
- RenderSystem.scalef(-size, -size, size);
- float float_3 = (float) (-fontRenderer.getStringWidth(string_1)) / 2.0F + 0.4f;
- RenderSystem.enableAlphaTest();
- IRenderTypeBuffer.Impl vertexConsumerProvider$Immediate_1 = IRenderTypeBuffer.getImpl(Tessellator.getInstance().getBuffer());
- fontRenderer.renderString(string_1, float_3, -3.5f, level > crossLevel ? 0xff042404 : 0xff731111, false, TransformationMatrix.identity().getMatrix(), vertexConsumerProvider$Immediate_1, false, 0, 15728880);
- vertexConsumerProvider$Immediate_1.finish();
- RenderSystem.popMatrix();
- }
-
- @SubscribeEvent(receiveCanceled = true)
- public static void handleInput(InputEvent.KeyInputEvent event) {
- if (enableOverlay.isPressed())
- enabled = !enabled;
- if (increaseReach.isPressed()) {
- if (reach < 64)
- reach++;
- try {
- saveConfig(configFile);
- } catch (IOException e) {
- e.printStackTrace();
- }
- Minecraft.getInstance().player.sendStatusMessage(new TranslationTextComponent("text.lightoverlay-forge.current_reach", reach), false);
- }
- if (decreaseReach.isPressed()) {
- if (reach > 1)
- reach--;
- try {
- saveConfig(configFile);
- } catch (IOException e) {
- e.printStackTrace();
- }
- Minecraft.getInstance().player.sendStatusMessage(new TranslationTextComponent("text.lightoverlay-forge.current_reach", reach), false);
- }
- if (increaseLineWidth.isPressed()) {
- if (lineWidth < 7)
- lineWidth += 0.1f;
- try {
- saveConfig(configFile);
- } catch (IOException e) {
- e.printStackTrace();
- }
- Minecraft.getInstance().player.sendStatusMessage(new TranslationTextComponent("text.lightoverlay-forge.current_line_width", FORMAT.format(lineWidth)), false);
- }
- if (decreaseLineWidth.isPressed()) {
- if (lineWidth > 1)
- lineWidth -= 0.1F;
- try {
- saveConfig(configFile);
- } catch (IOException e) {
- e.printStackTrace();
- }
- Minecraft.getInstance().player.sendStatusMessage(new TranslationTextComponent("text.lightoverlay-forge.current_line_width", FORMAT.format(lineWidth)), false);
- }
- }
-
- public static void queueChunkAndNear(ChunkPos pos) {
- for (int xOffset = -1; xOffset <= 1; xOffset++) {
- for (int zOffset = -1; zOffset <= 1; zOffset++) {
- queueChunk(new ChunkPos(pos.x + xOffset, pos.z + zOffset));
- }
- }
- }
-
- public static void queueChunk(ChunkPos pos) {
- if (!POS.contains(pos))
- POS.add(0, pos);
- }
-
- public static int getChunkRange() {
- return Math.max(MathHelper.ceil(reach / 16f), 1);
- }
-
- @SubscribeEvent
- public static void tick(TickEvent.ClientTickEvent event) {
- if (event.phase == TickEvent.Phase.END) {
- try {
- Minecraft minecraft = Minecraft.getInstance();
- ticks++;
- if (minecraft.player == null || !enabled) {
- POS.clear();
- CHUNK_MAP.clear();
- } else {
- ClientPlayerEntity player = minecraft.player;
- ClientWorld world = minecraft.world;
- ISelectionContext selectionContext = ISelectionContext.forEntity(player);
- Vec3d[] playerPos = {null};
- int playerPosX = ((int) player.getPosX()) >> 4;
- int playerPosZ = ((int) player.getPosZ()) >> 4;
- if (ticks % 20 == 0) {
- for (int chunkX = playerPosX - getChunkRange(); chunkX <= playerPosX + getChunkRange(); chunkX++) {
- for (int chunkZ = playerPosZ - getChunkRange(); chunkZ <= playerPosZ + getChunkRange(); chunkZ++) {
- ChunkPos chunkPos = new ChunkPos(chunkX, chunkZ);
- if (!CHUNK_MAP.containsKey(chunkPos))
- queueChunk(chunkPos);
- }
- }
- }
- if (!POS.isEmpty()) {
- if (playerPos[0] == null) {
- playerPos[0] = player.getPositionVec();
- }
- ChunkPos pos = POS.stream().min(Comparator.comparingDouble(value -> value.getBlock(8, 0, 8).distanceSq(playerPos[0].x, 0, playerPos[0].z, false))).get();
- EXECUTOR.submit(() -> {
- if (MathHelper.abs(pos.x - playerPosX) <= getChunkRange() && MathHelper.abs(pos.z - playerPosZ) <= getChunkRange()) {
- calculateChunk(world.getChunkProvider().getChunk(pos.x, pos.z, ChunkStatus.FULL, false), world, pos, selectionContext);
- } else {
- CHUNK_MAP.remove(pos);
- }
- });
- POS.remove(pos);
- }
- Iterator<Map.Entry<ChunkPos, Map<Long, Object>>> chunkMapIterator = CHUNK_MAP.entrySet().iterator();
- while (chunkMapIterator.hasNext()) {
- Map.Entry<ChunkPos, Map<Long, Object>> pos = chunkMapIterator.next();
- if (MathHelper.abs(pos.getKey().x - playerPosX) > getChunkRange() * 2 || MathHelper.abs(pos.getKey().z - playerPosZ) > getChunkRange() * 2) {
- chunkMapIterator.remove();
- }
- }
- }
- } catch (Exception e) {
- LogManager.getLogger().throwing(e);
- }
- }
- }
-
- private static void calculateChunk(Chunk chunk, World world, ChunkPos chunkPos, ISelectionContext selectionContext) {
- Map<Long, Object> map = Maps.newHashMap();
- if (chunk != null) {
- IWorldLightListener block = chunk.getWorldLightManager().getLightEngine(LightType.BLOCK);
- IWorldLightListener sky = showNumber ? null : chunk.getWorldLightManager().getLightEngine(LightType.SKY);
- for (BlockPos pos : BlockPos.getAllInBoxMutable(chunkPos.getXStart(), 0, chunkPos.getZStart(), chunkPos.getXEnd(), 256, chunkPos.getZEnd())) {
- BlockPos down = pos.down();
- if (showNumber) {
- int level = LightOverlayClient.getCrossLevel(pos, down, chunk, block, selectionContext);
- if (level >= 0) {
- map.put(pos.toLong(), level);
- }
- } else {
- Biome biome = world.getBiomeManager().getBiome(pos);
- if (biome.getSpawningChance() > 0 && !biome.getSpawns(EntityClassification.MONSTER).isEmpty()) {
- CrossType type = LightOverlayClient.getCrossType(pos, down, chunk, block, sky, selectionContext);
- if (type != CrossType.NONE) {
- map.put(pos.toLong(), type);
- }
- }
- }
- }
- }
- CHUNK_MAP.put(chunkPos, map);
- }
-
- public static void renderWorldLast() {
- if (LightOverlayClient.enabled) {
- RenderSystem.pushMatrix();
- Minecraft client = Minecraft.getInstance();
- ClientPlayerEntity playerEntity = client.player;
- int playerPosX = ((int) playerEntity.getPosX()) >> 4;
- int playerPosZ = ((int) playerEntity.getPosZ()) >> 4;
- ISelectionContext selectionContext = ISelectionContext.forEntity(playerEntity);
- World world = client.world;
- BlockPos playerPos = playerEntity.getPosition();
- ActiveRenderInfo info = client.gameRenderer.getActiveRenderInfo();
- if (showNumber) {
- RenderSystem.enableTexture();
- RenderSystem.depthMask(true);
- BlockPos.Mutable mutable = new BlockPos.Mutable();
- for (Map.Entry<ChunkPos, Map<Long, Object>> entry : CHUNK_MAP.entrySet()) {
- if (MathHelper.abs(entry.getKey().x - playerPosX) > getChunkRange() || MathHelper.abs(entry.getKey().z - playerPosZ) > getChunkRange()) {
- continue;
- }
- for (Map.Entry<Long, Object> objectEntry : entry.getValue().entrySet()) {
- if (objectEntry.getValue() instanceof Integer) {
- mutable.setPos(BlockPos.unpackX(objectEntry.getKey()), BlockPos.unpackY(objectEntry.getKey()), BlockPos.unpackZ(objectEntry.getKey()));
- if (mutable.withinDistance(playerPos, reach)) {
- BlockPos down = mutable.down();
- LightOverlayClient.renderLevel(client, info, world, mutable, down, (Integer) objectEntry.getValue(), selectionContext);
- }
- }
- }
- }
- RenderSystem.color4f(1.0F, 1.0F, 1.0F, 1.0F);
- RenderSystem.enableDepthTest();
- } else {
- RenderSystem.enableDepthTest();
- RenderSystem.shadeModel(7425);
- RenderSystem.enableAlphaTest();
- RenderSystem.defaultAlphaFunc();
- RenderSystem.disableTexture();
- RenderSystem.disableBlend();
- if (smoothLines) GL11.glEnable(GL11.GL_LINE_SMOOTH);
- RenderSystem.lineWidth(lineWidth);
- Tessellator tessellator = Tessellator.getInstance();
- BufferBuilder buffer = tessellator.getBuffer();
- BlockPos.Mutable mutable = new BlockPos.Mutable();
- for (Map.Entry<ChunkPos, Map<Long, Object>> entry : CHUNK_MAP.entrySet()) {
- if (MathHelper.abs(entry.getKey().x - playerPosX) > getChunkRange() || MathHelper.abs(entry.getKey().z - playerPosZ) > getChunkRange()) {
- continue;
- }
- for (Map.Entry<Long, Object> objectEntry : entry.getValue().entrySet()) {
- if (objectEntry.getValue() instanceof CrossType) {
- mutable.setPos(BlockPos.unpackX(objectEntry.getKey()), BlockPos.unpackY(objectEntry.getKey()), BlockPos.unpackZ(objectEntry.getKey()));
- if (mutable.withinDistance(playerPos, reach)) {
- BlockPos down = mutable.down();
- int color = objectEntry.getValue() == CrossType.RED ? redColor : yellowColor;
- LightOverlayClient.renderCross(info, tessellator, buffer, world, mutable, color, selectionContext);
- }
- }
- }
- }
- RenderSystem.enableBlend();
- RenderSystem.enableTexture();
- RenderSystem.shadeModel(7424);
- if (smoothLines) GL11.glDisable(GL11.GL_LINE_SMOOTH);
- }
- RenderSystem.popMatrix();
- }
- }
-
- private static KeyBinding registerKeybind(ResourceLocation resourceLocation, InputMappings.Type type, int keyCode, String category) {
- KeyBinding keyBinding = new KeyBinding("key." + resourceLocation.getNamespace() + "." + resourceLocation.getPath(), KeyConflictContext.IN_GAME, KeyModifier.NONE, type, keyCode, category);
- ClientRegistry.registerKeyBinding(keyBinding);
- return keyBinding;
- }
-
- static void loadConfig(File file) {
- try {
- redColor = 0xFF0000;
- yellowColor = 0xFFFF00;
- if (!file.exists() || !file.canRead())
- saveConfig(file);
- FileInputStream fis = new FileInputStream(file);
- Properties properties = new Properties();
- properties.load(fis);
- fis.close();
- reach = Integer.parseInt((String) properties.computeIfAbsent("reach", a -> "12"));
- crossLevel = Integer.parseInt((String) properties.computeIfAbsent("crossLevel", a -> "7"));
- showNumber = ((String) properties.computeIfAbsent("showNumber", a -> "false")).equalsIgnoreCase("true");
- smoothLines = ((String) properties.computeIfAbsent("smoothLines", a -> "true")).equalsIgnoreCase("true");
- underwater = ((String) properties.computeIfAbsent("underwater", a -> "false")).equalsIgnoreCase("true");
- lineWidth = Float.parseFloat((String) properties.computeIfAbsent("lineWidth", a -> "1"));
- {
- int r, g, b;
- r = Integer.parseInt((String) properties.computeIfAbsent("yellowColorRed", a -> "255"));
- g = Integer.parseInt((String) properties.computeIfAbsent("yellowColorGreen", a -> "255"));
- b = Integer.parseInt((String) properties.computeIfAbsent("yellowColorBlue", a -> "0"));
- yellowColor = (r << 16) + (g << 8) + b;
- }
- {
- int r, g, b;
- r = Integer.parseInt((String) properties.computeIfAbsent("redColorRed", a -> "255"));
- g = Integer.parseInt((String) properties.computeIfAbsent("redColorGreen", a -> "0"));
- b = Integer.parseInt((String) properties.computeIfAbsent("redColorBlue", a -> "0"));
- redColor = (r << 16) + (g << 8) + b;
- }
- saveConfig(file);
- } catch (Exception e) {
- e.printStackTrace();
- reach = 12;
- lineWidth = 1.0F;
- redColor = 0xFF0000;
- yellowColor = 0xFFFF00;
- showNumber = false;
- smoothLines = true;
- underwater = false;
- try {
- saveConfig(file);
- } catch (IOException ex) {
- ex.printStackTrace();
- }
- }
- }
-
- static void saveConfig(File file) throws IOException {
- FileOutputStream fos = new FileOutputStream(file, false);
- fos.write("# Light Overlay Config".getBytes());
- fos.write("\n".getBytes());
- fos.write(("reach=" + reach).getBytes());
- fos.write("\n".getBytes());
- fos.write(("crossLevel=" + crossLevel).getBytes());
- fos.write("\n".getBytes());
- fos.write(("showNumber=" + showNumber).getBytes());
- fos.write("\n".getBytes());
- fos.write(("smoothLines=" + smoothLines).getBytes());
- fos.write("\n".getBytes());
- fos.write(("underwater=" + underwater).getBytes());
- fos.write("\n".getBytes());
- fos.write(("lineWidth=" + FORMAT.format(lineWidth)).getBytes());
- fos.write("\n".getBytes());
- fos.write(("yellowColorRed=" + ((yellowColor >> 16) & 255)).getBytes());
- fos.write("\n".getBytes());
- fos.write(("yellowColorGreen=" + ((yellowColor >> 8) & 255)).getBytes());
- fos.write("\n".getBytes());
- fos.write(("yellowColorBlue=" + (yellowColor & 255)).getBytes());
- fos.write("\n".getBytes());
- fos.write(("redColorRed=" + ((redColor >> 16) & 255)).getBytes());
- fos.write("\n".getBytes());
- fos.write(("redColorGreen=" + ((redColor >> 8) & 255)).getBytes());
- fos.write("\n".getBytes());
- fos.write(("redColorBlue=" + (redColor & 255)).getBytes());
- fos.close();
- }
-
- public static void processPacket(IPacket<?> packet) {
- if (packet instanceof SChangeBlockPacket) {
- LightOverlayClient.queueChunkAndNear(new ChunkPos(((SChangeBlockPacket) packet).getPos()));
- } else if (packet instanceof SChunkDataPacket) {
- LightOverlayClient.queueChunkAndNear(new ChunkPos(((SChunkDataPacket) packet).getChunkX(), ((SChunkDataPacket) packet).getChunkZ()));
- } else if (packet instanceof SMultiBlockChangePacket) {
- ChunkPos chunkPos = ObfuscationReflectionHelper.getPrivateValue(SMultiBlockChangePacket.class, (SMultiBlockChangePacket) packet, "field_148925_b");
- LightOverlayClient.queueChunkAndNear(new ChunkPos(chunkPos.x, chunkPos.z));
- }
- }
-
- private enum CrossType {
- YELLOW,
- RED,
- NONE
- }
-
-}
+++ /dev/null
-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();
- });
- }
-}
+++ /dev/null
-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
+++ /dev/null
-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
+++ /dev/null
-{
- "NetworkManager": "META-INF/NetworkManager.js",
- "WorldRenderer": "META-INF/WorldRenderer.js"
-}
\ No newline at end of file
+++ /dev/null
-# This is an example mods.toml file. It contains the data relating to the loading mods.
-# There are several mandatory fields (#mandatory), and many more that are optional (#optional).
-# The overall format is standard TOML format, v0.5.0.
-# Note that there are a couple of TOML lists in this file.
-# Find more information on toml format here: https://github.com/toml-lang/toml
-# The name of the mod loader type to load - for regular FML @Mod mods it should be javafml
-modLoader="javafml" #mandatory
-# A version range to match for said mod loader - for regular FML @Mod it will be the forge version
-loaderVersion="[29,)" #mandatory (24 is current forge version)
-# A URL to refer people to when problems occur with this mod
-issueTrackerURL="https://github.com/shedaniel/LightOverlay-Forge/issues/" #optional
-# A URL for the "homepage" for this mod, displayed in the mod UI
-displayURL="http://shedaniel.me/" #optional
-# A file name (in the root of the mod JAR) containing a logo for display
-logoFile="lightoverlay_icon_lowres.png" #optional
-# A text field displayed in the mod UI
-credits="" #optional
-# A text field displayed in the mod UI
-authors="Danielshe" #optional
-# A list of mods - how many allowed here is determined by the individual mod loader
-[[mods]] #mandatory
-# The modid of the mod
- modId="lightoverlay-forge" #mandatory
-# The version number of the mod - there's a few well known ${} variables useable here or just hardcode it
- version="${file.jarVersion}" #mandatory
- # A display name for the mod
- displayName="Light Overlay Forge" #mandatory
-# A URL to query for updates for this mod. See the JSON update specification <here>
-# updateJSONURL="http://myurl.me/" #optional
-# The description text for the mod (multi line!) (#mandatory)
- description='''
-To provide users with NEI-like light overlay.
-'''
-
-[[dependencies.lightoverlay-forge]] #optional
- # the modid of the dependency
- modId="cloth-config2" #mandatory
- # Does this dependency have to exist - if not, ordering below must be specified
- mandatory=true #mandatory
- # The version range of the dependency
- versionRange="[3.0,)" #mandatory
- ordering="NONE"
- # Side this dependency is applied on - BOTH, CLIENT or SERVER
- side="CLIENT"
+++ /dev/null
-{
- "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
+++ /dev/null
-{
- "pack": {
- "description": "Light Overlay Forge",
- "pack_format": 4
- }
-}