Changes

22 bytes removed ,  19:42, 2 August 2021
Update to 1.17
Line 5: Line 5:  
== Serialization and Deserialization ==
 
== Serialization and Deserialization ==
   −
The primary use for Codecs is to serialize java objects to some serialized type, such as a JsonElement or an INBT, and to deserialize an serialized object back to its proper java type. This is accomplished with <code>Codec#encodeStart</code> and <code>Codec#parse</code>, respectively. Given a Codec<SomeJavaType> and a DynamicOps<SomeSerializedType>, we can convert instances of SomeJavaType to instances of SomeSerializedType and back.
+
The primary use for Codecs is to serialize java objects to some serialized type, such as a JsonElement or a Tag, and to deserialize an serialized object back to its proper java type. This is accomplished with <code>Codec#encodeStart</code> and <code>Codec#parse</code>, respectively. Given a Codec<SomeJavaType> and a DynamicOps<SomeSerializedType>, we can convert instances of SomeJavaType to instances of SomeSerializedType and back.
    
Each of these methods take a [[DynamicOps]] instance and an instance of the object we are serializing or deserializing, and returns a DataResult:
 
Each of these methods take a [[DynamicOps]] instance and an instance of the object we are serializing or deserializing, and returns a DataResult:
Line 12: Line 12:  
// let someCodec be a Codec<SomeJavaType>
 
// let someCodec be a Codec<SomeJavaType>
 
// let someJavaObject be an instance of SomeJavaType
 
// let someJavaObject be an instance of SomeJavaType
// let someNBT and someJsonElement be instances of INBT and JsonElement, respectively
+
// let someTag and someJsonElement be instances of Tag and JsonElement, respectively
   −
// serialize some java object to INBT
+
// serialize some java object to Tag
DataResult<INBT> result = someCodec.encodeStart(NBTDynamicOps.INSTANCE, someJavaObject);
+
DataResult<Tag> result = someCodec.encodeStart(NBTOps.INSTANCE, someJavaObject);
   −
// deserialize some INBT instance back to a proper java object
+
// deserialize some Tag instance back to a proper java object
DataResult<SomeJavaType> result = someCodec.parse(NBTDynamicOps.INSTANCE, someNBT);
+
DataResult<SomeJavaType> result = someCodec.parse(NBTOps.INSTANCE, someTag );
    
// serialize some java object to a JsonElement
 
// serialize some java object to a JsonElement
Line 50: Line 50:  
Each vanilla <code>Registry</code> acts as the Codec for the type of object the registry contains; e.g. <code>Registry.BLOCK</code> is itself a <code>Codec<Block></code>. Forge Registries, however, do not currently implement Codec and cannot yet be used in this way; custom codecs must be created for forge-specific registries that are not tied to specific vanilla registries.
 
Each vanilla <code>Registry</code> acts as the Codec for the type of object the registry contains; e.g. <code>Registry.BLOCK</code> is itself a <code>Codec<Block></code>. Forge Registries, however, do not currently implement Codec and cannot yet be used in this way; custom codecs must be created for forge-specific registries that are not tied to specific vanilla registries.
   −
Of particular note here is the CompoundNBT.CODEC, which can be used to e.g. serialize a CompoundNBT into a json file. This has a notable limitation in that CompoundNBT.CODEC *cannot* safely deserialize lists of numbers from json, due to the strong typing of ListNBT and the way that the NBTDynamicOps deserializer reads numeric values.
+
Of particular note here is the CompoundTag.CODEC, which can be used to e.g. serialize a CompoundTag into a json file. This has a notable limitation in that CompoundTag.CODEC *cannot* safely deserialize lists of numbers from json, due to the strong typing of ListTag and the way that the NBTOps deserializer reads numeric values.
    
= Creating Codecs =
 
= Creating Codecs =
Line 65: Line 65:     
     public int getSomeInt() { return this.someInt; }
 
     public int getSomeInt() { return this.someInt; }
     public Item getItem() { return this.someItem; }
+
     public Item getItem() { return this.item; }
 
     public List<BlockPos> getBlockPositions() { return this.blockPositions; }
 
     public List<BlockPos> getBlockPositions() { return this.blockPositions; }
 
}
 
}
Line 88: Line 88:  
* a <code>Codec<Item></code>
 
* a <code>Codec<Item></code>
 
* a <code>Codec<List<BlockPos>></code>
 
* a <code>Codec<List<BlockPos>></code>
And then we'll need to assemble these into a <code>Codec<ExampleCodecClass</code>.
+
And then we'll need to assemble these into a <code>Codec<ExampleCodecClass></code>.
    
As previously mentioned, we can use <code>Codec.INT</code> for the integer codec, and <code>Registry.ITEM</code> for the Item codec. We don't have a builtin codec for list-of-blockpos, but we can use BlockPos.CODEC to create one.
 
