Skip to content

Commit 0a04fdc

Browse files
committed
new: implements FabricMC/fabric#4541
1 parent 0432c59 commit 0a04fdc

File tree

8 files changed

+138
-6
lines changed

8 files changed

+138
-6
lines changed

build.gradle

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,10 +80,12 @@ configure (allprojects - project(":tests")) {
8080

8181
[
8282
"fabric-networking-api-v1",
83-
"fabric-lifecycle-events-v1",
83+
// "fabric-lifecycle-events-v1",
8484
].forEach {
8585
modCompileOnly(fabricApi.module(it, "${project.fabric_version}"))
8686
}
87+
88+
modCompileOnly("com.github.FabricMC.fabric:fabric-lifecycle-events-v1:c814733d8423a4464332bc4d15b3e6a190fcbcfe")
8789
}
8890

8991
loom {
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package com.ishland.c2me.base.common.config;
2+
3+
import org.slf4j.Logger;
4+
import org.slf4j.LoggerFactory;
5+
6+
public class LateModStatuses {
7+
8+
private static final Logger LOGGER = LoggerFactory.getLogger(ModStatuses.class);
9+
public static final boolean fabric_lifecycle_events_v1_CHUNK_LEVEL_TYPE_CHANGE = isAvailable0_fabric_lifecycle_events_v1_CHUNK_LEVEL_TYPE_CHANGE();
10+
11+
private static boolean isAvailable0_fabric_lifecycle_events_v1_CHUNK_LEVEL_TYPE_CHANGE() {
12+
if (!ModStatuses.fabric_lifecycle_events_v1) {
13+
LOGGER.info("Fabric Lifecycle Events (v1) not found, skipping compat");
14+
return false;
15+
}
16+
Class<?> clazzServerChunkEvents;
17+
try {
18+
clazzServerChunkEvents = Class.forName("net.fabricmc.fabric.api.event.lifecycle.v1.ServerChunkEvents");
19+
} catch (ClassNotFoundException e) {
20+
LOGGER.warn("net.fabricmc.fabric.api.event.lifecycle.v1.ServerChunkEvents not found, but fabric-loader claims fabric-lifecycle-events-v1 is present, outdated fabric-api?", e);
21+
return false;
22+
}
23+
try {
24+
clazzServerChunkEvents.getField("CHUNK_LEVEL_TYPE_CHANGE");
25+
} catch (NoSuchFieldException e) {
26+
LOGGER.warn("Lnet/fabricmc/fabric/api/event/lifecycle/v1/ServerChunkEvents;CHUNK_LEVEL_TYPE_CHANGE:Lnet/fabricmc/fabric/api/event/Event; not found, outdated fabric-api?", e);
27+
return false;
28+
}
29+
30+
return true;
31+
}
32+
33+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package com.ishland.c2me.base.mixin.access.fapi;
2+
3+
import org.spongepowered.asm.mixin.Mixin;
4+
import org.spongepowered.asm.mixin.gen.Accessor;
5+
6+
@Mixin(targets = "net.fabricmc.fabric.impl.base.event.ArrayBackedEvent", remap = false)
7+
public interface IArrayBackedEvent<T> {
8+
9+
@Accessor("handlers")
10+
T[] c2me$getHandlers();
11+
12+
}

c2me-base/src/main/resources/c2me-base.mixins.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@
5555
"access.IXoroshiro128PlusPlusRandom",
5656
"access.IXoroshiro128PlusPlusRandomDeriver",
5757
"access.IXoroshiro128PlusPlusRandomImpl",
58+
"access.fapi.IArrayBackedEvent",
5859
"instrumentation.MixinServerChunkManager",
5960
"report.MixinDedicatedServerWatchdog",
6061
"scheduler.MixinThreadedAnvilChunkStorage",

c2me-rewrites-chunk-system/src/main/java/com/ishland/c2me/rewrites/chunksystem/common/fapi/LifecycleEventInvoker.java

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,65 @@
11
package com.ishland.c2me.rewrites.chunksystem.common.fapi;
22

3+
import com.ishland.c2me.base.mixin.access.fapi.IArrayBackedEvent;
34
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerChunkEvents;
5+
import net.minecraft.server.world.ChunkLevelType;
46
import net.minecraft.server.world.ServerWorld;
57
import net.minecraft.world.chunk.WorldChunk;
8+
import org.slf4j.Logger;
9+
import org.slf4j.LoggerFactory;
610

711
public class LifecycleEventInvoker {
812

13+
private static final Logger LOGGER = LoggerFactory.getLogger(LifecycleEventInvoker.class);
14+
915
public static void invokeChunkLoaded(ServerWorld world, WorldChunk chunk, boolean newChunk) {
1016
try {
1117
ServerChunkEvents.CHUNK_LOAD.invoker().onChunkLoad(world, chunk);
1218
if (newChunk) {
1319
ServerChunkEvents.CHUNK_GENERATE.invoker().onChunkGenerate(world, chunk);
1420
}
1521
} catch (Throwable t) {
16-
t.printStackTrace();
22+
LOGGER.error("Failed to invoke chunk load event (world={}, pos={}, newChunk={})", world, chunk.getPos(), newChunk, t);
1723
}
1824
}
1925

2026
public static void invokeChunkUnload(ServerWorld world, WorldChunk chunk) {
2127
try {
2228
ServerChunkEvents.CHUNK_UNLOAD.invoker().onChunkUnload(world, chunk);
2329
} catch (Throwable t) {
24-
t.printStackTrace();
30+
LOGGER.error("Failed to invoke chunk unload event (world={}, pos={})", world, chunk.getPos(), t);
31+
}
32+
}
33+
34+
private static boolean cachedNeedsInvokeChunkLevelTypeChange = false;
35+
36+
public static boolean needsInvokeChunkLevelTypeChange() {
37+
if (cachedNeedsInvokeChunkLevelTypeChange) return true;
38+
try {
39+
if (ServerChunkEvents.CHUNK_LEVEL_TYPE_CHANGE instanceof IArrayBackedEvent<?> accessor0) {
40+
IArrayBackedEvent<ServerChunkEvents.LevelTypeChange> accessor = (IArrayBackedEvent<ServerChunkEvents.LevelTypeChange>) accessor0;
41+
if (accessor.c2me$getHandlers().length > 0) {
42+
cachedNeedsInvokeChunkLevelTypeChange = true;
43+
return true;
44+
}
45+
} else {
46+
LOGGER.warn("Unexpected Event implementation of ServerChunkEvents.CHUNK_LEVEL_TYPE_CHANGE: {}", ServerChunkEvents.CHUNK_LEVEL_TYPE_CHANGE.getClass().getName());
47+
cachedNeedsInvokeChunkLevelTypeChange = true;
48+
return true;
49+
}
50+
return false;
51+
} catch (Throwable t) {
52+
LOGGER.error("Failed to check if chunk level type change event is needed", t);
53+
cachedNeedsInvokeChunkLevelTypeChange = true;
54+
return true;
55+
}
56+
}
57+
58+
public static void invokeChunkLevelTypeChange(ServerWorld world, WorldChunk chunk, ChunkLevelType oldLevelType, ChunkLevelType newLevelType) {
59+
try {
60+
ServerChunkEvents.CHUNK_LEVEL_TYPE_CHANGE.invoker().onChunkLevelTypeChange(world, chunk, oldLevelType, newLevelType);
61+
} catch (Throwable t) {
62+
LOGGER.error("Failed to invoke chunk level type change event (world={}, pos={}, oldLevelType={}, newLevelType={})", world, chunk.getPos(), oldLevelType, newLevelType, t);
2563
}
2664
}
2765

c2me-rewrites-chunk-system/src/main/java/com/ishland/c2me/rewrites/chunksystem/common/statuses/ServerAccessible.java

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.ishland.c2me.rewrites.chunksystem.common.statuses;
22

33
import com.google.common.base.Preconditions;
4+
import com.ishland.c2me.base.common.config.LateModStatuses;
45
import com.ishland.c2me.base.common.config.ModStatuses;
56
import com.ishland.c2me.base.common.threadstate.ThreadInstrumentation;
67
import com.ishland.c2me.base.mixin.access.IThreadedAnvilChunkStorage;
@@ -16,6 +17,7 @@
1617
import net.minecraft.entity.EntityType;
1718
import net.minecraft.entity.SpawnReason;
1819
import net.minecraft.nbt.NbtCompound;
20+
import net.minecraft.server.world.ChunkLevelType;
1921
import net.minecraft.server.world.ServerWorld;
2022
import net.minecraft.world.chunk.Chunk;
2123
import net.minecraft.world.chunk.ChunkStatus;
@@ -58,6 +60,10 @@ public CompletionStage<Void> upgradeToThis(ChunkLoadingContext context, Cancella
5860
}
5961
}
6062

63+
if (LateModStatuses.fabric_lifecycle_events_v1_CHUNK_LEVEL_TYPE_CHANGE) {
64+
LifecycleEventInvoker.invokeChunkLevelTypeChange(serverWorld, worldChunk, ChunkLevelType.INACCESSIBLE, ChunkLevelType.FULL);
65+
}
66+
6167
((IThreadedAnvilChunkStorage) context.tacs()).getCurrentChunkHolders().put(context.holder().getKey().toLong(), context.holder().getUserData().get());
6268
((IThreadedAnvilChunkStorage) context.tacs()).setChunkHolderListDirty(true);
6369
}
@@ -88,12 +94,18 @@ public CompletionStage<Void> downgradeFromThis(ChunkLoadingContext context, Canc
8894
((IThreadedAnvilChunkStorage) context.tacs()).getCurrentChunkHolders().remove(context.holder().getKey().toLong());
8995
((IThreadedAnvilChunkStorage) context.tacs()).setChunkHolderListDirty(true);
9096
final WorldChunk worldChunk = (WorldChunk) chunk;
97+
ServerWorld serverWorld = ((IThreadedAnvilChunkStorage) context.tacs()).getWorld();
9198
// worldChunk.setLoadedToWorld(false);
9299
// worldChunk.removeChunkTickSchedulers(((IThreadedAnvilChunkStorage) context.tacs()).getWorld());
100+
101+
if (LateModStatuses.fabric_lifecycle_events_v1_CHUNK_LEVEL_TYPE_CHANGE) {
102+
LifecycleEventInvoker.invokeChunkLevelTypeChange(serverWorld, worldChunk, ChunkLevelType.FULL, ChunkLevelType.INACCESSIBLE);
103+
}
104+
LithiumChunkStatusTrackerInvoker.invokeOnChunkInaccessible(((IThreadedAnvilChunkStorage) context.tacs()).getWorld(), context.holder().getKey());
105+
93106
worldChunk.setLevelTypeProvider(null);
94107
worldChunk.setUnsavedListener(pos -> {
95108
});
96-
LithiumChunkStatusTrackerInvoker.invokeOnChunkInaccessible(((IThreadedAnvilChunkStorage) context.tacs()).getWorld(), context.holder().getKey());
97109
context.holder().getItem().set(new ChunkState(state.protoChunk(), state.protoChunk(), ChunkStatus.FULL));
98110
}, ((IThreadedAnvilChunkStorage) context.tacs()).getMainThreadExecutor());
99111
}

