<commit_before>package org.cyclops.cyclopscore.persist.world;

import lombok.experimental.Delegate;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.world.WorldSavedData;
import net.minecraftforge.fml.common.FMLCommonHandler;
import net.minecraftforge.fml.common.event.FMLServerAboutToStartEvent;
import net.minecraftforge.fml.common.event.FMLServerStartedEvent;
import net.minecraftforge.fml.common.event.FMLServerStoppingEvent;
import org.cyclops.cyclopscore.init.ModBase;
import org.cyclops.cyclopscore.persist.nbt.INBTProvider;
import org.cyclops.cyclopscore.persist.nbt.NBTProviderComponent;

/**
 * Instances of this can store data inside the world NBT.
 * @author rubensworks
 */
public abstract class WorldStorage implements INBTProvider {

    protected final ModBase mod;
    @Delegate
    private INBTProvider nbtProviderComponent = new NBTProviderComponent(this);

    public WorldStorage(ModBase mod) {
        this.mod = mod;
    }

    /**
     * Read the counters.
     * @param tag The tag to read from.
     */
    public void readFromNBT(NBTTagCompound tag) {
        readGeneratedFieldsFromNBT(tag);
    }

    /**
     * Write the counters.
     * @param tag The tag to write to.
     */
    public void writeToNBT(NBTTagCompound tag) {
        writeGeneratedFieldsToNBT(tag);
    }

    /**
     * Reset the stored data because it will be reloaded from NBT.
     */
    public abstract void reset();

    /**
     * When a server is started.
     * @param event The received event.
     */
    public void onAboutToStartEvent(FMLServerAboutToStartEvent event) {
        reset();
    }

    /**
     * When a server is started.
     * @param event The received event.
     */
    public void onStartedEvent(FMLServerStartedEvent event) {
        loadData();
        afterLoad();
    }

    /**
     * When a server is stopping.
     * @param event The received event.
     */
    public void onStoppingEvent(FMLServerStoppingEvent event) {
        beforeSave();
        saveData();
    }

    protected abstract String getDataId();

    private NBTDataHolder getDataHolder() {
        String dataId = mod.getModId() + "_" + getDataId();
        NBTDataHolder data = (NBTDataHolder) FMLCommonHandler.instance().getMinecraftServerInstance().worldServers[0].
                loadItemData(NBTDataHolder.class, dataId);
        if(data == null) {
            data = new NBTDataHolder(dataId);
            FMLCommonHandler.instance().getMinecraftServerInstance().worldServers[0].setItemData(dataId, data);
        }
        return data;
    }

    private synchronized void loadData() {
        readFromNBT(getDataHolder().tag);
    }

    private synchronized void saveData() {
        NBTDataHolder data = getDataHolder();
        writeToNBT(data.tag);
        data.setDirty(true);
    }

    /**
     * Called after the data is loaded from the world storage.
     */
    public void afterLoad() {

    }

    /**
     * Called before the data is saved to the world storage.
     */
    public void beforeSave() {

    }

    /**
     * Data holder for the global counter data.
     */
    public static class NBTDataHolder extends WorldSavedData {

        private static String KEY = "WorldStorageData";

        public NBTTagCompound tag;

        /**
         * Make a new instance.
         * @param key The key for the global counter data.
         */
        public NBTDataHolder(String key) {
            super(key);
            this.tag = new NBTTagCompound();
        }

        @Override
        public void readFromNBT(NBTTagCompound tag) {
            this.tag = tag.getCompoundTag(KEY);
        }

        @Override
        public NBTTagCompound writeToNBT(NBTTagCompound tag) {
            tag.setTag(KEY, this.tag);
            return tag;
        }

    }<commit_msg>Ensure WorldStorage saving on server crashes The problem was that a server crash does not trigger FMLServerStoppingEvent, so a the data holder did not contain the latest data in that case.<commit_after>package org.cyclops.cyclopscore.persist.world;

import lombok.experimental.Delegate;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.world.WorldSavedData;
import net.minecraftforge.fml.common.FMLCommonHandler;
import net.minecraftforge.fml.common.event.FMLServerAboutToStartEvent;
import net.minecraftforge.fml.common.event.FMLServerStartedEvent;
import net.minecraftforge.fml.common.event.FMLServerStoppingEvent;
import org.cyclops.cyclopscore.init.ModBase;
import org.cyclops.cyclopscore.persist.nbt.INBTProvider;
import org.cyclops.cyclopscore.persist.nbt.NBTProviderComponent;

/**
 * Instances of this can store data inside the world NBT.
 * @author rubensworks
 */
public abstract class WorldStorage implements INBTProvider {

