diff --git a/build.gradle b/build.gradle index 861bbc23..0724ddb6 100644 --- a/build.gradle +++ b/build.gradle @@ -93,6 +93,17 @@ repositories { name 'grondag' url 'https://maven.dblsaiko.net/' } + exclusiveContent { + forRepository { + maven { + name "Modrinth" + url "https://api.modrinth.com/maven" + } + } + filter { + includeGroup "maven.modrinth" + } + } } configurations { @@ -119,6 +130,8 @@ dependencies { transitive = false } + modImplementation "maven.modrinth:sodium:${project.sodium_version}" + shadow 'com.electronwill.night-config:core:3.6.6' shadow 'com.electronwill.night-config:toml:3.6.6' } diff --git a/gradle.properties b/gradle.properties index 4ad13179..54fee9c6 100644 --- a/gradle.properties +++ b/gradle.properties @@ -21,3 +21,4 @@ curseforge_id=393442 spruceui_version=5.0.0+1.20 pridelib_version=1.2.0+1.19.4 modmenu_version=7.0.1 +sodium_version=mc1.20.1-0.5.1 \ No newline at end of file diff --git a/src/main/java/dev/lambdaurora/lambdynlights/LambDynLightsCompat.java b/src/main/java/dev/lambdaurora/lambdynlights/LambDynLightsCompat.java index 7c220941..7a2801fd 100644 --- a/src/main/java/dev/lambdaurora/lambdynlights/LambDynLightsCompat.java +++ b/src/main/java/dev/lambdaurora/lambdynlights/LambDynLightsCompat.java @@ -10,6 +10,8 @@ package dev.lambdaurora.lambdynlights; import net.fabricmc.loader.api.FabricLoader; +import net.fabricmc.loader.api.Version; +import net.fabricmc.loader.api.VersionParsingException; /** * Represents a utility class for compatibility. @@ -47,4 +49,14 @@ public static boolean isSodium010Installed() { return FabricLoader.getInstance().getModContainer("sodium").map(mod -> mod.getMetadata().getVersion().getFriendlyString().startsWith("0.1.0")) .orElse(false); } + + public static boolean isSodium05XInstalled() { + return FabricLoader.getInstance().getModContainer("sodium").map(mod -> { + try { + return mod.getMetadata().getVersion().compareTo(Version.parse("0.5.0")) >= 0; + } catch (VersionParsingException e) { + throw new RuntimeException(e); + } + }).orElse(false); + } } diff --git a/src/main/java/dev/lambdaurora/lambdynlights/LambDynLightsMixinPlugin.java b/src/main/java/dev/lambdaurora/lambdynlights/LambDynLightsMixinPlugin.java index 0f8c793f..e32bdb02 100644 --- a/src/main/java/dev/lambdaurora/lambdynlights/LambDynLightsMixinPlugin.java +++ b/src/main/java/dev/lambdaurora/lambdynlights/LambDynLightsMixinPlugin.java @@ -32,6 +32,11 @@ public LambDynLightsMixinPlugin() { boolean ltrInstalled = LambDynLightsCompat.isLilTaterReloadedInstalled(); this.conditionalMixins.put("dev.lambdaurora.lambdynlights.mixin.ltr.LilTaterBlocksMixin", ltrInstalled); this.conditionalMixins.put("dev.lambdaurora.lambdynlights.mixin.ltr.LilTaterBlockEntityMixin", ltrInstalled); + + boolean sodium05XInstalled = LambDynLightsCompat.isSodium05XInstalled(); + this.conditionalMixins.put("dev.lambdaurora.lambdynlights.mixin.sodium.ArrayLightDataCache", sodium05XInstalled); + this.conditionalMixins.put("dev.lambdaurora.lambdynlights.mixin.sodium.FlatLightPipelineMixin", sodium05XInstalled); + this.conditionalMixins.put("dev.lambdaurora.lambdynlights.mixin.sodium.LightDataAccessMixin", sodium05XInstalled); } @Override diff --git a/src/main/java/dev/lambdaurora/lambdynlights/mixin/sodium/ArrayLightDataCacheMixin.java b/src/main/java/dev/lambdaurora/lambdynlights/mixin/sodium/ArrayLightDataCacheMixin.java new file mode 100644 index 00000000..00a06908 --- /dev/null +++ b/src/main/java/dev/lambdaurora/lambdynlights/mixin/sodium/ArrayLightDataCacheMixin.java @@ -0,0 +1,36 @@ +/* + * Copyright © 2023 LambdAurora + * + * This file is part of LambDynamicLights. + * + * Licensed under the MIT license. For more information, + * see the LICENSE file. + */ + +package dev.lambdaurora.lambdynlights.mixin.sodium; + +import dev.lambdaurora.lambdynlights.LambDynLights; +import dev.lambdaurora.lambdynlights.util.SodiumDynamicLightHandler; +import me.jellysquid.mods.sodium.client.model.light.data.LightDataAccess; +import org.spongepowered.asm.mixin.Dynamic; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Pseudo; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +@Pseudo +@Mixin(targets = "me.jellysquid.mods.sodium.client.model.light.data.ArrayLightDataCache", remap = false) +public abstract class ArrayLightDataCacheMixin extends LightDataAccess { + @Dynamic + @Inject(method = "get(III)I", at = @At("HEAD")) + private void lambdynlights$storeLightPos(int x, int y, int z, CallbackInfoReturnable cir) { + if (!LambDynLights.get().config.getDynamicLightsMode().isEnabled()) + return; + + // Store the current light position. + // This is possible under smooth lighting scenarios, because AoFaceData in Sodium runs a get() call + // before getting the lightmap. + SodiumDynamicLightHandler.lambdynlights$pos.get().set(x, y, z); + } +} diff --git a/src/main/java/dev/lambdaurora/lambdynlights/mixin/sodium/FlatLightPipelineMixin.java b/src/main/java/dev/lambdaurora/lambdynlights/mixin/sodium/FlatLightPipelineMixin.java new file mode 100644 index 00000000..48ab66fe --- /dev/null +++ b/src/main/java/dev/lambdaurora/lambdynlights/mixin/sodium/FlatLightPipelineMixin.java @@ -0,0 +1,32 @@ +/* + * Copyright © 2023 LambdAurora + * + * This file is part of LambDynamicLights. + * + * Licensed under the MIT license. For more information, + * see the LICENSE file. + */ + +package dev.lambdaurora.lambdynlights.mixin.sodium; + +import dev.lambdaurora.lambdynlights.util.SodiumDynamicLightHandler; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Direction; +import org.spongepowered.asm.mixin.Dynamic; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Pseudo; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; +import org.spongepowered.asm.mixin.injection.callback.LocalCapture; + +@Pseudo +@Mixin(targets = "me.jellysquid.mods.sodium.client.model.light.flat.FlatLightPipeline", remap = false) +public abstract class FlatLightPipelineMixin { + @Dynamic + @Inject(method = "getOffsetLightmap", at = @At(value = "RETURN", ordinal = 1), remap = false, locals = LocalCapture.CAPTURE_FAILHARD, cancellable = true) + private void lambdynlights$getLightmap(BlockPos pos, Direction face, CallbackInfoReturnable cir, int word, int adjWord) { + int lightmap = SodiumDynamicLightHandler.lambdynlights$getLightmap(pos, adjWord, cir.getReturnValueI()); + cir.setReturnValue(lightmap); + } +} diff --git a/src/main/java/dev/lambdaurora/lambdynlights/mixin/sodium/LightDataAccessMixin.java b/src/main/java/dev/lambdaurora/lambdynlights/mixin/sodium/LightDataAccessMixin.java new file mode 100644 index 00000000..22ed98ec --- /dev/null +++ b/src/main/java/dev/lambdaurora/lambdynlights/mixin/sodium/LightDataAccessMixin.java @@ -0,0 +1,29 @@ +/* + * Copyright © 2023 LambdAurora + * + * This file is part of LambDynamicLights. + * + * Licensed under the MIT license. For more information, + * see the LICENSE file. + */ + +package dev.lambdaurora.lambdynlights.mixin.sodium; + +import dev.lambdaurora.lambdynlights.util.SodiumDynamicLightHandler; +import org.spongepowered.asm.mixin.Dynamic; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Pseudo; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +@Pseudo +@Mixin(targets = "me.jellysquid.mods.sodium.client.model.light.data.LightDataAccess", remap = false) +public abstract class LightDataAccessMixin { + @Dynamic + @Inject(method = "getLightmap", at = @At("RETURN"), remap = false, cancellable = true) + private static void lambdynlights$getLightmap(int word, CallbackInfoReturnable cir) { + int lightmap = SodiumDynamicLightHandler.lambdynlights$getLightmap(SodiumDynamicLightHandler.lambdynlights$pos.get(), word, cir.getReturnValueI()); + cir.setReturnValue(lightmap); + } +} diff --git a/src/main/java/dev/lambdaurora/lambdynlights/util/SodiumDynamicLightHandler.java b/src/main/java/dev/lambdaurora/lambdynlights/util/SodiumDynamicLightHandler.java new file mode 100644 index 00000000..f4f9cc00 --- /dev/null +++ b/src/main/java/dev/lambdaurora/lambdynlights/util/SodiumDynamicLightHandler.java @@ -0,0 +1,35 @@ +/* + * Copyright © 2023 LambdAurora + * + * This file is part of LambDynamicLights. + * + * Licensed under the MIT license. For more information, + * see the LICENSE file. + */ + +package dev.lambdaurora.lambdynlights.util; + +import dev.lambdaurora.lambdynlights.LambDynLights; +import me.jellysquid.mods.sodium.client.model.light.data.LightDataAccess; +import net.minecraft.util.math.BlockPos; +import org.jetbrains.annotations.ApiStatus; + +@ApiStatus.Internal +public interface SodiumDynamicLightHandler { + // Stores the current light position being used by ArrayLightDataCache#get + // We use ThreadLocal because Sodium's chunk builder is multithreaded, otherwise it will break + // catastrophically. + ThreadLocal lambdynlights$pos = ThreadLocal.withInitial(BlockPos.Mutable::new); + + static int lambdynlights$getLightmap(BlockPos pos, int word, int lightmap) { + if (!LambDynLights.get().config.getDynamicLightsMode().isEnabled()) + return lightmap; + + // Equivalent to world.getBlockState(pos).isOpaqueFullCube(world, pos) + if (LightDataAccess.unpackFO(word)) + return lightmap; + + double dynamic = LambDynLights.get().getDynamicLightLevel(pos); + return LambDynLights.get().getLightmapWithDynamicLight(dynamic, lightmap); + } +} diff --git a/src/main/resources/lambdynlights.mixins.json b/src/main/resources/lambdynlights.mixins.json index dc524e03..680bd786 100644 --- a/src/main/resources/lambdynlights.mixins.json +++ b/src/main/resources/lambdynlights.mixins.json @@ -7,14 +7,17 @@ "BlockEntityTypeMixin", "ClientWorldMixin", "CommonWorldRendererMixin", - "EntityTypeMixin", "DebugHudMixin", "EntityRendererMixin", + "EntityTypeMixin", "MinecraftClientMixin", "VideoOptionsScreenMixin", "WorldMixin", "ltr.LilTaterBlockEntityMixin", - "ltr.LilTaterBlocksMixin" + "ltr.LilTaterBlocksMixin", + "sodium.ArrayLightDataCacheMixin", + "sodium.FlatLightPipelineMixin", + "sodium.LightDataAccessMixin" ], "injectors": { "defaultRequire": 1