BuilderManager class

Пакет: com.hypixel.hytale.server.npc.asset.builder

Файл: com/hypixel/hytale/server/npc/asset/builder/BuilderManager.java

Поля (174)

МодификаторыТипИмя
final String CONTENT_KEY
NORMAL, FAILING
SCHEMA_BUILDER_MANAGER
SCHEMA_BUILDER_MANAGER
continue
private final boolean includeTests
private final AssetPack pack
return
return
Int2ObjectOpenHashMap var1
Object2IntOpenHashMap var1
ObjectArrayList var1
BuilderManager var1
IEventDispatcher var1
String var1
var1
final Object2IntOpenHashMap var10
BuilderManager.TestType var10
var10
Builder var10
EventKind var10
int var11
BuilderInfo var11
JsonElement var11
NPCLoadTimeValidationHelper var11
int var11
StringBuilder var12
String var12
BuilderFactory var12
String var12
ObjectIterator var13
String var13
var13
Schema var13
it.unimi.dsi.fastutil.objects.Object2IntMap.Entry var14
StateMappingHelper var14
Schema var14
JsonElement var15
ObjectSchema var15
JsonElement var16
ObjectListIterator var17
JsonElement var17
String var18
String var18
String var18
Int2ObjectOpenHashMap var19
String var19
BuilderInfo var19
Class var2
BuilderInfo var2
int var2
var2
AssetMonitor var2
HashSet var2
String var2
Builder var2
BuilderFactory var2
ObjectIterator var2
BuilderInfo var2
var2
IEventDispatcher var2
BuilderInfo var2
int var2
BuilderInfo var2
ObjectIterator var2
int var2
Int2ObjectOpenHashMap var2
BuilderParameters var20
ObjectListIterator var21
InternalReferenceResolver var21
AssetExtraInfo.Data var22
String var23
AssetExtraInfo var23
ObjectArrayList var24
BuilderValidationHelper var25
ObjectListIterator var26
Evaluator var27
String var28
ObjectIterator var3
String var3
boolean var3
var3
var3
var3
ObjectArrayList var3
ObjectArrayList var3
BuilderInfo var3
BuilderInfo var3
BuilderInfo var3
it.unimi.dsi.fastutil.objects.Object2IntMap.Entry var3
IntSet var3
BuilderFactory var3
GsonBuilder var3
BuilderInfo var3
Builder var3
Entry var3
StringBuilder var3
HashSet var3
String var39
var39
it.unimi.dsi.fastutil.objects.Object2IntMap.Entry var4
ObjectArrayList var4
Int2ObjectOpenHashMap var4
IntIterator var4
Schema var4
String var4
Gson var4
Int2ObjectOpenHashMap var4
int var4
var4
var4
Builder var4
BuilderInfo var4
IntIterator var4
int var4
int var4
int var4
HashSet var4
JsonElement var40
Class var41
BuilderFactory var42
Builder var43
var43
BuilderInfo var44
JsonElement var45
Path var5
AssetMonitor var5
HashSet var5
ObjectSchema var5
Builder var5
BuilderInfo var5
ExecutionContext var5
Builder var5
IntSet var5
var5
Builder var5
String var5
HashSet var5
final ObjectArrayList var6
int var6
int var6
StringSchema var6
int var6
String var6
ExecutionContext var6
boolean var6
var6
var6
var6
Builder var6
ObjectArrayList var6
String var7
JsonObject var7
var7
Schema var7
Builder var7
String var7
var7
String var7
IntIterator var7
Schema[] var8
Builder var8
boolean var8
var8
ModelAsset var8
NPCLoadTimeValidationHelper var8
int var8
Path var9
JsonReader var9
int var9
String var9
Model var9
BuilderInfo var9
Path var9

Методы (101)

МодификаторыВозвратСигнатура
private record AssetPathrecord AssetPath(String path, String label)
public BuilderAssetMonitorHandlerpublic BuilderAssetMonitorHandler(AssetPack nullx, boolean nullxx)
abstract throw new IllegalArgumentExceptionthrow new IllegalArgumentException()
abstract throw new IllegalArgumentExceptionthrow new IllegalArgumentException("No factory class supplied!")
abstract throw new IllegalStateExceptionthrow new IllegalStateException("Group tags must occupy the first indexes of the name-to-index map")
TestType TestType()
public void acceptpublic void accept(Map<Path, EventKind> var1)
public void addBuilderReloadListenervoid addBuilderReloadListener(@Nonnull Consumer<BuilderInfo> var1)
public void addCategoryvoid addCategory(String var1, Class<?> var2)
public void assetEditorLoadFilevoid assetEditorLoadFile(@Nonnull Path var1)
public void assetEditorRemoveFilevoid assetEditorRemoveFile(@Nonnull Path var1)
protected int cacheBuilderint cacheBuilder(String var1, Builder<?> var2, Path var3)
public void checkIfDeprecatedvoid checkIfDeprecated(@Nonnull Builder<?> var1, @Nonnull BuilderFactory<?> var2, @Nonnull JsonElement var3, String var4, String var5)
private void finishLoadingBuildersvoid finishLoadingBuilders(@Nonnull Int2ObjectOpenHashMap<BuilderInfo> var1, @Nonnull List<String> var2)
for for(BuilderManager.AssetPath var4 : this.assetPaths)
for for(BuilderManager.AssetPath var8 : this.assetPaths)
for for(String var4 : var2)
for for(Consumer var3 : var1.builderReloadListeners)
public void forceValidationvoid forceValidation(int var1)
public <T> Builder<T> getCachedBuilder<T> Builder<T> getCachedBuilder(int var1, @Nonnull Class<?> var2)
public String getCategoryNameString getCategoryName(@Nonnull Class<?> var1)
public int getIndexint getIndex(@Nullable String var1)
public Object getKeypublic Object getKey()
public int getOrCreateIndexint getOrCreateIndex(String var1)
static int getPlayerGroupIDint getPlayerGroupID()
static int getSelfGroupIDint getSelfGroupID()
public List<String> getTemplateNamesList<String> getTemplateNames()
if if(var2 == null)
if if(var1 < 0)
if if(var2 != null)
if if(var2 >= 0)
if if(var2 != null)
if if(var6 < 0)
if if(this.autoReload)
if if(var11 != null)
if if(!var4)
if if(var15 != null)
if if(var16 != null)
if if(var41 == null)
if if(var3 != null)
if if(var10 == BuilderManager.TestType.FAILING)
if if(var15 != null)
if if(var10 == BuilderManager.TestType.FAILING)
if if(var45 == null)
if if(var1 == null)
if if(var2 == null)
if if(var1 < 0)
if if(var3 == null)
if if(!this.setup)
if if(var2 != null)
if if(var2 != null)
if if(var5 != null)
if if(var2)
if if(var4 >= 0)
if if(var2 != null)
if if(var2 >= 0)
if if(var3 == null)
if if(var8 == null)
if if(var5 instanceof OfflineValidatable)
if if(var9 == null)
if if(var5 == null)
if if(var10 != EventKind.ENTRY_CREATE && var10 != EventKind.ENTRY_MODIFY)
if if(var10 == EventKind.ENTRY_DELETE)
if if(var11 >= 0)
if if(BuilderManager.this.autoReload)
protected boolean isDependantboolean isDependant(@Nonnull Builder<?> var1, int var2, int var3)
public boolean isEmptyboolean isEmpty()
static boolean isIgnoredFileboolean isIgnoredFile(@Nonnull Path var0)
static boolean isJsonFileboolean isJsonFile(@Nonnull Path var0)
static boolean isJsonFileNameboolean isJsonFileName(@Nonnull Path var0, EventKind var1)
private void iterateDependenciesvoid iterateDependencies(@Nonnull IntIterator var1, @Nonnull IntSet var2, @Nonnull IntArrayList var3)
public boolean loadBuildersboolean loadBuilders(@Nonnull AssetPack var1, final boolean var2)
public int loadFileint loadFile(@Nonnull Path var1, boolean var2, @Nonnull List<String> var3)
public int loadFileint loadFile(@Nonnull Path var1, @Nonnull List<String> var2, @Nullable Object2IntMap<String> var3, boolean var4, boolean var5)
public void onAllBuildersLoadedvoid onAllBuildersLoaded(@Nonnull Int2ObjectMap<BuilderInfo> var1)
static void onBuilderAddedvoid onBuilderAdded(@Nonnull BuilderInfo var0)
abstract onBuilderAdded onBuilderAdded(var5)
static void onBuilderReloadedvoid onBuilderReloaded(@Nonnull BuilderInfo var0)
abstract onBuilderReloaded onBuilderReloaded(var11)
abstract onBuilderReloaded onBuilderReloaded(var3)
public void registerAssetPathvoid registerAssetPath(@Nonnull String var1, @Nonnull String var2)
public <T> void registerFactory<T> void registerFactory(@Nonnull BuilderFactory<T> var1)
private void reloadDependantsvoid reloadDependants(int var1)
private void removeBuildervoid removeBuilder(int var1)
private void removeBuildervoid removeBuilder(String var1)
static void saveDescriptorsvoid saveDescriptors(List<BuilderDescriptor> var0, @Nonnull Path var1)
static void sendReloadNotificationvoid sendReloadNotification(Message var0, @Nonnull Set<String> var1)
public void setAutoReloadvoid setAutoReload(boolean var1)
public boolean testpublic boolean test(Path var1, EventKind var2)
public void unloadBuildersvoid unloadBuilders(AssetPack var1)
public void validateAllLoadedBuildersvoid validateAllLoadedBuilders(@Nonnull Int2ObjectMap<BuilderInfo> var1, boolean var2, @Nonnull List<String> var3)
static void validateAllSpawnableNPCsvoid validateAllSpawnableNPCs(@Nonnull Int2ObjectMap<BuilderInfo> var0, @Nonnull List<String> var1)
abstract validateAllSpawnableNPCs validateAllSpawnableNPCs(var1, var3)
abstract validateAllSpawnableNPCs validateAllSpawnableNPCs(var4, var3)
public boolean validateBuilderboolean validateBuilder(@Nonnull BuilderInfo var1)
private boolean validateBuilderboolean validateBuilder(@Nonnull BuilderInfo var1, @Nonnull IntSet var2, @Nonnull IntArrayList var3)
static void validateOfflineValidatableBuildersvoid validateOfflineValidatableBuilders(@Nonnull Int2ObjectMap<BuilderInfo> var0, @Nonnull List<String> var1)
abstract validateOfflineValidatableBuilders validateOfflineValidatableBuilders(var1, var3)
abstract validateOfflineValidatableBuilders validateOfflineValidatableBuilders(var4, var3)
public FileVisitResult visitFilepublic FileVisitResult visitFile(@Nonnull Path var1, @Nonnull BasicFileAttributes var2)
public FileVisitResult visitFilepublic FileVisitResult visitFile(@Nonnull Path var1, @Nonnull BasicFileAttributes var2x)

