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

package com.hypixel.hytale.server.spawning;

import com.hypixel.hytale.component.dependency.RootDependency;
import com.hypixel.hytale.component.dependency.Dependency;
import com.hypixel.hytale.component.Archetype;
import com.hypixel.hytale.server.core.modules.entity.component.HiddenFromAdventurePlayers;
import java.util.UUID;
import com.hypixel.hytale.server.core.entity.Entity;
import org.bson.BsonDocument;
import com.hypixel.hytale.server.core.modules.entity.AllLegacyEntityTypesQuery;
import com.hypixel.hytale.component.data.unknown.UnknownComponents;
import com.hypixel.hytale.server.core.entity.UUIDComponent;
import com.hypixel.hytale.server.core.entity.nameplate.Nameplate;
import com.hypixel.hytale.server.core.modules.entity.component.PersistentModel;
import java.util.function.Predicate;
import com.hypixel.hytale.component.spatial.SpatialStructure;
import com.hypixel.hytale.component.CommandBuffer;
import com.hypixel.hytale.component.ArchetypeChunk;
import com.hypixel.hytale.common.util.FormatUtil;
import joptsimple.OptionSpec;
import com.hypixel.hytale.server.core.Options;
import com.hypixel.hytale.logger.HytaleLogger;
import it.unimi.dsi.fastutil.ints.IntIterator;
import javax.annotation.Nullable;
import com.hypixel.hytale.server.spawning.world.manager.WorldSpawnWrapper;
import it.unimi.dsi.fastutil.objects.ObjectIterator;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import com.hypixel.hytale.server.npc.asset.builder.BuilderInfo;
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
import java.util.Set;
import com.hypixel.hytale.component.Holder;
import it.unimi.dsi.fastutil.objects.ObjectListIterator;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import com.hypixel.hytale.component.AddReason;
import com.hypixel.hytale.component.RemoveReason;
import com.hypixel.hytale.server.core.universe.Universe;
import com.hypixel.hytale.common.map.IWeightedMap;
import com.hypixel.hytale.server.spawning.assets.spawns.config.RoleSpawnParameters;
import java.util.Iterator;
import com.hypixel.hytale.server.spawning.assets.spawns.config.NPCSpawn;
import java.util.Map;
import it.unimi.dsi.fastutil.ints.IntSet;
import java.util.List;
import com.hypixel.hytale.server.spawning.world.manager.EnvironmentSpawnParameters;
import com.hypixel.hytale.server.core.universe.world.World;
import com.hypixel.hytale.server.spawning.world.WorldEnvironmentSpawnData;
import com.hypixel.hytale.server.spawning.wrappers.SpawnWrapper;
import com.hypixel.hytale.server.spawning.managers.SpawnManager;
import com.hypixel.hytale.server.spawning.world.WorldNPCSpawnStat;
import com.hypixel.hytale.server.core.modules.time.WorldTimeResource;
import com.hypixel.hytale.server.npc.entities.NPCEntity;
import com.hypixel.hytale.component.Store;
import com.hypixel.hytale.server.spawning.wrappers.BeaconSpawnWrapper;
import java.util.logging.Level;
import com.hypixel.hytale.math.util.MathUtil;
import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent;
import com.hypixel.hytale.server.core.entity.entities.Player;
import com.hypixel.hytale.codec.builder.BuilderCodec;
import com.hypixel.hytale.server.spawning.interactions.TriggerSpawnMarkersInteraction;
import com.hypixel.hytale.component.query.Query;
import com.hypixel.hytale.server.spawning.blockstates.SpawnMarkerBlockStateSystems;
import com.hypixel.hytale.server.spawning.world.system.ChunkSpawningSystems;
import com.hypixel.hytale.server.spawning.world.system.WorldSpawnJobSystems;
import com.hypixel.hytale.server.spawning.world.system.WorldSpawningSystem;
import com.hypixel.hytale.server.spawning.world.system.MoonPhaseChangeEventSystem;
import com.hypixel.hytale.server.spawning.world.system.WorldSpawnTrackingSystem;
import com.hypixel.hytale.server.npc.systems.SpawnReferenceSystems;
import com.hypixel.hytale.server.spawning.spawnmarkers.SpawnMarkerSystems;
import com.hypixel.hytale.server.spawning.beacons.SpawnBeaconSystems;
import com.hypixel.hytale.server.spawning.local.LocalSpawnForceTriggerSystem;
import com.hypixel.hytale.server.spawning.local.LocalSpawnBeaconSystem;
import com.hypixel.hytale.server.spawning.local.LocalSpawnControllerSystem;
import com.hypixel.hytale.builtin.weather.components.WeatherTracker;
import com.hypixel.hytale.server.spawning.local.LocalSpawnSetupSystem;
import com.hypixel.hytale.server.spawning.suppression.system.ChunkSuppressionSystems;
import com.hypixel.hytale.server.spawning.suppression.system.SpawnMarkerSuppressionSystem;
import com.hypixel.hytale.server.spawning.systems.BeaconSpatialSystem;
import com.hypixel.hytale.server.spawning.systems.SpawnMarkerSpatialSystem;
import com.hypixel.hytale.server.spawning.systems.LegacyBeaconSpatialSystem;
import com.hypixel.hytale.component.system.ISystem;
import com.hypixel.hytale.server.spawning.suppression.system.SpawnSuppressionSystems;
import com.hypixel.hytale.server.core.modules.entity.EntityModule;
import com.hypixel.hytale.component.spatial.KDTree;
import com.hypixel.hytale.codec.Codec;
import com.hypixel.hytale.server.spawning.blockstates.SpawnMarkerBlockState;
import com.hypixel.hytale.server.core.universe.world.meta.BlockStateModule;
import com.hypixel.hytale.server.npc.asset.builder.Builder;
import java.util.function.Supplier;
import com.hypixel.hytale.server.spawning.corecomponents.builders.BuilderActionTriggerSpawnBeacon;
import com.hypixel.hytale.server.npc.NPCPlugin;
import com.hypixel.hytale.builtin.tagset.config.NPCGroup;
import com.hypixel.hytale.assetstore.map.IndexedAssetMap;
import com.hypixel.hytale.server.core.asset.type.responsecurve.config.ResponseCurve;
import com.hypixel.hytale.server.spawning.assets.spawnsuppression.SpawnSuppression;
import com.hypixel.hytale.server.core.asset.type.blockset.config.BlockSet;
import com.hypixel.hytale.assetstore.map.IndexedLookupTableAssetMap;
import com.hypixel.hytale.assetstore.AssetRegistry;
import com.hypixel.hytale.server.core.modules.interaction.interaction.config.Interaction;
import com.hypixel.hytale.server.flock.config.FlockAsset;
import java.util.function.Function;
import com.hypixel.hytale.assetstore.codec.AssetCodec;
import com.hypixel.hytale.assetstore.map.DefaultAssetMap;
import com.hypixel.hytale.assetstore.AssetStore;
import com.hypixel.hytale.server.core.asset.HytaleAssetStore;
import com.hypixel.hytale.server.spawning.beacons.SpawnBeacon;
import com.hypixel.hytale.codec.DirectDecodeCodec;
import com.hypixel.hytale.server.spawning.beacons.LegacySpawnBeaconEntity;
import com.hypixel.hytale.server.core.asset.LoadAssetEvent;
import com.hypixel.hytale.server.core.asset.type.model.config.ModelAsset;
import com.hypixel.hytale.server.spawning.assets.spawns.config.BeaconNPCSpawn;
import com.hypixel.hytale.server.spawning.assets.spawns.config.WorldNPCSpawn;
import com.hypixel.hytale.assetstore.event.RemovedAssetsEvent;
import com.hypixel.hytale.server.spawning.assets.spawnmarker.config.SpawnMarker;
import com.hypixel.hytale.server.npc.AllNPCsLoadedEvent;
import com.hypixel.hytale.server.core.asset.type.environment.config.Environment;
import com.hypixel.hytale.assetstore.event.LoadedAssetsEvent;
import com.hypixel.hytale.server.core.command.system.AbstractCommand;
import com.hypixel.hytale.server.spawning.commands.SpawnCommand;
import javax.annotation.Nonnull;
import com.hypixel.hytale.server.core.plugin.JavaPluginInit;
import com.hypixel.hytale.server.spawning.blockstates.SpawnMarkerBlockReference;
import com.hypixel.hytale.server.spawning.util.FloodFillEntryPoolProviderSimple;
import com.hypixel.hytale.server.spawning.util.FloodFillPositionSelector;
import com.hypixel.hytale.server.npc.components.SpawnBeaconReference;
import com.hypixel.hytale.server.npc.components.SpawnMarkerReference;
import com.hypixel.hytale.server.spawning.beacons.InitialBeaconDelay;
import com.hypixel.hytale.server.spawning.suppression.component.ChunkSuppressionEntry;
import com.hypixel.hytale.server.spawning.suppression.component.ChunkSuppressionQueue;
import com.hypixel.hytale.server.spawning.world.component.ChunkSpawnedNPCData;
import com.hypixel.hytale.server.spawning.world.component.ChunkSpawnData;
import com.hypixel.hytale.server.spawning.world.component.SpawnJobData;
import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore;
import com.hypixel.hytale.server.spawning.local.LocalSpawnState;
import com.hypixel.hytale.server.spawning.local.LocalSpawnBeacon;
import com.hypixel.hytale.server.spawning.suppression.component.SpawnSuppressionController;
import com.hypixel.hytale.server.spawning.suppression.component.SpawnSuppressionComponent;
import com.hypixel.hytale.server.spawning.world.component.WorldSpawnData;
import com.hypixel.hytale.server.spawning.local.LocalSpawnController;
import com.hypixel.hytale.server.spawning.spawnmarkers.SpawnMarkerEntity;
import com.hypixel.hytale.component.ComponentType;
import com.hypixel.hytale.component.Ref;
import com.hypixel.hytale.component.spatial.SpatialResource;
import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
import com.hypixel.hytale.component.ResourceType;
import com.hypixel.hytale.server.core.util.Config;
import com.hypixel.hytale.server.spawning.managers.BeaconSpawnManager;
import com.hypixel.hytale.server.spawning.world.manager.WorldSpawnManager;
import com.hypixel.hytale.server.core.asset.type.model.config.Model;
import com.hypixel.hytale.server.core.plugin.JavaPlugin;