c2me-rewrites-chunk-system/src/main/java/com/ishland/c2me/rewrites/chunksystem/common/statuses/ServerBlockTicking.java

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.ishland.c2me.rewrites.chunksystem.common.statuses;
22

33
import com.google.common.base.Suppliers;
4+
import com.ishland.c2me.base.common.config.LateModStatuses;
45
import com.ishland.c2me.base.common.threadstate.ThreadInstrumentation;
56
import com.ishland.c2me.base.mixin.access.IThreadedAnvilChunkStorage;
67
import com.ishland.c2me.rewrites.chunksystem.common.ChunkLoadingContext;
@@ -9,6 +10,7 @@
910
import com.ishland.c2me.rewrites.chunksystem.common.NewChunkHolderVanillaInterface;
1011
import com.ishland.c2me.rewrites.chunksystem.common.NewChunkStatus;
1112
import com.ishland.c2me.rewrites.chunksystem.common.ducks.WorldChunkExtension;
13+
import com.ishland.c2me.rewrites.chunksystem.common.fapi.LifecycleEventInvoker;
1214
import com.ishland.c2me.rewrites.chunksystem.common.quirks.FlowableFluidUtils;
1315
import com.ishland.c2me.rewrites.chunksystem.common.threadstate.ChunkTaskWork;
1416
import com.ishland.flowsched.scheduler.Cancellable;
@@ -19,6 +21,8 @@
1921
import net.minecraft.block.BlockState;
2022
import net.minecraft.fluid.FlowableFluid;
2123
import net.minecraft.fluid.FluidState;
24+
import net.minecraft.server.world.ChunkLevelType;
25+
import net.minecraft.server.world.ServerWorld;
2226
import net.minecraft.util.math.BlockPos;
2327
import net.minecraft.util.math.ChunkPos;
2428
import net.minecraft.world.ChunkRegion;
@@ -64,11 +68,15 @@ public CompletionStage<Void> upgradeToThis(ChunkLoadingContext context, Cancella
6468
return CompletableFuture.runAsync(() -> {
6569
try (var ignored = ThreadInstrumentation.getCurrent().begin(new ChunkTaskWork(context, this, true))) {
6670
final WorldChunk chunk = (WorldChunk) context.holder().getItem().get().chunk();
67-
chunk.runPostProcessing(((IThreadedAnvilChunkStorage) context.tacs()).getWorld());
68-
((IThreadedAnvilChunkStorage) context.tacs()).getWorld().disableTickSchedulers(chunk);
71+
ServerWorld serverWorld = ((IThreadedAnvilChunkStorage) context.tacs()).getWorld();
72+
chunk.runPostProcessing(serverWorld);
73+
serverWorld.disableTickSchedulers(chunk);
6974
sendChunkToPlayer(context);
7075
((IThreadedAnvilChunkStorage) context.tacs()).getTotalChunksLoadedCount().incrementAndGet(); // never decremented in vanilla
7176
((WorldChunkExtension) chunk).c2me$setBlockTicking(true);
77+
if (LateModStatuses.fabric_lifecycle_events_v1_CHUNK_LEVEL_TYPE_CHANGE) {
78+
LifecycleEventInvoker.invokeChunkLevelTypeChange(serverWorld, chunk, ChunkLevelType.FULL, ChunkLevelType.BLOCK_TICKING);
79+
}
7280
}
7381
}, ((IThreadedAnvilChunkStorage) context.tacs()).getMainThreadExecutor());
7482
}
@@ -117,8 +125,14 @@ private static void sendChunkToPlayer(ChunkLoadingContext context) {
117125

118126
@Override
119127
public CompletionStage<Void> downgradeFromThis(ChunkLoadingContext context, Cancellable cancellable) {
128+
ServerWorld serverWorld = ((IThreadedAnvilChunkStorage) context.tacs()).getWorld();
120129
final WorldChunk chunk = (WorldChunk) context.holder().getItem().get().chunk();
121130
((WorldChunkExtension) chunk).c2me$setBlockTicking(false);
131+
if (LateModStatuses.fabric_lifecycle_events_v1_CHUNK_LEVEL_TYPE_CHANGE && LifecycleEventInvoker.needsInvokeChunkLevelTypeChange()) {
132+
return CompletableFuture.runAsync(() -> {
133+
LifecycleEventInvoker.invokeChunkLevelTypeChange(serverWorld, chunk, ChunkLevelType.BLOCK_TICKING, ChunkLevelType.FULL);
134+
}, ((IThreadedAnvilChunkStorage) context.tacs()).getMainThreadExecutor());
135+
}
122136
return CompletableFuture.completedStage(null);
123137
// TODO check if syncing is needed
124138
}

c2me-rewrites-chunk-system/src/main/java/com/ishland/c2me/rewrites/chunksystem/common/statuses/ServerEntityTicking.java

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,19 @@
11
package com.ishland.c2me.rewrites.chunksystem.common.statuses;
22

3+
import com.ishland.c2me.base.common.config.LateModStatuses;
4+
import com.ishland.c2me.base.mixin.access.IThreadedAnvilChunkStorage;
35
import com.ishland.c2me.rewrites.chunksystem.common.ChunkLoadingContext;
46
import com.ishland.c2me.rewrites.chunksystem.common.ChunkState;
57
import com.ishland.c2me.rewrites.chunksystem.common.NewChunkStatus;
8+
import com.ishland.c2me.rewrites.chunksystem.common.fapi.LifecycleEventInvoker;
69
import com.ishland.flowsched.scheduler.Cancellable;
710
import com.ishland.flowsched.scheduler.ItemHolder;
811
import com.ishland.flowsched.scheduler.KeyStatusPair;
12+
import net.minecraft.server.world.ChunkLevelType;
13+
import net.minecraft.server.world.ServerWorld;
914
import net.minecraft.util.math.ChunkPos;
1015
import net.minecraft.world.chunk.ChunkStatus;
16+
import net.minecraft.world.chunk.WorldChunk;
1117

1218
import java.util.concurrent.CompletableFuture;
1319
import java.util.concurrent.CompletionStage;
@@ -35,11 +41,25 @@ public ServerEntityTicking(int ordinal) {
3541

3642
@Override
3743
public CompletionStage<Void> upgradeToThis(ChunkLoadingContext context, Cancellable cancellable) {
44+
if (LateModStatuses.fabric_lifecycle_events_v1_CHUNK_LEVEL_TYPE_CHANGE && LifecycleEventInvoker.needsInvokeChunkLevelTypeChange()) {
45+
return CompletableFuture.runAsync(() -> {
46+
ServerWorld serverWorld = ((IThreadedAnvilChunkStorage) context.tacs()).getWorld();
47+
final WorldChunk chunk = (WorldChunk) context.holder().getItem().get().chunk();
48+
LifecycleEventInvoker.invokeChunkLevelTypeChange(serverWorld, chunk, ChunkLevelType.BLOCK_TICKING, ChunkLevelType.ENTITY_TICKING);
49+
}, ((IThreadedAnvilChunkStorage) context.tacs()).getMainThreadExecutor());
50+
}
3851
return CompletableFuture.completedStage(null);
3952
}
4053

4154
@Override
4255
public CompletionStage<Void> downgradeFromThis(ChunkLoadingContext context, Cancellable cancellable) {
56+
if (LateModStatuses.fabric_lifecycle_events_v1_CHUNK_LEVEL_TYPE_CHANGE && LifecycleEventInvoker.needsInvokeChunkLevelTypeChange()) {
57+
return CompletableFuture.runAsync(() -> {
58+
ServerWorld serverWorld = ((IThreadedAnvilChunkStorage) context.tacs()).getWorld();
59+
final WorldChunk chunk = (WorldChunk) context.holder().getItem().get().chunk();
60+
LifecycleEventInvoker.invokeChunkLevelTypeChange(serverWorld, chunk, ChunkLevelType.ENTITY_TICKING, ChunkLevelType.BLOCK_TICKING);
61+
}, ((IThreadedAnvilChunkStorage) context.tacs()).getMainThreadExecutor());
62+
}
4363
return CompletableFuture.completedStage(null);
4464
}
4565

0 commit comments

Comments
 (0)