Исходный код

Показать/скрыть
class="kw">package com.hypixel.hytale.server.npc.asset.builder;

class="kw">import com.google.gson.FieldNamingPolicy;
class="kw">import com.google.gson.Gson;
class="kw">import com.google.gson.GsonBuilder;
class="kw">import com.google.gson.JsonElement;
class="kw">import com.google.gson.JsonObject;
class="kw">import com.google.gson.JsonParser;
class="kw">import com.google.gson.JsonPrimitive;
class="kw">import com.google.gson.JsonSerializer;
class="kw">import com.google.gson.stream.JsonReader;
class="kw">import com.hypixel.fastutil.ints.Int2ObjectConcurrentHashMap;
class="kw">import com.hypixel.hytale.assetstore.AssetExtraInfo;
class="kw">import com.hypixel.hytale.assetstore.AssetPack;
class="kw">import com.hypixel.hytale.assetstore.map.CaseInsensitiveHashStrategy;
class="kw">import com.hypixel.hytale.codec.schema.SchemaContext;
class="kw">import com.hypixel.hytale.codec.schema.config.ObjectSchema;
class="kw">import com.hypixel.hytale.codec.schema.config.Schema;
class="kw">import com.hypixel.hytale.codec.schema.config.StringSchema;
class="kw">import com.hypixel.hytale.common.util.ArrayUtil;
class="kw">import com.hypixel.hytale.event.IEventDispatcher;
class="kw">import com.hypixel.hytale.logger.sentry.SkipSentryException;
class="kw">import com.hypixel.hytale.protocol.packets.interface_.NotificationStyle;
class="kw">import com.hypixel.hytale.server.core.HytaleServer;
class="kw">import com.hypixel.hytale.server.core.Message;
class="kw">import com.hypixel.hytale.server.core.asset.AssetModule;
class="kw">import com.hypixel.hytale.server.core.asset.monitor.AssetMonitor;
class="kw">import com.hypixel.hytale.server.core.asset.monitor.AssetMonitorHandler;
class="kw">import com.hypixel.hytale.server.core.asset.monitor.EventKind;
class="kw">import com.hypixel.hytale.server.core.asset.type.model.config.Model;
class="kw">import com.hypixel.hytale.server.core.asset.type.model.config.ModelAsset;
class="kw">import com.hypixel.hytale.server.core.util.NotificationUtil;
class="kw">import com.hypixel.hytale.server.core.util.io.FileUtil;
class="kw">import com.hypixel.hytale.server.npc.AllNPCsLoadedEvent;
class="kw">import com.hypixel.hytale.server.npc.NPCPlugin;
class="kw">import com.hypixel.hytale.server.npc.asset.builder.providerevaluators.ProviderEvaluatorTypeRegistry;
class="kw">import com.hypixel.hytale.server.npc.asset.builder.validators.ValidatorTypeRegistry;
class="kw">import com.hypixel.hytale.server.npc.decisionmaker.core.Evaluator;
class="kw">import com.hypixel.hytale.server.npc.role.Role;
class="kw">import com.hypixel.hytale.server.npc.util.expression.ExecutionContext;
class="kw">import com.hypixel.hytale.server.npc.util.expression.StdLib;
class="kw">import com.hypixel.hytale.server.npc.validators.NPCLoadTimeValidationHelper;
class="kw">import com.hypixel.hytale.server.spawning.ISpawnableWithModel;
class="kw">import com.hypixel.hytale.server.spawning.LoadedNPCEvent;
class="kw">import com.hypixel.hytale.sneakythrow.SneakyThrow;
class="kw">import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
class="kw">import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
class="kw">import it.unimi.dsi.fastutil.ints.IntArrayList;
class="kw">import it.unimi.dsi.fastutil.ints.IntIterator;
class="kw">import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
class="kw">import it.unimi.dsi.fastutil.ints.IntSet;
class="kw">import it.unimi.dsi.fastutil.objects.Object2IntMap;
class="kw">import it.unimi.dsi.fastutil.objects.Object2IntMaps;
class="kw">import it.unimi.dsi.fastutil.objects.Object2IntOpenCustomHashMap;
class="kw">import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
class="kw">import it.unimi.dsi.fastutil.objects.ObjectArrayList;
class="kw">import it.unimi.dsi.fastutil.objects.ObjectIterator;
class="kw">import it.unimi.dsi.fastutil.objects.ObjectListIterator;
class="kw">import java.io.BufferedReader;
class="kw">import java.io.BufferedWriter;
class="kw">import java.io.IOException;
class="kw">import java.nio.file.FileVisitResult;
class="kw">import java.nio.file.Files;
class="kw">import java.nio.file.Path;
class="kw">import java.nio.file.SimpleFileVisitor;
class="kw">import java.nio.file.attribute.BasicFileAttributes;
class="kw">import java.time.Duration;
class="kw">import java.time.Period;
class="kw">import java.util.Collection;
class="kw">import java.util.HashMap;
class="kw">import java.util.HashSet;
class="kw">import java.util.List;
class="kw">import java.util.Locale;
class="kw">import java.util.Map;
class="kw">import java.util.Objects;
class="kw">import java.util.Set;
class="kw">import java.util.Map.Entry;
class="kw">import java.util.concurrent.atomic.AtomicInteger;
class="kw">import java.util.concurrent.locks.ReentrantReadWriteLock;
class="kw">import java.util.function.BiConsumer;
class="kw">import java.util.function.BiPredicate;
class="kw">import java.util.function.Consumer;
class="kw">import java.util.function.Predicate;
class="kw">import java.util.logging.Level;
class="kw">import javax.annotation.Nonnull;
class="kw">import javax.annotation.Nullable;