public class SpawningPlugin extends JavaPlugin
{
    private static final String DEFAULT_SPAWN_MARKER_MODEL = "NPC_Spawn_Marker";
    private static final int TICK_COLUMN_BUDGET = 20480;
    private static final float OVERPOPULATION_RATIO = 0.25f;
    private static final int OVERPOPULATION_GROUP_BUFFER = 4;
    private static SpawningPlugin instance;
    private Model spawnMarkerModel;
    private double localSpawnControllerJoinDelay;
    private int tickColumnBudget;
    private final WorldSpawnManager worldSpawnManager;
    private final BeaconSpawnManager beaconSpawnManager;
    private final Config<NPCSpawningConfig> config;
    private ResourceType<EntityStore, SpatialResource<Ref<EntityStore>, EntityStore>> legacyBeaconSpatialResource;
    private ResourceType<EntityStore, SpatialResource<Ref<EntityStore>, EntityStore>> spawnMarkerSpatialResource;
    private ResourceType<EntityStore, SpatialResource<Ref<EntityStore>, EntityStore>> manualSpawnBeaconSpatialResource;
    private ComponentType<EntityStore, SpawnMarkerEntity> spawnMarkerComponentType;
    private ComponentType<EntityStore, LocalSpawnController> localSpawnControllerComponentType;
    private ResourceType<EntityStore, WorldSpawnData> worldSpawnDataResourceType;
    private ComponentType<EntityStore, SpawnSuppressionComponent> spawnSuppressorComponentType;
    private ResourceType<EntityStore, SpawnSuppressionController> spawnSuppressionControllerResourceType;
    private ComponentType<EntityStore, LocalSpawnBeacon> localSpawnBeaconComponentType;
    private ResourceType<EntityStore, LocalSpawnState> localSpawnStateResourceType;
    private ComponentType<ChunkStore, SpawnJobData> spawnJobDataComponentType;
    private ComponentType<ChunkStore, ChunkSpawnData> chunkSpawnDataComponentType;
    private ComponentType<ChunkStore, ChunkSpawnedNPCData> chunkSpawnedNPCDataComponentType;
    private ResourceType<ChunkStore, ChunkSuppressionQueue> chunkSuppressionQueueResourceType;
    private ComponentType<ChunkStore, ChunkSuppressionEntry> chunkSuppressionEntryComponentType;
    private ComponentType<EntityStore, InitialBeaconDelay> initialBeaconDelayComponentType;
    private ComponentType<EntityStore, SpawnMarkerReference> spawnMarkerReferenceComponentType;
    private ComponentType<EntityStore, SpawnBeaconReference> spawnBeaconReferenceComponentType;
    private ComponentType<EntityStore, FloodFillPositionSelector> floodFillPositionSelectorComponentType;
    private ResourceType<EntityStore, FloodFillEntryPoolProviderSimple> floodFillEntryPoolProviderSimpleResourceType;
    private ComponentType<EntityStore, SpawnMarkerBlockReference> spawnMarkerBlockReferenceComponentType;
    static final /* synthetic */ boolean $assertionsDisabled;
    
    public static SpawningPlugin get() {
        return SpawningPlugin.instance;
    }
    
    public SpawningPlugin(@Nonnull final JavaPluginInit init) {
        super(init);
        this.worldSpawnManager = new WorldSpawnManager();
        this.beaconSpawnManager = new BeaconSpawnManager();
        this.config = this.withConfig("SpawningModule", NPCSpawningConfig.CODEC);
    }
    
