From 32288c278963de69cc628aaaa2f10fbde4f3c99c Mon Sep 17 00:00:00 2001 From: shedaniel Date: Fri, 2 Oct 2020 18:06:41 +0800 Subject: [PATCH] Multi-loader common module build Signed-off-by: shedaniel --- .gitignore | 1 + architectPlugin/build.gradle | 35 ++ architectPlugin/settings.gradle | 6 + .../plugin/architect/ArchitectPlugin.kt | 28 + .../architect/ArchitectPluginExtension.kt | 7 + .../plugin/architect/RemapMCPTask.kt | 147 +++++ build.gradle | 8 +- common/build.gradle | 26 + .../lightoverlay/common/AbstractPlatform.java | 18 + .../lightoverlay/common/LightOverlayCore.java | 489 ++++++++++++++++ .../assets/lightoverlay/lang/en_us.json | 0 common/src/main/resources/fabric.mod.json | 7 + .../src/main/resources/icon.png | Bin fabric/build.gradle | 31 +- .../lightoverlay/fabric/FrustumHelper.java | 20 +- .../lightoverlay/fabric/LOModMenuEntry.java | 50 +- .../lightoverlay/fabric/LightOverlay.java | 500 +--------------- .../fabric/mixin/MixinClientConnection.java | 38 +- .../fabric/mixin/MixinWorldRenderer.java | 16 +- .../main/resources/lightoverlay.accesswidener | 10 +- forge/build.gradle | 34 +- .../lightoverlay/forge/FrustumHelper.java | 16 +- .../forge/LightOverlayClient.java | 542 ++---------------- .../lightoverlay/forge/LightOverlayCloth.java | 34 +- .../assets/lightoverlay/lang/en_us.json | 18 - forge/src/main/resources/icon.png | Bin 22942 -> 0 bytes gradle.properties | 5 +- settings.gradle | 4 + 28 files changed, 985 insertions(+), 1105 deletions(-) create mode 100644 architectPlugin/build.gradle create mode 100644 architectPlugin/settings.gradle create mode 100644 architectPlugin/src/main/kotlin/me/shedaniel/plugin/architect/ArchitectPlugin.kt create mode 100644 architectPlugin/src/main/kotlin/me/shedaniel/plugin/architect/ArchitectPluginExtension.kt create mode 100644 architectPlugin/src/main/kotlin/me/shedaniel/plugin/architect/RemapMCPTask.kt create mode 100644 common/build.gradle create mode 100644 common/src/main/java/me/shedaniel/lightoverlay/common/AbstractPlatform.java create mode 100644 common/src/main/java/me/shedaniel/lightoverlay/common/LightOverlayCore.java rename {fabric => common}/src/main/resources/assets/lightoverlay/lang/en_us.json (100%) create mode 100755 common/src/main/resources/fabric.mod.json rename {fabric => common}/src/main/resources/icon.png (100%) delete mode 100755 forge/src/main/resources/assets/lightoverlay/lang/en_us.json delete mode 100644 forge/src/main/resources/icon.png diff --git a/.gitignore b/.gitignore index e787a1c..d74b152 100644 --- a/.gitignore +++ b/.gitignore @@ -12,6 +12,7 @@ */out /run /classes +logs/ # IDE nonsense that could go in source control but really shouldn't .classpath diff --git a/architectPlugin/build.gradle b/architectPlugin/build.gradle new file mode 100644 index 0000000..7db7ebe --- /dev/null +++ b/architectPlugin/build.gradle @@ -0,0 +1,35 @@ +plugins { + id "org.jetbrains.kotlin.jvm" version "1.3.72" +} + +repositories { + jcenter() + maven { url "https://maven.fabricmc.net/" } +} + +apply plugin: 'java-gradle-plugin' + +dependencies { + implementation gradleApi() + implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.3.72" + implementation "org.jetbrains.kotlin:kotlin-reflect:1.3.72" + implementation "net.fabricmc:fabric-loom:0.5-SNAPSHOT" + runtime "net.fabricmc:fabric-loom:0.5-SNAPSHOT" + implementation "net.fabricmc:tiny-remapper:0.3.0.70" + implementation "net.fabricmc:tiny-mappings-parser:0.2.2.14" +} + +compileKotlin { + kotlinOptions { + jvmTarget = "1.8" + } +} + +gradlePlugin { + plugins { + architect { + id = 'me.shedaniel.plugin.architect' + implementationClass = 'me.shedaniel.plugin.architect.ArchitectPlugin' + } + } +} \ No newline at end of file diff --git a/architectPlugin/settings.gradle b/architectPlugin/settings.gradle new file mode 100644 index 0000000..9c68a5a --- /dev/null +++ b/architectPlugin/settings.gradle @@ -0,0 +1,6 @@ +pluginManagement { + repositories { + jcenter() + gradlePluginPortal() + } +} diff --git a/architectPlugin/src/main/kotlin/me/shedaniel/plugin/architect/ArchitectPlugin.kt b/architectPlugin/src/main/kotlin/me/shedaniel/plugin/architect/ArchitectPlugin.kt new file mode 100644 index 0000000..21638f8 --- /dev/null +++ b/architectPlugin/src/main/kotlin/me/shedaniel/plugin/architect/ArchitectPlugin.kt @@ -0,0 +1,28 @@ +package me.shedaniel.plugin.architect + +import org.gradle.api.JavaVersion +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.api.plugins.JavaPluginExtension + +class ArchitectPlugin : Plugin { + override fun apply(project: Project) { + project.apply(mapOf( + "plugin" to "java", + "plugin" to "eclipse", + "plugin" to "idea" + )) + project.extensions.create("architect", ArchitectPluginExtension::class.java, project) + + project.afterEvaluate { + project.extensions.getByType(JavaPluginExtension::class.java).apply { + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 + } + } + + project.tasks.register("remapMcp", RemapMCPTask::class.java) { + + } + } +} \ No newline at end of file diff --git a/architectPlugin/src/main/kotlin/me/shedaniel/plugin/architect/ArchitectPluginExtension.kt b/architectPlugin/src/main/kotlin/me/shedaniel/plugin/architect/ArchitectPluginExtension.kt new file mode 100644 index 0000000..260bdfa --- /dev/null +++ b/architectPlugin/src/main/kotlin/me/shedaniel/plugin/architect/ArchitectPluginExtension.kt @@ -0,0 +1,7 @@ +package me.shedaniel.plugin.architect + +import org.gradle.api.Project + +open class ArchitectPluginExtension(val project: Project) { + var minecraft = "" +} \ No newline at end of file diff --git a/architectPlugin/src/main/kotlin/me/shedaniel/plugin/architect/RemapMCPTask.kt b/architectPlugin/src/main/kotlin/me/shedaniel/plugin/architect/RemapMCPTask.kt new file mode 100644 index 0000000..e6a62b0 --- /dev/null +++ b/architectPlugin/src/main/kotlin/me/shedaniel/plugin/architect/RemapMCPTask.kt @@ -0,0 +1,147 @@ +@file:Suppress("UnstableApiUsage") + +package me.shedaniel.plugin.architect + +import net.fabricmc.loom.LoomGradleExtension +import net.fabricmc.loom.util.GradleSupport +import net.fabricmc.loom.util.TinyRemapperMappingsHelper +import net.fabricmc.mapping.tree.TinyTree +import net.fabricmc.tinyremapper.IMappingProvider +import net.fabricmc.tinyremapper.NonClassCopyMode +import net.fabricmc.tinyremapper.OutputConsumerPath +import net.fabricmc.tinyremapper.TinyRemapper +import org.gradle.api.file.RegularFileProperty +import org.gradle.api.tasks.TaskAction +import org.gradle.jvm.tasks.Jar +import java.io.FileNotFoundException +import java.io.InputStream +import java.net.URL +import java.nio.file.Files +import java.nio.file.Path + + +open class RemapMCPTask : Jar() { + private val fromM: String = "named" + private val toM: String = "official" + val input: RegularFileProperty = GradleSupport.getfileProperty(project) + + @TaskAction + fun doTask() { + val input: Path = this.input.asFile.get().toPath() + val output: Path = this.archiveFile.get().asFile.toPath() + + output.toFile().delete() + + if (!Files.exists(input)) { + throw FileNotFoundException(input.toString()) + } + + val remapperBuilder: TinyRemapper.Builder = TinyRemapper.newRemapper() + val mappings = getMappings() + val mojmapToMcpClass = createMojmapToMcpClass(mappings) + remapperBuilder.withMappings(remapToMcp(TinyRemapperMappingsHelper.create(mappings, fromM, toM, false), mojmapToMcpClass)) + + project.logger.lifecycle(":remapping " + input.fileName) + + val architectFolder = project.rootProject.buildDir.resolve("tmp/architect") + architectFolder.deleteRecursively() + architectFolder.mkdirs() + val manifestFile = architectFolder.resolve("META-INF/MANIFEST.MF") + manifestFile.parentFile.mkdirs() + manifestFile.writeText(""" +Manifest-Version: 1.0 +FMLModType: LIBRARY + """.trimIndent()) + + val remapper = remapperBuilder.build() + + try { + OutputConsumerPath.Builder(output).build().use { outputConsumer -> + outputConsumer.addNonClassFiles(input, NonClassCopyMode.SKIP_META_INF, null) + outputConsumer.addNonClassFiles(architectFolder.toPath(), NonClassCopyMode.UNCHANGED, null) + remapper.readInputs(input) + remapper.apply(outputConsumer) + } + } catch (e: Exception) { + remapper.finish() + throw RuntimeException("Failed to remap $input to $output", e) + } + + architectFolder.deleteRecursively() + remapper.finish() + + if (!Files.exists(output)) { + throw RuntimeException("Failed to remap $input to $output - file missing!") + } + } + + private fun remapToMcp(parent: IMappingProvider, mojmapToMcpClass: Map): IMappingProvider = IMappingProvider { + it.acceptClass("net/fabricmc/api/Environment","net/minecraftforge/api/distmarker/OnlyIn") + it.acceptClass("net/fabricmc/api/EnvType","net/minecraftforge/api/distmarker/Dist") + it.acceptField(IMappingProvider.Member("net/fabricmc/api/EnvType", "SERVER", "Lnet/fabricmc/api/EnvType;"),"DEDICATED_SERVER") + + parent.load(object : IMappingProvider.MappingAcceptor { + override fun acceptClass(srcName: String?, dstName: String?) { + it.acceptClass(srcName, mojmapToMcpClass[srcName] ?: srcName) + } + + override fun acceptMethod(method: IMappingProvider.Member?, dstName: String?) { + it.acceptMethod(method, dstName) + } + + override fun acceptMethodArg(method: IMappingProvider.Member?, lvIndex: Int, dstName: String?) { + it.acceptMethodArg(method, lvIndex, dstName) + } + + override fun acceptMethodVar(method: IMappingProvider.Member?, lvIndex: Int, startOpIdx: Int, asmIndex: Int, dstName: String?) { + it.acceptMethodVar(method, lvIndex, startOpIdx, asmIndex, dstName) + } + + override fun acceptField(field: IMappingProvider.Member?, dstName: String?) { + it.acceptField(field, dstName) + } + }) + } + + private fun getMappings(): TinyTree { + val loomExtension = project.extensions.getByType(LoomGradleExtension::class.java) + return loomExtension.mappingsProvider.mappings + } + + private fun getRootExtension(): ArchitectPluginExtension = + project.rootProject.extensions.getByType(ArchitectPluginExtension::class.java) + + private fun createMojmapToMcpClass(mappings: TinyTree): Map { + val mcpMappings = readMCPMappings(getRootExtension().minecraft) + val mutableMap = mutableMapOf() + mappings.classes.forEach { clazz -> + val official = clazz.getName("official") + val named = clazz.getName("named") + val mcp = mcpMappings[official] + if (mcp != null) { + mutableMap[named] = mcp + } + } + return mutableMap + } + + private fun readMCPMappings(version: String): Map { + val file = project.rootProject.file(".gradle/mappings/mcp-$version.tsrg") + if (file.exists().not()) { + file.parentFile.mkdirs() + file.writeText(URL("https://raw.githubusercontent.com/MinecraftForge/MCPConfig/master/versions/release/$version/joined.tsrg").readText()) + } + return mutableMapOf().also { readMappings(it, file.inputStream()) } + } + + private fun readMappings(mutableMap: MutableMap, inputStream: InputStream) { + inputStream.bufferedReader().forEachLine { + if (!it.startsWith("\t")) { + val split = it.split(" ") + val obf = split[0] + val className = split[1] + mutableMap[obf] = className + } + } + } +} \ No newline at end of file diff --git a/build.gradle b/build.gradle index a87cca4..228b6bd 100644 --- a/build.gradle +++ b/build.gradle @@ -20,17 +20,21 @@ import java.util.zip.ZipInputStream import java.util.zip.ZipOutputStream plugins { + id "me.shedaniel.plugin.architect" id "org.jetbrains.gradle.plugin.idea-ext" version "0.7" } +architect { + minecraft = minecraft_version +} + allprojects { apply plugin: "java" + apply plugin: "me.shedaniel.plugin.architect" group "me.shedaniel" archivesBaseName = rootProject.name version = rootProject.mod_version - - sourceCompatibility = targetCompatibility = compileJava.sourceCompatibility = compileJava.targetCompatibility = JavaVersion.VERSION_1_8 } idea.project.settings { diff --git a/common/build.gradle b/common/build.gradle new file mode 100644 index 0000000..79e9531 --- /dev/null +++ b/common/build.gradle @@ -0,0 +1,26 @@ +plugins { + id "fabric-loom" +} + +dependencies { + minecraft "com.mojang:minecraft:${rootProject.architect.minecraft}" + mappings minecraft.officialMojangMappings() + modCompile "net.fabricmc:fabric-loader:${rootProject.fabric_loader_version}" +} + +configurations { + mcp +} + +remapMcp { + input.set(file("${project.buildDir}/libs/$archivesBaseName-${project.version}-dev.jar")) + classifier("mcp") +} + +remapJar.dependsOn(jar) + +artifacts { + mcp file: file("${project.buildDir}/libs/$archivesBaseName-${version}-mcp.jar"), type: "jar", builtBy: remapMcp +} + +build.dependsOn(remapMcp) \ No newline at end of file diff --git a/common/src/main/java/me/shedaniel/lightoverlay/common/AbstractPlatform.java b/common/src/main/java/me/shedaniel/lightoverlay/common/AbstractPlatform.java new file mode 100644 index 0000000..a237060 --- /dev/null +++ b/common/src/main/java/me/shedaniel/lightoverlay/common/AbstractPlatform.java @@ -0,0 +1,18 @@ +package me.shedaniel.lightoverlay.common; + +import net.minecraft.client.KeyMapping; +import net.minecraft.client.renderer.culling.Frustum; + +import java.nio.file.Path; + +public interface AbstractPlatform { + Path getConfigDir(); + + void registerClientTick(Runnable runnable); + + void registerDebugRenderer(Runnable runnable); + + KeyMapping registerKeyBinding(KeyMapping keyMapping); + + boolean isFrustumVisible(Frustum frustum, double minX, double minY, double minZ, double maxX, double maxY, double maxZ); +} diff --git a/common/src/main/java/me/shedaniel/lightoverlay/common/LightOverlayCore.java b/common/src/main/java/me/shedaniel/lightoverlay/common/LightOverlayCore.java new file mode 100644 index 0000000..2c40c2d --- /dev/null +++ b/common/src/main/java/me/shedaniel/lightoverlay/common/LightOverlayCore.java @@ -0,0 +1,489 @@ +package me.shedaniel.lightoverlay.common; + +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.mojang.blaze3d.platform.GlStateManager; +import com.mojang.blaze3d.platform.InputConstants; +import com.mojang.blaze3d.systems.RenderSystem; +import com.mojang.blaze3d.vertex.Tesselator; +import com.mojang.math.Transformation; +import it.unimi.dsi.fastutil.longs.Long2ReferenceMap; +import it.unimi.dsi.fastutil.longs.Long2ReferenceOpenHashMap; +import net.minecraft.client.Camera; +import net.minecraft.client.KeyMapping; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.Font; +import net.minecraft.client.multiplayer.ClientLevel; +import net.minecraft.client.player.LocalPlayer; +import net.minecraft.client.renderer.MultiBufferSource; +import net.minecraft.client.renderer.culling.Frustum; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.tags.BlockTags; +import net.minecraft.util.LazyLoadedValue; +import net.minecraft.util.Mth; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.entity.MobCategory; +import net.minecraft.world.level.BlockGetter; +import net.minecraft.world.level.ChunkPos; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.LightLayer; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.chunk.ChunkStatus; +import net.minecraft.world.level.chunk.LevelChunk; +import net.minecraft.world.level.lighting.LayerLightEventListener; +import net.minecraft.world.phys.shapes.CollisionContext; +import net.minecraft.world.phys.shapes.VoxelShape; +import org.apache.logging.log4j.LogManager; +import org.lwjgl.opengl.GL11; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.text.DecimalFormat; +import java.util.Comparator; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.concurrent.Executors; +import java.util.concurrent.ThreadPoolExecutor; + +public class LightOverlayCore { + public static final DecimalFormat FORMAT = new DecimalFormat("#.#"); + private static final String KEYBIND_CATEGORY = "key.lightoverlay.category"; + private static final ResourceLocation ENABLE_OVERLAY_KEYBIND = new ResourceLocation("lightoverlay", "enable_overlay"); + public static int reach = 12; + public static int crossLevel = 7; + public static int secondaryLevel = -1; + public static int lowerCrossLevel = -1; + public static int higherCrossLevel = -1; + public static boolean caching = false; + public static boolean showNumber = false; + public static boolean smoothLines = true; + public static boolean underwater = false; + public static float lineWidth = 1.0F; + public static int yellowColor = 0xFFFF00, redColor = 0xFF0000, secondaryColor = 0x0000FF; + public static File configFile; + private static KeyMapping enableOverlay; + private static boolean enabled = false; + private static final LazyLoadedValue> TESTING_ENTITY_TYPE = new LazyLoadedValue<>(() -> + EntityType.Builder.createNothing(MobCategory.MONSTER).sized(0f, 0f).noSave().build(null)); + private static int threadNumber = 0; + public static Frustum frustum; + private static final ThreadPoolExecutor EXECUTOR = (ThreadPoolExecutor) Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors(), r -> { + Thread thread = new Thread(r, "light-overlay-" + threadNumber++); + thread.setDaemon(true); + return thread; + }); + private static final List POS = Lists.newCopyOnWriteArrayList(); + private static final Map> CHUNK_MAP = Maps.newConcurrentMap(); + private static final Minecraft CLIENT = Minecraft.getInstance(); + private static long ticks = 0; + + public static void register(AbstractPlatform platform) { + // Load Config + configFile = new File(platform.getConfigDir().toFile(), "lightoverlay.properties"); + loadConfig(configFile); + + enableOverlay = platform.registerKeyBinding(createKeyBinding(ENABLE_OVERLAY_KEYBIND, InputConstants.Type.KEYSYM, 296, KEYBIND_CATEGORY)); + + platform.registerDebugRenderer(() -> { + if (enabled) { + LocalPlayer playerEntity = CLIENT.player; + int playerPosX = ((int) playerEntity.getX()) >> 4; + int playerPosZ = ((int) playerEntity.getZ()) >> 4; + CollisionContext collisionContext = CollisionContext.of(playerEntity); + Level world = CLIENT.level; + BlockPos playerPos = new BlockPos(playerEntity.getX(), playerEntity.getY(), playerEntity.getZ()); + Camera camera = CLIENT.gameRenderer.getMainCamera(); + + if (showNumber) { + RenderSystem.enableTexture(); + RenderSystem.depthMask(true); + BlockPos.MutableBlockPos mutable = new BlockPos.MutableBlockPos(); + BlockPos.MutableBlockPos downMutable = new BlockPos.MutableBlockPos(); + for (Map.Entry> entry : CHUNK_MAP.entrySet()) { + if (caching && (Mth.abs(entry.getKey().x - playerPosX) > getChunkRange() || Mth.abs(entry.getKey().z - playerPosZ) > getChunkRange())) { + continue; + } + for (Long2ReferenceMap.Entry objectEntry : entry.getValue().long2ReferenceEntrySet()) { + if (objectEntry.getValue() instanceof Byte) { + mutable.set(BlockPos.getX(objectEntry.getLongKey()), BlockPos.getY(objectEntry.getLongKey()), BlockPos.getZ(objectEntry.getLongKey())); + if (mutable.closerThan(playerPos, reach)) { + if (frustum == null || platform.isFrustumVisible(frustum, mutable.getX(), mutable.getY(), mutable.getZ(), mutable.getX() + 1, mutable.getX() + 1, mutable.getX() + 1)) { + downMutable.set(mutable.getX(), mutable.getY() - 1, mutable.getZ()); + renderLevel(CLIENT, camera, world, mutable, downMutable, (Byte) objectEntry.getValue(), collisionContext); + } + } + } + } + } + RenderSystem.enableDepthTest(); + } else { + RenderSystem.enableDepthTest(); + RenderSystem.disableTexture(); + RenderSystem.enableBlend(); + RenderSystem.blendFunc(GlStateManager.SourceFactor.SRC_ALPHA, GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA); + if (smoothLines) GL11.glEnable(GL11.GL_LINE_SMOOTH); + GL11.glLineWidth(lineWidth); + GL11.glBegin(GL11.GL_LINES); + BlockPos.MutableBlockPos mutable = new BlockPos.MutableBlockPos(); + for (Map.Entry> entry : CHUNK_MAP.entrySet()) { + if (caching && (Mth.abs(entry.getKey().x - playerPosX) > getChunkRange() || Mth.abs(entry.getKey().z - playerPosZ) > getChunkRange())) { + continue; + } + for (Long2ReferenceMap.Entry objectEntry : entry.getValue().long2ReferenceEntrySet()) { + if (objectEntry.getValue() instanceof CrossType) { + mutable.set(BlockPos.getX(objectEntry.getLongKey()), BlockPos.getY(objectEntry.getLongKey()), BlockPos.getZ(objectEntry.getLongKey())); + if (mutable.closerThan(playerPos, reach)) { + if (frustum == null || platform.isFrustumVisible(frustum, mutable.getX(), mutable.getY(), mutable.getZ(), mutable.getX() + 1, mutable.getX() + 1, mutable.getX() + 1)) { + int color = objectEntry.getValue() == CrossType.RED ? redColor : objectEntry.getValue() == CrossType.YELLOW ? yellowColor : secondaryColor; + renderCross(camera, world, mutable, color, collisionContext); + } + } + } + } + } + GL11.glEnd(); + RenderSystem.disableBlend(); + RenderSystem.enableTexture(); + if (smoothLines) GL11.glDisable(GL11.GL_LINE_SMOOTH); + } + } + }); + + platform.registerClientTick(() -> { + while (enableOverlay.consumeClick()) + enabled = !enabled; + + try { + ticks++; + if (CLIENT.player == null || !enabled) { + POS.clear(); + CHUNK_MAP.clear(); + } else { + LocalPlayer player = CLIENT.player; + ClientLevel world = CLIENT.level; + CollisionContext collisionContext = CollisionContext.of(player); + + if (!caching) { + POS.clear(); + CHUNK_MAP.clear(); + BlockPos playerPos = player.blockPosition(); + LayerLightEventListener block = world.getLightEngine().getLayerListener(LightLayer.BLOCK); + LayerLightEventListener sky = showNumber ? null : world.getLightEngine().getLayerListener(LightLayer.SKY); + BlockPos.MutableBlockPos downPos = new BlockPos.MutableBlockPos(); + Iterable iterate = BlockPos.betweenClosed(playerPos.getX() - reach, playerPos.getY() - reach, playerPos.getZ() - reach, + playerPos.getX() + reach, playerPos.getY() + reach, playerPos.getZ() + reach); + Long2ReferenceMap map = new Long2ReferenceOpenHashMap<>(); + CHUNK_MAP.put(new ChunkPos(0, 0), map); + for (BlockPos blockPos : iterate) { + downPos.set(blockPos.getX(), blockPos.getY() - 1, blockPos.getZ()); + if (showNumber) { + int level = getCrossLevel(blockPos, downPos, world, block, collisionContext); + if (level >= 0) { + map.put(blockPos.asLong(), Byte.valueOf((byte) level)); + } + } else { + CrossType type = getCrossType(blockPos, downPos, world, block, sky, collisionContext); + if (type != CrossType.NONE) { + map.put(blockPos.asLong(), type); + } + } + } + } else { + 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); + } + } + } + POS.removeIf(pos -> Mth.abs(pos.x - playerPosX) > getChunkRange() || Mth.abs(pos.z - playerPosZ) > getChunkRange()); + for (int k = 0; k < 2; k++) { + if (!POS.isEmpty()) { + ChunkPos pos = POS.stream().min(Comparator.comparingDouble(value -> { + int i = Math.abs(value.x - playerPosX); + int j = Math.abs(value.z - playerPosZ); + return i * i + j * j; + })).get(); + POS.remove(pos); + EXECUTOR.submit(() -> { + try { + calculateChunk(world.getChunkSource().getChunk(pos.x, pos.z, ChunkStatus.FULL, false), world, pos, collisionContext); + } catch (Throwable throwable) { + LogManager.getLogger().throwing(throwable); + } + }); + } + } + if (ticks % 50 == 0) { + CHUNK_MAP.entrySet().removeIf(pos -> Mth.abs(pos.getKey().x - playerPosX) > getChunkRange() * 2 || Mth.abs(pos.getKey().z - playerPosZ) > getChunkRange() * 2); + } + } + } + } catch (Throwable throwable) { + LogManager.getLogger().throwing(throwable); + } + }); + } + + 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 (caching) + if (!POS.contains(pos)) + POS.add(0, pos); + } + + public static int getChunkRange() { + return Math.max(Mth.ceil(reach / 16f), 1); + } + + private static void calculateChunk(LevelChunk chunk, Level world, ChunkPos chunkPos, CollisionContext entityContext) { + if (world != null && chunk != null) { + Long2ReferenceMap map = new Long2ReferenceOpenHashMap<>(); + LayerLightEventListener block = world.getLightEngine().getLayerListener(LightLayer.BLOCK); + LayerLightEventListener sky = showNumber ? null : world.getLightEngine().getLayerListener(LightLayer.SKY); + for (BlockPos pos : BlockPos.betweenClosed(chunkPos.getMinBlockX(), 0, chunkPos.getMinBlockZ(), chunkPos.getMaxBlockX(), 256, chunkPos.getMaxBlockZ())) { + BlockPos down = pos.below(); + if (showNumber) { + int level = getCrossLevel(pos, down, chunk, block, entityContext); + if (level >= 0) { + map.put(pos.asLong(), Byte.valueOf((byte) level)); + } + } else { + CrossType type = getCrossType(pos, down, chunk, block, sky, entityContext); + if (type != CrossType.NONE) { + map.put(pos.asLong(), type); + } + } + } + CHUNK_MAP.put(chunkPos, map); + } else { + CHUNK_MAP.remove(chunkPos); + } + } + + public static CrossType getCrossType(BlockPos pos, BlockPos down, BlockGetter world, LayerLightEventListener block, LayerLightEventListener sky, CollisionContext entityContext) { + BlockState blockBelowState = world.getBlockState(down); + BlockState blockUpperState = world.getBlockState(pos); + VoxelShape upperCollisionShape = blockUpperState.getCollisionShape(world, pos, entityContext); + if (!underwater && !blockUpperState.getFluidState().isEmpty()) + return CrossType.NONE; + // Check if the outline is full + if (Block.isFaceFull(upperCollisionShape, Direction.UP)) + return CrossType.NONE; + // TODO: Not to hard code no redstone + if (blockUpperState.isSignalSource()) + return CrossType.NONE; + // Check if the collision has a bump + if (upperCollisionShape.max(Direction.Axis.Y) > 0) + return CrossType.NONE; + if (blockUpperState.getBlock().is(BlockTags.RAILS)) + return CrossType.NONE; + // Check block state allow spawning (excludes bedrock and barriers automatically) + if (!blockBelowState.isValidSpawn(world, down, TESTING_ENTITY_TYPE.get())) + return CrossType.NONE; + int blockLightLevel = block.getLightValue(pos); + int skyLightLevel = sky.getLightValue(pos); + if (blockLightLevel > higherCrossLevel) + return CrossType.NONE; + if (skyLightLevel > higherCrossLevel) + return CrossType.YELLOW; + return lowerCrossLevel >= 0 && blockLightLevel > lowerCrossLevel ? CrossType.SECONDARY : CrossType.RED; + } + + public static int getCrossLevel(BlockPos pos, BlockPos down, BlockGetter world, LayerLightEventListener view, CollisionContext collisionContext) { + BlockState blockBelowState = world.getBlockState(down); + BlockState blockUpperState = world.getBlockState(pos); + VoxelShape collisionShape = blockBelowState.getCollisionShape(world, down, collisionContext); + VoxelShape upperCollisionShape = blockUpperState.getCollisionShape(world, pos, collisionContext); + if (!underwater && !blockUpperState.getFluidState().isEmpty()) + return -1; + if (!blockBelowState.getFluidState().isEmpty()) + return -1; + if (blockBelowState.isAir()) + return -1; + if (Block.isFaceFull(upperCollisionShape, Direction.DOWN)) + return -1; + return view.getLightValue(pos); + } + + public static void renderCross(Camera camera, Level world, BlockPos pos, int color, CollisionContext collisionContext) { + double d0 = camera.getPosition().x; + double d1 = camera.getPosition().y - .005D; + VoxelShape upperOutlineShape = world.getBlockState(pos).getShape(world, pos, collisionContext); + if (!upperOutlineShape.isEmpty()) + d1 -= upperOutlineShape.max(Direction.Axis.Y); + double d2 = camera.getPosition().z; + + int red = (color >> 16) & 255; + int green = (color >> 8) & 255; + int blue = color & 255; + int x = pos.getX(); + int y = pos.getY(); + int z = pos.getZ(); + RenderSystem.color4f(red / 255f, green / 255f, blue / 255f, 1f); + GL11.glVertex3d(x + .01 - d0, y - d1, z + .01 - d2); + GL11.glVertex3d(x - .01 + 1 - d0, y - d1, z - .01 + 1 - d2); + GL11.glVertex3d(x - .01 + 1 - d0, y - d1, z + .01 - d2); + GL11.glVertex3d(x + .01 - d0, y - d1, z - .01 + 1 - d2); + } + + @SuppressWarnings("deprecation") + public static void renderLevel(Minecraft client, Camera camera, Level world, BlockPos pos, BlockPos down, int level, CollisionContext collisionContext) { + String text = String.valueOf(level); + Font textRenderer_1 = client.font; + double double_4 = camera.getPosition().x; + double double_5 = camera.getPosition().y; + VoxelShape upperOutlineShape = world.getBlockState(down).getShape(world, down, collisionContext); + if (!upperOutlineShape.isEmpty()) + double_5 += 1 - upperOutlineShape.max(Direction.Axis.Y); + double double_6 = camera.getPosition().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.width(text)) / 2.0F + 0.4f; + RenderSystem.enableAlphaTest(); + MultiBufferSource.BufferSource immediate = MultiBufferSource.immediate(Tesselator.getInstance().getBuilder()); + textRenderer_1.drawInBatch(text, float_3, -3.5f, level > higherCrossLevel ? 0xff042404 : (lowerCrossLevel >= 0 && level > lowerCrossLevel ? 0xff0066ff : 0xff731111), false, Transformation.identity().getMatrix(), immediate, false, 0, 15728880); + immediate.endBatch(); + RenderSystem.popMatrix(); + } + + public static void loadConfig(File file) { + try { + redColor = 0xFF0000; + yellowColor = 0xFFFF00; + secondaryColor = 0x0000FF; + 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")); + secondaryLevel = Integer.parseInt((String) properties.computeIfAbsent("secondaryLevel", a -> "-1")); + caching = ((String) properties.computeIfAbsent("caching", a -> "false")).equalsIgnoreCase("true"); + showNumber = ((String) properties.computeIfAbsent("showNumber", a -> "false")).equalsIgnoreCase("true"); + smoothLines = ((String) properties.computeIfAbsent("smoothLines", a -> "true")).equalsIgnoreCase("true"); + underwater = ((String) properties.computeIfAbsent("underwater", a -> "false")).equalsIgnoreCase("true"); + 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; + } + { + int r, g, b; + r = Integer.parseInt((String) properties.computeIfAbsent("secondaryColorRed", a -> "0")); + g = Integer.parseInt((String) properties.computeIfAbsent("secondaryColorGreen", a -> "0")); + b = Integer.parseInt((String) properties.computeIfAbsent("secondaryColorBlue", a -> "255")); + secondaryColor = (r << 16) + (g << 8) + b; + } + saveConfig(file); + } catch (Exception e) { + e.printStackTrace(); + reach = 12; + crossLevel = 7; + secondaryLevel = -1; + lineWidth = 1.0F; + redColor = 0xFF0000; + yellowColor = 0xFFFF00; + secondaryColor = 0x0000FF; + caching = false; + showNumber = false; + smoothLines = true; + underwater = false; + try { + saveConfig(file); + } catch (IOException ex) { + ex.printStackTrace(); + } + } + if (secondaryLevel >= crossLevel) System.err.println("[Light Overlay] Secondary Level is higher than Cross Level"); + lowerCrossLevel = Math.min(crossLevel, secondaryLevel); + higherCrossLevel = Math.max(crossLevel, secondaryLevel); + CHUNK_MAP.clear(); + POS.clear(); + } + + public 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(("secondaryLevel=" + secondaryLevel).getBytes()); + fos.write("\n".getBytes()); + fos.write(("caching=" + caching).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.write("\n".getBytes()); + fos.write(("secondaryColorRed=" + ((secondaryColor >> 16) & 255)).getBytes()); + fos.write("\n".getBytes()); + fos.write(("secondaryColorGreen=" + ((secondaryColor >> 8) & 255)).getBytes()); + fos.write("\n".getBytes()); + fos.write(("secondaryColorBlue=" + (secondaryColor & 255)).getBytes()); + fos.close(); + } + + private static KeyMapping createKeyBinding(ResourceLocation id, InputConstants.Type type, int code, String category) { + return new KeyMapping("key." + id.getNamespace() + "." + id.getPath(), type, code, category); + } + + private enum CrossType { + YELLOW, + RED, + SECONDARY, + NONE + } +} diff --git a/fabric/src/main/resources/assets/lightoverlay/lang/en_us.json b/common/src/main/resources/assets/lightoverlay/lang/en_us.json similarity index 100% rename from fabric/src/main/resources/assets/lightoverlay/lang/en_us.json rename to common/src/main/resources/assets/lightoverlay/lang/en_us.json diff --git a/common/src/main/resources/fabric.mod.json b/common/src/main/resources/fabric.mod.json new file mode 100755 index 0000000..4fed948 --- /dev/null +++ b/common/src/main/resources/fabric.mod.json @@ -0,0 +1,7 @@ +{ + "_comment": "This file is here to make fabric load this on Knot classloader.", + "schemaVersion": 1, + "id": "lightoverlay-core", + "name": "Light Overlay Core", + "version": "0.0.1" +} diff --git a/fabric/src/main/resources/icon.png b/common/src/main/resources/icon.png similarity index 100% rename from fabric/src/main/resources/icon.png rename to common/src/main/resources/icon.png diff --git a/fabric/build.gradle b/fabric/build.gradle index 6cd73c2..9b8058d 100755 --- a/fabric/build.gradle +++ b/fabric/build.gradle @@ -1,5 +1,6 @@ plugins { - id("fabric-loom") version "0.4-SNAPSHOT" + id "fabric-loom" + id "com.github.johnrengelman.shadow" version "5.0.0" } minecraft { @@ -13,14 +14,13 @@ processResources { inputs.property "version", project.version } -repositories { - jcenter() - maven { url "https://dl.bintray.com/shedaniel/legacy-yarn-updated" } +configurations { + shadow } dependencies { - minecraft "com.mojang:minecraft:${rootProject.fabric_minecraft_version}" - mappings "me.shedaniel:legacy-yarn:${rootProject.yarn_build}:v2" + minecraft "com.mojang:minecraft:${rootProject.architect.minecraft}" + mappings minecraft.officialMojangMappings() modCompile "net.fabricmc:fabric-loader:${rootProject.fabric_loader_version}" modCompile "net.fabricmc.fabric-api:fabric-api:${rootProject.fabric_api_version}" @@ -39,4 +39,23 @@ dependencies { include("me.shedaniel.cloth:config-2:${rootProject.cloth_config_version}") { transitive = false } + + compile(project(":common")) { + transitive = false + } + shadow(project(":common")) { + transitive = false + } } + +shadowJar { + relocate "me.shedaniel.lightoverlay.common", "me.shedaniel.lightoverlay.common.fabric" + + configurations = [project.configurations.shadow] + classifier "shadow" +} + +remapJar { + dependsOn(shadowJar) + input.set(shadowJar.archivePath) +} \ No newline at end of file diff --git a/fabric/src/main/java/me/shedaniel/lightoverlay/fabric/FrustumHelper.java b/fabric/src/main/java/me/shedaniel/lightoverlay/fabric/FrustumHelper.java index 278ff84..ee36fdc 100644 --- a/fabric/src/main/java/me/shedaniel/lightoverlay/fabric/FrustumHelper.java +++ b/fabric/src/main/java/me/shedaniel/lightoverlay/fabric/FrustumHelper.java @@ -1,21 +1,21 @@ package me.shedaniel.lightoverlay.fabric; -import net.minecraft.client.render.Frustum; -import net.minecraft.client.util.math.Vector4f; +import com.mojang.math.Vector4f; +import net.minecraft.client.renderer.culling.Frustum; public class FrustumHelper { public static boolean isVisible(Frustum frustum, double minX, double minY, double minZ, double maxX, double maxY, double maxZ) { - float x1 = (float) (minX - frustum.x); - float y1 = (float) (minY - frustum.y); - float z1 = (float) (minZ - frustum.z); - float x2 = (float) (maxX - frustum.x); - float y2 = (float) (maxY - frustum.y); - float z2 = (float) (maxZ - frustum.z); + float x1 = (float) (minX - frustum.camX); + float y1 = (float) (minY - frustum.camY); + float z1 = (float) (minZ - frustum.camZ); + float x2 = (float) (maxX - frustum.camX); + float y2 = (float) (maxY - frustum.camY); + float z2 = (float) (maxZ - frustum.camZ); return isAnyCornerVisible(frustum, x1, y1, z1, x2, y2, z2); } private static boolean isAnyCornerVisible(Frustum frustum, float x1, float y1, float z1, float x2, float y2, float z2) { - Vector4f[] homogeneousCoordinates = frustum.homogeneousCoordinates; + Vector4f[] homogeneousCoordinates = frustum.frustumData; for (Vector4f vector4f : homogeneousCoordinates) { if (dotProduct(vector4f, x1, y1, z1, 1.0F) <= 0.0F && dotProduct(vector4f, x2, y1, z1, 1.0F) <= 0.0F && dotProduct(vector4f, x1, y2, z1, 1.0F) <= 0.0F && dotProduct(vector4f, x2, y2, z1, 1.0F) <= 0.0F && dotProduct(vector4f, x1, y1, z2, 1.0F) <= 0.0F && dotProduct(vector4f, x2, y1, z2, 1.0F) <= 0.0F && dotProduct(vector4f, x1, y2, z2, 1.0F) <= 0.0F && dotProduct(vector4f, x2, y2, z2, 1.0F) <= 0.0F) { return false; @@ -26,6 +26,6 @@ public class FrustumHelper { } private static float dotProduct(Vector4f self, float x, float y, float z, float w) { - return self.getX() * x + self.getY() * y + self.getZ() * z + self.getW() * w; + return self.x() * x + self.y() * y + self.z() * z + self.w() * w; } } diff --git a/fabric/src/main/java/me/shedaniel/lightoverlay/fabric/LOModMenuEntry.java b/fabric/src/main/java/me/shedaniel/lightoverlay/fabric/LOModMenuEntry.java index c7f4c85..741e3a2 100644 --- a/fabric/src/main/java/me/shedaniel/lightoverlay/fabric/LOModMenuEntry.java +++ b/fabric/src/main/java/me/shedaniel/lightoverlay/fabric/LOModMenuEntry.java @@ -6,12 +6,13 @@ import me.shedaniel.clothconfig2.api.ConfigBuilder; import me.shedaniel.clothconfig2.api.ConfigCategory; import me.shedaniel.clothconfig2.api.ConfigEntryBuilder; import me.shedaniel.clothconfig2.gui.entries.IntegerSliderEntry; -import net.minecraft.client.gui.screen.Screen; -import net.minecraft.client.resource.language.I18n; -import net.minecraft.text.LiteralText; -import net.minecraft.text.Text; -import net.minecraft.text.TranslatableText; -import net.minecraft.util.math.MathHelper; +import me.shedaniel.lightoverlay.common.LightOverlayCore; +import net.minecraft.client.gui.screens.Screen; +import net.minecraft.client.resources.language.I18n; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.TextComponent; +import net.minecraft.network.chat.TranslatableComponent; +import net.minecraft.util.Mth; import java.util.Optional; @@ -22,35 +23,36 @@ public class LOModMenuEntry implements ModMenuApi { } public Screen getConfigScreenByCloth(Screen parent) { - ConfigBuilder builder = ConfigBuilder.create().setParentScreen(parent).setTitle(new TranslatableText("key.lightoverlay.category")); + ConfigBuilder builder = ConfigBuilder.create().setParentScreen(parent).setTitle(new TranslatableComponent("key.lightoverlay.category")); ConfigEntryBuilder eb = builder.entryBuilder(); - ConfigCategory general = builder.getOrCreateCategory(new TranslatableText("config.lightoverlay.general")); - general.addEntry(eb.startTextDescription(Text.of("§7" + I18n.translate("description.lightoverlay.caching"))).build()); - general.addEntry(eb.startBooleanToggle(new TranslatableText("config.lightoverlay.caching"), LightOverlay.caching).setDefaultValue(false).setSaveConsumer(bool -> LightOverlay.caching = bool).build()); - general.addEntry(eb.startIntSlider(new TranslatableText("config.lightoverlay.reach"), LightOverlay.reach, 1, 64).setDefaultValue(12).setTextGetter(integer -> Text.of("Reach: " + integer + " Blocks")).setSaveConsumer(integer -> LightOverlay.reach = integer).build()); - IntegerSliderEntry crossLevel = eb.startIntSlider(new TranslatableText("config.lightoverlay.crossLevel"), LightOverlay.crossLevel, 0, 15).setDefaultValue(7).setTextGetter(integer -> Text.of("Cross Level: " + integer)).setSaveConsumer(integer -> LightOverlay.crossLevel = integer).build(); + ConfigCategory general = builder.getOrCreateCategory(new TranslatableComponent("config.lightoverlay.general")); + general.addEntry(eb.startTextDescription(Component.nullToEmpty("§7" + I18n.get("description.lightoverlay.caching"))).build()); + general.addEntry(eb.startBooleanToggle(new TranslatableComponent("config.lightoverlay.caching"), LightOverlayCore.caching).setDefaultValue(false).setSaveConsumer(bool -> LightOverlayCore.caching = bool).build()); + general.addEntry(eb.startIntSlider(new TranslatableComponent("config.lightoverlay.reach"), LightOverlayCore.reach, 1, 64).setDefaultValue(12).setTextGetter(integer -> Component.nullToEmpty("Reach: " + integer + " Blocks")).setSaveConsumer(integer -> LightOverlayCore.reach = integer).build()); + IntegerSliderEntry crossLevel = eb.startIntSlider(new TranslatableComponent("config.lightoverlay.crossLevel"), LightOverlayCore.crossLevel, 0, 15).setDefaultValue(7).setTextGetter(integer -> Component.nullToEmpty("Cross Level: " + integer)).setSaveConsumer(integer -> LightOverlayCore.crossLevel = integer).build(); general.addEntry(crossLevel); - general.addEntry(eb.startIntSlider(new TranslatableText("config.lightoverlay.secondaryLevel"), LightOverlay.secondaryLevel, -1, 15) + general.addEntry(eb.startIntSlider(new TranslatableComponent("config.lightoverlay.secondaryLevel"), LightOverlayCore.secondaryLevel, -1, 15) .setErrorSupplier(integer -> { - if (integer >= 0 && integer >= crossLevel.getValue()) return Optional.of(new LiteralText("Secondary Level cannot be higher than Cross Level!")); + if (integer >= 0 && integer >= crossLevel.getValue()) + return Optional.of(new TextComponent("Secondary Level cannot be higher than Cross Level!")); return Optional.empty(); - }).setDefaultValue(-1).setTextGetter(integer -> new LiteralText(integer < 0 ? "Off" : "Level: " + integer)).setSaveConsumer(integer -> LightOverlay.secondaryLevel = integer).build()); - general.addEntry(eb.startBooleanToggle(new TranslatableText("config.lightoverlay.showNumber"), LightOverlay.showNumber).setDefaultValue(false).setSaveConsumer(bool -> LightOverlay.showNumber = bool).build()); - general.addEntry(eb.startBooleanToggle(new TranslatableText("config.lightoverlay.smoothLines"), LightOverlay.smoothLines).setDefaultValue(true).setSaveConsumer(bool -> LightOverlay.smoothLines = bool).build()); - general.addEntry(eb.startBooleanToggle(new TranslatableText("config.lightoverlay.underwater"), LightOverlay.underwater).setDefaultValue(false).setSaveConsumer(bool -> LightOverlay.underwater = bool).build()); - general.addEntry(eb.startIntSlider(new TranslatableText("config.lightoverlay.lineWidth"), MathHelper.floor(LightOverlay.lineWidth * 100), 100, 700).setDefaultValue(100).setTextGetter(integer -> new LiteralText("Light Width: " + LightOverlay.FORMAT.format(integer / 100d))).setSaveConsumer(integer -> LightOverlay.lineWidth = integer / 100f).build()); - general.addEntry(eb.startColorField(new TranslatableText("config.lightoverlay.yellowColor"), LightOverlay.yellowColor).setDefaultValue(0xFFFF00).setSaveConsumer(color -> LightOverlay.yellowColor = color).build()); - general.addEntry(eb.startColorField(new TranslatableText("config.lightoverlay.redColor"), LightOverlay.redColor).setDefaultValue(0xFF0000).setSaveConsumer(color -> LightOverlay.redColor = color).build()); - general.addEntry(eb.startColorField(new TranslatableText("config.lightoverlay.secondaryColor"), LightOverlay.secondaryColor).setDefaultValue(0x0000FF).setSaveConsumer(color -> LightOverlay.secondaryColor = color).build()); + }).setDefaultValue(-1).setTextGetter(integer -> new TextComponent(integer < 0 ? "Off" : "Level: " + integer)).setSaveConsumer(integer -> LightOverlayCore.secondaryLevel = integer).build()); + general.addEntry(eb.startBooleanToggle(new TranslatableComponent("config.lightoverlay.showNumber"), LightOverlayCore.showNumber).setDefaultValue(false).setSaveConsumer(bool -> LightOverlayCore.showNumber = bool).build()); + general.addEntry(eb.startBooleanToggle(new TranslatableComponent("config.lightoverlay.smoothLines"), LightOverlayCore.smoothLines).setDefaultValue(true).setSaveConsumer(bool -> LightOverlayCore.smoothLines = bool).build()); + general.addEntry(eb.startBooleanToggle(new TranslatableComponent("config.lightoverlay.underwater"), LightOverlayCore.underwater).setDefaultValue(false).setSaveConsumer(bool -> LightOverlayCore.underwater = bool).build()); + general.addEntry(eb.startIntSlider(new TranslatableComponent("config.lightoverlay.lineWidth"), Mth.floor(LightOverlayCore.lineWidth * 100), 100, 700).setDefaultValue(100).setTextGetter(integer -> new TextComponent("Light Width: " + LightOverlayCore.FORMAT.format(integer / 100d))).setSaveConsumer(integer -> LightOverlayCore.lineWidth = integer / 100f).build()); + general.addEntry(eb.startColorField(new TranslatableComponent("config.lightoverlay.yellowColor"), LightOverlayCore.yellowColor).setDefaultValue(0xFFFF00).setSaveConsumer(color -> LightOverlayCore.yellowColor = color).build()); + general.addEntry(eb.startColorField(new TranslatableComponent("config.lightoverlay.redColor"), LightOverlayCore.redColor).setDefaultValue(0xFF0000).setSaveConsumer(color -> LightOverlayCore.redColor = color).build()); + general.addEntry(eb.startColorField(new TranslatableComponent("config.lightoverlay.secondaryColor"), LightOverlayCore.secondaryColor).setDefaultValue(0x0000FF).setSaveConsumer(color -> LightOverlayCore.secondaryColor = color).build()); return builder.setSavingRunnable(() -> { try { - LightOverlay.saveConfig(LightOverlay.configFile); + LightOverlayCore.saveConfig(LightOverlayCore.configFile); } catch (Exception e) { e.printStackTrace(); } - LightOverlay.loadConfig(LightOverlay.configFile); + LightOverlayCore.loadConfig(LightOverlayCore.configFile); }).build(); } } diff --git a/fabric/src/main/java/me/shedaniel/lightoverlay/fabric/LightOverlay.java b/fabric/src/main/java/me/shedaniel/lightoverlay/fabric/LightOverlay.java index 23303ba..da0f190 100644 --- a/fabric/src/main/java/me/shedaniel/lightoverlay/fabric/LightOverlay.java +++ b/fabric/src/main/java/me/shedaniel/lightoverlay/fabric/LightOverlay.java @@ -1,499 +1,45 @@ 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 it.unimi.dsi.fastutil.longs.Long2ReferenceMap; -import it.unimi.dsi.fastutil.longs.Long2ReferenceOpenHashMap; import me.shedaniel.cloth.api.client.events.v0.ClothClientHooks; +import me.shedaniel.lightoverlay.common.AbstractPlatform; +import me.shedaniel.lightoverlay.common.LightOverlayCore; import net.fabricmc.api.ClientModInitializer; import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents; import net.fabricmc.fabric.api.client.keybinding.v1.KeyBindingHelper; 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.options.KeyBinding; -import net.minecraft.client.render.Camera; -import net.minecraft.client.render.Frustum; -import net.minecraft.client.render.Tessellator; -import net.minecraft.client.render.VertexConsumerProvider; -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.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.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 net.minecraft.client.KeyMapping; +import net.minecraft.client.renderer.culling.Frustum; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.text.DecimalFormat; -import java.util.Comparator; -import java.util.List; -import java.util.Map; -import java.util.Properties; -import java.util.concurrent.Executors; -import java.util.concurrent.ThreadPoolExecutor; +import java.nio.file.Path; -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"); - static int reach = 12; - static int crossLevel = 7; - static int secondaryLevel = -1; - static int lowerCrossLevel = -1; - static int higherCrossLevel = -1; - static boolean caching = false; - static boolean showNumber = false; - static boolean smoothLines = true; - static boolean underwater = false; - static float lineWidth = 1.0F; - static int yellowColor = 0xFFFF00, redColor = 0xFF0000, secondaryColor = 0x0000FF; - static File configFile = new File(FabricLoader.getInstance().getConfigDir().toFile(), "lightoverlay.properties"); - private static final KeyBinding ENABLE_OVERLAY = createKeyBinding(ENABLE_OVERLAY_KEYBIND, InputUtil.Type.KEYSYM, 296, KEYBIND_CATEGORY); - private static boolean enabled = false; - private static EntityType testingEntityType; - private static int threadNumber = 0; - public static Frustum frustum; - private static final ThreadPoolExecutor EXECUTOR = (ThreadPoolExecutor) Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors(), r -> { - Thread thread = new Thread(r, "light-overlay-" + threadNumber++); - thread.setDaemon(true); - return thread; - }); - private static final List POS = Lists.newCopyOnWriteArrayList(); - private static final Map> CHUNK_MAP = Maps.newConcurrentMap(); - private static final MinecraftClient CLIENT = MinecraftClient.getInstance(); - private static long ticks = 0; - - static { - ClientTickEvents.END_CLIENT_TICK.register(client -> { - try { - ticks++; - if (CLIENT.player == null || !enabled) { - POS.clear(); - CHUNK_MAP.clear(); - } else { - if (!caching) { - POS.clear(); - CHUNK_MAP.clear(); - ClientPlayerEntity player = CLIENT.player; - ClientWorld world = CLIENT.world; - BlockPos playerPos = player.getSenseCenterPos(); - EntityContext entityContext = EntityContext.of(player); - ChunkLightingView block = world.getLightingProvider().get(LightType.BLOCK); - ChunkLightingView sky = showNumber ? null : world.getLightingProvider().get(LightType.SKY); - BlockPos.Mutable downPos = new BlockPos.Mutable(); - Iterable iterate = BlockPos.iterate(playerPos.getX() - reach, playerPos.getY() - reach, playerPos.getZ() - reach, - playerPos.getX() + reach, playerPos.getY() + reach, playerPos.getZ() + reach); - Long2ReferenceMap map = new Long2ReferenceOpenHashMap<>(); - CHUNK_MAP.put(new ChunkPos(0, 0), map); - for (BlockPos blockPos : iterate) { - downPos.set(blockPos.getX(), blockPos.getY() - 1, blockPos.getZ()); - if (showNumber) { - int level = getCrossLevel(blockPos, downPos, world, block, entityContext); - if (level >= 0) { - map.put(blockPos.asLong(), Integer.valueOf(level)); - } - } else { - CrossType type = getCrossType(blockPos, downPos, world, block, sky, entityContext); - if (type != CrossType.NONE) { - map.put(blockPos.asLong(), type); - } - } - } - } else { - ClientPlayerEntity player = CLIENT.player; - ClientWorld world = CLIENT.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); - } - } - } - POS.removeIf(pos -> MathHelper.abs(pos.x - playerPosX) > getChunkRange() || MathHelper.abs(pos.z - playerPosZ) > getChunkRange()); - for (int k = 0; k < 2; k++) { - if (!POS.isEmpty()) { - if (playerPos[0] == null) { - playerPos[0] = player.getPos(); - } - ChunkPos pos = POS.stream().min(Comparator.comparingDouble(value -> { - int i = Math.abs(value.x - playerPosX); - int j = Math.abs(value.z - playerPosZ); - return i * i + j * j; - })).get(); - POS.remove(pos); - EXECUTOR.submit(() -> { - try { - calculateChunk(world.getChunkManager().getChunk(pos.x, pos.z, ChunkStatus.FULL, false), world, pos, entityContext); - } catch (Throwable throwable) { - LogManager.getLogger().throwing(throwable); - } - }); - } - } - if (ticks % 50 == 0) { - CHUNK_MAP.entrySet().removeIf(pos -> MathHelper.abs(pos.getKey().x - playerPosX) > getChunkRange() * 2 || MathHelper.abs(pos.getKey().z - playerPosZ) > getChunkRange() * 2); - } - } - } - } catch (Throwable throwable) { - LogManager.getLogger().throwing(throwable); - } - }); - } - - 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 (caching) - 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) { - if (world != null && chunk != null) { - Long2ReferenceMap map = new Long2ReferenceOpenHashMap<>(); - ChunkLightingView block = world.getLightingProvider().get(LightType.BLOCK); - ChunkLightingView sky = showNumber ? null : world.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(), Integer.valueOf(level)); - } - } else { - CrossType type = LightOverlay.getCrossType(pos, down, chunk, block, sky, entityContext); - if (type != CrossType.NONE) { - map.put(pos.asLong(), type); - } - } - } - CHUNK_MAP.put(chunkPos, map); - } else { - CHUNK_MAP.remove(chunkPos); - } - } - - 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().isIn(BlockTags.RAILS)) - return CrossType.NONE; - // Check block state allow spawning (excludes bedrock and barriers automatically) - if (!blockBelowState.allowsSpawning(world, down, testingEntityType)) - return CrossType.NONE; - int blockLightLevel = block.getLightLevel(pos); - int skyLightLevel = sky.getLightLevel(pos); - if (blockLightLevel > higherCrossLevel) - return CrossType.NONE; - if (skyLightLevel > higherCrossLevel) - return CrossType.YELLOW; - return lowerCrossLevel >= 0 && blockLightLevel > lowerCrossLevel ? CrossType.SECONDARY : 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(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; - - int red = (color >> 16) & 255; - int green = (color >> 8) & 255; - int blue = color & 255; - int x = pos.getX(); - int y = pos.getY(); - int z = pos.getZ(); - RenderSystem.color4f(red / 255f, green / 255f, blue / 255f, 1f); - GL11.glVertex3d(x + .01 - d0, y - d1, z + .01 - d2); - GL11.glVertex3d(x - .01 + 1 - d0, y - d1, z - .01 + 1 - d2); - GL11.glVertex3d(x - .01 + 1 - d0, y - d1, z + .01 - d2); - GL11.glVertex3d(x + .01 - d0, y - d1, z - .01 + 1 - d2); - } - - @SuppressWarnings("deprecation") - public static void renderLevel(MinecraftClient client, Camera camera, World world, BlockPos pos, BlockPos down, int level, EntityContext entityContext) { - String text = 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(text)) / 2.0F + 0.4f; - RenderSystem.enableAlphaTest(); - VertexConsumerProvider.Immediate immediate = VertexConsumerProvider.immediate(Tessellator.getInstance().getBuffer()); - textRenderer_1.draw(text, float_3, -3.5f, level > higherCrossLevel ? 0xff042404 : (lowerCrossLevel >= 0 && level > lowerCrossLevel ? 0xff0066ff : 0xff731111), false, Rotation3.identity().getMatrix(), immediate, false, 0, 15728880); - immediate.draw(); - RenderSystem.popMatrix(); +public class LightOverlay implements ClientModInitializer, AbstractPlatform { + @Override + public void onInitializeClient() { + LightOverlayCore.register(this); } - static void loadConfig(File file) { - try { - redColor = 0xFF0000; - yellowColor = 0xFFFF00; - secondaryColor = 0x0000FF; - 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")); - secondaryLevel = Integer.parseInt((String) properties.computeIfAbsent("secondaryLevel", a -> "-1")); - caching = ((String) properties.computeIfAbsent("caching", a -> "false")).equalsIgnoreCase("true"); - showNumber = ((String) properties.computeIfAbsent("showNumber", a -> "false")).equalsIgnoreCase("true"); - smoothLines = ((String) properties.computeIfAbsent("smoothLines", a -> "true")).equalsIgnoreCase("true"); - underwater = ((String) properties.computeIfAbsent("underwater", a -> "false")).equalsIgnoreCase("true"); - 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; - } - { - int r, g, b; - r = Integer.parseInt((String) properties.computeIfAbsent("secondaryColorRed", a -> "0")); - g = Integer.parseInt((String) properties.computeIfAbsent("secondaryColorGreen", a -> "0")); - b = Integer.parseInt((String) properties.computeIfAbsent("secondaryColorBlue", a -> "255")); - secondaryColor = (r << 16) + (g << 8) + b; - } - saveConfig(file); - } catch (Exception e) { - e.printStackTrace(); - reach = 12; - crossLevel = 7; - secondaryLevel = -1; - lineWidth = 1.0F; - redColor = 0xFF0000; - yellowColor = 0xFFFF00; - secondaryColor = 0x0000FF; - caching = false; - showNumber = false; - smoothLines = true; - underwater = false; - try { - saveConfig(file); - } catch (IOException ex) { - ex.printStackTrace(); - } - } - if (secondaryLevel >= crossLevel) System.err.println("[Light Overlay] Secondary Level is higher than Cross Level"); - lowerCrossLevel = Math.min(crossLevel, secondaryLevel); - higherCrossLevel = Math.max(crossLevel, secondaryLevel); - CHUNK_MAP.clear(); - POS.clear(); + @Override + public Path getConfigDir() { + return FabricLoader.getInstance().getConfigDir(); } - 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(("secondaryLevel=" + secondaryLevel).getBytes()); - fos.write("\n".getBytes()); - fos.write(("caching=" + caching).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.write("\n".getBytes()); - fos.write(("secondaryColorRed=" + ((secondaryColor >> 16) & 255)).getBytes()); - fos.write("\n".getBytes()); - fos.write(("secondaryColorGreen=" + ((secondaryColor >> 8) & 255)).getBytes()); - fos.write("\n".getBytes()); - fos.write(("secondaryColorBlue=" + (secondaryColor & 255)).getBytes()); - fos.close(); + @Override + public void registerClientTick(Runnable runnable) { + ClientTickEvents.END_CLIENT_TICK.register(minecraft -> runnable.run()); } - private static KeyBinding createKeyBinding(Identifier id, InputUtil.Type type, int code, String category) { - return KeyBindingHelper.registerKeyBinding(new KeyBinding("key." + id.getNamespace() + "." + id.getPath(), type, code, category)); + @Override + public void registerDebugRenderer(Runnable runnable) { + ClothClientHooks.DEBUG_RENDER_PRE.register(runnable); } @Override - public void onInitializeClient() { - // Load Config - loadConfig(configFile); - - // Setup - testingEntityType = EntityType.Builder.create(EntityCategory.MONSTER).setDimensions(0f, 0f).disableSaving().build(null); - ClientTickEvents.END_CLIENT_TICK.register(minecraftClient -> { - while (ENABLE_OVERLAY.wasPressed()) - enabled = !enabled; - }); - 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 = CLIENT.gameRenderer.getCamera(); - if (showNumber) { - RenderSystem.enableTexture(); - RenderSystem.depthMask(true); - BlockPos.Mutable mutable = new BlockPos.Mutable(); - BlockPos.Mutable downMutable = new BlockPos.Mutable(); - for (Map.Entry> entry : CHUNK_MAP.entrySet()) { - if (caching && (MathHelper.abs(entry.getKey().x - playerPosX) > getChunkRange() || MathHelper.abs(entry.getKey().z - playerPosZ) > getChunkRange())) { - continue; - } - for (Long2ReferenceMap.Entry objectEntry : entry.getValue().long2ReferenceEntrySet()) { - if (objectEntry.getValue() instanceof Integer) { - mutable.set(BlockPos.unpackLongX(objectEntry.getLongKey()), BlockPos.unpackLongY(objectEntry.getLongKey()), BlockPos.unpackLongZ(objectEntry.getLongKey())); - if (mutable.isWithinDistance(playerPos, reach)) { - if (frustum == null || FrustumHelper.isVisible(frustum, mutable.getX(), mutable.getY(), mutable.getZ(), mutable.getX() + 1, mutable.getX() + 1, mutable.getX() + 1)) { - downMutable.set(mutable.getX(), mutable.getY() - 1, mutable.getZ()); - LightOverlay.renderLevel(CLIENT, camera, world, mutable, downMutable, (Integer) objectEntry.getValue(), entityContext); - } - } - } - } - } - RenderSystem.enableDepthTest(); - } else { - RenderSystem.enableDepthTest(); - RenderSystem.disableTexture(); - RenderSystem.enableBlend(); - RenderSystem.blendFunc(GlStateManager.SrcFactor.SRC_ALPHA, GlStateManager.DstFactor.ONE_MINUS_SRC_ALPHA); - if (smoothLines) GL11.glEnable(GL11.GL_LINE_SMOOTH); - GL11.glLineWidth(lineWidth); - GL11.glBegin(GL11.GL_LINES); - BlockPos.Mutable mutable = new BlockPos.Mutable(); - for (Map.Entry> entry : CHUNK_MAP.entrySet()) { - if (caching && (MathHelper.abs(entry.getKey().x - playerPosX) > getChunkRange() || MathHelper.abs(entry.getKey().z - playerPosZ) > getChunkRange())) { - continue; - } - for (Long2ReferenceMap.Entry objectEntry : entry.getValue().long2ReferenceEntrySet()) { - if (objectEntry.getValue() instanceof CrossType) { - mutable.set(BlockPos.unpackLongX(objectEntry.getLongKey()), BlockPos.unpackLongY(objectEntry.getLongKey()), BlockPos.unpackLongZ(objectEntry.getLongKey())); - if (mutable.isWithinDistance(playerPos, reach)) { - if (frustum == null || FrustumHelper.isVisible(frustum, mutable.getX(), mutable.getY(), mutable.getZ(), mutable.getX() + 1, mutable.getX() + 1, mutable.getX() + 1)) { - int color = objectEntry.getValue() == CrossType.RED ? redColor : objectEntry.getValue() == CrossType.YELLOW ? yellowColor : secondaryColor; - LightOverlay.renderCross(camera, world, mutable, color, entityContext); - } - } - } - } - } - GL11.glEnd(); - RenderSystem.disableBlend(); - RenderSystem.enableTexture(); - if (smoothLines) GL11.glDisable(GL11.GL_LINE_SMOOTH); - } - } - }); + public KeyMapping registerKeyBinding(KeyMapping keyMapping) { + return KeyBindingHelper.registerKeyBinding(keyMapping); } - private enum CrossType { - YELLOW, - RED, - SECONDARY, - NONE + @Override + public boolean isFrustumVisible(Frustum frustum, double minX, double minY, double minZ, double maxX, double maxY, double maxZ) { + return FrustumHelper.isVisible(frustum, minX, minY, minZ, maxX, maxY, maxZ); } } diff --git a/fabric/src/main/java/me/shedaniel/lightoverlay/fabric/mixin/MixinClientConnection.java b/fabric/src/main/java/me/shedaniel/lightoverlay/fabric/mixin/MixinClientConnection.java index 4fed3aa..bdb88d7 100644 --- a/fabric/src/main/java/me/shedaniel/lightoverlay/fabric/mixin/MixinClientConnection.java +++ b/fabric/src/main/java/me/shedaniel/lightoverlay/fabric/mixin/MixinClientConnection.java @@ -1,31 +1,31 @@ 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.network.packet.s2c.play.LightUpdateS2CPacket; -import net.minecraft.util.math.ChunkPos; +import me.shedaniel.lightoverlay.common.LightOverlayCore; +import net.minecraft.network.Connection; +import net.minecraft.network.PacketListener; +import net.minecraft.network.protocol.Packet; +import net.minecraft.network.protocol.game.ClientboundBlockUpdatePacket; +import net.minecraft.network.protocol.game.ClientboundLightUpdatePacket; +import net.minecraft.network.protocol.game.ClientboundSectionBlocksUpdatePacket; +import net.minecraft.network.protocol.game.ClientboundSetChunkCacheCenterPacket; +import net.minecraft.world.level.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) +@Mixin(Connection.class) public class MixinClientConnection { - @Inject(method = "handlePacket", at = @At("HEAD")) + @Inject(method = "genericsFtw", 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).sectionPos.getSectionX(), ((ChunkDeltaUpdateS2CPacket) packet).sectionPos.getSectionZ())); - } else if (packet instanceof LightUpdateS2CPacket) { - LightOverlay.queueChunk(new ChunkPos(((LightUpdateS2CPacket) packet).getChunkX(), ((LightUpdateS2CPacket) packet).getChunkZ())); + if (packet instanceof ClientboundBlockUpdatePacket) { + LightOverlayCore.queueChunkAndNear(new ChunkPos(((ClientboundBlockUpdatePacket) packet).getPos())); + } else if (packet instanceof ClientboundSetChunkCacheCenterPacket) { + LightOverlayCore.queueChunkAndNear(new ChunkPos(((ClientboundSetChunkCacheCenterPacket) packet).getX(), ((ClientboundSetChunkCacheCenterPacket) packet).getZ())); + } else if (packet instanceof ClientboundSectionBlocksUpdatePacket) { + LightOverlayCore.queueChunkAndNear(new ChunkPos(((ClientboundSectionBlocksUpdatePacket) packet).sectionPos.getX(), ((ClientboundSectionBlocksUpdatePacket) packet).sectionPos.getZ())); + } else if (packet instanceof ClientboundLightUpdatePacket) { + LightOverlayCore.queueChunk(new ChunkPos(((ClientboundLightUpdatePacket) packet).getX(), ((ClientboundLightUpdatePacket) packet).getZ())); } } } diff --git a/fabric/src/main/java/me/shedaniel/lightoverlay/fabric/mixin/MixinWorldRenderer.java b/fabric/src/main/java/me/shedaniel/lightoverlay/fabric/mixin/MixinWorldRenderer.java index 4c0b271..8183969 100644 --- a/fabric/src/main/java/me/shedaniel/lightoverlay/fabric/mixin/MixinWorldRenderer.java +++ b/fabric/src/main/java/me/shedaniel/lightoverlay/fabric/mixin/MixinWorldRenderer.java @@ -1,18 +1,18 @@ package me.shedaniel.lightoverlay.fabric.mixin; -import me.shedaniel.lightoverlay.fabric.LightOverlay; -import net.minecraft.client.render.Camera; -import net.minecraft.client.render.Frustum; -import net.minecraft.client.render.WorldRenderer; +import me.shedaniel.lightoverlay.common.LightOverlayCore; +import net.minecraft.client.Camera; +import net.minecraft.client.renderer.LevelRenderer; +import net.minecraft.client.renderer.culling.Frustum; 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(WorldRenderer.class) +@Mixin(LevelRenderer.class) public class MixinWorldRenderer { - @Inject(method = "setupTerrain", at = @At("HEAD")) - private void setupTerrain(Camera camera, Frustum frustum, boolean hasForcedFrustum, int frame, boolean spectator, CallbackInfo ci) { - LightOverlay.frustum = frustum; + @Inject(method = "setupRender", at = @At("HEAD")) + private void setupTerrain(Camera camera, Frustum frustum, boolean bl, int i, boolean bl2, CallbackInfo ci) { + LightOverlayCore.frustum = frustum; } } diff --git a/fabric/src/main/resources/lightoverlay.accesswidener b/fabric/src/main/resources/lightoverlay.accesswidener index 5339db9..ee2fe74 100644 --- a/fabric/src/main/resources/lightoverlay.accesswidener +++ b/fabric/src/main/resources/lightoverlay.accesswidener @@ -1,6 +1,6 @@ accessWidener v1 named -accessible field net/minecraft/network/packet/s2c/play/ChunkDeltaUpdateS2CPacket sectionPos Lnet/minecraft/util/math/ChunkSectionPos; -accessible field net/minecraft/client/render/Frustum x D -accessible field net/minecraft/client/render/Frustum y D -accessible field net/minecraft/client/render/Frustum z D -accessible field net/minecraft/client/render/Frustum homogeneousCoordinates [Lnet/minecraft/client/util/math/Vector4f; \ No newline at end of file +accessible field net/minecraft/network/protocol/game/ClientboundSectionBlocksUpdatePacket sectionPos Lnet/minecraft/core/SectionPos; +accessible field net/minecraft/client/renderer/culling/Frustum camX D +accessible field net/minecraft/client/renderer/culling/Frustum camY D +accessible field net/minecraft/client/renderer/culling/Frustum camZ D +accessible field net/minecraft/client/renderer/culling/Frustum frustumData [Lcom/mojang/math/Vector4f; \ No newline at end of file diff --git a/forge/build.gradle b/forge/build.gradle index 655e587..7c65caf 100644 --- a/forge/build.gradle +++ b/forge/build.gradle @@ -9,11 +9,15 @@ buildscript { } } +plugins { + id "com.github.johnrengelman.shadow" version "5.0.0" + id "eclipse" +} + apply plugin: "net.minecraftforge.gradle" -apply plugin: "eclipse" minecraft { - mappings(channel: "snapshot", version: rootProject.mcp_snapshot) + mappings(channel: "official", version: rootProject.architect.minecraft) accessTransformer = file('src/main/resources/META-INF/accesstransformer.cfg') runs { client { @@ -36,11 +40,35 @@ minecraft { } repositories { + jcenter() maven { url "https://files.minecraftforge.net/maven" } maven { url "https://dl.bintray.com/shedaniel/cloth-config-2" } } +configurations { + shadow +} + dependencies { - minecraft("net.minecraftforge:forge:${rootProject.forge_minecraft_version}-${project.forge_version}") + minecraft("net.minecraftforge:forge:${rootProject.architect.minecraft}-${project.forge_version}") implementation(fg.deobf("me.shedaniel.cloth:cloth-config-forge:${rootProject.cloth_config_forge}")) + + compile(project(path: ":common", configuration: "mcp")) { + transitive = false + } + shadow(project(path: ":common", configuration: "mcp")) { + transitive = false + } +} + +shadowJar { + relocate "me.shedaniel.lightoverlay.common", "me.shedaniel.lightoverlay.common.forge" + exclude "fabric.mod.json" + + configurations = [project.configurations.shadow] + classifier null +} + +reobf { + shadowJar {} } \ No newline at end of file diff --git a/forge/src/main/java/me/shedaniel/lightoverlay/forge/FrustumHelper.java b/forge/src/main/java/me/shedaniel/lightoverlay/forge/FrustumHelper.java index 918b4b2..b02448d 100644 --- a/forge/src/main/java/me/shedaniel/lightoverlay/forge/FrustumHelper.java +++ b/forge/src/main/java/me/shedaniel/lightoverlay/forge/FrustumHelper.java @@ -5,17 +5,17 @@ import net.minecraft.util.math.vector.Vector4f; public class FrustumHelper { public static boolean isVisible(ClippingHelper frustum, double minX, double minY, double minZ, double maxX, double maxY, double maxZ) { - float x1 = (float) (minX - frustum.cameraX); - float y1 = (float) (minY - frustum.cameraY); - float z1 = (float) (minZ - frustum.cameraZ); - float x2 = (float) (maxX - frustum.cameraX); - float y2 = (float) (maxY - frustum.cameraY); - float z2 = (float) (maxZ - frustum.cameraZ); + float x1 = (float) (minX - frustum.camX); + float y1 = (float) (minY - frustum.camY); + float z1 = (float) (minZ - frustum.camZ); + float x2 = (float) (maxX - frustum.camX); + float y2 = (float) (maxY - frustum.camY); + float z2 = (float) (maxZ - frustum.camZ); return isAnyCornerVisible(frustum, x1, y1, z1, x2, y2, z2); } private static boolean isAnyCornerVisible(ClippingHelper frustum, float x1, float y1, float z1, float x2, float y2, float z2) { - Vector4f[] homogeneousCoordinates = frustum.frustum; + Vector4f[] homogeneousCoordinates = frustum.frustumData; for (Vector4f vector4f : homogeneousCoordinates) { if (dotProduct(vector4f, x1, y1, z1, 1.0F) <= 0.0F && dotProduct(vector4f, x2, y1, z1, 1.0F) <= 0.0F && dotProduct(vector4f, x1, y2, z1, 1.0F) <= 0.0F && dotProduct(vector4f, x2, y2, z1, 1.0F) <= 0.0F && dotProduct(vector4f, x1, y1, z2, 1.0F) <= 0.0F && dotProduct(vector4f, x2, y1, z2, 1.0F) <= 0.0F && dotProduct(vector4f, x1, y2, z2, 1.0F) <= 0.0F && dotProduct(vector4f, x2, y2, z2, 1.0F) <= 0.0F) { return false; @@ -26,6 +26,6 @@ public class FrustumHelper { } private static float dotProduct(Vector4f self, float x, float y, float z, float w) { - return self.getX() * x + self.getY() * y + self.getZ() * z + self.getW() * w; + return self.x() * x + self.y() * y + self.z() * z + self.w() * w; } } diff --git a/forge/src/main/java/me/shedaniel/lightoverlay/forge/LightOverlayClient.java b/forge/src/main/java/me/shedaniel/lightoverlay/forge/LightOverlayClient.java index b9f3740..30ab28a 100644 --- a/forge/src/main/java/me/shedaniel/lightoverlay/forge/LightOverlayClient.java +++ b/forge/src/main/java/me/shedaniel/lightoverlay/forge/LightOverlayClient.java @@ -1,545 +1,77 @@ package me.shedaniel.lightoverlay.forge; -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 it.unimi.dsi.fastutil.longs.Long2ReferenceMap; -import it.unimi.dsi.fastutil.longs.Long2ReferenceOpenHashMap; -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.ActiveRenderInfo; -import net.minecraft.client.renderer.IRenderTypeBuffer; -import net.minecraft.client.renderer.Tessellator; +import me.shedaniel.lightoverlay.common.AbstractPlatform; +import me.shedaniel.lightoverlay.common.LightOverlayCore; import net.minecraft.client.renderer.culling.ClippingHelper; 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.network.play.server.SUpdateLightPacket; -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.shapes.ISelectionContext; -import net.minecraft.util.math.shapes.VoxelShape; -import net.minecraft.util.math.vector.TransformationMatrix; -import net.minecraft.util.math.vector.Vector3d; -import net.minecraft.world.IBlockReader; -import net.minecraft.world.LightType; -import net.minecraft.world.World; -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 org.apache.logging.log4j.LogManager; -import org.lwjgl.opengl.GL11; +import net.minecraftforge.fml.loading.FMLPaths; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.text.DecimalFormat; -import java.util.Comparator; -import java.util.List; -import java.util.Map; -import java.util.Properties; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; +import java.nio.file.Path; public class LightOverlayClient { - static final DecimalFormat FORMAT = new DecimalFormat("#.#"); - private static final String KEYBIND_CATEGORY = "key.lightoverlay.category"; - private static final ResourceLocation ENABLE_OVERLAY_KEYBIND = new ResourceLocation("lightoverlay", "enable_overlay"); - static int reach = 12; - static int crossLevel = 7; - static int secondaryLevel = -1; - static int lowerCrossLevel = -1; - static int higherCrossLevel = -1; - static boolean caching = false; - static boolean showNumber = false; - static boolean smoothLines = true; - static boolean underwater = false; - static float lineWidth = 1.0F; - static int yellowColor = 0xFFFF00, redColor = 0xFF0000, secondaryColor = 0x0000FF; - static File configFile = new File(new File(Minecraft.getInstance().gameDir, "config"), "lightoverlay.properties"); - private static final KeyBinding ENABLE_OVERLAY; - private static boolean enabled = false; - private static EntityType testingEntityType; - private static int threadNumber = 0; - public static ClippingHelper frustum; - private static final ExecutorService EXECUTOR = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors(), r -> { - Thread thread = new Thread(r, "light-overlay-" + threadNumber++); - thread.setDaemon(true); - return thread; - }); - private static final List POS = Lists.newCopyOnWriteArrayList(); - private static final Map> CHUNK_MAP = Maps.newConcurrentMap(); - private static long ticks = 0; - - static { - ENABLE_OVERLAY = registerKeybind(ENABLE_OVERLAY_KEYBIND, InputMappings.Type.KEYSYM, 296, KEYBIND_CATEGORY); - } + private static Runnable debugRenderer = () -> {}; public static void register() { - // Load Config - loadConfig(configFile); - - // Setup - testingEntityType = EntityType.Builder.create(EntityClassification.MONSTER).size(0f, 0f).disableSerialization().build(null); - 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; - // 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; - int blockLightLevel = block.getLightFor(pos); - int skyLightLevel = sky.getLightFor(pos); - if (blockLightLevel > higherCrossLevel) - return CrossType.NONE; - if (skyLightLevel > higherCrossLevel) - return CrossType.YELLOW; - return lowerCrossLevel >= 0 && blockLightLevel > lowerCrossLevel ? CrossType.SECONDARY : 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, 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; - int red = (color >> 16) & 255; - int green = (color >> 8) & 255; - int blue = color & 255; - int x = pos.getX(); - int y = pos.getY(); - int z = pos.getZ(); - RenderSystem.color4f(red / 255f, green / 255f, blue / 255f, 1f); - GL11.glVertex3d(x + .01 - d0, y - d1, z + .01 - d2); - GL11.glVertex3d(x - .01 + 1 - d0, y - d1, z - .01 + 1 - d2); - GL11.glVertex3d(x - .01 + 1 - d0, y - d1, z + .01 - d2); - GL11.glVertex3d(x + .01 - d0, y - d1, z - .01 + 1 - d2); - } - - 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 > higherCrossLevel ? 0xff042404 : (lowerCrossLevel >= 0 && level > lowerCrossLevel ? 0xff0066ff : 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 (ENABLE_OVERLAY.isPressed()) - enabled = !enabled; - } - - 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 (caching) - 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 { - if (!caching) { - POS.clear(); - CHUNK_MAP.clear(); - ClientPlayerEntity player = minecraft.player; - ClientWorld world = minecraft.world; - BlockPos playerPos = player.getPosition(); - ISelectionContext entityContext = ISelectionContext.forEntity(player); - IWorldLightListener block = world.getLightManager().getLightEngine(LightType.BLOCK); - IWorldLightListener sky = showNumber ? null : world.getLightManager().getLightEngine(LightType.SKY); - BlockPos.Mutable downPos = new BlockPos.Mutable(); - Iterable iterate = BlockPos.getAllInBoxMutable(playerPos.getX() - reach, playerPos.getY() - reach, playerPos.getZ() - reach, - playerPos.getX() + reach, playerPos.getY() + reach, playerPos.getZ() + reach); - Long2ReferenceMap map = new Long2ReferenceOpenHashMap<>(); - CHUNK_MAP.put(new ChunkPos(0, 0), map); - for (BlockPos blockPos : iterate) { - downPos.setPos(blockPos.getX(), blockPos.getY() - 1, blockPos.getZ()); - if (showNumber) { - int level = getCrossLevel(blockPos, downPos, world, block, entityContext); - if (level >= 0) { - map.put(blockPos.toLong(), Integer.valueOf(level)); - } - } else { - CrossType type = getCrossType(blockPos, downPos, world, block, sky, entityContext); - if (type != CrossType.NONE) { - map.put(blockPos.toLong(), type); - } - } - } - } else { - ClientPlayerEntity player = minecraft.player; - ClientWorld world = minecraft.world; - ISelectionContext selectionContext = ISelectionContext.forEntity(player); - Vector3d[] 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); - } - } - } - POS.removeIf(pos -> MathHelper.abs(pos.x - playerPosX) > getChunkRange() || MathHelper.abs(pos.z - playerPosZ) > getChunkRange()); - for (int k = 0; k < 2; k++) { - if (!POS.isEmpty()) { - if (playerPos[0] == null) { - playerPos[0] = player.getPositionVec(); - } - ChunkPos pos = POS.stream().min(Comparator.comparingDouble(value -> { - int i = Math.abs(value.x - playerPosX); - int j = Math.abs(value.z - playerPosZ); - return i * i + j * j; - })).get(); - POS.remove(pos); - EXECUTOR.submit(() -> { - try { - calculateChunk(world.getChunkProvider().getChunk(pos.x, pos.z, ChunkStatus.FULL, false), world, pos, selectionContext); - } catch (Throwable throwable) { - LogManager.getLogger().throwing(throwable); - } - }); - } - } - if (ticks % 50 == 0) { - CHUNK_MAP.entrySet().removeIf(pos -> MathHelper.abs(pos.getKey().x - playerPosX) > getChunkRange() * 2 || MathHelper.abs(pos.getKey().z - playerPosZ) > getChunkRange() * 2); - } - } - } - } catch (Exception e) { - LogManager.getLogger().throwing(e); + LightOverlayCore.register(new AbstractPlatform() { + @Override + public Path getConfigDir() { + return FMLPaths.CONFIGDIR.get(); } - } - } - - public static void updateFrustum(ClippingHelper helper) { - frustum = helper; - } - - private static void calculateChunk(Chunk chunk, World world, ChunkPos chunkPos, ISelectionContext selectionContext) { - if (world != null && chunk != null) { - Long2ReferenceMap map = new Long2ReferenceOpenHashMap<>(); - 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(), Integer.valueOf(level)); - } - } else { - CrossType type = LightOverlayClient.getCrossType(pos, down, chunk, block, sky, selectionContext); - if (type != CrossType.NONE) { - map.put(pos.toLong(), type); - } - } + + @Override + public void registerClientTick(Runnable runnable) { + MinecraftForge.EVENT_BUS.addListener(event -> runnable.run()); } - CHUNK_MAP.put(chunkPos, map); - } else { - CHUNK_MAP.remove(chunkPos); - } - } - - 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(); - BlockPos.Mutable downMutable = new BlockPos.Mutable(); - for (Map.Entry> entry : CHUNK_MAP.entrySet()) { - if (caching && (MathHelper.abs(entry.getKey().x - playerPosX) > getChunkRange() || MathHelper.abs(entry.getKey().z - playerPosZ) > getChunkRange())) { - continue; - } - for (Long2ReferenceMap.Entry objectEntry : entry.getValue().long2ReferenceEntrySet()) { - if (objectEntry.getValue() instanceof Integer) { - mutable.setPos(BlockPos.unpackX(objectEntry.getLongKey()), BlockPos.unpackY(objectEntry.getLongKey()), BlockPos.unpackZ(objectEntry.getLongKey())); - if (mutable.withinDistance(playerPos, reach)) { - if (frustum == null || FrustumHelper.isVisible(frustum, mutable.getX(), mutable.getY(), mutable.getZ(), mutable.getX() + 1, mutable.getX() + 1, mutable.getX() + 1)) { - downMutable.setPos(mutable.getX(), mutable.getY() - 1, mutable.getZ()); - LightOverlayClient.renderLevel(client, info, world, mutable, downMutable, (Integer) objectEntry.getValue(), selectionContext); - } - } - } - } - } - RenderSystem.color4f(1.0F, 1.0F, 1.0F, 1.0F); - RenderSystem.enableDepthTest(); - } else { - RenderSystem.enableDepthTest(); - RenderSystem.disableTexture(); - RenderSystem.enableBlend(); - RenderSystem.blendFunc(GlStateManager.SourceFactor.SRC_ALPHA, GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA); - if (smoothLines) GL11.glEnable(GL11.GL_LINE_SMOOTH); - GL11.glLineWidth(lineWidth); - GL11.glBegin(GL11.GL_LINES); - BlockPos.Mutable mutable = new BlockPos.Mutable(); - for (Map.Entry> entry : CHUNK_MAP.entrySet()) { - if (caching && (MathHelper.abs(entry.getKey().x - playerPosX) > getChunkRange() || MathHelper.abs(entry.getKey().z - playerPosZ) > getChunkRange())) { - continue; - } - for (Long2ReferenceMap.Entry objectEntry : entry.getValue().long2ReferenceEntrySet()) { - if (objectEntry.getValue() instanceof CrossType) { - mutable.setPos(BlockPos.unpackX(objectEntry.getLongKey()), BlockPos.unpackY(objectEntry.getLongKey()), BlockPos.unpackZ(objectEntry.getLongKey())); - if (mutable.withinDistance(playerPos, reach)) { - if (frustum == null || FrustumHelper.isVisible(frustum, mutable.getX(), mutable.getY(), mutable.getZ(), mutable.getX() + 1, mutable.getX() + 1, mutable.getX() + 1)) { - int color = objectEntry.getValue() == CrossType.RED ? redColor : objectEntry.getValue() == CrossType.YELLOW ? yellowColor : secondaryColor; - renderCross(info, world, mutable, color, selectionContext); - } - } - } - } - } - GL11.glEnd(); - RenderSystem.disableBlend(); - RenderSystem.enableTexture(); - if (smoothLines) GL11.glDisable(GL11.GL_LINE_SMOOTH); + + @Override + public void registerDebugRenderer(Runnable runnable) { + debugRenderer = runnable; } - 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; - secondaryColor = 0x0000FF; - 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")); - secondaryLevel = Integer.parseInt((String) properties.computeIfAbsent("secondaryLevel", a -> "-1")); - caching = ((String) properties.computeIfAbsent("caching", a -> "false")).equalsIgnoreCase("true"); - showNumber = ((String) properties.computeIfAbsent("showNumber", a -> "false")).equalsIgnoreCase("true"); - smoothLines = ((String) properties.computeIfAbsent("smoothLines", a -> "true")).equalsIgnoreCase("true"); - underwater = ((String) properties.computeIfAbsent("underwater", a -> "false")).equalsIgnoreCase("true"); - 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; + + @Override + public KeyBinding registerKeyBinding(KeyBinding keyBinding) { + ClientRegistry.registerKeyBinding(keyBinding); + return keyBinding; } - { - 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; + + @Override + public boolean isFrustumVisible(ClippingHelper clippingHelper, double v, double v1, double v2, double v3, double v4, double v5) { + return FrustumHelper.isVisible(clippingHelper, v, v1, v2, v3, v4, v5); } - { - int r, g, b; - r = Integer.parseInt((String) properties.computeIfAbsent("secondaryColorRed", a -> "0")); - g = Integer.parseInt((String) properties.computeIfAbsent("secondaryColorGreen", a -> "0")); - b = Integer.parseInt((String) properties.computeIfAbsent("secondaryColorBlue", a -> "255")); - secondaryColor = (r << 16) + (g << 8) + b; - } - saveConfig(file); + }); + + try { + //noinspection Convert2MethodRef + DistExecutor.runWhenOn(Dist.CLIENT, () -> () -> LightOverlayCloth.register()); } catch (Exception e) { e.printStackTrace(); - reach = 12; - crossLevel = 7; - secondaryLevel = -1; - lineWidth = 1.0F; - redColor = 0xFF0000; - yellowColor = 0xFFFF00; - secondaryColor = 0x0000FF; - caching = false; - showNumber = false; - smoothLines = true; - underwater = false; - try { - saveConfig(file); - } catch (IOException ex) { - ex.printStackTrace(); - } } - if (secondaryLevel >= crossLevel) System.err.println("[Light Overlay] Secondary Level is higher than Cross Level"); - lowerCrossLevel = Math.min(crossLevel, secondaryLevel); - higherCrossLevel = Math.max(crossLevel, secondaryLevel); - 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(("secondaryLevel=" + secondaryLevel).getBytes()); - fos.write("\n".getBytes()); - fos.write(("caching=" + caching).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.write("\n".getBytes()); - fos.write(("secondaryColorRed=" + ((secondaryColor >> 16) & 255)).getBytes()); - fos.write("\n".getBytes()); - fos.write(("secondaryColorGreen=" + ((secondaryColor >> 8) & 255)).getBytes()); - fos.write("\n".getBytes()); - fos.write(("secondaryColorBlue=" + (secondaryColor & 255)).getBytes()); - fos.close(); + public static void renderWorldLast() { + debugRenderer.run(); } public static void processPacket(IPacket packet) { if (packet instanceof SChangeBlockPacket) { - LightOverlayClient.queueChunkAndNear(new ChunkPos(((SChangeBlockPacket) packet).getPos())); + LightOverlayCore.queueChunkAndNear(new ChunkPos(((SChangeBlockPacket) packet).getPos())); } else if (packet instanceof SChunkDataPacket) { - LightOverlayClient.queueChunkAndNear(new ChunkPos(((SChunkDataPacket) packet).getChunkX(), ((SChunkDataPacket) packet).getChunkZ())); + LightOverlayCore.queueChunkAndNear(new ChunkPos(((SChunkDataPacket) packet).getX(), ((SChunkDataPacket) packet).getZ())); } else if (packet instanceof SMultiBlockChangePacket) { - LightOverlayClient.queueChunkAndNear(new ChunkPos(((SMultiBlockChangePacket) packet).field_244305_a.getSectionX(), ((SMultiBlockChangePacket) packet).field_244305_a.getSectionZ())); + LightOverlayCore.queueChunkAndNear(new ChunkPos(((SMultiBlockChangePacket) packet).sectionPos.getX(), ((SMultiBlockChangePacket) packet).sectionPos.getZ())); } else if (packet instanceof SUpdateLightPacket) { - LightOverlayClient.queueChunkAndNear(new ChunkPos(((SUpdateLightPacket) packet).getChunkX(), ((SUpdateLightPacket) packet).getChunkZ())); + LightOverlayCore.queueChunkAndNear(new ChunkPos(((SUpdateLightPacket) packet).getX(), ((SUpdateLightPacket) packet).getZ())); } } - - private enum CrossType { - YELLOW, - RED, - SECONDARY, - NONE - } } diff --git a/forge/src/main/java/me/shedaniel/lightoverlay/forge/LightOverlayCloth.java b/forge/src/main/java/me/shedaniel/lightoverlay/forge/LightOverlayCloth.java index 2eaff45..42b3118 100644 --- a/forge/src/main/java/me/shedaniel/lightoverlay/forge/LightOverlayCloth.java +++ b/forge/src/main/java/me/shedaniel/lightoverlay/forge/LightOverlayCloth.java @@ -4,6 +4,7 @@ import me.shedaniel.clothconfig2.forge.api.ConfigBuilder; import me.shedaniel.clothconfig2.forge.api.ConfigCategory; import me.shedaniel.clothconfig2.forge.api.ConfigEntryBuilder; import me.shedaniel.clothconfig2.forge.gui.entries.IntegerSliderEntry; +import me.shedaniel.lightoverlay.common.LightOverlayCore; import net.minecraft.client.resources.I18n; import net.minecraft.util.math.MathHelper; import net.minecraft.util.text.ITextComponent; @@ -20,31 +21,32 @@ public class LightOverlayCloth { ConfigEntryBuilder eb = builder.entryBuilder(); ConfigCategory general = builder.getOrCreateCategory(new TranslationTextComponent("config.lightoverlay.general")); - general.addEntry(eb.startTextDescription(ITextComponent.func_244388_a("§7" + I18n.format("description.lightoverlay.caching"))).build()); - general.addEntry(eb.startBooleanToggle(new TranslationTextComponent("config.lightoverlay.caching"), LightOverlayClient.caching).setDefaultValue(false).setSaveConsumer(bool -> LightOverlayClient.caching = bool).build()); - general.addEntry(eb.startIntSlider(new TranslationTextComponent("config.lightoverlay.reach"), LightOverlayClient.reach, 1, 64).setDefaultValue(12).setTextGetter(integer -> ITextComponent.func_244388_a("Reach: " + integer + " Blocks")).setSaveConsumer(integer -> LightOverlayClient.reach = integer).build()); - IntegerSliderEntry crossLevel = eb.startIntSlider(new TranslationTextComponent("config.lightoverlay.crossLevel"), LightOverlayClient.crossLevel, 0, 15).setDefaultValue(7).setTextGetter(integer -> ITextComponent.func_244388_a("Cross Level: " + integer)).setSaveConsumer(integer -> LightOverlayClient.crossLevel = integer).build(); + general.addEntry(eb.startTextDescription(ITextComponent.nullToEmpty("§7" + I18n.get("description.lightoverlay.caching"))).build()); + general.addEntry(eb.startBooleanToggle(new TranslationTextComponent("config.lightoverlay.caching"), LightOverlayCore.caching).setDefaultValue(false).setSaveConsumer(bool -> LightOverlayCore.caching = bool).build()); + general.addEntry(eb.startIntSlider(new TranslationTextComponent("config.lightoverlay.reach"), LightOverlayCore.reach, 1, 64).setDefaultValue(12).setTextGetter(integer -> ITextComponent.nullToEmpty("Reach: " + integer + " Blocks")).setSaveConsumer(integer -> LightOverlayCore.reach = integer).build()); + IntegerSliderEntry crossLevel = eb.startIntSlider(new TranslationTextComponent("config.lightoverlay.crossLevel"), LightOverlayCore.crossLevel, 0, 15).setDefaultValue(7).setTextGetter(integer -> ITextComponent.nullToEmpty("Cross Level: " + integer)).setSaveConsumer(integer -> LightOverlayCore.crossLevel = integer).build(); general.addEntry(crossLevel); - general.addEntry(eb.startIntSlider(new TranslationTextComponent("config.lightoverlay.secondaryLevel"), LightOverlayClient.secondaryLevel, -1, 15) + general.addEntry(eb.startIntSlider(new TranslationTextComponent("config.lightoverlay.secondaryLevel"), LightOverlayCore.secondaryLevel, -1, 15) .setErrorSupplier(integer -> { - if (integer >= 0 && integer >= crossLevel.getValue()) return Optional.of(ITextComponent.func_244388_a("Secondary Level cannot be higher than Cross Level!")); + if (integer >= 0 && integer >= crossLevel.getValue()) + return Optional.of(ITextComponent.nullToEmpty("Secondary Level cannot be higher than Cross Level!")); return Optional.empty(); - }).setDefaultValue(-1).setTextGetter(integer -> ITextComponent.func_244388_a(integer < 0 ? "Off" : "Level: " + integer)).setSaveConsumer(integer -> LightOverlayClient.secondaryLevel = integer).build()); - general.addEntry(eb.startBooleanToggle(new TranslationTextComponent("config.lightoverlay.showNumber"), LightOverlayClient.showNumber).setDefaultValue(false).setSaveConsumer(bool -> LightOverlayClient.showNumber = bool).build()); - general.addEntry(eb.startBooleanToggle(new TranslationTextComponent("config.lightoverlay.smoothLines"), LightOverlayClient.smoothLines).setDefaultValue(true).setSaveConsumer(bool -> LightOverlayClient.smoothLines = bool).build()); - general.addEntry(eb.startBooleanToggle(new TranslationTextComponent("config.lightoverlay.underwater"), LightOverlayClient.underwater).setDefaultValue(false).setSaveConsumer(bool -> LightOverlayClient.underwater = bool).build()); - general.addEntry(eb.startIntSlider(new TranslationTextComponent("config.lightoverlay.lineWidth"), MathHelper.floor(LightOverlayClient.lineWidth * 100), 100, 700).setDefaultValue(100).setTextGetter(integer -> ITextComponent.func_244388_a("Light Width: " + LightOverlayClient.FORMAT.format(integer / 100d))).setSaveConsumer(integer -> LightOverlayClient.lineWidth = integer / 100f).build()); - general.addEntry(eb.startColorField(new TranslationTextComponent("config.lightoverlay.yellowColor"), LightOverlayClient.yellowColor).setDefaultValue(0xFFFF00).setSaveConsumer(color -> LightOverlayClient.yellowColor = color).build()); - general.addEntry(eb.startColorField(new TranslationTextComponent("config.lightoverlay.redColor"), LightOverlayClient.redColor).setDefaultValue(0xFF0000).setSaveConsumer(color -> LightOverlayClient.redColor = color).build()); - general.addEntry(eb.startColorField(new TranslationTextComponent("config.lightoverlay.secondaryColor"), LightOverlayClient.secondaryColor).setDefaultValue(0x0000FF).setSaveConsumer(color -> LightOverlayClient.secondaryColor = color).build()); + }).setDefaultValue(-1).setTextGetter(integer -> ITextComponent.nullToEmpty(integer < 0 ? "Off" : "Level: " + integer)).setSaveConsumer(integer -> LightOverlayCore.secondaryLevel = integer).build()); + general.addEntry(eb.startBooleanToggle(new TranslationTextComponent("config.lightoverlay.showNumber"), LightOverlayCore.showNumber).setDefaultValue(false).setSaveConsumer(bool -> LightOverlayCore.showNumber = bool).build()); + general.addEntry(eb.startBooleanToggle(new TranslationTextComponent("config.lightoverlay.smoothLines"), LightOverlayCore.smoothLines).setDefaultValue(true).setSaveConsumer(bool -> LightOverlayCore.smoothLines = bool).build()); + general.addEntry(eb.startBooleanToggle(new TranslationTextComponent("config.lightoverlay.underwater"), LightOverlayCore.underwater).setDefaultValue(false).setSaveConsumer(bool -> LightOverlayCore.underwater = bool).build()); + general.addEntry(eb.startIntSlider(new TranslationTextComponent("config.lightoverlay.lineWidth"), MathHelper.floor(LightOverlayCore.lineWidth * 100), 100, 700).setDefaultValue(100).setTextGetter(integer -> ITextComponent.nullToEmpty("Light Width: " + LightOverlayCore.FORMAT.format(integer / 100d))).setSaveConsumer(integer -> LightOverlayCore.lineWidth = integer / 100f).build()); + general.addEntry(eb.startColorField(new TranslationTextComponent("config.lightoverlay.yellowColor"), LightOverlayCore.yellowColor).setDefaultValue(0xFFFF00).setSaveConsumer(color -> LightOverlayCore.yellowColor = color).build()); + general.addEntry(eb.startColorField(new TranslationTextComponent("config.lightoverlay.redColor"), LightOverlayCore.redColor).setDefaultValue(0xFF0000).setSaveConsumer(color -> LightOverlayCore.redColor = color).build()); + general.addEntry(eb.startColorField(new TranslationTextComponent("config.lightoverlay.secondaryColor"), LightOverlayCore.secondaryColor).setDefaultValue(0x0000FF).setSaveConsumer(color -> LightOverlayCore.secondaryColor = color).build()); return builder.setSavingRunnable(() -> { try { - LightOverlayClient.saveConfig(LightOverlayClient.configFile); + LightOverlayCore.saveConfig(LightOverlayCore.configFile); } catch (Exception e) { e.printStackTrace(); } - LightOverlayClient.loadConfig(LightOverlayClient.configFile); + LightOverlayCore.loadConfig(LightOverlayCore.configFile); }).build(); }); } diff --git a/forge/src/main/resources/assets/lightoverlay/lang/en_us.json b/forge/src/main/resources/assets/lightoverlay/lang/en_us.json deleted file mode 100755 index 5346550..0000000 --- a/forge/src/main/resources/assets/lightoverlay/lang/en_us.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "key.lightoverlay.category": "Light Overlay", - "key.lightoverlay.enable_overlay": "Toggle Light Overlay", - "description.lightoverlay.caching": "Caching is recommended if the reach is above 16 or if you are running on a potato.", - "config.lightoverlay.general": "General", - "config.lightoverlay.reach": "Reach:", - "config.lightoverlay.caching": "Caching:", - "config.lightoverlay.crossLevel": "Light Level:", - "config.lightoverlay.secondaryLevel": "Secondary 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.secondaryColor": "Secondary Color:", - "config.lightoverlay.redColor": "Red Color:", - "config.lightoverlay.invalidColor": "Invalid Color" -} \ No newline at end of file diff --git a/forge/src/main/resources/icon.png b/forge/src/main/resources/icon.png deleted file mode 100644 index 8bfd85a4e8697f60569254d9e22c3c2361f047e7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 22942 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