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

package com.hypixel.hytale.server.npc;

import com.hypixel.hytale.server.core.modules.entitystats.EntityStatMap;
import com.hypixel.hytale.server.core.modules.entitystats.EntityStatsSystems;
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.server.core.universe.world.World;
import com.hypixel.hytale.component.RemoveReason;
import com.hypixel.hytale.server.npc.corecomponents.ISensorEntityCollector;
import com.hypixel.hytale.server.npc.corecomponents.ISensorEntityPrioritiser;
import com.hypixel.hytale.server.npc.corecomponents.IEntityFilter;
import com.hypixel.hytale.server.npc.statetransition.StateTransitionController;
import com.hypixel.hytale.server.npc.corecomponents.utility.builders.BuilderValueToParameterMapping;
import com.hypixel.hytale.server.npc.corecomponents.builders.BuilderWeightedAction;
import com.hypixel.hytale.server.npc.corecomponents.WeightedAction;
import com.hypixel.hytale.server.npc.path.builders.BuilderRelativeWaypointDefinition;
import com.hypixel.hytale.builtin.path.waypoint.RelativeWaypointDefinition;
import com.hypixel.hytale.server.npc.path.builders.BuilderTransientPathDefinition;
import com.hypixel.hytale.server.npc.instructions.builders.BuilderInstructionReference;
import com.hypixel.hytale.server.npc.instructions.builders.BuilderInstructionRandomized;
import com.hypixel.hytale.server.npc.instructions.builders.BuilderInstruction;
import com.hypixel.hytale.server.npc.instructions.builders.BuilderActionList;
import com.hypixel.hytale.server.npc.movement.controllers.builders.BuilderMotionControllerMap;
import com.hypixel.hytale.server.npc.movement.controllers.BuilderMotionControllerMapUtil;
import com.hypixel.hytale.server.npc.movement.controllers.builders.BuilderMotionControllerDive;
import com.hypixel.hytale.server.npc.movement.controllers.builders.BuilderMotionControllerFly;
import com.hypixel.hytale.server.npc.movement.controllers.builders.BuilderMotionControllerWalk;
import com.hypixel.hytale.server.npc.movement.controllers.MotionController;
import com.hypixel.hytale.server.npc.role.builders.BuilderRoleVariant;
import com.hypixel.hytale.server.npc.role.builders.BuilderRoleAbstract;
import com.hypixel.hytale.server.npc.role.builders.BuilderRole;
import java.util.Objects;
import com.hypixel.hytale.server.npc.asset.builder.BuilderFactory;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.CompletableFuture;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import java.util.function.Consumer;
import com.hypixel.hytale.server.npc.util.expression.StdScope;
import com.hypixel.hytale.server.npc.util.expression.Scope;
import com.hypixel.hytale.server.npc.asset.builder.BuilderSupport;
import com.hypixel.hytale.component.AddReason;
import com.hypixel.hytale.server.core.modules.entity.component.PersistentModel;
import com.hypixel.hytale.server.core.modules.entity.component.ModelComponent;
import com.hypixel.hytale.server.core.entity.UUIDComponent;
import com.hypixel.hytale.server.core.modules.entity.component.DisplayNameComponent;
import com.hypixel.hytale.server.core.Message;
import com.hypixel.hytale.server.core.modules.entity.component.HeadRotation;
import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent;
import com.hypixel.hytale.server.core.modules.time.WorldTimeResource;
import com.hypixel.hytale.component.Holder;
import com.hypixel.hytale.logger.sentry.SkipSentryException;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import com.hypixel.hytale.server.npc.asset.builder.BuilderInfo;
import it.unimi.dsi.fastutil.objects.Object2IntMap;
import com.hypixel.hytale.builtin.tagset.TagSetPlugin;
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
import java.util.Iterator;
import java.util.Map;
import com.hypixel.hytale.assetstore.AssetMap;
import com.hypixel.hytale.component.query.Query;
import com.hypixel.hytale.server.core.universe.Universe;
import com.hypixel.hytale.server.flock.FlockPlugin;
import com.hypixel.hytale.server.flock.config.FlockAsset;
import com.hypixel.hytale.function.consumer.TriConsumer;
import com.hypixel.hytale.server.core.asset.type.model.config.Model;
import com.hypixel.hytale.server.core.universe.world.npc.INonPlayerCharacter;
import it.unimi.dsi.fastutil.Pair;
import com.hypixel.hytale.math.vector.Vector3d;
import com.hypixel.hytale.component.Store;
import com.hypixel.hytale.server.npc.corecomponents.entity.prioritisers.builders.BuilderSensorEntityPrioritiserAttitude;
import com.hypixel.hytale.server.npc.corecomponents.entity.filters.builders.BuilderEntityFilterInsideBlock;
import com.hypixel.hytale.server.npc.corecomponents.entity.filters.builders.BuilderEntityFilterAltitude;
import com.hypixel.hytale.server.npc.corecomponents.entity.filters.builders.BuilderEntityFilterOr;
import com.hypixel.hytale.server.npc.corecomponents.entity.filters.builders.BuilderEntityFilterAnd;
import com.hypixel.hytale.server.npc.corecomponents.entity.filters.builders.BuilderEntityFilterNot;
import com.hypixel.hytale.server.npc.corecomponents.entity.filters.builders.BuilderEntityFilterInventory;
import com.hypixel.hytale.server.npc.corecomponents.entity.filters.builders.BuilderEntityFilterStat;
import com.hypixel.hytale.server.npc.corecomponents.entity.filters.builders.BuilderEntityFilterStandingOnBlock;
import com.hypixel.hytale.server.npc.corecomponents.entity.filters.builders.BuilderEntityFilterSpotsMe;
import com.hypixel.hytale.server.npc.corecomponents.entity.filters.builders.BuilderEntityFilterMovementState;
import com.hypixel.hytale.server.npc.corecomponents.entity.filters.builders.BuilderEntityFilterNPCGroup;
import com.hypixel.hytale.server.npc.corecomponents.entity.filters.builders.BuilderEntityFilterItemInHand;
import com.hypixel.hytale.server.npc.corecomponents.entity.filters.builders.BuilderEntityFilterCombat;
import com.hypixel.hytale.server.npc.corecomponents.entity.filters.builders.BuilderEntityFilterViewSector;
import com.hypixel.hytale.server.npc.corecomponents.entity.filters.builders.BuilderEntityFilterHeightDifference;
import com.hypixel.hytale.server.npc.corecomponents.entity.filters.builders.BuilderEntityFilterLineOfSight;
import com.hypixel.hytale.server.npc.corecomponents.entity.filters.builders.BuilderEntityFilterAttitude;
import com.hypixel.hytale.server.npc.corecomponents.utility.builders.BuilderSensorValueProviderWrapper;
import com.hypixel.hytale.server.npc.corecomponents.entity.builders.BuilderSensorSelf;
import com.hypixel.hytale.server.npc.corecomponents.world.builders.BuilderSensorBlockType;
import com.hypixel.hytale.server.npc.corecomponents.world.builders.BuilderSensorSearchRay;
import com.hypixel.hytale.server.npc.corecomponents.utility.builders.BuilderSensorAdjustPosition;
import com.hypixel.hytale.server.npc.corecomponents.timer.builders.BuilderSensorAlarm;
import com.hypixel.hytale.server.npc.corecomponents.interaction.builders.BuilderSensorInteractionContext;
import com.hypixel.hytale.server.npc.corecomponents.statemachine.builders.BuilderSensorIsBusy;
import com.hypixel.hytale.server.npc.corecomponents.world.builders.BuilderSensorInWater;
import com.hypixel.hytale.server.npc.corecomponents.movement.builders.BuilderSensorNav;
import com.hypixel.hytale.server.npc.corecomponents.world.builders.BuilderSensorCanPlace;
import com.hypixel.hytale.server.npc.corecomponents.audiovisual.builders.BuilderSensorAnimation;
import com.hypixel.hytale.server.npc.corecomponents.world.builders.BuilderSensorReadPosition;
import com.hypixel.hytale.server.npc.corecomponents.interaction.builders.BuilderSensorHasInteracted;
import com.hypixel.hytale.server.npc.corecomponents.interaction.builders.BuilderSensorCanInteract;
import com.hypixel.hytale.server.npc.corecomponents.utility.builders.BuilderSensorRandom;
import com.hypixel.hytale.server.npc.corecomponents.world.builders.BuilderSensorEntityEvent;
import com.hypixel.hytale.server.npc.corecomponents.world.builders.BuilderSensorBlockChange;
import com.hypixel.hytale.server.npc.corecomponents.world.builders.BuilderSensorBlock;
import com.hypixel.hytale.server.npc.corecomponents.world.builders.BuilderSensorWeather;
import com.hypixel.hytale.server.npc.corecomponents.world.builders.BuilderSensorPath;
import com.hypixel.hytale.server.npc.corecomponents.items.builders.BuilderSensorDroppedItem;
import com.hypixel.hytale.server.npc.corecomponents.utility.builders.BuilderSensorFlag;
import com.hypixel.hytale.server.npc.corecomponents.lifecycle.builders.BuilderSensorAge;
import com.hypixel.hytale.server.npc.corecomponents.world.builders.BuilderSensorLight;
import com.hypixel.hytale.server.npc.corecomponents.utility.builders.BuilderSensorSwitch;
import com.hypixel.hytale.server.npc.corecomponents.timer.builders.BuilderSensorTimer;
import com.hypixel.hytale.server.npc.corecomponents.entity.builders.BuilderSensorTarget;
import com.hypixel.hytale.server.npc.corecomponents.entity.builders.BuilderSensorCount;
import com.hypixel.hytale.server.npc.corecomponents.world.builders.BuilderSensorTime;
import com.hypixel.hytale.server.npc.corecomponents.world.builders.BuilderSensorLeash;
import com.hypixel.hytale.server.npc.corecomponents.movement.builders.BuilderSensorMotionController;
import com.hypixel.hytale.server.npc.corecomponents.entity.builders.BuilderSensorBeacon;
import com.hypixel.hytale.server.npc.corecomponents.entity.builders.BuilderSensorKill;
import com.hypixel.hytale.server.npc.corecomponents.combat.builders.BuilderSensorIsBackingAway;
import com.hypixel.hytale.server.npc.corecomponents.combat.builders.BuilderSensorDamage;
import com.hypixel.hytale.server.npc.corecomponents.utility.builders.BuilderSensorEval;
import com.hypixel.hytale.server.npc.corecomponents.movement.builders.BuilderSensorOnGround;
import com.hypixel.hytale.server.npc.corecomponents.movement.builders.BuilderSensorInAir;
import com.hypixel.hytale.server.npc.corecomponents.statemachine.builders.BuilderSensorState;
import com.hypixel.hytale.server.npc.corecomponents.entity.builders.BuilderSensorEntity;
import com.hypixel.hytale.server.npc.corecomponents.entity.builders.BuilderSensorPlayer;
import com.hypixel.hytale.server.npc.corecomponents.utility.builders.BuilderSensorNot;
import com.hypixel.hytale.server.npc.corecomponents.utility.builders.BuilderSensorOr;
import com.hypixel.hytale.server.npc.corecomponents.utility.builders.BuilderSensorAnd;
import com.hypixel.hytale.server.npc.corecomponents.utility.builders.BuilderSensorAny;
import com.hypixel.hytale.server.npc.corecomponents.entity.builders.BuilderActionSetStat;
import com.hypixel.hytale.server.npc.corecomponents.world.builders.BuilderActionResetPath;
import com.hypixel.hytale.server.npc.corecomponents.combat.builders.BuilderActionApplyEntityEffect;
import com.hypixel.hytale.server.npc.corecomponents.lifecycle.builders.BuilderActionRemove;
import com.hypixel.hytale.server.npc.corecomponents.lifecycle.builders.BuilderActionDie;
import com.hypixel.hytale.server.npc.corecomponents.world.builders.BuilderActionResetSearchRays;
import com.hypixel.hytale.server.npc.corecomponents.movement.builders.BuilderActionOverrideAltitude;
import com.hypixel.hytale.server.npc.corecomponents.statemachine.builders.BuilderActionToggleStateEvaluator;
import com.hypixel.hytale.server.npc.corecomponents.timer.builders.BuilderActionSetAlarm;
import com.hypixel.hytale.server.npc.corecomponents.audiovisual.builders.BuilderActionModelAttachment;
import com.hypixel.hytale.server.npc.corecomponents.entity.builders.BuilderActionIgnoreForAvoidance;
import com.hypixel.hytale.server.npc.corecomponents.movement.builders.BuilderActionRecomputePath;
import com.hypixel.hytale.server.npc.corecomponents.world.builders.BuilderActionPlaceBlock;
import com.hypixel.hytale.server.npc.corecomponents.world.builders.BuilderActionSetBlockToPlace;
import com.hypixel.hytale.server.npc.corecomponents.world.builders.BuilderActionStorePosition;
import com.hypixel.hytale.server.npc.corecomponents.interaction.builders.BuilderActionLockOnInteractionTarget;
import com.hypixel.hytale.server.npc.corecomponents.interaction.builders.BuilderActionSetInteractable;
import com.hypixel.hytale.server.npc.corecomponents.entity.builders.BuilderActionOverrideAttitude;
import com.hypixel.hytale.server.npc.corecomponents.world.builders.BuilderActionMakePath;
import com.hypixel.hytale.server.npc.corecomponents.world.builders.BuilderActionResetBlockSensors;
import com.hypixel.hytale.server.npc.corecomponents.world.builders.BuilderActionTriggerSpawners;
import com.hypixel.hytale.server.npc.corecomponents.entity.builders.BuilderActionNotify;
import com.hypixel.hytale.server.npc.corecomponents.statemachine.builders.BuilderActionParentState;
import com.hypixel.hytale.server.npc.corecomponents.utility.builders.BuilderActionResetInstructions;
import com.hypixel.hytale.server.npc.corecomponents.items.builders.BuilderActionPickUpItem;
import com.hypixel.hytale.server.npc.corecomponents.items.builders.BuilderActionDropItem;
import com.hypixel.hytale.server.npc.corecomponents.utility.builders.BuilderActionSetFlag;
import com.hypixel.hytale.server.npc.corecomponents.lifecycle.builders.BuilderActionRole;
import com.hypixel.hytale.server.npc.corecomponents.debug.builders.BuilderActionLog;
import com.hypixel.hytale.server.npc.corecomponents.debug.builders.BuilderActionTest;
import com.hypixel.hytale.server.npc.corecomponents.timer.builders.BuilderActionTimerRestart;
import com.hypixel.hytale.server.npc.corecomponents.timer.builders.BuilderActionTimerStop;
import com.hypixel.hytale.server.npc.corecomponents.timer.builders.BuilderActionTimerModify;
import com.hypixel.hytale.server.npc.corecomponents.timer.builders.BuilderActionTimerPause;
import com.hypixel.hytale.server.npc.corecomponents.timer.builders.BuilderActionTimerContinue;
import com.hypixel.hytale.server.npc.corecomponents.timer.builders.BuilderActionTimerStart;
import com.hypixel.hytale.server.npc.corecomponents.movement.builders.BuilderActionCrouch;
import com.hypixel.hytale.server.npc.corecomponents.audiovisual.builders.BuilderActionSpawnParticles;
import com.hypixel.hytale.server.npc.corecomponents.lifecycle.builders.BuilderActionDelayDespawn;
import com.hypixel.hytale.server.npc.corecomponents.audiovisual.builders.BuilderActionPlayAnimation;
import com.hypixel.hytale.server.npc.corecomponents.lifecycle.builders.BuilderActionDespawn;
import com.hypixel.hytale.server.npc.corecomponents.audiovisual.builders.BuilderActionPlaySound;
import com.hypixel.hytale.server.npc.corecomponents.world.builders.BuilderActionSetLeashPosition;
import com.hypixel.hytale.server.npc.corecomponents.entity.builders.BuilderActionBeacon;
import com.hypixel.hytale.server.npc.corecomponents.utility.builders.BuilderActionRandom;
import com.hypixel.hytale.server.npc.corecomponents.utility.builders.BuilderActionSequence;
import com.hypixel.hytale.server.npc.corecomponents.audiovisual.builders.BuilderActionDisplayName;
import com.hypixel.hytale.server.npc.corecomponents.items.builders.BuilderActionInventory;
import com.hypixel.hytale.server.npc.corecomponents.entity.builders.BuilderActionSetMarkedTarget;
import com.hypixel.hytale.server.npc.corecomponents.entity.builders.BuilderActionReleaseTarget;
import com.hypixel.hytale.server.npc.corecomponents.statemachine.builders.BuilderActionState;
import com.hypixel.hytale.server.npc.corecomponents.combat.builders.BuilderActionAttack;
import com.hypixel.hytale.server.npc.corecomponents.utility.builders.BuilderActionNothing;
import com.hypixel.hytale.server.npc.corecomponents.lifecycle.builders.BuilderActionSpawn;
import com.hypixel.hytale.server.npc.corecomponents.utility.builders.BuilderActionTimeout;
import com.hypixel.hytale.server.npc.corecomponents.audiovisual.builders.BuilderActionAppearance;
import com.hypixel.hytale.server.npc.corecomponents.utility.builders.BuilderHeadMotionNothing;
import com.hypixel.hytale.server.npc.corecomponents.timer.builders.BuilderHeadMotionTimer;
import com.hypixel.hytale.server.npc.corecomponents.utility.builders.BuilderHeadMotionSequence;
import com.hypixel.hytale.server.npc.corecomponents.world.builders.BuilderHeadMotionObserve;
import com.hypixel.hytale.server.npc.corecomponents.entity.builders.BuilderHeadMotionWatch;
import com.hypixel.hytale.server.npc.corecomponents.combat.builders.BuilderHeadMotionAim;
import com.hypixel.hytale.server.npc.corecomponents.combat.builders.BuilderBodyMotionAimCharge;
import com.hypixel.hytale.server.npc.corecomponents.movement.builders.BuilderBodyMotionMaintainDistance;
import com.hypixel.hytale.server.npc.corecomponents.movement.builders.BuilderBodyMotionMatchLook;
import com.hypixel.hytale.server.npc.corecomponents.movement.builders.BuilderBodyMotionLand;
import com.hypixel.hytale.server.npc.corecomponents.movement.builders.BuilderBodyMotionTeleport;
import com.hypixel.hytale.server.npc.corecomponents.debug.builders.BuilderBodyMotionTestProbe;
import com.hypixel.hytale.server.npc.corecomponents.movement.builders.BuilderBodyMotionTakeOff;
import com.hypixel.hytale.server.npc.corecomponents.world.builders.BuilderBodyMotionPath;
import com.hypixel.hytale.server.npc.corecomponents.movement.builders.BuilderBodyMotionLeave;
import com.hypixel.hytale.server.npc.corecomponents.movement.builders.BuilderBodyMotionFind;
import com.hypixel.hytale.server.npc.corecomponents.movement.builders.BuilderBodyMotionMoveAway;
import com.hypixel.hytale.server.npc.corecomponents.utility.builders.BuilderBodyMotionSequence;
import com.hypixel.hytale.server.npc.corecomponents.timer.builders.BuilderBodyMotionTimer;
import com.hypixel.hytale.server.npc.corecomponents.movement.builders.BuilderBodyMotionWanderInRect;
import com.hypixel.hytale.server.npc.corecomponents.movement.builders.BuilderBodyMotionWanderInCircle;
import com.hypixel.hytale.server.npc.corecomponents.movement.builders.BuilderBodyMotionWander;
import com.hypixel.hytale.server.npc.asset.builder.Builder;
import java.util.function.Supplier;
import com.hypixel.hytale.server.npc.corecomponents.utility.builders.BuilderBodyMotionNothing;
import com.hypixel.hytale.server.npc.instructions.ActionList;
import com.hypixel.hytale.builtin.path.path.TransientPathDefinition;
import com.hypixel.hytale.server.npc.instructions.Instruction;
import com.hypixel.hytale.server.npc.instructions.Sensor;
import com.hypixel.hytale.server.npc.instructions.Action;
import com.hypixel.hytale.server.npc.instructions.HeadMotion;
import com.hypixel.hytale.server.npc.instructions.BodyMotion;
import com.hypixel.hytale.server.npc.role.Role;
import com.hypixel.hytale.codec.schema.config.Schema;
import com.hypixel.hytale.component.dependency.Dependency;
import com.hypixel.hytale.event.EventRegistry;
import com.hypixel.hytale.component.ComponentRegistryProxy;
import com.hypixel.hytale.server.npc.systems.NPCVelocityInstructionSystem;
import com.hypixel.hytale.server.npc.systems.NPCDamageSystems;
import com.hypixel.hytale.server.npc.systems.NPCDeathSystems;
import com.hypixel.hytale.server.npc.systems.NPCSpatialSystem;
import com.hypixel.hytale.server.npc.systems.StepCleanupSystem;
import com.hypixel.hytale.server.npc.systems.MessageSupportSystem;
import com.hypixel.hytale.server.npc.systems.MovementStatesSystem;
import com.hypixel.hytale.server.npc.systems.ComputeVelocitySystem;
import com.hypixel.hytale.server.core.modules.entity.EntityModule;
import com.hypixel.hytale.server.npc.systems.TimerSystem;
import com.hypixel.hytale.server.npc.systems.SteeringSystem;
import com.hypixel.hytale.server.npc.systems.AvoidanceSystem;
import java.util.Set;
import com.hypixel.hytale.component.dependency.SystemDependency;
import com.hypixel.hytale.component.dependency.Order;
import com.hypixel.hytale.server.npc.systems.NPCPreTickSystem;
import com.hypixel.hytale.server.npc.systems.StateEvaluatorSystem;
import com.hypixel.hytale.server.npc.systems.FailedSpawnSystem;
import com.hypixel.hytale.server.npc.systems.NPCInteractionSystems;
import com.hypixel.hytale.server.npc.systems.PositionCacheSystems;
import com.hypixel.hytale.server.npc.systems.RoleSystems;
import com.hypixel.hytale.server.npc.systems.BalancingInitialisationSystem;
import com.hypixel.hytale.server.npc.systems.RoleBuilderSystem;
import com.hypixel.hytale.server.npc.systems.NPCSystems;
import com.hypixel.hytale.component.system.ISystem;
import com.hypixel.hytale.server.npc.systems.BlackboardSystems;
import com.hypixel.hytale.component.spatial.KDTree;
import com.hypixel.hytale.server.core.modules.migrations.Migration;
import java.nio.file.Path;
import com.hypixel.hytale.server.migrations.RenameSpawnMarkerMigration;
import com.hypixel.hytale.server.core.modules.migrations.MigrationModule;
import com.hypixel.hytale.server.core.modules.interaction.interaction.config.RootInteraction;
import com.hypixel.hytale.server.npc.interactions.SpawnNPCInteraction;
import com.hypixel.hytale.server.npc.interactions.UseNPCInteraction;
import com.hypixel.hytale.codec.builder.BuilderCodec;
import com.hypixel.hytale.server.npc.interactions.ContextualUseNPCInteraction;
import com.hypixel.hytale.server.core.modules.interaction.interaction.config.Interaction;
import com.hypixel.hytale.codec.DirectDecodeCodec;
import com.hypixel.hytale.server.npc.entities.NPCEntity;
import com.hypixel.hytale.server.core.modules.entitystats.asset.EntityStatType;
import com.hypixel.hytale.server.core.asset.type.responsecurve.config.ResponseCurve;
import com.hypixel.hytale.server.npc.decisionmaker.core.conditions.base.Condition;
import com.hypixel.hytale.assetstore.map.DefaultAssetMap;
import com.hypixel.hytale.server.core.asset.type.item.config.Item;
import com.hypixel.hytale.assetstore.AssetRegistry;
import java.util.function.Function;
import com.hypixel.hytale.assetstore.codec.AssetCodec;
import com.hypixel.hytale.assetstore.map.IndexedLookupTableAssetMap;
import com.hypixel.hytale.assetstore.AssetStore;
import com.hypixel.hytale.server.core.asset.HytaleAssetStore;
import com.hypixel.hytale.server.core.asset.GenerateSchemaEvent;
import com.hypixel.hytale.server.core.asset.AssetPackUnregisterEvent;
import com.hypixel.hytale.server.core.asset.AssetPackRegisterEvent;
import com.hypixel.hytale.common.util.FormatUtil;
import com.hypixel.hytale.assetstore.AssetPack;
import com.hypixel.hytale.server.core.asset.AssetModule;
import joptsimple.OptionSpec;
import com.hypixel.hytale.server.core.Options;
import java.util.logging.Level;
import com.hypixel.hytale.logger.HytaleLogger;
import com.hypixel.hytale.server.core.asset.LoadAssetEvent;
import com.hypixel.hytale.server.core.universe.world.path.WorldPathChangedEvent;
import com.hypixel.hytale.server.npc.config.balancing.BalanceAsset;
import com.hypixel.hytale.server.npc.config.ItemAttitudeGroup;
import com.hypixel.hytale.server.npc.config.AttitudeGroup;
import com.hypixel.hytale.assetstore.event.RemovedAssetsEvent;
import com.hypixel.hytale.builtin.tagset.config.NPCGroup;
import com.hypixel.hytale.server.core.asset.type.model.config.ModelAsset;
import com.hypixel.hytale.assetstore.event.LoadedAssetsEvent;
import com.hypixel.hytale.server.core.command.system.AbstractCommand;
import com.hypixel.hytale.server.npc.commands.NPCCommand;
import java.util.concurrent.locks.ReentrantLock;
import com.hypixel.hytale.server.core.plugin.JavaPluginInit;
import com.hypixel.hytale.server.npc.valuestore.ValueStore;
import com.hypixel.hytale.server.npc.decisionmaker.stateevaluator.StateEvaluator;
import com.hypixel.hytale.server.npc.components.Timers;
import com.hypixel.hytale.server.npc.components.FailedSpawnComponent;
import com.hypixel.hytale.server.npc.components.StepComponent;
import com.hypixel.hytale.server.npc.components.messaging.PlayerEntityEventSupport;
import com.hypixel.hytale.server.npc.components.messaging.NPCEntityEventSupport;
import com.hypixel.hytale.server.npc.components.messaging.PlayerBlockEventSupport;
import com.hypixel.hytale.server.npc.components.messaging.NPCBlockEventSupport;
import com.hypixel.hytale.server.npc.components.messaging.BeaconSupport;
import com.hypixel.hytale.server.npc.commands.NPCRunTestsCommand;
import com.hypixel.hytale.component.ComponentType;
import com.hypixel.hytale.component.Ref;
import com.hypixel.hytale.component.spatial.SpatialResource;
import com.hypixel.hytale.server.npc.navigation.AStarNodePoolProviderSimple;
import com.hypixel.hytale.server.npc.components.SortBufferProviderResource;
import com.hypixel.hytale.server.npc.systems.NewSpawnStartTickingSystem;
import com.hypixel.hytale.server.npc.systems.RoleChangeSystem;
import com.hypixel.hytale.server.npc.blackboard.view.combat.CombatViewSystems;
import com.hypixel.hytale.server.npc.blackboard.Blackboard;
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.math.vector.Vector3f;
import com.hypixel.hytale.server.npc.blackboard.view.attitude.ItemAttitudeMap;
import com.hypixel.hytale.server.npc.blackboard.view.attitude.AttitudeMap;
import com.hypixel.hytale.server.npc.util.SensorSupportBenchmark;
import javax.annotation.Nullable;
import com.hypixel.hytale.common.benchmark.TimeDistributionRecorder;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.atomic.AtomicInteger;
import com.hypixel.hytale.server.npc.asset.builder.BuilderManager;
import com.hypixel.hytale.server.npc.asset.builder.BuilderDescriptor;
import java.util.List;
import javax.annotation.Nonnull;
import com.hypixel.hytale.server.core.plugin.JavaPlugin;