    public void setup() {
        SpawningPlugin.instance = this;
        this.getCommandRegistry().registerCommand(new SpawnCommand());
        this.getEventRegistry().register(LoadedAssetsEvent.class, Environment.class, this::onEnvironmentChange);
        this.getEventRegistry().register(AllNPCsLoadedEvent.class, this::onLoadedNPCEvent);
        this.getEventRegistry().register(LoadedAssetsEvent.class, SpawnMarker.class, this::onSpawnMarkersChange);
        this.getEventRegistry().register(RemovedAssetsEvent.class, SpawnMarker.class, SpawningPlugin::onSpawnMarkersRemove);
        this.getEventRegistry().register(LoadedAssetsEvent.class, WorldNPCSpawn.class, this::onWorldNPCSpawnsLoaded);
        this.getEventRegistry().register(LoadedAssetsEvent.class, BeaconNPCSpawn.class, this::onBeaconNPCSpawnsLoaded);
        this.getEventRegistry().register(RemovedAssetsEvent.class, WorldNPCSpawn.class, this::onWorldNPCSpawnsRemoved);
        this.getEventRegistry().register(RemovedAssetsEvent.class, BeaconNPCSpawn.class, this::onBeaconNPCSpawnsRemoved);
        this.getEventRegistry().register(LoadedAssetsEvent.class, ModelAsset.class, this::onModelAssetChange);
        this.getEventRegistry().register((short)(-7), LoadAssetEvent.class, this::onLoadAsset);
        this.getEntityRegistry().registerEntity("LegacySpawnBeacon", LegacySpawnBeaconEntity.class, LegacySpawnBeaconEntity::new, LegacySpawnBeaconEntity.CODEC);
        this.getEntityRegistry().registerEntity("SpawnBeacon", SpawnBeacon.class, SpawnBeacon::new, SpawnBeacon.CODEC);
        AssetRegistry.register(((HytaleAssetStore.Builder)((HytaleAssetStore.Builder)((HytaleAssetStore.Builder)HytaleAssetStore.builder(SpawnMarker.class, new DefaultAssetMap()).setPath()).setCodec(SpawnMarker.CODEC)).setKeyFunction(SpawnMarker::getId).loadsAfter(FlockAsset.class, ModelAsset.class).loadsBefore(Interaction.class)).build());
        AssetRegistry.register(((HytaleAssetStore.Builder)((HytaleAssetStore.Builder)((HytaleAssetStore.Builder)((HytaleAssetStore.Builder)HytaleAssetStore.builder(WorldNPCSpawn.class, new IndexedLookupTableAssetMap(WorldNPCSpawn[]::new)).setPath()).setCodec(WorldNPCSpawn.CODEC)).setKeyFunction(WorldNPCSpawn::getId)).setReplaceOnRemove(WorldNPCSpawn::new).loadsAfter(Environment.class, BlockSet.class, FlockAsset.class)).build());
        AssetRegistry.register(((HytaleAssetStore.Builder)((HytaleAssetStore.Builder)((HytaleAssetStore.Builder)((HytaleAssetStore.Builder)HytaleAssetStore.builder(BeaconNPCSpawn.class, new IndexedLookupTableAssetMap(BeaconNPCSpawn[]::new)).setPath()).setCodec(BeaconNPCSpawn.CODEC)).setKeyFunction(BeaconNPCSpawn::getId)).setReplaceOnRemove(BeaconNPCSpawn::new).loadsAfter(Environment.class, BlockSet.class, SpawnSuppression.class, FlockAsset.class, ModelAsset.class, ResponseCurve.class)).build());
        AssetRegistry.register(((HytaleAssetStore.Builder)((HytaleAssetStore.Builder)((HytaleAssetStore.Builder)((HytaleAssetStore.Builder)HytaleAssetStore.builder(SpawnSuppression.class, new IndexedAssetMap()).setPath()).setCodec(SpawnSuppression.CODEC)).setKeyFunction(SpawnSuppression::getId)).setReplaceOnRemove(SpawnSuppression::new).loadsAfter(NPCGroup.class)).build());
        NPCPlugin.get().registerCoreComponentType("TriggerSpawnBeacon", (Supplier<Builder<Object>>)BuilderActionTriggerSpawnBeacon::new);
        BlockStateModule.get().registerBlockState(SpawnMarkerBlockState.class, "SpawnMarkerBlock", SpawnMarkerBlockState.CODEC, SpawnMarkerBlockState.Data.class, SpawnMarkerBlockState.Data.CODEC);
        this.spawnMarkerComponentType = this.getEntityStoreRegistry().registerComponent(SpawnMarkerEntity.class, "SpawnMarkerComponent", SpawnMarkerEntity.CODEC);
        this.localSpawnControllerComponentType = this.getEntityStoreRegistry().registerComponent(LocalSpawnController.class, LocalSpawnController::new);
        this.worldSpawnDataResourceType = this.getEntityStoreRegistry().registerResource(WorldSpawnData.class, WorldSpawnData::new);
        this.localSpawnBeaconComponentType = this.getEntityStoreRegistry().registerComponent(LocalSpawnBeacon.class, "LocalSpawnBeacon", LocalSpawnBeacon.CODEC);
        this.localSpawnStateResourceType = this.getEntityStoreRegistry().registerResource(LocalSpawnState.class, LocalSpawnState::new);
        this.legacyBeaconSpatialResource = this.getEntityStoreRegistry().registerSpatialResource(() -> new KDTree(Ref::isValid));
        this.spawnMarkerSpatialResource = this.getEntityStoreRegistry().registerSpatialResource(() -> new KDTree(Ref::isValid));
        this.manualSpawnBeaconSpatialResource = this.getEntityStoreRegistry().registerSpatialResource(() -> new KDTree(Ref::isValid));
        this.spawnSuppressorComponentType = this.getEntityStoreRegistry().registerComponent(SpawnSuppressionComponent.class, "SpawnSuppression", SpawnSuppressionComponent.CODEC);
        this.spawnSuppressionControllerResourceType = this.getEntityStoreRegistry().registerResource(SpawnSuppressionController.class, "SpawnSuppressionController", SpawnSuppressionController.CODEC);
        this.initialBeaconDelayComponentType = this.getEntityStoreRegistry().registerComponent(InitialBeaconDelay.class, InitialBeaconDelay::new);
        this.spawnMarkerReferenceComponentType = this.getEntityStoreRegistry().registerComponent(SpawnMarkerReference.class, "SpawnMarkerReference", SpawnMarkerReference.CODEC);
        this.spawnBeaconReferenceComponentType = this.getEntityStoreRegistry().registerComponent(SpawnBeaconReference.class, "SpawnBeaconReference", SpawnBeaconReference.CODEC);
        this.floodFillPositionSelectorComponentType = this.getEntityStoreRegistry().registerComponent(FloodFillPositionSelector.class, () -> {
            throw new UnsupportedOperationException("Not implemented");
        });
        this.floodFillEntryPoolProviderSimpleResourceType = this.getEntityStoreRegistry().registerResource(FloodFillEntryPoolProviderSimple.class, FloodFillEntryPoolProviderSimple::new);
        this.spawnMarkerBlockReferenceComponentType = this.getEntityStoreRegistry().registerComponent(SpawnMarkerBlockReference.class, "SpawnMarkerBlockReference", SpawnMarkerBlockReference.CODEC);
        this.spawnJobDataComponentType = this.getChunkStoreRegistry().registerComponent(SpawnJobData.class, SpawnJobData::new);
        this.chunkSpawnDataComponentType = this.getChunkStoreRegistry().registerComponent(ChunkSpawnData.class, ChunkSpawnData::new);
        this.chunkSpawnedNPCDataComponentType = this.getChunkStoreRegistry().registerComponent(ChunkSpawnedNPCData.class, "ChunkSpawnedNPCData", ChunkSpawnedNPCData.CODEC);
        this.chunkSuppressionQueueResourceType = this.getChunkStoreRegistry().registerResource(ChunkSuppressionQueue.class, ChunkSuppressionQueue::new);
        this.chunkSuppressionEntryComponentType = this.getChunkStoreRegistry().registerComponent(ChunkSuppressionEntry.class, () -> {
            throw new UnsupportedOperationException("Not implemented");
        });
        final EntityModule entityModule = EntityModule.get();
        final ComponentType<EntityStore, Player> playerComponentType = entityModule.getPlayerComponentType();
        final ComponentType<EntityStore, TransformComponent> transformComponentType = entityModule.getTransformComponentType();
        final ComponentType<EntityStore, LegacySpawnBeaconEntity> legacySpawnBeaconComponentType = entityModule.getComponentType(LegacySpawnBeaconEntity.class);
        final ComponentType<EntityStore, SpawnBeacon> spawnBeaconComponentType = entityModule.getComponentType(SpawnBeacon.class);
        final ResourceType<EntityStore, SpatialResource<Ref<EntityStore>, EntityStore>> playerSpatialComponent = entityModule.getPlayerSpatialResourceType();
        this.getEntityStoreRegistry().registerSystem(new SpawnSuppressionSystems.EnsureNetworkSendable());
        this.getEntityStoreRegistry().registerSystem(new SpawnSuppressionSystems.Load(this.spawnSuppressionControllerResourceType, this.spawnMarkerComponentType, this.chunkSuppressionQueueResourceType, this.chunkSuppressionEntryComponentType));
        this.getEntityStoreRegistry().registerSystem(new SpawnSuppressionSystems.Suppressor(this.spawnSuppressorComponentType, this.spawnSuppressionControllerResourceType, this.spawnMarkerComponentType, this.chunkSuppressionQueueResourceType, this.spawnMarkerSpatialResource));
        this.getEntityStoreRegistry().registerSystem(new LegacyBeaconSpatialSystem(this.legacyBeaconSpatialResource));
        this.getEntityStoreRegistry().registerSystem(new SpawnMarkerSpatialSystem(this.spawnMarkerSpatialResource));
        this.getEntityStoreRegistry().registerSystem(new BeaconSpatialSystem(this.manualSpawnBeaconSpatialResource));
        this.getEntityStoreRegistry().registerSystem(new SpawnMarkerSuppressionSystem(this.spawnMarkerComponentType, this.spawnSuppressionControllerResourceType));
        this.getChunkStoreRegistry().registerSystem(new ChunkSuppressionSystems.ChunkAdded(this.chunkSuppressionEntryComponentType, this.spawnSuppressionControllerResourceType));
        this.getChunkStoreRegistry().registerSystem(new ChunkSuppressionSystems.Ticking(this.chunkSuppressionEntryComponentType, this.chunkSuppressionQueueResourceType));
        this.getEntityStoreRegistry().registerSystem(new LocalSpawnSetupSystem(playerComponentType));
        this.getEntityStoreRegistry().registerSystem(new LocalSpawnControllerSystem(this.localSpawnControllerComponentType, transformComponentType, WeatherTracker.getComponentType(), this.localSpawnBeaconComponentType, legacySpawnBeaconComponentType, this.localSpawnStateResourceType, this.legacyBeaconSpatialResource));
        this.getEntityStoreRegistry().registerSystem(new LocalSpawnBeaconSystem(this.localSpawnBeaconComponentType, this.localSpawnStateResourceType));
        this.getEntityStoreRegistry().registerSystem(new LocalSpawnForceTriggerSystem(this.localSpawnControllerComponentType, this.localSpawnStateResourceType));
        this.getEntityStoreRegistry().registerSystem(new SpawnBeaconSystems.LegacyEntityAdded(legacySpawnBeaconComponentType));
        this.getEntityStoreRegistry().registerSystem(new SpawnBeaconSystems.EntityAdded(spawnBeaconComponentType));
        this.getEntityStoreRegistry().registerSystem(new SpawnBeaconSystems.CheckDespawn(legacySpawnBeaconComponentType, this.initialBeaconDelayComponentType));
        this.getEntityStoreRegistry().registerSystem(new SpawnBeaconSystems.PositionSelectorUpdate(this.floodFillPositionSelectorComponentType, this.floodFillEntryPoolProviderSimpleResourceType));
        this.getEntityStoreRegistry().registerSystem(new SpawnBeaconSystems.ControllerTick(legacySpawnBeaconComponentType, this.floodFillPositionSelectorComponentType, this.initialBeaconDelayComponentType));
        this.getEntityStoreRegistry().registerSystem(new SpawnBeaconSystems.SpawnJobTick(legacySpawnBeaconComponentType, this.initialBeaconDelayComponentType));
        this.getEntityStoreRegistry().registerSystem(new SpawnBeaconSystems.LoadTimeDelay(this.initialBeaconDelayComponentType));
        this.getEntityStoreRegistry().registerSystem(new SpawnMarkerSystems.LegacyEntityMigration());
        this.getEntityStoreRegistry().registerSystem(new SpawnMarkerSystems.EnsureNetworkSendable());
        this.getEntityStoreRegistry().registerSystem(new SpawnMarkerSystems.CacheMarker(this.spawnMarkerComponentType));
        this.getEntityStoreRegistry().registerSystem(new SpawnMarkerSystems.EntityAdded(this.spawnMarkerComponentType));
        this.getEntityStoreRegistry().registerSystem(new SpawnMarkerSystems.EntityAddedFromExternal(this.spawnMarkerComponentType));
        this.getEntityStoreRegistry().registerSystem(new SpawnMarkerSystems.AddedFromWorldGen());
        this.getEntityStoreRegistry().registerSystem(new SpawnMarkerSystems.Ticking(this.spawnMarkerComponentType, playerSpatialComponent));
        this.getEntityStoreRegistry().registerSystem(new SpawnReferenceSystems.MarkerAddRemoveSystem(SpawnMarkerReference.getComponentType(), this.spawnMarkerComponentType));
        this.getEntityStoreRegistry().registerSystem(new SpawnReferenceSystems.BeaconAddRemoveSystem(SpawnBeaconReference.getComponentType(), legacySpawnBeaconComponentType));
        this.getEntityStoreRegistry().registerSystem(new WorldSpawnTrackingSystem(this.worldSpawnDataResourceType, this.chunkSpawnDataComponentType, this.chunkSpawnedNPCDataComponentType));
        this.getEntityStoreRegistry().registerSystem(new MoonPhaseChangeEventSystem());
        this.getEntityStoreRegistry().registerSystem(new SpawnReferenceSystems.TickingSpawnMarkerSystem(this.spawnMarkerReferenceComponentType, this.spawnMarkerComponentType));
        this.getEntityStoreRegistry().registerSystem(new SpawnReferenceSystems.TickingSpawnBeaconSystem(this.spawnBeaconReferenceComponentType));
        this.getChunkStoreRegistry().registerSystem(new WorldSpawningSystem(this.worldSpawnDataResourceType, this.chunkSpawnDataComponentType, this.chunkSpawnedNPCDataComponentType, this.spawnJobDataComponentType));
        this.getChunkStoreRegistry().registerSystem(new WorldSpawnJobSystems.EntityRemoved(this.worldSpawnDataResourceType, this.spawnJobDataComponentType));
        this.getChunkStoreRegistry().registerSystem(new WorldSpawnJobSystems.Ticking(this.worldSpawnDataResourceType, this.spawnSuppressionControllerResourceType, this.spawnJobDataComponentType, this.chunkSpawnDataComponentType, this.chunkSpawnedNPCDataComponentType));
        this.getChunkStoreRegistry().registerSystem((ISystem<ChunkStore>)new WorldSpawnJobSystems.TickingState(this.worldSpawnDataResourceType, this.spawnJobDataComponentType));
        this.getChunkStoreRegistry().registerSystem(new ChunkSpawningSystems.ChunkRefAdded(this.worldSpawnDataResourceType, this.chunkSpawnDataComponentType, this.chunkSpawnedNPCDataComponentType));
        this.getChunkStoreRegistry().registerSystem((ISystem<ChunkStore>)new ChunkSpawningSystems.TickingState(this.worldSpawnDataResourceType, this.chunkSpawnDataComponentType, this.chunkSpawnedNPCDataComponentType));
        final ComponentType<ChunkStore, SpawnMarkerBlockState> spawnMarkerBlockStateComponentType = BlockStateModule.get().getComponentType(SpawnMarkerBlockState.class);
        this.getChunkStoreRegistry().registerSystem(new SpawnMarkerBlockStateSystems.AddOrRemove(spawnMarkerBlockStateComponentType));
        this.getChunkStoreRegistry().registerSystem(new SpawnMarkerBlockStateSystems.TickHeartbeat(spawnMarkerBlockStateComponentType));
        this.getEntityStoreRegistry().registerSystem(new SpawnMarkerBlockStateSystems.SpawnMarkerAddedFromExternal(this.spawnMarkerBlockReferenceComponentType));
        this.getEntityStoreRegistry().registerSystem(new SpawnMarkerBlockStateSystems.SpawnMarkerTickHeartbeat(this.spawnMarkerBlockReferenceComponentType));
        this.getEntityStoreRegistry().registerSystem(new EntityModule.HiddenFromPlayerMigrationSystem(this.spawnSuppressorComponentType), true);
        this.getEntityStoreRegistry().registerSystem(new LegacySpawnSuppressorEntityMigration());
        Interaction.CODEC.register("TriggerSpawnMarkers", TriggerSpawnMarkersInteraction.class, TriggerSpawnMarkersInteraction.CODEC);
    }
    