class="kw">public class BuilderManager {
   class="kw">public class="kw">static class="kw">final String CONTENT_KEY = "Content";
   class="kw">private class="kw">static class="kw">final String CLASS_KEY = "Class";
   class="kw">private class="kw">static class="kw">final String TEST_TYPE_KEY = "TestType";
   class="kw">private class="kw">static class="kw">final String FAIL_REASON_KEY = "FailReason";
   class="kw">private class="kw">static class="kw">final String PLAYER_GROUP_TAG = "$player";
   class="kw">private class="kw">static class="kw">final String SELF_GROUP_TAG = "$self";
   class="kw">private class="kw">static class="kw">final int PLAYER_GROUP_ID = 0;
   class="kw">private class="kw">static class="kw">final int SELF_GROUP_ID = 1;
   class="kw">private class="kw">final Int2ObjectConcurrentHashMap<BuilderInfo> builderCache = new Int2ObjectConcurrentHashMap<>();
   class="kw">private class="kw">final String elementTypeName = "NPC";
   class="kw">private class="kw">final String defaultFileType = "Role";
   class="kw">private boolean autoReload;
   class="kw">private class="kw">final Map<Class<?>, BuilderFactory<?>> factoryMap = new HashMap<>();
   class="kw">private class="kw">final Map<String, Class<?>> categoryNames = new HashMap<>();
   @Nonnull
   class="kw">private class="kw">final Object2IntMap<String> nameToIndexMap;
   class="kw">private class="kw">final AtomicInteger nextIndex = new AtomicInteger();
   class="kw">private class="kw">final ReentrantReadWriteLock indexLock = new ReentrantReadWriteLock();
   class="kw">private boolean setup;
   class="kw">private class="kw">final List<BuilderManager.AssetPath> assetPaths = new ObjectArrayList(List.of(new BuilderManager.AssetPath("Server/NPC/Roles", "NPC")));
   class="kw">private class="kw">final List<Consumer<BuilderInfo>> builderReloadListeners = new ObjectArrayList();
   @Nullable
   class="kw">public class="kw">static BuilderManager SCHEMA_BUILDER_MANAGER;

   class="kw">public BuilderManager() {
      this.nameToIndexMap = new Object2IntOpenCustomHashMap(CaseInsensitiveHashStrategy.getInstance());
      this.nameToIndexMap.defaultReturnValue(Integer.MIN_VALUE);
      if (this.getOrCreateIndex("$player") != 0 || this.getOrCreateIndex("$self") != 1) {
         throw new IllegalStateException("Group tags must occupy the first indexes of the name-to-index map");
      }
   }

   class="kw">public <T> void registerFactory(@Nonnull BuilderFactory<T> var1) {
      Class var2 = var1.getCategory();
      if (var2 == null) {
         throw new IllegalArgumentException();
      }

      if (this.factoryMap.containsKey(var2)) {
         throw new IllegalArgumentException(var1.getClass().getSimpleName());
      }

      this.factoryMap.put(var2, var1);
   }

   class="kw">public void addCategory(String var1, Class<?> var2) {
      this.categoryNames.put(var1, var2);
   }

   class="kw">public void registerAssetPath(@Nonnull String var1, @Nonnull String var2) {
      if (this.assetPaths.stream().noneMatch(var1x -> var1x.path().equals(var1))) {
         this.assetPaths.add(new BuilderManager.AssetPath(var1, var2));
      }
   }

   class="kw">public void addBuilderReloadListener(@Nonnull Consumer<BuilderInfo> var1) {
      this.builderReloadListeners.add(var1);
   }

   class="kw">public String getCategoryName(@Nonnull Class<?> var1) {
      for (Entry var3 : this.categoryNames.entrySet()) {
         if (var3.getValue() == var1) {
            class="kw">return var3.getKey();
         }
      }

      class="kw">return var1.getSimpleName();
   }

   class="kw">public int getIndex(@Nullable String var1) {
      if (var1 != null && !var1.isEmpty()) {
         this.indexLock.readLock().lock();

         try {
            class="kw">return this.nameToIndexMap.getInt(var1);
         } class="kw">finally {
            this.indexLock.readLock().unlock();
         }
      } else {
         class="kw">return Integer.MIN_VALUE;
      }
   }

   class="kw">public void setAutoReload(boolean var1) {
      this.autoReload = var1;
   }

   @Nullable
   class="kw">public String lookupName(int var1) {
      if (var1 < 0) {
         class="kw">return null;
      }

      BuilderInfo var2 = this.builderCache.get(var1);
      if (var2 != null) {
         class="kw">return var2.getKeyName();
      }

      this.indexLock.readLock().lock();

      try {
         ObjectIterator var3 = Object2IntMaps.fastIterator(this.nameToIndexMap);

         while (var3.hasNext()) {
            it.unimi.dsi.fastutil.objects.Object2IntMap.Entry var4 = (it.unimi.dsi.fastutil.objects.Object2IntMap.Entry<String>)var3.next();
            if (var4.getIntValue() == var1) {
               class="kw">return (String)var4.getKey();
            }
         }
      } class="kw">finally {
         this.indexLock.readLock().unlock();
      }

      class="kw">return null;
   }

   class="kw">public int getOrCreateIndex(String var1) {
      this.indexLock.writeLock().lock();

      try {
         int var2 = this.nameToIndexMap.getInt(var1);
         if (var2 >= 0) {
            class="kw">return var2;
         }

         var2 = this.nextIndex.getAndIncrement();
         this.nameToIndexMap.put(var1, var2);
         class="kw">return var2;
      } class="kw">finally {
         this.indexLock.writeLock().unlock();
      }
   }

   @Nullable
   class="kw">public BuilderInfo tryGetBuilderInfo(int var1) {
      class="kw">return var1 < 0 ? null : this.builderCache.get(var1);
   }

   class="kw">public void unloadBuilders(AssetPack var1) {
      AssetMonitor var2 = AssetModule.get().getAssetMonitor();

      try {
         for (BuilderManager.AssetPath var4 : this.assetPaths) {
            Path var5 = var1.getRoot().resolve(var4.path());
            if (Files.isDirectory(var5)) {
               if (var2 != null) {
                  var2.removeMonitorDirectoryFiles(var5, var1);
               }

               Files.walkFileTree(var5, FileUtil.DEFAULT_WALK_TREE_OPTIONS_SET, Integer.MAX_VALUE, new SimpleFileVisitor<Path>() {
                  @Nonnull
                  class="kw">public FileVisitResult visitFile(@Nonnull Path var1, @Nonnull BasicFileAttributes var2) {
                     if (BuilderManager.isJsonFile(var1) && !BuilderManager.isIgnoredFile(var1)) {
                        String var3 = BuilderManager.builderNameFromPath(var1);
                        BuilderManager.this.removeBuilder(var3);
                        NPCPlugin.get().getLogger().at(Level.INFO).log("Deleted builder %s", var3);
                     }

                     class="kw">return FileVisitResult.CONTINUE;
                  }
               });
            }
         }
      } catch (IOException var6) {
         throw SneakyThrow.sneakyThrow(var6);
      }
   }

   class="kw">public boolean loadBuilders(@Nonnull AssetPack var1, class="kw">final boolean var2) {
      boolean var3 = true;
      NPCPlugin.get().getLogger().at(Level.INFO).log("Starting to load builders!");
      ObjectArrayList var4 = new ObjectArrayList();