public class NPCPlugin extends JavaPlugin
{
    @Nonnull
    public static String FACTORY_CLASS_ROLE;
    @Nonnull
    public static String FACTORY_CLASS_BODY_MOTION;
    @Nonnull
    public static String FACTORY_CLASS_HEAD_MOTION;
    @Nonnull
    public static String FACTORY_CLASS_ACTION;
    @Nonnull
    public static String FACTORY_CLASS_SENSOR;
    @Nonnull
    public static String FACTORY_CLASS_INSTRUCTION;
    @Nonnull
    public static String FACTORY_CLASS_TRANSIENT_PATH;
    @Nonnull
    public static String FACTORY_CLASS_ACTION_LIST;
    @Nonnull
    public static String ROLE_ASSETS_PATH;
    private static NPCPlugin instance;
    protected List<BuilderDescriptor> builderDescriptors;
    protected final BuilderManager builderManager;
    protected boolean validateBuilder;
    protected int maxBlackboardBlockCountPerType;
    protected boolean logFailingTestErrors;
    protected String[] presetCoverageTestNPCs;
    @Nonnull
    protected AtomicInteger pathChangeRevision;
    @Nonnull
    protected Lock benchmarkLock;
    @Nullable
    protected Int2ObjectMap<TimeDistributionRecorder> roleTickDistribution;
    @Nullable
    protected Int2ObjectMap<SensorSupportBenchmark> roleSensorSupportDistribution;
    @Nullable
    protected TimeDistributionRecorder roleTickDistributionAll;
    @Nullable
    protected SensorSupportBenchmark roleSensorSupportDistributionAll;
    protected boolean autoReload;
    private AttitudeMap attitudeMap;
    private ItemAttitudeMap itemAttitudeMap;
    private static final Vector3f NULL_ROTATION;
    public static final short PRIORITY_LOAD_NPC = -8;
    public static final short PRIORITY_SPAWN_VALIDATION = -7;
    private final Config<NPCConfig> config;
    private ResourceType<EntityStore, Blackboard> blackboardResourceType;
    private ResourceType<EntityStore, CombatViewSystems.CombatDataPool> combatDataPoolResourceType;
    private ResourceType<EntityStore, RoleChangeSystem.RoleChangeQueue> roleChangeQueueResourceType;
    private ResourceType<EntityStore, NewSpawnStartTickingSystem.QueueResource> newSpawnStartTickingQueueResourceType;
    private ResourceType<EntityStore, SortBufferProviderResource> sortBufferProviderResourceResourceType;
    private ResourceType<EntityStore, AStarNodePoolProviderSimple> aStarNodePoolProviderSimpleResourceType;
    private ResourceType<EntityStore, SpatialResource<Ref<EntityStore>, EntityStore>> npcSpatialResource;
    private ComponentType<EntityStore, CombatViewSystems.CombatData> combatDataComponentType;
    private ComponentType<EntityStore, NPCRunTestsCommand.NPCTestData> npcTestDataComponentType;
    private ComponentType<EntityStore, BeaconSupport> beaconSupportComponentType;
    private ComponentType<EntityStore, NPCBlockEventSupport> npcBlockEventSupportComponentType;
    private ComponentType<EntityStore, PlayerBlockEventSupport> playerBlockEventSupportComponentType;
    private ComponentType<EntityStore, NPCEntityEventSupport> npcEntityEventSupportComponentType;
    private ComponentType<EntityStore, PlayerEntityEventSupport> playerEntityEventSupportComponentType;
    private ComponentType<EntityStore, StepComponent> stepComponentType;
    private ComponentType<EntityStore, FailedSpawnComponent> failedSpawnComponentType;
    private ComponentType<EntityStore, Timers> timersComponentType;
    private ComponentType<EntityStore, StateEvaluator> stateEvaluatorComponentType;
    private ComponentType<EntityStore, ValueStore> valueStoreComponentType;
    static final /* synthetic */ boolean $assertionsDisabled;
    