    public void start() {
        final NPCSpawningConfig config = this.config.get();
        final String spawnMarkerModelId = config.defaultMarkerModel;
        this.localSpawnControllerJoinDelay = config.localSpawnControllerJoinDelay;
        this.tickColumnBudget = MathUtil.floor(config.spawnBudgetFactor * 20480.0);
        final DefaultAssetMap<String, ModelAsset> modelAssetMap = ModelAsset.getAssetMap();
        ModelAsset modelAsset = modelAssetMap.getAsset(spawnMarkerModelId);
        if (modelAsset == null) {
            this.getLogger().at(Level.SEVERE).log("Spawn marker model %s does not exist");
            modelAsset = modelAssetMap.getAsset("NPC_Spawn_Marker");
            if (modelAsset == null) {
                throw new IllegalStateException(String.format("Default spawn marker '%s' not found", "NPC_Spawn_Marker"));
            }
        }
        this.spawnMarkerModel = Model.createUnitScaleModel(modelAsset);
        this.setUpWithAllRoles();
    }
    
    public void shutdown() {
    }
    
    public ResourceType<EntityStore, SpatialResource<Ref<EntityStore>, EntityStore>> getSpawnMarkerSpatialResource() {
        return this.spawnMarkerSpatialResource;
    }
    
    public ResourceType<EntityStore, SpatialResource<Ref<EntityStore>, EntityStore>> getManualSpawnBeaconSpatialResource() {
        return this.manualSpawnBeaconSpatialResource;
    }
    
    public ComponentType<EntityStore, SpawnMarkerEntity> getSpawnMarkerComponentType() {
        return this.spawnMarkerComponentType;
    }
    
    public ComponentType<EntityStore, LocalSpawnController> getLocalSpawnControllerComponentType() {
        return this.localSpawnControllerComponentType;
    }
    
    public ResourceType<EntityStore, WorldSpawnData> getWorldSpawnDataResourceType() {
        return this.worldSpawnDataResourceType;
    }
    
    public ComponentType<EntityStore, SpawnSuppressionComponent> getSpawnSuppressorComponentType() {
        return this.spawnSuppressorComponentType;
    }
    
    public ResourceType<EntityStore, SpawnSuppressionController> getSpawnSuppressionControllerResourceType() {
        return this.spawnSuppressionControllerResourceType;
    }
    
    public ComponentType<EntityStore, LocalSpawnBeacon> getLocalSpawnBeaconComponentType() {
        return this.localSpawnBeaconComponentType;
    }
    
    public ResourceType<EntityStore, LocalSpawnState> getLocalSpawnStateResourceType() {
        return this.localSpawnStateResourceType;
    }
    
    public ComponentType<EntityStore, InitialBeaconDelay> getInitialBeaconDelayComponentType() {
        return this.initialBeaconDelayComponentType;
    }
    
    public ComponentType<ChunkStore, SpawnJobData> getSpawnJobDataComponentType() {
        return this.spawnJobDataComponentType;
    }
    
    public ComponentType<ChunkStore, ChunkSpawnData> getChunkSpawnDataComponentType() {
        return this.chunkSpawnDataComponentType;
    }
    
    public ComponentType<ChunkStore, ChunkSpawnedNPCData> getChunkSpawnedNPCDataComponentType() {
        return this.chunkSpawnedNPCDataComponentType;
    }
    
    public ResourceType<ChunkStore, ChunkSuppressionQueue> getChunkSuppressionQueueResourceType() {
        return this.chunkSuppressionQueueResourceType;
    }
    
    public ResourceType<EntityStore, FloodFillEntryPoolProviderSimple> getFloodFillEntryPoolProviderSimpleResourceType() {
        return this.floodFillEntryPoolProviderSimpleResourceType;
    }
    
    public ComponentType<ChunkStore, ChunkSuppressionEntry> getChunkSuppressionEntryComponentType() {
        return this.chunkSuppressionEntryComponentType;
    }
    
    public BeaconSpawnWrapper getBeaconSpawnWrapper(final int configId) {
        return ((SpawnManager<BeaconSpawnWrapper, U>)this.beaconSpawnManager).getSpawnWrapper(configId);
    }
    
    public ComponentType<EntityStore, SpawnMarkerReference> getSpawnMarkerReferenceComponentType() {
        return this.spawnMarkerReferenceComponentType;
    }
    
    public ComponentType<EntityStore, SpawnBeaconReference> getSpawnBeaconReferenceComponentType() {
        return this.spawnBeaconReferenceComponentType;
    }
    
