<commit_before>package org.cyclops.integrateddynamics.core.network;

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.common.capabilities.Capability;
import net.minecraftforge.common.capabilities.CapabilityDispatcher;
import org.apache.logging.log4j.Level;
import org.cyclops.cyclopscore.helper.TileHelpers;
import org.cyclops.integrateddynamics.IntegratedDynamics;
import org.cyclops.integrateddynamics.api.network.*;
import org.cyclops.integrateddynamics.api.network.event.INetworkEvent;
import org.cyclops.integrateddynamics.api.network.event.INetworkEventBus;
import org.cyclops.integrateddynamics.api.path.IPathElement;
import org.cyclops.integrateddynamics.capability.network.NetworkCarrierConfig;
import org.cyclops.integrateddynamics.capability.networkelementprovider.NetworkElementProviderConfig;
import org.cyclops.integrateddynamics.core.network.diagnostics.NetworkDiagnostics;
import org.cyclops.integrateddynamics.core.network.event.NetworkElementAddEvent;
import org.cyclops.integrateddynamics.core.network.event.NetworkElementRemoveEvent;
import org.cyclops.integrateddynamics.core.network.event.NetworkEventBus;
import org.cyclops.integrateddynamics.core.path.Cluster;
import org.cyclops.integrateddynamics.core.path.PathFinder;
import org.cyclops.integrateddynamics.core.persist.world.NetworkWorldStorage;

import java.util.*;

/**
 * A network instance that can hold a set of {@link INetworkElement}s.
 * Note that this network only contains references to the relevant data, it does not contain the actual information.
 * @author rubensworks
 */
public class Network implements INetwork {

    private Cluster baseCluster;

    private final INetworkEventBus eventBus = new NetworkEventBus();
    private final TreeSet<INetworkElement> elements = Sets.newTreeSet();
    private TreeSet<INetworkElement> updateableElements = null;
    private TreeMap<INetworkElement, Integer> updateableElementsTicks = null;
    private Map<INetworkElement, Long> lastSecondDurations = Maps.newHashMap();

    private final CapabilityDispatcher capabilityDispatcher;
    private IFullNetworkListener[] fullNetworkListeners;

    private volatile boolean changed = false;
    private volatile boolean killed = false;

    private boolean crashed = false;

    /**
     * Initiate a full network from the given start position.
     * @param pathElement The path element to start from.
     * @return The newly formed network.
     */
    public static Network initiateNetworkSetup(IPathElement pathElement) {
        Network network = new Network(PathFinder.getConnectedCluster(pathElement));
        NetworkWorldStorage.getInstance(IntegratedDynamics._instance).addNewNetwork(network);
        return network;
    }

    /**
     * Check if two networks are equal.
     * @param networkA A network.
     * @param networkB Another network.
     * @return If they are equal.
     */
    public static boolean areNetworksEqual(Network networkA, Network networkB) {
        return networkA.elements.containsAll(networkB.elements) && networkA.elements.size() == networkB.elements.size();
    }

    /**
     * This constructor should not be called, except for the process of constructing networks from NBT.
     */
    public Network() {
        this.baseCluster = new Cluster();
        this.capabilityDispatcher = gatherCapabilities();
        onConstruct();
    }

    /**
     * Create a new network from a given cluster of path elements.
     * Each path element will be checked if it has a {@link INetworkElementProvider} capability at its position
     * and will add all its elements to the network in that case.
     * Each path element that has an {@link org.cyclops.integrateddynamics.api.part.IPartContainer} capability
     * will have the network stored in its part container.
     * @param pathElements The path elements that make up the connections in the network which can potentially provide network
     *               elements.
     */
    public Network(Cluster pathElements) {
        this.baseCluster = pathElements;
        this.capabilityDispatcher = gatherCapabilities();
        onConstruct();
        deriveNetworkElements(baseCluster);
    }

    protected CapabilityDispatcher gatherCapabilities() {
        AttachCapabilitiesEventNetwork event = new AttachCapabilitiesEventNetwork(this);
        MinecraftForge.EVENT_BUS.post(event);
        List<IFullNetworkListener> listeners = event.getFullNetworkListeners();
        this.fullNetworkListeners = listeners.toArray(new IFullNetworkListener[listeners.size()]);
        return event.getCapabilities().size() > 0 ? new CapabilityDispatcher(event.getCapabilities()) : null;
    }

