diff --git a/src/main/java/appeng/tile/AEBaseBlockEntity.java b/src/main/java/appeng/tile/AEBaseBlockEntity.java index 6ceb2570b..d81e643a0 100644 --- a/src/main/java/appeng/tile/AEBaseBlockEntity.java +++ b/src/main/java/appeng/tile/AEBaseBlockEntity.java @@ -20,7 +20,6 @@ package appeng.tile; import java.io.IOException; import java.lang.ref.WeakReference; -import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -31,7 +30,6 @@ import javax.annotation.Nullable; import io.netty.buffer.Unpooled; import net.fabricmc.fabric.api.block.entity.BlockEntityClientSerializable; -import net.fabricmc.fabric.api.event.lifecycle.v1.ServerChunkEvents; import net.fabricmc.fabric.api.rendering.data.v1.RenderAttachmentBlockEntity; import net.minecraft.block.BlockState; import net.minecraft.block.entity.BlockEntity; @@ -69,24 +67,8 @@ import appeng.util.SettingsFrom; public class AEBaseBlockEntity extends BlockEntity implements IOrientable, ICommonTile, ICustomNameObject, BlockEntityClientSerializable, RenderAttachmentBlockEntity, AttributeProvider { - // FIXME: should probably remove at start of next server tick! static { - ServerChunkEvents.CHUNK_UNLOAD.register((serverWorld, worldChunk) -> { - List entitiesToRemove = null; - for (BlockEntity value : worldChunk.getBlockEntities().values()) { - if (value instanceof AEBaseBlockEntity) { - if (entitiesToRemove == null) { - entitiesToRemove = new ArrayList<>(); - } - entitiesToRemove.add((AEBaseBlockEntity) value); - } - } - if (entitiesToRemove != null) { - for (AEBaseBlockEntity blockEntity : entitiesToRemove) { - blockEntity.onChunkUnloaded(); - } - } - }); + DeferredTileEntityUnloader.register(); } protected void onChunkUnloaded() { diff --git a/src/main/java/appeng/tile/DeferredTileEntityUnloader.java b/src/main/java/appeng/tile/DeferredTileEntityUnloader.java new file mode 100644 index 000000000..6767003fc --- /dev/null +++ b/src/main/java/appeng/tile/DeferredTileEntityUnloader.java @@ -0,0 +1,68 @@ +/* + * This file is part of Applied Energistics 2. + * Copyright (c) 2013 - 2014, AlgorithmX2, All rights reserved. + * + * Applied Energistics 2 is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Applied Energistics 2 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Applied Energistics 2. If not, see . + */ +package appeng.tile; + +import appeng.hooks.TickHandler; +import appeng.util.IWorldCallable; +import net.fabricmc.fabric.api.event.lifecycle.v1.ServerChunkEvents; +import net.minecraft.block.entity.BlockEntity; +import net.minecraft.world.World; + +import java.util.ArrayList; +import java.util.List; + +/** + * We need to defer actually unloading tile entities until the end of the tick, + * after the chunk has been saved to disk. The CHUNK_UNLOAD event runs before + * the chunk has been saved, and if we disconnect nodes at that point, the saved + * data will be missing information from the node (such as the player id). + */ +class DeferredTileEntityUnloader implements IWorldCallable { + + public static void register() { + ServerChunkEvents.CHUNK_UNLOAD.register((serverWorld, worldChunk) -> { + List entitiesToRemove = null; + for (BlockEntity value : worldChunk.getBlockEntities().values()) { + if (value instanceof AEBaseBlockEntity) { + if (entitiesToRemove == null) { + entitiesToRemove = new ArrayList<>(); + } + entitiesToRemove.add((AEBaseBlockEntity) value); + } + } + if (entitiesToRemove != null) { + TickHandler.instance().addCallable(serverWorld, new DeferredTileEntityUnloader(entitiesToRemove)); + } + }); + } + + private final List entitiesToRemove; + + public DeferredTileEntityUnloader(List entitiesToRemove) { + this.entitiesToRemove = entitiesToRemove; + } + + @Override + public Void call(World world) { + for (AEBaseBlockEntity blockEntity : entitiesToRemove) { + blockEntity.onChunkUnloaded(); + } + return null; + } + +}