// 
// Decompiled by Procyon v0.6.0
// 

package com.hypixel.hytale.component;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import com.hypixel.hytale.metrics.MetricResults;
import com.hypixel.hytale.component.system.tick.TickingSystem;
import com.hypixel.hytale.component.system.MetricSystem;
import com.hypixel.hytale.component.metric.SystemMetricData;
import com.hypixel.hytale.component.system.QuerySystem;
import com.hypixel.hytale.component.system.ArchetypeChunkSystem;
import com.hypixel.hytale.codec.Codec;
import com.hypixel.hytale.component.data.unknown.UnknownComponents;
import com.hypixel.hytale.component.data.change.ComponentChange;
import com.hypixel.hytale.component.system.StoreSystem;
import com.hypixel.hytale.component.system.tick.ArchetypeTickingSystem;
import com.hypixel.hytale.component.system.WorldEventSystem;
import com.hypixel.hytale.component.event.WorldEventType;
import com.hypixel.hytale.component.system.EntityEventSystem;
import com.hypixel.hytale.component.event.EntityEventType;
import com.hypixel.hytale.component.system.EcsEvent;
import java.util.Iterator;
import java.util.Collection;
import com.hypixel.hytale.component.system.data.ArchetypeDataSystem;
import java.util.List;
import com.hypixel.hytale.component.task.ParallelRangeTask;
import com.hypixel.hytale.function.consumer.IntBiObjectConsumer;
import java.util.function.BiPredicate;
import java.util.function.BiConsumer;
import com.hypixel.hytale.component.system.RefChangeSystem;
import java.util.Objects;
import com.hypixel.hytale.component.system.RefSystem;
import com.hypixel.hytale.component.system.HolderSystem;
import javax.annotation.Nullable;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import com.hypixel.hytale.component.metric.ArchetypeChunkData;
import com.hypixel.hytale.component.query.ExactArchetypeQuery;
import com.hypixel.hytale.component.query.Query;
import java.util.concurrent.CompletableFuture;
import com.hypixel.hytale.component.system.tick.TickableSystem;
import java.util.concurrent.TimeUnit;
import com.hypixel.hytale.component.data.change.DataChange;
import com.hypixel.hytale.component.system.ISystem;
import com.hypixel.hytale.component.data.change.SystemChange;
import com.hypixel.hytale.component.data.change.ChangeType;
import java.util.Arrays;
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
import com.hypixel.hytale.common.util.ArrayUtil;
import java.util.ArrayDeque;
import com.hypixel.hytale.metrics.metric.HistoricMetric;
import it.unimi.dsi.fastutil.objects.Object2IntMap;
import java.util.BitSet;
import com.hypixel.hytale.component.system.data.EntityDataSystem;
import com.hypixel.hytale.component.data.ForEachTaskData;
import com.hypixel.hytale.component.system.tick.EntityTickingSystem;
import com.hypixel.hytale.component.task.ParallelTask;
import java.util.Deque;
import javax.annotation.Nonnull;
import com.hypixel.hytale.metrics.MetricsRegistry;

public class Store<ECS_TYPE> implements ComponentAccessor<ECS_TYPE>
{
    public static final Store[] EMPTY_ARRAY;
    @Nonnull
    public static final MetricsRegistry<Store<?>> METRICS_REGISTRY;
    @Nonnull
    private final ComponentRegistry<ECS_TYPE> registry;
    @Nonnull
    private final ECS_TYPE externalData;
    @Nonnull
    private final IResourceStorage resourceStorage;
    private final Deque<CommandBuffer<ECS_TYPE>> commandBuffers;
    private final Thread thread;
    @Nonnull
    private final ParallelTask<EntityTickingSystem.SystemTaskData<ECS_TYPE>> parallelTask;
    @Nonnull
    private final ParallelTask<ForEachTaskData<ECS_TYPE>> forEachTask;
    @Nonnull
    private final ParallelTask<EntityDataSystem.SystemTaskData<ECS_TYPE, ?, ?>> fetchTask;
    @Nonnull
    private final ProcessingCounter processing;
    private boolean shutdown;
    int storeIndex;
    private int entitiesSize;
    @Nonnull
    private Ref<ECS_TYPE>[] refs;
    @Nonnull
    private int[] entityToArchetypeChunk;
    @Nonnull
    private int[] entityChunkIndex;
    @Nonnull
    private BitSet[] systemIndexToArchetypeChunkIndexes;
    @Nonnull
    private BitSet[] archetypeChunkIndexesToSystemIndex;
    @Nonnull
    private final Object2IntMap<Archetype<ECS_TYPE>> archetypeToIndexMap;
    private int archetypeSize;
    @Nonnull
    private final BitSet archetypeChunkReuse;
    @Nonnull
    private ArchetypeChunk<ECS_TYPE>[] archetypeChunks;
    @Nonnull
    private Resource<ECS_TYPE>[] resources;
    @Nonnull
    private HistoricMetric[] systemMetrics;
    @Deprecated(forRemoval = true)
    private boolean disableProcessingAssert;
    static final /* synthetic */ boolean $assertionsDisabled;
    
    Store(@Nonnull final ComponentRegistry<ECS_TYPE> registry, final int storeIndex, @Nonnull final ECS_TYPE externalData, @Nonnull final IResourceStorage resourceStorage) {
        this.commandBuffers = new ArrayDeque<CommandBuffer<ECS_TYPE>>();
        this.thread = Thread.currentThread();
        this.parallelTask = new ParallelTask<EntityTickingSystem.SystemTaskData<ECS_TYPE>>(EntityTickingSystem.SystemTaskData::new);
        this.forEachTask = new ParallelTask<ForEachTaskData<ECS_TYPE>>(ForEachTaskData::new);
        this.fetchTask = new ParallelTask<EntityDataSystem.SystemTaskData<ECS_TYPE, ?, ?>>(EntityDataSystem.SystemTaskData::new);
        this.processing = new ProcessingCounter();
        this.refs = new Ref[16];
        this.entityToArchetypeChunk = new int[16];
        this.entityChunkIndex = new int[16];
        this.systemIndexToArchetypeChunkIndexes = ArrayUtil.EMPTY_BITSET_ARRAY;
        this.archetypeChunkIndexesToSystemIndex = ArrayUtil.EMPTY_BITSET_ARRAY;
        this.archetypeToIndexMap = new Object2IntOpenHashMap<Archetype<ECS_TYPE>>();
        this.archetypeChunkReuse = new BitSet();
        this.archetypeChunks = ArchetypeChunk.emptyArray();
        this.resources = Resource.EMPTY_ARRAY;
        this.systemMetrics = HistoricMetric.EMPTY_ARRAY;
        this.disableProcessingAssert = false;
        this.registry = registry;
        this.storeIndex = storeIndex;
        this.externalData = externalData;
        this.resourceStorage = resourceStorage;
        this.archetypeToIndexMap.defaultReturnValue(Integer.MIN_VALUE);
        Arrays.fill(this.entityToArchetypeChunk, Integer.MIN_VALUE);
        Arrays.fill(this.entityChunkIndex, Integer.MIN_VALUE);
    }
    
    @Nonnull
    CommandBuffer<ECS_TYPE> takeCommandBuffer() {
        this.assertThread();
        if (this.commandBuffers.isEmpty()) {
            return new CommandBuffer<ECS_TYPE>(this);
        }
        final CommandBuffer<ECS_TYPE> buffer = this.commandBuffers.pop();
        assert buffer.setThread();
        return buffer;
    }
    
    void storeCommandBuffer(@Nonnull final CommandBuffer<ECS_TYPE> commandBuffer) {
        this.assertThread();
        commandBuffer.validateEmpty();
        this.commandBuffers.add(commandBuffer);
    }
    
    public int getStoreIndex() {
        return this.storeIndex;
    }
    
    @Nonnull
    public ComponentRegistry<ECS_TYPE> getRegistry() {
        return this.registry;
    }
    
    @Nonnull
    @Override
    public ECS_TYPE getExternalData() {
        return this.externalData;
    }
    
    @Nonnull
    public IResourceStorage getResourceStorage() {
        return this.resourceStorage;
    }
    
    @Nonnull
    public ParallelTask<EntityTickingSystem.SystemTaskData<ECS_TYPE>> getParallelTask() {
        return this.parallelTask;
    }
    
    @Nonnull
    public ParallelTask<EntityDataSystem.SystemTaskData<ECS_TYPE, ?, ?>> getFetchTask() {
        return this.fetchTask;
    }
    
    @Nonnull
    public HistoricMetric[] getSystemMetrics() {
        this.assertThread();
        return this.systemMetrics;
    }
    
    public boolean isShutdown() {
        return this.shutdown;
    }
    
    void onAdd(@Nonnull final ComponentRegistry.Data<ECS_TYPE> data) {
        this.updateArchetypeIndexes(data);
        final int resourceSize = data.getResourceSize();
        this.resources = Arrays.copyOf(this.resources, resourceSize);
        for (int index = 0; index < resourceSize; ++index) {
            final ResourceType<ECS_TYPE, ? extends Resource<ECS_TYPE>> resourceType = (ResourceType<ECS_TYPE, ? extends Resource<ECS_TYPE>>)data.getResourceType(index);
            if (resourceType != null) {
                this.resources[index] = (Resource)this.resourceStorage.load(this, data, resourceType).join();
            }
        }
        for (int systemIndex = 0; systemIndex < data.getSystemSize(); ++systemIndex) {
            this.updateData(data, data, new SystemChange<Object>(ChangeType.REGISTERED, data.getSystem(systemIndex)));
        }
        this.systemMetrics = Arrays.copyOf(this.systemMetrics, data.getSystemSize());
        final SystemType<ECS_TYPE, TickableSystem<ECS_TYPE>> tickingSystemType = this.registry.getTickableSystemType();
        final BitSet systemIndexes = data.getSystemIndexesForType(tickingSystemType);
        int systemIndex2 = -1;
        while ((systemIndex2 = systemIndexes.nextSetBit(systemIndex2 + 1)) >= 0) {
            this.systemMetrics[systemIndex2] = HistoricMetric.builder(33333333L, TimeUnit.NANOSECONDS).addPeriod(1L, TimeUnit.SECONDS).addPeriod(1L, TimeUnit.MINUTES).addPeriod(5L, TimeUnit.MINUTES).build();
        }
    }
    
    public void shutdown() {
        if (this.shutdown) {
            throw new IllegalStateException("Store is already shutdown!");
        }
        this.registry.removeStore(this);
    }
    
    void shutdown0(@Nonnull final ComponentRegistry.Data<ECS_TYPE> data) {
        if (this.thread.isAlive() && !this.thread.equals(Thread.currentThread())) {
            throw new IllegalArgumentException("Unable to shutdown store while thread is still running!");
        }
        for (int systemIndex = data.getSystemSize() - 1; systemIndex >= 0; --systemIndex) {
            this.updateData(data, data, new SystemChange<Object>(ChangeType.UNREGISTERED, data.getSystem(systemIndex)));
        }
        this.saveAllResources0(data).join();
        this.processing.lock();
        try {
            for (int i = 0; i < this.entitiesSize; ++i) {
                this.refs[i].invalidate();
                this.refs[i] = null;
            }
        }
        finally {
            this.processing.unlock();
        }
        this.shutdown = true;
    }
    
    @Nonnull
    public CompletableFuture<Void> saveAllResources() {
        return this.saveAllResources0(this.registry.getData());
    }
    
    @Nonnull
    private CompletableFuture<Void> saveAllResources0(@Nonnull final ComponentRegistry.Data<ECS_TYPE> data) {
        final int resourceSize = data.getResourceSize();
        final CompletableFuture<Void>[] futures = new CompletableFuture[resourceSize];
        int idx = 0;
        for (int index = 0; index < resourceSize; ++index) {
            final ResourceType<ECS_TYPE, ? extends Resource<ECS_TYPE>> resourceType = (ResourceType<ECS_TYPE, ? extends Resource<ECS_TYPE>>)data.getResourceType(index);
            if (resourceType != null) {
                futures[idx++] = this.resourceStorage.save(this, data, (ResourceType<ECS_TYPE, Resource<ECS_TYPE>>)resourceType, this.resources[index]);
            }
        }
        return CompletableFuture.allOf((CompletableFuture<?>[])Arrays.copyOf(futures, idx));
    }
    
    public int getEntityCount() {
        return this.entitiesSize;
    }
    
    public int getEntityCountFor(@Nonnull final Query<ECS_TYPE> query) {
        this.assertThread();
        if (!(query instanceof ExactArchetypeQuery)) {
            int count = 0;
            for (int archetypeIndex = 0; archetypeIndex < this.archetypeSize; ++archetypeIndex) {
                final ArchetypeChunk<ECS_TYPE> archetypeChunk = this.archetypeChunks[archetypeIndex];
                if (archetypeChunk != null) {
                    if (query.test(archetypeChunk.getArchetype())) {
                        count += archetypeChunk.size();
                    }
                }
            }
            return count;
        }
        final ExactArchetypeQuery<ECS_TYPE> exactQuery = (ExactArchetypeQuery)query;
        final int archetypeIndex2 = this.archetypeToIndexMap.getInt(exactQuery.getArchetype());
        if (archetypeIndex2 != Integer.MIN_VALUE) {
            return this.archetypeChunks[archetypeIndex2].size();
        }
        return 0;
    }
    