    private static String KEY = "WorldStorageData";

    protected final ModBase mod;
    @Delegate
    private INBTProvider nbtProviderComponent = new NBTProviderComponent(this);

    public WorldStorage(ModBase mod) {
        this.mod = mod;
    }

    /**
     * Read the counters.
     * @param tag The tag to read from.
     */
    public void readFromNBT(NBTTagCompound tag) {
        readGeneratedFieldsFromNBT(tag);
    }

    /**
     * Write the counters.
     * @param tag The tag to write to.
     */
    public void writeToNBT(NBTTagCompound tag) {
        writeGeneratedFieldsToNBT(tag);
    }

    /**
     * Reset the stored data because it will be reloaded from NBT.
     */
    public abstract void reset();

    /**
     * When a server is started.
     * @param event The received event.
     */
    public void onAboutToStartEvent(FMLServerAboutToStartEvent event) {
        reset();
    }

    /**
     * When a server is started.
     * @param event The received event.
     */
    public void onStartedEvent(FMLServerStartedEvent event) {
        loadData();
        afterLoad();
    }

    /**
     * When a server is stopping.
     * @param event The received event.
     */
    public void onStoppingEvent(FMLServerStoppingEvent event) {
        beforeSave();
        saveData();
    }

    protected abstract String getDataId();

    private NBTDataHolder initDataHolder(boolean loading) {
        String dataId = mod.getModId() + "_" + getDataId();
        NBTDataHolder data = (NBTDataHolder) FMLCommonHandler.instance().getMinecraftServerInstance().worldServers[0].
                loadItemData(NBTDataHolder.class, dataId);
        if(data == null) {
            data = new NBTDataHolder(dataId);
            FMLCommonHandler.instance().getMinecraftServerInstance().worldServers[0].setItemData(dataId, data);
        } else if (loading) {
            NBTTagCompound tempTag = data.getTempTagAndReset();
            if (tempTag != null) {
                readFromNBT(tempTag);
            }
        }
        data.setParentStorage(this);
        return data;
    }

    private synchronized void loadData() {
        initDataHolder(true);
    }

    private synchronized void saveData() {
        initDataHolder(false);
    }

    /**
     * Called after the data is loaded from the world storage.
     */
    public void afterLoad() {

    }

    /**
     * Called before the data is saved to the world storage.
     */
    public void beforeSave() {

    }

    /**
     * Data holder for the global counter data.
     */
    public static class NBTDataHolder extends WorldSavedData {

        private WorldStorage parentStorage = null;
        private NBTTagCompound tempTag = null;

        /**
         * Make a new instance.
         * @param key The key for the global counter data.
         */
        public NBTDataHolder(String key) {
            super(key);
        }

        @Override
        public void readFromNBT(NBTTagCompound tag) {
            if (parentStorage == null) {
                this.tempTag = tag.getCompoundTag(KEY);
            } else {
                parentStorage.readFromNBT(tag.getCompoundTag(KEY));
            }
        }

        @Override
        public NBTTagCompound writeToNBT(NBTTagCompound tag) {
            NBTTagCompound dataTag = new NBTTagCompound();
            parentStorage.writeToNBT(dataTag);
            tag.setTag(KEY, dataTag);
            return tag;
        }

        @Override
        public boolean isDirty() {
            // If this proves to be too inefficient, add a decent implementation for this.
            return true;
        }

        public void setParentStorage(WorldStorage parentStorage) {
            this.parentStorage = parentStorage;
        }

        public NBTTagCompound getTempTagAndReset() {
            NBTTagCompound tempTag = this.tempTag;
            this.tempTag = null;
            return tempTag;
        }
    }
{'fraction_non_alphanumeric': 0.08755601516718373, 'fraction_numerical': 0.0004596116281741928, 'mean_word_length': 2.3775708187815288, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4045635', 'n_tokens_mistral': 2505, 'n_tokens_neox': 2436, 'n_words': 625}