    public static NPCPlugin get() {
        return NPCPlugin.instance;
    }
    
    public NPCPlugin(@Nonnull final JavaPluginInit init) {
        super(init);
        this.builderManager = new BuilderManager();
        this.maxBlackboardBlockCountPerType = 20;
        this.pathChangeRevision = new AtomicInteger(0);
        this.benchmarkLock = new ReentrantLock();
        this.config = this.withConfig("NPCModule", NPCConfig.CODEC);
    }
    
    @Override
    protected void setup() {
        NPCPlugin.instance = this;
        final ComponentRegistryProxy<EntityStore> entityStoreRegistry = this.getEntityStoreRegistry();
        final EventRegistry eventRegistry = this.getEventRegistry();
        this.getCommandRegistry().registerCommand(new NPCCommand());
        eventRegistry.register(LoadedAssetsEvent.class, ModelAsset.class, this::onModelsChanged);
        eventRegistry.register(LoadedAssetsEvent.class, NPCGroup.class, this::onNPCGroupsLoaded);
        eventRegistry.register(RemovedAssetsEvent.class, NPCGroup.class, this::onNPCGroupsRemoved);
        eventRegistry.register(LoadedAssetsEvent.class, AttitudeGroup.class, this::onAttitudeGroupsLoaded);
        eventRegistry.register(RemovedAssetsEvent.class, AttitudeGroup.class, this::onAttitudeGroupsRemoved);
        eventRegistry.register(LoadedAssetsEvent.class, ItemAttitudeGroup.class, this::onItemAttitudeGroupsLoaded);
        eventRegistry.register(RemovedAssetsEvent.class, ItemAttitudeGroup.class, this::onItemAttitudeGroupsRemoved);
        eventRegistry.register(LoadedAssetsEvent.class, BalanceAsset.class, NPCPlugin::onBalanceAssetsChanged);
        eventRegistry.register(RemovedAssetsEvent.class, BalanceAsset.class, NPCPlugin::onBalanceAssetsRemoved);
        eventRegistry.register(WorldPathChangedEvent.class, this::onPathChange);
        eventRegistry.register(AllNPCsLoadedEvent.class, this::onNPCsLoaded);
        eventRegistry.register((short)(-8), LoadAssetEvent.class, event -> {
            HytaleLogger.getLogger().at(Level.INFO).log("Loading NPC assets phase...");
            final long start = System.nanoTime();
            this.builderManager.setAutoReload(this.autoReload);
            final boolean validateAssets = Options.getOptionSet().has(Options.VALIDATE_ASSETS);
            final List<AssetPack> assetPacks = AssetModule.get().getAssetPacks();
            for (int i = 0; i < assetPacks.size(); ++i) {
                final boolean includeTests = i == 0;
                final boolean loadSucceeded = this.builderManager.loadBuilders(assetPacks.get(i), includeTests);
                if (!loadSucceeded) {
                    event.failed(validateAssets, "failed to validate npc's");
                }
            }
            HytaleLogger.getLogger().at(Level.INFO).log("Loading NPC assets phase completed! Boot time %s, Took %s", FormatUtil.nanosToString(System.nanoTime() - event.getBootStart()), FormatUtil.nanosToString(System.nanoTime() - start));
            return;
        });
        eventRegistry.register(AssetPackRegisterEvent.class, event -> this.builderManager.loadBuilders(event.getAssetPack(), false));
        eventRegistry.register(AssetPackUnregisterEvent.class, event -> this.builderManager.unloadBuilders(event.getAssetPack()));
        eventRegistry.register(GenerateSchemaEvent.class, this::onSchemaGenerate);
        AssetRegistry.register(((HytaleAssetStore.Builder)((HytaleAssetStore.Builder)((HytaleAssetStore.Builder)((HytaleAssetStore.Builder)HytaleAssetStore.builder(AttitudeGroup.class, new IndexedLookupTableAssetMap(AttitudeGroup[]::new)).setPath()).setCodec(AttitudeGroup.CODEC)).setKeyFunction(AttitudeGroup::getId)).setReplaceOnRemove(AttitudeGroup::new).loadsAfter(NPCGroup.class)).build());
        AssetRegistry.register(((HytaleAssetStore.Builder)((HytaleAssetStore.Builder)((HytaleAssetStore.Builder)((HytaleAssetStore.Builder)HytaleAssetStore.builder(ItemAttitudeGroup.class, new IndexedLookupTableAssetMap(ItemAttitudeGroup[]::new)).setPath()).setCodec(ItemAttitudeGroup.CODEC)).setKeyFunction(ItemAttitudeGroup::getId)).setReplaceOnRemove(ItemAttitudeGroup::new).loadsAfter(Item.class)).build());
        AssetRegistry.register(((HytaleAssetStore.Builder)((HytaleAssetStore.Builder)((HytaleAssetStore.Builder)HytaleAssetStore.builder(BalanceAsset.class, new DefaultAssetMap()).setPath()).setCodec(BalanceAsset.CODEC)).setKeyFunction(BalanceAsset::getId).loadsAfter(Condition.class)).build());
        AssetRegistry.register(((HytaleAssetStore.Builder)((HytaleAssetStore.Builder)((HytaleAssetStore.Builder)((HytaleAssetStore.Builder)HytaleAssetStore.builder(Condition.class, new IndexedLookupTableAssetMap(Condition[]::new)).setPath()).setCodec(Condition.CODEC)).setKeyFunction(Condition::getId)).setReplaceOnRemove(Condition::getAlwaysTrueFor).loadsAfter(ResponseCurve.class, NPCGroup.class, EntityStatType.class)).build());
        this.getEntityRegistry().registerEntity("NPC", NPCEntity.class, NPCEntity::new, NPCEntity.CODEC);
        Interaction.CODEC.register("ContextualUseNPC", ContextualUseNPCInteraction.class, ContextualUseNPCInteraction.CODEC);
        Interaction.CODEC.register("UseNPC", UseNPCInteraction.class, UseNPCInteraction.CODEC);
        Interaction.CODEC.register("SpawnNPC", SpawnNPCInteraction.class, SpawnNPCInteraction.CODEC);
        Interaction.getAssetStore().loadAssets("Hytale:Hytale", List.of(new UseNPCInteraction("*UseNPC")));
        RootInteraction.getAssetStore().loadAssets("Hytale:Hytale", List.of(UseNPCInteraction.DEFAULT_ROOT));
        MigrationModule.get().register("spawnMarkers", (Function<Path, Migration>)RenameSpawnMarkerMigration::new);
        this.setupNPCLoading();
        this.blackboardResourceType = entityStoreRegistry.registerResource(Blackboard.class, Blackboard::new);
        this.combatDataPoolResourceType = entityStoreRegistry.registerResource(CombatViewSystems.CombatDataPool.class, CombatViewSystems.CombatDataPool::new);
        this.roleChangeQueueResourceType = entityStoreRegistry.registerResource(RoleChangeSystem.RoleChangeQueue.class, RoleChangeSystem.RoleChangeQueue::new);
        this.newSpawnStartTickingQueueResourceType = entityStoreRegistry.registerResource(NewSpawnStartTickingSystem.QueueResource.class, NewSpawnStartTickingSystem.QueueResource::new);
        this.sortBufferProviderResourceResourceType = entityStoreRegistry.registerResource(SortBufferProviderResource.class, SortBufferProviderResource::new);
        this.aStarNodePoolProviderSimpleResourceType = entityStoreRegistry.registerResource(AStarNodePoolProviderSimple.class, AStarNodePoolProviderSimple::new);
        this.npcSpatialResource = entityStoreRegistry.registerSpatialResource(() -> new KDTree(Ref::isValid));
        this.combatDataComponentType = entityStoreRegistry.registerComponent(CombatViewSystems.CombatData.class, CombatViewSystems.CombatData::new);
        this.npcTestDataComponentType = entityStoreRegistry.registerComponent(NPCRunTestsCommand.NPCTestData.class, NPCRunTestsCommand.NPCTestData::new);
        this.beaconSupportComponentType = entityStoreRegistry.registerComponent(BeaconSupport.class, BeaconSupport::new);
        this.npcBlockEventSupportComponentType = entityStoreRegistry.registerComponent(NPCBlockEventSupport.class, NPCBlockEventSupport::new);
        this.playerBlockEventSupportComponentType = entityStoreRegistry.registerComponent(PlayerBlockEventSupport.class, PlayerBlockEventSupport::new);
        this.npcEntityEventSupportComponentType = entityStoreRegistry.registerComponent(NPCEntityEventSupport.class, NPCEntityEventSupport::new);
        this.playerEntityEventSupportComponentType = entityStoreRegistry.registerComponent(PlayerEntityEventSupport.class, PlayerEntityEventSupport::new);
        this.stepComponentType = entityStoreRegistry.registerComponent(StepComponent.class, () -> {
            throw new UnsupportedOperationException("Not implemented");
        });
        this.failedSpawnComponentType = entityStoreRegistry.registerComponent(FailedSpawnComponent.class, FailedSpawnComponent::new);
        this.timersComponentType = entityStoreRegistry.registerComponent(Timers.class, () -> {
            throw new UnsupportedOperationException("Not implemented");
        });
        this.stateEvaluatorComponentType = entityStoreRegistry.registerComponent(StateEvaluator.class, () -> {
            throw new UnsupportedOperationException("Not implemented");
        });
        this.valueStoreComponentType = entityStoreRegistry.registerComponent(ValueStore.class, () -> {
            throw new UnsupportedOperationException("Not implemented");
        });
        final ComponentType<EntityStore, NPCEntity> npcComponentType = NPCEntity.getComponentType();
        entityStoreRegistry.registerSystem(new BlackboardSystems.InitSystem(this.blackboardResourceType));
        entityStoreRegistry.registerSystem(new BlackboardSystems.TickingSystem(this.blackboardResourceType));
        entityStoreRegistry.registerSystem(new BlackboardSystems.DamageBlockEventSystem());
        entityStoreRegistry.registerSystem(new BlackboardSystems.BreakBlockEventSystem());
        entityStoreRegistry.registerSystem(new CombatViewSystems.Ensure(this.combatDataComponentType));
        entityStoreRegistry.registerSystem(new CombatViewSystems.EntityRemoved(this.combatDataComponentType, this.combatDataPoolResourceType));
        entityStoreRegistry.registerSystem(new CombatViewSystems.Ticking(this.combatDataComponentType, this.combatDataPoolResourceType));
        entityStoreRegistry.registerSystem((ISystem<EntityStore>)new NPCSystems.ModelChangeSystem());
        entityStoreRegistry.registerSystem(new RoleBuilderSystem());
        entityStoreRegistry.registerSystem(new BalancingInitialisationSystem());
        entityStoreRegistry.registerSystem(new RoleSystems.RoleActivateSystem(npcComponentType));
        entityStoreRegistry.registerSystem(new PositionCacheSystems.RoleActivateSystem(npcComponentType, this.stateEvaluatorComponentType));
        entityStoreRegistry.registerSystem(new NPCInteractionSystems.AddSimulationManagerSystem(npcComponentType));
        entityStoreRegistry.registerSystem(new NPCInteractionSystems.TickHeldInteractionsSystem(npcComponentType));
        entityStoreRegistry.registerSystem(new FailedSpawnSystem());
        entityStoreRegistry.registerSystem(new NPCSystems.AddedSystem(npcComponentType));
        entityStoreRegistry.registerSystem(new NPCSystems.AddedFromExternalSystem(npcComponentType));
        entityStoreRegistry.registerSystem(new NPCSystems.AddedFromWorldGenSystem());
        entityStoreRegistry.registerSystem(new NPCSystems.AddSpawnEntityEffectSystem(npcComponentType));
        entityStoreRegistry.registerSystem(new RoleSystems.BehaviourTickSystem(npcComponentType, this.stepComponentType));
        entityStoreRegistry.registerSystem(new RoleSystems.PreBehaviourSupportTickSystem(npcComponentType));
        entityStoreRegistry.registerSystem(new StateEvaluatorSystem(this.stateEvaluatorComponentType, npcComponentType));
        entityStoreRegistry.registerSystem(new PositionCacheSystems.UpdateSystem(npcComponentType, this.npcSpatialResource));
        entityStoreRegistry.registerSystem(new NPCPreTickSystem(npcComponentType));
        final Set<Dependency<EntityStore>> postBehaviourDependency = (Set<Dependency<EntityStore>>)Set.of(new SystemDependency(Order.AFTER, RoleSystems.PostBehaviourSupportTickSystem.class));
        entityStoreRegistry.registerSystem(new AvoidanceSystem(npcComponentType));
        entityStoreRegistry.registerSystem(new SteeringSystem(npcComponentType));
        entityStoreRegistry.registerSystem(new RoleSystems.PostBehaviourSupportTickSystem(npcComponentType));
        entityStoreRegistry.registerSystem(new RoleSystems.RoleDebugSystem(npcComponentType, postBehaviourDependency));
        entityStoreRegistry.registerSystem(new TimerSystem(this.timersComponentType, postBehaviourDependency));
        entityStoreRegistry.registerSystem(new ComputeVelocitySystem(npcComponentType, EntityModule.get().getVelocityComponentType(), postBehaviourDependency));
        entityStoreRegistry.registerSystem(new MovementStatesSystem(npcComponentType, EntityModule.get().getVelocityComponentType(), EntityModule.get().getMovementStatesComponentType()));
        entityStoreRegistry.registerSystem(new MessageSupportSystem.BeaconSystem(this.beaconSupportComponentType, postBehaviourDependency));
        entityStoreRegistry.registerSystem(new MessageSupportSystem.NPCBlockEventSystem(this.npcBlockEventSupportComponentType, postBehaviourDependency));
        entityStoreRegistry.registerSystem(new MessageSupportSystem.PlayerBlockEventSystem(this.playerBlockEventSupportComponentType, postBehaviourDependency));
        entityStoreRegistry.registerSystem(new MessageSupportSystem.NPCEntityEventSystem(this.npcEntityEventSupportComponentType, postBehaviourDependency));
        entityStoreRegistry.registerSystem(new MessageSupportSystem.PlayerEntityEventSystem(this.playerEntityEventSupportComponentType, postBehaviourDependency));
        entityStoreRegistry.registerSystem(new StepCleanupSystem(this.stepComponentType));
        entityStoreRegistry.registerSystem(new NewSpawnStartTickingSystem(this.newSpawnStartTickingQueueResourceType));
        entityStoreRegistry.registerSystem(new RoleChangeSystem(this.roleChangeQueueResourceType, this.beaconSupportComponentType, this.playerBlockEventSupportComponentType, this.npcBlockEventSupportComponentType, this.playerEntityEventSupportComponentType, this.npcEntityEventSupportComponentType, this.timersComponentType, this.stateEvaluatorComponentType, this.valueStoreComponentType));
        entityStoreRegistry.registerSystem(new NPCSpatialSystem(this.npcSpatialResource));
        entityStoreRegistry.registerSystem((ISystem<EntityStore>)new NPCDeathSystems.NPCKillsEntitySystem());
        entityStoreRegistry.registerSystem((ISystem<EntityStore>)new NPCDeathSystems.EntityViewSystem());
        entityStoreRegistry.registerSystem(new NPCDamageSystems.FilterDamageSystem());
        entityStoreRegistry.registerSystem(new NPCDamageSystems.DamageReceivedSystem());
        entityStoreRegistry.registerSystem(new NPCDamageSystems.DamageDealtSystem());
        entityStoreRegistry.registerSystem(new NPCDamageSystems.DamageReceivedEventViewSystem());
        entityStoreRegistry.registerSystem((ISystem<EntityStore>)new NPCDamageSystems.DropDeathItems());
        entityStoreRegistry.registerSystem((ISystem<EntityStore>)new NPCSystems.OnTeleportSystem());
        entityStoreRegistry.registerSystem((ISystem<EntityStore>)new NPCSystems.OnDeathSystem());
        entityStoreRegistry.registerSystem(new NPCSystems.LegacyWorldGenId());
        entityStoreRegistry.registerSystem(new NPCSystems.KillFeedKillerEventSystem());
        entityStoreRegistry.registerSystem(new NPCSystems.KillFeedDecedentEventSystem());
        entityStoreRegistry.registerSystem(new NPCSystems.PrefabPlaceEntityEventSystem());
        entityStoreRegistry.registerSystem(new NPCVelocityInstructionSystem());
        this.getEntityStoreRegistry().registerSystem(new NPCEntityRegenerateStatsSystem());
    }
    
