EntryType enum

Пакет: com.hypixel.hytale.codec.builder

Файл: com/hypixel/hytale/codec/builder/BuilderCodec.java

Поля (1)

МодификаторыТипИмя
FIELD, IGNORE, IGNORE_IN_BASE_OBJECT

Исходный код

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

class="kw">import com.hypixel.hytale.codec.Codec;
class="kw">import com.hypixel.hytale.codec.DirectDecodeCodec;
class="kw">import com.hypixel.hytale.codec.EmptyExtraInfo;
class="kw">import com.hypixel.hytale.codec.ExtraInfo;
class="kw">import com.hypixel.hytale.codec.InheritCodec;
class="kw">import com.hypixel.hytale.codec.KeyedCodec;
class="kw">import com.hypixel.hytale.codec.RawJsonCodec;
class="kw">import com.hypixel.hytale.codec.VersionedExtraInfo;
class="kw">import com.hypixel.hytale.codec.exception.CodecException;
class="kw">import com.hypixel.hytale.codec.schema.SchemaContext;
class="kw">import com.hypixel.hytale.codec.schema.config.NullSchema;
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.metadata.Metadata;
class="kw">import com.hypixel.hytale.codec.schema.metadata.ui.UIDisplayMode;
class="kw">import com.hypixel.hytale.codec.util.RawJsonReader;
class="kw">import com.hypixel.hytale.codec.validation.ValidatableCodec;
class="kw">import com.hypixel.hytale.codec.validation.ValidationResults;
class="kw">import com.hypixel.hytale.function.consumer.TriConsumer;
class="kw">import it.unimi.dsi.fastutil.objects.Object2ObjectLinkedOpenHashMap;
class="kw">import it.unimi.dsi.fastutil.objects.ObjectArrayList;
class="kw">import java.io.IOException;
class="kw">import java.util.Collections;
class="kw">import java.util.Comparator;
class="kw">import java.util.List;
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.function.BiConsumer;
class="kw">import java.util.function.BiFunction;
class="kw">import java.util.function.Consumer;
class="kw">import java.util.function.Function;
class="kw">import java.util.function.Supplier;
class="kw">import javax.annotation.Nonnull;
class="kw">import javax.annotation.Nullable;
class="kw">import org.bson.BsonDocument;
class="kw">import org.bson.BsonValue;

class="kw">public class BuilderCodec<T> class="kw">implements Codec<T>, DirectDecodeCodec<T>, RawJsonCodec<T>, InheritCodec<T>, ValidatableCodec<T> {
   class="kw">public class="kw">static class="kw">final int UNSET_VERSION = Integer.MIN_VALUE;
   class="kw">public class="kw">static class="kw">final int UNSET_MAX_VERSION = Integer.MAX_VALUE;
   class="kw">public class="kw">static class="kw">final int INITIAL_VERSION = 0;
   class="kw">public class="kw">static class="kw">final BuilderCodec<?>[] EMPTY_ARRAY = new BuilderCodec[0];
   class="kw">private class="kw">static class="kw">final KeyedCodec<Integer> VERSION = new KeyedCodec<>("Version", INTEGER);
   class="kw">private class="kw">static class="kw">final Map<String, BuilderCodec.EntryType> BASE_IGNORED_KEYS = Map.ofEntries(
      Map.entry("$Title", BuilderCodec.EntryType.IGNORE),
      Map.entry("$Comment", BuilderCodec.EntryType.IGNORE),
      Map.entry("$TODO", BuilderCodec.EntryType.IGNORE),
      Map.entry("$Author", BuilderCodec.EntryType.IGNORE),
      Map.entry("$Position", BuilderCodec.EntryType.IGNORE),
      Map.entry("$FloatingFunctionNodes", BuilderCodec.EntryType.IGNORE),
      Map.entry("$Groups", BuilderCodec.EntryType.IGNORE),
      Map.entry("$WorkspaceID", BuilderCodec.EntryType.IGNORE),
      Map.entry("$NodeEditorMetadata", BuilderCodec.EntryType.IGNORE),
      Map.entry("$NodeId", BuilderCodec.EntryType.IGNORE),
      Map.entry("Parent", BuilderCodec.EntryType.IGNORE_IN_BASE_OBJECT)
   );
   class="kw">protected class="kw">final Class<T> tClass;
   class="kw">protected class="kw">final Supplier<T> supplier;
   @Nullable
   class="kw">protected class="kw">final BuilderCodec<? super T> parentCodec;
   @Nonnull
   class="kw">protected class="kw">final Map<String, List<BuilderField<T, ?>>> entries;
   @Nonnull
   class="kw">protected class="kw">final Map<String, List<BuilderField<T, ?>>> unmodifiableEntries;
   class="kw">protected class="kw">final BiConsumer<T, ValidationResults> validator;
   class="kw">protected class="kw">final BiConsumer<T, ExtraInfo> afterDecode;
   class="kw">protected class="kw">final boolean hasNonNullValidator;
   class="kw">protected class="kw">final String documentation;
   class="kw">protected class="kw">final List<Metadata> metadata;
   class="kw">protected class="kw">final int codecVersion;
   class="kw">protected class="kw">final int minCodecVersion;
   class="kw">protected class="kw">final boolean versioned;
   @Deprecated
   class="kw">protected class="kw">final boolean useLegacyVersion;
   @Nonnull
   class="kw">protected class="kw">final StringTreeMap<BuilderCodec.KeyEntry<T>> stringTreeMap;

