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

package com.hypixel.hytale.server.spawning.spawnmarkers;

import com.hypixel.hytale.server.core.asset.type.model.config.ModelAsset;
import java.util.Collection;
import java.util.HashSet;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import com.hypixel.hytale.component.RemoveReason;
import com.hypixel.hytale.math.vector.Vector3f;
import com.hypixel.hytale.function.consumer.TriConsumer;
import com.hypixel.hytale.server.core.universe.world.World;
import it.unimi.dsi.fastutil.objects.ObjectList;
import com.hypixel.hytale.server.npc.asset.builder.Builder;
import com.hypixel.hytale.server.npc.asset.builder.BuilderInfo;
import com.hypixel.hytale.server.core.entity.group.EntityGroup;
import com.hypixel.hytale.server.flock.FlockPlugin;
import com.hypixel.hytale.server.core.asset.type.model.config.Model;
import com.hypixel.hytale.server.core.modules.entity.component.WorldGenId;
import com.hypixel.hytale.component.ComponentAccessor;
import com.hypixel.hytale.server.npc.components.SpawnMarkerReference;
import com.hypixel.hytale.server.spawning.SpawnTestResult;
import com.hypixel.hytale.server.core.modules.entity.EntityModule;
import com.hypixel.hytale.component.spatial.SpatialResource;
import com.hypixel.hytale.server.spawning.ISpawnableWithModel;
import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent;
import com.hypixel.hytale.server.npc.NPCPlugin;
import com.hypixel.hytale.server.core.entity.UUIDComponent;
import java.util.logging.Level;
import java.util.Random;
import java.util.concurrent.ThreadLocalRandom;
import com.hypixel.hytale.component.Store;
import com.hypixel.hytale.server.spawning.SpawningPlugin;
import com.hypixel.hytale.component.ComponentType;
import com.hypixel.hytale.server.npc.entities.NPCEntity;
import com.hypixel.hytale.component.Ref;
import it.unimi.dsi.fastutil.Pair;
import java.util.List;
import com.hypixel.hytale.server.flock.StoredFlock;
import com.hypixel.hytale.math.vector.Vector3d;
import com.hypixel.hytale.server.spawning.SpawningContext;
import java.util.UUID;
import java.util.Set;
import java.time.Instant;
import javax.annotation.Nullable;
import java.time.Duration;
import com.hypixel.hytale.server.spawning.assets.spawnmarker.config.SpawnMarker;
import com.hypixel.hytale.codec.builder.BuilderCodec;
import com.hypixel.hytale.codec.codecs.array.ArrayCodec;
import javax.annotation.Nonnull;
import com.hypixel.hytale.server.core.entity.reference.InvalidatablePersistentRef;
import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
import com.hypixel.hytale.component.Component;

public class SpawnMarkerEntity implements Component<EntityStore>
{
    private static final double SPAWN_LOST_TIMEOUT = 35.0;
    @Nonnull
    private static final InvalidatablePersistentRef[] EMPTY_REFERENCES;
    public static final ArrayCodec<InvalidatablePersistentRef> NPC_REFERENCES_CODEC;
    @Nonnull
    public static final BuilderCodec<SpawnMarkerEntity> CODEC;
    private static final int MAX_FAILED_SPAWNS = 5;
    private String spawnMarkerId;
    private SpawnMarker cachedMarker;
    private double respawnCounter;
    @Nullable
    private Duration gameTimeRespawn;
    @Nullable
    private Instant spawnAfter;
    private int spawnCount;
    @Nullable
    private Set<UUID> suppressedBy;
    private int failedSpawns;
    @Nonnull
    private final SpawningContext context;
    private final Vector3d spawnPosition;
    private InvalidatablePersistentRef[] npcReferences;
    @Nullable
    private StoredFlock storedFlock;
    @Nullable
    private List<Pair<Ref<EntityStore>, NPCEntity>> tempStorageList;
    private double timeToDeactivation;
    private boolean despawnStarted;
    private double spawnLostTimeoutCounter;
    
    public static ComponentType<EntityStore, SpawnMarkerEntity> getComponentType() {
        return SpawningPlugin.get().getSpawnMarkerComponentType();
    }
    