    protected IFullNetworkListener[] gatherFullNetworkListeners() {
        List<IFullNetworkListener> listeners = Lists.newArrayList();

        return listeners.toArray(new IFullNetworkListener[listeners.size()]);
    }

    protected void onConstruct() {

    }

    private void deriveNetworkElements(Cluster pathElements) {
        if(!killIfEmpty()) {
            for (IPathElement pathElement : pathElements) {
                World world = pathElement.getPosition().getWorld();
                BlockPos pos = pathElement.getPosition().getBlockPos();
                INetworkElementProvider networkElementProvider = (INetworkElementProvider)
                        TileHelpers.getCapability(pathElement.getPosition(), null, NetworkElementProviderConfig.CAPABILITY);
                if (networkElementProvider != null) {
                    for(INetworkElement element : networkElementProvider.createNetworkElements(world, pos)) {
                        addNetworkElement(element, true);
                    }
                }
                INetworkCarrier networkCarrier = TileHelpers.getCapability(world, pos, null, NetworkCarrierConfig.CAPABILITY);
                if (networkCarrier != null) {
                    // Correctly remove any previously saved network in this carrier
                    // and set the new network to this.
                    INetwork network = networkCarrier.getNetwork();
                    if (network != null) {
                        network.removePathElement(pathElement);
                    }
                    networkCarrier.setNetwork(null);
                    networkCarrier.setNetwork(this);
                }
            }
            onNetworkChanged();
        }
    }

    @Override
    public INetworkEventBus getEventBus() {
        return this.eventBus;
    }

    /**
     * Initialize the network element data.
     */
    public void initialize() {
        initialize(false);
    }

    @Override
    public boolean equals(Object object) {
        return object instanceof Network && areNetworksEqual(this, (Network) object);
    }