   class="kw">protected BuilderCodec(@Nonnull BuilderCodec.BuilderBase<T, ?> var1) {
      this.tClass = var1.tClass;
      this.supplier = var1.supplier;
      this.parentCodec = var1.parentCodec;
      this.entries = Objects.requireNonNull(var1.entries, "entries parameter can't be null");
      this.unmodifiableEntries = Collections.unmodifiableMap(var1.entries);
      this.stringTreeMap = var1.stringTreeMap;
      this.validator = var1.validator;
      this.afterDecode = var1.afterDecode;
      this.documentation = var1.documentation;
      this.metadata = var1.metadata;
      boolean var2 = false;

      for (List var4 : this.entries.values()) {
         var4.sort(Comparator.comparingInt(BuilderField::getMinVersion));

         for (BuilderField var6 : var4) {
            var2 |= var6.hasNonNullValidator();
         }
      }

      this.hasNonNullValidator = var2;
      int var11;
      if (var1.codecVersion != Integer.MIN_VALUE) {
         var11 = var1.codecVersion;
      } else {
         int var12 = Integer.MIN_VALUE;

         for (List var16 : this.entries.values()) {
            for (BuilderField var8 : var16) {
               var12 = Math.max(var12, var8.getHighestSupportedVersion());
            }
         }

         var11 = var12;
      }

      int var13;
      if (var1.minCodecVersion != Integer.MAX_VALUE) {
         var13 = var1.minCodecVersion;
      } else {
         int var15 = Integer.MAX_VALUE;

         for (List var18 : this.entries.values()) {
            for (BuilderField var9 : var18) {
               int var10 = var9.getMinVersion();
               if (var10 != Integer.MIN_VALUE) {
                  var15 = Math.min(var15, var10);
               }
            }
         }

         var13 = var15;
      }

      if (this.parentCodec != null) {
         var11 = Math.max(var11, this.parentCodec.codecVersion);
         var13 = Math.min(var13, this.parentCodec.minCodecVersion);
         this.versioned = var1.versioned || this.parentCodec.versioned;
         this.useLegacyVersion = var1.useLegacyVersion || this.parentCodec.useLegacyVersion;
      } else {
         this.versioned = var1.versioned;
         this.useLegacyVersion = var1.useLegacyVersion;
      }

      this.codecVersion = var11;
      this.minCodecVersion = var13;
      if (this.versioned && !this.declaresVersionField()) {
         this.stringTreeMap.put(VERSION.getKey(), new BuilderCodec.KeyEntry<>(BuilderCodec.EntryType.IGNORE));
      }
   }

   class="kw">private boolean declaresVersionField() {
      for (BuilderCodec var1 = this; var1 != null; var1 = var1.parentCodec) {
         if (var1.entries.containsKey(VERSION.getKey())) {
            class="kw">return true;
         }
      }

      class="kw">return false;
   }

   class="kw">public Class<T> getInnerClass() {
      class="kw">return this.tClass;
   }

   class="kw">public Supplier<T> getSupplier() {
      class="kw">return this.supplier;
   }

   class="kw">public T getDefaultValue() {
      class="kw">return this.getDefaultValue(EmptyExtraInfo.EMPTY);
   }

   class="kw">public T getDefaultValue(ExtraInfo var1) {
      Object var2 = this.supplier.get();
      this.afterDecode(var2, var1);
      class="kw">return var2;
   }

   @Nonnull
   class="kw">public Map<String, List<BuilderField<T, ?>>> getEntries() {
      class="kw">return this.unmodifiableEntries;
   }

   class="kw">public BiConsumer<T, ExtraInfo> getAfterDecode() {
      class="kw">return this.afterDecode;
   }

   @Nullable
   class="kw">public BuilderCodec<? super T> getParent() {
      class="kw">return this.parentCodec;
   }

   class="kw">public String getDocumentation() {
      class="kw">return this.documentation;
   }

   class="kw">public int getCodecVersion() {
      class="kw">return this.codecVersion;
   }

   class="kw">public void inherit(T var1, @Nonnull T var2, @Nonnull ExtraInfo var3) {
      if (this.parentCodec != null) {
         this.parentCodec.inherit(var1, var2, var3);
      }

      if (this.getInnerClass().isAssignableFrom(var2.getClass())) {
         for (List var5 : this.entries.values()) {
            BuilderField var6 = findField(var5, var3);
            if (var6 != null) {
               var6.inherit(var1, var2, var3);
            }
         }
      }
   }

   class="kw">public void afterDecode(T var1, ExtraInfo var2) {
      if (this.parentCodec != null) {
         this.parentCodec.afterDecode(var1, var2);
      }

      if (this.afterDecode != null) {
         this.afterDecode.accept(var1, var2);
      }
   }

   class="kw">public void afterDecodeAndValidate(T var1, @Nonnull ExtraInfo var2) {
      if (this.parentCodec != null) {
         this.parentCodec.afterDecodeAndValidate(var1, var2);
      }

      if (this.afterDecode != null) {
         this.afterDecode.accept(var1, var2);
      }