    public int getEntityCountFor(final int systemIndex) {
        this.assertThread();
        int count = 0;
        final BitSet indexes = this.systemIndexToArchetypeChunkIndexes[systemIndex];
        int index = -1;
        while ((index = indexes.nextSetBit(index + 1)) >= 0) {
            count += this.archetypeChunks[index].size();
        }
        return count;
    }
    
    public int getArchetypeChunkCount() {
        this.assertThread();
        return this.archetypeSize;
    }
    
    @Nonnull
    public ArchetypeChunkData[] collectArchetypeChunkData() {
        this.assertThread();
        final ObjectArrayList<ArchetypeChunkData> result = new ObjectArrayList<ArchetypeChunkData>(this.archetypeSize);
        for (int i = 0; i < this.archetypeSize; ++i) {
            final ArchetypeChunk<ECS_TYPE> chunk = this.archetypeChunks[i];
            if (chunk != null) {
                final Archetype<ECS_TYPE> archetype = chunk.getArchetype();
                final String[] componentTypeNames = new String[archetype.count()];
                int nameIndex = 0;
                for (int j = archetype.getMinIndex(); j < archetype.length(); ++j) {
                    final ComponentType<ECS_TYPE, ? extends Component<ECS_TYPE>> componentType = (ComponentType<ECS_TYPE, ? extends Component<ECS_TYPE>>)archetype.get(j);
                    if (componentType != null) {
                        componentTypeNames[nameIndex++] = componentType.getTypeClass().getName();
                    }
                }
                result.add(new ArchetypeChunkData(componentTypeNames, chunk.size()));
            }
        }
        return result.toArray(ArchetypeChunkData[]::new);
    }
    
    public int getArchetypeChunkCountFor(final int systemIndex) {
        this.assertThread();
        return this.systemIndexToArchetypeChunkIndexes[systemIndex].cardinality();
    }
    
    protected void setEntityChunkIndex(@Nonnull final Ref<ECS_TYPE> ref, final int newEntityChunkIndex) {
        if (ref.isValid()) {
            this.entityChunkIndex[ref.getIndex()] = newEntityChunkIndex;
        }
    }
    
    @Nullable
    public Ref<ECS_TYPE> addEntity(@Nonnull final Archetype<ECS_TYPE> archetype, @Nonnull final AddReason reason) {
        this.assertThread();
        this.assertWriteProcessing();
        Component<ECS_TYPE>[] entityComponents;
        if (archetype.isEmpty()) {
            entityComponents = Component.EMPTY_ARRAY;
        }
        else {
            final ComponentRegistry.Data<ECS_TYPE> data = this.registry._internal_getData();
            entityComponents = new Component[archetype.length()];
            for (int i = archetype.getMinIndex(); i < archetype.length(); ++i) {
                final ComponentType<ECS_TYPE, ? extends Component<ECS_TYPE>> componentType = (ComponentType<ECS_TYPE, ? extends Component<ECS_TYPE>>)archetype.get(i);
                if (componentType != null) {
                    entityComponents[componentType.getIndex()] = data.createComponent(componentType);
                }
            }
        }
        return this.addEntity(this.registry.newHolder(archetype, entityComponents), new Ref<ECS_TYPE>(this), reason);
    }
    
    @Nullable
    @Override
    public Ref<ECS_TYPE> addEntity(@Nonnull final Holder<ECS_TYPE> holder, @Nonnull final AddReason reason) {
        return this.addEntity(holder, new Ref<ECS_TYPE>(this), reason);
    }
    
    @Nullable
    public Ref<ECS_TYPE> addEntity(@Nonnull final Holder<ECS_TYPE> holder, @Nonnull final Ref<ECS_TYPE> ref, @Nonnull final AddReason reason) {
        if (ref.isValid()) {
            throw new IllegalArgumentException("EntityReference is already in use!");
        }
        if (ref.getStore() != this) {
            throw new IllegalArgumentException("EntityReference is not for this store!");
        }
        this.assertThread();
        this.assertWriteProcessing();
        final CommandBuffer<ECS_TYPE> commandBuffer = this.takeCommandBuffer();
        final ComponentRegistry.Data<ECS_TYPE> data = this.registry._internal_getData();
        this.processing.lock();
        try {
            final SystemType<ECS_TYPE, HolderSystem<ECS_TYPE>> systemType = this.registry.getHolderSystemType();
            final BitSet systemIndexes = data.getSystemIndexesForType(systemType);
            int systemIndex = -1;
            while ((systemIndex = systemIndexes.nextSetBit(systemIndex + 1)) >= 0) {
                final HolderSystem<ECS_TYPE> system = data.getSystem(systemIndex, systemType);
                if (system.test(this.registry, holder.getArchetype())) {
                    system.onEntityAdd(holder, reason, this);
                }
            }
            final int entityIndex = this.entitiesSize++;
            final int oldLength = this.refs.length;
            if (oldLength <= entityIndex) {
                final int newLength = ArrayUtil.grow(entityIndex);
                this.refs = Arrays.copyOf(this.refs, newLength);
                this.entityToArchetypeChunk = Arrays.copyOf(this.entityToArchetypeChunk, newLength);
                this.entityChunkIndex = Arrays.copyOf(this.entityChunkIndex, newLength);
                Arrays.fill(this.entityToArchetypeChunk, oldLength, newLength, Integer.MIN_VALUE);
                Arrays.fill(this.entityChunkIndex, oldLength, newLength, Integer.MIN_VALUE);
            }
            (this.refs[entityIndex] = ref).setIndex(entityIndex);
            final int archetypeIndex = this.findOrCreateArchetypeChunk(holder.getArchetype());
            final ArchetypeChunk<ECS_TYPE> archetypeChunk = this.archetypeChunks[archetypeIndex];
            final int chunkEntityRef = archetypeChunk.addEntity(ref, holder);
            this.entityToArchetypeChunk[entityIndex] = archetypeIndex;
            this.entityChunkIndex[entityIndex] = chunkEntityRef;
            final SystemType<ECS_TYPE, RefSystem<ECS_TYPE>> systemType2 = this.registry.getRefSystemType();
            final BitSet systemIndexes2 = data.getSystemIndexesForType(systemType2);
            final BitSet entityProcessedBySystemIndexes = this.archetypeChunkIndexesToSystemIndex[archetypeIndex];
            commandBuffer.track(ref);
            int systemIndex2 = -1;
            while ((systemIndex2 = systemIndexes2.nextSetBit(systemIndex2 + 1)) >= 0) {
                if (entityProcessedBySystemIndexes.get(systemIndex2)) {
                    final RefSystem<ECS_TYPE> system2 = data.getSystem(systemIndex2, systemType2);
                    final boolean oldDisableProcessingAssert = this.disableProcessingAssert;
                    this.disableProcessingAssert = (system2 instanceof DisableProcessingAssert);
                    system2.onEntityAdded(ref, reason, this, commandBuffer);
                    this.disableProcessingAssert = oldDisableProcessingAssert;
                    if (commandBuffer.consumeWasTrackedRefRemoved()) {
                        break;
                    }
                    continue;
                }
            }
        }
        finally {
            this.processing.unlock();
        }
        commandBuffer.consume();
        return ref.isValid() ? ref : null;
    }
    
    @Nonnull
    @Override
    public Ref<ECS_TYPE>[] addEntities(@Nonnull final Holder<ECS_TYPE>[] holders, @Nonnull final AddReason reason) {
        return this.addEntities(holders, 0, holders.length, reason);
    }
    
    @Nonnull
    public Ref<ECS_TYPE>[] addEntities(@Nonnull final Holder<ECS_TYPE>[] holders, final int start, final int length, @Nonnull final AddReason reason) {
        final Ref<ECS_TYPE>[] refs = new Ref[length];
        for (int i = 0; i < length; ++i) {
            refs[i] = new Ref<ECS_TYPE>(this);
        }
        this.addEntities(holders, start, refs, 0, length, reason);
        return refs;
    }
    
    public void addEntities(@Nonnull final Holder<ECS_TYPE>[] holders, @Nonnull final Ref<ECS_TYPE>[] refs, @Nonnull final AddReason reason) {
        if (holders.length != refs.length) {
            throw new IllegalArgumentException("EntityHolder and EntityReference array length doesn't match!");
        }
        this.addEntities(holders, 0, refs, 0, holders.length, reason);
    }
    
    public void addEntities(@Nonnull final Holder<ECS_TYPE>[] holders, final int holderStart, @Nonnull final Ref<ECS_TYPE>[] refs, final int refStart, int length, @Nonnull final AddReason reason) {
        final int holderEnd = holderStart + length;
        int refEnd = refStart + length;
        if (holders.length < holderEnd) {
            throw new IllegalArgumentException("EntityHolder start and length exceed array length!");
        }
        if (refs.length < refEnd) {
            throw new IllegalArgumentException("EntityReference start and length exceed array length!");
        }
        for (int i = refStart; i < refEnd; ++i) {
            final Ref<ECS_TYPE> ref = refs[i];
            if (ref.isValid()) {
                throw new IllegalArgumentException("EntityReference is already in use!");
            }
            if (ref.getStore() != this) {
                throw new IllegalArgumentException("EntityReference is not for this store!");
            }
        }
        this.assertThread();
        this.assertWriteProcessing();
        final CommandBuffer<ECS_TYPE> commandBuffer = this.takeCommandBuffer();
        final ComponentRegistry.Data<ECS_TYPE> data = this.registry._internal_getData();
        this.processing.lock();
        try {
            final SystemType<ECS_TYPE, HolderSystem<ECS_TYPE>> systemType = this.registry.getHolderSystemType();
            final BitSet systemIndexes = data.getSystemIndexesForType(systemType);
            int systemIndex = -1;
            while ((systemIndex = systemIndexes.nextSetBit(systemIndex + 1)) >= 0) {
                final HolderSystem<ECS_TYPE> system = data.getSystem(systemIndex, systemType);
                for (int j = holderStart; j < holderEnd; ++j) {
                    if (system.test(this.registry, holders[j].getArchetype())) {
                        system.onEntityAdd(holders[j], reason, this);
                    }
                }
            }
            final int firstIndex = this.entitiesSize;
            this.entitiesSize += length;
            final int oldLength = this.refs.length;
            if (oldLength <= this.entitiesSize) {
                final int newLength = ArrayUtil.grow(this.entitiesSize);
                this.refs = Arrays.copyOf(this.refs, newLength);
                this.entityToArchetypeChunk = Arrays.copyOf(this.entityToArchetypeChunk, newLength);
                this.entityChunkIndex = Arrays.copyOf(this.entityChunkIndex, newLength);
                Arrays.fill(this.entityToArchetypeChunk, oldLength, newLength, Integer.MIN_VALUE);
                Arrays.fill(this.entityChunkIndex, oldLength, newLength, Integer.MIN_VALUE);
            }
            System.arraycopy(refs, refStart, this.refs, firstIndex, length);
            for (int k = refStart, entityIndex = firstIndex; k < refEnd; ++k, ++entityIndex) {
                refs[k].setIndex(entityIndex);
            }
            for (int k = 0, entityIndex = firstIndex; k < length; ++k, ++entityIndex) {
                final Ref<ECS_TYPE> ref2 = refs[refStart + k];
                final Holder<ECS_TYPE> holder = holders[holderStart + k];
                final int archetypeIndex = this.findOrCreateArchetypeChunk(holder.getArchetype());
                final ArchetypeChunk<ECS_TYPE> archetypeChunk = this.archetypeChunks[archetypeIndex];
                final int chunkEntityRef = archetypeChunk.addEntity(ref2, holder);
                this.entityToArchetypeChunk[entityIndex] = archetypeIndex;
                this.entityChunkIndex[entityIndex] = chunkEntityRef;
            }
            final SystemType<ECS_TYPE, RefSystem<ECS_TYPE>> systemType2 = this.registry.getRefSystemType();
            final BitSet systemIndexes2 = data.getSystemIndexesForType(systemType2);
            int systemIndex2 = -1;
            while ((systemIndex2 = systemIndexes2.nextSetBit(systemIndex2 + 1)) >= 0) {
                for (int l = refStart; l < refEnd; ++l) {
                    final Ref<ECS_TYPE> ref3 = refs[l];
                    final int archetypeIndex2 = this.entityToArchetypeChunk[ref3.getIndex()];
                    final BitSet entityProcessedBySystemIndexes = this.archetypeChunkIndexesToSystemIndex[archetypeIndex2];
                    if (entityProcessedBySystemIndexes.get(systemIndex2)) {
                        final RefSystem<ECS_TYPE> system2 = data.getSystem(systemIndex2, systemType2);
                        final boolean oldDisableProcessingAssert = this.disableProcessingAssert;
                        this.disableProcessingAssert = (system2 instanceof DisableProcessingAssert);
                        commandBuffer.track(ref3);
                        system2.onEntityAdded(ref3, reason, this, commandBuffer);
                        if (commandBuffer.consumeWasTrackedRefRemoved()) {
                            final int remaining = refEnd - l;
                            if (remaining > 1) {
                                System.arraycopy(refs, l + 1, refs, l, remaining - 1);
                                refs[refEnd - 1] = ref3;
                                --l;
                            }
                            --refEnd;
                            --length;
                        }
                        this.disableProcessingAssert = oldDisableProcessingAssert;
                    }
                }
            }
        }
        finally {
            this.processing.unlock();
        }
        commandBuffer.consume();
    }
    