    public SpawnMarkerEntity() {
        this.spawnPosition = new Vector3d();
        this.context = new SpawningContext();
        this.npcReferences = SpawnMarkerEntity.EMPTY_REFERENCES;
    }
    
    public SpawnMarker getCachedMarker() {
        return this.cachedMarker;
    }
    
    public void setCachedMarker(@Nonnull final SpawnMarker marker) {
        this.cachedMarker = marker;
    }
    
    public int getSpawnCount() {
        return this.spawnCount;
    }
    
    public void setSpawnCount(final int spawnCount) {
        this.spawnCount = spawnCount;
    }
    
    public void setRespawnCounter(final double respawnCounter) {
        this.respawnCounter = respawnCounter;
    }
    
    public void setSpawnAfter(@Nullable final Instant spawnAfter) {
        this.spawnAfter = spawnAfter;
    }
    
    @Nullable
    public Instant getSpawnAfter() {
        return this.spawnAfter;
    }
    
    public void setGameTimeRespawn(@Nullable final Duration gameTimeRespawn) {
        this.gameTimeRespawn = gameTimeRespawn;
    }
    
    @Nullable
    public Duration pollGameTimeRespawn() {
        final Duration ret = this.gameTimeRespawn;
        this.gameTimeRespawn = null;
        return ret;
    }
    
    public boolean tickRespawnTimer(final float dt) {
        final double respawnCounter = this.respawnCounter - dt;
        this.respawnCounter = respawnCounter;
        return respawnCounter <= 0.0;
    }
    
    @Nullable
    public Set<UUID> getSuppressedBy() {
        return this.suppressedBy;
    }
    
    public void setStoredFlock(@Nonnull final StoredFlock storedFlock) {
        this.storedFlock = storedFlock;
    }
    
    @Nullable
    public StoredFlock getStoredFlock() {
        return this.storedFlock;
    }
    
    public double getTimeToDeactivation() {
        return this.timeToDeactivation;
    }
    
    public void setTimeToDeactivation(final double timeToDeactivation) {
        this.timeToDeactivation = timeToDeactivation;
    }
    
    public boolean tickTimeToDeactivation(final float dt) {
        final double timeToDeactivation = this.timeToDeactivation - dt;
        this.timeToDeactivation = timeToDeactivation;
        return timeToDeactivation <= 0.0;
    }
    
    public boolean tickSpawnLostTimeout(final float dt) {
        final double spawnLostTimeoutCounter = this.spawnLostTimeoutCounter - dt;
        this.spawnLostTimeoutCounter = spawnLostTimeoutCounter;
        return spawnLostTimeoutCounter <= 0.0;
    }
    
    @Nonnull
    public Vector3d getSpawnPosition() {
        return this.spawnPosition;
    }
    
    public InvalidatablePersistentRef[] getNpcReferences() {
        return this.npcReferences;
    }
    
    public void setNpcReferences(@Nullable final InvalidatablePersistentRef[] npcReferences) {
        this.npcReferences = ((npcReferences != null) ? npcReferences : SpawnMarkerEntity.EMPTY_REFERENCES);
    }
    
    @Nullable
    public List<Pair<Ref<EntityStore>, NPCEntity>> getTempStorageList() {
        return this.tempStorageList;
    }
    
    public void setTempStorageList(@Nonnull final List<Pair<Ref<EntityStore>, NPCEntity>> tempStorageList) {
        this.tempStorageList = tempStorageList;
    }
    
    public boolean isDespawnStarted() {
        return this.despawnStarted;
    }
    
    public void setDespawnStarted(final boolean despawnStarted) {
        this.despawnStarted = despawnStarted;
    }
    
    public void refreshTimeout() {
        this.spawnLostTimeoutCounter = 35.0;
    }
    