      ValidationResults var3 = var2.getValidationResults();
      if (this.hasNonNullValidator) {
         for (List var5 : this.entries.values()) {
            BuilderField var6 = findField(var5, var2);
            if (var6 != null) {
               var2.pushKey(var6.codec.getKey());

               try {
                  var6.nullValidate(var1, var3, var2);
               } class="kw">finally {
                  var2.popKey();
               }
            }
         }

         if (this.validator != null) {
            this.validator.accept(var1, var3);
         }

         var3._processValidationResults();
      } else if (this.validator != null) {
         this.validator.accept(var1, var3);
         var3._processValidationResults();
      }
   }

   @Override
   class="kw">public T decode(@Nonnull BsonValue var1, @Nonnull ExtraInfo var2) {
      if (this.supplier == null) {
         throw new CodecException("This BuilderCodec is for an class="kw">abstract or direct codec. To use this codec you must specify an existing object to decode into.");
      }

      Object var3 = this.supplier.get();
      this.decode(var1.asDocument(), var3, var2);
      class="kw">return var3;
   }

   @Nonnull
   class="kw">public BsonDocument encode(T var1, @Nonnull ExtraInfo var2) {
      BsonDocument var3 = new BsonDocument();
      if (this.versioned) {
         if (this.codecVersion != 0) {
            VERSION.put(var3, this.codecVersion, var2);
         }

         var2 = new VersionedExtraInfo(this.codecVersion, var2);
      }

      class="kw">return this.encode0(var1, var3, var2);
   }

   @Override
   class="kw">public void decode(@Nonnull BsonValue var1, T var2, @Nonnull ExtraInfo var3) {
      BsonDocument var4 = var1.asDocument();
      if (this.versioned) {
         var3 = this.decodeVersion(var4, var3);
      }

      this.decode0(var4, var2, var3);
      this.afterDecodeAndValidate(var2, var3);
   }

   class="kw">protected void decode0(@Nonnull BsonDocument var1, T var2, ExtraInfo var3) {
      for (Entry var5 : var1.entrySet()) {
         String var6 = var5.getKey();
         BuilderField var7 = findEntry(this, var6, var3);
         if (var7 != null) {
            var7.decode(var1, var2, var3);
         } else if (!this.isIgnoredKey(var6, var3)) {
            var3.addUnknownKey(var6);
         }
      }
   }

   @Nonnull
   class="kw">protected BsonDocument encode0(T var1, @Nonnull BsonDocument var2, @Nonnull ExtraInfo var3) {
      if (this.parentCodec != null) {
         this.parentCodec.encode0(var1, var2, var3);
      }

      for (List var5 : this.entries.values()) {
         BuilderField var6 = findField(var5, var3);
         if (var6 != null) {
            var6.encode(var2, var1, var3);
         }
      }

      class="kw">return var2;
   }

   @Override
   class="kw">public T decodeJson(@Nonnull RawJsonReader var1, @Nonnull ExtraInfo var2) class="kw">throws IOException {
      if (this.supplier == null) {
         throw new CodecException("This BuilderCodec is for an class="kw">abstract or direct codec. To use this codec you must specify an existing object to decode into.");
      }

      Object var3 = this.supplier.get();
      if (this.versioned) {
         var2 = this.decodeVersion(var1, var2);
      }

      this.decodeJson0(var1, var3, var2);
      this.afterDecodeAndValidate(var3, var2);
      class="kw">return var3;
   }

   class="kw">private void decodeJson0(@Nonnull RawJsonReader var1, T var2, ExtraInfo var3) class="kw">throws IOException {
      var1.expect('{');
      var1.consumeWhiteSpace();
      if (!var1.tryConsume('}')) {
         while (true) {
            this.readEntry(var1, var2, var3);
            var1.consumeWhiteSpace();
            if (var1.tryConsumeOrExpect('}', ',')) {
               class="kw">return;
            }

            var1.consumeWhiteSpace();
         }
      }
   }

   class="kw">protected void readEntry(@Nonnull RawJsonReader var1, T var2, @Nonnull ExtraInfo var3) class="kw">throws IOException {
      var1.mark();
      StringTreeMap var4 = this.stringTreeMap.findEntry(var1);
      BuilderCodec.KeyEntry var5;
      if (var4 != null && (var5 = var4.getValue()) != null) {
         class="kw">switch (var5.getType()) {
            case FIELD:
               String var6 = var4.getKey();
               List var7 = var5.getFields();
               this.readField(var1, var2, var3, var6, var7);
               break;
            case IGNORE:
               var1.unmark();
               this.skipField(var1);
               break;
            case IGNORE_IN_BASE_OBJECT:
               if (var3.getKeysSize() == 0) {
                  var1.unmark();
                  this.skipField(var1);
               } else {
                  var1.reset();
                  this.readUnknownField(var1, var3);
               }
         }
      } else {
         var1.reset();
         this.readUnknownField(var1, var3);
      }
   }

   class="kw">private void skipField(@Nonnull RawJsonReader var1) class="kw">throws IOException {
      var1.consumeWhiteSpace();
      var1.expect(':');
      var1.consumeWhiteSpace();
      var1.skipValue();
   }

   class="kw">private void readField(@Nonnull RawJsonReader var1, T var2, @Nonnull ExtraInfo var3, String var4, @Nonnull List<BuilderField<T, ?>> var5) class="kw">throws IOException {
      BuilderField var6 = null;

