BuilderAssetMonitorHandler class

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

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

implements: AssetMonitorHandler

Поля (13)

МодификаторыТипИмя
private final boolean includeTests
private final AssetPack pack
EventKind var10
int var11
String var12
String var18
BuilderInfo var19
Int2ObjectOpenHashMap var2
HashSet var3
HashSet var4
HashSet var5
ObjectArrayList var6
Path var9

Методы (7)

МодификаторыВозвратСигнатура
public void acceptpublic void accept(Map<Path, EventKind> var1)
public Object getKeypublic Object getKey()
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)
public boolean testpublic boolean test(Path var1, EventKind var2)

Исходный код

Показать/скрыть
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() {
      }
   }
}