    public ComponentType<EntityStore, FloodFillPositionSelector> getFloodFillPositionSelectorComponentType() {
        return this.floodFillPositionSelectorComponentType;
    }
    
    public ComponentType<EntityStore, SpawnMarkerBlockReference> getSpawnMarkerBlockReferenceComponentType() {
        return this.spawnMarkerBlockReferenceComponentType;
    }
    
    public boolean shouldNPCDespawn(@Nonnull final Store<EntityStore> store, @Nonnull final NPCEntity npcComponent, @Nonnull final WorldTimeResource timeManager, final int configuration, final boolean beaconSpawn) {
        if (configuration == Integer.MIN_VALUE) {
            return false;
        }
        final SpawnManager manager = (SpawnManager)(beaconSpawn ? this.beaconSpawnManager : this.worldSpawnManager);
        final SpawnWrapper wrapper = manager.getSpawnWrapper(configuration);
        if (wrapper == null) {
            return false;
        }
        if (!beaconSpawn) {
            final int environment = npcComponent.getEnvironment();
            if (environment != Integer.MIN_VALUE) {
                final WorldSpawnData worldSpawnData = store.getResource(WorldSpawnData.getResourceType());
                final WorldEnvironmentSpawnData environmentSpawnData = worldSpawnData.getWorldEnvironmentSpawnData(environment);
                if (environmentSpawnData != null) {
                    final WorldNPCSpawnStat npcSpawnData = environmentSpawnData.getNpcStatMap().get(npcComponent.getRoleIndex());
                    if (npcSpawnData != null && npcSpawnData.getActual() > npcSpawnData.getExpected() * 1.25 + 4.0) {
                        get().getLogger().at(Level.WARNING).log("Removing NPC of type %s due to overpopulation (expected: %f, actual: %d)", npcComponent.getRoleName(), npcSpawnData.getExpected(), npcSpawnData.getActual());
                        return true;
                    }
                }
            }
        }
        final World world = store.getExternalData().getWorld();
        return wrapper.shouldDespawn(world, timeManager);
    }
    
    public Model getSpawnMarkerModel() {
        return this.spawnMarkerModel;
    }
    
    public EnvironmentSpawnParameters getWorldEnvironmentSpawnParameters(final int environmentIndex) {
        return this.worldSpawnManager.getEnvironmentSpawnParameters(environmentIndex);
    }
    
    public List<BeaconSpawnWrapper> getBeaconSpawnsForEnvironment(final int environmentIndex) {
        return this.beaconSpawnManager.getBeaconSpawns(environmentIndex);
    }
    
    public IntSet getRolesForEnvironment(final int environment) {
        return this.worldSpawnManager.getRolesForEnvironment(environment);
    }
    
    public int getTickColumnBudget() {
        return this.tickColumnBudget;
    }
    
    public int getMaxActiveJobs() {
        return this.config.get().maxActiveJobs;
    }
    
    public double getLocalSpawnControllerJoinDelay() {
        return this.localSpawnControllerJoinDelay;
    }
    
    public static <T extends NPCSpawn> void validateSpawnsConfigurations(final String type, @Nonnull final Map<String, T> spawns, @Nonnull final List<String> errors) {
        for (Map.Entry<String, T> spawn : spawns.entrySet()) {
            final RoleSpawnParameters[] npCs;
            final RoleSpawnParameters[] npcs = npCs = spawn.getValue().getNPCs();
            for (int length = npCs.length, i = 0; i < length; ++i) {
                final RoleSpawnParameters npc = npCs[i];
                try {
                    NPCPlugin.get().validateSpawnableRole(npc.getId());
                }
                catch (final IllegalArgumentException e) {
                    errors.add(type + " " + (String)spawn.getKey() + ": " + e.getMessage());
                }
            }
        }
    }
    
    public static void validateSpawnMarkers(@Nonnull final Map<String, SpawnMarker> markers, @Nonnull final List<String> errors) {
        for (Map.Entry<String, SpawnMarker> marker : markers.entrySet()) {
            final IWeightedMap<SpawnMarker.SpawnConfiguration> npcs = marker.getValue().getWeightedConfigurations();
            npcs.forEach(config -> {
                try {
                    final String npc = config.getNpc();
                    if (npc != null) {
                        NPCPlugin.get().validateSpawnableRole(npc);
                    }
                }
                catch (final IllegalArgumentException e) {
                    errors.add("Spawn marker " + (String)marker.getKey() + ": " + e.getMessage());
                }
            });
        }
    }
    
    public double getEnvironmentDensity(final int environmentIndex) {
        final EnvironmentSpawnParameters environment = this.getWorldEnvironmentSpawnParameters(environmentIndex);
        return (environment != null) ? environment.getSpawnDensity() : 0.0;
    }
    
    protected void onSpawnMarkersChange(@Nonnull final LoadedAssetsEvent<String, SpawnMarker, DefaultAssetMap<String, SpawnMarker>> event) {
        final Map<String, SpawnMarker> loadedAssets = event.getLoadedAssets();
        Universe.get().getWorlds().forEach((name, world) -> world.execute(() -> world.getEntityStore().getStore().forEachChunk(SpawnMarkerEntity.getComponentType(), (archetypeChunk, commandBuffer) -> {
            for (int index = 0; index < archetypeChunk.size(); ++index) {
                final SpawnMarkerEntity spawnMarkerEntity = archetypeChunk.getComponent(index, SpawnMarkerEntity.getComponentType());
                if (!(!loadedAssets.containsKey(spawnMarkerEntity.getSpawnMarkerId()))) {
                    final Holder<EntityStore> holder = commandBuffer.removeEntity(archetypeChunk.getReferenceTo(index), EntityStore.REGISTRY.newHolder(), RemoveReason.UNLOAD);
                    commandBuffer.addEntity(holder, AddReason.LOAD);
                }
            }
        })));
        if (NPCPlugin.get().getBuilderManager().isEmpty()) {
            return;
        }
        final ObjectArrayList<String> errors = new ObjectArrayList<String>();
        validateSpawnMarkers(event.getLoadedAssets(), errors);
        for (final String error : errors) {
            this.getLogger().at(Level.SEVERE).log(error);
        }
    }
    
    protected static void onSpawnMarkersRemove(@Nonnull final RemovedAssetsEvent<String, SpawnMarker, DefaultAssetMap<String, SpawnMarker>> event) {
        final Set<String> removedAssets = event.getRemovedAssets();
        Universe.get().getWorlds().forEach((name, world) -> world.execute(() -> world.getEntityStore().getStore().forEachChunk(SpawnMarkerEntity.getComponentType(), (archetypeChunk, commandBuffer) -> {
            for (int index = 0; index < archetypeChunk.size(); ++index) {
                final SpawnMarkerEntity spawnMarkerEntity = archetypeChunk.getComponent(index, SpawnMarkerEntity.getComponentType());
                if (!(!removedAssets.contains(spawnMarkerEntity.getSpawnMarkerId()))) {
                    commandBuffer.removeEntity(archetypeChunk.getReferenceTo(index), RemoveReason.REMOVE);
                }
            }
        })));
    }
    
    private void onEnvironmentChange(@Nonnull final LoadedAssetsEvent<String, Environment, IndexedLookupTableAssetMap<String, Environment>> event) {
        final IndexedLookupTableAssetMap<String, Environment> environmentAssetMap = Environment.getAssetMap();
        for (final Map.Entry<String, Environment> entry : event.getLoadedAssets().entrySet()) {
            final String environment = entry.getKey();
            final int index = environmentAssetMap.getIndex(environment);
            if (index == Integer.MIN_VALUE) {
                throw new IllegalArgumentException("Unknown key! " + environment);
            }
            this.worldSpawnManager.updateSpawnParameters(index, entry.getValue());
        }
        WorldSpawnManager.onEnvironmentChanged();
    }
    
    private void onWorldNPCSpawnsLoaded(@Nonnull final LoadedAssetsEvent<String, WorldNPCSpawn, IndexedLookupTableAssetMap<String, WorldNPCSpawn>> event) {
        if (NPCPlugin.get().getBuilderManager().isEmpty()) {
            return;
        }
        final IntOpenHashSet changeSet = new IntOpenHashSet();
        for (final String config : event.getLoadedAssets().keySet()) {
            final int index = event.getAssetMap().getIndex((Object)config);
            if (index == Integer.MIN_VALUE) {
                throw new IllegalArgumentException("Unknown key! " + config);
            }
            changeSet.add(index);
        }
        this.worldSpawnManager.rebuildConfigurations(changeSet);
        final ObjectArrayList<String> errors = new ObjectArrayList<String>();
        validateSpawnsConfigurations("World spawn", event.getLoadedAssets(), errors);
        for (final String error : errors) {
            this.getLogger().at(Level.SEVERE).log(error);
        }
    }
    
