diff --git a/CHANGELOG.md b/CHANGELOG.md index 11a7ddc..d7be796 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,17 +25,17 @@ - Added water-sensitive check for items and light sources. - Added data item tag `#lambdynlights:water_sensitive` which lists every item which can't light up in the water. - Added an option to enable/disable the feature - - Updated SpruceUI to v1.5.6 to fix latest snapshots issues. - - Added "early/WIP" compatibility with [Canvas Renderer](https://www.curseforge.com/minecraft/mc-mods/canvas-renderer). + - Updated [SpruceUI] to v1.5.6 to fix latest snapshots issues. + - Added "early/WIP" compatibility with [Canvas Renderer]. - Added a warning message about performance issues. - - Fixed a crash with Sodium rc7 with smooth lighting set to HIGH. + - Fixed a crash with [Sodium] rc7 with smooth lighting set to HIGH. ### v1.2.1 - Added TNT dynamic lighting. - Added lighting options for TNT and Creepers. - Added luminance value to Fire charge item. - - Updated SpruceUI to v1.5.8 + - Updated [SpruceUI] to v1.5.8 - Fixed player dynamic lighting not getting tracked when changing dimensions. ### v1.2.2 @@ -53,9 +53,17 @@ - Added spectral arrow as item emitting light ([#17](https://github.com/LambdAurora/LambDynamicLights/pull/17)). - Added dynamic lighting on glowing entities ([#17](https://github.com/LambdAurora/LambDynamicLights/pull/17)). - Updated to Minecraft 1.16.2 - - Updated [SpruceUI](https://github.com/LambdAurora/SpruceUI) to v1.6.2. + - Updated [SpruceUI] to v1.6.2. - Fixed dynamic lighting update issues at chunk borders ([#12](https://github.com/LambdAurora/LambDynamicLights/issues/12)). - Fixed water-sensitive items lighting up in water on dedicated servers. ([#3](https://github.com/LambdAurora/LambDynamicLights/issues/3)) - Added new JSON API to add item luminance and water-sensitivity through resource packs. - Added `DynamicLightHandler#isWaterSensitive` to make some entities water-sensitive like the blaze. - - Fixed incompatibility with future Sodium versions. ([#6](https://github.com/LambdAurora/LambDynamicLights/issues/6)) + - Fixed incompatibility with future [Sodium] versions. ([#6](https://github.com/LambdAurora/LambDynamicLights/issues/6)) + +## v1.3.1 + + - Fixed entity lighting issue with [Sodium] 0.1.0. ([#23](https://github.com/LambdAurora/LambDynamicLights/issues/23)) + +[SpruceUI]: https://github.com/LambdAurora/SpruceUI "SpruceUI page" +[Sodium]: https://www.curseforge.com/minecraft/mc-mods/sodium "Sodium CurseForge page" +[Canvas Renderer]: https://www.curseforge.com/minecraft/mc-mods/canvas-renderer "Canvas Renderer CurseForge page" diff --git a/HOW_DOES_IT_WORK.md b/HOW_DOES_IT_WORK.md index 9875664..0c82cb8 100644 --- a/HOW_DOES_IT_WORK.md +++ b/HOW_DOES_IT_WORK.md @@ -12,7 +12,7 @@ UpcraftLP method can be found here: https://gist.github.com/UpcraftLP/93db478535 AtomicStryker method can be found here: https://github.com/AtomicStryker/atomicstrykers-minecraft-mods/tree/1.14.3/DynamicLights Both methods suffer from a "laggy" dynamic lighting, the way the light moves is too much tied to -the block positions as it only injects at luminance getters in `WorldChunk` or a lighting provider. +the block positions as it only injects at light level getters in `WorldChunk` or a lighting provider. It will only provide the dynamic light value of the light source if the block position is the position of the light source. @@ -36,16 +36,16 @@ light sources, their distances and the luminance), if the dynamic value is highe then we replace it. This also means that we can't just do like the previous method and only give the source luminance but -we also have to calculate the surrounding luminance created by the dynamic light source in a specified +we also have to calculate the surrounding light level created by the dynamic light sources in a specified range (which is 7.75 to limit chunk rebuilding to 8 chunks (which is still a lot)). When getting the dynamic light value at the specified position, it has to be a `double` and not -an integer as the light value calculated with the range has to be precise. +an integer as the light level calculated within the range has to be precise. To modify the lightmap with the dynamic light value, it has to be multiplied by 16.0 instead of using a bitshift to preserve as much as possible the precision. -Dynamic light value at a specified position is calculated in a for-loop with all the dynamic light -sources, only the highest light value is kept. The light value is calculated as follow: +Dynamic light level at a specified position is calculated in a for-loop with all the dynamic light +sources, only the highest light level is kept. The light level is calculated as follow: ```java // dist being the distance between the light source origin and the position where the diff --git a/build.gradle b/build.gradle index 182a096..620d300 100644 --- a/build.gradle +++ b/build.gradle @@ -56,7 +56,7 @@ dependencies { modImplementation "io.github.prospector:modmenu:${project.modmenu_version}" //modImplementation "grondag:canvas-mc116:1.0.+" - //modImplementation "me.jellysquid.mods:sodium:0.1.0+v8-SNAPSHOT" + modImplementation "com.github.jellysquid3:sodium-fabric:mc1.16.3-0.1.0" shadow "com.electronwill.night-config:core:3.6.3" shadow "com.electronwill.night-config:toml:3.6.3" diff --git a/gradle.properties b/gradle.properties index a347eb3..aa59ad1 100644 --- a/gradle.properties +++ b/gradle.properties @@ -3,17 +3,17 @@ org.gradle.jvmargs=-Xmx1G # Fabric Properties # check these on https://fabricmc.net/use -minecraft_version=1.16.2 -yarn_mappings=1.16.2+build.6 -loader_version=0.9.1+build.205 +minecraft_version=1.16.3 +yarn_mappings=1.16.3+build.17 +loader_version=0.9.3+build.207 # Mod Properties -mod_version = 1.3.0 +mod_version = 1.3.1 maven_group = me.lambdaurora archives_base_name = lambdynamiclights # Dependencies # currently not on the main fabric site, check on the maven: https://maven.fabricmc.net/net/fabricmc/fabric-api/fabric-api -fabric_version=0.17.2+build.396-1.16 -spruceui_version=1.6.2 +fabric_version=0.20.2+build.402-1.16 +spruceui_version=1.6.4 modmenu_version=1.14.6+build.31 diff --git a/src/main/java/me/lambdaurora/lambdynlights/LambDynLights.java b/src/main/java/me/lambdaurora/lambdynlights/LambDynLights.java index 9ec194f..01f336a 100644 --- a/src/main/java/me/lambdaurora/lambdynlights/LambDynLights.java +++ b/src/main/java/me/lambdaurora/lambdynlights/LambDynLights.java @@ -48,7 +48,7 @@ * Represents the LambDynamicLights mod. * * @author LambdAurora - * @version 1.3.0 + * @version 1.3.1 * @since 1.0.0 */ public class LambDynLights implements ClientModInitializer @@ -129,7 +129,7 @@ public void updateAll(@NotNull WorldRenderer renderer) */ public int getLightmapWithDynamicLight(@NotNull BlockPos pos, int lightmap) { - return this.getLightmapWithDynamicLight(this.getDynamicLuminance(pos), lightmap); + return this.getLightmapWithDynamicLight(this.getDynamicLightLevel(pos), lightmap); } /** @@ -141,29 +141,29 @@ public int getLightmapWithDynamicLight(@NotNull BlockPos pos, int lightmap) */ public int getLightmapWithDynamicLight(@NotNull Entity entity, int lightmap) { - int posLuminance = (int) this.getDynamicLuminance(entity.getBlockPos()); + int posLightLevel = (int) this.getDynamicLightLevel(entity.getBlockPos()); int entityLuminance = ((DynamicLightSource) entity).getLuminance(); - return this.getLightmapWithDynamicLight(Math.max(posLuminance, entityLuminance), lightmap); + return this.getLightmapWithDynamicLight(Math.max(posLightLevel, entityLuminance), lightmap); } /** * Returns the lightmap with combined light levels. * - * @param dynamicLuminance The dynamic light level. - * @param lightmap The vanilla lightmap. + * @param dynamicLightLevel The dynamic light level. + * @param lightmap The vanilla lightmap. * @return The modified lightmap. */ - public int getLightmapWithDynamicLight(double dynamicLuminance, int lightmap) + public int getLightmapWithDynamicLight(double dynamicLightLevel, int lightmap) { - if (dynamicLuminance > 0) { + if (dynamicLightLevel > 0) { // lightmap is (skyLevel << 20 | blockLevel << 4) // Get vanilla block light level. int blockLevel = LightmapTextureManager.getBlockLightCoordinates(lightmap); - if (dynamicLuminance > blockLevel) { + if (dynamicLightLevel > blockLevel) { // Equivalent to a << 4 bitshift with a little quirk: this one ensure more precision (more decimals are saved). - int luminance = (int) (dynamicLuminance * 16.0); + int luminance = (int) (dynamicLightLevel * 16.0); lightmap &= 0xfff00000; lightmap |= luminance & 0x000fffff; } @@ -178,11 +178,11 @@ public int getLightmapWithDynamicLight(double dynamicLuminance, int lightmap) * @param pos The position. * @return The dynamic light level at the spec */ - public double getDynamicLuminance(@NotNull BlockPos pos) + public double getDynamicLightLevel(@NotNull BlockPos pos) { double result = 0; for (DynamicLightSource lightSource : this.dynamicLightSources) { - result = maxDynamicLuminance(pos, lightSource, result); + result = maxDynamicLightLevel(pos, lightSource, result); } return MathHelper.clamp(result, 0, 15); @@ -191,12 +191,12 @@ public double getDynamicLuminance(@NotNull BlockPos pos) /** * Returns the dynamic light level generated by the light source at the specified position. * - * @param pos The position. - * @param lightSource The light source. - * @param currentLuminance The current surrounding dynamic luminance. + * @param pos The position. + * @param lightSource The light source. + * @param currentLightLevel The current surrounding dynamic light level. * @return The dynamic light level. */ - public static double maxDynamicLuminance(@NotNull BlockPos pos, @NotNull DynamicLightSource lightSource, double currentLuminance) + public static double maxDynamicLightLevel(@NotNull BlockPos pos, @NotNull DynamicLightSource lightSource, double currentLightLevel) { int luminance = lightSource.getLuminance(); if (luminance > 0) { @@ -211,12 +211,12 @@ public static double maxDynamicLuminance(@NotNull BlockPos pos, @NotNull Dynamic if (distance <= MAX_RADIUS) { double multiplier = 1.0 - distance / MAX_RADIUS; double lightLevel = multiplier * (double) luminance; - if (lightLevel > currentLuminance) { - currentLuminance = lightLevel; + if (lightLevel > currentLightLevel) { + currentLightLevel = lightLevel; } } } - return currentLuminance; + return currentLightLevel; } /** diff --git a/src/main/java/me/lambdaurora/lambdynlights/LambDynLightsCompat.java b/src/main/java/me/lambdaurora/lambdynlights/LambDynLightsCompat.java index 42188a1..28e4f84 100644 --- a/src/main/java/me/lambdaurora/lambdynlights/LambDynLightsCompat.java +++ b/src/main/java/me/lambdaurora/lambdynlights/LambDynLightsCompat.java @@ -15,7 +15,7 @@ * Represents a utility class for compatibility. * * @author LambdAurora - * @version 1.3.0 + * @version 1.3.1 * @since 1.0.0 */ public final class LambDynLightsCompat @@ -50,4 +50,15 @@ public static boolean isSodiumInstalled() { return FabricLoader.getInstance().isModLoaded("sodium"); } + + /** + * Returns whether Sodium 0.1.0 is installed. + * + * @return True if Sodium 0.1.0 is installed, else false. + */ + public static boolean isSodium010Installed() + { + return FabricLoader.getInstance().getModContainer("sodium").map(mod -> mod.getMetadata().getVersion().getFriendlyString().equalsIgnoreCase("0.1.0")) + .orElse(false); + } } diff --git a/src/main/java/me/lambdaurora/lambdynlights/LambDynLightsMixinPlugin.java b/src/main/java/me/lambdaurora/lambdynlights/LambDynLightsMixinPlugin.java index 75e3925..7acb8f1 100644 --- a/src/main/java/me/lambdaurora/lambdynlights/LambDynLightsMixinPlugin.java +++ b/src/main/java/me/lambdaurora/lambdynlights/LambDynLightsMixinPlugin.java @@ -21,7 +21,7 @@ * LambDynamicLights mixin plugin for conditional mixins. * * @author LambdAurora - * @version 1.3.0 + * @version 1.3.1 * @since 1.0.0 */ public class LambDynLightsMixinPlugin implements IMixinConfigPlugin @@ -37,6 +37,7 @@ public LambDynLightsMixinPlugin() boolean canvasInstalled = LambDynLightsCompat.isCanvasInstalled(); boolean sodiumInstalled = LambDynLightsCompat.isSodiumInstalled(); this.conditionalMixins.put("me.lambdaurora.lambdynlights.mixin.WorldRendererMixin", !sodiumInstalled && !canvasInstalled); + this.conditionalMixins.put("me.lambdaurora.lambdynlights.mixin.EntityLighterMixin", LambDynLightsCompat.isSodium010Installed()); } @Override diff --git a/src/main/java/me/lambdaurora/lambdynlights/api/item/ItemLightSource.java b/src/main/java/me/lambdaurora/lambdynlights/api/item/ItemLightSource.java index a28caf5..0c922c7 100644 --- a/src/main/java/me/lambdaurora/lambdynlights/api/item/ItemLightSource.java +++ b/src/main/java/me/lambdaurora/lambdynlights/api/item/ItemLightSource.java @@ -16,18 +16,20 @@ import net.minecraft.block.Blocks; import net.minecraft.item.BlockItem; import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; import net.minecraft.item.Items; import net.minecraft.util.Identifier; import net.minecraft.util.registry.Registry; import org.jetbrains.annotations.NotNull; +import java.util.Objects; import java.util.Optional; /** * Represents an item light source. * * @author LambdAurora - * @version 1.3.0 + * @version 1.3.1 * @since 1.3.0 */ public class ItemLightSource @@ -50,7 +52,14 @@ public ItemLightSource(@NotNull Identifier id, @NotNull Item item, int luminance this.waterSensitive = waterSensitive; } - public int getLuminance(boolean submergedInWater) + /** + * Gets the luminance of the item. + * + * @param stack The item stack. + * @param submergedInWater True if submerged in water, else false. + * @return The luminance value between 0 and 15. + */ + public int getLuminance(@NotNull ItemStack stack, boolean submergedInWater) { if (this.waterSensitive && LambDynLights.get().config.hasWaterSensitiveCheck() && submergedInWater) return 0; // Don't emit light with water sensitive items while submerged in water. @@ -58,6 +67,24 @@ public int getLuminance(boolean submergedInWater) return this.luminance; } + @Override + public boolean equals(Object o) + { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + ItemLightSource that = (ItemLightSource) o; + return luminance == that.luminance && + waterSensitive == that.waterSensitive && + Objects.equals(id, that.id) && + Objects.equals(item, that.item); + } + + @Override + public int hashCode() + { + return Objects.hash(id, item, luminance, waterSensitive); + } + @Override public String toString() { diff --git a/src/main/java/me/lambdaurora/lambdynlights/api/item/ItemLightSources.java b/src/main/java/me/lambdaurora/lambdynlights/api/item/ItemLightSources.java index 764d6bb..d52170f 100644 --- a/src/main/java/me/lambdaurora/lambdynlights/api/item/ItemLightSources.java +++ b/src/main/java/me/lambdaurora/lambdynlights/api/item/ItemLightSources.java @@ -30,12 +30,13 @@ * Represents an item light sources manager. * * @author LambdAurora - * @version 1.3.0 + * @version 1.3.1 * @since 1.3.0 */ public final class ItemLightSources { - private static final List ITEM_LIGHT_SOURCES = new ArrayList<>(); + private static final List ITEM_LIGHT_SOURCES = new ArrayList<>(); + private static final List STATIC_ITEM_LIGHT_SOURCES = new ArrayList<>(); /** * Loads the item light source data from resource pack. @@ -47,6 +48,8 @@ public static void load(@NotNull ResourceManager resourceManager) ITEM_LIGHT_SOURCES.clear(); resourceManager.findResources("dynamiclights/item", path -> path.endsWith(".json")).forEach(id -> load(resourceManager, id)); + + ITEM_LIGHT_SOURCES.addAll(STATIC_ITEM_LIGHT_SOURCES); } private static void load(@NotNull ResourceManager resourceManager, @NotNull Identifier resourceId) @@ -61,7 +64,10 @@ private static void load(@NotNull ResourceManager resourceManager, @NotNull Iden return; } - register(result.get()); + ItemLightSource data = result.get(); + if (STATIC_ITEM_LIGHT_SOURCES.contains(data)) + return; + register(data); } catch (IOException | IllegalStateException e) { LambDynLights.get().warn("Failed to load item light source \"" + id + "\"."); } @@ -72,7 +78,7 @@ private static void load(@NotNull ResourceManager resourceManager, @NotNull Iden * * @param data The item light source data. */ - public static void register(@NotNull ItemLightSource data) + private static void register(@NotNull ItemLightSource data) { for (ItemLightSource other : ITEM_LIGHT_SOURCES) { if (other.item == data.item) { @@ -85,6 +91,24 @@ public static void register(@NotNull ItemLightSource data) ITEM_LIGHT_SOURCES.add(data); } + /** + * Registers an item light source data. + * + * @param data The item light source data. + */ + public static void registerItemLightSource(@NotNull ItemLightSource data) + { + for (ItemLightSource other : STATIC_ITEM_LIGHT_SOURCES) { + if (other.item == data.item) { + LambDynLights.get().warn("Failed to register item light source \"" + data.id + "\", duplicates item \"" + + Registry.ITEM.getId(data.item) + "\" found in \"" + other.id + "\"."); + return; + } + } + + STATIC_ITEM_LIGHT_SOURCES.add(data); + } + /** * Returns the luminance of the item in the stack. * @@ -96,7 +120,7 @@ public static int getLuminance(@NotNull ItemStack stack, boolean submergedInWate { for (ItemLightSource data : ITEM_LIGHT_SOURCES) { if (data.item == stack.getItem()) { - return data.getLuminance(submergedInWater); + return data.getLuminance(stack, submergedInWater); } } if (stack.getItem() instanceof BlockItem) diff --git a/src/main/java/me/lambdaurora/lambdynlights/mixin/EntityLighterMixin.java b/src/main/java/me/lambdaurora/lambdynlights/mixin/EntityLighterMixin.java new file mode 100644 index 0000000..2bfad42 --- /dev/null +++ b/src/main/java/me/lambdaurora/lambdynlights/mixin/EntityLighterMixin.java @@ -0,0 +1,44 @@ +/* + * Copyright © 2020 LambdAurora + * + * This file is part of LambDynamicLights. + * + * Licensed under the MIT license. For more information, + * see the LICENSE file. + */ + +package me.lambdaurora.lambdynlights.mixin; + +import me.jellysquid.mods.sodium.client.model.light.EntityLighter; +import me.lambdaurora.lambdynlights.LambDynLights; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.LightType; +import net.minecraft.world.World; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Redirect; + +@Mixin(value = EntityLighter.class) +public class EntityLighterMixin +{ + @Redirect( + method = "getBlendedLight", + at = @At( + value = "INVOKE", + target = "Lnet/minecraft/world/World;getLightLevel(Lnet/minecraft/world/LightType;Lnet/minecraft/util/math/BlockPos;)I", + ordinal = 1 + ) + ) + private static int onGetWorldLight(World world, LightType type, BlockPos pos) + { + /* + TO BE REMOVED IN FUTURE VERSIONS + + This is only here to keep compatibility with Sodium 0.1.0. + In newer versions this is broken and use EntityRendererMixin instead (which is much simpler) + */ + int vanilla = world.getLightLevel(type, pos); + double luminance = LambDynLights.get().getDynamicLightLevel(pos); + return (int) Math.max(vanilla, Math.round(luminance)); + } +} diff --git a/src/main/java/me/lambdaurora/lambdynlights/mixin/EntityRendererMixin.java b/src/main/java/me/lambdaurora/lambdynlights/mixin/EntityRendererMixin.java index 4fe86be..6c3b1d3 100644 --- a/src/main/java/me/lambdaurora/lambdynlights/mixin/EntityRendererMixin.java +++ b/src/main/java/me/lambdaurora/lambdynlights/mixin/EntityRendererMixin.java @@ -29,7 +29,7 @@ private void onGetBlockLight(T entity, BlockPos pos, CallbackInfoReturnable=0.9.0", "fabric": ">=0.14.0", "minecraft": ">=1.16.2", - "spruceui": ">=1.6.2" + "spruceui": ">=1.6.4" }, "recommends": { "modmenu": ">=1.14.0" }, "suggests": { "flamingo": "*", - "ltr": "*" + "ltr": "*", + "sodium": "*", + "canvas": "*" }, "breaks": { "modmenu": "<1.14.0", diff --git a/src/main/resources/lambdynlights.mixins.json b/src/main/resources/lambdynlights.mixins.json index 7f1e45d..f7b26c9 100644 --- a/src/main/resources/lambdynlights.mixins.json +++ b/src/main/resources/lambdynlights.mixins.json @@ -5,6 +5,7 @@ "compatibilityLevel": "JAVA_8", "client": [ "CommonWorldRendererMixin", + "EntityLighterMixin", "EntityRendererMixin", "VideoOptionsScreenMixin", "WorldMixin",