      for (BuilderField var8 : var5) {
         if (var8.supportsVersion(var3.getVersion())) {
            var6 = var8;
            break;
         }
      }

      if (var6 == null) {
         var1.reset();
         this.readUnknownField(var1, var3);
      } else {
         var1.unmark();
         var1.consumeWhiteSpace();
         var1.expect(':');
         var1.consumeWhiteSpace();
         var3.pushKey(var4, var1);

         try {
            var6.decodeJson(var1, var2, var3);
         } catch (Exception var12) {
            throw new CodecException("Failed to decode", var1, var3, var12);
         } class="kw">finally {
            var3.popKey();
         }
      }
   }

   class="kw">private void readUnknownField(@Nonnull RawJsonReader var1, @Nonnull ExtraInfo var2) class="kw">throws IOException {
      var2.readUnknownKey(var1);
      var1.consumeWhiteSpace();
      var1.expect(':');
      var1.consumeWhiteSpace();
      var1.skipValue();
   }

   class="kw">public void decodeJson(@Nonnull RawJsonReader var1, T var2, @Nonnull ExtraInfo var3) class="kw">throws IOException {
      if (this.versioned) {
         var3 = this.decodeVersion(var1, var3);
      }

      this.decodeJson0(var1, var2, var3);
      this.afterDecodeAndValidate(var2, var3);
   }

   @Override
   class="kw">public T decodeAndInherit(@Nonnull BsonDocument var1, T var2, ExtraInfo var3) {
      Object var4 = this.supplier.get();
      this.decodeAndInherit(var1, var4, var2, var3);
      class="kw">return var4;
   }

   @Override
   class="kw">public void decodeAndInherit(@Nonnull BsonDocument var1, T var2, @Nullable T var3, ExtraInfo var4) {
      if (this.versioned) {
         var4 = this.decodeVersion(var1, var4);
      }

      if (var3 != null) {
         this.inherit(var2, var3, var4);
      }

      this.decodeAndInherit0(var1, var2, var3, var4);
      this.afterDecodeAndValidate(var2, var4);
   }

   class="kw">protected void decodeAndInherit0(@Nonnull BsonDocument var1, T var2, T var3, @Nonnull ExtraInfo var4) {
      for (Entry var6 : var1.entrySet()) {
         String var7 = var6.getKey();
         BuilderField var8 = findEntry(this, var7, var4);
         if (var8 != null) {
            if (var8.codec.getChildCodec() class="kw">instanceof BuilderCodec) {
               decodeAndInherit(var8, var1, var2, var3, var4);
            } else {
               var8.decodeAndInherit(var1, var2, var3, var4);
            }
         } else if (!this.isIgnoredKey(var7, var4)) {
            var4.addUnknownKey(var7);
         }
      }
   }

   class="kw">private class="kw">static <Type, FieldType> void decodeAndInherit(
      @Nonnull BuilderField<Type, FieldType> var0, @Nonnull BsonDocument var1, Type var2, @Nullable Type var3, @Nonnull ExtraInfo var4
   ) {
      KeyedCodec var5 = var0.codec;
      String var6 = var5.getKey();
      BsonValue var7 = var1.get(var6);
      if (Codec.isNullBsonValue(var7)) {
         if (var7 != null && var7.isNull()) {
            var0.setValue(var2, null, var4);
         }
      } else {
         var4.pushKey(var6);

         try {
            BuilderCodec var8 = (BuilderCodec<FieldType>)var5.getChildCodec();
            Object var9 = var8.getSupplier().get();
            Object var10 = var3 != null ? var0.getter.apply(var3, var4) : null;
            var8.decodeAndInherit(var7.asDocument(), var9, var10, var4);
            var0.setValue(var2, var9, var4);
         } catch (Exception var14) {
            throw new CodecException("Failed to decode", var7, var4, var14);
         } class="kw">finally {
            var4.popKey();
         }
      }
   }

   @Override
   class="kw">public T decodeAndInheritJson(@Nonnull RawJsonReader var1, T var2, ExtraInfo var3) class="kw">throws IOException {
      Object var4 = this.supplier.get();
      this.decodeAndInheritJson(var1, var4, var2, var3);
      class="kw">return var4;
   }

   @Override
   class="kw">public void decodeAndInheritJson(@Nonnull RawJsonReader var1, T var2, @Nullable T var3, ExtraInfo var4) class="kw">throws IOException {
      if (this.versioned) {
         var4 = this.decodeVersion(var1, var4);
      }

      if (var3 != null) {
         this.inherit(var2, var3, var4);
      }

      this.decodeAndInheritJson0(var1, var2, var3, var4);
      this.afterDecodeAndValidate(var2, var4);
   }

   class="kw">public void decodeAndInheritJson0(@Nonnull RawJsonReader var1, T var2, T var3, @Nonnull ExtraInfo var4) class="kw">throws IOException {
      var1.expect('{');
      var1.consumeWhiteSpace();
      if (!var1.tryConsume('}')) {
         while (true) {
            this.readAndInheritEntry(var1, var2, var3, var4);
            var1.consumeWhiteSpace();
            if (var1.tryConsumeOrExpect('}', ',')) {
               class="kw">return;
            }

            var1.consumeWhiteSpace();
         }
      }
   }