As previously mentioned, we can use <code>Codec.INT</code> for the integer codec, and <code>Registry.ITEM</code> for the Item codec. We don't have a builtin codec for list-of-blockpos, but we can use BlockPos.CODEC to create one.
Line 100: Line 100:  
</syntaxhighlight>
 
</syntaxhighlight>
   −
Codecs created via listOf() serialize things to listlike objects, such as [] json arrays or ListNBTs.
+
Codecs created via listOf() serialize things to listlike objects, such as [] json arrays or ListTags.
    
Deserializing a list in this manner produces an ''immutable'' list. If a mutable list is needed, [[Codecs#Equivalent_Types_and_xmap|xmap]] can be used to convert the list after deserializing.
 
Deserializing a list in this manner produces an ''immutable'' list. If a mutable list is needed, [[Codecs#Equivalent_Types_and_xmap|xmap]] can be used to convert the list after deserializing.
    
== Records ==
 
== Records ==
RecordCodecBuilder is used to generate codecs that serialize instances of classes with explicitly named fields, like our example above. Codecs created via RecordCodecBuilder serialize things to maplike objects, such as {} json objects or CompoundNBTs.
+
RecordCodecBuilder is used to generate codecs that serialize instances of classes with explicitly named fields, like our example above. Codecs created via RecordCodecBuilder serialize things to maplike objects, such as {} json objects or CompoundTags.
    
RecordCodecBuilder can be used in several ways, but the simplest form is as follows:
 
RecordCodecBuilder can be used in several ways, but the simplest form is as follows:
Line 130: Line 130:     
===Optional and Default Values in Record Fields===
 
===Optional and Default Values in Record Fields===
When RecordCodecBuilder is used as shown above, all of the fields are *required* to be in the serialized object (the JsonObject/CompoundNBT/etc), or the entire thing will fail to parse when the codec tries to deserialize it. If we wish to have optional or default values, we have several alternatives of fieldOf() we can use.
+
When RecordCodecBuilder is used as shown above, all of the fields are *required* to be in the serialized object (the JsonObject/CompoundTag/etc), or the entire thing will fail to parse when the codec tries to deserialize it. If we wish to have optional or default values, we have several alternatives of fieldOf() we can use.
    
* <code>someCodec.optionalFieldOf("field_name")</code> creates a field for an Optional. If the field in the json/nbt is not present or invalid, it will deserialize as an empty optional. Empty optionals will not be serialized; the field will be omitted from the json or nbt.
 
* <code>someCodec.optionalFieldOf("field_name")</code> creates a field for an Optional. If the field in the json/nbt is not present or invalid, it will deserialize as an empty optional. Empty optionals will not be serialized; the field will be omitted from the json or nbt.
Line 195: Line 195:  
</syntaxhighlight>
 
</syntaxhighlight>
   −
The serialized form of maps serialized by this codec will be a JsonObject or CompoundNBT, whose fields are the key-value pairs in the map; the map's keys will be used as the field names, and the map's values will be the values of those fields
+
The serialized form of maps serialized by this codec will be a JsonObject or CompoundTag, whose fields are the key-value pairs in the map; the map's keys will be used as the field names, and the map's values will be the values of those fields
    
A limitation of using unboundedMap is that it only supports key codecs that serialize to Strings (including codecs for things like ResourceLocation that aren't Strings themselves but still serialize to strings). To create a codec for a Map whose keys are not fundamentally strings, the Map must be serialized as a list of key-value pairs instead of using unboundedMap.
 
A limitation of using unboundedMap is that it only supports key codecs that serialize to Strings (including codecs for things like ResourceLocation that aren't Strings themselves but still serialize to strings). To create a codec for a Map whose keys are not fundamentally strings, the Map must be serialized as a list of key-value pairs instead of using unboundedMap.
Line 264: Line 264:     
Several examples of vanilla classes that use dispatch codecs:
 
Several examples of vanilla classes that use dispatch codecs:
* RuleTest and IRuleTestType
+
* RuleTest and RuleTestType
* IParticleData and ParticleType
+
* BlockPlacer and BlockPlacerType
* ConfiguredPlacement and Placement
+
* ConfiguredDecorator and FeatureDecorator
    
=External Links=
 
=External Links=
 
* [https://github.com/Mojang/DataFixerUpper/blob/master/src/main/java/com/mojang/serialization/Codec.java Codecs in Mojang's official public DataFixerUpper repository]
 
* [https://github.com/Mojang/DataFixerUpper/blob/master/src/main/java/com/mojang/serialization/Codec.java Codecs in Mojang's official public DataFixerUpper repository]
 
* [https://kvverti.github.io/Documented-DataFixerUpper/snapshot/com/mojang/serialization/Codec.html#flatXmap-java.util.function.Function-java.util.function.Function- Unofficial Codec Javadocs]
 
* [https://kvverti.github.io/Documented-DataFixerUpper/snapshot/com/mojang/serialization/Codec.html#flatXmap-java.util.function.Function-java.util.function.Function- Unofficial Codec Javadocs]