    @Override
    public NBTTagCompound toNBT() {
        NBTTagCompound tag = new NBTTagCompound();
        tag.setTag("baseCluster", this.baseCluster.toNBT());
        tag.setBoolean("crashed", this.crashed);<commit_msg>Fix network resets calling add/remove listeners in the wrong order<commit_after>package org.cyclops.integrateddynamics.core.network;

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.common.capabilities.Capability;
import net.minecraftforge.common.capabilities.CapabilityDispatcher;
import org.apache.logging.log4j.Level;
import org.cyclops.cyclopscore.helper.TileHelpers;
import org.cyclops.integrateddynamics.IntegratedDynamics;
import org.cyclops.integrateddynamics.api.network.*;
import org.cyclops.integrateddynamics.api.network.event.INetworkEvent;
import org.cyclops.integrateddynamics.api.network.event.INetworkEventBus;
import org.cyclops.integrateddynamics.api.path.IPathElement;
import org.cyclops.integrateddynamics.capability.network.NetworkCarrierConfig;
import org.cyclops.integrateddynamics.capability.networkelementprovider.NetworkElementProviderConfig;
import org.cyclops.integrateddynamics.core.network.diagnostics.NetworkDiagnostics;
import org.cyclops.integrateddynamics.core.network.event.NetworkElementAddEvent;
import org.cyclops.integrateddynamics.core.network.event.NetworkElementRemoveEvent;
import org.cyclops.integrateddynamics.core.network.event.NetworkEventBus;
import org.cyclops.integrateddynamics.core.path.Cluster;
import org.cyclops.integrateddynamics.core.path.PathFinder;
import org.cyclops.integrateddynamics.core.persist.world.NetworkWorldStorage;

import java.util.*;

/**
 * A network instance that can hold a set of {@link INetworkElement}s.
 * Note that this network only contains references to the relevant data, it does not contain the actual information.
 * @author rubensworks
 */
public class Network implements INetwork {

    private Cluster baseCluster;

    private final INetworkEventBus eventBus = new NetworkEventBus();
    private final TreeSet<INetworkElement> elements = Sets.newTreeSet();
    private TreeSet<INetworkElement> updateableElements = null;
    private TreeMap<INetworkElement, Integer> updateableElementsTicks = null;
    private Map<INetworkElement, Long> lastSecondDurations = Maps.newHashMap();

    private final CapabilityDispatcher capabilityDispatcher;
    private IFullNetworkListener[] fullNetworkListeners;

    private volatile boolean changed = false;
    private volatile boolean killed = false;

    private boolean crashed = false;

    /**
     * Initiate a full network from the given start position.
     * @param pathElement The path element to start from.
     * @return The newly formed network.
     */
    public static Network initiateNetworkSetup(IPathElement pathElement) {
        Network network = new Network(PathFinder.getConnectedCluster(pathElement));
        NetworkWorldStorage.getInstance(IntegratedDynamics._instance).addNewNetwork(network);
        return network;
    }

    /**
     * Check if two networks are equal.
     * @param networkA A network.
     * @param networkB Another network.
     * @return If they are equal.
     */
    public static boolean areNetworksEqual(Network networkA, Network networkB) {
        return networkA.elements.containsAll(networkB.elements) && networkA.elements.size() == networkB.elements.size();
    }

    /**
     * This constructor should not be called, except for the process of constructing networks from NBT.
     */
    public Network() {
        this.baseCluster = new Cluster();
        this.capabilityDispatcher = gatherCapabilities();
        onConstruct();
    }

    /**
     * Create a new network from a given cluster of path elements.
     * Each path element will be checked if it has a {@link INetworkElementProvider} capability at its position
     * and will add all its elements to the network in that case.
     * Each path element that has an {@link org.cyclops.integrateddynamics.api.part.IPartContainer} capability
     * will have the network stored in its part container.
     * @param pathElements The path elements that make up the connections in the network which can potentially provide network
     *               elements.
     */
    public Network(Cluster pathElements) {
        this.baseCluster = pathElements;
        this.capabilityDispatcher = gatherCapabilities();
        onConstruct();
        deriveNetworkElements(baseCluster);
    }

    protected CapabilityDispatcher gatherCapabilities() {
        AttachCapabilitiesEventNetwork event = new AttachCapabilitiesEventNetwork(this);
        MinecraftForge.EVENT_BUS.post(event);
        List<IFullNetworkListener> listeners = event.getFullNetworkListeners();
        this.fullNetworkListeners = listeners.toArray(new IFullNetworkListener[listeners.size()]);
        return event.getCapabilities().size() > 0 ? new CapabilityDispatcher(event.getCapabilities()) : null;
    }

    protected IFullNetworkListener[] gatherFullNetworkListeners() {
        List<IFullNetworkListener> listeners = Lists.newArrayList();

        return listeners.toArray(new IFullNetworkListener[listeners.size()]);
    }

    protected void onConstruct() {

    }

    private void deriveNetworkElements(Cluster pathElements) {
        if(!killIfEmpty()) {
            for (IPathElement pathElement : pathElements) {
                World world = pathElement.getPosition().getWorld();
                BlockPos pos = pathElement.getPosition().getBlockPos();
                INetworkCarrier networkCarrier = TileHelpers.getCapability(
                        world, pos, null, NetworkCarrierConfig.CAPABILITY);
                if (networkCarrier != null) {
                    // Correctly remove any previously saved network in this carrier
                    // and set the new network to this.
                    INetwork network = networkCarrier.getNetwork();
                    if (network != null) {
                        network.removePathElement(pathElement);
                    }
                    networkCarrier.setNetwork(null);
                    networkCarrier.setNetwork(this);
                }
                INetworkElementProvider networkElementProvider = TileHelpers.getCapability(
                        pathElement.getPosition(), null, NetworkElementProviderConfig.CAPABILITY);
                if (networkElementProvider != null) {
                    for(INetworkElement element : networkElementProvider.createNetworkElements(world, pos)) {
                        addNetworkElement(element, true);
                    }
                }
            }
            onNetworkChanged();
        }
    }

    @Override
    public INetworkEventBus getEventBus() {
        return this.eventBus;
    }

    /**
     * Initialize the network element data.
     */
    public void initialize() {
        initialize(false);
    }

    @Override
    public boolean equals(Object object) {
        return object instanceof Network && areNetworksEqual(this, (Network) object);
    }

    @Override
    public NBTTagCompound toNBT() {
        NBTTagCompound tag = new NBTTagCompound();
        tag.setTag("baseCluster", this.baseCluster.toNBT());
        tag.setBoolean("crashed", this.crashed);
{'fraction_non_alphanumeric': 0.08178003989270238, 'fraction_numerical': 0.0002751220854254075, 'mean_word_length': 3.246495327102804, 'pattern_counts': {'":': 0, '<': 15, '<?xml version=': 0, '>': 17, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3229425', 'n_tokens_mistral': 3725, 'n_tokens_neox': 3584, 'n_words': 903}