    @Nonnull
    public Holder<ECS_TYPE> copyEntity(@Nonnull final Ref<ECS_TYPE> ref) {
        return this.copyEntity(ref, this.registry.newHolder());
    }
    
    @Nonnull
    public Holder<ECS_TYPE> copyEntity(@Nonnull final Ref<ECS_TYPE> ref, @Nonnull final Holder<ECS_TYPE> holder) {
        this.assertThread();
        ref.validate();
        final int refIndex = ref.getIndex();
        final int archetypeIndex = this.entityToArchetypeChunk[refIndex];
        return this.archetypeChunks[archetypeIndex].copyEntity(this.entityChunkIndex[refIndex], holder);
    }
    
    @Nonnull
    public Holder<ECS_TYPE> copySerializableEntity(@Nonnull final Ref<ECS_TYPE> ref) {
        return this.copySerializableEntity(ref, this.registry.newHolder());
    }
    
    @Nonnull
    public Holder<ECS_TYPE> copySerializableEntity(@Nonnull final Ref<ECS_TYPE> ref, @Nonnull final Holder<ECS_TYPE> holder) {
        this.assertThread();
        ref.validate();
        final int refIndex = ref.getIndex();
        final int archetypeIndex = this.entityToArchetypeChunk[refIndex];
        return this.archetypeChunks[archetypeIndex].copySerializableEntity(this.registry.getData(), this.entityChunkIndex[refIndex], holder);
    }
    
    @Nonnull
    @Override
    public Archetype<ECS_TYPE> getArchetype(@Nonnull final Ref<ECS_TYPE> ref) {
        this.assertThread();
        ref.validate();
        final int archetypeIndex = this.entityToArchetypeChunk[ref.getIndex()];
        return this.archetypeChunks[archetypeIndex].getArchetype();
    }
    
    @Nonnull
    protected Archetype<ECS_TYPE> __internal_getArchetype(@Nonnull final Ref<ECS_TYPE> ref) {
        ref.validate();
        final int archetypeIndex = this.entityToArchetypeChunk[ref.getIndex()];
        return this.archetypeChunks[archetypeIndex].getArchetype();
    }
    
    @Nonnull
    public Holder<ECS_TYPE> removeEntity(@Nonnull final Ref<ECS_TYPE> ref, @Nonnull final RemoveReason reason) {
        return this.removeEntity(ref, this.registry.newHolder(), reason);
    }
    
    @Nonnull
    @Override
    public Holder<ECS_TYPE> removeEntity(@Nonnull final Ref<ECS_TYPE> ref, @Nonnull final Holder<ECS_TYPE> holder, @Nonnull final RemoveReason reason) {
        return this.removeEntity(ref, holder, reason, null);
    }
    
    @Nonnull
    Holder<ECS_TYPE> removeEntity(@Nonnull final Ref<ECS_TYPE> ref, @Nonnull final Holder<ECS_TYPE> holder, @Nonnull final RemoveReason reason, @Nullable final Throwable proxyReason) {
        this.assertThread();
        this.assertWriteProcessing();
        ref.validate();
        final CommandBuffer<ECS_TYPE> commandBuffer = this.takeCommandBuffer();
        final int entityIndex = ref.getIndex();
        final int archetypeIndex = this.entityToArchetypeChunk[entityIndex];
        final int chunkEntityRef = this.entityChunkIndex[entityIndex];
        final ComponentRegistry.Data<ECS_TYPE> data = this.registry._internal_getData();
        this.processing.lock();
        try {
            final SystemType<ECS_TYPE, RefSystem<ECS_TYPE>> systemType = this.registry.getRefSystemType();
            final BitSet systemIndexes = data.getSystemIndexesForType(systemType);
            final BitSet entityProcessedBySystemIndexes = this.archetypeChunkIndexesToSystemIndex[archetypeIndex];
            int systemIndex = -1;
            while ((systemIndex = systemIndexes.nextSetBit(systemIndex + 1)) >= 0) {
                if (entityProcessedBySystemIndexes.get(systemIndex)) {
                    data.getSystem(systemIndex, systemType).onEntityRemove(ref, reason, this, commandBuffer);
                }
            }
            final int lastIndex = this.entitiesSize - 1;
            if (entityIndex != lastIndex) {
                final Ref<ECS_TYPE> lastEntityRef = this.refs[lastIndex];
                final int lastSelfEntityRef = this.entityToArchetypeChunk[lastIndex];
                final int lastEntityChunkIndex = this.entityChunkIndex[lastIndex];
                lastEntityRef.setIndex(entityIndex);
                this.refs[entityIndex] = lastEntityRef;
                this.entityToArchetypeChunk[entityIndex] = lastSelfEntityRef;
                this.entityChunkIndex[entityIndex] = lastEntityChunkIndex;
            }
            this.refs[lastIndex] = null;
            this.entityToArchetypeChunk[lastIndex] = Integer.MIN_VALUE;
            this.entityChunkIndex[lastIndex] = Integer.MIN_VALUE;
            this.entitiesSize = lastIndex;
            final ArchetypeChunk<ECS_TYPE> archetypeChunk = this.archetypeChunks[archetypeIndex];
            archetypeChunk.removeEntity(chunkEntityRef, holder);
            if (archetypeChunk.size() == 0) {
                this.removeArchetypeChunk(archetypeIndex);
            }
            ref.invalidate(proxyReason);
        }
        finally {
            this.processing.unlock();
        }
        commandBuffer.consume();
        this.processing.lock();
        try {
            final SystemType<ECS_TYPE, HolderSystem<ECS_TYPE>> systemType2 = this.registry.getHolderSystemType();
            final BitSet systemIndexes = data.getSystemIndexesForType(systemType2);
            int systemIndex2 = -1;
            while ((systemIndex2 = systemIndexes.nextSetBit(systemIndex2 + 1)) >= 0) {
                final HolderSystem<ECS_TYPE> system = data.getSystem(systemIndex2, systemType2);
                if (system.test(this.registry, holder.getArchetype())) {
                    system.onEntityRemoved(holder, reason, this);
                }
            }
        }
        finally {
            this.processing.unlock();
        }
        return holder;
    }
    
    @Nonnull
    public Holder<ECS_TYPE>[] removeEntities(@Nonnull final Ref<ECS_TYPE>[] refs, @Nonnull final RemoveReason reason) {
        return this.removeEntities(refs, 0, refs.length, reason);
    }
    
    @Nonnull
    public Holder<ECS_TYPE>[] removeEntities(@Nonnull final Ref<ECS_TYPE>[] refs, final int start, final int length, @Nonnull final RemoveReason reason) {
        final Holder<ECS_TYPE>[] holders = new Holder[length];
        for (int i = 0; i < length; ++i) {
            holders[i] = this.registry.newHolder();
        }
        return this.removeEntities(refs, start, holders, 0, length, reason);
    }
    
    @Nonnull
    public Holder<ECS_TYPE>[] removeEntities(@Nonnull final Ref<ECS_TYPE>[] refs, @Nonnull final Holder<ECS_TYPE>[] holders, @Nonnull final RemoveReason reason) {
        if (refs.length != holders.length) {
            throw new IllegalArgumentException("EntityHolder and EntityReference array length doesn't match!");
        }
        return this.removeEntities(refs, 0, holders, 0, refs.length, reason);
    }
    
    @Nonnull
    public Holder<ECS_TYPE>[] removeEntities(@Nonnull final Ref<ECS_TYPE>[] refArr, final int refStart, @Nonnull final Holder<ECS_TYPE>[] holders, final int holderStart, final int length, @Nonnull final RemoveReason reason) {
        final int refEnd = refStart + length;
        final int holderEnd = holderStart + length;
        if (refArr.length < refEnd) {
            throw new IllegalArgumentException("EntityReference start and length exceed array length!");
        }
        if (holders.length < holderEnd) {
            throw new IllegalArgumentException("EntityHolder start and length exceed array length!");
        }
        for (int i = refStart; i < refEnd; ++i) {
            refArr[i].validate();
        }
        this.assertThread();
        this.assertWriteProcessing();
        final CommandBuffer<ECS_TYPE> commandBuffer = this.takeCommandBuffer();
        final ComponentRegistry.Data<ECS_TYPE> data = this.registry._internal_getData();
        this.processing.lock();
        try {
            final SystemType<ECS_TYPE, RefSystem<ECS_TYPE>> systemType = this.registry.getRefSystemType();
            final BitSet systemIndexes = data.getSystemIndexesForType(systemType);
            int systemIndex = -1;
            while ((systemIndex = systemIndexes.nextSetBit(systemIndex + 1)) >= 0) {
                for (int j = refStart; j < refEnd; ++j) {
                    final Ref<ECS_TYPE> ref = refArr[j];
                    final int archetypeIndex = this.entityToArchetypeChunk[ref.getIndex()];
                    final BitSet entityProcessedBySystemIndexes = this.archetypeChunkIndexesToSystemIndex[archetypeIndex];
                    if (entityProcessedBySystemIndexes.get(systemIndex)) {
                        data.getSystem(systemIndex, systemType).onEntityRemove(refArr[j], reason, this, commandBuffer);
                    }
                }
            }
            for (int k = 0; k < length; ++k) {
                final int entityIndex = refArr[refStart + k].getIndex();
                final int archetypeIndex2 = this.entityToArchetypeChunk[entityIndex];
                final int chunkEntityRef = this.entityChunkIndex[entityIndex];
                final int lastIndex = this.entitiesSize - 1;
                if (entityIndex != lastIndex) {
                    final Ref<ECS_TYPE> lastEntityRef = this.refs[lastIndex];
                    final int lastSelfEntityRef = this.entityToArchetypeChunk[lastIndex];
                    final int lastEntityChunkIndex = this.entityChunkIndex[lastIndex];
                    lastEntityRef.setIndex(entityIndex);
                    this.refs[entityIndex] = lastEntityRef;
                    this.entityToArchetypeChunk[entityIndex] = lastSelfEntityRef;
                    this.entityChunkIndex[entityIndex] = lastEntityChunkIndex;
                }
                this.refs[lastIndex] = null;
                this.entityToArchetypeChunk[lastIndex] = Integer.MIN_VALUE;
                this.entityChunkIndex[lastIndex] = Integer.MIN_VALUE;
                this.entitiesSize = lastIndex;
                final Holder<ECS_TYPE> holder = holders[holderStart + k];
                final ArchetypeChunk<ECS_TYPE> archetypeChunk = this.archetypeChunks[archetypeIndex2];
                archetypeChunk.removeEntity(chunkEntityRef, holder);
                if (archetypeChunk.size() == 0) {
                    this.removeArchetypeChunk(archetypeIndex2);
                }
            }
            for (int k = refStart; k < refEnd; ++k) {
                refArr[k].invalidate();
            }
        }
        finally {
            this.processing.unlock();
        }
        commandBuffer.consume();
        this.processing.lock();
        try {
            final SystemType<ECS_TYPE, HolderSystem<ECS_TYPE>> systemType2 = this.registry.getHolderSystemType();
            final BitSet systemIndexes = data.getSystemIndexesForType(systemType2);
            int systemIndex = -1;
            while ((systemIndex = systemIndexes.nextSetBit(systemIndex + 1)) >= 0) {
                final HolderSystem<ECS_TYPE> system = data.getSystem(systemIndex, systemType2);
                for (int l = holderStart; l < holderEnd; ++l) {
                    if (system.test(this.registry, holders[l].getArchetype())) {
                        system.onEntityRemoved(holders[l], reason, this);
                    }
                }
            }
        }
        finally {
            this.processing.unlock();
        }
        return holders;
    }
    
    public <T extends Component<ECS_TYPE>> void ensureComponent(@Nonnull final Ref<ECS_TYPE> ref, @Nonnull final ComponentType<ECS_TYPE, T> componentType) {
        this.assertThread();
        this.assertWriteProcessing();
        componentType.validateRegistry(this.registry);
        componentType.validate();
        final CommandBuffer<ECS_TYPE> commandBuffer = this.takeCommandBuffer();
        final int archetypeIndex = this.entityToArchetypeChunk[ref.getIndex()];
        this.processing.lock();
        try {
            final ArchetypeChunk<ECS_TYPE> archetypeChunk = this.archetypeChunks[archetypeIndex];
            if (!archetypeChunk.getArchetype().contains(componentType)) {
                final T component = this.registry._internal_getData().createComponent(componentType);
                this.datachunk_addComponent(ref, archetypeIndex, componentType, component, commandBuffer);
            }
        }
        finally {
            this.processing.unlock();
        }
        commandBuffer.consume();
    }
    