    public void onSchemaGenerate(@Nonnull final GenerateSchemaEvent event) {
        final Schema schema = this.builderManager.generateSchema(event.getContext());
        event.addSchema("NPCRole.json", schema);
        event.addSchemaLink("NPCRole", List.of("NPC/Roles/*.json", "NPC/Roles/**/*.json"), null);
        final Schema.HytaleMetadata hytale = schema.getHytale();
        hytale.setPath("NPC/Roles");
        hytale.setExtension(".json");
        schema.setId("NPCRole.json");
        schema.setTitle("NPCRole");
    }
    
    @Override
    protected void start() {
        final NPCConfig config = this.config.get();
        if (config.isGenerateDescriptors()) {
            this.generateDescriptors();
            if (config.isGenerateDescriptorsFile()) {
                this.saveDescriptors();
            }
        }
    }
    
    public ResourceType<EntityStore, Blackboard> getBlackboardResourceType() {
        return this.blackboardResourceType;
    }
    
    public ResourceType<EntityStore, CombatViewSystems.CombatDataPool> getCombatDataPoolResourceType() {
        return this.combatDataPoolResourceType;
    }
    
    public ResourceType<EntityStore, RoleChangeSystem.RoleChangeQueue> getRoleChangeQueueResourceType() {
        return this.roleChangeQueueResourceType;
    }
    
    public ResourceType<EntityStore, NewSpawnStartTickingSystem.QueueResource> getNewSpawnStartTickingQueueResourceType() {
        return this.newSpawnStartTickingQueueResourceType;
    }
    
    public ResourceType<EntityStore, SortBufferProviderResource> getSortBufferProviderResourceResourceType() {
        return this.sortBufferProviderResourceResourceType;
    }
    
    public ResourceType<EntityStore, AStarNodePoolProviderSimple> getAStarNodePoolProviderSimpleResourceType() {
        return this.aStarNodePoolProviderSimpleResourceType;
    }
    
    public ResourceType<EntityStore, SpatialResource<Ref<EntityStore>, EntityStore>> getNpcSpatialResource() {
        return this.npcSpatialResource;
    }
    
    public ComponentType<EntityStore, CombatViewSystems.CombatData> getCombatDataComponentType() {
        return this.combatDataComponentType;
    }
    
    public ComponentType<EntityStore, NPCRunTestsCommand.NPCTestData> getNpcTestDataComponentType() {
        return this.npcTestDataComponentType;
    }
    
    public ComponentType<EntityStore, BeaconSupport> getBeaconSupportComponentType() {
        return this.beaconSupportComponentType;
    }
    
    public ComponentType<EntityStore, NPCBlockEventSupport> getNpcBlockEventSupportComponentType() {
        return this.npcBlockEventSupportComponentType;
    }
    
    public ComponentType<EntityStore, PlayerBlockEventSupport> getPlayerBlockEventSupportComponentType() {
        return this.playerBlockEventSupportComponentType;
    }
    
    public ComponentType<EntityStore, NPCEntityEventSupport> getNpcEntityEventSupportComponentType() {
        return this.npcEntityEventSupportComponentType;
    }
    
    public ComponentType<EntityStore, PlayerEntityEventSupport> getPlayerEntityEventSupportComponentType() {
        return this.playerEntityEventSupportComponentType;
    }
    
    public ComponentType<EntityStore, StepComponent> getStepComponentType() {
        return this.stepComponentType;
    }
    
    public ComponentType<EntityStore, FailedSpawnComponent> getFailedSpawnComponentType() {
        return this.failedSpawnComponentType;
    }
    
    public ComponentType<EntityStore, Timers> getTimersComponentType() {
        return this.timersComponentType;
    }
    
    public ComponentType<EntityStore, StateEvaluator> getStateEvaluatorComponentType() {
        return this.stateEvaluatorComponentType;
    }
    
    public ComponentType<EntityStore, ValueStore> getValueStoreComponentType() {
        return this.valueStoreComponentType;
    }
    