    public boolean spawnNPC(@Nonnull final Ref<EntityStore> ref, @Nonnull final SpawnMarker marker, @Nonnull final Store<EntityStore> store) {
        final SpawnMarker.SpawnConfiguration spawn = marker.getWeightedConfigurations().get(ThreadLocalRandom.current());
        if (spawn == null) {
            SpawningPlugin.get().getLogger().at(Level.SEVERE).log("Marker %s has no spawn configuration to spawn", ref);
            this.refreshTimeout();
            return false;
        }
        final boolean realtime = marker.isRealtimeRespawn();
        if (realtime) {
            this.respawnCounter = spawn.getRealtimeRespawnTime();
        }
        else {
            this.spawnAfter = null;
            this.gameTimeRespawn = spawn.getSpawnAfterGameTime();
        }
        final UUIDComponent uuidComponent = store.getComponent(ref, UUIDComponent.getComponentType());
        assert uuidComponent != null;
        final UUID uuid = uuidComponent.getUuid();
        final String roleName = spawn.getNpc();
        if (roleName == null || roleName.isEmpty()) {
            SpawningPlugin.get().getLogger().at(Level.FINE).log("Marker %s performed noop spawn and set repawn to %s", uuid, realtime ? Double.valueOf(this.respawnCounter) : this.gameTimeRespawn);
            this.refreshTimeout();
            return true;
        }
        final NPCPlugin npcModule = NPCPlugin.get();
        final int roleIndex = npcModule.getIndex(roleName);
        final TransformComponent transformComponent = store.getComponent(ref, TransformComponent.getComponentType());
        assert transformComponent != null;
        final Vector3d position = transformComponent.getPosition();
        final BuilderInfo builderInfo = npcModule.getRoleBuilderInfo(roleIndex);
        if (builderInfo == null) {
            SpawningPlugin.get().getLogger().at(Level.SEVERE).log("Marker %s attempted to spawn non-existent NPC role '%s'", uuid, roleName);
            this.fail(ref, uuid, roleName, position, store, FailReason.NONEXISTENT_ROLE);
            return false;
        }
        final Builder<?> role = builderInfo.isValid() ? builderInfo.getBuilder() : null;
        if (role == null) {
            SpawningPlugin.get().getLogger().at(Level.SEVERE).log("Marker %s attempted to spawn invalid NPC role '%s'", uuid, roleName);
            this.fail(ref, uuid, roleName, position, store, FailReason.INVALID_ROLE);
            return false;
        }
        if (!role.isSpawnable()) {
            SpawningPlugin.get().getLogger().at(Level.SEVERE).log("Marker %s attempted to spawn a non-spawnable (abstract) role '%s'", uuid, roleName);
            this.fail(ref, uuid, roleName, position, store, FailReason.INVALID_ROLE);
            return false;
        }
        if (!this.context.setSpawnable((ISpawnableWithModel)role)) {
            SpawningPlugin.get().getLogger().at(Level.SEVERE).log("Marker %s failed to spawn NPC role '%s' due to failed role validation", uuid, roleName);
            this.fail(ref, uuid, roleName, position, store, FailReason.FAILED_ROLE_VALIDATION);
            return false;
        }
        final ObjectList<Ref<EntityStore>> results = SpatialResource.getThreadLocalReferenceList();
        final SpatialResource<Ref<EntityStore>, EntityStore> spatialResource = store.getResource(EntityModule.get().getPlayerSpatialResourceType());
        spatialResource.getSpatialStructure().collect(position, marker.getExclusionRadius(), results);
        final boolean hasPlayersInRange = !results.isEmpty();
        if (hasPlayersInRange) {
            this.refreshTimeout();
            return false;
        }
        final World world = store.getExternalData().getWorld();
        if (!this.context.set(world, position.x, position.y, position.z)) {
            SpawningPlugin.get().getLogger().at(Level.FINE).log("Marker %s attempted to spawn NPC '%s' at %s but could not fit", uuid, roleName, position);
            this.fail(ref, uuid, roleName, position, store, FailReason.NO_ROOM);
            return false;
        }
        final SpawnTestResult testResult = this.context.canSpawn(true, false);
        if (testResult != SpawnTestResult.TEST_OK) {
            SpawningPlugin.get().getLogger().at(Level.FINE).log("Marker %s attempted to spawn NPC '%s' at %s but could not fit: %s", uuid, roleName, position, testResult);
            this.fail(ref, uuid, roleName, position, store, FailReason.NO_ROOM);
            return false;
        }
        this.spawnPosition.assign(this.context.xSpawn, this.context.ySpawn, this.context.zSpawn);
        if (this.spawnPosition.distanceSquaredTo(position) > marker.getMaxDropHeightSquared()) {
            SpawningPlugin.get().getLogger().at(Level.FINE).log("Marker %s attempted to spawn NPC '%s' but was offset too far from the ground at %s", uuid, roleName, position);
            this.fail(ref, uuid, roleName, position, store, FailReason.TOO_HIGH);
            return false;
        }
        final TriConsumer<NPCEntity, Ref<EntityStore>, Store<EntityStore>> postSpawn = (_entity, _ref, _store) -> {
            final SpawnMarkerReference spawnMarkerReference = _store.ensureAndGetComponent(_ref, SpawnMarkerReference.getComponentType());
            spawnMarkerReference.getReference().setEntity(ref, _store);
            spawnMarkerReference.refreshTimeoutCounter();
            final WorldGenId worldGenIdComponent = _store.getComponent(ref, WorldGenId.getComponentType());
            final int worldGenId = (worldGenIdComponent != null) ? worldGenIdComponent.getWorldGenId() : 0;
            _store.putComponent(_ref, WorldGenId.getComponentType(), new WorldGenId(worldGenId));
            return;
        };
        final Vector3f rotation = transformComponent.getRotation();
        final Pair<Ref<EntityStore>, NPCEntity> npcPair = npcModule.spawnEntity(store, roleIndex, this.spawnPosition, rotation, null, postSpawn);
        if (npcPair == null) {
            SpawningPlugin.get().getLogger().at(Level.SEVERE).log("Marker %s failed to spawn NPC role '%s' due to an internal error", uuid, roleName);
            this.fail(ref, uuid, roleName, position, store, FailReason.INVALID_ROLE);
            return false;
        }
        final Ref<EntityStore> npcRef = npcPair.first();
        final NPCEntity npcComponent = npcPair.second();
        final Ref<EntityStore> flockReference = FlockPlugin.trySpawnFlock(npcRef, npcComponent, store, roleIndex, this.spawnPosition, rotation, spawn.getFlockDefinition(), postSpawn);
        final EntityGroup group = (flockReference == null) ? null : store.getComponent(flockReference, EntityGroup.getComponentType());
        this.spawnCount = ((group != null) ? group.size() : 1);
        if (this.storedFlock != null) {
            this.despawnStarted = false;
            this.npcReferences = new InvalidatablePersistentRef[this.spawnCount];
            if (group != null) {
                group.forEachMember((index, member, referenceArray) -> {
                    final InvalidatablePersistentRef reference2 = new InvalidatablePersistentRef();
                    reference2.setEntity(member, store);
                    referenceArray[index] = reference2;
                    return;
                }, this.npcReferences);
            }
            else {
                final InvalidatablePersistentRef reference = new InvalidatablePersistentRef();
                reference.setEntity(npcRef, store);
                this.npcReferences[0] = reference;
            }
            this.storedFlock.clear();
        }
        SpawningPlugin.get().getLogger().at(Level.FINE).log("Marker %s spawned %s and set respawn to %s", uuid, npcComponent.getRoleName(), realtime ? Double.valueOf(this.respawnCounter) : this.gameTimeRespawn);
        this.refreshTimeout();
        return true;
    }
    
