From fecd3c08f7c5634d31cb0ea4038dd9b7c8ad8377 Mon Sep 17 00:00:00 2001 From: Anatoly Belikov Date: Wed, 14 Dec 2022 11:47:58 +0400 Subject: [PATCH] fix memory leak (#28) --- .../Server/ServerStateMachine.java | 19 +++--- .../events/ServerEntityEventsVereya.java | 68 +++++++++++++++++++ .../mixin/ServerEntityManagerMixin.java | 14 ++++ .../mixin/ServerWorldEntityLoaderMixin.java | 29 ++++++++ .../mixin/ServerWorldMixin.java | 26 +++++++ src/main/resources/vereya.mixins.json | 3 +- 6 files changed, 150 insertions(+), 9 deletions(-) create mode 100644 src/main/java/io/singularitynet/events/ServerEntityEventsVereya.java create mode 100644 src/main/java/io/singularitynet/mixin/ServerEntityManagerMixin.java create mode 100644 src/main/java/io/singularitynet/mixin/ServerWorldEntityLoaderMixin.java create mode 100644 src/main/java/io/singularitynet/mixin/ServerWorldMixin.java diff --git a/src/main/java/io/singularitynet/Server/ServerStateMachine.java b/src/main/java/io/singularitynet/Server/ServerStateMachine.java index ad9e1a8e..17f53231 100644 --- a/src/main/java/io/singularitynet/Server/ServerStateMachine.java +++ b/src/main/java/io/singularitynet/Server/ServerStateMachine.java @@ -20,13 +20,13 @@ import io.singularitynet.*; import io.singularitynet.MissionHandlers.MissionBehaviour; +import io.singularitynet.events.ServerEntityEventsVereya; import io.singularitynet.projectmalmo.*; import io.singularitynet.utils.SchemaHelper; import io.singularitynet.utils.ScreenHelper; import jakarta.xml.bind.JAXBElement; import jakarta.xml.bind.JAXBException; -import net.fabricmc.fabric.api.event.lifecycle.v1.ServerEntityEvents; import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents; import net.fabricmc.fabric.api.event.lifecycle.v1.ServerTickEvents; import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking; @@ -40,6 +40,7 @@ import net.minecraft.server.MinecraftServer; import net.minecraft.server.network.ServerPlayerEntity; import net.minecraft.server.world.ServerWorld; +import net.minecraft.util.ActionResult; import net.minecraft.world.GameMode; import net.minecraft.world.GameRules; import net.minecraft.world.World; @@ -84,11 +85,11 @@ public ServerStateMachine(ServerState initialState, MissionInit minit, Minecraft // Register ourself on the event busses, so we can harness the server tick: ServerTickEvents.END_SERVER_TICK.register(s -> this.onServerTick(s)); ServerLifecycleEvents.SERVER_STOPPED.register(s -> this.onServerStopped(s)); - ServerEntityEvents.ENTITY_LOAD.register((e, w) -> this.onGetPotentialSpawns(e, w)); + ServerEntityEventsVereya.BEFORE_ENTITY_ADD.register((e, w) -> this.onGetPotentialSpawns(e, w)); } /** Used to prevent spawning in our world.*/ - public void onGetPotentialSpawns(Entity entity, ServerWorld world) + public ActionResult onGetPotentialSpawns(Entity entity, ServerWorld world) { // Decide whether or not to allow spawning. // We shouldn't allow spawning unless it has been specifically turned on - whether @@ -114,24 +115,26 @@ public void onGetPotentialSpawns(Entity entity, ServerWorld world) if (mob.value().toLowerCase().equals(mobName.toLowerCase())) { allowed = true; } } if (!allowed) { - if (entity.isPlayer()) return; - if (!entity.isLiving()) return; + if (entity.isPlayer()) return ActionResult.PASS; + if (!entity.isLiving()) return ActionResult.PASS; LOGGER.trace("removing mob " + mobName + ": it's disabled"); entity.remove(Entity.RemovalReason.DISCARDED); - return; + return ActionResult.FAIL; } } } // Cancel spawn event: if (!allowSpawning) { - if (!entity.isLiving()) return; + if (!entity.isLiving()) return ActionResult.PASS; LOGGER.trace("removing mob " + mobName + " : spawning is disabled"); if (entity.isPlayer()) { LOGGER.info("not removing player " + ((ServerPlayerEntity)entity).getName().getString()); - return; + return ActionResult.PASS; } entity.remove(Entity.RemovalReason.DISCARDED); + return ActionResult.FAIL; } + return ActionResult.PASS; } private void onServerStopped(MinecraftServer s){ diff --git a/src/main/java/io/singularitynet/events/ServerEntityEventsVereya.java b/src/main/java/io/singularitynet/events/ServerEntityEventsVereya.java new file mode 100644 index 00000000..ab953ca3 --- /dev/null +++ b/src/main/java/io/singularitynet/events/ServerEntityEventsVereya.java @@ -0,0 +1,68 @@ +package io.singularitynet.events; + +import net.fabricmc.fabric.api.event.Event; +import net.fabricmc.fabric.api.event.EventFactory; +import net.minecraft.entity.Entity; +import net.minecraft.server.world.ServerWorld; +import net.minecraft.util.ActionResult; +import net.minecraft.util.profiler.Profiler; + +public interface ServerEntityEventsVereya { + + /** + * Called when an Entity is about to be loaded into a ServerWorld. + * + *

Can be used to cancel entity loading. + */ + public static final Event BEFORE_ENTITY_LOAD = EventFactory.createArrayBacked(ServerEntityEventsVereya.class, callbacks -> (entity, world) -> { + if (EventFactory.isProfilingEnabled()) { + final Profiler profiler = world.getProfiler(); + profiler.push("fabricServerEntityLoadBefore"); + + for (ServerEntityEventsVereya callback : callbacks) { + profiler.push(EventFactory.getHandlerName(callback)); + ActionResult result = callback.interact(entity, world); + profiler.pop(); + if (result != ActionResult.PASS) { + return result; + } + } + profiler.pop(); + } else { + for (ServerEntityEventsVereya callback : callbacks) { + ActionResult result = callback.interact(entity, world); + if (result != ActionResult.PASS) { + return result; + } + } + } + return ActionResult.PASS; + }); + + public static final Event BEFORE_ENTITY_ADD = EventFactory.createArrayBacked(ServerEntityEventsVereya.class, callbacks -> (entity, world) -> { + if (EventFactory.isProfilingEnabled()) { + final Profiler profiler = world.getProfiler(); + profiler.push("fabricServerEntityAddBefore"); + + for (ServerEntityEventsVereya callback : callbacks) { + profiler.push(EventFactory.getHandlerName(callback)); + ActionResult result = callback.interact(entity, world); + profiler.pop(); + if (result != ActionResult.PASS) { + return result; + } + } + profiler.pop(); + } else { + for (ServerEntityEventsVereya callback : callbacks) { + ActionResult result = callback.interact(entity, world); + if (result != ActionResult.PASS) { + return result; + } + } + } + return ActionResult.PASS; + }); + + ActionResult interact( Entity entity, ServerWorld world); +} diff --git a/src/main/java/io/singularitynet/mixin/ServerEntityManagerMixin.java b/src/main/java/io/singularitynet/mixin/ServerEntityManagerMixin.java new file mode 100644 index 00000000..3ddad208 --- /dev/null +++ b/src/main/java/io/singularitynet/mixin/ServerEntityManagerMixin.java @@ -0,0 +1,14 @@ +package io.singularitynet.mixin; + +import net.minecraft.entity.Entity; +import net.minecraft.server.world.ServerEntityManager; +import net.minecraft.world.entity.EntityLike; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Invoker; + +@Mixin(ServerEntityManager.class) +public interface ServerEntityManagerMixin { + + @Invoker("stopTracking") + public void stopTracking(T entity); +} diff --git a/src/main/java/io/singularitynet/mixin/ServerWorldEntityLoaderMixin.java b/src/main/java/io/singularitynet/mixin/ServerWorldEntityLoaderMixin.java new file mode 100644 index 00000000..d9384991 --- /dev/null +++ b/src/main/java/io/singularitynet/mixin/ServerWorldEntityLoaderMixin.java @@ -0,0 +1,29 @@ +package io.singularitynet.mixin; + +import io.singularitynet.events.ServerEntityEventsVereya; +import net.minecraft.entity.Entity; +import net.minecraft.server.world.ServerWorld; +import net.minecraft.util.ActionResult; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(targets = "net/minecraft/server/world/ServerWorld$ServerEntityHandler") +abstract class ServerWorldEntityLoaderMixin { + // final synthetic Lnet/minecraft/server/world/ServerWorld; field_26936 + @SuppressWarnings("ShadowTarget") + @Shadow + @Final + private ServerWorld field_26936; + + @Inject(method = "startTracking(Lnet/minecraft/entity/Entity;)V", at = @At("HEAD"), cancellable = true) + private void invokeEntityLoadEvent(Entity entity, CallbackInfo ci) { + ActionResult result = ServerEntityEventsVereya.BEFORE_ENTITY_LOAD.invoker().interact(entity, this.field_26936); + if (result == ActionResult.FAIL) { + ci.cancel(); + } + } +} diff --git a/src/main/java/io/singularitynet/mixin/ServerWorldMixin.java b/src/main/java/io/singularitynet/mixin/ServerWorldMixin.java new file mode 100644 index 00000000..b66356e8 --- /dev/null +++ b/src/main/java/io/singularitynet/mixin/ServerWorldMixin.java @@ -0,0 +1,26 @@ +package io.singularitynet.mixin; + +import io.singularitynet.events.ServerEntityEventsVereya; +import net.minecraft.entity.Entity; +import net.minecraft.server.world.ServerWorld; +import net.minecraft.util.ActionResult; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +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; + +@Mixin(ServerWorld.class) +class ServerWorldMixin { + + @Inject(method = "Lnet/minecraft/server/world/ServerWorld;addEntity(Lnet/minecraft/entity/Entity;)Z", at = @At("HEAD"), cancellable = true) + void invokeEntityAddEvent(Entity entity, CallbackInfoReturnable cir) { + ActionResult result = ServerEntityEventsVereya.BEFORE_ENTITY_ADD.invoker().interact(entity, (ServerWorld)(Object)this); + if (result == ActionResult.FAIL) { + cir.cancel(); + } + } +} + diff --git a/src/main/resources/vereya.mixins.json b/src/main/resources/vereya.mixins.json index 078fc2cb..2404b25d 100644 --- a/src/main/resources/vereya.mixins.json +++ b/src/main/resources/vereya.mixins.json @@ -6,7 +6,8 @@ "mixins": [], "client": [ "TitleScreenMixin", "SessionMixin", "MinecraftClientMixin", "GameOptionsMixin", - "KeyBindingMixin", "MouseMixin", "InGameHudMixin" + "KeyBindingMixin", "MouseMixin", "InGameHudMixin", "ServerWorldEntityLoaderMixin", + "ServerEntityManagerMixin", "ServerWorldMixin" ], "server": [], "injectors": {