    public void setupNPCLoading() {
        this.builderManager.addCategory(NPCPlugin.FACTORY_CLASS_ROLE, Role.class);
        this.builderManager.addCategory(NPCPlugin.FACTORY_CLASS_BODY_MOTION, BodyMotion.class);
        this.builderManager.addCategory(NPCPlugin.FACTORY_CLASS_HEAD_MOTION, HeadMotion.class);
        this.builderManager.addCategory(NPCPlugin.FACTORY_CLASS_ACTION, Action.class);
        this.builderManager.addCategory(NPCPlugin.FACTORY_CLASS_SENSOR, Sensor.class);
        this.builderManager.addCategory(NPCPlugin.FACTORY_CLASS_INSTRUCTION, Instruction.class);
        this.builderManager.addCategory(NPCPlugin.FACTORY_CLASS_TRANSIENT_PATH, TransientPathDefinition.class);
        this.builderManager.addCategory(NPCPlugin.FACTORY_CLASS_ACTION_LIST, ActionList.class);
        this.registerCoreFactories();
        this.registerCoreComponentType("Nothing", (Supplier<Builder<Object>>)BuilderBodyMotionNothing::new).registerCoreComponentType("Wander", (Supplier<Builder<Object>>)BuilderBodyMotionWander::new).registerCoreComponentType("WanderInCircle", (Supplier<Builder<Object>>)BuilderBodyMotionWanderInCircle::new).registerCoreComponentType("WanderInRect", (Supplier<Builder<Object>>)BuilderBodyMotionWanderInRect::new).registerCoreComponentType("Timer", (Supplier<Builder<Object>>)BuilderBodyMotionTimer::new).registerCoreComponentType("Sequence", (Supplier<Builder<Object>>)BuilderBodyMotionSequence::new).registerCoreComponentType("Flee", (Supplier<Builder<Object>>)BuilderBodyMotionMoveAway::new).registerCoreComponentType("Seek", (Supplier<Builder<Object>>)BuilderBodyMotionFind::new).registerCoreComponentType("Leave", (Supplier<Builder<Object>>)BuilderBodyMotionLeave::new).registerCoreComponentType("Path", (Supplier<Builder<Object>>)BuilderBodyMotionPath::new).registerCoreComponentType("TakeOff", (Supplier<Builder<Object>>)BuilderBodyMotionTakeOff::new).registerCoreComponentType("TestProbe", (Supplier<Builder<Object>>)BuilderBodyMotionTestProbe::new).registerCoreComponentType("Teleport", (Supplier<Builder<Object>>)BuilderBodyMotionTeleport::new).registerCoreComponentType("Land", (Supplier<Builder<Object>>)BuilderBodyMotionLand::new).registerCoreComponentType("MatchLook", (Supplier<Builder<Object>>)BuilderBodyMotionMatchLook::new).registerCoreComponentType("MaintainDistance", (Supplier<Builder<Object>>)BuilderBodyMotionMaintainDistance::new).registerCoreComponentType("AimCharge", (Supplier<Builder<Object>>)BuilderBodyMotionAimCharge::new);
        this.registerCoreComponentType("Aim", (Supplier<Builder<Object>>)BuilderHeadMotionAim::new).registerCoreComponentType("Watch", (Supplier<Builder<Object>>)BuilderHeadMotionWatch::new).registerCoreComponentType("Observe", (Supplier<Builder<Object>>)BuilderHeadMotionObserve::new).registerCoreComponentType("Sequence", (Supplier<Builder<Object>>)BuilderHeadMotionSequence::new).registerCoreComponentType("Timer", (Supplier<Builder<Object>>)BuilderHeadMotionTimer::new).registerCoreComponentType("Nothing", (Supplier<Builder<Object>>)BuilderHeadMotionNothing::new);
        this.registerCoreComponentType("Appearance", (Supplier<Builder<Object>>)BuilderActionAppearance::new).registerCoreComponentType("Timeout", (Supplier<Builder<Object>>)BuilderActionTimeout::new).registerCoreComponentType("Spawn", (Supplier<Builder<Object>>)BuilderActionSpawn::new).registerCoreComponentType("Nothing", (Supplier<Builder<Object>>)BuilderActionNothing::new).registerCoreComponentType("Attack", (Supplier<Builder<Object>>)BuilderActionAttack::new).registerCoreComponentType("State", (Supplier<Builder<Object>>)BuilderActionState::new).registerCoreComponentType("ReleaseTarget", (Supplier<Builder<Object>>)BuilderActionReleaseTarget::new).registerCoreComponentType("SetMarkedTarget", (Supplier<Builder<Object>>)BuilderActionSetMarkedTarget::new).registerCoreComponentType("Inventory", (Supplier<Builder<Object>>)BuilderActionInventory::new).registerCoreComponentType("DisplayName", (Supplier<Builder<Object>>)BuilderActionDisplayName::new).registerCoreComponentType("Sequence", (Supplier<Builder<Object>>)BuilderActionSequence::new).registerCoreComponentType("Random", (Supplier<Builder<Object>>)BuilderActionRandom::new).registerCoreComponentType("Beacon", (Supplier<Builder<Object>>)BuilderActionBeacon::new).registerCoreComponentType("SetLeashPosition", (Supplier<Builder<Object>>)BuilderActionSetLeashPosition::new).registerCoreComponentType("PlaySound", (Supplier<Builder<Object>>)BuilderActionPlaySound::new).registerCoreComponentType("Despawn", (Supplier<Builder<Object>>)BuilderActionDespawn::new).registerCoreComponentType("PlayAnimation", (Supplier<Builder<Object>>)BuilderActionPlayAnimation::new).registerCoreComponentType("DelayDespawn", (Supplier<Builder<Object>>)BuilderActionDelayDespawn::new).registerCoreComponentType("SpawnParticles", (Supplier<Builder<Object>>)BuilderActionSpawnParticles::new).registerCoreComponentType("Crouch", (Supplier<Builder<Object>>)BuilderActionCrouch::new).registerCoreComponentType("TimerStart", (Supplier<Builder<Object>>)BuilderActionTimerStart::new).registerCoreComponentType("TimerContinue", (Supplier<Builder<Object>>)BuilderActionTimerContinue::new).registerCoreComponentType("TimerPause", (Supplier<Builder<Object>>)BuilderActionTimerPause::new).registerCoreComponentType("TimerModify", (Supplier<Builder<Object>>)BuilderActionTimerModify::new).registerCoreComponentType("TimerStop", (Supplier<Builder<Object>>)BuilderActionTimerStop::new).registerCoreComponentType("TimerRestart", (Supplier<Builder<Object>>)BuilderActionTimerRestart::new).registerCoreComponentType("Test", (Supplier<Builder<Object>>)BuilderActionTest::new).registerCoreComponentType("Log", (Supplier<Builder<Object>>)BuilderActionLog::new).registerCoreComponentType("Role", (Supplier<Builder<Object>>)BuilderActionRole::new).registerCoreComponentType("SetFlag", (Supplier<Builder<Object>>)BuilderActionSetFlag::new).registerCoreComponentType("DropItem", (Supplier<Builder<Object>>)BuilderActionDropItem::new).registerCoreComponentType("PickUpItem", (Supplier<Builder<Object>>)BuilderActionPickUpItem::new).registerCoreComponentType("ResetInstructions", (Supplier<Builder<Object>>)BuilderActionResetInstructions::new).registerCoreComponentType("ParentState", (Supplier<Builder<Object>>)BuilderActionParentState::new).registerCoreComponentType("Notify", (Supplier<Builder<Object>>)BuilderActionNotify::new).registerCoreComponentType("TriggerSpawners", (Supplier<Builder<Object>>)BuilderActionTriggerSpawners::new).registerCoreComponentType("ResetBlockSensors", (Supplier<Builder<Object>>)BuilderActionResetBlockSensors::new).registerCoreComponentType("MakePath", (Supplier<Builder<Object>>)BuilderActionMakePath::new).registerCoreComponentType("OverrideAttitude", (Supplier<Builder<Object>>)BuilderActionOverrideAttitude::new).registerCoreComponentType("SetInteractable", (Supplier<Builder<Object>>)BuilderActionSetInteractable::new).registerCoreComponentType("LockOnInteractionTarget", (Supplier<Builder<Object>>)BuilderActionLockOnInteractionTarget::new).registerCoreComponentType("StorePosition", (Supplier<Builder<Object>>)BuilderActionStorePosition::new).registerCoreComponentType("SetBlockToPlace", (Supplier<Builder<Object>>)BuilderActionSetBlockToPlace::new).registerCoreComponentType("PlaceBlock", (Supplier<Builder<Object>>)BuilderActionPlaceBlock::new).registerCoreComponentType("RecomputePath", (Supplier<Builder<Object>>)BuilderActionRecomputePath::new).registerCoreComponentType("IgnoreForAvoidance", (Supplier<Builder<Object>>)BuilderActionIgnoreForAvoidance::new).registerCoreComponentType("ModelAttachment", (Supplier<Builder<Object>>)BuilderActionModelAttachment::new).registerCoreComponentType("SetAlarm", (Supplier<Builder<Object>>)BuilderActionSetAlarm::new).registerCoreComponentType("ToggleStateEvaluator", (Supplier<Builder<Object>>)BuilderActionToggleStateEvaluator::new).registerCoreComponentType("OverrideAltitude", (Supplier<Builder<Object>>)BuilderActionOverrideAltitude::new).registerCoreComponentType("ResetSearchRays", (Supplier<Builder<Object>>)BuilderActionResetSearchRays::new).registerCoreComponentType("Die", (Supplier<Builder<Object>>)BuilderActionDie::new).registerCoreComponentType("Remove", (Supplier<Builder<Object>>)BuilderActionRemove::new).registerCoreComponentType("ApplyEntityEffect", (Supplier<Builder<Object>>)BuilderActionApplyEntityEffect::new).registerCoreComponentType("ResetPath", (Supplier<Builder<Object>>)BuilderActionResetPath::new).registerCoreComponentType("SetStat", (Supplier<Builder<Object>>)BuilderActionSetStat::new);
        this.registerCoreComponentType("Any", (Supplier<Builder<Object>>)BuilderSensorAny::new).registerCoreComponentType("And", (Supplier<Builder<Object>>)BuilderSensorAnd::new).registerCoreComponentType("Or", (Supplier<Builder<Object>>)BuilderSensorOr::new).registerCoreComponentType("Not", (Supplier<Builder<Object>>)BuilderSensorNot::new).registerCoreComponentType("Player", (Supplier<Builder<Object>>)BuilderSensorPlayer::new).registerCoreComponentType("Mob", (Supplier<Builder<Object>>)BuilderSensorEntity::new).registerCoreComponentType("State", (Supplier<Builder<Object>>)BuilderSensorState::new).registerCoreComponentType("InAir", (Supplier<Builder<Object>>)BuilderSensorInAir::new).registerCoreComponentType("OnGround", (Supplier<Builder<Object>>)BuilderSensorOnGround::new).registerCoreComponentType("Eval", (Supplier<Builder<Object>>)BuilderSensorEval::new).registerCoreComponentType("Damage", (Supplier<Builder<Object>>)BuilderSensorDamage::new).registerCoreComponentType("IsBackingAway", (Supplier<Builder<Object>>)BuilderSensorIsBackingAway::new).registerCoreComponentType("Kill", (Supplier<Builder<Object>>)BuilderSensorKill::new).registerCoreComponentType("Beacon", (Supplier<Builder<Object>>)BuilderSensorBeacon::new).registerCoreComponentType("MotionController", (Supplier<Builder<Object>>)BuilderSensorMotionController::new).registerCoreComponentType("Leash", (Supplier<Builder<Object>>)BuilderSensorLeash::new).registerCoreComponentType("Time", (Supplier<Builder<Object>>)BuilderSensorTime::new).registerCoreComponentType("Count", (Supplier<Builder<Object>>)BuilderSensorCount::new).registerCoreComponentType("Target", (Supplier<Builder<Object>>)BuilderSensorTarget::new).registerCoreComponentType("Timer", (Supplier<Builder<Object>>)BuilderSensorTimer::new).registerCoreComponentType("Switch", (Supplier<Builder<Object>>)BuilderSensorSwitch::new).registerCoreComponentType("Light", (Supplier<Builder<Object>>)BuilderSensorLight::new).registerCoreComponentType("Age", (Supplier<Builder<Object>>)BuilderSensorAge::new).registerCoreComponentType("Flag", (Supplier<Builder<Object>>)BuilderSensorFlag::new).registerCoreComponentType("DroppedItem", (Supplier<Builder<Object>>)BuilderSensorDroppedItem::new).registerCoreComponentType("Path", (Supplier<Builder<Object>>)BuilderSensorPath::new).registerCoreComponentType("Weather", (Supplier<Builder<Object>>)BuilderSensorWeather::new).registerCoreComponentType("Block", (Supplier<Builder<Object>>)BuilderSensorBlock::new).registerCoreComponentType("BlockChange", (Supplier<Builder<Object>>)BuilderSensorBlockChange::new).registerCoreComponentType("EntityEvent", (Supplier<Builder<Object>>)BuilderSensorEntityEvent::new).registerCoreComponentType("Random", (Supplier<Builder<Object>>)BuilderSensorRandom::new).registerCoreComponentType("CanInteract", (Supplier<Builder<Object>>)BuilderSensorCanInteract::new).registerCoreComponentType("HasInteracted", (Supplier<Builder<Object>>)BuilderSensorHasInteracted::new).registerCoreComponentType("ReadPosition", (Supplier<Builder<Object>>)BuilderSensorReadPosition::new).registerCoreComponentType("Animation", (Supplier<Builder<Object>>)BuilderSensorAnimation::new).registerCoreComponentType("CanPlaceBlock", (Supplier<Builder<Object>>)BuilderSensorCanPlace::new).registerCoreComponentType("Nav", (Supplier<Builder<Object>>)BuilderSensorNav::new).registerCoreComponentType("InWater", (Supplier<Builder<Object>>)BuilderSensorInWater::new).registerCoreComponentType("IsBusy", (Supplier<Builder<Object>>)BuilderSensorIsBusy::new).registerCoreComponentType("InteractionContext", (Supplier<Builder<Object>>)BuilderSensorInteractionContext::new).registerCoreComponentType("Alarm", (Supplier<Builder<Object>>)BuilderSensorAlarm::new).registerCoreComponentType("AdjustPosition", (Supplier<Builder<Object>>)BuilderSensorAdjustPosition::new).registerCoreComponentType("SearchRay", (Supplier<Builder<Object>>)BuilderSensorSearchRay::new).registerCoreComponentType("BlockType", (Supplier<Builder<Object>>)BuilderSensorBlockType::new).registerCoreComponentType("Self", (Supplier<Builder<Object>>)BuilderSensorSelf::new).registerCoreComponentType("ValueProviderWrapper", (Supplier<Builder<Object>>)BuilderSensorValueProviderWrapper::new);
        this.registerCoreComponentType("Attitude", (Supplier<Builder<Object>>)BuilderEntityFilterAttitude::new).registerCoreComponentType("LineOfSight", (Supplier<Builder<Object>>)BuilderEntityFilterLineOfSight::new).registerCoreComponentType("HeightDifference", (Supplier<Builder<Object>>)BuilderEntityFilterHeightDifference::new).registerCoreComponentType("ViewSector", (Supplier<Builder<Object>>)BuilderEntityFilterViewSector::new).registerCoreComponentType("Combat", (Supplier<Builder<Object>>)BuilderEntityFilterCombat::new).registerCoreComponentType("ItemInHand", (Supplier<Builder<Object>>)BuilderEntityFilterItemInHand::new).registerCoreComponentType("NPCGroup", (Supplier<Builder<Object>>)BuilderEntityFilterNPCGroup::new).registerCoreComponentType("MovementState", (Supplier<Builder<Object>>)BuilderEntityFilterMovementState::new).registerCoreComponentType("SpotsMe", (Supplier<Builder<Object>>)BuilderEntityFilterSpotsMe::new).registerCoreComponentType("StandingOnBlock", (Supplier<Builder<Object>>)BuilderEntityFilterStandingOnBlock::new).registerCoreComponentType("Stat", (Supplier<Builder<Object>>)BuilderEntityFilterStat::new).registerCoreComponentType("Inventory", (Supplier<Builder<Object>>)BuilderEntityFilterInventory::new).registerCoreComponentType("Not", (Supplier<Builder<Object>>)BuilderEntityFilterNot::new).registerCoreComponentType("And", (Supplier<Builder<Object>>)BuilderEntityFilterAnd::new).registerCoreComponentType("Or", (Supplier<Builder<Object>>)BuilderEntityFilterOr::new).registerCoreComponentType("Altitude", (Supplier<Builder<Object>>)BuilderEntityFilterAltitude::new).registerCoreComponentType("InsideBlock", (Supplier<Builder<Object>>)BuilderEntityFilterInsideBlock::new);
        this.registerCoreComponentType("Attitude", (Supplier<Builder<Object>>)BuilderSensorEntityPrioritiserAttitude::new);
        final NPCConfig config = this.config.get();
        this.autoReload = config.isAutoReload();
        this.validateBuilder = config.isValidateBuilder();
        this.maxBlackboardBlockCountPerType = config.getMaxBlackboardBlockType();
        this.logFailingTestErrors = config.isLogFailingTestErrors();
        this.presetCoverageTestNPCs = config.getPresetCoverageTestNPCs();
    }
    