   class="kw">protected void readAndInheritEntry(@Nonnull RawJsonReader var1, T var2, T var3, @Nonnull ExtraInfo var4) class="kw">throws IOException {
      var1.mark();
      StringTreeMap var5 = this.stringTreeMap.findEntry(var1);
      BuilderCodec.KeyEntry var6;
      if (var5 != null && (var6 = var5.getValue()) != null) {
         class="kw">switch (var6.getType()) {
            case FIELD:
               String var7 = var5.getKey();
               List var8 = var6.getFields();
               this.readAndInheritField(var1, var2, var3, var4, var7, var8);
               break;
            case IGNORE:
               var1.unmark();
               this.skipField(var1);
               break;
            case IGNORE_IN_BASE_OBJECT:
               if (var4.getKeysSize() == 0) {
                  var1.unmark();
                  this.skipField(var1);
               } else {
                  var1.reset();
                  this.readUnknownField(var1, var4);
               }
         }
      } else {
         var1.reset();
         this.readUnknownField(var1, var4);
      }
   }

   class="kw">private void readAndInheritField(@Nonnull RawJsonReader var1, T var2, T var3, @Nonnull ExtraInfo var4, String var5, @Nonnull List<BuilderField<T, ?>> var6) class="kw">throws IOException {
      BuilderField var7 = null;

      for (BuilderField var9 : var6) {
         if (var9.supportsVersion(var4.getVersion())) {
            var7 = var9;
            break;
         }
      }

      if (var7 == null) {
         var1.reset();
         this.readUnknownField(var1, var4);
      } else {
         var1.unmark();
         var1.consumeWhiteSpace();
         var1.expect(':');
         var1.consumeWhiteSpace();
         var4.pushKey(var5, var1);

         try {
            if (var7.codec.getChildCodec() class="kw">instanceof BuilderCodec) {
               decodeAndInheritJson(var7, var1, var2, var3, var4);
            } else {
               var7.decodeAndInheritJson(var1, var2, var3, var4);
            }
         } catch (Exception var13) {
            throw new CodecException("Failed to decode", var1, var4, var13);
         } class="kw">finally {
            var4.popKey();
         }
      }
   }

   class="kw">private class="kw">static <Type, FieldType> void decodeAndInheritJson(
      @Nonnull BuilderField<Type, FieldType> var0, @Nonnull RawJsonReader var1, Type var2, @Nullable Type var3, @Nonnull ExtraInfo var4
   ) class="kw">throws IOException {
      int var5 = var1.peek();
      if (var5 == -1) {
         throw new IOException("Unexpected EOF!");
      }

      class="kw">switch (var5) {
         case 78:
         case 110:
            var1.readNullValue();
            var0.setValue(var2, null, var4);
            class="kw">return;
         class="kw">default:
            BuilderCodec var6 = (BuilderCodec<FieldType>)var0.codec.getChildCodec();
            Object var7 = var6.getSupplier().get();
            Object var8 = var3 != null ? var0.getter.apply(var3, var4) : null;
            var6.decodeAndInheritJson(var1, var7, var8, var4);
            var0.setValue(var2, var7, var4);
      }
   }

   class="kw">private boolean isIgnoredKey(@Nonnull String var1, @Nonnull ExtraInfo var2) {
      StringTreeMap var3 = this.stringTreeMap.findEntry(var1);
      if (var3 == null) {
         class="kw">return false;
      }

      BuilderCodec.KeyEntry var4 = var3.getValue();
      if (var4 == null) {
         class="kw">return false;
      }

      class="kw">return class="kw">switch (var4.getType()) {
         case FIELD -> false;
         case IGNORE -> true;
         case IGNORE_IN_BASE_OBJECT -> var2.getKeysSize() == 0;
      };
   }

   @Nonnull
   class="kw">protected ExtraInfo decodeVersion(BsonDocument var1, @Nonnull ExtraInfo var2) {
      if (this.useLegacyVersion && var2.getLegacyVersion() != Integer.MAX_VALUE) {
         class="kw">return new VersionedExtraInfo(var2.getLegacyVersion(), var2);
      } else {
         int var3 = VERSION.get(var1, var2).orElse(0);
         if (var3 > this.codecVersion) {
            throw new IllegalArgumentException("Version " + var3 + " is newer than expected version " + this.codecVersion);
         } else if (this.minCodecVersion != Integer.MAX_VALUE && var3 < this.minCodecVersion) {
            throw new IllegalArgumentException("Version " + var3 + " is older than min supported version " + this.minCodecVersion);
         } else {
            class="kw">return new VersionedExtraInfo(var3, var2);
         }
      }
   }

   @Nonnull
   class="kw">protected ExtraInfo decodeVersion(@Nonnull RawJsonReader var1, @Nonnull ExtraInfo var2) class="kw">throws IOException {
      if (this.useLegacyVersion && var2.getLegacyVersion() != Integer.MAX_VALUE) {
         class="kw">return new VersionedExtraInfo(var2.getLegacyVersion(), var2);
      }

      var1.mark();
      int var3 = 0;
      if (RawJsonReader.seekToKey(var1, VERSION.getKey())) {
         var3 = var1.readIntValue();
      }

      if (var3 > this.codecVersion) {
         throw new IllegalArgumentException("Version " + var3 + " is newer than expected version " + this.codecVersion);
      }