    @Nonnull
    @Override
    public <T extends Component<ECS_TYPE>> T ensureAndGetComponent(@Nonnull final Ref<ECS_TYPE> ref, @Nonnull final ComponentType<ECS_TYPE, T> componentType) {
        this.assertThread();
        this.assertWriteProcessing();
        final int refIndex = ref.getIndex();
        componentType.validateRegistry(this.registry);
        componentType.validate();
        final CommandBuffer<ECS_TYPE> commandBuffer = this.takeCommandBuffer();
        final int archetypeIndex = this.entityToArchetypeChunk[refIndex];
        this.processing.lock();
        T component;
        try {
            final ArchetypeChunk<ECS_TYPE> archetypeChunk = this.archetypeChunks[archetypeIndex];
            component = archetypeChunk.getComponent(this.entityChunkIndex[refIndex], componentType);
            if (component == null) {
                component = this.registry._internal_getData().createComponent(componentType);
                this.datachunk_addComponent(ref, archetypeIndex, componentType, component, commandBuffer);
            }
        }
        finally {
            this.processing.unlock();
        }
        commandBuffer.consume();
        return component;
    }
    
    @Nonnull
    @Override
    public <T extends Component<ECS_TYPE>> T addComponent(@Nonnull final Ref<ECS_TYPE> ref, @Nonnull final ComponentType<ECS_TYPE, T> componentType) {
        this.assertThread();
        this.assertWriteProcessing();
        final T component = this.registry._internal_getData().createComponent(componentType);
        this.addComponent(ref, componentType, component);
        return component;
    }
    
    @Override
    public <T extends Component<ECS_TYPE>> void addComponent(@Nonnull final Ref<ECS_TYPE> ref, @Nonnull final ComponentType<ECS_TYPE, T> componentType, @Nonnull final T component) {
        this.assertThread();
        this.assertWriteProcessing();
        ref.validate();
        componentType.validateRegistry(this.registry);
        componentType.validate();
        Objects.requireNonNull(component);
        final CommandBuffer<ECS_TYPE> commandBuffer = this.takeCommandBuffer();
        final int archetypeIndex = this.entityToArchetypeChunk[ref.getIndex()];
        this.processing.lock();
        try {
            this.datachunk_addComponent(ref, archetypeIndex, componentType, component, commandBuffer);
        }
        finally {
            this.processing.unlock();
        }
        commandBuffer.consume();
    }
    
    public <T extends Component<ECS_TYPE>> void replaceComponent(@Nonnull final Ref<ECS_TYPE> ref, @Nonnull final ComponentType<ECS_TYPE, T> componentType, @Nonnull final T component) {
        this.assertThread();
        this.assertWriteProcessing();
        ref.validate();
        componentType.validateRegistry(this.registry);
        componentType.validate();
        Objects.requireNonNull(component);
        final CommandBuffer<ECS_TYPE> commandBuffer = this.takeCommandBuffer();
        final int archetypeIndex = this.entityToArchetypeChunk[ref.getIndex()];
        this.processing.lock();
        try {
            final ArchetypeChunk<ECS_TYPE> archetypeChunk = this.archetypeChunks[archetypeIndex];
            final int chunkEntityRef = this.entityChunkIndex[ref.getIndex()];
            final T oldComponent = archetypeChunk.getComponent(chunkEntityRef, componentType);
            archetypeChunk.setComponent(chunkEntityRef, componentType, component);
            final BitSet entityProcessedBySystemIndexes = this.archetypeChunkIndexesToSystemIndex[archetypeIndex];
            final ComponentRegistry.Data<ECS_TYPE> data = this.registry._internal_getData();
            final BitSet systemIndexes = data.getSystemIndexesForType(this.registry.getRefChangeSystemType());
            int systemIndex = -1;
            while ((systemIndex = systemIndexes.nextSetBit(systemIndex + 1)) >= 0) {
                if (entityProcessedBySystemIndexes.get(systemIndex)) {
                    final RefChangeSystem<ECS_TYPE, T> system = (RefChangeSystem)data.getSystem(systemIndex);
                    if (system.componentType().getIndex() != componentType.getIndex()) {
                        continue;
                    }
                    system.onComponentSet(ref, oldComponent, component, this, commandBuffer);
                }
            }
        }
        finally {
            this.processing.unlock();
        }
        commandBuffer.consume();
    }
    
    @Override
    public <T extends Component<ECS_TYPE>> void putComponent(@Nonnull final Ref<ECS_TYPE> ref, @Nonnull final ComponentType<ECS_TYPE, T> componentType, @Nonnull final T component) {
        this.assertThread();
        this.assertWriteProcessing();
        ref.validate();
        componentType.validateRegistry(this.registry);
        componentType.validate();
        Objects.requireNonNull(component);
        final CommandBuffer<ECS_TYPE> commandBuffer = this.takeCommandBuffer();
        final int archetypeIndex = this.entityToArchetypeChunk[ref.getIndex()];
        this.processing.lock();
        try {
            final ArchetypeChunk<ECS_TYPE> archetypeChunk = this.archetypeChunks[archetypeIndex];
            if (archetypeChunk.getArchetype().contains(componentType)) {
                final int chunkEntityRef = this.entityChunkIndex[ref.getIndex()];
                final T oldComponent = archetypeChunk.getComponent(chunkEntityRef, componentType);
                archetypeChunk.setComponent(chunkEntityRef, componentType, component);
                final BitSet entityProcessedBySystemIndexes = this.archetypeChunkIndexesToSystemIndex[archetypeIndex];
                final ComponentRegistry.Data<ECS_TYPE> data = this.registry._internal_getData();
                final BitSet systemIndexes = data.getSystemIndexesForType(this.registry.getRefChangeSystemType());
                int systemIndex = -1;
                while ((systemIndex = systemIndexes.nextSetBit(systemIndex + 1)) >= 0) {
                    if (entityProcessedBySystemIndexes.get(systemIndex)) {
                        final RefChangeSystem<ECS_TYPE, T> system = (RefChangeSystem)data.getSystem(systemIndex);
                        if (system.componentType().getIndex() != componentType.getIndex()) {
                            continue;
                        }
                        system.onComponentSet(ref, oldComponent, component, this, commandBuffer);
                    }
                }
            }
            else {
                this.datachunk_addComponent(ref, archetypeIndex, componentType, component, commandBuffer);
            }
        }
        finally {
            this.processing.unlock();
        }
        commandBuffer.consume();
    }
    
    @Override
    public <T extends Component<ECS_TYPE>> T getComponent(@Nonnull final Ref<ECS_TYPE> ref, @Nonnull final ComponentType<ECS_TYPE, T> componentType) {
        this.assertThread();
        return (T)this.__internal_getComponent(ref, (ComponentType<ECS_TYPE, Component>)componentType);
    }
    
    @Nullable
    protected <T extends Component<ECS_TYPE>> T __internal_getComponent(@Nonnull final Ref<ECS_TYPE> ref, @Nonnull final ComponentType<ECS_TYPE, T> componentType) {
        ref.validate();
        componentType.validateRegistry(this.registry);
        componentType.validate();
        final int archetypeIndex = this.entityToArchetypeChunk[ref.getIndex()];
        final ArchetypeChunk<ECS_TYPE> archetypeChunk = this.archetypeChunks[archetypeIndex];
        return archetypeChunk.getComponent(this.entityChunkIndex[ref.getIndex()], componentType);
    }
    
    @Override
    public <T extends Component<ECS_TYPE>> void removeComponent(@Nonnull final Ref<ECS_TYPE> ref, @Nonnull final ComponentType<ECS_TYPE, T> componentType) {
        this.assertThread();
        this.assertWriteProcessing();
        ref.validate();
        componentType.validateRegistry(this.registry);
        componentType.validate();
        final CommandBuffer<ECS_TYPE> commandBuffer = this.takeCommandBuffer();
        final int entityIndex = ref.getIndex();
        final int fromArchetypeIndex = this.entityToArchetypeChunk[entityIndex];
        this.processing.lock();
        try {
            final ArchetypeChunk<ECS_TYPE> fromArchetypeChunk = this.archetypeChunks[fromArchetypeIndex];
            final Holder<ECS_TYPE> holder = this.registry._internal_newEntityHolder();
            fromArchetypeChunk.removeEntity(this.entityChunkIndex[entityIndex], holder);
            final T component = holder.getComponent(componentType);
            assert component != null;
            holder.removeComponent(componentType);
            final int toArchetypeIndex = this.findOrCreateArchetypeChunk(holder.getArchetype());
            final ArchetypeChunk<ECS_TYPE> toArchetypeChunk = this.archetypeChunks[toArchetypeIndex];
            final int chunkEntityRef = toArchetypeChunk.addEntity(ref, holder);
            this.entityToArchetypeChunk[entityIndex] = toArchetypeIndex;
            this.entityChunkIndex[entityIndex] = chunkEntityRef;
            final BitSet entityProcessedBySystemIndexes = this.archetypeChunkIndexesToSystemIndex[fromArchetypeIndex];
            final ComponentRegistry.Data<ECS_TYPE> data = this.registry._internal_getData();
            final BitSet systemIndexes = data.getSystemIndexesForType(this.registry.getRefChangeSystemType());
            int systemIndex = -1;
            while ((systemIndex = systemIndexes.nextSetBit(systemIndex + 1)) >= 0) {
                if (entityProcessedBySystemIndexes.get(systemIndex)) {
                    final RefChangeSystem<ECS_TYPE, T> system = (RefChangeSystem)data.getSystem(systemIndex);
                    if (system.componentType().getIndex() != componentType.getIndex()) {
                        continue;
                    }
                    system.onComponentRemoved(ref, component, this, commandBuffer);
                }
            }
            if (fromArchetypeChunk.size() == 0) {
                this.removeArchetypeChunk(fromArchetypeIndex);
            }
        }
        finally {
            this.processing.unlock();
        }
        commandBuffer.consume();
    }
    
    @Override
    public <T extends Component<ECS_TYPE>> void tryRemoveComponent(@Nonnull final Ref<ECS_TYPE> ref, @Nonnull final ComponentType<ECS_TYPE, T> componentType) {
        this.removeComponentIfExists(ref, componentType);
    }
    
    public <T extends Component<ECS_TYPE>> boolean removeComponentIfExists(@Nonnull final Ref<ECS_TYPE> ref, @Nonnull final ComponentType<ECS_TYPE, T> componentType) {
        this.assertThread();
        this.assertWriteProcessing();
        ref.validate();
        componentType.validateRegistry(this.registry);
        componentType.validate();
        final CommandBuffer<ECS_TYPE> commandBuffer = this.takeCommandBuffer();
        final int entityIndex = ref.getIndex();
        final int fromArchetypeIndex = this.entityToArchetypeChunk[entityIndex];
        this.processing.lock();
        boolean result;
        try {
            final ArchetypeChunk<ECS_TYPE> fromArchetypeChunk = this.archetypeChunks[fromArchetypeIndex];
            if (!fromArchetypeChunk.getArchetype().contains(componentType)) {
                result = false;
            }
            else {
                final Holder<ECS_TYPE> holder = this.registry._internal_newEntityHolder();
                fromArchetypeChunk.removeEntity(this.entityChunkIndex[entityIndex], holder);
                final T component = holder.getComponent(componentType);
                assert component != null;
                holder.removeComponent(componentType);
                final int toArchetypeIndex = this.findOrCreateArchetypeChunk(holder.getArchetype());
                final ArchetypeChunk<ECS_TYPE> toArchetypeChunk = this.archetypeChunks[toArchetypeIndex];
                final int chunkEntityRef = toArchetypeChunk.addEntity(ref, holder);
                this.entityToArchetypeChunk[entityIndex] = toArchetypeIndex;
                this.entityChunkIndex[entityIndex] = chunkEntityRef;
                final BitSet entityProcessedBySystemIndexes = this.archetypeChunkIndexesToSystemIndex[fromArchetypeIndex];
                final ComponentRegistry.Data<ECS_TYPE> data = this.registry._internal_getData();
                final BitSet systemIndexes = data.getSystemIndexesForType(this.registry.getRefChangeSystemType());
                int systemIndex = -1;
                while ((systemIndex = systemIndexes.nextSetBit(systemIndex + 1)) >= 0) {
                    if (entityProcessedBySystemIndexes.get(systemIndex)) {
                        final RefChangeSystem<ECS_TYPE, T> system = (RefChangeSystem)data.getSystem(systemIndex);
                        if (system.componentType().getIndex() != componentType.getIndex()) {
                            continue;
                        }
                        system.onComponentRemoved(ref, component, this, commandBuffer);
                    }
                }
                if (fromArchetypeChunk.size() == 0) {
                    this.removeArchetypeChunk(fromArchetypeIndex);
                }
                result = true;
            }
        }
        finally {
            this.processing.unlock();
        }
        commandBuffer.consume();
        return result;
    }
    
    public <T extends Resource<ECS_TYPE>> void replaceResource(@Nonnull final ResourceType<ECS_TYPE, T> resourceType, @Nonnull final T resource) {
        this.assertThread();
        resourceType.validateRegistry(this.registry);
        Objects.requireNonNull(resource);
        this.resources[resourceType.getIndex()] = resource;
    }
    
    @Nonnull
    @Override
    public <T extends Resource<ECS_TYPE>> T getResource(@Nonnull final ResourceType<ECS_TYPE, T> resourceType) {
        resourceType.validateRegistry(this.registry);
        return (T)this.resources[resourceType.getIndex()];
    }
    
    @Nonnull
    protected <T extends Resource<ECS_TYPE>> T __internal_getResource(@Nonnull final ResourceType<ECS_TYPE, T> resourceType) {
        resourceType.validateRegistry(this.registry);
        return (T)this.resources[resourceType.getIndex()];
    }
    