    private void fail(@Nonnull final Ref<EntityStore> self, @Nonnull final UUID uuid, @Nonnull final String role, @Nonnull final Vector3d position, @Nonnull final Store<EntityStore> store, @Nonnull final FailReason reason) {
        if (++this.failedSpawns >= 5) {
            SpawningPlugin.get().getLogger().at(Level.WARNING).log("Marker %s at %s removed due to repeated spawning fails of %s with reason: %s", uuid, position, role, reason);
            store.removeEntity(self, RemoveReason.REMOVE);
            return;
        }
        this.refreshTimeout();
    }
    
    public void setSpawnMarker(@Nonnull final SpawnMarker marker) {
        this.spawnMarkerId = marker.getId();
        this.cachedMarker = marker;
        if (this.cachedMarker.getDeactivationDistance() > 0.0) {
            this.storedFlock = new StoredFlock();
            this.tempStorageList = new ObjectArrayList<Pair<Ref<EntityStore>, NPCEntity>>();
        }
        else {
            this.storedFlock = null;
            this.tempStorageList = null;
        }
    }
    
    public int decrementAndGetSpawnCount() {
        return --this.spawnCount;
    }
    
    public String getSpawnMarkerId() {
        return this.spawnMarkerId;
    }
    
    public boolean isManualTrigger() {
        return this.cachedMarker.isManualTrigger();
    }
    