      try {
         AssetMonitor var5 = AssetModule.get().getAssetMonitor();
         class="kw">final ObjectArrayList var6 = new ObjectArrayList();

         for (BuilderManager.AssetPath var8 : this.assetPaths) {
            Path var9 = var1.getRoot().resolve(var8.path());
            if (Files.isDirectory(var9)) {
               if (var5 != null && !var1.isImmutable()) {
                  var5.removeMonitorDirectoryFiles(var9, var1);
                  var5.monitorDirectoryFiles(var9, new BuilderManager.BuilderAssetMonitorHandler(var1, var2));
               }

               class="kw">final Object2IntOpenHashMap var10 = new Object2IntOpenHashMap();
               int var11 = this.builderCache.size();
               Files.walkFileTree(var9, FileUtil.DEFAULT_WALK_TREE_OPTIONS_SET, Integer.MAX_VALUE, new SimpleFileVisitor<Path>() {
                  @Nonnull
                  class="kw">public FileVisitResult visitFile(@Nonnull Path var1, @Nonnull BasicFileAttributes var2x) {
                     if (BuilderManager.isJsonFile(var1) && !BuilderManager.isIgnoredFile(var1)) {
                        BuilderManager.this.loadFile(var1, var6, var10, var2, false);
                     }

                     class="kw">return FileVisitResult.CONTINUE;
                  }
               });
               StringBuilder var12 = new StringBuilder();
               var12.append("Loaded ").append(this.builderCache.size() - var11).append(' ').append(var8.label()).append(" configurations");
               ObjectIterator var13 = var10.object2IntEntrySet().iterator();

               while (var13.hasNext()) {
                  it.unimi.dsi.fastutil.objects.Object2IntMap.Entry var14 = (it.unimi.dsi.fastutil.objects.Object2IntMap.Entry<String>)var13.next();
                  var12.append(", ").append((String)var14.getKey()).append(": ").append(var14.getIntValue());
               }

               var4.add(var12.toString());
            }
         }

         Int2ObjectOpenHashMap var19 = new Int2ObjectOpenHashMap();

         for (BuilderInfo var22 : this.builderCache.values()) {
            try {
               if (this.validateBuilder(var22)) {
                  var19.put(var22.getIndex(), var22);
               } else {
                  var3 = false;
               }
            } catch (IllegalArgumentException | IllegalStateException var15) {
               var3 = false;
               var6.add(String.format("%s: %s", var22.getKeyName(), var15.getMessage()));
            }
         }

         this.setup = true;
         this.validateAllLoadedBuilders(var19, false, var6);
         if (!var6.isEmpty()) {
            var3 = false;
            ObjectListIterator var21 = var6.iterator();

            while (var21.hasNext()) {
               String var23 = (String)var21.next();
               NPCPlugin.get().getLogger().at(Level.SEVERE).log("FAIL: %s", var23);
            }
         }

         var6.clear();
         this.onAllBuildersLoaded(var19);
      } catch (IOException var16) {
         throw new SkipSentryException(new RuntimeException(var16));
      }

      ObjectListIterator var17 = var4.iterator();

      while (var17.hasNext()) {
         String var18 = (String)var17.next();
         NPCPlugin.get().getLogger().at(Level.INFO).log("%s", var18);
      }

      class="kw">return var3;
   }

   class="kw">private void finishLoadingBuilders(@Nonnull Int2ObjectOpenHashMap<BuilderInfo> var1, @Nonnull List<String> var2) {
      this.onAllBuildersLoaded(var1);
      this.validateAllLoadedBuilders(var1, true, var2);
      if (!var2.isEmpty()) {
         for (String var4 : var2) {
            NPCPlugin.get().getLogger().at(Level.SEVERE).log("%s", var4);
         }
      }

      var2.clear();
   }

   class="kw">public void assetEditorLoadFile(@Nonnull Path var1) {
      HashSet var2 = new HashSet<>();
      ObjectArrayList var3 = new ObjectArrayList();
      Int2ObjectOpenHashMap var4 = new Int2ObjectOpenHashMap();
      HashSet var5 = new HashSet<>();

      try {
         int var6 = this.loadFile(var1, var3, null, true, true);
         if (var6 < 0) {
            class="kw">return;
         }

         String var7 = builderNameFromPath(var1);
         NPCPlugin.get().getLogger().at(Level.INFO).log("Reloaded builder %s", var7);
         var5.add(var7);

         for (BuilderInfo var9 : this.builderCache.values()) {
            if (this.isDependant(var9.getBuilder(), var9.getIndex(), var6)) {
               var9.setNeedsValidation();
            }
         }

         if (this.autoReload) {
            this.reloadDependants(var6);
         }

         BuilderInfo var11 = this.builderCache.get(var6);
         onBuilderReloaded(var11);
         var4.put(var6, var11);
      } catch (Throwable var10) {
         NPCPlugin.get().getLogger().at(Level.SEVERE).log("Failed to reload config %s: %s", var1, var10.getMessage());
         var2.add(builderNameFromPath(var1) + ": " + var10.getMessage());
      }

      sendReloadNotification(Message.translation("server.general.assetstore.reloadAssets").param("class", "builder"), var5);
      sendReloadNotification(Message.translation("server.general.assetstore.loadFailed").param("class", "builder"), var2);
      this.finishLoadingBuilders(var4, var3);
   }

   class="kw">public void assetEditorRemoveFile(@Nonnull Path var1) {
      String var2 = builderNameFromPath(var1);
      this.removeBuilder(var2);
      NPCPlugin.get().getLogger().at(Level.INFO).log("Deleted builder %s", var2);
      sendReloadNotification(Message.translation("server.general.assetstore.removedAssets").param("class", "builder"), Set.of(var2));
      ObjectArrayList var3 = new ObjectArrayList();
      this.finishLoadingBuilders(new Int2ObjectOpenHashMap(), var3);
   }

   class="kw">public int loadFile(@Nonnull Path var1, boolean var2, @Nonnull List<String> var3) {
      class="kw">return this.loadFile(var1, var3, null, false, var2);
   }

   class="kw">public int loadFile(@Nonnull Path var1, @Nonnull List<String> var2, @Nullable Object2IntMap<String> var3, boolean var4, boolean var5) {
      int var6 = var2.size();

      JsonObject var7;
      try (BufferedReader var8 = Files.newBufferedReader(var1)) {
         JsonReader var9 = new JsonReader(var8);

         try {
            var7 = JsonParser.parseReader(var9).getAsJsonObject();
         } catch (Throwable var36) {
            try {
               var9.close();
            } catch (Throwable var30) {
               var36.addSuppressed(var30);
            }

            throw var36;
         }

         var9.close();
      } catch (Exception var38) {
         var2.add(var1 + ": Failed to load builder: " + var38.getMessage());
         class="kw">return Integer.MIN_VALUE;
      }

      String var39 = "Role";
      JsonElement var40 = var7;
      BuilderManager.TestType var10 = null;
      JsonElement var11 = var7.get("TestType");
      if (var11 != null) {
         try {
            var10 = Enum.valueOf(BuilderManager.TestType.class, var11.getAsString().toUpperCase(Locale.ROOT));
         } catch (Exception var35) {
            var2.add(var1 + ": " + var35.getMessage());
         }

         if (!var4) {
            class="kw">return Integer.MIN_VALUE;
         }
      }

      String var12 = builderNameFromPath(var1);
      String var13 = null;
      StateMappingHelper var14 = new StateMappingHelper();
      JsonElement var15 = var7.get("Class");
      if (var15 != null) {
         var39 = var15.getAsString();
         var14.readComponentDefaultLocalState(var7);
         JsonElement var16 = var7.get("Interface");
         if (var16 != null) {
            var13 = var16.getAsString();
         }
      }

      Class var41 = this.categoryNames.get(var39);
      if (var41 == null) {
         var2.add(var1 + ": Failed to load builder, unknown class " + var39);
         class="kw">return Integer.MIN_VALUE;
      }

      if (var3 != null) {
         JsonElement var17 = var7.get("Type");
         String var18 = var10 == null ? (var17 != null ? var17.getAsString() : var39) : "Test";
         var3.mergeInt(var18, 1, Integer::sum);
      }

      BuilderFactory var42 = this.getFactory(var41);

      Builder var43;
      try {
         var43 = var42.createBuilder(var40);
      } catch (Exception var34) {
         var2.add(var1 + ": " + var34.getMessage());
         class="kw">return Integer.MIN_VALUE;
      }

      String var19 = var1.toString();
      this.checkIfDeprecated(var43, var42, var40, var19, var39);
      var43.setLabel(var39 + "|" + var42.getKeyName(var40));
      var43.ignoreAttribute("TestType");
      if (var10 == BuilderManager.TestType.FAILING) {
         var43.ignoreAttribute("FailReason");
      }