      if (this.minCodecVersion != Integer.MAX_VALUE && var3 < this.minCodecVersion) {
         throw new IllegalArgumentException("Version " + var3 + " is older than min supported version " + this.minCodecVersion);
      }

      var1.reset();
      class="kw">return new VersionedExtraInfo(var3, var2);
   }

   @Override
   class="kw">public void validate(T var1, @Nonnull ExtraInfo var2) {
      if (this.parentCodec != null) {
         this.parentCodec.validate(var1, var2);
      }

      for (List var4 : this.entries.values()) {
         BuilderField var5 = findField(var4, var2);
         if (var5 != null) {
            var2.pushKey(var5.codec.getKey());

            try {
               var5.validate(var1, var2);
            } class="kw">finally {
               var2.popKey();
            }
         }
      }
   }

   @Override
   class="kw">public void validateDefaults(@Nonnull ExtraInfo var1, @Nonnull Set<Codec<?>> var2) {
      if (var2.add(this)) {
         Object var3 = this.supplier.get();
         this.afterDecode(var3, var1);

         for (BuilderCodec var4 = this; var4 != null; var4 = (BuilderCodec<T>)var4.parentCodec) {
            for (List var6 : var4.entries.values()) {
               BuilderField var7 = findField(var6, var1);
               if (var7 != null) {
                  var1.pushKey(var7.codec.getKey());

                  try {
                     var7.validateDefaults(var3, var1, var2);
                  } class="kw">finally {
                     var1.popKey();
                  }
               }
            }
         }
      }
   }

   @Nonnull
   class="kw">public ObjectSchema toSchema(@Nonnull SchemaContext var1) {
      Object var2 = this.getDefaultValue();
      class="kw">return this.toSchema(var1, var2);
   }

   @Nonnull
   class="kw">public ObjectSchema toSchema(@Nonnull SchemaContext var1, @Nullable T var2) {
      ObjectSchema var3 = new ObjectSchema();
      var3.setAdditionalProperties(false);
      var3.setTitle(this.tClass.getSimpleName());
      var3.setMarkdownDescription(this.documentation);
      var3.getHytale().setMergesProperties(true);
      Map var4 = new Object2ObjectLinkedOpenHashMap();
      if (this.versioned) {
         var4.put(VERSION.getKey(), VERSION.getChildCodec().toSchema(var1));
      }

      Schema var5 = new Schema();
      var5.getHytale().setUiPropertyTitle("Comment");
      var5.setDoNotSuggest(true);
      var5.setDescription("Comments don't have any function other than allowing users to add certain internal comments or notes to an asset");
      UIDisplayMode.HIDDEN.modify(var5);
      var4.put("$Title", var5);
      var4.put("$Comment", var5);
      var4.put("$Author", var5);
      var4.put("$TODO", var5);
      var4.put("$Position", var5);
      var4.put("$FloatingFunctionNodes", var5);
      var4.put("$Groups", var5);
      var4.put("$WorkspaceID", var5);
      var4.put("$NodeId", var5);
      var4.put("$NodeEditorMetadata", var5);
      var3.setProperties(var4);
      createSchemaFields(var1, var2, this, var4);
      if (this.metadata != null) {
         for (int var6 = 0; var6 < this.metadata.size(); var6++) {
            Metadata var7 = this.metadata.get(var6);
            var7.modify(var3);
         }
      }

      class="kw">return var3;
   }

   class="kw">private class="kw">static <T> void createSchemaFields(@Nonnull SchemaContext var0, @Nullable T var1, @Nonnull BuilderCodec<T> var2, @Nonnull Map<String, Schema> var3) {
      if (var2.parentCodec != null) {
         createSchemaFields(var0, var1, var2.parentCodec, var3);
      }

      for (Entry var5 : var2.getEntries().entrySet()) {
         String var6 = var5.getKey();
         List var7 = var5.getValue();
         BuilderField var8 = var7.getLast();
         Codec var9 = var8.getCodec().getChildCodec();

         Object var10;
         try {
            var10 = var8.getter.apply(var1, EmptyExtraInfo.EMPTY);
         } catch (UnsupportedOperationException var14) {
            class="kw">continue;
         }

         Schema var11 = var0.refDefinition(var9, (T)var10);
         var8.updateSchema(var0, var11);
         Schema var12 = var11;
         String var13 = Schema.CODEC.getIdFor((Class<? class="kw">extends Schema>)var11.getClass());
         if (!var13.isEmpty()) {
            if (!var8.hasNonNullValidator() && !var8.isPrimitive) {
               var11.setTypes(new String[]{var13, "null"});
            }

            var3.put(var6, var11);
         } else if (var8.hasNonNullValidator()) {
            var3.put(var6, var11);
         } else {
            var3.put(var6, var12 = Schema.anyOf(var11, NullSchema.INSTANCE));
         }

         var12.setMarkdownDescription(var8.getDocumentation());
      }
   }

   @Nonnull
   @Override
   class="kw">public String toString() {
      class="kw">return "BuilderCodec{supplier="
         + this.supplier
         + ", parentCodec="
         + this.parentCodec
         + ", entries="
         + this.entries
         + ", afterDecode="
         + this.afterDecode
         + "}";
   }