    public boolean trigger(@Nonnull final Ref<EntityStore> markerRef, @Nonnull final Store<EntityStore> store) {
        return this.cachedMarker.isManualTrigger() && this.spawnCount <= 0 && this.spawnNPC(markerRef, this.cachedMarker, store);
    }
    
    public void suppress(@Nonnull final UUID suppressor) {
        if (this.suppressedBy == null) {
            this.suppressedBy = new HashSet<UUID>();
        }
        this.suppressedBy.add(suppressor);
    }
    
    public void releaseSuppression(@Nonnull final UUID suppressor) {
        if (this.suppressedBy != null) {
            this.suppressedBy.remove(suppressor);
        }
    }
    
    public void clearAllSuppressions() {
        if (this.suppressedBy != null) {
            this.suppressedBy.clear();
        }
    }
    
    @Nonnull
    @Override
    public Component<EntityStore> clone() {
        final SpawnMarkerEntity spawnMarker = new SpawnMarkerEntity();
        spawnMarker.spawnMarkerId = this.spawnMarkerId;
        spawnMarker.cachedMarker = this.cachedMarker;
        spawnMarker.respawnCounter = this.respawnCounter;
        spawnMarker.gameTimeRespawn = this.gameTimeRespawn;
        spawnMarker.spawnAfter = this.spawnAfter;
        spawnMarker.spawnCount = this.spawnCount;
        spawnMarker.suppressedBy = ((this.suppressedBy != null) ? new HashSet<UUID>(this.suppressedBy) : null);
        spawnMarker.failedSpawns = this.failedSpawns;
        spawnMarker.spawnPosition.assign(this.spawnPosition);
        spawnMarker.npcReferences = this.npcReferences;
        spawnMarker.storedFlock = ((this.storedFlock != null) ? this.storedFlock.clone() : null);
        spawnMarker.timeToDeactivation = this.timeToDeactivation;
        spawnMarker.despawnStarted = this.despawnStarted;
        spawnMarker.spawnLostTimeoutCounter = this.spawnLostTimeoutCounter;
        return spawnMarker;
    }
    
    @Nonnull
    @Override
    public String toString() {
        return "SpawnMarkerEntity{spawnMarkerId='" + this.spawnMarkerId + "', cachedMarker=" + String.valueOf(this.cachedMarker) + ", respawnCounter=" + this.respawnCounter + ", gameTimeRespawn=" + String.valueOf(this.gameTimeRespawn) + ", spawnAfter=" + String.valueOf(this.spawnAfter) + ", spawnCount=" + this.spawnCount + ", spawnLostTimeoutCounter=" + this.spawnLostTimeoutCounter + ", failedSpawns=" + this.failedSpawns + ", context=" + String.valueOf(this.context) + ", spawnPosition=" + String.valueOf(this.spawnPosition) + ", storedFlock=" + String.valueOf(this.storedFlock) + "} " + super.toString();
    }
    
