diff --git a/patches/minecraft/net/minecraft/server/level/ThreadedLevelLightEngine.java.patch b/patches/minecraft/net/minecraft/server/level/ThreadedLevelLightEngine.java.patch new file mode 100644 index 0000000000..308661d58d --- /dev/null +++ b/patches/minecraft/net/minecraft/server/level/ThreadedLevelLightEngine.java.patch @@ -0,0 +1,10 @@ +--- a/net/minecraft/server/level/ThreadedLevelLightEngine.java ++++ b/net/minecraft/server/level/ThreadedLevelLightEngine.java +@@ -171,6 +_,7 @@ + }, () -> "lightChunk " + chunkpos + " " + p_9355_)); + return CompletableFuture.supplyAsync(() -> { + p_9354_.setLightCorrect(true); ++ net.minecraftforge.common.ForgeHooks.fireLightingCalculatedEvent(p_9354_); + return p_9354_; + }, p_280982_ -> this.addTask(chunkpos.x, chunkpos.z, ThreadedLevelLightEngine.TaskType.POST_UPDATE, p_280982_)); + } diff --git a/src/main/java/net/minecraftforge/common/ForgeHooks.java b/src/main/java/net/minecraftforge/common/ForgeHooks.java index 2d15293e70..b783de66d8 100644 --- a/src/main/java/net/minecraftforge/common/ForgeHooks.java +++ b/src/main/java/net/minecraftforge/common/ForgeHooks.java @@ -153,6 +153,7 @@ import net.minecraftforge.event.entity.player.PlayerEvent; import net.minecraftforge.event.entity.living.LivingGetProjectileEvent; import net.minecraftforge.event.level.BlockEvent; import net.minecraftforge.event.level.NoteBlockEvent; +import net.minecraftforge.event.level.ChunkEvent; import net.minecraftforge.event.network.CustomPayloadEvent; import net.minecraftforge.eventbus.api.Event; import net.minecraftforge.fluids.FluidType; @@ -188,6 +189,7 @@ import net.minecraft.world.level.GameType; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.state.pattern.BlockInWorld; import net.minecraft.world.level.material.Fluid; +import net.minecraft.world.level.chunk.ChunkAccess; import org.apache.maven.artifact.versioning.ArtifactVersion; import org.apache.maven.artifact.versioning.DefaultArtifactVersion; import org.jetbrains.annotations.ApiStatus; @@ -204,6 +206,10 @@ public final class ForgeHooks { private ForgeHooks() {} + public static void fireLightingCalculatedEvent(ChunkAccess chunk) { + MinecraftForge.EVENT_BUS.post(new ChunkEvent.LightingCalculated(chunk)); + } + public static boolean canContinueUsing(@NotNull ItemStack from, @NotNull ItemStack to) { if (!from.isEmpty() && !to.isEmpty()) { return from.getItem().canContinueUsing(from, to); diff --git a/src/main/java/net/minecraftforge/event/level/ChunkEvent.java b/src/main/java/net/minecraftforge/event/level/ChunkEvent.java index 9b3da0cb8a..ff198effb1 100644 --- a/src/main/java/net/minecraftforge/event/level/ChunkEvent.java +++ b/src/main/java/net/minecraftforge/event/level/ChunkEvent.java @@ -22,24 +22,20 @@ import org.jetbrains.annotations.ApiStatus; *
* All children of this event are fired on the {@link MinecraftForge#EVENT_BUS}.
**/ -public class ChunkEvent extends LevelEvent -{ +public class ChunkEvent extends LevelEvent { private final ChunkAccess chunk; - public ChunkEvent(ChunkAccess chunk) - { + public ChunkEvent(ChunkAccess chunk) { super(chunk.getWorldForge()); this.chunk = chunk; } - public ChunkEvent(ChunkAccess chunk, LevelAccessor level) - { + public ChunkEvent(ChunkAccess chunk, LevelAccessor level) { super(level); this.chunk = chunk; } - public ChunkAccess getChunk() - { + public ChunkAccess getChunk() { return chunk; } @@ -56,13 +52,11 @@ public class ChunkEvent extends LevelEvent *
* This event is fired on the {@link MinecraftForge#EVENT_BUS}.
**/ - public static final class Load extends ChunkEvent - { + public static final class Load extends ChunkEvent { private final boolean newChunk; @ApiStatus.Internal - public Load(ChunkAccess chunk, boolean newChunk) - { + public Load(ChunkAccess chunk, boolean newChunk) { super(chunk); this.newChunk = newChunk; } @@ -74,8 +68,7 @@ public class ChunkEvent extends LevelEvent * * @return whether the Chunk is newly generated */ - public boolean isNewChunk() - { + public boolean isNewChunk() { return newChunk; } } @@ -91,10 +84,27 @@ public class ChunkEvent extends LevelEvent *
* This event is fired on the {@link MinecraftForge#EVENT_BUS}.
**/ - public static final class Unload extends ChunkEvent - { - public Unload(ChunkAccess chunk) - { + public static final class Unload extends ChunkEvent { + public Unload(ChunkAccess chunk) { + super(chunk); + } + } + + /** + * ChunkEvent.LightingCalculated is fired when MinecraftForge flags that lighting is correct in a chunk.
+ * This event is fired during light propagation in ThreadedLevelLightEngine.CompletableFuture(), specifically upon setting + * the ChunkAccess isLightCorrect to true.
+ *
+ * The game test for this event is lighting_event_test in net.minecraftforge.debug.chunk
+ *
+ * This event is not {@link Cancelable}.
+ *
+ * This event does not have a result. {@link HasResult}
+ *
+ * This event is fired on the {@link MinecraftForge#EVENT_BUS}.
+ **/ + public static final class LightingCalculated extends ChunkEvent { + public LightingCalculated(ChunkAccess chunk) { super(chunk); } } diff --git a/src/main/resources/META-INF/mods.toml b/src/main/resources/META-INF/mods.toml index 0b0fa9927b..52cbfa8197 100644 --- a/src/main/resources/META-INF/mods.toml +++ b/src/main/resources/META-INF/mods.toml @@ -5,13 +5,13 @@ logoFile="forge_logo.png" license="LGPL v2.1" [[mods]] - modId="forge" - version="${global.forgeVersion}" - updateJSONURL="https://files.minecraftforge.net/net/minecraftforge/forge/promotions_slim.json" - displayURL="https://minecraftforge.net/" - displayName="Forge" - credits="Anyone who has contributed on Github and supports our development" - authors="LexManos,cpw" - description=''' - Forge, a broad compatibility API. - ''' +modId="forge" +version="${global.forgeVersion}" +updateJSONURL="https://files.minecraftforge.net/net/minecraftforge/forge/promotions_slim.json" +displayURL="https://minecraftforge.net/" +displayName="Forge" +credits="Anyone who has contributed on Github and supports our development" +authors="LexManos,cpw" +description=''' +Forge, a broad compatibility API. +''' diff --git a/src/test/generated/lighting_event_test/data/forge/test_instance/lighting_event_test/test_lighting_event_fires.json b/src/test/generated/lighting_event_test/data/forge/test_instance/lighting_event_test/test_lighting_event_fires.json new file mode 100644 index 0000000000..3b55c2d746 --- /dev/null +++ b/src/test/generated/lighting_event_test/data/forge/test_instance/lighting_event_test/test_lighting_event_fires.json @@ -0,0 +1,7 @@ +{ + "type": "minecraft:function", + "environment": "minecraft:default", + "function": "forge:lighting_event_test/test_lighting_event_fires", + "max_ticks": 100, + "structure": "forge:empty3x3x3" +} \ No newline at end of file diff --git a/src/test/java/net/minecraftforge/debug/chunk/LightingEventTest.java b/src/test/java/net/minecraftforge/debug/chunk/LightingEventTest.java new file mode 100644 index 0000000000..ec235a75bd --- /dev/null +++ b/src/test/java/net/minecraftforge/debug/chunk/LightingEventTest.java @@ -0,0 +1,67 @@ +package net.minecraftforge.debug.chunk; + +import net.minecraft.ChatFormatting; +import net.minecraft.core.BlockPos; +import net.minecraft.gametest.framework.GameTestHelper; +import net.minecraft.network.chat.Component; +import net.minecraft.util.RandomSource; +import net.minecraft.world.level.chunk.ChunkAccess; +import net.minecraftforge.event.level.ChunkEvent; +import net.minecraftforge.fml.common.Mod; +import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext; +import net.minecraftforge.gametest.GameTest; +import net.minecraftforge.gametest.GameTestNamespace; +import net.minecraftforge.test.BaseTestMod; + +/** + * This is a test for the ChunkEvent.LightingCalculated event. + */ +@GameTestNamespace("forge") +@Mod(LightingEventTest.MODID) +public class LightingEventTest extends BaseTestMod { + public static final String MODID = "lighting_event_test"; + + private static final int MAX_CHUNK_LOCATION_ATTEMPTS = 5; + + public LightingEventTest(FMLJavaModLoadingContext context) { + super(context); + } + + @GameTest + public static void testLightingEventFires(GameTestHelper helper) { + var eventFired = helper.boolFlag("eventFired"); + helper.addEventListener(event -> eventFired.set(true)); + + var random = RandomSource.create(); + var level = helper.getLevel(); + var chunkSource = level.getChunkSource(); + + // try to find a far away chunk that is not already ticking + int attempts; + var origin = helper.absolutePos(BlockPos.ZERO); + ChunkAccess chunk = null; + for (attempts = 0; attempts < MAX_CHUNK_LOCATION_ATTEMPTS && chunk == null; attempts++) { + int x = random.nextInt(1, 10) * 10000; + int z = random.nextInt(1, 10) * 10000; + chunk = level.getChunk(origin.offset(x, 0, z)); + + // If the chunk is already ticking, we can't force it + chunk = chunkSource.isPositionTicking(chunk.getPos().toLong()) ? null : chunk; + } + + if (chunk == null) { + helper.fail(Component.literal("Failed to find a far away chunk that is not already ticking after " + MAX_CHUNK_LOCATION_ATTEMPTS + " attempts")); + return; + } else if (attempts > 1) { + helper.say("WARNING: Finding a far away chunk took " + attempts + " attempts.", ChatFormatting.YELLOW); + } + + helper.runAfterDelay(20, () -> { + if (!eventFired.getBool()) { + helper.fail("LightingCalculated event was not fired!"); + } + + helper.succeed(); + }); + } +} diff --git a/src/test/java/net/minecraftforge/debug/gameplay/level/ForcedChunkLoadingTest.java b/src/test/java/net/minecraftforge/debug/gameplay/level/ForcedChunkLoadingTest.java index 9a7b0de3ad..bc403e57c2 100644 --- a/src/test/java/net/minecraftforge/debug/gameplay/level/ForcedChunkLoadingTest.java +++ b/src/test/java/net/minecraftforge/debug/gameplay/level/ForcedChunkLoadingTest.java @@ -22,12 +22,12 @@ import net.minecraftforge.test.BaseTestMod; public class ForcedChunkLoadingTest extends BaseTestMod { static final String MOD_ID = "forced_chunk_loading"; + private static final int MAX_CHUNK_LOCATION_ATTEMPTS = 5; + public ForcedChunkLoadingTest(FMLJavaModLoadingContext context) { super(context); } - private static final int MAX_CHUNK_LOCATION_ATTEMPTS = 5; - @GameTest public static void force_far_away_chunk(GameTestHelper helper) { var random = RandomSource.create();