      var43.ignoreAttribute("Parameters");
      BuilderParameters var20 = new BuilderParameters(StdLib.getInstance(), var19, var13);

      try {
         var20.readJSON(var7, var14);
      } catch (Exception var33) {
         var2.add(var19 + ": Failed to load builder, 'Parameters' section invalid: " + var33.getMessage());
         class="kw">return Integer.MIN_VALUE;
      }

      if (var15 != null) {
         var43.ignoreAttribute("Class");
         var43.ignoreAttribute("Interface");
         var43.ignoreAttribute("DefaultState");
         var43.ignoreAttribute("ResetOnStateChange");
      }

      var20.addParametersToScope();
      InternalReferenceResolver var21 = new InternalReferenceResolver();
      AssetExtraInfo.Data var22 = new AssetExtraInfo.Data(null, var12, null);
      AssetExtraInfo var23 = new AssetExtraInfo<>(var22);
      ObjectArrayList var24 = new ObjectArrayList();
      BuilderValidationHelper var25 = new BuilderValidationHelper(
         var19,
         new FeatureEvaluatorHelper(var43.canRequireFeature()),
         var21,
         var14,
         new InstructionContextHelper(InstructionType.Component),
         var23,
         var24,
         var2
      );

      try {
         var43.readConfig(null, var40, this, var20, var25);
      } catch (Exception var32) {
         var2.add(var19 + ": Failed to load builder: " + var32.getMessage());
         class="kw">return Integer.MIN_VALUE;
      }

      var21.validateInternalReferences(var19, var2);
      var22.loadContainedAssets(var5);
      ObjectListIterator var26 = var24.iterator();

      while (var26.hasNext()) {
         Evaluator var27 = (Evaluator<?>)var26.next();
         var27.initialise();
      }

      var21.optimise();
      var20.disposeCompileContext();
      var14.validate(var19, var2);
      var14.optimise();
      BuilderInfo var44 = this.tryGetBuilderInfo(this.getIndex(var12));
      if (var44 != null && var44.getPath() != null) {
         try {
            if (!Files.isSameFile(var1, var44.getPath())) {
               NPCPlugin.get().getLogger().at(Level.WARNING).log("Replacing asset '%s' of file '%s' with other file '%s'", var12, var44.getPath(), var1);
            }
         } catch (IOException var31) {
         }
      }

      if (var10 == BuilderManager.TestType.FAILING) {
         JsonElement var45 = var7.get("FailReason");
         if (var45 == null) {
            var2.add(var1 + ": Missing fail reason!");
            class="kw">return Integer.MIN_VALUE;
         }

         if (var2.size() == var6) {
            var2.add(var1 + ": Should have failed validation: " + var45.getAsString());
            class="kw">return Integer.MIN_VALUE;
         }

         if (var2.size() - var6 > 1) {
            var2.add(var1 + ": Should have failed validation: " + var45.getAsString() + ", but additional errors were also detected.");
            class="kw">return Integer.MIN_VALUE;
         }

         String var28 = var2.removeLast();
         if (!var28.contains(var45.getAsString())) {
            var2.add(var1 + ": Should have failed validation: " + var45.getAsString() + ", but was instead: " + var28);
            class="kw">return Integer.MIN_VALUE;
         }

         if (NPCPlugin.get().isLogFailingTestErrors()) {
            NPCPlugin.get().getLogger().at(Level.WARNING).log("Expected test failure: %s", var28);
         }

         class="kw">return Integer.MIN_VALUE;
      } else {
         class="kw">return var2.size() > var6 ? Integer.MIN_VALUE : this.cacheBuilder(var12, var43, var1);
      }
   }

   class="kw">public boolean validateBuilder(@Nonnull BuilderInfo var1) {
      if (var1.isValidated()) {
         class="kw">return var1.isValid();
      }

      if (!var1.canBeValidated()) {
         class="kw">return false;
      }

      Builder var2 = var1.getBuilder();
      class="kw">return var2.getDependencies().isEmpty() && !var2.hasDynamicDependencies()
         ? var1.setValidated(true)
         : this.validateBuilder(var1, new IntOpenHashSet(), new IntArrayList());
   }

   @Nonnull
   class="kw">public <T> BuilderFactory<T> getFactory(@Nonnull Class<?> var1) {
      if (var1 == null) {
         throw new IllegalArgumentException("No factory class supplied!");
      } else {
         BuilderFactory var2 = (BuilderFactory<T>)this.factoryMap.get(var1);
         if (var2 == null) {
            throw new NullPointerException(String.format("Factory for type '%s' is not registered!", var1.getSimpleName()));
         } else if (var2.getCategory() != var1) {
            throw new IllegalArgumentException(
               String.format("Factory class mismatch! Expected %s, was %s", var1.getSimpleName(), var2.getCategory().getSimpleName())
            );
         } else {
            class="kw">return var2;
         }
      }
   }

   @Nonnull
   class="kw">public BuilderInfo getCachedBuilderInfo(int var1, @Nonnull Class<?> var2) {
      if (var1 < 0) {
         throw new SkipSentryException(new IllegalArgumentException("Builder asset can't have negative index " + var1));
      } else {
         BuilderInfo var3 = this.tryGetCachedBuilderInfo(var1, var2);
         if (var3 == null) {
            throw new SkipSentryException(new IllegalArgumentException(String.format("Asset '%s' (%s) is not available", this.lookupName(var1), var1)));
         } else {
            class="kw">return var3;
         }
      }
   }

   @Nullable
   class="kw">public <T> Builder<T> tryGetCachedValidBuilder(int var1, @Nonnull Class<?> var2) {
      BuilderInfo var3 = this.tryGetCachedBuilderInfo(var1, var2);
      class="kw">return (Builder<T>)(var3 != null && var3.isValid() ? var3.getBuilder() : null);
   }

   class="kw">public <T> Builder<T> getCachedBuilder(int var1, @Nonnull Class<?> var2) {
      BuilderInfo var3 = this.getCachedBuilderInfo(var1, var2);
      class="kw">return (Builder<T>)var3.getBuilder();
   }

   class="kw">public boolean isEmpty() {
      class="kw">return this.builderCache.isEmpty();
   }

   @Nonnull
   class="kw">public Int2ObjectMap<BuilderInfo> getAllBuilders() {
      Int2ObjectOpenHashMap var1 = new Int2ObjectOpenHashMap();

      for (BuilderInfo var3 : this.builderCache.values()) {
         var1.put(var3.getIndex(), var3);
      }

      class="kw">return var1;
   }

   class="kw">public <T class="kw">extends Collection<?>> T collectMatchingBuilders(T var1, @Nonnull Predicate<BuilderInfo> var2, @Nonnull BiConsumer<BuilderInfo, T> var3) {
      for (BuilderInfo var5 : this.builderCache.values()) {
         if (var2.test(var5)) {
            var3.accept(var5, var1);
         }
      }

      class="kw">return var1;
   }

   @Nonnull
   class="kw">public Object2IntMap<String> getNameToIndexMap() {
      Object2IntOpenHashMap var1 = new Object2IntOpenHashMap();
      if (!this.setup) {
         class="kw">return Object2IntMaps.unmodifiable(var1);
      }

      this.indexLock.readLock().lock();

      try {
         ObjectIterator var2 = Object2IntMaps.fastIterator(this.nameToIndexMap);

         while (var2.hasNext()) {
            it.unimi.dsi.fastutil.objects.Object2IntMap.Entry var3 = (it.unimi.dsi.fastutil.objects.Object2IntMap.Entry<String>)var2.next();
            var1.put((String)var3.getKey(), var3.getIntValue());
         }
      } class="kw">finally {
         this.indexLock.readLock().unlock();
      }

      class="kw">return Object2IntMaps.unmodifiable(var1);
   }

   @Nullable
   class="kw">public <T> BuilderInfo findMatchingBuilder(@Nonnull BiPredicate<BuilderInfo, T> var1, T var2) {
      for (BuilderInfo var4 : this.builderCache.values()) {
         if (var1.test(var4, var2)) {
            class="kw">return var4;
         }
      }