   @Nullable
   class="kw">protected class="kw">static <T> BuilderField<? super T, ?> findEntry(@Nonnull BuilderCodec<? super T> var0, String var1, @Nonnull ExtraInfo var2) {
      List var3 = var0.entries.get(var1);
      if (var3 != null && var3.size() == 1) {
         BuilderField var4 = (BuilderField<? super T, ?>)var3.getFirst();
         if (var4.supportsVersion(var2.getVersion())) {
            class="kw">return var4;
         }
      }

      BuilderField var5 = null;

      while (var0 != null) {
         var5 = findField(var0.entries.get(var1), var2);
         if (var5 != null) {
            class="kw">return var5;
         }

         var0 = var0.parentCodec;
      }

      class="kw">return var5;
   }

   @Nullable
   class="kw">protected class="kw">static <T, F class="kw">extends BuilderField<T, ?>> F findField(@Nullable List<F> var0, @Nonnull ExtraInfo var1) {
      if (var0 == null) {
         class="kw">return null;
      }

      int var2 = 0;

      for (int var3 = var0.size(); var2 < var3; var2++) {
         BuilderField var4 = (F)var0.get(var2);
         if (var4.supportsVersion(var1.getVersion())) {
            class="kw">return var4;
         }
      }

      class="kw">return null;
   }

   @Nonnull
   class="kw">public class="kw">static <T> BuilderCodec.Builder<T> builder(Class<T> var0, Supplier<T> var1) {
      class="kw">return new BuilderCodec.Builder<>(var0, var1);
   }

   @Nonnull
   class="kw">public class="kw">static <T> BuilderCodec.Builder<T> builder(Class<T> var0, Supplier<T> var1, BuilderCodec<? super T> var2) {
      class="kw">return new BuilderCodec.Builder<>(var0, var1, var2);
   }

   @Nonnull
   class="kw">public class="kw">static <T> BuilderCodec.Builder<T> abstractBuilder(Class<T> var0) {
      class="kw">return new BuilderCodec.Builder<>(var0, null);
   }

   @Nonnull
   class="kw">public class="kw">static <T> BuilderCodec.Builder<T> abstractBuilder(Class<T> var0, BuilderCodec<? super T> var1) {
      class="kw">return new BuilderCodec.Builder<>(var0, null, var1);
   }

   class="kw">public class="kw">static class Builder<T> class="kw">extends BuilderCodec.BuilderBase<T, BuilderCodec.Builder<T>> {
      class="kw">protected Builder(Class<T> var1, Supplier<T> var2) {
         super(var1, var2);
      }

      class="kw">protected Builder(Class<T> var1, Supplier<T> var2, @Nullable BuilderCodec<? super T> var3) {
         super(var1, var2, var3);
      }
   }

   class="kw">public class="kw">abstract class="kw">static class BuilderBase<T, S class="kw">extends BuilderCodec.BuilderBase<T, S>> {
      class="kw">protected class="kw">final Class<T> tClass;
      class="kw">protected class="kw">final Supplier<T> supplier;
      @Nullable
      class="kw">protected class="kw">final BuilderCodec<? super T> parentCodec;
      class="kw">protected class="kw">final Map<String, List<BuilderField<T, ?>>> entries = new Object2ObjectLinkedOpenHashMap();
      @Nonnull
      class="kw">protected class="kw">final StringTreeMap<BuilderCodec.KeyEntry<T>> stringTreeMap;
      class="kw">protected BiConsumer<T, ValidationResults> validator;
      class="kw">protected BiConsumer<T, ExtraInfo> afterDecode;
      class="kw">protected String documentation;
      class="kw">protected List<Metadata> metadata;
      class="kw">protected int codecVersion = Integer.MIN_VALUE;
      class="kw">protected int minCodecVersion = Integer.MAX_VALUE;
      class="kw">protected boolean versioned = false;
      class="kw">protected boolean useLegacyVersion = false;

      class="kw">protected BuilderBase(Class<T> var1, Supplier<T> var2) {
         this(var1, var2, null);
      }

      class="kw">protected BuilderBase(Class<T> var1, Supplier<T> var2, @Nullable BuilderCodec<? super T> var3) {
         this.tClass = var1;
         this.supplier = var2;
         this.parentCodec = var3;
         if (var3 != null) {
            this.stringTreeMap = new StringTreeMap<>((StringTreeMap<BuilderCodec.KeyEntry<T>>)var3.stringTreeMap);
         } else {
            this.stringTreeMap = new StringTreeMap<>();
            BuilderCodec.BASE_IGNORED_KEYS.forEach((var1x, var2x) -> this.stringTreeMap.put(var1x, new BuilderCodec.KeyEntry<>(var2x)));
         }
      }

      class="kw">private S self() {
         class="kw">return (S)this;
      }

      @Nonnull
      class="kw">public S documentation(String var1) {
         this.documentation = var1;
         class="kw">return this.self();
      }

      @Nonnull
      class="kw">public S versioned() {
         this.versioned = true;
         class="kw">return this.self();
      }

      @Nonnull
      @Deprecated
      class="kw">public S legacyVersioned() {
         this.versioned = true;
         this.useLegacyVersion = true;
         class="kw">return this.self();
      }

      @Nonnull
      @Deprecated
      class="kw">public <FieldType> S addField(@Nonnull KeyedCodec<FieldType> var1, @Nonnull BiConsumer<T, FieldType> var2, @Nonnull Function<T, FieldType> var3) {
         class="kw">return this.addField(new BuilderField<>(var1, (var1x, var2x, var3x) -> var2.accept(var1x, var2x), (var1x, var2x) -> var3.apply(var1x), null));
      }