    public String[] getPresetCoverageTestNPCs() {
        return this.presetCoverageTestNPCs;
    }
    
    @Nullable
    public Pair<Ref<EntityStore>, INonPlayerCharacter> spawnNPC(@Nonnull final Store<EntityStore> store, @Nonnull final String npcType, @Nullable final String groupType, @Nonnull final Vector3d position, @Nonnull final Vector3f rotation) {
        final int roleIndex = this.getIndex(npcType);
        if (roleIndex < 0) {
            return null;
        }
        final Pair<Ref<EntityStore>, NPCEntity> npcPair = this.spawnEntity(store, roleIndex, position, rotation, null, null);
        final FlockAsset flockDefinition = (groupType != null) ? FlockAsset.getAssetMap().getAsset(groupType) : null;
        if (npcPair != null) {
            final Ref<EntityStore> npcRef = npcPair.first();
            final NPCEntity npcComponent = npcPair.second();
            FlockPlugin.trySpawnFlock(npcRef, npcComponent, store, roleIndex, position, rotation, flockDefinition, null);
            return (Pair<Ref<EntityStore>, INonPlayerCharacter>)Pair.of(npcPair.first(), (INonPlayerCharacter)npcPair.second());
        }
        return null;
    }
    
    public static void reloadNPCsWithRole(final int roleIndex) {
        Universe.get().getWorlds().forEach((s, world) -> world.execute(() -> world.getEntityStore().getStore().forEachChunk(NPCEntity.getComponentType(), (archetypeChunk, commandBuffer) -> {
            for (int index = 0; index < archetypeChunk.size(); ++index) {
                final NPCEntity npc = archetypeChunk.getComponent(index, NPCEntity.getComponentType());
                if (npc.getRoleIndex() == roleIndex && !npc.getRole().isRoleChangeRequested()) {
                    RoleChangeSystem.requestRoleChange(archetypeChunk.getReferenceTo(index), npc.getRole(), roleIndex, true, world.getEntityStore().getStore());
                }
            }
        })));
    }
    
    protected void onNPCGroupsLoaded(final LoadedAssetsEvent<String, NPCGroup, AssetMap<String, NPCGroup>> event) {
        this.putNPCGroups();
    }
    
    protected void onNPCGroupsRemoved(final RemovedAssetsEvent<String, NPCGroup, AssetMap<String, NPCGroup>> event) {
        this.putNPCGroups();
    }
    
    protected void onAttitudeGroupsLoaded(@Nonnull final LoadedAssetsEvent<String, AttitudeGroup, AssetMap<String, AttitudeGroup>> event) {
        if (this.attitudeMap == null) {
            this.putAttitudeGroups();
            return;
        }
        final Map<String, AttitudeGroup> loadedAssets = event.getLoadedAssets();
        final IndexedLookupTableAssetMap<String, AttitudeGroup> assets = AttitudeGroup.getAssetMap();
        final int attitudeGroupCount = this.attitudeMap.getAttitudeGroupCount();
        final Iterator<String> iterator = loadedAssets.keySet().iterator();
        String id = null;
        while (iterator.hasNext()) {
            id = iterator.next();
            final int index = assets.getIndex(id);
            if (index == Integer.MIN_VALUE) {
                throw new IllegalArgumentException("Unknown key! " + id);
            }
            if (index >= attitudeGroupCount) {
                this.putAttitudeGroups();
                return;
            }
        }
        loadedAssets.forEach((id, group) -> {
            final int index2 = assets.getIndex(id);
            if (index2 == Integer.MIN_VALUE) {
                throw new IllegalArgumentException("Unknown key! " + id);
            }
            else {
                this.attitudeMap.updateAttitudeGroup(index2, group);
            }
        });
    }
    
    protected void onAttitudeGroupsRemoved(final RemovedAssetsEvent<String, AttitudeGroup, AssetMap<String, AttitudeGroup>> event) {
        this.putAttitudeGroups();
    }
    
    protected void onItemAttitudeGroupsLoaded(@Nonnull final LoadedAssetsEvent<String, ItemAttitudeGroup, AssetMap<String, ItemAttitudeGroup>> event) {
        if (this.itemAttitudeMap == null) {
            this.putItemAttitudeGroups();
            return;
        }
        final Map<String, ItemAttitudeGroup> loadedAssets = event.getLoadedAssets();
        final IndexedLookupTableAssetMap<String, ItemAttitudeGroup> assets = ItemAttitudeGroup.getAssetMap();
        final int attitudeGroupCount = this.itemAttitudeMap.getAttitudeGroupCount();
        final Iterator<String> iterator = loadedAssets.keySet().iterator();
        String id = null;
        while (iterator.hasNext()) {
            id = iterator.next();
            final int index = assets.getIndex(id);
            if (index == Integer.MIN_VALUE) {
                throw new IllegalArgumentException("Unknown key! " + id);
            }
            if (index >= attitudeGroupCount) {
                this.putItemAttitudeGroups();
                return;
            }
        }
        loadedAssets.forEach((id, group) -> {
            final int index2 = assets.getIndex(id);
            if (index2 == Integer.MIN_VALUE) {
                throw new IllegalArgumentException("Unknown key! " + id);
            }
            else {
                this.itemAttitudeMap.updateAttitudeGroup(index2, group);
            }
        });
    }
    
    protected void onItemAttitudeGroupsRemoved(final RemovedAssetsEvent<String, ItemAttitudeGroup, AssetMap<String, ItemAttitudeGroup>> event) {
        this.putItemAttitudeGroups();
    }
    
    private void putItemAttitudeGroups() {
        final ItemAttitudeMap.Builder builder = new ItemAttitudeMap.Builder();
        builder.addAttitudeGroups(ItemAttitudeGroup.getAssetMap().getAssetMap());
        this.itemAttitudeMap = builder.build();
    }
    
    protected void onPathChange(final WorldPathChangedEvent event) {
        this.pathChangeRevision.getAndIncrement();
    }
    
    public int getPathChangeRevision() {
        return this.pathChangeRevision.get();
    }
    
    protected void onNPCsLoaded(final AllNPCsLoadedEvent event) {
        this.putNPCGroups();
    }
    
    private void putNPCGroups() {
        final IndexedLookupTableAssetMap<String, NPCGroup> indexedAssetMap = NPCGroup.getAssetMap();
        final Object2IntOpenHashMap<String> tagSetIndexMap = new Object2IntOpenHashMap<String>();
        indexedAssetMap.getAssetMap().forEach((name, group) -> {
            final int index = indexedAssetMap.getIndex(name);
            if (index == Integer.MIN_VALUE) {
                throw new IllegalArgumentException("Unknown key! " + name);
            }
            else {
                tagSetIndexMap.put(name, index);
                return;
            }
        });
        TagSetPlugin.get(NPCGroup.class).putAssetSets(indexedAssetMap.getAssetMap(), tagSetIndexMap, this.builderManager.getNameToIndexMap());
        this.putAttitudeGroups();
    }
    
    private void putAttitudeGroups() {
        final AttitudeMap.Builder builder = new AttitudeMap.Builder();
        builder.addAttitudeGroups(AttitudeGroup.getAssetMap().getAssetMap());
        this.attitudeMap = builder.build();
    }
    
    @Nullable
    public String getName(final int builderIndex) {
        return this.builderManager.lookupName(builderIndex);
    }
    
    public int getIndex(final String builderName) {
        return this.builderManager.getIndex(builderName);
    }
    
    @Nullable
    public Builder<Role> tryGetCachedValidRole(final int roleIndex) {
        return this.builderManager.tryGetCachedValidRole(roleIndex);
    }
    
    @Nullable
    public BuilderInfo getBuilderInfo(final Builder<?> builder) {
        return this.builderManager.getBuilderInfo(builder);
    }
    
    public List<String> getRoleTemplateNames(final boolean spawnableOnly) {
        return this.builderManager.collectMatchingBuilders((ObjectArrayList)new ObjectArrayList(), entry -> entry.getBuilder().category() == Role.class && (!spawnableOnly || entry.getBuilder().isSpawnable()), (builderInfo, objects) -> objects.add(builderInfo.getKeyName()));
    }
    
    public boolean hasRoleName(final String roleName) {
        return this.getRoleBuilderInfo(this.getIndex(roleName)) != null;
    }
    
    public void validateSpawnableRole(final String roleName) {
        final BuilderInfo builder = this.getRoleBuilderInfo(this.getIndex(roleName));
        if (builder == null) {
            throw new SkipSentryException(new IllegalArgumentException(roleName + " does not exist as a role!"));
        }
        if (!builder.getBuilder().isSpawnable()) {
            throw new SkipSentryException(new IllegalArgumentException(roleName + " is an abstract role type and cannot be spawned directly!"));
        }
    }
    
    @Nullable
    public BuilderInfo getRoleBuilderInfo(final int roleIndex) {
        final BuilderInfo builderInfo = this.builderManager.tryGetBuilderInfo(roleIndex);
        return (builderInfo != null && builderInfo.getBuilder().category() == Role.class) ? builderInfo : null;
    }
    
    public void setBuilderInvalid(final int builderIndex) {
        final BuilderInfo builderInfo = this.builderManager.tryGetBuilderInfo(builderIndex);
        if (builderInfo != null) {
            builderInfo.setNeedsReload();
        }
    }
    
    public AttitudeMap getAttitudeMap() {
        return this.attitudeMap;
    }
    
    public ItemAttitudeMap getItemAttitudeMap() {
        return this.itemAttitudeMap;
    }
    
    public boolean testAndValidateRole(@Nullable final BuilderInfo builderInfo) {
        return builderInfo != null && builderInfo.getBuilder() != null && builderInfo.getBuilder().category() == Role.class && this.builderManager.validateBuilder(builderInfo);
    }
    
    public void forceValidation(final int builderIndex) {
        this.builderManager.forceValidation(builderIndex);
    }
    
    @Nullable
    public Pair<Ref<EntityStore>, NPCEntity> spawnEntity(@Nonnull final Store<EntityStore> store, final int roleIndex, @Nonnull final Vector3d position, @Nullable final Vector3f rotation, @Nullable final Model spawnModel, @Nullable final TriConsumer<NPCEntity, Ref<EntityStore>, Store<EntityStore>> postSpawn) {
        return this.spawnEntity(store, roleIndex, position, rotation, spawnModel, null, postSpawn);
    }
    
    @Nullable
    public Pair<Ref<EntityStore>, NPCEntity> spawnEntity(@Nonnull final Store<EntityStore> store, final int roleIndex, @Nonnull final Vector3d position, @Nullable Vector3f rotation, @Nullable final Model spawnModel, @Nullable final TriConsumer<NPCEntity, Holder<EntityStore>, Store<EntityStore>> preAddToWorld, @Nullable final TriConsumer<NPCEntity, Ref<EntityStore>, Store<EntityStore>> postSpawn) {
        final WorldTimeResource worldTimeResource = store.getResource(WorldTimeResource.getResourceType());
        final NPCEntity npcComponent = new NPCEntity();
        npcComponent.setSpawnInstant(worldTimeResource.getGameTime());
        if (rotation == null) {
            rotation = NPCPlugin.NULL_ROTATION;
        }
        npcComponent.saveLeashInformation(position, rotation);
        final String roleName = this.getName(roleIndex);
        if (roleName == null) {
            get().getLogger().at(Level.WARNING).log("Unable to spawn entity with invalid role index: %s!", roleIndex);
            return null;
        }
        npcComponent.setRoleName(roleName);
        npcComponent.setRoleIndex(roleIndex);
        final Holder<EntityStore> holder = EntityStore.REGISTRY.newHolder();
        holder.addComponent(NPCEntity.getComponentType(), npcComponent);
        holder.addComponent(TransformComponent.getComponentType(), new TransformComponent(position, rotation));
        holder.addComponent(HeadRotation.getComponentType(), new HeadRotation(rotation));
        final DisplayNameComponent displayNameComponent = new DisplayNameComponent(Message.raw(roleName));
        holder.addComponent(DisplayNameComponent.getComponentType(), displayNameComponent);
        holder.ensureComponent(UUIDComponent.getComponentType());
        if (spawnModel != null) {
            npcComponent.setInitialModelScale(spawnModel.getScale());
            holder.addComponent(ModelComponent.getComponentType(), new ModelComponent(spawnModel));
            holder.addComponent(PersistentModel.getComponentType(), new PersistentModel(spawnModel.toReference()));
        }
        if (preAddToWorld != null) {
            preAddToWorld.accept(npcComponent, holder, store);
        }
        final Ref<EntityStore> ref = store.addEntity(holder, AddReason.SPAWN);
        if (ref == null) {
            get().getLogger().at(Level.WARNING).log("Unable to handle non-spawned entity: %s!", this.getName(roleIndex));
            return null;
        }
        if (postSpawn != null) {
            postSpawn.accept(npcComponent, ref, store);
        }
        return Pair.of(ref, npcComponent);
    }
    