    private void onBeaconNPCSpawnsLoaded(@Nonnull final LoadedAssetsEvent<String, BeaconNPCSpawn, IndexedLookupTableAssetMap<String, BeaconNPCSpawn>> event) {
        if (NPCPlugin.get().getBuilderManager().isEmpty()) {
            return;
        }
        final IntOpenHashSet changeSet = new IntOpenHashSet();
        for (final String config : event.getLoadedAssets().keySet()) {
            final int index = event.getAssetMap().getIndex((Object)config);
            if (index == Integer.MIN_VALUE) {
                throw new IllegalArgumentException("Unknown key! " + config);
            }
            changeSet.add(index);
        }
        this.rebuildBeaconSpawnConfigurations(changeSet);
        final Map<String, BeaconNPCSpawn> loadedAssets = event.getLoadedAssets();
        Universe.get().getWorlds().forEach((name, world) -> world.execute(() -> {
            final Store<EntityStore> store = world.getEntityStore().getStore();
            store.forEachChunk(LegacySpawnBeaconEntity.getComponentType(), (archetypeChunk, commandBuffer) -> {
                int index2 = 0;
                while (index2 < archetypeChunk.size()) {
                    final LegacySpawnBeaconEntity legacySpawnBeaconComponent = archetypeChunk.getComponent(index2, LegacySpawnBeaconEntity.getComponentType());
                    if (!SpawningPlugin.$assertionsDisabled && legacySpawnBeaconComponent == null) {
                        throw new AssertionError();
                    }
                    else {
                        if (!(!loadedAssets.containsKey(legacySpawnBeaconComponent.getSpawnConfigId()))) {
                            final Ref<EntityStore> spawnBeaconRef = archetypeChunk.getReferenceTo(index2);
                            final Holder<EntityStore> holder = commandBuffer.removeEntity(spawnBeaconRef, EntityStore.REGISTRY.newHolder(), RemoveReason.UNLOAD);
                            commandBuffer.addEntity(holder, AddReason.LOAD);
                        }
                        ++index2;
                    }
                }
                return;
            });
            store.forEachChunk(SpawnBeacon.getComponentType(), (archetypeChunk, commandBuffer) -> {
                int index3 = 0;
                while (index3 < archetypeChunk.size()) {
                    final SpawnBeacon legacySpawnBeaconComponent2 = archetypeChunk.getComponent(index3, SpawnBeacon.getComponentType());
                    if (!SpawningPlugin.$assertionsDisabled && legacySpawnBeaconComponent2 == null) {
                        throw new AssertionError();
                    }
                    else {
                        if (!(!loadedAssets.containsKey(legacySpawnBeaconComponent2.getSpawnConfigId()))) {
                            final Ref<EntityStore> spawnBeaconRef2 = archetypeChunk.getReferenceTo(index3);
                            final Holder<EntityStore> holder2 = commandBuffer.removeEntity(spawnBeaconRef2, EntityStore.REGISTRY.newHolder(), RemoveReason.UNLOAD);
                            commandBuffer.addEntity(holder2, AddReason.LOAD);
                        }
                        ++index3;
                    }
                }
            });
        }));
        final ObjectArrayList<String> errors = new ObjectArrayList<String>();
        validateSpawnsConfigurations("Beacon spawn", event.getLoadedAssets(), errors);
        for (final String error : errors) {
            this.getLogger().at(Level.SEVERE).log(error);
        }
    }
    
    private void onWorldNPCSpawnsRemoved(@Nonnull final RemovedAssetsEvent<String, WorldNPCSpawn, IndexedLookupTableAssetMap<String, WorldNPCSpawn>> event) {
        for (final String removed : event.getRemovedAssets()) {
            this.worldSpawnManager.onNPCSpawnRemoved(removed);
        }
        WorldSpawnManager.onEnvironmentChanged();
    }
    
    private void onBeaconNPCSpawnsRemoved(@Nonnull final RemovedAssetsEvent<String, BeaconNPCSpawn, IndexedLookupTableAssetMap<String, BeaconNPCSpawn>> event) {
        for (final String removed : event.getRemovedAssets()) {
            this.beaconSpawnManager.onNPCSpawnRemoved(removed);
        }
        final Set<String> removedAssets = event.getRemovedAssets();
        Universe.get().getWorlds().forEach((name, world) -> world.execute(() -> {
            final Store<EntityStore> store = world.getEntityStore().getStore();
            store.forEachChunk(LegacySpawnBeaconEntity.getComponentType(), (archetypeChunk, commandBuffer) -> {
                int index = 0;
                while (index < archetypeChunk.size()) {
                    final LegacySpawnBeaconEntity spawnBeaconComponent = archetypeChunk.getComponent(index, LegacySpawnBeaconEntity.getComponentType());
                    if (!SpawningPlugin.$assertionsDisabled && spawnBeaconComponent == null) {
                        throw new AssertionError();
                    }
                    else {
                        if (!(!removedAssets.contains(spawnBeaconComponent.getSpawnConfigId()))) {
                            final Ref<EntityStore> spawnBeaconRef = archetypeChunk.getReferenceTo(index);
                            commandBuffer.removeEntity(spawnBeaconRef, RemoveReason.REMOVE);
                        }
                        ++index;
                    }
                }
                return;
            });
            store.forEachChunk(SpawnBeacon.getComponentType(), (archetypeChunk, commandBuffer) -> {
                int index2 = 0;
                while (index2 < archetypeChunk.size()) {
                    final SpawnBeacon spawnBeaconComponent2 = archetypeChunk.getComponent(index2, SpawnBeacon.getComponentType());
                    if (!SpawningPlugin.$assertionsDisabled && spawnBeaconComponent2 == null) {
                        throw new AssertionError();
                    }
                    else {
                        if (!(!removedAssets.contains(spawnBeaconComponent2.getSpawnConfigId()))) {
                            final Ref<EntityStore> spawnBeaconRef2 = archetypeChunk.getReferenceTo(index2);
                            commandBuffer.removeEntity(spawnBeaconRef2, RemoveReason.REMOVE);
                        }
                        ++index2;
                    }
                }
            });
        }));
    }
    
    private void onLoadedNPCEvent(@Nonnull final AllNPCsLoadedEvent loadedNPCEvent) {
        final IntOpenHashSet changeSet = new IntOpenHashSet();
        final Int2ObjectMap<BuilderInfo> loadedNPCs = loadedNPCEvent.getLoadedNPCs();
        for (final BuilderInfo builder : loadedNPCs.values()) {
            final String key = builder.getKeyName();
            this.worldSpawnManager.onNPCLoaded(key, changeSet);
            this.beaconSpawnManager.onNPCLoaded(key, changeSet);
        }
        this.worldSpawnManager.rebuildConfigurations(changeSet);
        this.rebuildBeaconSpawnConfigurations(changeSet);
    }
    
    private void setUpWithAllRoles() {
        final IntOpenHashSet changeSet = new IntOpenHashSet();
        final IndexedLookupTableAssetMap<String, WorldNPCSpawn> npcWorldSpawnMap = WorldNPCSpawn.getAssetMap();
        final Map<String, WorldNPCSpawn> assetMap = npcWorldSpawnMap.getAssetMap();
        int worldSetupCount = 0;
        for (final Map.Entry<String, WorldNPCSpawn> entry : assetMap.entrySet()) {
            final WorldNPCSpawn value = entry.getValue();
            if (this.worldSpawnManager.addSpawnWrapper(new WorldSpawnWrapper(value))) {
                ++worldSetupCount;
            }
            final String key = entry.getKey();
            final int index = npcWorldSpawnMap.getIndex(key);
            if (index == Integer.MIN_VALUE) {
                throw new IllegalArgumentException("Unknown key! " + key);
            }
            changeSet.add(index);
        }
        final IndexedLookupTableAssetMap<String, BeaconNPCSpawn> beaconSpawnMap = BeaconNPCSpawn.getAssetMap();
        final Map<String, BeaconNPCSpawn> beaconSpawnAssetMap = beaconSpawnMap.getAssetMap();
        int beaconSetupCount = 0;
        for (final Map.Entry<String, BeaconNPCSpawn> entry2 : beaconSpawnAssetMap.entrySet()) {
            final BeaconNPCSpawn value2 = entry2.getValue();
            if (this.beaconSpawnManager.addSpawnWrapper(new BeaconSpawnWrapper(value2))) {
                ++beaconSetupCount;
            }
            final String key2 = entry2.getKey();
            final int index2 = beaconSpawnMap.getIndex(key2);
            if (index2 == Integer.MIN_VALUE) {
                throw new IllegalArgumentException("Unknown key! " + key2);
            }
            changeSet.add(index2);
        }
        WorldSpawnManager.trackNPCs(changeSet);
        this.getLogger().at(Level.INFO).log("Successfully set up %s world spawn configurations", worldSetupCount);
        this.getLogger().at(Level.INFO).log("Successfully set up %s beacon spawn configurations", beaconSetupCount);
    }
    
