From f2427ee347504d78149fab9dcce23201f3394edf Mon Sep 17 00:00:00 2001 From: Sebastian Hartte Date: Sat, 19 Sep 2020 16:54:30 +0200 Subject: [PATCH] Fixes #4747: The chunk unload event was triggering before the chunk was actually saved to disk, which means disconnecting grid connections at that point led to grid nodes not saving their data to the chunk. --- .../java/appeng/tile/AEBaseBlockEntity.java | 20 +----- .../tile/DeferredTileEntityUnloader.java | 68 +++++++++++++++++++ 2 files changed, 69 insertions(+), 19 deletions(-) create mode 100644 src/main/java/appeng/tile/DeferredTileEntityUnloader.java 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; + } + +}