package com.hypixel.hytale.server.npc.asset.builder; import com.hypixel.hytale.server.npc.instructions.Instruction; import com.hypixel.hytale.server.npc.instructions.builders.BuilderInstructionReference; import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import it.unimi.dsi.fastutil.ints.IntArrayList; import it.unimi.dsi.fastutil.ints.IntIterator; import it.unimi.dsi.fastutil.ints.IntOpenHashSet; import it.unimi.dsi.fastutil.ints.IntSet; import it.unimi.dsi.fastutil.objects.Object2IntMap; import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; import it.unimi.dsi.fastutil.objects.ObjectArrayList; import java.util.List; import java.util.Objects; import javax.annotation.Nonnull; import javax.annotation.Nullable; public class InternalReferenceResolver { private final List builders = new ObjectArrayList(); @Nullable private Object2IntMap indexMap; @Nullable private Int2ObjectMap nameMap = new Int2ObjectOpenHashMap(); @Nullable private IntSet recordedDependencies; public InternalReferenceResolver() { this.indexMap = new Object2IntOpenHashMap(); this.indexMap.defaultReturnValue(Integer.MIN_VALUE); } public int getOrCreateIndex(String name) { int index = this.indexMap.getInt(name); if (index == Integer.MIN_VALUE) { index = this.builders.size(); this.indexMap.put(name, index); this.nameMap.put(index, name); this.builders.add(null); } if (this.recordedDependencies != null) { this.recordedDependencies.add(index); } return index; } public void setRecordDependencies() { this.recordedDependencies = new IntOpenHashSet(); } @Nullable public IntSet getRecordedDependenices() { return this.recordedDependencies; } public void stopRecordingDependencies() { this.recordedDependencies = null; } public void addBuilder(int index, BuilderInstructionReference builder) { Objects.requireNonNull(builder, "Builder cannot be null when adding as a reference"); if (index < 0 || index >= this.builders.size()) { throw new IllegalArgumentException("Slot for putting builder must be >= 0 and < the size of the list"); } else if (this.builders.get(index) != null) { throw new IllegalStateException(String.format("Duplicate internal reference builder with name: %s", this.nameMap.get(index))); } else { this.builders.set(index, builder); } } public void validateInternalReferences(String configName, @Nonnull List errors) { for (int i = 0; i < this.builders.size(); i++) { BuilderInstructionReference builder = this.builders.get(i); if (builder == null) { errors.add(configName + ": Internal reference builder: " + (String)this.nameMap.get(i) + " doesn't exist"); } else { try { this.validateNoCycles(builder, i, new IntArrayList()); } catch (IllegalArgumentException var6) { errors.add(configName + ": " + var6.getMessage()); } } } } private void validateNoCycles(@Nonnull BuilderInstructionReference builder, int index, @Nonnull IntArrayList path) { if (path.contains(index)) { throw new IllegalArgumentException("Cyclic reference detected for internal component reference: " + (String)this.nameMap.get(index)); } else { path.add(index); IntIterator i = builder.getInternalDependencies().iterator(); while (i.hasNext()) { int dependency = i.nextInt(); BuilderInstructionReference nextBuilder = this.builders.get(dependency); if (nextBuilder == null) { throw new IllegalStateException("Reference to internal reference builder: " + (String)this.nameMap.get(dependency) + " which doesn't exist"); } this.validateNoCycles(nextBuilder, dependency, path); } path.removeInt(path.size() - 1); } } public Builder getBuilder(int index, Class classType) { if (classType != Instruction.class) { throw new IllegalArgumentException("Internal references are currently only supported for instruction list"); } else { return this.builders.get(index); } } public void optimise() { this.indexMap = null; this.nameMap = null; } }