    private void rebuildBeaconSpawnConfigurations(@Nullable final IntSet changeSet) {
        if (changeSet == null || changeSet.isEmpty()) {
            return;
        }
        int setupCount = 0;
        for (final int configIndex : changeSet) {
            this.beaconSpawnManager.removeSpawnWrapper(configIndex);
            final BeaconNPCSpawn spawn = BeaconNPCSpawn.getAssetMap().getAssetOrDefault(configIndex, null);
            if (spawn == null) {
                continue;
            }
            if (!this.beaconSpawnManager.addSpawnWrapper(new BeaconSpawnWrapper(spawn))) {
                continue;
            }
            ++setupCount;
        }
        this.getLogger().at(Level.INFO).log("Successfully rebuilt %s beacon spawn configurations", setupCount);
    }
    
    private void onModelAssetChange(@Nonnull final LoadedAssetsEvent<String, ModelAsset, DefaultAssetMap<String, ModelAsset>> event) {
        if (this.spawnMarkerModel == null) {
            return;
        }
        final Map<String, ModelAsset> modelMap = event.getLoadedAssets();
        final ModelAsset modelAsset = modelMap.get(this.spawnMarkerModel.getModelAssetId());
        if (modelAsset == null) {
            return;
        }
        this.spawnMarkerModel = Model.createUnitScaleModel(modelAsset);
    }
    
    private void onLoadAsset(@Nonnull final LoadAssetEvent event) {
        HytaleLogger.getLogger().at(Level.INFO).log("Validating Spawn assets phase...");
        final long start = System.nanoTime();
        final ObjectArrayList<String> errors = new ObjectArrayList<String>();
        validateSpawnsConfigurations("World spawn", WorldNPCSpawn.getAssetMap().getAssetMap(), errors);
        validateSpawnsConfigurations("Beacon spawn", BeaconNPCSpawn.getAssetMap().getAssetMap(), errors);
        validateSpawnMarkers(SpawnMarker.getAssetMap().getAssetMap(), errors);
        for (final String error : errors) {
            this.getLogger().at(Level.SEVERE).log(error);
        }
        if (!errors.isEmpty()) {
            event.failed(Options.getOptionSet().has(Options.VALIDATE_ASSETS), "failed to validate spawning assets");
        }
        HytaleLogger.getLogger().at(Level.INFO).log("Spawn assets validation phase completed! Boot time %s, Took %s", FormatUtil.nanosToString(System.nanoTime() - event.getBootStart()), FormatUtil.nanosToString(System.nanoTime() - start));
    }
    
    @Deprecated(forRemoval = true)
    public static class LegacySpawnSuppressorEntityMigration extends EntityModule.MigrationSystem
    {
        private final ComponentType<EntityStore, PersistentModel> persistentModelComponentType;
        private final ComponentType<EntityStore, Nameplate> nameplateComponentType;
        private final ComponentType<EntityStore, UUIDComponent> uuidComponentType;
        private final ComponentType<EntityStore, UnknownComponents<EntityStore>> unknownComponentsComponentType;
        private final Query<EntityStore> query;
        
        public LegacySpawnSuppressorEntityMigration() {
            this.persistentModelComponentType = PersistentModel.getComponentType();
            this.nameplateComponentType = Nameplate.getComponentType();
            this.uuidComponentType = UUIDComponent.getComponentType();
            this.unknownComponentsComponentType = EntityStore.REGISTRY.getUnknownComponentType();
            this.query = (Query<EntityStore>)Query.and(this.unknownComponentsComponentType, Query.not((Query<Object>)AllLegacyEntityTypesQuery.INSTANCE));
        }
        
        @Override
        public void onEntityAdd(@Nonnull final Holder<EntityStore> holder, @Nonnull final AddReason reason, @Nonnull final Store<EntityStore> store) {
            final Map<String, BsonDocument> unknownComponents = holder.getComponent(this.unknownComponentsComponentType).getUnknownComponents();
            final BsonDocument spawnSuppressor = unknownComponents.remove("SpawnSuppressor");
            if (spawnSuppressor == null) {
                return;
            }
            final Archetype<EntityStore> archetype = holder.getArchetype();
            if (!archetype.contains(this.persistentModelComponentType)) {
                final Model.ModelReference modelReference = Entity.MODEL.get(spawnSuppressor).get();
                holder.addComponent(this.persistentModelComponentType, new PersistentModel(modelReference));
            }
            if (!archetype.contains(this.nameplateComponentType)) {
                holder.addComponent(this.nameplateComponentType, new Nameplate(Entity.DISPLAY_NAME.get(spawnSuppressor).get()));
            }
            if (!archetype.contains(this.uuidComponentType)) {
                holder.addComponent(this.uuidComponentType, new UUIDComponent(Entity.UUID.get(spawnSuppressor).get()));
            }
            holder.ensureComponent(HiddenFromAdventurePlayers.getComponentType());
        }
        
        @Override
        public void onEntityRemoved(@Nonnull final Holder<EntityStore> holder, @Nonnull final RemoveReason reason, @Nonnull final Store<EntityStore> store) {
        }
        
        @Nonnull
        @Override
        public Query<EntityStore> getQuery() {
            return this.query;
        }
        
        @Nonnull
        @Override
        public Set<Dependency<EntityStore>> getDependencies() {
            return RootDependency.firstSet();
        }
    }
    
    public static class NPCSpawningConfig
    {
        public static final BuilderCodec<NPCSpawningConfig> CODEC;
        private double spawnBudgetFactor;
        private int maxActiveJobs;
        private String defaultMarkerModel;
        private double localSpawnControllerJoinDelay;
        
        public NPCSpawningConfig() {
            this.spawnBudgetFactor = 1.0;
            this.maxActiveJobs = 20;
            this.defaultMarkerModel = "NPC_Spawn_Marker";
            this.localSpawnControllerJoinDelay = 15.0;
        }
        