      class="kw">return null;
   }

   @Nullable
   class="kw">public BuilderInfo getBuilderInfo(Builder<?> var1) {
      class="kw">return this.findMatchingBuilder((var0, var1x) -> var0.getBuilder() == var1x, var1);
   }

   class="kw">public List<String> getTemplateNames() {
      class="kw">return this.collectMatchingBuilders(new ObjectArrayList(), var0 -> true, (var0, var1) -> var1.add(var0.getKeyName()));
   }

   class="kw">public void forceValidation(int var1) {
      BuilderInfo var2 = this.tryGetBuilderInfo(var1);
      if (var2 != null) {
         IntSet var3 = this.computeAllDependencies(var2.getBuilder(), var2.getIndex());
         var2.setForceValidation();
         IntIterator var4 = var3.iterator();

         while (var4.hasNext()) {
            var2 = this.tryGetBuilderInfo(var4.nextInt());
            if (var2 != null) {
               var2.setForceValidation();
            }
         }
      }
   }

   class="kw">public void checkIfDeprecated(@Nonnull Builder<?> var1, @Nonnull BuilderFactory<?> var2, @Nonnull JsonElement var3, String var4, String var5) {
      if (var1.isDeprecated()) {
         NPCPlugin.get()
            .getLogger()
            .at(Level.WARNING)
            .log(
               "Builder %s of type %s is deprecated and should be replaced in %s: %s",
               var2.getKeyName(var3),
               this.getCategoryName(var2.getCategory()),
               var5,
               var4
            );
      }
   }

   @Nonnull
   class="kw">public Schema generateSchema(@Nonnull SchemaContext var1) {
      class="kw">return this.generateSchema(var1, Role.class);
   }

   @Nonnull
   class="kw">public Schema generateSchema(@Nonnull SchemaContext var1, @Nonnull Class<?> var2) {
      try {
         SCHEMA_BUILDER_MANAGER = this;
         BuilderFactory var3 = this.factoryMap.get(var2);
         Schema var4 = var3.toSchema(var1, true);
         ObjectSchema var5 = new ObjectSchema();
         var5.setRequired("Class", "Type");
         StringSchema var6 = new StringSchema();
         var6.setEnum(this.categoryNames.keySet().toArray(String[]::new));
         var5.setProperties(Map.of("Class", var6));
         var5.setProperties(Map.of("Type", StringSchema.constant("Component")));
         Schema var7 = new Schema();
         var7.setIf(var5);
         Schema[] var8 = new Schema[this.categoryNames.size()];
         int var9 = 0;

         for (Entry var11 : this.categoryNames.entrySet()) {
            BuilderFactory var12 = this.getFactory(var11.getValue());
            Schema var13 = var12.toSchema(var1, true);
            Schema var14 = new Schema();
            ObjectSchema var15 = new ObjectSchema();
            var15.setProperties(Map.of("Class", StringSchema.constant(var11.getKey())));
            var14.setIf(var15);
            var14.setThen(var13);
            var14.setElse(false);
            var8[var9++] = var14;
         }

         var7.setThen(Schema.anyOf(var8));
         var7.setElse(false);
         var4.getThen().setAnyOf(ArrayUtil.append(var4.getThen().getAnyOf(), var7));
         class="kw">return var4;
      } class="kw">finally {
         SCHEMA_BUILDER_MANAGER = null;
      }
   }

   @Nonnull
   class="kw">public List<BuilderDescriptor> generateDescriptors() {
      ObjectArrayList var1 = new ObjectArrayList();

      for (BuilderFactory var3 : this.factoryMap.values()) {
         String var4 = this.getCategoryName(var3.getCategory());
         Builder var5 = var3.tryCreateDefaultBuilder();
         if (var5 != null) {
            try {
               var1.add(var5.getDescriptor(var4, var4, this));
            } catch (IllegalStateException | NullPointerException var11) {
               NPCPlugin.get().getLogger().at(Level.SEVERE).log("Failed to build descriptor for %s %s: %s", var4, var4, var11.getMessage());
            }
         }

         for (String var7 : var3.getBuilderNames()) {
            Builder var8 = var3.createBuilder(var7);
            Objects.requireNonNull(var8, "Unable to create builder for descriptor generation");
            String var9 = var7 != null && !var7.isEmpty() ? var7 : var4;
            if (!var9.equals("Component")) {
               try {
                  var1.add(var8.getDescriptor(var9, var4, this));
               } catch (IllegalStateException | NullPointerException var12) {
                  NPCPlugin.get().getLogger().at(Level.SEVERE).log("Failed to build descriptor for %s %s: %s", var4, var9, var12.getMessage());
               }
            }
         }
      }

      class="kw">return var1;
   }

   class="kw">public class="kw">static void saveDescriptors(List<BuilderDescriptor> var0, @Nonnull Path var1) {
      try (BufferedWriter var2 = Files.newBufferedWriter(var1)) {
         GsonBuilder var3 = new GsonBuilder().setPrettyPrinting().serializeNulls().setFieldNamingPolicy(FieldNamingPolicy.UPPER_CAMEL_CASE);
         var3.registerTypeAdapter(Duration.class, (JsonSerializer)(var0x, var1x, var2x) -> new JsonPrimitive(var0x.toString()));
         var3.registerTypeAdapter(Period.class, (JsonSerializer)(var0x, var1x, var2x) -> new JsonPrimitive(var0x.toString()));
         ValidatorTypeRegistry.registerTypes(var3);
         ProviderEvaluatorTypeRegistry.registerTypes(var3);
         Gson var4 = var3.create();
         var4.toJson(var0, var2);
      } catch (IOException var7) {
         NPCPlugin.get().getLogger().at(Level.SEVERE).log("Failed to write builder descriptors to %s", var1);
      }
   }

   @Nullable
   class="kw">public Builder<Role> tryGetCachedValidRole(int var1) {
      class="kw">return this.tryGetCachedValidBuilder(var1, Role.class);
   }

   class="kw">public void validateAllLoadedBuilders(@Nonnull Int2ObjectMap<BuilderInfo> var1, boolean var2, @Nonnull List<String> var3) {
      NPCPlugin.get().getLogger().at(Level.INFO).log("Validating loaded configurations...");
      validateAllSpawnableNPCs(var1, var3);
      validateOfflineValidatableBuilders(var1, var3);
      if (var2) {
         Int2ObjectOpenHashMap var4 = new Int2ObjectOpenHashMap();
         var1.forEach(
            (var2x, var3x) -> {
               for (BuilderInfo var5 : this.builderCache.values()) {
                  int var6 = var5.getIndex();
                  Builder var7 = var5.getBuilder();

                  boolean var8;
                  try {
                     var8 = this.isDependant(var7, var6, var2x);
                  } catch (SkipSentryException | IllegalStateException | IllegalArgumentException var10) {
                     NPCPlugin.get()
                        .getLogger()
                        .at(Level.WARNING)
                        .log("Could not check if builder %s was dependent: %s", this.lookupName(var5.getIndex()), var10.getMessage());
                     class="kw">continue;
                  }

                  if ((var7.isSpawnable() || var7 class="kw">instanceof OfflineValidatable) && var8) {
                     var4.put(var6, var5);
                  }
               }
            }
         );
         validateAllSpawnableNPCs(var4, var3);
         validateOfflineValidatableBuilders(var4, var3);
      }

      NPCPlugin.get().getLogger().at(Level.INFO).log("Validation complete.");
   }

   class="kw">public void onAllBuildersLoaded(@Nonnull Int2ObjectMap<BuilderInfo> var1) {
      if (!var1.isEmpty()) {
         IEventDispatcher var2 = HytaleServer.get().getEventBus().dispatchFor(AllNPCsLoadedEvent.class);
         if (var2.hasListener()) {
            var2.dispatch(new AllNPCsLoadedEvent(this.getAllBuilders(), var1));
         }

         this.getAllBuilders().forEach((var1x, var2x) -> {
            if (var2x.needsValidation()) {
               if (var2x.getBuilder().category() == Role.class) {
                  NPCPlugin.get().testAndValidateRole(var2x);
               } else {
                  this.validateBuilder(var2x);
               }
            }
         });
      }
   }