    public void forEachChunk(@Nonnull final BiConsumer<ArchetypeChunk<ECS_TYPE>, CommandBuffer<ECS_TYPE>> consumer) {
        if (this.shutdown) {
            throw new IllegalStateException("Store is shutdown!");
        }
        this.assertThread();
        final CommandBuffer<ECS_TYPE> commandBuffer = this.takeCommandBuffer();
        this.processing.lock();
        try {
            for (int archetypeIndex = 0; archetypeIndex < this.archetypeSize; ++archetypeIndex) {
                final ArchetypeChunk<ECS_TYPE> archetypeChunk = this.archetypeChunks[archetypeIndex];
                if (archetypeChunk != null) {
                    consumer.accept(archetypeChunk, commandBuffer);
                }
            }
        }
        finally {
            this.processing.unlock();
        }
        commandBuffer.consume();
    }
    
    public boolean forEachChunk(@Nonnull final BiPredicate<ArchetypeChunk<ECS_TYPE>, CommandBuffer<ECS_TYPE>> predicate) {
        if (this.shutdown) {
            throw new IllegalStateException("Store is shutdown!");
        }
        this.assertThread();
        final CommandBuffer<ECS_TYPE> commandBuffer = this.takeCommandBuffer();
        boolean result = false;
        this.processing.lock();
        try {
            for (int archetypeIndex = 0; archetypeIndex < this.archetypeSize; ++archetypeIndex) {
                final ArchetypeChunk<ECS_TYPE> archetypeChunk = this.archetypeChunks[archetypeIndex];
                if (archetypeChunk != null) {
                    if (predicate.test(archetypeChunk, commandBuffer)) {
                        result = true;
                        break;
                    }
                }
            }
        }
        finally {
            this.processing.unlock();
        }
        commandBuffer.consume();
        return result;
    }
    
    public void forEachChunk(final Query<ECS_TYPE> query, @Nonnull final BiConsumer<ArchetypeChunk<ECS_TYPE>, CommandBuffer<ECS_TYPE>> consumer) {
        if (this.shutdown) {
            throw new IllegalStateException("Store is shutdown!");
        }
        this.assertThread();
        final CommandBuffer<ECS_TYPE> commandBuffer = this.takeCommandBuffer();
        this.processing.lock();
        try {
            if (query instanceof final ExactArchetypeQuery exactArchetypeQuery) {
                final ExactArchetypeQuery<ECS_TYPE> exactQuery = exactArchetypeQuery;
                final int archetypeIndex = this.archetypeToIndexMap.getInt(exactQuery.getArchetype());
                if (archetypeIndex != Integer.MIN_VALUE) {
                    consumer.accept(this.archetypeChunks[archetypeIndex], commandBuffer);
                }
            }
            else {
                for (int archetypeIndex = 0; archetypeIndex < this.archetypeSize; ++archetypeIndex) {
                    final ArchetypeChunk<ECS_TYPE> archetypeChunk = this.archetypeChunks[archetypeIndex];
                    if (archetypeChunk != null) {
                        if (query.test(archetypeChunk.getArchetype())) {
                            consumer.accept(this.archetypeChunks[archetypeIndex], commandBuffer);
                        }
                    }
                }
            }
        }
        finally {
            this.processing.unlock();
        }
        commandBuffer.consume();
    }
    
    public boolean forEachChunk(final Query<ECS_TYPE> query, @Nonnull final BiPredicate<ArchetypeChunk<ECS_TYPE>, CommandBuffer<ECS_TYPE>> predicate) {
        if (this.shutdown) {
            throw new IllegalStateException("Store is shutdown!");
        }
        this.assertThread();
        final CommandBuffer<ECS_TYPE> commandBuffer = this.takeCommandBuffer();
        boolean result = false;
        this.processing.lock();
        try {
            if (query instanceof final ExactArchetypeQuery exactArchetypeQuery) {
                final ExactArchetypeQuery<ECS_TYPE> exactQuery = exactArchetypeQuery;
                final int archetypeIndex = this.archetypeToIndexMap.getInt(exactQuery.getArchetype());
                if (archetypeIndex != Integer.MIN_VALUE) {
                    result = predicate.test(this.archetypeChunks[archetypeIndex], commandBuffer);
                }
            }
            else {
                for (int archetypeIndex = 0; archetypeIndex < this.archetypeSize; ++archetypeIndex) {
                    final ArchetypeChunk<ECS_TYPE> archetypeChunk = this.archetypeChunks[archetypeIndex];
                    if (archetypeChunk != null) {
                        if (query.test(archetypeChunk.getArchetype()) && predicate.test(this.archetypeChunks[archetypeIndex], commandBuffer)) {
                            result = true;
                            break;
                        }
                    }
                }
            }
        }
        finally {
            this.processing.unlock();
        }
        commandBuffer.consume();
        return result;
    }
    
    public void forEachChunk(final int systemIndex, @Nonnull final BiConsumer<ArchetypeChunk<ECS_TYPE>, CommandBuffer<ECS_TYPE>> consumer) {
        if (this.shutdown) {
            throw new IllegalStateException("Store is shutdown!");
        }
        this.assertThread();
        final CommandBuffer<ECS_TYPE> commandBuffer = this.takeCommandBuffer();
        this.processing.lock();
        try {
            final BitSet indexes = this.systemIndexToArchetypeChunkIndexes[systemIndex];
            int index = -1;
            while ((index = indexes.nextSetBit(index + 1)) >= 0) {
                consumer.accept(this.archetypeChunks[index], commandBuffer);
            }
        }
        finally {
            this.processing.unlock();
        }
        commandBuffer.consume();
    }
    
    public boolean forEachChunk(final int systemIndex, @Nonnull final BiPredicate<ArchetypeChunk<ECS_TYPE>, CommandBuffer<ECS_TYPE>> predicate) {
        if (this.shutdown) {
            throw new IllegalStateException("Store is shutdown!");
        }
        this.assertThread();
        final CommandBuffer<ECS_TYPE> commandBuffer = this.takeCommandBuffer();
        boolean result = false;
        this.processing.lock();
        try {
            final BitSet indexes = this.systemIndexToArchetypeChunkIndexes[systemIndex];
            int index = -1;
            while ((index = indexes.nextSetBit(index + 1)) >= 0) {
                if (predicate.test(this.archetypeChunks[index], commandBuffer)) {
                    result = true;
                    break;
                }
            }
        }
        finally {
            this.processing.unlock();
        }
        commandBuffer.consume();
        return result;
    }
    
    public void forEachEntityParallel(final IntBiObjectConsumer<ArchetypeChunk<ECS_TYPE>, CommandBuffer<ECS_TYPE>> consumer) {
        if (this.shutdown) {
            throw new IllegalStateException("Store is shutdown!");
        }
        this.assertThread();
        final CommandBuffer<ECS_TYPE> commandBuffer = this.takeCommandBuffer();
        this.forEachTask.init();
        this.processing.lock();
        try {
            for (int archetypeIndex = 0; archetypeIndex < this.archetypeSize; ++archetypeIndex) {
                final ArchetypeChunk<ECS_TYPE> archetypeChunk = this.archetypeChunks[archetypeIndex];
                if (archetypeChunk != null) {
                    final int size = archetypeChunk.size();
                    if (size != 0) {
                        final ParallelRangeTask<ForEachTaskData<ECS_TYPE>> systemTask = this.forEachTask.appendTask();
                        systemTask.init(0, size);
                        for (int i = 0, systemTaskSize = systemTask.size(); i < systemTaskSize; ++i) {
                            systemTask.get(i).init(consumer, archetypeChunk, commandBuffer.fork());
                        }
                    }
                }
            }
            ForEachTaskData.invokeParallelTask(this.forEachTask, commandBuffer);
        }
        finally {
            this.processing.unlock();
        }
        commandBuffer.consume();
    }
    
    public void forEachEntityParallel(final Query<ECS_TYPE> query, final IntBiObjectConsumer<ArchetypeChunk<ECS_TYPE>, CommandBuffer<ECS_TYPE>> consumer) {
        if (this.shutdown) {
            throw new IllegalStateException("Store is shutdown!");
        }
        this.assertThread();
        final CommandBuffer<ECS_TYPE> commandBuffer = this.takeCommandBuffer();
        this.forEachTask.init();
        this.processing.lock();
        try {
            if (query instanceof final ExactArchetypeQuery exactArchetypeQuery) {
                final ExactArchetypeQuery<ECS_TYPE> exactQuery = exactArchetypeQuery;
                final int archetypeIndex = this.archetypeToIndexMap.getInt(exactQuery.getArchetype());
                if (archetypeIndex != Integer.MIN_VALUE) {
                    final ArchetypeChunk<ECS_TYPE> archetypeChunk = this.archetypeChunks[archetypeIndex];
                    final int archetypeChunkSize = archetypeChunk.size();
                    if (archetypeChunkSize != 0) {
                        final ParallelRangeTask<ForEachTaskData<ECS_TYPE>> systemTask = this.forEachTask.appendTask();
                        systemTask.init(0, archetypeChunkSize);
                        for (int i = 0, systemSize = systemTask.size(); i < systemSize; ++i) {
                            systemTask.get(i).init(consumer, archetypeChunk, commandBuffer.fork());
                        }
                    }
                }
            }
            else {
                for (int archetypeIndex = 0; archetypeIndex < this.archetypeSize; ++archetypeIndex) {
                    final ArchetypeChunk<ECS_TYPE> archetypeChunk = this.archetypeChunks[archetypeIndex];
                    if (archetypeChunk != null) {
                        if (query.test(archetypeChunk.getArchetype())) {
                            final int archetypeChunkSize = archetypeChunk.size();
                            if (archetypeChunkSize != 0) {
                                final ParallelRangeTask<ForEachTaskData<ECS_TYPE>> systemTask = this.forEachTask.appendTask();
                                systemTask.init(0, archetypeChunkSize);
                                for (int i = 0, systemTaskSize = systemTask.size(); i < systemTaskSize; ++i) {
                                    systemTask.get(i).init(consumer, archetypeChunk, commandBuffer.fork());
                                }
                            }
                        }
                    }
                }
            }
            ForEachTaskData.invokeParallelTask(this.forEachTask, commandBuffer);
        }
        finally {
            this.processing.unlock();
        }
        commandBuffer.consume();
    }
    
    public <T extends ArchetypeDataSystem<ECS_TYPE, Q, R>, Q, R> void fetch(@Nonnull final SystemType<ECS_TYPE, T> systemType, final Q query, @Nonnull final List<R> results) {
        if (this.shutdown) {
            throw new IllegalStateException("Store is shutdown!");
        }
        this.assertThread();
        final ComponentRegistry.Data<ECS_TYPE> data = this.registry._internal_getData();
        final CommandBuffer<ECS_TYPE> commandBuffer = this.takeCommandBuffer();
        this.fetchTask.init();
        final BitSet systemIndexes = data.getSystemIndexesForType(systemType);
        this.processing.lock();
        try {
            int systemIndex = -1;
            while ((systemIndex = systemIndexes.nextSetBit(systemIndex + 1)) >= 0) {
                final T system = data.getSystem(systemIndex, systemType);
                final BitSet indexes = this.systemIndexToArchetypeChunkIndexes[systemIndex];
                int index = -1;
                while ((index = indexes.nextSetBit(index + 1)) >= 0) {
                    system.fetch(this.archetypeChunks[index], this, commandBuffer, query, results);
                }
            }
            EntityDataSystem.SystemTaskData.invokeParallelTask(this.fetchTask, commandBuffer, results);
        }
        finally {
            this.processing.unlock();
        }
        commandBuffer.consume();
    }
    
    public <T extends EntityDataSystem<ECS_TYPE, Q, R>, Q, R> void fetch(@Nonnull final Collection<Ref<ECS_TYPE>> refs, @Nonnull final SystemType<ECS_TYPE, T> systemType, @Nonnull final Q query, @Nonnull final List<R> results) {
        if (this.shutdown) {
            throw new IllegalStateException("Store is shutdown!");
        }
        this.assertThread();
        final ComponentRegistry.Data<ECS_TYPE> data = this.registry._internal_getData();
        final CommandBuffer<ECS_TYPE> commandBuffer = this.takeCommandBuffer();
        this.fetchTask.init();
        final BitSet systemIndexes = data.getSystemIndexesForType(systemType);
        this.processing.lock();
        try {
            int systemIndex = -1;
            while ((systemIndex = systemIndexes.nextSetBit(systemIndex + 1)) >= 0) {
                final T system = data.getSystem(systemIndex, systemType);
                for (final Ref<ECS_TYPE> ref : refs) {
                    final int entityIndex = ref.getIndex();
                    final int archetypeIndex = this.entityToArchetypeChunk[entityIndex];
                    final BitSet entityProcessedBySystemIndexes = this.archetypeChunkIndexesToSystemIndex[archetypeIndex];
                    if (entityProcessedBySystemIndexes.get(systemIndex)) {
                        final int index = this.entityChunkIndex[entityIndex];
                        final ArchetypeChunk<ECS_TYPE> archetypeChunk = this.archetypeChunks[archetypeIndex];
                        system.fetch(index, archetypeChunk, this, commandBuffer, query, results);
                    }
                }
            }
            EntityDataSystem.SystemTaskData.invokeParallelTask(this.fetchTask, commandBuffer, results);
        }
        finally {
            this.processing.unlock();
        }
        commandBuffer.consume();
    }
    