    public static Model getModel(@Nonnull final SpawnMarker marker) {
        final String modelName = marker.getModel();
        ModelAsset modelAsset = null;
        if (modelName != null && !modelName.isEmpty()) {
            modelAsset = ModelAsset.getAssetMap().getAsset(modelName);
        }
        Model model;
        if (modelAsset == null) {
            model = SpawningPlugin.get().getSpawnMarkerModel();
        }
        else {
            model = Model.createUnitScaleModel(modelAsset);
        }
        return model;
    }
    
    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/server/spawning/spawnmarkers/SpawnMarkerEntity.$assertionsDisabled:Z
        //    16: iconst_0       
        //    17: anewarray       Lcom/hypixel/hytale/server/core/entity/reference/InvalidatablePersistentRef;
        //    20: putstatic       com/hypixel/hytale/server/spawning/spawnmarkers/SpawnMarkerEntity.EMPTY_REFERENCES:[Lcom/hypixel/hytale/server/core/entity/reference/InvalidatablePersistentRef;
        //    23: new             Lcom/hypixel/hytale/codec/codecs/array/ArrayCodec;
        //    26: dup            
        //    27: getstatic       com/hypixel/hytale/server/core/entity/reference/InvalidatablePersistentRef.CODEC:Lcom/hypixel/hytale/codec/builder/BuilderCodec;
        //    30: invokedynamic   BootstrapMethod #3, apply:()Ljava/util/function/IntFunction;
        //    35: invokespecial   com/hypixel/hytale/codec/codecs/array/ArrayCodec.<init>:(Lcom/hypixel/hytale/codec/Codec;Ljava/util/function/IntFunction;)V
        //    38: putstatic       com/hypixel/hytale/server/spawning/spawnmarkers/SpawnMarkerEntity.NPC_REFERENCES_CODEC:Lcom/hypixel/hytale/codec/codecs/array/ArrayCodec;
        //    41: ldc             Lcom/hypixel/hytale/server/spawning/spawnmarkers/SpawnMarkerEntity;.class
        //    43: invokedynamic   BootstrapMethod #4, get:()Ljava/util/function/Supplier;
        //    48: invokestatic    com/hypixel/hytale/codec/builder/BuilderCodec.builder:(Ljava/lang/Class;Ljava/util/function/Supplier;)Lcom/hypixel/hytale/codec/builder/BuilderCodec$Builder;
        //    51: new             Lcom/hypixel/hytale/codec/KeyedCodec;
        //    54: dup            
        //    55: ldc_w           "SpawnMarker"
        //    58: getstatic       com/hypixel/hytale/codec/Codec.STRING:Lcom/hypixel/hytale/codec/codecs/simple/StringCodec;
        //    61: invokespecial   com/hypixel/hytale/codec/KeyedCodec.<init>:(Ljava/lang/String;Lcom/hypixel/hytale/codec/Codec;)V
        //    64: invokedynamic   BootstrapMethod #5, accept:()Ljava/util/function/BiConsumer;
        //    69: invokedynamic   BootstrapMethod #6, apply:()Ljava/util/function/Function;
        //    74: invokevirtual   com/hypixel/hytale/codec/builder/BuilderCodec$Builder.addField:(Lcom/hypixel/hytale/codec/KeyedCodec;Ljava/util/function/BiConsumer;Ljava/util/function/Function;)Lcom/hypixel/hytale/codec/builder/BuilderCodec$BuilderBase;
        //    77: checkcast       Lcom/hypixel/hytale/codec/builder/BuilderCodec$Builder;
        //    80: new             Lcom/hypixel/hytale/codec/KeyedCodec;
        //    83: dup            
        //    84: ldc_w           "RespawnTime"
        //    87: getstatic       com/hypixel/hytale/codec/Codec.DOUBLE:Lcom/hypixel/hytale/codec/codecs/simple/DoubleCodec;
        //    90: invokespecial   com/hypixel/hytale/codec/KeyedCodec.<init>:(Ljava/lang/String;Lcom/hypixel/hytale/codec/Codec;)V
        //    93: invokedynamic   BootstrapMethod #7, accept:()Ljava/util/function/BiConsumer;
        //    98: invokedynamic   BootstrapMethod #8, apply:()Ljava/util/function/Function;
        //   103: invokevirtual   com/hypixel/hytale/codec/builder/BuilderCodec$Builder.addField:(Lcom/hypixel/hytale/codec/KeyedCodec;Ljava/util/function/BiConsumer;Ljava/util/function/Function;)Lcom/hypixel/hytale/codec/builder/BuilderCodec$BuilderBase;
        //   106: checkcast       Lcom/hypixel/hytale/codec/builder/BuilderCodec$Builder;
        //   109: new             Lcom/hypixel/hytale/codec/KeyedCodec;
        //   112: dup            
        //   113: ldc_w           "SpawnCount"
        //   116: getstatic       com/hypixel/hytale/codec/Codec.INTEGER:Lcom/hypixel/hytale/codec/codecs/simple/IntegerCodec;
        //   119: invokespecial   com/hypixel/hytale/codec/KeyedCodec.<init>:(Ljava/lang/String;Lcom/hypixel/hytale/codec/Codec;)V
        //   122: invokedynamic   BootstrapMethod #9, accept:()Ljava/util/function/BiConsumer;
        //   127: invokedynamic   BootstrapMethod #10, apply:()Ljava/util/function/Function;
        //   132: invokevirtual   com/hypixel/hytale/codec/builder/BuilderCodec$Builder.addField:(Lcom/hypixel/hytale/codec/KeyedCodec;Ljava/util/function/BiConsumer;Ljava/util/function/Function;)Lcom/hypixel/hytale/codec/builder/BuilderCodec$BuilderBase;
        //   135: checkcast       Lcom/hypixel/hytale/codec/builder/BuilderCodec$Builder;
        //   138: new             Lcom/hypixel/hytale/codec/KeyedCodec;
        //   141: dup            
        //   142: ldc_w           "GameTimeRespawn"
        //   145: getstatic       com/hypixel/hytale/codec/Codec.DURATION:Lcom/hypixel/hytale/codec/function/FunctionCodec;
        //   148: invokespecial   com/hypixel/hytale/codec/KeyedCodec.<init>:(Ljava/lang/String;Lcom/hypixel/hytale/codec/Codec;)V
        //   151: invokedynamic   BootstrapMethod #11, accept:()Ljava/util/function/BiConsumer;
        //   156: invokedynamic   BootstrapMethod #12, apply:()Ljava/util/function/Function;
        //   161: invokevirtual   com/hypixel/hytale/codec/builder/BuilderCodec$Builder.addField:(Lcom/hypixel/hytale/codec/KeyedCodec;Ljava/util/function/BiConsumer;Ljava/util/function/Function;)Lcom/hypixel/hytale/codec/builder/BuilderCodec$BuilderBase;
        //   164: checkcast       Lcom/hypixel/hytale/codec/builder/BuilderCodec$Builder;
        //   167: new             Lcom/hypixel/hytale/codec/KeyedCodec;
        //   170: dup            
        //   171: ldc_w           "SpawnAfter"
        //   174: getstatic       com/hypixel/hytale/codec/Codec.INSTANT:Lcom/hypixel/hytale/codec/function/FunctionCodec;
        //   177: invokespecial   com/hypixel/hytale/codec/KeyedCodec.<init>:(Ljava/lang/String;Lcom/hypixel/hytale/codec/Codec;)V
        //   180: invokedynamic   BootstrapMethod #13, accept:()Ljava/util/function/BiConsumer;
        //   185: invokedynamic   BootstrapMethod #14, apply:()Ljava/util/function/Function;
        //   190: invokevirtual   com/hypixel/hytale/codec/builder/BuilderCodec$Builder.addField:(Lcom/hypixel/hytale/codec/KeyedCodec;Ljava/util/function/BiConsumer;Ljava/util/function/Function;)Lcom/hypixel/hytale/codec/builder/BuilderCodec$BuilderBase;
        //   193: checkcast       Lcom/hypixel/hytale/codec/builder/BuilderCodec$Builder;
        //   196: new             Lcom/hypixel/hytale/codec/KeyedCodec;
        //   199: dup            
        //   200: ldc_w           "NPCReferences"
        //   203: getstatic       com/hypixel/hytale/server/spawning/spawnmarkers/SpawnMarkerEntity.NPC_REFERENCES_CODEC:Lcom/hypixel/hytale/codec/codecs/array/ArrayCodec;
        //   206: invokespecial   com/hypixel/hytale/codec/KeyedCodec.<init>:(Ljava/lang/String;Lcom/hypixel/hytale/codec/Codec;)V
        //   209: invokedynamic   BootstrapMethod #15, accept:()Ljava/util/function/BiConsumer;
        //   214: invokedynamic   BootstrapMethod #16, apply:()Ljava/util/function/Function;
        //   219: invokevirtual   com/hypixel/hytale/codec/builder/BuilderCodec$Builder.addField:(Lcom/hypixel/hytale/codec/KeyedCodec;Ljava/util/function/BiConsumer;Ljava/util/function/Function;)Lcom/hypixel/hytale/codec/builder/BuilderCodec$BuilderBase;
        //   222: checkcast       Lcom/hypixel/hytale/codec/builder/BuilderCodec$Builder;
        //   225: new             Lcom/hypixel/hytale/codec/KeyedCodec;
        //   228: dup            
        //   229: ldc_w           "PersistedFlock"
        //   232: getstatic       com/hypixel/hytale/server/flock/StoredFlock.CODEC:Lcom/hypixel/hytale/codec/builder/BuilderCodec;
        //   235: invokespecial   com/hypixel/hytale/codec/KeyedCodec.<init>:(Ljava/lang/String;Lcom/hypixel/hytale/codec/Codec;)V
        //   238: invokedynamic   BootstrapMethod #17, accept:()Ljava/util/function/BiConsumer;
        //   243: invokedynamic   BootstrapMethod #18, apply:()Ljava/util/function/Function;
        //   248: invokevirtual   com/hypixel/hytale/codec/builder/BuilderCodec$Builder.addField:(Lcom/hypixel/hytale/codec/KeyedCodec;Ljava/util/function/BiConsumer;Ljava/util/function/Function;)Lcom/hypixel/hytale/codec/builder/BuilderCodec$BuilderBase;
        //   251: checkcast       Lcom/hypixel/hytale/codec/builder/BuilderCodec$Builder;
        //   254: new             Lcom/hypixel/hytale/codec/KeyedCodec;
        //   257: dup            
        //   258: ldc_w           "SpawnPosition"
        //   261: getstatic       com/hypixel/hytale/math/vector/Vector3d.CODEC:Lcom/hypixel/hytale/codec/builder/BuilderCodec;
        //   264: invokespecial   com/hypixel/hytale/codec/KeyedCodec.<init>:(Ljava/lang/String;Lcom/hypixel/hytale/codec/Codec;)V
        //   267: invokedynamic   BootstrapMethod #19, accept:()Ljava/util/function/BiConsumer;
        //   272: invokedynamic   BootstrapMethod #20, apply:()Ljava/util/function/Function;
        //   277: invokevirtual   com/hypixel/hytale/codec/builder/BuilderCodec$Builder.addField:(Lcom/hypixel/hytale/codec/KeyedCodec;Ljava/util/function/BiConsumer;Ljava/util/function/Function;)Lcom/hypixel/hytale/codec/builder/BuilderCodec$BuilderBase;
        //   280: checkcast       Lcom/hypixel/hytale/codec/builder/BuilderCodec$Builder;
        //   283: invokevirtual   com/hypixel/hytale/codec/builder/BuilderCodec$Builder.build:()Lcom/hypixel/hytale/codec/builder/BuilderCodec;
        //   286: putstatic       com/hypixel/hytale/server/spawning/spawnmarkers/SpawnMarkerEntity.CODEC:Lcom/hypixel/hytale/codec/builder/BuilderCodec;
        //   289: return         
        //    StackMapTable: 00 02 0C 40 01
        // 
        // 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.visitClassType(TypeSubstitutionVisitor.java:267)
        //     at com.strobel.assembler.metadata.TypeSubstitutionVisitor.visitClassType(TypeSubstitutionVisitor.java:25)
        //     at com.strobel.assembler.metadata.TypeDefinition.accept(TypeDefinition.java:189)
        //     at com.strobel.assembler.metadata.TypeSubstitutionVisitor.visit(TypeSubstitutionVisitor.java:40)
        //     at com.strobel.assembler.metadata.TypeSubstitutionVisitor.visitMethod(TypeSubstitutionVisitor.java:324)
        //     at com.strobel.decompiler.ast.TypeAnalysis.inferCall(TypeAnalysis.java:2586)
        //     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: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: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: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: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:1510)
        //     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.inferCall(TypeAnalysis.java:2483)
        //     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:782)
        //     at com.strobel.decompiler.ast.TypeAnalysis.inferTypeForExpression(TypeAnalysis.java:778)
        //     at com.strobel.decompiler.ast.TypeAnalysis.inferCall(TypeAnalysis.java:2483)
        //     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.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 enum FailReason
    {
        INVALID_ROLE, 
        NONEXISTENT_ROLE, 
        FAILED_ROLE_VALIDATION, 
        NO_ROOM, 
        TOO_HIGH;
    }
}
