diff --git a/src/main/java/xyz/nucleoid/plasmid/Plasmid.java b/src/main/java/xyz/nucleoid/plasmid/Plasmid.java index dc20c1e2..d5b481ce 100644 --- a/src/main/java/xyz/nucleoid/plasmid/Plasmid.java +++ b/src/main/java/xyz/nucleoid/plasmid/Plasmid.java @@ -15,7 +15,6 @@ import net.minecraft.util.ActionResult; import net.minecraft.util.Hand; import net.minecraft.util.Identifier; -import net.minecraft.util.dynamic.Codecs; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import xyz.nucleoid.plasmid.command.*; @@ -114,6 +113,10 @@ private void registerCallbacks() { GamePortalManager.INSTANCE.tick(); }); + ServerLifecycleEvents.END_DATA_PACK_RELOAD.register( + (_server, resourceManager, success) -> GameSpaceManager.get().onReload(resourceManager, success) + ); + ServerLifecycleEvents.SERVER_STARTING.register(server -> { GameSpaceManager.openServer(server); GamePortalManager.INSTANCE.setup(server); diff --git a/src/main/java/xyz/nucleoid/plasmid/duck/ServerEntityManagerAccess.java b/src/main/java/xyz/nucleoid/plasmid/duck/ServerEntityManagerAccess.java new file mode 100644 index 00000000..84e181ac --- /dev/null +++ b/src/main/java/xyz/nucleoid/plasmid/duck/ServerEntityManagerAccess.java @@ -0,0 +1,7 @@ +package xyz.nucleoid.plasmid.duck; + +import it.unimi.dsi.fastutil.longs.LongSet; + +public interface ServerEntityManagerAccess { + void plasmid$clearChunks(LongSet chunksToDrop); +} diff --git a/src/main/java/xyz/nucleoid/plasmid/duck/ThreadedAnvilChunkStorageAccess.java b/src/main/java/xyz/nucleoid/plasmid/duck/ThreadedAnvilChunkStorageAccess.java new file mode 100644 index 00000000..7215d64d --- /dev/null +++ b/src/main/java/xyz/nucleoid/plasmid/duck/ThreadedAnvilChunkStorageAccess.java @@ -0,0 +1,9 @@ +package xyz.nucleoid.plasmid.duck; + +import it.unimi.dsi.fastutil.longs.LongSet; +import net.minecraft.world.gen.chunk.ChunkGenerator; + +public interface ThreadedAnvilChunkStorageAccess { + void plasmid$setGenerator(ChunkGenerator generator); + void plasmid$clearChunks(LongSet chunksToDrop); +} diff --git a/src/main/java/xyz/nucleoid/plasmid/game/event/GameActivityEvents.java b/src/main/java/xyz/nucleoid/plasmid/game/event/GameActivityEvents.java index a4358437..2ca0a1a2 100644 --- a/src/main/java/xyz/nucleoid/plasmid/game/event/GameActivityEvents.java +++ b/src/main/java/xyz/nucleoid/plasmid/game/event/GameActivityEvents.java @@ -1,5 +1,6 @@ package xyz.nucleoid.plasmid.game.event; +import net.minecraft.resource.LifecycledResourceManager; import net.minecraft.text.Text; import org.jetbrains.annotations.Nullable; import xyz.nucleoid.plasmid.game.GameActivity; @@ -32,6 +33,19 @@ public final class GameActivityEvents { } }); + /** + * Called after datapacks are reloaded. + */ + public static final StimulusEvent RELOAD = StimulusEvent.create(Reload.class, ctx -> (resourceManager, success) -> { + try { + for (var listener : ctx.getListeners()) { + listener.onReload(resourceManager, success); + } + } catch (Throwable throwable) { + ctx.handleException(throwable); + } + }); + /** * Called when a {@link GameActivity} should be disabled. This happens when a {@link GameActivity} is replaced by * another on a {@link GameSpace} or when a {@link GameSpace} is closed. @@ -143,6 +157,10 @@ public interface Tick { void onTick(); } + public interface Reload { + void onReload(LifecycledResourceManager resourceManager, boolean success); + } + public interface RequestStart { @Nullable GameResult onRequestStart(); diff --git a/src/main/java/xyz/nucleoid/plasmid/game/manager/GameSpaceManager.java b/src/main/java/xyz/nucleoid/plasmid/game/manager/GameSpaceManager.java index 91d6be10..ac93e67d 100644 --- a/src/main/java/xyz/nucleoid/plasmid/game/manager/GameSpaceManager.java +++ b/src/main/java/xyz/nucleoid/plasmid/game/manager/GameSpaceManager.java @@ -6,6 +6,7 @@ import it.unimi.dsi.fastutil.objects.Reference2ObjectOpenHashMap; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.registry.RegistryKey; +import net.minecraft.resource.LifecycledResourceManager; import net.minecraft.server.MinecraftServer; import net.minecraft.server.network.ServerPlayerEntity; import net.minecraft.util.Identifier; @@ -192,6 +193,12 @@ void removePlayerFromGameSpace(ManagedGameSpace gameSpace, ServerPlayerEntity pl this.playerToGameSpace.remove(player.getUuid(), gameSpace); } + public void onReload(LifecycledResourceManager resourceManager, boolean success) { + for (var gameSpace : this.gameSpaces) { + gameSpace.onReload(resourceManager, success); + } + } + private void close() { Stimuli.unregisterSelector(this.listenerSelector); diff --git a/src/main/java/xyz/nucleoid/plasmid/game/manager/ManagedGameSpace.java b/src/main/java/xyz/nucleoid/plasmid/game/manager/ManagedGameSpace.java index 8572fdf5..30c48fed 100644 --- a/src/main/java/xyz/nucleoid/plasmid/game/manager/ManagedGameSpace.java +++ b/src/main/java/xyz/nucleoid/plasmid/game/manager/ManagedGameSpace.java @@ -2,6 +2,7 @@ import com.google.common.collect.Lists; import net.minecraft.registry.RegistryKey; +import net.minecraft.resource.LifecycledResourceManager; import net.minecraft.server.MinecraftServer; import net.minecraft.server.network.ServerPlayerEntity; import net.minecraft.text.Text; @@ -240,6 +241,10 @@ void onPlayerRemove(ServerPlayerEntity player) { this.manager.removePlayerFromGameSpace(this, player); } + void onReload(LifecycledResourceManager resourceManager, boolean success) { + this.state.invoker(GameActivityEvents.RELOAD).onReload(resourceManager, success); + } + void onAddWorld(RuntimeWorldHandle worldHandle) { this.manager.addDimensionToGameSpace(this, worldHandle.asWorld().getRegistryKey()); } diff --git a/src/main/java/xyz/nucleoid/plasmid/game/manager/ManagedGameSpaceWorlds.java b/src/main/java/xyz/nucleoid/plasmid/game/manager/ManagedGameSpaceWorlds.java index 3db1e1a6..9fe00f24 100644 --- a/src/main/java/xyz/nucleoid/plasmid/game/manager/ManagedGameSpaceWorlds.java +++ b/src/main/java/xyz/nucleoid/plasmid/game/manager/ManagedGameSpaceWorlds.java @@ -1,17 +1,20 @@ package xyz.nucleoid.plasmid.game.manager; import com.google.common.collect.Iterators; +import it.unimi.dsi.fastutil.longs.LongSet; import it.unimi.dsi.fastutil.objects.Reference2ObjectOpenHashMap; import net.minecraft.registry.RegistryKey; import net.minecraft.server.world.ServerWorld; import net.minecraft.world.GameRules; import net.minecraft.world.World; +import net.minecraft.world.gen.chunk.ChunkGenerator; import org.jetbrains.annotations.NotNull; import xyz.nucleoid.fantasy.Fantasy; import xyz.nucleoid.fantasy.RuntimeWorldConfig; import xyz.nucleoid.fantasy.RuntimeWorldHandle; import xyz.nucleoid.fantasy.util.GameRuleStore; import xyz.nucleoid.plasmid.game.world.GameSpaceWorlds; +import xyz.nucleoid.plasmid.duck.ThreadedAnvilChunkStorageAccess; import java.util.Iterator; import java.util.Map; @@ -37,6 +40,23 @@ public ServerWorld add(RuntimeWorldConfig worldConfig) { return worldHandle.asWorld(); } + @Override + public void regenerate(ServerWorld world, ChunkGenerator generator, LongSet chunksToDrop) { + if (!this.worlds.containsKey(world.getRegistryKey())) { + throw new IllegalArgumentException(String.format("The given world %s was not part of the game space!", world.getRegistryKey().getValue())); + } + + var tacs = ((ThreadedAnvilChunkStorageAccess) world.getChunkManager().threadedAnvilChunkStorage); + + // Set chunk generator + tacs.plasmid$setGenerator(generator); + + // Clear all chunks + tacs.plasmid$clearChunks(chunksToDrop); + + // We don't need to actually initiate a regeneration, as Minecraft will notice that they are missing and do it + } + @Override public boolean remove(ServerWorld world) { var dimension = world.getRegistryKey(); diff --git a/src/main/java/xyz/nucleoid/plasmid/game/world/GameSpaceWorlds.java b/src/main/java/xyz/nucleoid/plasmid/game/world/GameSpaceWorlds.java index 016cf973..9dc5ddfa 100644 --- a/src/main/java/xyz/nucleoid/plasmid/game/world/GameSpaceWorlds.java +++ b/src/main/java/xyz/nucleoid/plasmid/game/world/GameSpaceWorlds.java @@ -1,8 +1,11 @@ package xyz.nucleoid.plasmid.game.world; +import it.unimi.dsi.fastutil.longs.LongSet; import net.minecraft.server.world.ServerWorld; +import net.minecraft.world.gen.chunk.ChunkGenerator; import org.jetbrains.annotations.NotNull; import xyz.nucleoid.fantasy.RuntimeWorldConfig; +import xyz.nucleoid.map_templates.BlockBounds; import xyz.nucleoid.plasmid.game.GameSpace; import java.util.Iterator; @@ -21,6 +24,37 @@ public interface GameSpaceWorlds extends Iterable { */ ServerWorld add(RuntimeWorldConfig worldConfig); + /** + * Regenerates a temporary world associated with this {@link GameSpace} with the given generator. This will + * reset all blocks and all entities (other than players) in the world. + *

+ * The parameter `chunksToDrop` should contain all chunks which have been generated and are nonempty. Otherwise, + * unexpected behaviour (such as incorrect lighting or generation) may be encountered. + * + * @param world the world to regenerate + * @param generator the chunk generator to regenerate it with + * @param chunksToDrop the list of chunks to regenerate. This should consist of the world's nonempty, generated + * chunks. + */ + void regenerate(ServerWorld world, ChunkGenerator generator, LongSet chunksToDrop); + + /** + * Regenerates a temporary world associated with this {@link GameSpace} with the given generator. This will + * reset all blocks and all entities (other than players) in the world. + *

+ * The parameter `worldBounds` should contain all chunks which have been generated and are nonempty. For instance, + * when using an {@link xyz.nucleoid.plasmid.game.world.generator.TemplateChunkGenerator}, providing the union of + * the old and new {@link xyz.nucleoid.map_templates.MapTemplate}s's bounds is sufficient. + * + * @param world the world to regenerate + * @param generator the chunk generator to regenerate it with + * @param worldBounds the bounds of the world to regenerate + * chunks. + */ + default void regenerate(ServerWorld world, ChunkGenerator generator, BlockBounds worldBounds) { + this.regenerate(world, generator, worldBounds.asChunks()); + } + /** * Removes and deletes a temporary world that is associated with this {@link GameSpace}. * The passed world must have been created through {@link GameSpaceWorlds#add(RuntimeWorldConfig)}. diff --git a/src/main/java/xyz/nucleoid/plasmid/mixin/game/world/ChunkHolderAccessor.java b/src/main/java/xyz/nucleoid/plasmid/mixin/game/world/ChunkHolderAccessor.java new file mode 100644 index 00000000..61958cc9 --- /dev/null +++ b/src/main/java/xyz/nucleoid/plasmid/mixin/game/world/ChunkHolderAccessor.java @@ -0,0 +1,14 @@ +package xyz.nucleoid.plasmid.mixin.game.world; + +import net.minecraft.server.world.ChunkHolder; +import net.minecraft.server.world.ThreadedAnvilChunkStorage; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Invoker; + +import java.util.concurrent.Executor; + +@Mixin(ChunkHolder.class) +public interface ChunkHolderAccessor { + @Invoker("updateFutures") + void plasmid$updateFutures(ThreadedAnvilChunkStorage chunkStorage, Executor executor); +} diff --git a/src/main/java/xyz/nucleoid/plasmid/mixin/game/world/ServerEntityManagerMixin.java b/src/main/java/xyz/nucleoid/plasmid/mixin/game/world/ServerEntityManagerMixin.java new file mode 100644 index 00000000..d338b311 --- /dev/null +++ b/src/main/java/xyz/nucleoid/plasmid/mixin/game/world/ServerEntityManagerMixin.java @@ -0,0 +1,45 @@ +package xyz.nucleoid.plasmid.mixin.game.world; + +import it.unimi.dsi.fastutil.longs.LongOpenHashSet; +import it.unimi.dsi.fastutil.longs.LongSet; +import net.minecraft.server.world.ServerEntityManager; +import net.minecraft.util.math.ChunkPos; +import net.minecraft.world.storage.ChunkDataList; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; +import xyz.nucleoid.plasmid.duck.ServerEntityManagerAccess; + +import java.util.List; +import java.util.Queue; +import java.util.function.Consumer; + +@Mixin(ServerEntityManager.class) +public class ServerEntityManagerMixin implements ServerEntityManagerAccess { + @Shadow @Final private Queue> loadingQueue; + @Unique + private final LongSet plasmid$chunksToDropData = new LongOpenHashSet(); + + @Override + public void plasmid$clearChunks(LongSet chunksToDrop) { + this.plasmid$chunksToDropData.addAll(chunksToDrop); + } + + @Inject(method = "scheduleRead", at = @At(value = "INVOKE", target = "Lit/unimi/dsi/fastutil/longs/Long2ObjectMap;put(JLjava/lang/Object;)Ljava/lang/Object;", shift = At.Shift.AFTER), cancellable = true) + private void scheduleRead(long chunkPos, CallbackInfo ci) { + if (this.plasmid$chunksToDropData.remove(chunkPos)) { + this.loadingQueue.add(new ChunkDataList<>(new ChunkPos(chunkPos), List.of())); + ci.cancel(); + } + } + + @Inject(method = "trySave", at = @At("HEAD")) + private void trySave(long chunkPos, Consumer action, CallbackInfoReturnable cir) { + this.plasmid$chunksToDropData.remove(chunkPos); + } +} diff --git a/src/main/java/xyz/nucleoid/plasmid/mixin/game/world/ServerLightingProviderAccessor.java b/src/main/java/xyz/nucleoid/plasmid/mixin/game/world/ServerLightingProviderAccessor.java new file mode 100644 index 00000000..1abeaae8 --- /dev/null +++ b/src/main/java/xyz/nucleoid/plasmid/mixin/game/world/ServerLightingProviderAccessor.java @@ -0,0 +1,12 @@ +package xyz.nucleoid.plasmid.mixin.game.world; + +import net.minecraft.server.world.ServerLightingProvider; +import net.minecraft.util.math.ChunkPos; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Invoker; + +@Mixin(ServerLightingProvider.class) +public interface ServerLightingProviderAccessor { + @Invoker("updateChunkStatus") + void plasmid$updateChunkStatus(ChunkPos pos); +} diff --git a/src/main/java/xyz/nucleoid/plasmid/mixin/game/world/ThreadedAnvilChunkStorageMixin.java b/src/main/java/xyz/nucleoid/plasmid/mixin/game/world/ThreadedAnvilChunkStorageMixin.java new file mode 100644 index 00000000..2b3d79c8 --- /dev/null +++ b/src/main/java/xyz/nucleoid/plasmid/mixin/game/world/ThreadedAnvilChunkStorageMixin.java @@ -0,0 +1,170 @@ +package xyz.nucleoid.plasmid.mixin.game.world; + +import it.unimi.dsi.fastutil.longs.*; +import net.minecraft.entity.Entity; +import net.minecraft.nbt.NbtCompound; +import net.minecraft.registry.DynamicRegistryManager; +import net.minecraft.registry.RegistryKeys; +import net.minecraft.server.world.*; +import net.minecraft.util.TypeFilter; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.ChunkPos; +import net.minecraft.util.thread.ThreadExecutor; +import net.minecraft.world.chunk.Chunk; +import net.minecraft.world.chunk.WorldChunk; +import net.minecraft.world.gen.chunk.ChunkGenerator; +import net.minecraft.world.gen.chunk.ChunkGeneratorSettings; +import net.minecraft.world.gen.chunk.NoiseChunkGenerator; +import net.minecraft.world.gen.chunk.placement.StructurePlacementCalculator; +import net.minecraft.world.gen.noise.NoiseConfig; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; +import net.minecraft.server.network.ServerPlayerEntity; +import xyz.nucleoid.plasmid.duck.ServerEntityManagerAccess; +import xyz.nucleoid.plasmid.duck.ThreadedAnvilChunkStorageAccess; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.Queue; +import java.util.concurrent.CompletableFuture; + +@Mixin(ThreadedAnvilChunkStorage.class) +public class ThreadedAnvilChunkStorageMixin implements ThreadedAnvilChunkStorageAccess { + @Shadow @Final private Long2ObjectLinkedOpenHashMap currentChunkHolders; + @Shadow private volatile Long2ObjectLinkedOpenHashMap chunkHolders; + @Shadow private boolean chunkHolderListDirty; + @Shadow @Final LongSet unloadedChunks; + @Shadow @Final private Queue unloadTaskQueue; + @Shadow @Final private Long2ByteMap chunkToType; + @Shadow @Final private Long2LongMap chunkToNextSaveTimeMs; + @Shadow @Final private LongSet loadedChunks; + @Shadow @Final private ServerLightingProvider lightingProvider; + @Shadow @Final ServerWorld world; + @Shadow @Final private ChunkTaskPrioritySystem chunkTaskPrioritySystem; + @Shadow @Final private ThreadExecutor mainThreadExecutor; + + @Shadow private ChunkGenerator chunkGenerator; + + @Unique + private NoiseConfig plasmid$noiseConfig; + @Unique + private StructurePlacementCalculator plasmid$structurePlacementCalculator; + @Unique + private final LongSet plasmid$chunksToDropData = new LongOpenHashSet(); + + @Inject(method = "getUpdatedChunkNbt", at = @At("HEAD"), cancellable = true) + private void loadChunkData(ChunkPos chunkPos, CallbackInfoReturnable>> cir) { + if (this.plasmid$chunksToDropData.remove(chunkPos.toLong())) { + cir.setReturnValue(CompletableFuture.completedFuture(Optional.empty())); + } + } + + @Inject(method = "save(Lnet/minecraft/world/chunk/Chunk;)Z", at = @At("HEAD")) + private void save(Chunk chunk, CallbackInfoReturnable cir) { + if (chunk.needsSaving()) { + this.plasmid$chunksToDropData.remove(chunk.getPos().toLong()); + } + } + + @Inject(method = "getStructurePlacementCalculator", at = @At("HEAD"), cancellable = true) + protected void getStructurePlacementCalculator(CallbackInfoReturnable cir) { + if (this.plasmid$structurePlacementCalculator != null) { + cir.setReturnValue(this.plasmid$structurePlacementCalculator); + } + } + + @Inject(method = "getNoiseConfig", at = @At("HEAD"), cancellable = true) + protected void getNoiseConfig(CallbackInfoReturnable cir) { + if (this.plasmid$noiseConfig != null) { + cir.setReturnValue(this.plasmid$noiseConfig); + } + } + + @Override + public void plasmid$setGenerator(ChunkGenerator generator) { + this.chunkGenerator = generator; + DynamicRegistryManager dynamicRegistryManager = this.world.getRegistryManager(); + long l = this.world.getSeed(); + if (generator instanceof NoiseChunkGenerator noiseChunkGenerator) { + this.plasmid$noiseConfig = NoiseConfig.create(noiseChunkGenerator.getSettings().value(), dynamicRegistryManager.getWrapperOrThrow(RegistryKeys.NOISE_PARAMETERS), l); + } else { + this.plasmid$noiseConfig = NoiseConfig.create(ChunkGeneratorSettings.createMissingSettings(), dynamicRegistryManager.getWrapperOrThrow(RegistryKeys.NOISE_PARAMETERS), l); + } + + this.plasmid$structurePlacementCalculator = generator.createStructurePlacementCalculator(dynamicRegistryManager.getWrapperOrThrow(RegistryKeys.STRUCTURE_SET), this.plasmid$noiseConfig, l); + } + + @Override + public void plasmid$clearChunks(LongSet chunksToDrop) { + // Wait for any active generation work to complete + for (ChunkHolder chunkHolder : this.currentChunkHolders.values()) { + CompletableFuture savingFuture = chunkHolder.getSavingFuture(); + this.mainThreadExecutor.runTasks(savingFuture::isDone); + } + + System.out.println(); + + this.plasmid$chunksToDropData.addAll(chunksToDrop); + + Long2ByteMap oldLevels = new Long2ByteOpenHashMap(); + + List> futures = new ArrayList<>(); + for (ChunkHolder chunkHolder : this.currentChunkHolders.values()) { + ChunkPos pos = chunkHolder.getPos(); + ((ServerLightingProviderAccessor) this.lightingProvider).plasmid$updateChunkStatus(pos); + futures.add(this.lightingProvider.enqueue(pos.x, pos.z)); + oldLevels.put(pos.toLong(), (byte) chunkHolder.getLevel()); + + WorldChunk chunk = chunkHolder.getAccessibleChunk(); + if (chunk != null) { + for (BlockPos blockPos : chunk.getBlockEntityPositions()) { + chunk.removeBlockEntity(blockPos); + } + } + } + + this.lightingProvider.tick(); + for (CompletableFuture future : futures) { + this.mainThreadExecutor.runTasks(future::isDone); + } + + this.loadedChunks.clear(); + this.currentChunkHolders.clear(); + this.chunkHolders.clear(); + this.chunkHolderListDirty = true; + this.unloadedChunks.clear(); + this.unloadTaskQueue.clear(); + this.chunkToType.clear(); + this.chunkToNextSaveTimeMs.clear(); + + var toDelete = this.world.getEntitiesByType( + TypeFilter.instanceOf(Entity.class), + e -> !(e instanceof ServerPlayerEntity) && chunksToDrop.contains(e.getChunkPos().toLong()) + ); + + for (var entity : toDelete) { + entity.remove(Entity.RemovalReason.DISCARDED); + } + + ((ServerEntityManagerAccess) this.world.entityManager).plasmid$clearChunks(chunksToDrop); + + for (Long2ByteMap.Entry entry : Long2ByteMaps.fastIterable(oldLevels)) { + ChunkPos pos = new ChunkPos(entry.getLongKey()); + int level = entry.getByteValue(); + if (ChunkLevels.isAccessible(level)) { + ChunkHolder holder = new ChunkHolder(pos, level, this.world, this.lightingProvider, this.chunkTaskPrioritySystem, (ThreadedAnvilChunkStorage) (Object) this); + this.currentChunkHolders.put(pos.toLong(), holder); + } + } + + for (ChunkHolder chunkHolder : this.currentChunkHolders.values()) { + ((ChunkHolderAccessor) chunkHolder).plasmid$updateFutures((ThreadedAnvilChunkStorage) (Object) this, this.mainThreadExecutor); + } + } +} diff --git a/src/main/resources/plasmid.accesswidener b/src/main/resources/plasmid.accesswidener index 2f65b698..512b3be3 100644 --- a/src/main/resources/plasmid.accesswidener +++ b/src/main/resources/plasmid.accesswidener @@ -16,3 +16,5 @@ accessible field net/minecraft/world/GameRules$IntRule value I accessible method net/minecraft/entity/Entity unsetRemoved ()V accessible method net/minecraft/server/network/ServerPlayerInteractionManager setGameMode (Lnet/minecraft/world/GameMode;Lnet/minecraft/world/GameMode;)V + +accessible field net/minecraft/server/world/ServerWorld entityManager Lnet/minecraft/server/world/ServerEntityManager; diff --git a/src/main/resources/plasmid.mixins.json b/src/main/resources/plasmid.mixins.json index 4b302c15..ddf1f9fd 100644 --- a/src/main/resources/plasmid.mixins.json +++ b/src/main/resources/plasmid.mixins.json @@ -19,6 +19,10 @@ "game.space.PlayerManagerMixin", "game.space.ScreenHandlerMixin", "game.space.ServerPlayerEntityMixin", + "game.world.ChunkHolderAccessor", + "game.world.ServerEntityManagerMixin", + "game.world.ServerLightingProviderAccessor", + "game.world.ThreadedAnvilChunkStorageMixin", "storage.ServerWorldMixin" ], "injectors": {