    @Nonnull
    public BuilderInfo prepareRoleBuilderInfo(final int roleIndex) {
        BuilderInfo builderInfo;
        try {
            builderInfo = this.builderManager.getCachedBuilderInfo(roleIndex, Role.class);
            if (this.validateBuilder) {
                if (!builderInfo.isValidated()) {
                    this.builderManager.validateBuilder(builderInfo);
                }
                if (!builderInfo.isValid()) {
                    throw new SkipSentryException(new IllegalStateException("Builder " + builderInfo.getKeyName() + " failed validation!"));
                }
            }
        }
        catch (final RuntimeException e) {
            throw new SkipSentryException(new RuntimeException(String.format("Cannot use role template '%s' (%s): %s", this.getName(roleIndex), roleIndex, e.getMessage()), e));
        }
        return builderInfo;
    }
    
    @Nonnull
    public static Role buildRole(@Nonnull final Builder<Role> roleBuilder, @Nonnull final BuilderInfo builderInfo, @Nonnull final BuilderSupport builderSupport, final int roleIndex) {
        Role role;
        try {
            final StdScope scope = roleBuilder.getBuilderParameters().createScope();
            builderSupport.setScope(scope);
            builderSupport.setGlobalScope(scope);
            role = roleBuilder.build(builderSupport);
            role.postRoleBuilt(builderSupport);
        }
        catch (final Throwable e) {
            builderInfo.setNeedsReload();
            throw new SkipSentryException(e);
        }
        role.setRoleIndex(roleIndex, builderInfo.getKeyName());
        return role;
    }
    
    protected void onModelsChanged(@Nonnull final LoadedAssetsEvent<String, ModelAsset, DefaultAssetMap<String, ModelAsset>> event) {
        final Map<String, ModelAsset> loadedModelAssets = event.getLoadedAssets();
        Universe.get().getWorlds().values().forEach(world -> world.execute(() -> {
            final Store<EntityStore> store = world.getEntityStore().getStore();
            store.forEachEntityParallel(NPCEntity.getComponentType(), (index, archetypeChunk, commandBuffer) -> {
                final ModelComponent entityModelComponent = archetypeChunk.getComponent(index, ModelComponent.getComponentType());
                if (entityModelComponent != null) {
                    final Model oldModel = entityModelComponent.getModel();
                    final ModelAsset newModelAsset = loadedModelAssets.get(oldModel.getModelAssetId());
                    if (newModelAsset != null) {
                        final Ref<EntityStore> entityReference = archetypeChunk.getReferenceTo(index);
                        commandBuffer.putComponent(entityReference, ModelComponent.getComponentType(), new ModelComponent(Model.createScaledModel(newModelAsset, oldModel.getScale(), oldModel.getRandomAttachmentIds())));
                    }
                }
            });
        }));
    }
    
    protected void generateDescriptors() {
        this.getLogger().at(Level.INFO).log("===== Generating descriptors for NPC!");
        this.builderDescriptors = this.builderManager.generateDescriptors();
    }
    
    protected void saveDescriptors() {
        this.getLogger().at(Level.INFO).log("===== Saving descriptors for NPC!");
        final Path path = Path.of("npc_descriptors.json", new String[0]);
        BuilderManager.saveDescriptors(this.builderDescriptors, path);
        this.getLogger().at(Level.INFO).log("Saved NPC descriptors to: %s", path);
    }
    
    public BuilderManager getBuilderManager() {
        return this.builderManager;
    }
    
    public int getMaxBlackboardBlockCountPerType() {
        return this.maxBlackboardBlockCountPerType;
    }
    
    public boolean isLogFailingTestErrors() {
        return this.logFailingTestErrors;
    }
    
    public boolean startRoleBenchmark(final double seconds, @Nonnull final Consumer<Int2ObjectMap<TimeDistributionRecorder>> onFinished) {
        this.benchmarkLock.lock();
        try {
            if (this.isBenchmarking()) {
                return false;
            }
            this.roleTickDistribution = new Int2ObjectOpenHashMap<TimeDistributionRecorder>();
            this.roleTickDistributionAll = new TimeDistributionRecorder(0.01, 1.0E-5);
            this.roleTickDistribution.put(-1, this.roleTickDistributionAll);
        }
        finally {
            this.benchmarkLock.unlock();
        }
        new CompletableFuture<Object>().completeOnTimeout(null, Math.round(seconds * 1000.0), TimeUnit.MILLISECONDS).thenRunAsync(() -> {
            final Int2ObjectMap<TimeDistributionRecorder> distribution = this.roleTickDistribution;
            this.benchmarkLock.lock();
            try {
                this.roleTickDistribution = null;
                this.roleTickDistributionAll = null;
            }
            finally {
                this.benchmarkLock.unlock();
            }
            onFinished.accept(distribution);
            return;
        });
        return true;
    }
    
    public void collectRoleTick(final int roleIndex, final long nanos) {
        if (!this.benchmarkLock.tryLock()) {
            return;
        }
        try {
            if (this.roleTickDistribution != null) {
                this.roleTickDistribution.computeIfAbsent(roleIndex, i -> new TimeDistributionRecorder(0.01, 1.0E-5)).recordNanos(nanos);
                this.roleTickDistributionAll.recordNanos(nanos);
            }
        }
        finally {
            this.benchmarkLock.unlock();
        }
    }
    
    public boolean isBenchmarkingRole() {
        return this.roleTickDistribution != null;
    }
    
    public boolean startSensorSupportBenchmark(final double seconds, @Nonnull final Consumer<Int2ObjectMap<SensorSupportBenchmark>> onFinished) {
        this.benchmarkLock.lock();
        try {
            if (this.isBenchmarking()) {
                return false;
            }
            this.roleSensorSupportDistribution = new Int2ObjectOpenHashMap<SensorSupportBenchmark>();
            this.roleSensorSupportDistributionAll = new SensorSupportBenchmark();
            this.roleSensorSupportDistribution.put(-1, this.roleSensorSupportDistributionAll);
        }
        finally {
            this.benchmarkLock.unlock();
        }
        new CompletableFuture<Object>().completeOnTimeout(null, Math.round(seconds * 1000.0), TimeUnit.MILLISECONDS).thenRunAsync(() -> {
            final Int2ObjectMap<SensorSupportBenchmark> distribution = this.roleSensorSupportDistribution;
            this.benchmarkLock.lock();
            try {
                this.roleSensorSupportDistribution = null;
                this.roleSensorSupportDistributionAll = null;
            }
            finally {
                this.benchmarkLock.unlock();
            }
            onFinished.accept(distribution);
            return;
        });
        return true;
    }
    
    public boolean isBenchmarkingSensorSupport() {
        return this.roleSensorSupportDistributionAll != null;
    }
    
    protected boolean isBenchmarking() {
        return this.isBenchmarkingRole() || this.isBenchmarkingSensorSupport();
    }
    
    public void collectSensorSupportPlayerList(final int roleIndex, final long getNanos, final double maxPlayerDistanceSorted, final double maxPlayerDistance, final double maxPlayerDistanceAvoidance, final int numPlayers) {
        if (!this.benchmarkLock.tryLock()) {
            return;
        }
        try {
            if (this.roleSensorSupportDistribution != null) {
                this.roleSensorSupportDistribution.computeIfAbsent(roleIndex, i -> new SensorSupportBenchmark()).collectPlayerList(getNanos, maxPlayerDistanceSorted, maxPlayerDistance, maxPlayerDistanceAvoidance, numPlayers);
                this.roleSensorSupportDistributionAll.collectPlayerList(getNanos, maxPlayerDistanceSorted, maxPlayerDistance, maxPlayerDistanceAvoidance, numPlayers);
            }
        }
        finally {
            this.benchmarkLock.unlock();
        }
    }
    
    public void collectSensorSupportEntityList(final int roleIndex, final long getNanos, final double maxEntityDistanceSorted, final double maxEntityDistance, final double maxEntityDistanceAvoidance, final int numEntities) {
        if (!this.benchmarkLock.tryLock()) {
            return;
        }
        try {
            if (this.roleSensorSupportDistribution != null) {
                this.roleSensorSupportDistribution.computeIfAbsent(roleIndex, i -> new SensorSupportBenchmark()).collectEntityList(getNanos, maxEntityDistanceSorted, maxEntityDistance, maxEntityDistanceAvoidance, numEntities);
                this.roleSensorSupportDistributionAll.collectEntityList(getNanos, maxEntityDistanceSorted, maxEntityDistance, maxEntityDistanceAvoidance, numEntities);
            }
        }
        finally {
            this.benchmarkLock.unlock();
        }
    }
    
    public void collectSensorSupportLosTest(final int roleIndex, final boolean cacheHit, final long time) {
        if (!this.isBenchmarkingSensorSupport() || !this.benchmarkLock.tryLock()) {
            return;
        }
        try {
            if (this.roleSensorSupportDistribution != null) {
                this.roleSensorSupportDistribution.computeIfAbsent(roleIndex, i -> new SensorSupportBenchmark()).collectLosTest(cacheHit, time);
                this.roleSensorSupportDistributionAll.collectLosTest(cacheHit, time);
            }
        }
        finally {
            this.benchmarkLock.unlock();
        }
    }
    
    public void collectSensorSupportInverseLosTest(final int roleIndex, final boolean cacheHit) {
        if (!this.isBenchmarkingSensorSupport() || !this.benchmarkLock.tryLock()) {
            return;
        }
        try {
            if (this.roleSensorSupportDistribution != null) {
                this.roleSensorSupportDistribution.computeIfAbsent(roleIndex, i -> new SensorSupportBenchmark()).collectInverseLosTest(cacheHit);
                this.roleSensorSupportDistributionAll.collectInverseLosTest(cacheHit);
            }
        }
        finally {
            this.benchmarkLock.unlock();
        }
    }
    
    public void collectSensorSupportFriendlyBlockingTest(final int roleIndex, final boolean cacheHit) {
        if (!this.isBenchmarkingSensorSupport() || !this.benchmarkLock.tryLock()) {
            return;
        }
        try {
            if (this.roleSensorSupportDistribution != null) {
                this.roleSensorSupportDistribution.computeIfAbsent(roleIndex, i -> new SensorSupportBenchmark()).collectFriendlyBlockingTest(cacheHit);
                this.roleSensorSupportDistributionAll.collectFriendlyBlockingTest(cacheHit);
            }
        }
        finally {
            this.benchmarkLock.unlock();
        }
    }
    
    public void collectSensorSupportTickDone(final int roleIndex) {
        if (!this.isBenchmarkingSensorSupport() || !this.benchmarkLock.tryLock()) {
            return;
        }
        try {
            if (this.roleSensorSupportDistribution != null) {
                this.roleSensorSupportDistribution.computeIfAbsent(roleIndex, i -> new SensorSupportBenchmark()).tickDone();
                this.roleSensorSupportDistributionAll.tickDone();
            }
        }
        finally {
            this.benchmarkLock.unlock();
        }
    }
    
    @Nonnull
    public <T> NPCPlugin registerCoreComponentType(final String name, @Nonnull final Supplier<Builder<T>> builder) {
        final BuilderFactory<T> factory = this.builderManager.getFactory(builder.get().category());
        factory.add(name, builder);
        return this;
    }
    
    public void setRoleBuilderNeedsReload(final Builder<?> builder) {
        final BuilderInfo builderInfo = this.getBuilderInfo(builder);
        Objects.requireNonNull(builderInfo, "Have builder but can't get builderInfo for it");
        builderInfo.setNeedsReload();
    }
    
