From eb001a6382da7ffd943669a3214d5327bde4e4a6 Mon Sep 17 00:00:00 2001
From: Erizamanu <157750096+Erizamanu@users.noreply.github.com>
Date: Tue, 22 Apr 2025 15:11:26 -0500
Subject: [PATCH] Add ChunkEvent.LightingCalculated, fixes #8354 (#10533)
---
.../level/ThreadedLevelLightEngine.java.patch | 10 +++
.../net/minecraftforge/common/ForgeHooks.java | 6 ++
.../event/level/ChunkEvent.java | 46 ++++++++-----
src/main/resources/META-INF/mods.toml | 20 +++---
.../test_lighting_event_fires.json | 7 ++
.../debug/chunk/LightingEventTest.java | 67 +++++++++++++++++++
.../level/ForcedChunkLoadingTest.java | 4 +-
7 files changed, 130 insertions(+), 30 deletions(-)
create mode 100644 patches/minecraft/net/minecraft/server/level/ThreadedLevelLightEngine.java.patch
create mode 100644 src/test/generated/lighting_event_test/data/forge/test_instance/lighting_event_test/test_lighting_event_fires.json
create mode 100644 src/test/java/net/minecraftforge/debug/chunk/LightingEventTest.java
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();