        static {
            // 
            // This method could not be decompiled.
            // 
            // Original Bytecode:
            // 
            //     2: invokedynamic   BootstrapMethod #0, get:()Ljava/util/function/Supplier;
            //     7: invokestatic    com/hypixel/hytale/codec/builder/BuilderCodec.builder:(Ljava/lang/Class;Ljava/util/function/Supplier;)Lcom/hypixel/hytale/codec/builder/BuilderCodec$Builder;
            //    10: new             Lcom/hypixel/hytale/codec/KeyedCodec;
            //    13: dup            
            //    14: ldc             "SpawnBudgetFactor"
            //    16: getstatic       com/hypixel/hytale/codec/Codec.DOUBLE:Lcom/hypixel/hytale/codec/codecs/simple/DoubleCodec;
            //    19: invokespecial   com/hypixel/hytale/codec/KeyedCodec.<init>:(Ljava/lang/String;Lcom/hypixel/hytale/codec/Codec;)V
            //    22: invokedynamic   BootstrapMethod #1, accept:()Ljava/util/function/BiConsumer;
            //    27: invokedynamic   BootstrapMethod #2, apply:()Ljava/util/function/Function;
            //    32: invokevirtual   com/hypixel/hytale/codec/builder/BuilderCodec$Builder.append:(Lcom/hypixel/hytale/codec/KeyedCodec;Ljava/util/function/BiConsumer;Ljava/util/function/Function;)Lcom/hypixel/hytale/codec/builder/BuilderField$FieldBuilder;
            //    35: invokevirtual   com/hypixel/hytale/codec/builder/BuilderField$FieldBuilder.add:()Lcom/hypixel/hytale/codec/builder/BuilderCodec$BuilderBase;
            //    38: checkcast       Lcom/hypixel/hytale/codec/builder/BuilderCodec$Builder;
            //    41: new             Lcom/hypixel/hytale/codec/KeyedCodec;
            //    44: dup            
            //    45: ldc             "MaxActiveJobs"
            //    47: getstatic       com/hypixel/hytale/codec/Codec.INTEGER:Lcom/hypixel/hytale/codec/codecs/simple/IntegerCodec;
            //    50: invokespecial   com/hypixel/hytale/codec/KeyedCodec.<init>:(Ljava/lang/String;Lcom/hypixel/hytale/codec/Codec;)V
            //    53: invokedynamic   BootstrapMethod #3, accept:()Ljava/util/function/BiConsumer;
            //    58: invokedynamic   BootstrapMethod #4, apply:()Ljava/util/function/Function;
            //    63: invokevirtual   com/hypixel/hytale/codec/builder/BuilderCodec$Builder.append:(Lcom/hypixel/hytale/codec/KeyedCodec;Ljava/util/function/BiConsumer;Ljava/util/function/Function;)Lcom/hypixel/hytale/codec/builder/BuilderField$FieldBuilder;
            //    66: invokevirtual   com/hypixel/hytale/codec/builder/BuilderField$FieldBuilder.add:()Lcom/hypixel/hytale/codec/builder/BuilderCodec$BuilderBase;
            //    69: checkcast       Lcom/hypixel/hytale/codec/builder/BuilderCodec$Builder;
            //    72: new             Lcom/hypixel/hytale/codec/KeyedCodec;
            //    75: dup            
            //    76: ldc             "DefaultSpawnMarkerModel"
            //    78: getstatic       com/hypixel/hytale/codec/Codec.STRING:Lcom/hypixel/hytale/codec/codecs/simple/StringCodec;
            //    81: invokespecial   com/hypixel/hytale/codec/KeyedCodec.<init>:(Ljava/lang/String;Lcom/hypixel/hytale/codec/Codec;)V
            //    84: invokedynamic   BootstrapMethod #5, accept:()Ljava/util/function/BiConsumer;
            //    89: invokedynamic   BootstrapMethod #6, apply:()Ljava/util/function/Function;
            //    94: invokevirtual   com/hypixel/hytale/codec/builder/BuilderCodec$Builder.append:(Lcom/hypixel/hytale/codec/KeyedCodec;Ljava/util/function/BiConsumer;Ljava/util/function/Function;)Lcom/hypixel/hytale/codec/builder/BuilderField$FieldBuilder;
            //    97: invokevirtual   com/hypixel/hytale/codec/builder/BuilderField$FieldBuilder.add:()Lcom/hypixel/hytale/codec/builder/BuilderCodec$BuilderBase;
            //   100: checkcast       Lcom/hypixel/hytale/codec/builder/BuilderCodec$Builder;
            //   103: new             Lcom/hypixel/hytale/codec/KeyedCodec;
            //   106: dup            
            //   107: ldc             "LocalSpawnControllerJoinDelay"
            //   109: getstatic       com/hypixel/hytale/codec/Codec.DOUBLE:Lcom/hypixel/hytale/codec/codecs/simple/DoubleCodec;
            //   112: invokespecial   com/hypixel/hytale/codec/KeyedCodec.<init>:(Ljava/lang/String;Lcom/hypixel/hytale/codec/Codec;)V
            //   115: invokedynamic   BootstrapMethod #7, accept:()Ljava/util/function/BiConsumer;
            //   120: invokedynamic   BootstrapMethod #8, apply:()Ljava/util/function/Function;
            //   125: invokevirtual   com/hypixel/hytale/codec/builder/BuilderCodec$Builder.append:(Lcom/hypixel/hytale/codec/KeyedCodec;Ljava/util/function/BiConsumer;Ljava/util/function/Function;)Lcom/hypixel/hytale/codec/builder/BuilderField$FieldBuilder;
            //   128: invokevirtual   com/hypixel/hytale/codec/builder/BuilderField$FieldBuilder.add:()Lcom/hypixel/hytale/codec/builder/BuilderCodec$BuilderBase;
            //   131: checkcast       Lcom/hypixel/hytale/codec/builder/BuilderCodec$Builder;
            //   134: invokevirtual   com/hypixel/hytale/codec/builder/BuilderCodec$Builder.build:()Lcom/hypixel/hytale/codec/builder/BuilderCodec;
            //   137: putstatic       com/hypixel/hytale/server/spawning/SpawningPlugin$NPCSpawningConfig.CODEC:Lcom/hypixel/hytale/codec/builder/BuilderCodec;
            //   140: return         
            // 
            // The error that occurred was:
            // 
            // java.lang.UnsupportedOperationException: The requested operation is not supported.
            //     at com.strobel.util.ContractUtils.unsupported(ContractUtils.java:27)
            //     at com.strobel.assembler.metadata.TypeReference.getRawType(TypeReference.java:284)
            //     at com.strobel.assembler.metadata.TypeReference.getRawType(TypeReference.java:279)
            //     at com.strobel.assembler.metadata.TypeReference.makeGenericType(TypeReference.java:154)
            //     at com.strobel.assembler.metadata.TypeSubstitutionVisitor.visitParameterizedType(TypeSubstitutionVisitor.java:225)
            //     at com.strobel.assembler.metadata.TypeSubstitutionVisitor.visitParameterizedType(TypeSubstitutionVisitor.java:25)
            //     at com.strobel.assembler.metadata.ParameterizedType.accept(ParameterizedType.java:103)
            //     at com.strobel.assembler.metadata.TypeSubstitutionVisitor.visit(TypeSubstitutionVisitor.java:40)
            //     at com.strobel.assembler.metadata.TypeSubstitutionVisitor.visitParameterizedType(TypeSubstitutionVisitor.java:211)
            //     at com.strobel.assembler.metadata.TypeSubstitutionVisitor.visitParameterizedType(TypeSubstitutionVisitor.java:25)
            //     at com.strobel.assembler.metadata.ParameterizedType.accept(ParameterizedType.java:103)
            //     at com.strobel.assembler.metadata.TypeSubstitutionVisitor.visit(TypeSubstitutionVisitor.java:40)
            //     at com.strobel.assembler.metadata.TypeSubstitutionVisitor.visitMethod(TypeSubstitutionVisitor.java:314)
            //     at com.strobel.decompiler.ast.TypeAnalysis.inferCall(TypeAnalysis.java:2611)
            //     at com.strobel.decompiler.ast.TypeAnalysis.doInferTypeForExpression(TypeAnalysis.java:1040)
            //     at com.strobel.decompiler.ast.TypeAnalysis.inferTypeForExpression(TypeAnalysis.java:815)
            //     at com.strobel.decompiler.ast.TypeAnalysis.inferTypeForExpression(TypeAnalysis.java:790)
            //     at com.strobel.decompiler.ast.TypeAnalysis.inferCall(TypeAnalysis.java:2689)
            //     at com.strobel.decompiler.ast.TypeAnalysis.doInferTypeForExpression(TypeAnalysis.java:1040)
            //     at com.strobel.decompiler.ast.TypeAnalysis.inferTypeForExpression(TypeAnalysis.java:815)
            //     at com.strobel.decompiler.ast.TypeAnalysis.inferTypeForExpression(TypeAnalysis.java:782)
            //     at com.strobel.decompiler.ast.TypeAnalysis.inferTypeForExpression(TypeAnalysis.java:778)
            //     at com.strobel.decompiler.ast.TypeAnalysis.doInferTypeForExpression(TypeAnalysis.java:1510)
            //     at com.strobel.decompiler.ast.TypeAnalysis.inferTypeForExpression(TypeAnalysis.java:815)
            //     at com.strobel.decompiler.ast.TypeAnalysis.inferTypeForExpression(TypeAnalysis.java:790)
            //     at com.strobel.decompiler.ast.TypeAnalysis.inferCall(TypeAnalysis.java:2689)
            //     at com.strobel.decompiler.ast.TypeAnalysis.doInferTypeForExpression(TypeAnalysis.java:1040)
            //     at com.strobel.decompiler.ast.TypeAnalysis.inferTypeForExpression(TypeAnalysis.java:815)
            //     at com.strobel.decompiler.ast.TypeAnalysis.inferTypeForExpression(TypeAnalysis.java:790)
            //     at com.strobel.decompiler.ast.TypeAnalysis.inferCall(TypeAnalysis.java:2689)
            //     at com.strobel.decompiler.ast.TypeAnalysis.doInferTypeForExpression(TypeAnalysis.java:1040)
            //     at com.strobel.decompiler.ast.TypeAnalysis.inferTypeForExpression(TypeAnalysis.java:815)
            //     at com.strobel.decompiler.ast.TypeAnalysis.inferTypeForExpression(TypeAnalysis.java:782)
            //     at com.strobel.decompiler.ast.TypeAnalysis.inferTypeForExpression(TypeAnalysis.java:778)
            //     at com.strobel.decompiler.ast.TypeAnalysis.doInferTypeForExpression(TypeAnalysis.java:1510)
            //     at com.strobel.decompiler.ast.TypeAnalysis.inferTypeForExpression(TypeAnalysis.java:815)
            //     at com.strobel.decompiler.ast.TypeAnalysis.inferTypeForExpression(TypeAnalysis.java:790)
            //     at com.strobel.decompiler.ast.TypeAnalysis.inferCall(TypeAnalysis.java:2689)
            //     at com.strobel.decompiler.ast.TypeAnalysis.doInferTypeForExpression(TypeAnalysis.java:1040)
            //     at com.strobel.decompiler.ast.TypeAnalysis.inferTypeForExpression(TypeAnalysis.java:815)
            //     at com.strobel.decompiler.ast.TypeAnalysis.inferTypeForExpression(TypeAnalysis.java:782)
            //     at com.strobel.decompiler.ast.TypeAnalysis.inferTypeForExpression(TypeAnalysis.java:778)
            //     at com.strobel.decompiler.ast.TypeAnalysis.doInferTypeForExpression(TypeAnalysis.java:1083)
            //     at com.strobel.decompiler.ast.TypeAnalysis.inferTypeForExpression(TypeAnalysis.java:815)
            //     at com.strobel.decompiler.ast.TypeAnalysis.runInference(TypeAnalysis.java:684)
            //     at com.strobel.decompiler.ast.TypeAnalysis.runInference(TypeAnalysis.java:667)
            //     at com.strobel.decompiler.ast.TypeAnalysis.runInference(TypeAnalysis.java:373)
            //     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.addTypeMembers(AstBuilder.java:662)
            //     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.");
        }
    }
}