      @Nonnull
      class="kw">public <FieldType> BuilderField.FieldBuilder<T, FieldType, S> append(
         KeyedCodec<FieldType> var1, @Nonnull BiConsumer<T, FieldType> var2, @Nonnull Function<T, FieldType> var3
      ) {
         class="kw">return this.append(var1, (var1x, var2x, var3x) -> var2.accept(var1x, var2x), (var1x, var2x) -> var3.apply(var1x));
      }

      @Nonnull
      class="kw">public <FieldType> BuilderField.FieldBuilder<T, FieldType, S> append(
         KeyedCodec<FieldType> var1, TriConsumer<T, FieldType, ExtraInfo> var2, BiFunction<T, ExtraInfo, FieldType> var3
      ) {
         class="kw">return new BuilderField.FieldBuilder<>(this.self(), var1, var2, var3, null);
      }

      @Nonnull
      class="kw">public <FieldType> BuilderField.FieldBuilder<T, FieldType, S> appendInherited(
         KeyedCodec<FieldType> var1, @Nonnull BiConsumer<T, FieldType> var2, @Nonnull Function<T, FieldType> var3, @Nonnull BiConsumer<T, T> var4
      ) {
         class="kw">return this.appendInherited(
            var1, (var1x, var2x, var3x) -> var2.accept(var1x, var2x), (var1x, var2x) -> var3.apply(var1x), (var1x, var2x, var3x) -> var4.accept(var1x, var2x)
         );
      }

      @Nonnull
      class="kw">public <FieldType> BuilderField.FieldBuilder<T, FieldType, S> appendInherited(
         KeyedCodec<FieldType> var1, TriConsumer<T, FieldType, ExtraInfo> var2, BiFunction<T, ExtraInfo, FieldType> var3, TriConsumer<T, T, ExtraInfo> var4
      ) {
         class="kw">return new BuilderField.FieldBuilder<>(this.self(), var1, var2, var3, var4);
      }

      @Nonnull
      class="kw">public <FieldType> S addField(@Nonnull BuilderField<T, FieldType> var1) {
         if (var1.getMinVersion() > var1.getMaxVersion()) {
            throw new IllegalArgumentException("Min version must be less than the max version: " + var1);
         }

         List var2 = this.entries.computeIfAbsent(var1.getCodec().getKey(), var0 -> new ObjectArrayList());

         for (BuilderField var4 : var2) {
            if (var1.getMaxVersion() >= var4.getMinVersion() && var1.getMinVersion() <= var4.getMaxVersion()) {
               throw new IllegalArgumentException("Field already defined for this version range!");
            }
         }

         var2.add(var1);
         this.stringTreeMap.put(var1.getCodec().getKey(), new BuilderCodec.KeyEntry<>(var2));
         class="kw">return this.self();
      }

      @Nonnull
      class="kw">public S afterDecode(@Nonnull Consumer<T> var1) {
         Objects.requireNonNull(var1, "afterDecodeAndValidate can't be null!");
         class="kw">return this.afterDecode((var1x, var2) -> var1.accept(var1x));
      }

      @Nonnull
      class="kw">public S afterDecode(BiConsumer<T, ExtraInfo> var1) {
         this.afterDecode = Objects.requireNonNull(var1, "afterDecodeAndValidate can't be null!");
         class="kw">return this.self();
      }

      @Nonnull
      @Deprecated
      class="kw">public S validator(BiConsumer<T, ValidationResults> var1) {
         this.validator = Objects.requireNonNull(var1, "validator can't be null!");
         class="kw">return this.self();
      }

      @Nonnull
      class="kw">public S metadata(Metadata var1) {
         if (this.metadata == null) {
            this.metadata = new ObjectArrayList();
         }

         this.metadata.add(var1);
         class="kw">return this.self();
      }

      @Nonnull
      class="kw">public S codecVersion(int var1, int var2) {
         this.minCodecVersion = var1;
         this.codecVersion = var2;
         class="kw">return this.self();
      }

      @Nonnull
      class="kw">public S codecVersion(int var1) {
         this.minCodecVersion = 0;
         this.codecVersion = var1;
         class="kw">return this.self();
      }

      @Nonnull
      class="kw">public BuilderCodec<T> build() {
         class="kw">return new BuilderCodec<>(this);
      }
   }

   class="kw">protected enum EntryType {
      FIELD,
      IGNORE,
      IGNORE_IN_BASE_OBJECT;

      EntryType() {
      }
   }

   class="kw">protected class="kw">static class KeyEntry<T> {
      class="kw">private class="kw">final BuilderCodec.EntryType type;
      @Nullable
      class="kw">private class="kw">final List<BuilderField<T, ?>> fields;

      class="kw">public KeyEntry(BuilderCodec.EntryType var1) {
         this.type = var1;
         this.fields = null;
      }

      class="kw">public KeyEntry(List<BuilderField<T, ?>> var1) {
         this.type = BuilderCodec.EntryType.FIELD;
         this.fields = var1;
      }

      class="kw">public BuilderCodec.EntryType getType() {
         class="kw">return this.type;
      }

      @Nullable
      class="kw">public List<BuilderField<T, ?>> getFields() {
         class="kw">return this.fields;
      }
   }
}