    @Override
    public <Event extends EcsEvent> void invoke(@Nonnull final Ref<ECS_TYPE> ref, @Nonnull final Event param) {
        final EntityEventType<ECS_TYPE, ?> eventType = this.registry.getEntityEventTypeForClass((Class<?>)param.getClass());
        if (eventType == null) {
            return;
        }
        this.invoke(eventType, ref, param);
    }
    
    @Override
    public <Event extends EcsEvent> void invoke(@Nonnull final EntityEventType<ECS_TYPE, Event> systemType, @Nonnull final Ref<ECS_TYPE> ref, @Nonnull final Event param) {
        if (this.shutdown) {
            throw new IllegalStateException("Store is shutdown!");
        }
        this.assertThread();
        final ComponentRegistry.Data<ECS_TYPE> data = this.registry._internal_getData();
        final CommandBuffer<ECS_TYPE> commandBuffer = this.takeCommandBuffer();
        commandBuffer.track(ref);
        final BitSet systemIndexes = data.getSystemIndexesForType(systemType);
        this.processing.lock();
        try {
            final int entityIndex = ref.getIndex();
            final int archetypeIndex = this.entityToArchetypeChunk[entityIndex];
            final BitSet entityProcessedBySystemIndexes = this.archetypeChunkIndexesToSystemIndex[archetypeIndex];
            final ArchetypeChunk<ECS_TYPE> archetypeChunk = this.archetypeChunks[archetypeIndex];
            final int index = this.entityChunkIndex[entityIndex];
            int systemIndex = -1;
            while ((systemIndex = systemIndexes.nextSetBit(systemIndex + 1)) >= 0) {
                systemIndex = entityProcessedBySystemIndexes.nextSetBit(systemIndex);
                if (systemIndex < 0) {
                    break;
                }
                if (!systemIndexes.get(systemIndex)) {
                    continue;
                }
                final EntityEventSystem<ECS_TYPE, Event> system = data.getSystem(systemIndex, systemType);
                system.handleInternal(index, archetypeChunk, this, commandBuffer, param);
                if (commandBuffer.consumeWasTrackedRefRemoved()) {
                    break;
                }
            }
        }
        finally {
            this.processing.unlock();
        }
        commandBuffer.consume();
    }
    
    @Override
    public <Event extends EcsEvent> void invoke(@Nonnull final Event param) {
        final WorldEventType<ECS_TYPE, ?> eventType = this.registry.getWorldEventTypeForClass((Class<?>)param.getClass());
        if (eventType == null) {
            return;
        }
        this.invoke(eventType, param);
    }
    
    @Override
    public <Event extends EcsEvent> void invoke(@Nonnull final WorldEventType<ECS_TYPE, Event> systemType, @Nonnull final Event param) {
        if (this.shutdown) {
            throw new IllegalStateException("Store is shutdown!");
        }
        this.assertThread();
        final ComponentRegistry.Data<ECS_TYPE> data = this.registry._internal_getData();
        final CommandBuffer<ECS_TYPE> commandBuffer = this.takeCommandBuffer();
        final BitSet systemIndexes = data.getSystemIndexesForType(systemType);
        this.processing.lock();
        try {
            int systemIndex = -1;
            while ((systemIndex = systemIndexes.nextSetBit(systemIndex + 1)) >= 0) {
                final WorldEventSystem<ECS_TYPE, Event> system = data.getSystem(systemIndex, systemType);
                system.handleInternal(this, commandBuffer, param);
            }
        }
        finally {
            this.processing.unlock();
        }
        commandBuffer.consume();
    }
    
    protected <Event extends EcsEvent> void internal_invoke(final CommandBuffer<ECS_TYPE> sourceCommandBuffer, final Ref<ECS_TYPE> ref, final Event param) {
        final EntityEventType<ECS_TYPE, ?> eventType = this.registry.getEntityEventTypeForClass((Class<?>)param.getClass());
        if (eventType == null) {
            return;
        }
        this.internal_invoke(sourceCommandBuffer, eventType, ref, param);
    }
    
    protected <Event extends EcsEvent> void internal_invoke(final CommandBuffer<ECS_TYPE> sourceCommandBuffer, @Nonnull final EntityEventType<ECS_TYPE, Event> systemType, final Ref<ECS_TYPE> ref, final Event param) {
        final ComponentRegistry.Data<ECS_TYPE> data = this.registry._internal_getData();
        final CommandBuffer<ECS_TYPE> commandBuffer = sourceCommandBuffer.fork();
        commandBuffer.track(ref);
        final BitSet systemIndexes = data.getSystemIndexesForType(systemType);
        final int entityIndex = ref.getIndex();
        final int archetypeIndex = this.entityToArchetypeChunk[entityIndex];
        final BitSet entityProcessedBySystemIndexes = this.archetypeChunkIndexesToSystemIndex[archetypeIndex];
        final ArchetypeChunk<ECS_TYPE> archetypeChunk = this.archetypeChunks[archetypeIndex];
        final int index = this.entityChunkIndex[entityIndex];
        int systemIndex = -1;
        while ((systemIndex = systemIndexes.nextSetBit(systemIndex + 1)) >= 0) {
            systemIndex = entityProcessedBySystemIndexes.nextSetBit(systemIndex);
            if (systemIndex < 0) {
                break;
            }
            if (!systemIndexes.get(systemIndex)) {
                continue;
            }
            final EntityEventSystem<ECS_TYPE, Event> system = data.getSystem(systemIndex, systemType);
            system.handleInternal(index, archetypeChunk, this, commandBuffer, param);
            if (commandBuffer.consumeWasTrackedRefRemoved()) {
                break;
            }
        }
        commandBuffer.mergeParallel(sourceCommandBuffer);
    }
    
    protected <Event extends EcsEvent> void internal_invoke(final CommandBuffer<ECS_TYPE> sourceCommandBuffer, final Event param) {
        final WorldEventType<ECS_TYPE, ?> eventType = this.registry.getWorldEventTypeForClass((Class<?>)param.getClass());
        if (eventType == null) {
            return;
        }
        this.internal_invoke(sourceCommandBuffer, eventType, param);
    }
    
    protected <Event extends EcsEvent> void internal_invoke(final CommandBuffer<ECS_TYPE> sourceCommandBuffer, @Nonnull final WorldEventType<ECS_TYPE, Event> systemType, final Event param) {
        final ComponentRegistry.Data<ECS_TYPE> data = this.registry._internal_getData();
        final BitSet systemIndexes = data.getSystemIndexesForType(systemType);
        int systemIndex = -1;
        while ((systemIndex = systemIndexes.nextSetBit(systemIndex + 1)) >= 0) {
            final WorldEventSystem<ECS_TYPE, Event> system = data.getSystem(systemIndex, systemType);
            system.handleInternal(this, sourceCommandBuffer, param);
        }
    }
    
    public void tick(final float dt) {
        this.tickInternal(dt, this.registry.getTickingSystemType());
    }
    
    public void pausedTick(final float dt) {
        if (this.shutdown) {
            throw new IllegalStateException("Store is shutdown!");
        }
        this.assertThread();
        this.tickInternal(dt, this.registry.getRunWhenPausedSystemType());
    }
    
    private <Tickable extends TickableSystem<ECS_TYPE>> void tickInternal(final float dt, final SystemType<ECS_TYPE, Tickable> tickingSystemType) {
        if (this.shutdown) {
            throw new IllegalStateException("Store is shutdown!");
        }
        this.assertThread();
        this.registry.getDataUpdateLock().readLock().lock();
        try {
            final ComponentRegistry.Data<ECS_TYPE> data = this.registry.doDataUpdate();
            final BitSet systemIndexes = data.getSystemIndexesForType(tickingSystemType);
            int systemIndex = -1;
            while ((systemIndex = systemIndexes.nextSetBit(systemIndex + 1)) >= 0) {
                final Tickable tickingSystem = data.getSystem(systemIndex, tickingSystemType);
                final long start = System.nanoTime();
                tickingSystem.tick(dt, systemIndex, this);
                final long end = System.nanoTime();
                this.systemMetrics[systemIndex].add(end, end - start);
            }
        }
        finally {
            this.registry.getDataUpdateLock().readLock().unlock();
        }
    }
    
    public void tick(final ArchetypeTickingSystem<ECS_TYPE> system, final float dt, final int systemIndex) {
        if (this.shutdown) {
            throw new IllegalStateException("Store is shutdown!");
        }
        this.assertThread();
        final CommandBuffer<ECS_TYPE> commandBuffer = this.takeCommandBuffer();
        this.parallelTask.init();
        final boolean oldDisableProcessingAssert = this.disableProcessingAssert;
        this.disableProcessingAssert = (system instanceof DisableProcessingAssert);
        this.processing.lock();
        try {
            final BitSet indexes = this.systemIndexToArchetypeChunkIndexes[systemIndex];
            int index = -1;
            while ((index = indexes.nextSetBit(index + 1)) >= 0) {
                system.tick(dt, this.archetypeChunks[index], this, commandBuffer);
            }
            EntityTickingSystem.SystemTaskData.invokeParallelTask(this.parallelTask, commandBuffer);
        }
        finally {
            this.processing.unlock();
            this.disableProcessingAssert = oldDisableProcessingAssert;
        }
        commandBuffer.consume();
    }
    
    void updateData(@Nonnull final ComponentRegistry.Data<ECS_TYPE> oldData, @Nonnull final ComponentRegistry.Data<ECS_TYPE> data) {
        if (this.shutdown) {
            throw new IllegalStateException("Store is shutdown!");
        }
        final int resourceSize = data.getResourceSize();
        this.resources = Arrays.copyOf(this.resources, resourceSize);
        for (int index = 0; index < this.resources.length; ++index) {
            final ResourceType<ECS_TYPE, ? extends Resource<ECS_TYPE>> resourceType = (ResourceType<ECS_TYPE, ? extends Resource<ECS_TYPE>>)data.getResourceType(index);
            if (this.resources[index] == null && resourceType != null) {
                this.resources[index] = (Resource)this.resourceStorage.load(this, data, resourceType).join();
            }
            else if (this.resources[index] != null && resourceType == null) {
                this.resources[index] = null;
            }
        }
        boolean systemChanged = false;
        for (int i = 0; i < data.getDataChangeCount(); ++i) {
            final DataChange dataChange = data.getDataChange(i);
            systemChanged |= (dataChange instanceof SystemChange);
            this.updateData(oldData, data, dataChange);
        }
        final HistoricMetric[] oldSystemMetrics = this.systemMetrics;
        this.systemMetrics = new HistoricMetric[data.getSystemSize()];
        final SystemType<ECS_TYPE, TickableSystem<ECS_TYPE>> tickingSystemType = this.registry.getTickableSystemType();
        final BitSet systemIndexes = data.getSystemIndexesForType(tickingSystemType);
        int systemIndex = -1;
        while ((systemIndex = systemIndexes.nextSetBit(systemIndex + 1)) >= 0) {
            final ISystem<ECS_TYPE> system = data.getSystem(systemIndex);
            final int oldSystemIndex = oldData.indexOf(system);
            if (oldSystemIndex >= 0) {
                this.systemMetrics[systemIndex] = oldSystemMetrics[oldSystemIndex];
            }
            else {
                this.systemMetrics[systemIndex] = HistoricMetric.builder(33333333L, TimeUnit.NANOSECONDS).addPeriod(1L, TimeUnit.SECONDS).addPeriod(1L, TimeUnit.MINUTES).addPeriod(5L, TimeUnit.MINUTES).build();
            }
        }
        if (systemChanged) {
            this.updateArchetypeIndexes(data);
        }
    }
    
    private void updateData(@Nonnull final ComponentRegistry.Data<ECS_TYPE> oldData, @Nonnull final ComponentRegistry.Data<ECS_TYPE> newData, final DataChange dataChange) {
        this.processing.lock();
        try {
            this.updateData0(oldData, newData, dataChange);
        }
        finally {
            this.processing.unlock();
        }
        if (dataChange instanceof SystemChange) {
            final SystemChange<ECS_TYPE> systemChange = (SystemChange<ECS_TYPE>)dataChange;
            final ISystem<ECS_TYPE> system = systemChange.getSystem();
            switch (systemChange.getType()) {
                case REGISTERED: {
                    if (system instanceof final StoreSystem storeSystem) {
                        storeSystem.onSystemAddedToStore(this);
                        break;
                    }
                    break;
                }
                case UNREGISTERED: {
                    if (system instanceof final StoreSystem storeSystem2) {
                        storeSystem2.onSystemRemovedFromStore(this);
                        break;
                    }
                    break;
                }
            }
        }
    }
    