    protected void registerCoreFactories() {
        final BuilderFactory<Role> roleFactory = new BuilderFactory<Role>(Role.class, "Type");
        roleFactory.add("Generic", (Supplier<Builder<Role>>)BuilderRole::new);
        roleFactory.add("Abstract", (Supplier<Builder<Role>>)BuilderRoleAbstract::new);
        roleFactory.add("Variant", (Supplier<Builder<Role>>)BuilderRoleVariant::new);
        this.builderManager.registerFactory(roleFactory);
        final BuilderFactory<MotionController> motionControllerFactory = new BuilderFactory<MotionController>(MotionController.class, "Type");
        motionControllerFactory.add("Walk", (Supplier<Builder<MotionController>>)BuilderMotionControllerWalk::new);
        motionControllerFactory.add("Fly", (Supplier<Builder<MotionController>>)BuilderMotionControllerFly::new);
        motionControllerFactory.add("Dive", (Supplier<Builder<MotionController>>)BuilderMotionControllerDive::new);
        this.builderManager.registerFactory(motionControllerFactory);
        final BuilderFactory<Map<String, MotionController>> motionControllerMapFactory = new BuilderFactory<Map<String, MotionController>>(BuilderMotionControllerMapUtil.CLASS_REFERENCE, "Type", (Supplier<Builder<Map<String, MotionController>>>)BuilderMotionControllerMap::new);
        this.builderManager.registerFactory(motionControllerMapFactory);
        final BuilderFactory<ActionList> actionListFactory = new BuilderFactory<ActionList>(ActionList.class, "Type", (Supplier<Builder<ActionList>>)BuilderActionList::new);
        this.builderManager.registerFactory(actionListFactory);
        final BuilderFactory<Instruction> instructionFactory = new BuilderFactory<Instruction>(Instruction.class, "Type", (Supplier<Builder<Instruction>>)BuilderInstruction::new);
        instructionFactory.add("Random", (Supplier<Builder<Instruction>>)BuilderInstructionRandomized::new);
        instructionFactory.add("Reference", (Supplier<Builder<Instruction>>)BuilderInstructionReference::new);
        this.builderManager.registerFactory(instructionFactory);
        final BuilderFactory<TransientPathDefinition> transientPathFactory = new BuilderFactory<TransientPathDefinition>(TransientPathDefinition.class, "Type", (Supplier<Builder<TransientPathDefinition>>)BuilderTransientPathDefinition::new);
        this.builderManager.registerFactory(transientPathFactory);
        final BuilderFactory<RelativeWaypointDefinition> relativeWaypointFactory = new BuilderFactory<RelativeWaypointDefinition>(RelativeWaypointDefinition.class, "Type", (Supplier<Builder<RelativeWaypointDefinition>>)BuilderRelativeWaypointDefinition::new);
        this.builderManager.registerFactory(relativeWaypointFactory);
        final BuilderFactory<WeightedAction> weightedActionFactory = new BuilderFactory<WeightedAction>(WeightedAction.class, "Type", (Supplier<Builder<WeightedAction>>)BuilderWeightedAction::new);
        this.builderManager.registerFactory(weightedActionFactory);
        final BuilderFactory<BuilderValueToParameterMapping.ValueToParameterMapping> valueToParameterMappingFactory = new BuilderFactory<BuilderValueToParameterMapping.ValueToParameterMapping>(BuilderValueToParameterMapping.ValueToParameterMapping.class, "Type", (Supplier<Builder<BuilderValueToParameterMapping.ValueToParameterMapping>>)BuilderValueToParameterMapping::new);
        this.builderManager.registerFactory(valueToParameterMappingFactory);
        StateTransitionController.registerFactories(this.builderManager);
        this.builderManager.registerFactory(new BuilderFactory<Object>((Class<Object>)BodyMotion.class, "Type"));
        this.builderManager.registerFactory(new BuilderFactory<Object>((Class<Object>)HeadMotion.class, "Type"));
        this.builderManager.registerFactory(new BuilderFactory<Object>((Class<Object>)Action.class, "Type"));
        this.builderManager.registerFactory(new BuilderFactory<Object>((Class<Object>)Sensor.class, "Type"));
        this.builderManager.registerFactory(new BuilderFactory<Object>((Class<Object>)IEntityFilter.class, "Type"));
        this.builderManager.registerFactory(new BuilderFactory<Object>((Class<Object>)ISensorEntityPrioritiser.class, "Type"));
        this.builderManager.registerFactory(new BuilderFactory<Object>((Class<Object>)ISensorEntityCollector.class, "Type"));
    }
    
    protected static void onBalanceAssetsChanged(@Nonnull final LoadedAssetsEvent<String, BalanceAsset, DefaultAssetMap<String, BalanceAsset>> event) {
        final Map<String, BalanceAsset> loadedAssets = event.getLoadedAssets();
        Universe.get().getWorlds().forEach((name, world) -> world.execute(() -> world.getEntityStore().getStore().forEachChunk(NPCEntity.getComponentType(), (archetypeChunk, commandBuffer) -> {
            int index = 0;
            while (index < archetypeChunk.size()) {
                final NPCEntity npcComponent = archetypeChunk.getComponent(index, NPCEntity.getComponentType());
                if (!NPCPlugin.$assertionsDisabled && npcComponent == null) {
                    throw new AssertionError();
                }
                else {
                    if (!(!loadedAssets.containsKey(npcComponent.getRole().getBalanceAsset()))) {
                        final Ref<EntityStore> ref = archetypeChunk.getReferenceTo(index);
                        RoleChangeSystem.requestRoleChange(ref, npcComponent.getRole(), npcComponent.getRoleIndex(), false, world.getEntityStore().getStore());
                    }
                    ++index;
                }
            }
        })));
    }
    
    protected static void onBalanceAssetsRemoved(@Nonnull final RemovedAssetsEvent<String, BalanceAsset, DefaultAssetMap<String, BalanceAsset>> event) {
        final Set<String> removedAssets = event.getRemovedAssets();
        Universe.get().getWorlds().forEach((name, world) -> world.execute(() -> world.getEntityStore().getStore().forEachChunk(NPCEntity.getComponentType(), (archetypeChunk, commandBuffer) -> {
            int index = 0;
            while (index < archetypeChunk.size()) {
                final NPCEntity npcComponent = archetypeChunk.getComponent(index, NPCEntity.getComponentType());
                if (!NPCPlugin.$assertionsDisabled && npcComponent == null) {
                    throw new AssertionError();
                }
                else {
                    if (!(!removedAssets.contains(npcComponent.getRole().getBalanceAsset()))) {
                        final Ref<EntityStore> ref = archetypeChunk.getReferenceTo(index);
                        commandBuffer.removeEntity(ref, RemoveReason.REMOVE);
                    }
                    ++index;
                }
            }
        })));
    }
    
    static {
        NPCPlugin.FACTORY_CLASS_ROLE = "Role";
        NPCPlugin.FACTORY_CLASS_BODY_MOTION = "BodyMotion";
        NPCPlugin.FACTORY_CLASS_HEAD_MOTION = "HeadMotion";
        NPCPlugin.FACTORY_CLASS_ACTION = "Action";
        NPCPlugin.FACTORY_CLASS_SENSOR = "Sensor";
        NPCPlugin.FACTORY_CLASS_INSTRUCTION = "Instruction";
        NPCPlugin.FACTORY_CLASS_TRANSIENT_PATH = "Path";
        NPCPlugin.FACTORY_CLASS_ACTION_LIST = "ActionList";
        NPCPlugin.ROLE_ASSETS_PATH = "Server/NPC/Roles";
        NULL_ROTATION = new Vector3f(0.0f, 0.0f, 0.0f);
    }
    
    public static class NPCConfig
    {
        public static final BuilderCodec<NPCConfig> CODEC;
        private boolean generateDescriptors;
        private boolean generateDescriptorsFile;
        private boolean autoReload;
        private boolean validateBuilder;
        private int maxBlackboardBlockType;
        private boolean logFailingTestErrors;
        private String[] presetCoverageTestNPCs;
        
        public NPCConfig() {
            this.autoReload = true;
            this.validateBuilder = true;
            this.maxBlackboardBlockType = 20;
            this.presetCoverageTestNPCs = new String[] { "Test_Bird", "Test_Block_Sensor", "Test_Attack_Bow", "Test_Combat_Sensor_Sheep", "Test_Bow_Charge", "Test_OffHand_Swap", "Test_Patrol_Path", "Test_Flock_Mixed#4", "Test_Group_Sheep", "Test_Attack_Flying_Ranged", "Test_Interaction_Complete_Task", "Test_Hotbar_Weapon_Swap", "Test_Inventory_Contents", "Test_Dive_Flee", "Test_Jumping", "Test_Walk_Leave", "Test_Walk_Seek", "Test_Watch", "Test_Chained_Path", "Test_Spawn_Action", "Test_State_Evaluator_Toggle", "Test_State_Evaluator_Sleep", "Test_Alarm", "Test_Timer_Repeating", "Test_Action_Model_Attachment", "Test_Animation_State_Change", "Test_Block_Change", "Test_Crouch", "Test_Drop_Item", "Test_Entity_Damage_Event", "Test_Hover_Parrot", "Test_In_Water", "Test_Light_Sensor", "Test_Model_Change", "Test_Particle_Emotions", "Test_Place_Blocks", "Test_Position_Adjustment_Wrapper", "Test_Probe", "Test_Sensor_Age", "Test_Sensor_DroppedItem", "Test_Shoot_At_Block", "Test_Species_Attitude", "Test_Standing_On_Block_Sensor", "Test_Teleport", "Test_Throw_NPC", "Test_Trigger_Spawners", "Test_Weather_Sensor", "Test_Bird_Land" };
        }
        
        public boolean isGenerateDescriptors() {
            return this.generateDescriptors;
        }
        
        public boolean isGenerateDescriptorsFile() {
            return this.generateDescriptorsFile;
        }
        
        public boolean isAutoReload() {
            return this.autoReload;
        }
        
        public boolean isValidateBuilder() {
            return this.validateBuilder;
        }
        
        public int getMaxBlackboardBlockType() {
            return this.maxBlackboardBlockType;
        }
        
        public boolean isLogFailingTestErrors() {
            return this.logFailingTestErrors;
        }
        
        public String[] getPresetCoverageTestNPCs() {
            return this.presetCoverageTestNPCs;
        }
        
        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             "Descriptors"
            //    16: getstatic       com/hypixel/hytale/codec/Codec.BOOLEAN:Lcom/hypixel/hytale/codec/codecs/simple/BooleanCodec;
            //    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             "DescriptorsFile"
            //    47: getstatic       com/hypixel/hytale/codec/Codec.BOOLEAN:Lcom/hypixel/hytale/codec/codecs/simple/BooleanCodec;
            //    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             "AutoReload"
            //    78: getstatic       com/hypixel/hytale/codec/Codec.BOOLEAN:Lcom/hypixel/hytale/codec/codecs/simple/BooleanCodec;
            //    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             "ValidateBuilders"
            //   109: getstatic       com/hypixel/hytale/codec/Codec.BOOLEAN:Lcom/hypixel/hytale/codec/codecs/simple/BooleanCodec;
            //   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: new             Lcom/hypixel/hytale/codec/KeyedCodec;
            //   137: dup            
            //   138: ldc             "MaxBlackboardBlockType"
            //   140: getstatic       com/hypixel/hytale/codec/Codec.INTEGER:Lcom/hypixel/hytale/codec/codecs/simple/IntegerCodec;
            //   143: invokespecial   com/hypixel/hytale/codec/KeyedCodec.<init>:(Ljava/lang/String;Lcom/hypixel/hytale/codec/Codec;)V
            //   146: invokedynamic   BootstrapMethod #9, accept:()Ljava/util/function/BiConsumer;
            //   151: invokedynamic   BootstrapMethod #10, apply:()Ljava/util/function/Function;
            //   156: 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;
            //   159: invokevirtual   com/hypixel/hytale/codec/builder/BuilderField$FieldBuilder.add:()Lcom/hypixel/hytale/codec/builder/BuilderCodec$BuilderBase;
            //   162: checkcast       Lcom/hypixel/hytale/codec/builder/BuilderCodec$Builder;
            //   165: new             Lcom/hypixel/hytale/codec/KeyedCodec;
            //   168: dup            
            //   169: ldc             "LogFailingTestErrors"
            //   171: getstatic       com/hypixel/hytale/codec/Codec.BOOLEAN:Lcom/hypixel/hytale/codec/codecs/simple/BooleanCodec;
            //   174: invokespecial   com/hypixel/hytale/codec/KeyedCodec.<init>:(Ljava/lang/String;Lcom/hypixel/hytale/codec/Codec;)V
            //   177: invokedynamic   BootstrapMethod #11, accept:()Ljava/util/function/BiConsumer;
            //   182: invokedynamic   BootstrapMethod #12, apply:()Ljava/util/function/Function;
            //   187: 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;
            //   190: invokevirtual   com/hypixel/hytale/codec/builder/BuilderField$FieldBuilder.add:()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             "PresetCoverageTestNPCs"
            //   202: getstatic       com/hypixel/hytale/codec/Codec.STRING_ARRAY:Lcom/hypixel/hytale/codec/codecs/array/ArrayCodec;
            //   205: invokespecial   com/hypixel/hytale/codec/KeyedCodec.<init>:(Ljava/lang/String;Lcom/hypixel/hytale/codec/Codec;)V
            //   208: invokedynamic   BootstrapMethod #13, accept:()Ljava/util/function/BiConsumer;
            //   213: invokedynamic   BootstrapMethod #14, apply:()Ljava/util/function/Function;
            //   218: 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;
            //   221: invokevirtual   com/hypixel/hytale/codec/builder/BuilderField$FieldBuilder.add:()Lcom/hypixel/hytale/codec/builder/BuilderCodec$BuilderBase;
            //   224: checkcast       Lcom/hypixel/hytale/codec/builder/BuilderCodec$Builder;
            //   227: invokevirtual   com/hypixel/hytale/codec/builder/BuilderCodec$Builder.build:()Lcom/hypixel/hytale/codec/builder/BuilderCodec;
            //   230: putstatic       com/hypixel/hytale/server/npc/NPCPlugin$NPCConfig.CODEC:Lcom/hypixel/hytale/codec/builder/BuilderCodec;
            //   233: 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.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: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: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: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.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.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.");
        }
    }
    
    public static class NPCEntityRegenerateStatsSystem extends EntityStatsSystems.Regenerate<NPCEntity>
    {
        public NPCEntityRegenerateStatsSystem() {
            super(EntityStatMap.getComponentType(), NPCEntity.getComponentType());
        }
    }
}