   class="kw">public class="kw">static void onBuilderReloaded(@Nonnull BuilderInfo var0) {
      var0.getBuilder().clearDynamicDependencies();
      if (var0.getBuilder().category() == Role.class) {
         NPCPlugin.reloadNPCsWithRole(var0.getIndex());
      }

      BuilderManager var1 = NPCPlugin.get().getBuilderManager();

      for (Consumer var3 : var1.builderReloadListeners) {
         var3.accept(var0);
      }
   }

   class="kw">public class="kw">static int getPlayerGroupID() {
      class="kw">return 0;
   }

   class="kw">public class="kw">static int getSelfGroupID() {
      class="kw">return 1;
   }

   class="kw">protected class="kw">static void onBuilderAdded(@Nonnull BuilderInfo var0) {
      if (var0.getBuilder().isSpawnable()) {
         IEventDispatcher var1 = HytaleServer.get().getEventBus().dispatchFor(LoadedNPCEvent.class);
         if (var1.hasListener()) {
            var1.dispatch(new LoadedNPCEvent(var0));
         }
      }
   }

   class="kw">protected boolean isDependant(@Nonnull Builder<?> var1, int var2, int var3) {
      class="kw">return var2 == var3 ? true : this.computeAllDependencies(var1, var2).contains(var3);
   }

   class="kw">protected int cacheBuilder(String var1, Builder<?> var2, Path var3) {
      this.indexLock.writeLock().lock();

      int var4;
      try {
         var4 = this.nameToIndexMap.getInt(var1);
         if (var4 >= 0) {
            this.removeBuilder(var4);
         } else {
            var4 = this.nextIndex.getAndIncrement();
            this.nameToIndexMap.put(var1, var4);
         }
      } class="kw">finally {
         this.indexLock.writeLock().unlock();
      }

      BuilderInfo var5 = new BuilderInfo(var4, var1, var2, var3);
      this.builderCache.put(var4, var5);
      onBuilderAdded(var5);
      class="kw">return var4;
   }

   class="kw">private void removeBuilder(int var1) {
      BuilderInfo var2 = this.builderCache.remove(var1);
      if (var2 != null) {
         var2.setRemoved();
      }
   }

   class="kw">private void removeBuilder(String var1) {
      int var2 = this.getIndex(var1);
      if (var2 >= 0) {
         this.removeBuilder(var2);
      }
   }

   @Nullable
   class="kw">private Builder<?> tryGetCachedBuilder(int var1) {
      BuilderInfo var2 = this.tryGetBuilderInfo(var1);
      class="kw">return var2 == null ? null : var2.getBuilder();
   }

   @Nullable
   class="kw">private BuilderInfo tryGetCachedBuilderInfo(int var1, @Nonnull Class<?> var2) {
      BuilderInfo var3 = this.tryGetBuilderInfo(var1);
      if (var3 == null) {
         class="kw">return null;
      } else {
         Builder var4 = var3.getBuilder();
         if (var4.category() != var2) {
            throw new IllegalArgumentException(
               String.format(
                  "Asset '%s'(%s) is different type. Is '%s' but should be '%s'", this.lookupName(var1), var1, var4.category().getName(), var2.getName()
               )
            );
         } else {
            class="kw">return var3;
         }
      }
   }

   class="kw">private class="kw">static void validateAllSpawnableNPCs(@Nonnull Int2ObjectMap<BuilderInfo> var0, @Nonnull List<String> var1) {
      var0.forEach(
         (var1x, var2) -> {
            Builder var3 = var2.getBuilder();
            if (var3.isSpawnable() && var3 class="kw">instanceof ISpawnableWithModel var4) {
               ExecutionContext var5 = new ExecutionContext(var3.getBuilderParameters().createScope());
               String var6 = var2.getPath().toString();

               String var7;
               try {
                  var7 = var4.getSpawnModelName(var5, var4.createModifierScope(var5));
               } catch (SkipSentryException | IllegalStateException var12) {
                  var1.add(String.format("%s: %s", var6, var12.getMessage()));
                  var2.setValidated(false);
                  class="kw">return;
               }

               ModelAsset var8 = ModelAsset.getAssetMap().getAsset(var7);
               if (var8 == null) {
                  var1.add(String.format("%s: Model %s does not exist.", var6, var7));
                  var2.setValidated(false);
               } else {
                  Model var9 = Model.createScaledModel(var8, var8.getMaxScale());
                  Builder var10 = var2.getBuilder();
                  NPCLoadTimeValidationHelper var11 = new NPCLoadTimeValidationHelper(var6, var9, !var10.isSpawnable());
                  if (!var10.validate(var6, var11, var5, var5.getScope(), var1)
                     || !var11.validateMotionControllers(var1)
                     || !var11.getValueStoreValidator().validate(var1)) {
                     var2.setValidated(false);
                  }
               }
            }
         }
      );
   }

   class="kw">private class="kw">static void validateOfflineValidatableBuilders(@Nonnull Int2ObjectMap<BuilderInfo> var0, @Nonnull List<String> var1) {
      ObjectIterator var2 = var0.entrySet().iterator();

      while (var2.hasNext()) {
         Entry var3 = (Entry<Integer, BuilderInfo>)var2.next();
         BuilderInfo var4 = var3.getValue();
         Builder var5 = var4.getBuilder();
         if (var5 class="kw">instanceof OfflineValidatable) {
            ExecutionContext var6 = new ExecutionContext(var5.getBuilderParameters().createScope());
            String var7 = var4.getPath().toString();
            NPCLoadTimeValidationHelper var8 = new NPCLoadTimeValidationHelper(var7, null, !var5.isSpawnable());
            if (!var5.validate(var7, var8, var6, var6.getScope(), var1) || !var8.getValueStoreValidator().validate(var1)) {
               var4.setValidated(false);
            }
         }
      }
   }

   class="kw">private class="kw">static void sendReloadNotification(Message var0, @Nonnull Set<String> var1) {
      if (!var1.isEmpty()) {
         NotificationUtil.sendNotificationToUniverse(var0, Message.raw(var1.toString()), NotificationStyle.Warning);
      }
   }

   class="kw">private class="kw">static boolean isIgnoredFile(@Nonnull Path var0) {
      class="kw">return !var0.getFileName().toString().isEmpty() && var0.getFileName().toString().charAt(0) == '!';
   }

   class="kw">private class="kw">static boolean isJsonFile(@Nonnull Path var0) {
      class="kw">return Files.isRegularFile(var0) && var0.toString().endsWith(".json");
   }

   class="kw">private class="kw">static boolean isJsonFileName(@Nonnull Path var0, EventKind var1) {
      class="kw">return var0.toString().endsWith(".json");
   }

   @Nonnull
   class="kw">private class="kw">static String builderNameFromPath(@Nonnull Path var0) {
      String var1 = var0.getFileName().toString();
      if (var1.startsWith("NPCRole-")) {
         var1 = var1.split("-", 2)[1];
      }

      int var2 = var1.lastIndexOf(46);
      class="kw">return var2 >= 0 ? var1.substring(0, var2) : var1;
   }

   @Nonnull
   class="kw">private String buildPathString(@Nonnull IntArrayList var1, int var2) {
      if (var1.isEmpty()) {
         class="kw">return "";
      }

      StringBuilder var3 = new StringBuilder();
      var3.append(" (Path: ");
      IntIterator var4 = var1.iterator();

      while (var4.hasNext()) {
         var3.append(this.lookupName(var4.nextInt())).append(" -> ");
      }

      var3.append(this.lookupName(var2)).append(')');
      class="kw">return var3.toString();
   }

   class="kw">private boolean validateBuilder(@Nonnull BuilderInfo var1, @Nonnull IntSet var2, @Nonnull IntArrayList var3) {
      int var4 = var1.getIndex();
      if (var3.contains(var4)) {
         NPCPlugin.get()
            .getLogger()
            .at(Level.SEVERE)
            .log(
               "Builder '%s' validation failed: Cyclic reference detected for builder '%s'%s",
               this.lookupName(var3.getInt(0)),
               this.lookupName(var4),
               this.buildPathString(var3, var4)
            );
         class="kw">return var1.setValidated(false);
      }