    private void updateData0(@Nonnull final ComponentRegistry.Data<ECS_TYPE> oldData, @Nonnull final ComponentRegistry.Data<ECS_TYPE> newData, final DataChange dataChange) {
        if (dataChange instanceof ComponentChange) {
            final ComponentChange<ECS_TYPE, ? extends Component<ECS_TYPE>> componentChange = (ComponentChange<ECS_TYPE, ? extends Component<ECS_TYPE>>)dataChange;
            final ComponentType<ECS_TYPE, ? extends Component<ECS_TYPE>> componentType = componentChange.getComponentType();
            final ComponentType<ECS_TYPE, UnknownComponents<ECS_TYPE>> unknownComponentType = this.registry.getUnknownComponentType();
            switch (componentChange.getType()) {
                case REGISTERED: {
                    final String componentId = newData.getComponentId(componentType);
                    final Codec<Component<ECS_TYPE>> componentCodec = newData.getComponentCodec(componentType);
                    if (componentCodec != null) {
                        final Holder<ECS_TYPE> tempInternalEntityHolder = this.registry._internal_newEntityHolder();
                        for (int oldArchetypeSize = this.archetypeSize, archetypeIndex = 0; archetypeIndex < oldArchetypeSize; ++archetypeIndex) {
                            final ArchetypeChunk<ECS_TYPE> archetypeChunk = this.archetypeChunks[archetypeIndex];
                            if (archetypeChunk != null) {
                                final Archetype<ECS_TYPE> archetype = archetypeChunk.getArchetype();
                                if (!archetype.contains(componentType)) {
                                    if (archetype.contains(unknownComponentType)) {
                                        final Archetype<ECS_TYPE> newArchetype = Archetype.add(archetype, componentType);
                                        final int toArchetypeIndex = this.findOrCreateArchetypeChunk(newArchetype);
                                        final ArchetypeChunk<ECS_TYPE> toArchetypeChunk = this.archetypeChunks[toArchetypeIndex];
                                        archetypeChunk.transferSomeTo(tempInternalEntityHolder, toArchetypeChunk, index -> {
                                            final UnknownComponents<ECS_TYPE> unknownComponents = archetypeChunk.getComponent(index, unknownComponentType);
                                            if (!Store.$assertionsDisabled && unknownComponents == null) {
                                                throw new AssertionError();
                                            }
                                            else {
                                                return unknownComponents.contains(componentId);
                                            }
                                        }, entity -> {
                                            final UnknownComponents<ECS_TYPE> unknownComponents2 = entity.getComponent(unknownComponentType);
                                            if (!Store.$assertionsDisabled && unknownComponents2 == null) {
                                                throw new AssertionError();
                                            }
                                            else {
                                                final Component<ECS_TYPE> component = unknownComponents2.removeComponent(componentId, (Codec<Component<ECS_TYPE>>)componentCodec);
                                                entity.addComponent(componentType, component);
                                                return;
                                            }
                                        }, (newChunkEntityRef, ref) -> {
                                            this.entityToArchetypeChunk[ref.getIndex()] = toArchetypeIndex;
                                            this.entityChunkIndex[ref.getIndex()] = newChunkEntityRef;
                                            return;
                                        });
                                        if (archetypeChunk.size() == 0) {
                                            this.archetypeToIndexMap.removeInt(this.archetypeChunks[archetypeIndex].getArchetype());
                                            this.archetypeChunks[archetypeIndex] = null;
                                            for (int systemIndex = 0; systemIndex < oldData.getSystemSize(); ++systemIndex) {
                                                this.systemIndexToArchetypeChunkIndexes[systemIndex].clear(archetypeIndex);
                                            }
                                            this.archetypeChunkIndexesToSystemIndex[archetypeIndex].clear();
                                            this.archetypeChunkReuse.set(archetypeIndex);
                                        }
                                        if (toArchetypeChunk.size() == 0) {
                                            this.archetypeToIndexMap.removeInt(this.archetypeChunks[toArchetypeIndex].getArchetype());
                                            this.archetypeChunks[toArchetypeIndex] = null;
                                            for (int systemIndex = 0; systemIndex < oldData.getSystemSize(); ++systemIndex) {
                                                this.systemIndexToArchetypeChunkIndexes[systemIndex].clear(toArchetypeIndex);
                                            }
                                            this.archetypeChunkIndexesToSystemIndex[toArchetypeIndex].clear();
                                            this.archetypeChunkReuse.set(toArchetypeIndex);
                                        }
                                    }
                                }
                            }
                        }
                        break;
                    }
                    break;
                }
                case UNREGISTERED: {
                    final Holder<ECS_TYPE> tempInternalEntityHolder = this.registry._internal_newEntityHolder();
                    final String componentId = oldData.getComponentId(componentType);
                    final Codec<Component<ECS_TYPE>> componentCodec = oldData.getComponentCodec(componentType);
                    final int oldArchetypeSize = this.archetypeSize;
                    for (int archetypeIndex = 0; archetypeIndex < oldArchetypeSize; ++archetypeIndex) {
                        final ArchetypeChunk<ECS_TYPE> archetypeChunk = this.archetypeChunks[archetypeIndex];
                        if (archetypeChunk != null) {
                            final Archetype<ECS_TYPE> archetype = archetypeChunk.getArchetype();
                            if (archetype.contains(componentType)) {
                                this.archetypeToIndexMap.removeInt(this.archetypeChunks[archetypeIndex].getArchetype());
                                this.archetypeChunks[archetypeIndex] = null;
                                for (int systemIndex2 = 0; systemIndex2 < oldData.getSystemSize(); ++systemIndex2) {
                                    this.systemIndexToArchetypeChunkIndexes[systemIndex2].clear(archetypeIndex);
                                }
                                this.archetypeChunkIndexesToSystemIndex[archetypeIndex].clear();
                                this.archetypeChunkReuse.set(archetypeIndex);
                                Archetype<ECS_TYPE> newArchetype = Archetype.remove(archetype, componentType);
                                if (componentCodec != null && !newArchetype.contains(unknownComponentType)) {
                                    newArchetype = Archetype.add(newArchetype, unknownComponentType);
                                }
                                final int toArchetypeIndex = this.findOrCreateArchetypeChunk(newArchetype);
                                final ArchetypeChunk<ECS_TYPE> toArchetypeChunk = this.archetypeChunks[toArchetypeIndex];
                                archetypeChunk.transferTo(tempInternalEntityHolder, toArchetypeChunk, entity -> {
                                    if (componentCodec != null) {
                                        UnknownComponents<ECS_TYPE> unknownComponents3;
                                        if (entity.getArchetype().contains(unknownComponentType)) {
                                            unknownComponents3 = entity.getComponent(unknownComponentType);
                                            assert unknownComponents3 != null;
                                        }
                                        else {
                                            unknownComponents3 = new UnknownComponents<ECS_TYPE>();
                                            entity.addComponent(unknownComponentType, unknownComponents3);
                                        }
                                        final Component<ECS_TYPE> component2 = entity.getComponent(componentType);
                                        unknownComponents3.addComponent(componentId, component2, componentCodec);
                                    }
                                    entity.removeComponent(componentType);
                                    return;
                                }, (newChunkEntityRef, ref) -> {
                                    this.entityToArchetypeChunk[ref.getIndex()] = toArchetypeIndex;
                                    this.entityChunkIndex[ref.getIndex()] = newChunkEntityRef;
                                    return;
                                });
                            }
                        }
                    }
                    final int highestUsedIndex = this.archetypeChunkReuse.previousClearBit(oldArchetypeSize - 1);
                    this.archetypeSize = highestUsedIndex + 1;
                    this.archetypeChunkReuse.clear(this.archetypeSize, oldArchetypeSize);
                    break;
                }
            }
        }
        else if (dataChange instanceof SystemChange) {
            final SystemChange<ECS_TYPE> systemChange = (SystemChange<ECS_TYPE>)dataChange;
            final ISystem<ECS_TYPE> system = systemChange.getSystem();
            switch (systemChange.getType()) {
                case REGISTERED: {
                    if (system instanceof final ArchetypeChunkSystem archetypeChunkSystem2) {
                        final ArchetypeChunkSystem<ECS_TYPE> archetypeChunkSystem = archetypeChunkSystem2;
                        for (int archetypeIndex2 = 0; archetypeIndex2 < this.archetypeSize; ++archetypeIndex2) {
                            final ArchetypeChunk<ECS_TYPE> archetypeChunk2 = this.archetypeChunks[archetypeIndex2];
                            if (archetypeChunk2 != null) {
                                if (archetypeChunkSystem.test(this.registry, archetypeChunk2.getArchetype())) {
                                    archetypeChunkSystem.onSystemAddedToArchetypeChunk(archetypeChunk2);
                                }
                            }
                        }
                    }
                    break;
                }
                case UNREGISTERED: {
                    if (system instanceof final ArchetypeChunkSystem archetypeChunkSystem3) {
                        final ArchetypeChunkSystem<ECS_TYPE> archetypeChunkSystem = archetypeChunkSystem3;
                        for (int archetypeIndex2 = 0; archetypeIndex2 < this.archetypeSize; ++archetypeIndex2) {
                            final ArchetypeChunk<ECS_TYPE> archetypeChunk2 = this.archetypeChunks[archetypeIndex2];
                            if (archetypeChunk2 != null) {
                                if (archetypeChunkSystem.test(this.registry, archetypeChunk2.getArchetype())) {
                                    archetypeChunkSystem.onSystemRemovedFromArchetypeChunk(archetypeChunk2);
                                }
                            }
                        }
                        break;
                    }
                    break;
                }
            }
        }
    }
    
    private void updateArchetypeIndexes(@Nonnull final ComponentRegistry.Data<ECS_TYPE> data) {
        final int systemSize = data.getSystemSize();
        final int oldLength = this.systemIndexToArchetypeChunkIndexes.length;
        if (oldLength < systemSize) {
            this.systemIndexToArchetypeChunkIndexes = Arrays.copyOf(this.systemIndexToArchetypeChunkIndexes, systemSize);
            for (int i = oldLength; i < systemSize; ++i) {
                this.systemIndexToArchetypeChunkIndexes[i] = new BitSet(this.archetypeSize);
            }
        }
        for (int systemIndex = 0; systemIndex < oldLength; ++systemIndex) {
            this.systemIndexToArchetypeChunkIndexes[systemIndex].clear();
        }
        for (int archetypeIndex = 0; archetypeIndex < this.archetypeSize; ++archetypeIndex) {
            this.archetypeChunkIndexesToSystemIndex[archetypeIndex].clear();
        }
        final SystemType<ECS_TYPE, QuerySystem<ECS_TYPE>> entityQuerySystemType = this.registry.getQuerySystemType();
        final BitSet systemIndexes = data.getSystemIndexesForType(entityQuerySystemType);
        int systemIndex2 = -1;
        while ((systemIndex2 = systemIndexes.nextSetBit(systemIndex2 + 1)) >= 0) {
            final QuerySystem<ECS_TYPE> system = data.getSystem(systemIndex2, entityQuerySystemType);
            final BitSet archetypeChunkIndexes = this.systemIndexToArchetypeChunkIndexes[systemIndex2];
            for (int archetypeIndex2 = 0; archetypeIndex2 < this.archetypeSize; ++archetypeIndex2) {
                final ArchetypeChunk<ECS_TYPE> archetypeChunk = this.archetypeChunks[archetypeIndex2];
                if (archetypeChunk != null) {
                    if (system.test(this.registry, archetypeChunk.getArchetype())) {
                        archetypeChunkIndexes.set(archetypeIndex2);
                        this.archetypeChunkIndexesToSystemIndex[archetypeIndex2].set(systemIndex2);
                    }
                }
            }
        }
    }
    
    public void assertWriteProcessing() {
        if (this.processing.isHeld() && !this.disableProcessingAssert) {
            throw new IllegalStateException("Store is currently processing! Ensure you aren't calling a store method from a system.");
        }
    }
    
    @Deprecated
    public boolean isProcessing() {
        return this.processing.isHeld();
    }
    
    public void assertThread() {
        final Thread currentThread = Thread.currentThread();
        if (!currentThread.equals(this.thread) && this.thread.isAlive()) {
            throw new IllegalStateException("Assert not in thread! " + String.valueOf(this.thread) + " but was in " + String.valueOf(currentThread));
        }
    }
    
    public boolean isInThread() {
        return Thread.currentThread().equals(this.thread);
    }
    
    public boolean isAliveInDifferentThread() {
        return this.thread.isAlive() && !Thread.currentThread().equals(this.thread);
    }
    
    @Nonnull
    @Override
    public String toString() {
        return "Store{super()=" + String.valueOf(this.getClass()) + "@" + this.hashCode() + ", registry=" + String.valueOf(this.registry.getClass()) + "@" + this.registry.hashCode() + ", shutdown=" + this.shutdown + ", storeIndex=" + this.storeIndex + ", systemIndexToArchetypeChunkIndexes=" + Arrays.toString(this.systemIndexToArchetypeChunkIndexes) + ", archetypeSize=" + this.archetypeSize + ", archetypeChunks=" + Arrays.toString(this.archetypeChunks);
    }
    
