Skip to content

Commit

Permalink
Fix block entity memory leak (API 9) (#3813)
Browse files Browse the repository at this point in the history
* Fix block entity memory leak

* Update src/mixins/java/org/spongepowered/common/mixin/core/server/level/ServerLevelMixin.java
  • Loading branch information
Lignium committed Jan 13, 2023
1 parent 687a135 commit 02d449c
Show file tree
Hide file tree
Showing 2 changed files with 28 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.entity.JukeboxBlockEntity;
import net.minecraft.world.level.block.entity.TickingBlockEntity;
import net.minecraft.world.level.chunk.ChunkGenerator;
import net.minecraft.world.level.dimension.DimensionType;
import net.minecraft.world.level.gameevent.GameEvent;
Expand All @@ -57,6 +58,7 @@
import net.minecraft.world.level.storage.ServerLevelData;
import net.minecraft.world.level.storage.WorldData;
import net.minecraft.world.ticks.LevelTicks;
import org.objectweb.asm.Opcodes;
import org.spongepowered.api.ResourceKey;
import org.spongepowered.api.Sponge;
import org.spongepowered.api.block.BlockSnapshot;
Expand Down Expand Up @@ -520,6 +522,29 @@ public void save(@Nullable final ProgressListener progress, final boolean flush,
manager.add(pos, type);
}

@Inject(
method = "tick",
at = @At(
value = "FIELD",
target = "Lnet/minecraft/server/level/ServerLevel;emptyTime:I",
opcode = Opcodes.PUTFIELD,
shift = At.Shift.AFTER
)
)
private void impl$unloadBlockEntities(final BooleanSupplier param0, final CallbackInfo ci) {
/*
* This code fixes block entity memory leak when the level hasn't online players
* and forced chunks. For the first 300 ticks the level can still clean up removed
* block entities on its own (ticks are performed for block entities). After it
* this mixin code is responsible for the subsequent unloading of block entities.
* Such a memory leak occurs when a plugin writes a lot of blocks, but the level
* is without players.
*/
if (this.emptyTime >= 300 && !this.blockEntityTickers.isEmpty()) {
this.blockEntityTickers.removeIf(TickingBlockEntity::isRemoved);
}
}

@Override
public String toString() {
final Optional<ResourceKey> worldTypeKey = RegistryTypes.WORLD_TYPE.get().findValueKey((WorldType) this.shadow$dimensionType());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
import net.minecraft.world.level.Level;
import net.minecraft.world.level.LevelAccessor;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.entity.TickingBlockEntity;
import net.minecraft.world.level.border.WorldBorder;
import net.minecraft.world.level.chunk.LevelChunk;
import net.minecraft.world.level.dimension.DimensionType;
Expand Down Expand Up @@ -67,6 +68,7 @@
import org.spongepowered.common.util.DataUtil;
import org.spongepowered.math.vector.Vector3d;

import java.util.List;
import java.util.function.Predicate;

@Mixin(net.minecraft.world.level.Level.class)
Expand All @@ -78,6 +80,7 @@ public abstract class LevelMixin implements LevelBridge, LevelAccessor {
@Shadow protected float rainLevel;
@Shadow protected float oThunderLevel;
@Shadow protected float thunderLevel;
@Shadow @Final protected List<TickingBlockEntity> blockEntityTickers;

@Shadow public abstract LevelData shadow$getLevelData();
@Shadow public abstract void shadow$updateSkyBrightness();
Expand Down

0 comments on commit 02d449c

Please sign in to comment.