diff --git a/build.gradle b/build.gradle index 42a66cd42..9a888cda8 100644 --- a/build.gradle +++ b/build.gradle @@ -50,6 +50,11 @@ allprojects { maven { url = "https://modmaven.dev/" } + maven { + name "Modding Legacy Maven" + url "https://maven.moddinglegacy.com/artifactory/modding-legacy/" + } + flatDir { dirs 'deps' @@ -84,6 +89,8 @@ allprojects { api fg.deobf("curse.maven:mekanism-268560:${project.mekanism_fileid}") // Immersive Engineering api fg.deobf("curse.maven:immersiveengineering-231951:${project.immersiveengineering_fileid}") + // Lucent + api fg.deobf("com.legacy:lucent:${project.lucent_version}") compileOnly 'com.demonwav.mcdev:annotations:1.0' } diff --git a/gradle.properties b/gradle.properties index e979795ba..fc07570fc 100644 --- a/gradle.properties +++ b/gradle.properties @@ -4,7 +4,7 @@ org.gradle.parallel=false org.gradle.daemon=false # Mod Properties -mod_version=1.4.15 +mod_version=1.4.17 maven_group=qouteall archives_base_name=immersive-portals @@ -28,3 +28,4 @@ mantle_fileid=4353120 sereneseasons_fileid=3693807 mekanism_fileid=3875976 immersiveengineering_fileid=4412849 +lucent_version=1.18.2-1.2.2 diff --git a/imm_ptl_core/src/main/java/qouteall/imm_ptl/core/ClientWorldLoader.java b/imm_ptl_core/src/main/java/qouteall/imm_ptl/core/ClientWorldLoader.java index 8f4750c49..0b9fe3120 100644 --- a/imm_ptl_core/src/main/java/qouteall/imm_ptl/core/ClientWorldLoader.java +++ b/imm_ptl_core/src/main/java/qouteall/imm_ptl/core/ClientWorldLoader.java @@ -273,6 +273,23 @@ public static ClientLevel getWorld(ResourceKey dimension) { return clientWorldMap.get(dimension); } + + /** + * Get the client world and create if missing. + * If the dimension id is invalid, it will throw an error + * Use {@link #getWorld(ResourceKey)} instead if possible. + */ + public static synchronized ClientLevel getWorldAsync(ResourceKey dimension) { + Validate.notNull(dimension); + + initializeIfNeeded(); + + if (!clientWorldMap.containsKey(dimension)) { + return createSecondaryClientWorld(dimension); + } + + return clientWorldMap.get(dimension); + } /** * Get the client world and create if missing. diff --git a/imm_ptl_core/src/main/java/qouteall/imm_ptl/core/compat/IPCompatMixinPlugin.java b/imm_ptl_core/src/main/java/qouteall/imm_ptl/core/compat/IPCompatMixinPlugin.java index f55e63c66..689eebb76 100644 --- a/imm_ptl_core/src/main/java/qouteall/imm_ptl/core/compat/IPCompatMixinPlugin.java +++ b/imm_ptl_core/src/main/java/qouteall/imm_ptl/core/compat/IPCompatMixinPlugin.java @@ -43,6 +43,11 @@ public boolean shouldApplyMixin(String targetClassName, String mixinClassName) { boolean flywheelLoaded = LoadingModList.get().getModFileById("flywheel") != null; return flywheelLoaded; } + + if (mixinClassName.contains("Lucent")) { + boolean lucentLoaded = LoadingModList.get().getModFileById("lucent") != null; + return lucentLoaded; + } return false; } @@ -66,4 +71,5 @@ public void preApply(String targetClassName, ClassNode targetClass, String mixin public void postApply(String targetClassName, ClassNode targetClass, String mixinClassName, IMixinInfo mixinInfo) { } + } diff --git a/imm_ptl_core/src/main/java/qouteall/imm_ptl/core/compat/LucentCompat.java b/imm_ptl_core/src/main/java/qouteall/imm_ptl/core/compat/LucentCompat.java new file mode 100644 index 000000000..25ea9e3dc --- /dev/null +++ b/imm_ptl_core/src/main/java/qouteall/imm_ptl/core/compat/LucentCompat.java @@ -0,0 +1,30 @@ +package qouteall.imm_ptl.core.compat; + +import com.legacy.lucent.api.plugin.ILucentPlugin; +import com.legacy.lucent.api.plugin.LucentPlugin; +import com.legacy.lucent.api.registry.EntityLightSourcePosRegistry; +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.phys.Vec3; +import qouteall.imm_ptl.core.platform_specific.IPModEntry; +import qouteall.imm_ptl.core.render.context_management.RenderStates; + +@LucentPlugin +public class LucentCompat implements ILucentPlugin { + + @Override + public String ownerModID() { + return IPModEntry.MODID; + } + + @Override + public int getPriority() { + return 10; + } + + @Override + public void registerEntityLightSourcePositionGetter(EntityLightSourcePosRegistry registry) { + registry.register(EntityType.PLAYER, player -> new Vec3(RenderStates.originalPlayerPos.x(), + RenderStates.originalPlayerBoundingBox.getYsize() / 2.0 + RenderStates.originalPlayerPos.y(), + RenderStates.originalPlayerPos.z())); + } +} diff --git a/imm_ptl_core/src/main/java/qouteall/imm_ptl/core/compat/mixin/MixinLucent.java b/imm_ptl_core/src/main/java/qouteall/imm_ptl/core/compat/mixin/MixinLucent.java new file mode 100644 index 000000000..f0de54fe4 --- /dev/null +++ b/imm_ptl_core/src/main/java/qouteall/imm_ptl/core/compat/mixin/MixinLucent.java @@ -0,0 +1,125 @@ +package qouteall.imm_ptl.core.compat.mixin; + +import com.legacy.lucent.api.LucentData; +import com.legacy.lucent.core.dynamic_lighting.DynamicLightingEngine; +import com.legacy.lucent.core.dynamic_lighting.LightData; +import net.minecraft.client.Minecraft; +import net.minecraft.client.multiplayer.ClientLevel; +import net.minecraft.client.player.LocalPlayer; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.core.SectionPos; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.level.BlockAndTintGetter; +import net.minecraft.world.level.ClipContext; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.LightLayer; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.phys.AABB; +import net.minecraft.world.phys.BlockHitResult; +import net.minecraft.world.phys.Vec3; +import net.minecraft.world.phys.shapes.Shapes; +import net.minecraft.world.phys.shapes.VoxelShape; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.*; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; +import qouteall.imm_ptl.core.ClientWorldLoader; +import qouteall.imm_ptl.core.render.context_management.RenderStates; + +import java.util.List; +import java.util.Map; + +// TODO @Nick1st Optimize this, by reducing the mixins if possible, as they add some overhead. +@Mixin(value = DynamicLightingEngine.class, remap = false) +public abstract class MixinLucent { + + @Shadow + private static VoxelShape getShape(BlockState state, BlockPos pos, Direction dir) { + return null; + } + + + @Shadow private static Map lightData; + + // Make sure the getShape Helper method gets the right level + @Inject(method = "getShape", at = @At("HEAD"), cancellable = true) + private static void patchedGetShape(BlockState state, BlockPos pos, Direction dir, CallbackInfoReturnable cir) { + ClientLevel level = ClientWorldLoader.getWorldAsync(RenderStates.originalPlayerDimension); + VoxelShape shape = (state.getLightBlock(level, pos) < 15 && !state.canOcclude()) + || !state.getMaterial().isSolid() ? Shapes.empty() : state.getFaceOcclusionShape(level, pos, dir); + cir.setReturnValue(shape); + cir.cancel(); + } + + // Make sure the can lightPass occlusion test runs in the right level + @Inject(method = "canLightPass", at = @At("HEAD"), cancellable = true) + private static void patchedCanLightPass(BlockPos currentPos, BlockPos relativePos, Direction dir, CallbackInfoReturnable cir) { + ClientLevel level = ClientWorldLoader.getWorldAsync(RenderStates.originalPlayerDimension); + boolean canLightPass = !Shapes.faceShapeOccludes(getShape(level.getBlockState(currentPos), currentPos, dir), getShape(level.getBlockState(relativePos), relativePos, dir.getOpposite())); + cir.setReturnValue(canLightPass); + cir.cancel(); + } + + // Make sure Lucent gets the entities from the right level + @Redirect(method = "tick", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/multiplayer/ClientLevel;getEntitiesOfClass(Ljava/lang/Class;Lnet/minecraft/world/phys/AABB;)Ljava/util/List;"), remap = true) + private static List getAllEntitiesFromTheRightDimension(ClientLevel instance, Class entityClass, AABB aabb) { + aabb = RenderStates.originalPlayerBoundingBox.inflate(LucentData.maxVisibleDistance); + List allEntities = ClientWorldLoader.getWorldAsync(RenderStates.originalPlayerDimension).getEntitiesOfClass(entityClass, aabb); + if (!allEntities.contains(Minecraft.getInstance().player)) { + allEntities.add(Minecraft.getInstance().player); + } + return allEntities; + } + + //Give Lucent the correct player position + @ModifyArg(method = "getEntityLightLevel", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/ClipContext;(Lnet/minecraft/world/phys/Vec3;Lnet/minecraft/world/phys/Vec3;Lnet/minecraft/world/level/ClipContext$Block;Lnet/minecraft/world/level/ClipContext$Fluid;Lnet/minecraft/world/entity/Entity;)V"), index = 0) + private static Vec3 truePlayerEyePosition(Vec3 pFrom) { + return new Vec3(RenderStates.originalPlayerPos.x, RenderStates.originalPlayerPos.y + Minecraft.getInstance().player.getEyeY(), RenderStates.originalPlayerPos.z); // TODO @Nick1st Make sure I can get the EyeHeight like this + } + + // Give Lucent the correct level to clip + @Redirect(method = "getEntityLightLevel", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/Level;clip(Lnet/minecraft/world/level/ClipContext;)Lnet/minecraft/world/phys/BlockHitResult;"), remap = true) + private static BlockHitResult clipWithRightPosition(Level instance, ClipContext clipContext) { + return ClientWorldLoader.getWorldAsync(RenderStates.originalPlayerDimension).clip(clipContext); + } + + // Calculate the distance correctly. + @Redirect(method = "getEntityLightLevel", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/player/LocalPlayer;distanceTo(Lnet/minecraft/world/entity/Entity;)F"), remap = true) + private static float correctAlwaysVisibleDistanceCheck(LocalPlayer instance, Entity entity) { + Vec3 entityPos = new Vec3(entity.getX(), entity.getY(), entity.getZ()); + if (entity instanceof LocalPlayer) { + entityPos = RenderStates.originalPlayerPos; + } + return (float) RenderStates.originalPlayerPos.distanceTo(entityPos); + } + + // Calculate the correct Skylight value + @Redirect(method = "getEntityLightLevel", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/multiplayer/ClientLevel;getBrightness(Lnet/minecraft/world/level/LightLayer;Lnet/minecraft/core/BlockPos;)I"), remap = true) + private static int correctSkyLight(ClientLevel instance, LightLayer lightLayer, BlockPos blockPos) { + return ClientWorldLoader.getWorldAsync(RenderStates.originalPlayerDimension).getBrightness(lightLayer, blockPos); + } + + // Set the player level renderer dirty instead of a random one + @Inject(method = "setDirty", at = @At(value = "INVOKE", target = "Lcom/legacy/lucent/core/LucentClient;setDirty(Lnet/minecraft/core/SectionPos;)V"), cancellable = true) + private static void setDirtyPatched(SectionPos section, CallbackInfo ci) { + ClientWorldLoader.getWorldRenderer(RenderStates.originalPlayerDimension).setSectionDirty(section.getX(), section.getY(), section.getZ()); + ci.cancel(); + } + + // Fixes the level used to query the skylight + @ModifyVariable(method = "calcLight", at = @At(value = "HEAD"), argsOnly = true) + private static BlockAndTintGetter getCorrectSkylightLevel(BlockAndTintGetter level) { + return ClientWorldLoader.getWorldAsync(RenderStates.originalPlayerDimension); + } + + // Fix a race condition if the Renderstate originalPlayerDimension is not yet set. + @Inject(method = "blockChanged", at = @At("HEAD"), cancellable = true) + private static void injectNullCheck(BlockPos pos, CallbackInfo ci) { + if (RenderStates.originalPlayerDimension == null) { + ci.cancel(); + } + } + +} diff --git a/imm_ptl_core/src/main/java/qouteall/imm_ptl/core/compat/mixin/MixinLucentChunkRenderDispatcherHooks.java b/imm_ptl_core/src/main/java/qouteall/imm_ptl/core/compat/mixin/MixinLucentChunkRenderDispatcherHooks.java new file mode 100644 index 000000000..9378b52d1 --- /dev/null +++ b/imm_ptl_core/src/main/java/qouteall/imm_ptl/core/compat/mixin/MixinLucentChunkRenderDispatcherHooks.java @@ -0,0 +1,19 @@ +package qouteall.imm_ptl.core.compat.mixin; + +import com.legacy.lucent.core.asm_hooks.ChunkRenderDispatcherHooks; +import net.minecraft.core.SectionPos; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Redirect; +import qouteall.imm_ptl.core.ClientWorldLoader; +import qouteall.imm_ptl.core.render.context_management.RenderStates; + +@Mixin(value = ChunkRenderDispatcherHooks.class, remap = false) +public class MixinLucentChunkRenderDispatcherHooks { + + // Set the correct levelRenderer dirty + @Redirect(method = "tick", at = @At(value = "INVOKE", target = "Lcom/legacy/lucent/core/LucentClient;setDirty(Lnet/minecraft/core/SectionPos;)V")) + private static void setDirty(SectionPos section) { + ClientWorldLoader.getWorldRenderer(RenderStates.originalPlayerDimension).setSectionDirty(section.getX(), section.getY(), section.getZ()); + } +} diff --git a/imm_ptl_core/src/main/java/qouteall/imm_ptl/core/compat/mixin/MixinLucentLevelRenderer.java b/imm_ptl_core/src/main/java/qouteall/imm_ptl/core/compat/mixin/MixinLucentLevelRenderer.java new file mode 100644 index 000000000..e2cd49b00 --- /dev/null +++ b/imm_ptl_core/src/main/java/qouteall/imm_ptl/core/compat/mixin/MixinLucentLevelRenderer.java @@ -0,0 +1,36 @@ +package qouteall.imm_ptl.core.compat.mixin; + +import net.minecraft.client.multiplayer.ClientLevel; +import net.minecraft.client.renderer.LevelRenderer; +import net.minecraft.client.renderer.chunk.RenderChunkRegion; +import net.minecraft.core.BlockPos; +import net.minecraft.world.level.BlockAndTintGetter; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.api.distmarker.OnlyIn; +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.CallbackInfoReturnable; +import qouteall.imm_ptl.core.render.context_management.RenderStates; + +@OnlyIn(Dist.CLIENT) +@Mixin(value = LevelRenderer.class, priority = 900) +public class MixinLucentLevelRenderer { + + // Inject before Lucent and return if the level doesn't fit. + @Inject(at = @At(value = "RETURN", ordinal = 1), method = "getLightColor(Lnet/minecraft/world/level/BlockAndTintGetter;Lnet/minecraft/world/level/block/state/BlockState;Lnet/minecraft/core/BlockPos;)I", cancellable = true) + private static void blockLightDimensionCheck(BlockAndTintGetter level, BlockState state, BlockPos pos, CallbackInfoReturnable cir) + { + ClientLevel clientLevel = null; + if (level instanceof ClientLevel) { + clientLevel = (ClientLevel) level; + } else if (level instanceof RenderChunkRegion renderChunkRegion) { + clientLevel = (ClientLevel) renderChunkRegion.level; + } + + if (clientLevel == null || clientLevel.dimension() != RenderStates.originalPlayerDimension) { + cir.cancel(); + } + } +} diff --git a/imm_ptl_core/src/main/resources/META-INF/accesstransformer.cfg b/imm_ptl_core/src/main/resources/META-INF/accesstransformer.cfg index 094d859bc..155d1a246 100644 --- a/imm_ptl_core/src/main/resources/META-INF/accesstransformer.cfg +++ b/imm_ptl_core/src/main/resources/META-INF/accesstransformer.cfg @@ -29,6 +29,8 @@ public net.minecraft.client.renderer.chunk.ChunkRenderDispatcher$RenderChunk$Chu public-f net.minecraft.client.renderer.chunk.ChunkRenderDispatcher f_112674_ #freeBuffers +public net.minecraft.client.renderer.chunk.RenderChunkRegion f_112908_ # level + diff --git a/imm_ptl_core/src/main/resources/imm_ptl_compat.mixins.json b/imm_ptl_core/src/main/resources/imm_ptl_compat.mixins.json index c74a661d0..d24b719a1 100644 --- a/imm_ptl_core/src/main/resources/imm_ptl_compat.mixins.json +++ b/imm_ptl_core/src/main/resources/imm_ptl_compat.mixins.json @@ -21,7 +21,13 @@ "MixinSodiumShaderLoader", "MixinSodiumWorldRenderer" ], + "client": [ + "MixinLucent", + "MixinLucentChunkRenderDispatcherHooks", + "MixinLucentLevelRenderer" + ], "injectors": { "defaultRequire": 1 - } + }, + "refmap": "mixins.imm_ptl.refmap.json" } \ No newline at end of file diff --git a/src/main/resources/META-INF/accesstransformer.cfg b/src/main/resources/META-INF/accesstransformer.cfg index 670888cf7..92c96e8b0 100644 --- a/src/main/resources/META-INF/accesstransformer.cfg +++ b/src/main/resources/META-INF/accesstransformer.cfg @@ -30,3 +30,5 @@ public net.minecraft.client.renderer.chunk.ChunkRenderDispatcher$RenderChunk$Chu public-f net.minecraft.client.renderer.chunk.ChunkRenderDispatcher f_112674_ #freeBuffers public net.minecraft.server.level.ChunkMap$TrackedEntity + +public net.minecraft.client.renderer.chunk.RenderChunkRegion f_112908_ # level