    private <T extends Component<ECS_TYPE>> void datachunk_addComponent(@Nonnull final Ref<ECS_TYPE> ref, final int fromArchetypeIndex, @Nonnull final ComponentType<ECS_TYPE, T> componentType, @Nonnull final T component, @Nonnull final CommandBuffer<ECS_TYPE> commandBuffer) {
        final int entityIndex = ref.getIndex();
        final ArchetypeChunk<ECS_TYPE> fromArchetypeChunk = this.archetypeChunks[fromArchetypeIndex];
        final int oldChunkEntityRef = this.entityChunkIndex[entityIndex];
        final Holder<ECS_TYPE> holder = this.registry._internal_newEntityHolder();
        fromArchetypeChunk.removeEntity(oldChunkEntityRef, holder);
        holder.addComponent(componentType, component);
        final int toArchetypeIndex = this.findOrCreateArchetypeChunk(holder.getArchetype());
        final ArchetypeChunk<ECS_TYPE> toArchetypeChunk = this.archetypeChunks[toArchetypeIndex];
        final int chunkEntityRef = toArchetypeChunk.addEntity(ref, holder);
        this.entityToArchetypeChunk[entityIndex] = toArchetypeIndex;
        this.entityChunkIndex[entityIndex] = chunkEntityRef;
        final BitSet entityProcessedBySystemIndexes = this.archetypeChunkIndexesToSystemIndex[toArchetypeIndex];
        final ComponentRegistry.Data<ECS_TYPE> data = this.registry._internal_getData();
        final BitSet systemIndexes = data.getSystemIndexesForType(this.registry.getRefChangeSystemType());
        int systemIndex = -1;
        while ((systemIndex = systemIndexes.nextSetBit(systemIndex + 1)) >= 0) {
            if (entityProcessedBySystemIndexes.get(systemIndex)) {
                final RefChangeSystem<ECS_TYPE, T> system = (RefChangeSystem)data.getSystem(systemIndex);
                if (system.componentType().getIndex() != componentType.getIndex()) {
                    continue;
                }
                system.onComponentAdded(ref, component, this, commandBuffer);
            }
        }
        if (fromArchetypeChunk.size() == 0) {
            this.removeArchetypeChunk(fromArchetypeIndex);
        }
    }
    
    private int findOrCreateArchetypeChunk(@Nonnull final Archetype<ECS_TYPE> archetype) {
        int archetypeIndex = this.archetypeToIndexMap.getInt(archetype);
        if (archetypeIndex != Integer.MIN_VALUE) {
            return archetypeIndex;
        }
        final ComponentRegistry.Data<ECS_TYPE> data = this.registry._internal_getData();
        if (this.archetypeChunkReuse.isEmpty()) {
            archetypeIndex = this.archetypeSize++;
        }
        else {
            archetypeIndex = this.archetypeChunkReuse.nextSetBit(0);
            this.archetypeChunkReuse.clear(archetypeIndex);
        }
        final int oldLength = this.archetypeChunks.length;
        if (oldLength <= archetypeIndex) {
            final int newLength = ArrayUtil.grow(archetypeIndex);
            this.archetypeChunks = Arrays.copyOf(this.archetypeChunks, newLength);
            this.archetypeChunkIndexesToSystemIndex = Arrays.copyOf(this.archetypeChunkIndexesToSystemIndex, newLength);
            final int systemSize = data.getSystemSize();
            for (int i = oldLength; i < newLength; ++i) {
                this.archetypeChunkIndexesToSystemIndex[i] = new BitSet(systemSize);
            }
        }
        final ArchetypeChunk<ECS_TYPE> archetypeChunk = new ArchetypeChunk<ECS_TYPE>(this, archetype);
        this.archetypeChunks[archetypeIndex] = archetypeChunk;
        this.archetypeToIndexMap.put(archetype, archetypeIndex);
        final BitSet archetypeChunkToSystemIndex = this.archetypeChunkIndexesToSystemIndex[archetypeIndex];
        final SystemType<ECS_TYPE, QuerySystem<ECS_TYPE>> entityQuerySystemType = this.registry.getQuerySystemType();
        final BitSet systemIndexes = data.getSystemIndexesForType(entityQuerySystemType);
        int systemIndex = -1;
        while ((systemIndex = systemIndexes.nextSetBit(systemIndex + 1)) >= 0) {
            final QuerySystem<ECS_TYPE> system = data.getSystem(systemIndex, entityQuerySystemType);
            if (system.test(this.registry, archetype)) {
                this.systemIndexToArchetypeChunkIndexes[systemIndex].set(archetypeIndex);
                archetypeChunkToSystemIndex.set(systemIndex);
                if (!(system instanceof ArchetypeChunkSystem)) {
                    continue;
                }
                ((ArchetypeChunkSystem)system).onSystemAddedToArchetypeChunk(archetypeChunk);
            }
        }
        return archetypeIndex;
    }
    
    private void removeArchetypeChunk(final int archetypeIndex) {
        final ArchetypeChunk<ECS_TYPE> archetypeChunk = this.archetypeChunks[archetypeIndex];
        final Archetype<ECS_TYPE> archetype = archetypeChunk.getArchetype();
        this.archetypeToIndexMap.removeInt(archetype);
        this.archetypeChunks[archetypeIndex] = null;
        this.archetypeChunkIndexesToSystemIndex[archetypeIndex].clear();
        final ComponentRegistry.Data<ECS_TYPE> data = this.registry._internal_getData();
        final BitSet systemIndexes = data.getSystemIndexesForType(this.registry.getQuerySystemType());
        int systemIndex = -1;
        while ((systemIndex = systemIndexes.nextSetBit(systemIndex + 1)) >= 0) {
            this.systemIndexToArchetypeChunkIndexes[systemIndex].clear(archetypeIndex);
            final ISystem<ECS_TYPE> system = data.getSystem(systemIndex);
            if (system instanceof final ArchetypeChunkSystem archetypeChunkSystem2) {
                final ArchetypeChunkSystem<ECS_TYPE> archetypeChunkSystem = archetypeChunkSystem2;
                if (!archetypeChunkSystem.test(this.registry, archetype)) {
                    continue;
                }
                archetypeChunkSystem.onSystemRemovedFromArchetypeChunk(archetypeChunk);
            }
        }
        if (archetypeIndex == this.archetypeSize - 1) {
            final int highestUsedIndex = this.archetypeChunkReuse.previousClearBit(archetypeIndex - 1);
            this.archetypeSize = highestUsedIndex + 1;
            this.archetypeChunkReuse.clear(this.archetypeSize, archetypeIndex);
        }
        else {
            this.archetypeChunkReuse.set(archetypeIndex);
        }
    }
    
    static {
        // 
        // This method could not be decompiled.
        // 
        // Original Bytecode:
        // 
        //     2: invokevirtual   java/lang/Class.desiredAssertionStatus:()Z
        //     5: ifne            12
        //     8: iconst_1       
        //     9: goto            13
        //    12: iconst_0       
        //    13: putstatic       com/hypixel/hytale/component/Store.$assertionsDisabled:Z
        //    16: iconst_0       
        //    17: anewarray       Lcom/hypixel/hytale/component/Store;
        //    20: putstatic       com/hypixel/hytale/component/Store.EMPTY_ARRAY:[Lcom/hypixel/hytale/component/Store;
        //    23: new             Lcom/hypixel/hytale/metrics/MetricsRegistry;
        //    26: dup            
        //    27: invokespecial   com/hypixel/hytale/metrics/MetricsRegistry.<init>:()V
        //    30: ldc_w           "ArchetypeChunkCount"
        //    33: invokedynamic   BootstrapMethod #11, apply:()Ljava/util/function/Function;
        //    38: getstatic       com/hypixel/hytale/codec/Codec.INTEGER:Lcom/hypixel/hytale/codec/codecs/simple/IntegerCodec;
        //    41: invokevirtual   com/hypixel/hytale/metrics/MetricsRegistry.register:(Ljava/lang/String;Ljava/util/function/Function;Lcom/hypixel/hytale/codec/Codec;)Lcom/hypixel/hytale/metrics/MetricsRegistry;
        //    44: ldc_w           "EntityCount"
        //    47: invokedynamic   BootstrapMethod #12, apply:()Ljava/util/function/Function;
        //    52: getstatic       com/hypixel/hytale/codec/Codec.INTEGER:Lcom/hypixel/hytale/codec/codecs/simple/IntegerCodec;
        //    55: invokevirtual   com/hypixel/hytale/metrics/MetricsRegistry.register:(Ljava/lang/String;Ljava/util/function/Function;Lcom/hypixel/hytale/codec/Codec;)Lcom/hypixel/hytale/metrics/MetricsRegistry;
        //    58: ldc_w           "Systems"
        //    61: invokedynamic   BootstrapMethod #13, apply:()Ljava/util/function/Function;
        //    66: new             Lcom/hypixel/hytale/codec/codecs/array/ArrayCodec;
        //    69: dup            
        //    70: getstatic       com/hypixel/hytale/component/metric/SystemMetricData.CODEC:Lcom/hypixel/hytale/codec/Codec;
        //    73: invokedynamic   BootstrapMethod #14, apply:()Ljava/util/function/IntFunction;
        //    78: invokespecial   com/hypixel/hytale/codec/codecs/array/ArrayCodec.<init>:(Lcom/hypixel/hytale/codec/Codec;Ljava/util/function/IntFunction;)V
        //    81: invokevirtual   com/hypixel/hytale/metrics/MetricsRegistry.register:(Ljava/lang/String;Ljava/util/function/Function;Lcom/hypixel/hytale/codec/Codec;)Lcom/hypixel/hytale/metrics/MetricsRegistry;
        //    84: ldc_w           "ArchetypeChunks"
        //    87: invokedynamic   BootstrapMethod #15, apply:()Ljava/util/function/Function;
        //    92: new             Lcom/hypixel/hytale/codec/codecs/array/ArrayCodec;
        //    95: dup            
        //    96: getstatic       com/hypixel/hytale/component/metric/ArchetypeChunkData.CODEC:Lcom/hypixel/hytale/codec/Codec;
        //    99: invokedynamic   BootstrapMethod #16, apply:()Ljava/util/function/IntFunction;
        //   104: invokespecial   com/hypixel/hytale/codec/codecs/array/ArrayCodec.<init>:(Lcom/hypixel/hytale/codec/Codec;Ljava/util/function/IntFunction;)V
        //   107: invokevirtual   com/hypixel/hytale/metrics/MetricsRegistry.register:(Ljava/lang/String;Ljava/util/function/Function;Lcom/hypixel/hytale/codec/Codec;)Lcom/hypixel/hytale/metrics/MetricsRegistry;
        //   110: putstatic       com/hypixel/hytale/component/Store.METRICS_REGISTRY:Lcom/hypixel/hytale/metrics/MetricsRegistry;
        //   113: return         
        //    StackMapTable: 00 02 0C 40 01
        // 
        // The error that occurred was:
        // 
        // java.lang.IllegalStateException: Could not infer any expression.
        //     at com.strobel.decompiler.ast.TypeAnalysis.runInference(TypeAnalysis.java:382)
        //     at com.strobel.decompiler.ast.TypeAnalysis.run(TypeAnalysis.java:95)
        //     at com.strobel.decompiler.ast.AstOptimizer.optimize(AstOptimizer.java:344)
        //     at com.strobel.decompiler.ast.AstOptimizer.optimize(AstOptimizer.java:42)
        //     at com.strobel.decompiler.languages.java.ast.AstMethodBodyBuilder.createMethodBody(AstMethodBodyBuilder.java:206)
        //     at com.strobel.decompiler.languages.java.ast.AstMethodBodyBuilder.createMethodBody(AstMethodBodyBuilder.java:93)
        //     at com.strobel.decompiler.languages.java.ast.AstBuilder.createMethodBody(AstBuilder.java:868)
        //     at com.strobel.decompiler.languages.java.ast.AstBuilder.createMethod(AstBuilder.java:761)
        //     at com.strobel.decompiler.languages.java.ast.AstBuilder.addTypeMembers(AstBuilder.java:638)
        //     at com.strobel.decompiler.languages.java.ast.AstBuilder.createTypeCore(AstBuilder.java:605)
        //     at com.strobel.decompiler.languages.java.ast.AstBuilder.createTypeNoCache(AstBuilder.java:195)
        //     at com.strobel.decompiler.languages.java.ast.AstBuilder.createType(AstBuilder.java:162)
        //     at com.strobel.decompiler.languages.java.ast.AstBuilder.addType(AstBuilder.java:137)
        //     at com.strobel.decompiler.languages.java.JavaLanguage.buildAst(JavaLanguage.java:71)
        //     at com.strobel.decompiler.languages.java.JavaLanguage.decompileType(JavaLanguage.java:59)
        //     at com.strobel.decompiler.DecompilerDriver.decompileType(DecompilerDriver.java:333)
        //     at com.strobel.decompiler.DecompilerDriver.decompileJar(DecompilerDriver.java:254)
        //     at com.strobel.decompiler.DecompilerDriver.main(DecompilerDriver.java:129)
        // 
        throw new IllegalStateException("An error occurred while decompiling this method.");
    }
    
    private static class ProcessingCounter implements Lock
    {
        private int count;
        
        private ProcessingCounter() {
            this.count = 0;
        }
        
        public boolean isHeld() {
            return this.count > 0;
        }
        
        @Override
        public void lock() {
            ++this.count;
        }
        
        @Override
        public void lockInterruptibly() {
            throw new UnsupportedOperationException("lockInterruptibly() is not supported");
        }
        
        @Override
        public boolean tryLock() {
            throw new UnsupportedOperationException("tryLock() is not supported");
        }
        
        @Override
        public boolean tryLock(final long time, @Nonnull final TimeUnit unit) {
            throw new UnsupportedOperationException("tryLock() is not supported");
        }
        
        @Override
        public void unlock() {
            --this.count;
        }
        
        @Nonnull
        @Override
        public Condition newCondition() {
            throw new UnsupportedOperationException("Conditions are not supported");
        }
    }
}