      var3.add(var4);

      IntSet var5;
      try {
         var5 = this.computeAllDependencies(var1.getBuilder(), var1.getIndex());
      } catch (SkipSentryException | IllegalStateException | IllegalArgumentException var10) {
         NPCPlugin.get().getLogger().at(Level.SEVERE).log("Builder '%s' validation failed: %s", this.lookupName(var3.getInt(0)), var10.getMessage());
         class="kw">return var1.setValidated(false);
      }

      boolean var6 = true;
      IntIterator var7 = var5.iterator();

      while (var7.hasNext()) {
         int var8 = var7.nextInt();
         if (var3.contains(var8)) {
            NPCPlugin.get()
               .getLogger()
               .at(Level.SEVERE)
               .log(
                  "Builder '%s' validation failed: Cyclic reference detected for builder '%s'%s",
                  this.lookupName(var3.getInt(0)),
                  this.lookupName(var8),
                  this.buildPathString(var3, var4)
               );
            class="kw">return var1.setValidated(false);
         }

         if (var2.add(var8)) {
            BuilderInfo var9 = this.builderCache.get(var8);
            if (var9 == null) {
               NPCPlugin.get()
                  .getLogger()
                  .at(Level.SEVERE)
                  .log(
                     "Builder '%s' validation failed: Reference to unknown builder '%s'%s",
                     this.lookupName(var3.getInt(0)),
                     this.lookupName(var8),
                     this.buildPathString(var3, var8)
                  );
               var6 = false;
            } else if (!var9.isValidated()) {
               var6 = this.validateBuilder(var9, var2, var3);
            } else if (!var9.isValid()) {
               NPCPlugin.get()
                  .getLogger()
                  .at(Level.SEVERE)
                  .log(
                     "Builder '%s' validation failed: Reference to invalid builder '%s'%s",
                     this.lookupName(var3.getInt(0)),
                     var9.getKeyName(),
                     this.buildPathString(var3, var8)
                  );
               var6 = false;
            }
         }
      }

      var3.removeInt(var3.size() - 1);
      class="kw">return var1.setValidated(var6);
   }

   @Nonnull
   class="kw">private IntSet computeAllDependencies(@Nonnull Builder<?> var1, int var2) {
      class="kw">return this.computeAllDependencies(var1, var2, new IntOpenHashSet(), new IntArrayList());
   }

   @Nonnull
   class="kw">private IntSet computeAllDependencies(@Nonnull Builder<?> var1, int var2, @Nonnull IntSet var3, @Nonnull IntArrayList var4) {
      if (var4.contains(var2)) {
         throw new SkipSentryException(new IllegalArgumentException("Cyclic reference detected for builder: " + this.lookupName(var2)));
      }

      var4.add(var2);
      this.iterateDependencies(var1.getDependencies().iterator(), var3, var4);
      if (var1.hasDynamicDependencies()) {
         this.iterateDependencies(var1.getDynamicDependencies().iterator(), var3, var4);
      }

      var4.removeInt(var4.size() - 1);
      class="kw">return var3;
   }

   class="kw">private void iterateDependencies(@Nonnull IntIterator var1, @Nonnull IntSet var2, @Nonnull IntArrayList var3) {
      while (var1.hasNext()) {
         int var4 = var1.nextInt();
         if (!var2.contains(var4)) {
            var2.add(var4);
            Builder var5 = this.tryGetCachedBuilder(var4);
            if (var5 == null) {
               throw new SkipSentryException(new IllegalStateException("Reference to unknown builder: " + this.lookupName(var4)));
            }

            this.computeAllDependencies(var5, var4, var2, var3);
         }
      }
   }

   class="kw">private void reloadDependants(int var1) {
      for (BuilderInfo var3 : this.builderCache.values()) {
         int var4 = var3.getIndex();
         String var5 = var3.getKeyName();
         Builder var6 = var3.getBuilder();

         try {
            if (var6.isSpawnable() && this.isDependant(var6, var4, var1)) {
               NPCPlugin.get().getLogger().at(Level.INFO).log("Reloading entities of type '%s' because dependency '%s' changed", var5, this.lookupName(var1));
               onBuilderReloaded(var3);
            }
         } catch (Throwable var8) {
            NPCPlugin.get().getLogger().at(Level.INFO).log("Failed to reload entities of type '%s': %s", var5, var8.getMessage());
         }
      }
   }

   class="kw">private record AssetPath(String path, String label) {
      class="kw">private AssetPath {
      }
   }

   class="kw">private class BuilderAssetMonitorHandler class="kw">implements AssetMonitorHandler {
      class="kw">private class="kw">final AssetPack pack;
      class="kw">private class="kw">final boolean includeTests;

      class="kw">public BuilderAssetMonitorHandler(AssetPack nullx, boolean nullxx) {
         this.pack = nullx;
         this.includeTests = nullxx;
      }

      @Override
      class="kw">public Object getKey() {
         class="kw">return this.pack;
      }

      class="kw">public boolean test(Path var1, EventKind var2) {
         class="kw">return BuilderManager.isJsonFileName(var1, var2);
      }

      class="kw">public void accept(Map<Path, EventKind> var1) {
         Int2ObjectOpenHashMap var2 = new Int2ObjectOpenHashMap();
         HashSet var3 = new HashSet<>();
         HashSet var4 = new HashSet<>();
         HashSet var5 = new HashSet<>();
         ObjectArrayList var6 = new ObjectArrayList();

         for (Entry var8 : var1.entrySet()) {
            Path var9 = var8.getKey();
            EventKind var10 = var8.getValue();
            if (var10 != EventKind.ENTRY_CREATE && var10 != EventKind.ENTRY_MODIFY) {
               if (var10 == EventKind.ENTRY_DELETE) {
                  String var18 = BuilderManager.builderNameFromPath(var9);
                  BuilderManager.this.removeBuilder(var18);
                  NPCPlugin.get().getLogger().at(Level.INFO).log("Deleted builder %s", var18);
                  var5.add(var18);
               }
            } else if (Files.isRegularFile(var9) && !BuilderManager.isIgnoredFile(var9)) {
               try {
                  int var11 = BuilderManager.this.loadFile(var9, var6, null, this.includeTests, true);
                  if (var11 >= 0) {
                     String var12 = BuilderManager.builderNameFromPath(var9);
                     NPCPlugin.get().getLogger().at(Level.INFO).log("Reloaded builder %s", var12);
                     var3.add(var12);

                     for (BuilderInfo var14 : BuilderManager.this.builderCache.values()) {
                        try {
                           if (BuilderManager.this.isDependant(var14.getBuilder(), var14.getIndex(), var11)) {
                              var14.setNeedsValidation();
                           }
                        } catch (IllegalStateException | IllegalArgumentException var16) {
                           NPCPlugin.get()
                              .getLogger()
                              .at(Level.WARNING)
                              .log("Could not check if builder %s was dependent: %s", BuilderManager.this.lookupName(var14.getIndex()), var16.getMessage());
                        }
                     }

                     if (BuilderManager.this.autoReload) {
                        BuilderManager.this.reloadDependants(var11);
                     }

                     BuilderInfo var19 = BuilderManager.this.builderCache.get(var11);
                     BuilderManager.onBuilderReloaded(var19);
                     var2.put(var11, var19);
                  }
               } catch (Throwable var17) {
                  NPCPlugin.get().getLogger().at(Level.SEVERE).log("Failed to reload config %s: %s", var9, var17.getMessage());
                  var4.add(BuilderManager.builderNameFromPath(var9) + ": " + var17.getMessage());
               }
            }
         }

         BuilderManager.sendReloadNotification(Message.translation("server.general.assetstore.reloadAssets").param("class", "builder"), var3);
         BuilderManager.sendReloadNotification(Message.translation("server.general.assetstore.loadFailed").param("class", "builder"), var4);
         BuilderManager.sendReloadNotification(Message.translation("server.general.assetstore.removedAssets").param("class", "builder"), var5);
         BuilderManager.this.finishLoadingBuilders(var2, var6);
      }
   }

   class="kw">private enum TestType {
      NORMAL,
      FAILING;

      TestType() {
      }
   }
}