From eabc89520076e0d9980465aeaa1b12ced077e6e9 Mon Sep 17 00:00:00 2001 From: Dragon-Seeker Date: Tue, 13 Feb 2024 15:38:24 -0600 Subject: [PATCH 1/2] [endec] Add Nullability within RecordEndec and adjust SealedPolymorphic Location --- .../network/serialization/SealedPolymorphic.java | 4 ++++ .../serialization/annotations/NullableField.java | 14 ++++++++++++++ .../annotations/SealedPolymorphic.java | 11 +++++++++++ .../owo/serialization/endec/RecordEndec.java | 10 +++++++++- .../endec/ReflectiveEndecBuilder.java | 5 +++-- .../wispforest/uwu/network/NullablePacket.java | 8 ++++++++ .../wispforest/uwu/network/SealedTestClass.java | 2 +- .../uwu/network/UwuNetworkExample.java | 16 ++++++++++++++++ 8 files changed, 66 insertions(+), 4 deletions(-) create mode 100644 src/main/java/io/wispforest/owo/serialization/annotations/NullableField.java create mode 100644 src/main/java/io/wispforest/owo/serialization/annotations/SealedPolymorphic.java create mode 100644 src/testmod/java/io/wispforest/uwu/network/NullablePacket.java diff --git a/src/main/java/io/wispforest/owo/network/serialization/SealedPolymorphic.java b/src/main/java/io/wispforest/owo/network/serialization/SealedPolymorphic.java index 1c66fe36..e1971981 100644 --- a/src/main/java/io/wispforest/owo/network/serialization/SealedPolymorphic.java +++ b/src/main/java/io/wispforest/owo/network/serialization/SealedPolymorphic.java @@ -5,6 +5,10 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; +/** + * @Deprecated : Use {@link io.wispforest.owo.serialization.annotations.SealedPolymorphic} + */ @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) +@Deprecated(forRemoval = true) public @interface SealedPolymorphic {} diff --git a/src/main/java/io/wispforest/owo/serialization/annotations/NullableField.java b/src/main/java/io/wispforest/owo/serialization/annotations/NullableField.java new file mode 100644 index 00000000..48f68fa1 --- /dev/null +++ b/src/main/java/io/wispforest/owo/serialization/annotations/NullableField.java @@ -0,0 +1,14 @@ +package io.wispforest.owo.serialization.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Annotation used within {@link io.wispforest.owo.serialization.endec.RecordEndec} to mark a field nullable + */ +@Target({ElementType.FIELD, ElementType.RECORD_COMPONENT}) +@Retention(RetentionPolicy.RUNTIME) +public @interface NullableField { +} diff --git a/src/main/java/io/wispforest/owo/serialization/annotations/SealedPolymorphic.java b/src/main/java/io/wispforest/owo/serialization/annotations/SealedPolymorphic.java new file mode 100644 index 00000000..337c5a4b --- /dev/null +++ b/src/main/java/io/wispforest/owo/serialization/annotations/SealedPolymorphic.java @@ -0,0 +1,11 @@ +package io.wispforest.owo.serialization.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +public @interface SealedPolymorphic { +} diff --git a/src/main/java/io/wispforest/owo/serialization/endec/RecordEndec.java b/src/main/java/io/wispforest/owo/serialization/endec/RecordEndec.java index 18633613..339ef842 100644 --- a/src/main/java/io/wispforest/owo/serialization/endec/RecordEndec.java +++ b/src/main/java/io/wispforest/owo/serialization/endec/RecordEndec.java @@ -5,7 +5,9 @@ import io.wispforest.owo.serialization.Endec; import io.wispforest.owo.serialization.Serializer; import io.wispforest.owo.serialization.StructEndec; +import io.wispforest.owo.serialization.annotations.NullableField; import org.apache.commons.lang3.mutable.MutableInt; +import org.jetbrains.annotations.Nullable; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; @@ -44,9 +46,15 @@ public static RecordEndec create(Class recordClass) { var component = recordClass.getRecordComponents()[i]; var handle = lookup.unreflect(component.getAccessor()); + var endec = (Endec) ReflectiveEndecBuilder.get(component.getGenericType()); + + var nullAnnotation = component.getAnnotation(NullableField.class); + + if(nullAnnotation != null) endec = endec.nullableOf(); + fields.add(new StructField<>( component.getName(), - (Endec) ReflectiveEndecBuilder.get(component.getGenericType()), + endec, instance -> getRecordEntry(instance, handle) )); diff --git a/src/main/java/io/wispforest/owo/serialization/endec/ReflectiveEndecBuilder.java b/src/main/java/io/wispforest/owo/serialization/endec/ReflectiveEndecBuilder.java index 07216932..0413cbbf 100644 --- a/src/main/java/io/wispforest/owo/serialization/endec/ReflectiveEndecBuilder.java +++ b/src/main/java/io/wispforest/owo/serialization/endec/ReflectiveEndecBuilder.java @@ -1,10 +1,11 @@ package io.wispforest.owo.serialization.endec; -import io.wispforest.owo.network.serialization.SealedPolymorphic; + import io.wispforest.owo.serialization.Deserializer; import io.wispforest.owo.serialization.Endec; import io.wispforest.owo.serialization.Serializer; import io.wispforest.owo.serialization.StructEndec; +import io.wispforest.owo.serialization.annotations.SealedPolymorphic; import io.wispforest.owo.serialization.format.nbt.NbtEndec; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import it.unimi.dsi.fastutil.objects.Reference2IntOpenHashMap; @@ -126,7 +127,7 @@ public static Optional> maybeGet(Class clazz) { serializer = (Endec) Endec.forEnum((Class) clazz); } else if (clazz.isArray()) { serializer = (Endec) ReflectiveEndecBuilder.createArrayEndec(clazz.getComponentType()); - } else if (clazz.isAnnotationPresent(SealedPolymorphic.class)) { + } else if (clazz.isAnnotationPresent(io.wispforest.owo.network.serialization.SealedPolymorphic.class) || clazz.isAnnotationPresent(SealedPolymorphic.class)) { serializer = (Endec) ReflectiveEndecBuilder.createSealedSerializer(clazz); } else { return null; diff --git a/src/testmod/java/io/wispforest/uwu/network/NullablePacket.java b/src/testmod/java/io/wispforest/uwu/network/NullablePacket.java new file mode 100644 index 00000000..6fd46770 --- /dev/null +++ b/src/testmod/java/io/wispforest/uwu/network/NullablePacket.java @@ -0,0 +1,8 @@ +package io.wispforest.uwu.network; + +import io.wispforest.owo.serialization.annotations.NullableField; + +import java.util.List; + +public record NullablePacket(@NullableField String name, @NullableField List names) { +} diff --git a/src/testmod/java/io/wispforest/uwu/network/SealedTestClass.java b/src/testmod/java/io/wispforest/uwu/network/SealedTestClass.java index 6024d491..2b012ea6 100644 --- a/src/testmod/java/io/wispforest/uwu/network/SealedTestClass.java +++ b/src/testmod/java/io/wispforest/uwu/network/SealedTestClass.java @@ -1,6 +1,6 @@ package io.wispforest.uwu.network; -import io.wispforest.owo.network.serialization.SealedPolymorphic; +import io.wispforest.owo.serialization.annotations.SealedPolymorphic; @SealedPolymorphic public sealed interface SealedTestClass permits SealedSubclassOne, SealedSubclassTwo { diff --git a/src/testmod/java/io/wispforest/uwu/network/UwuNetworkExample.java b/src/testmod/java/io/wispforest/uwu/network/UwuNetworkExample.java index 339f0d9d..5d809569 100644 --- a/src/testmod/java/io/wispforest/uwu/network/UwuNetworkExample.java +++ b/src/testmod/java/io/wispforest/uwu/network/UwuNetworkExample.java @@ -38,6 +38,19 @@ public static void init() { CHANNEL.registerServerbound(MaldingPacket.class, (message, access) -> { access.player().sendMessage(Text.of(message.toString()), false); }); + + CHANNEL.registerServerbound(NullablePacket.class, (message, access) -> { + if(message.name() == null && message.names() == null) { + access.player().sendMessage(Text.of("NULLABLITY FOR THE WIN")); + } else { + var text = Text.literal(""); + + text.append(Text.of((message.name() == null) ? "null" : message.name())); + text.append(Text.of((message.names() == null) ? "null" : message.names().toString())); + + access.player().sendMessage(text); + } + }); } @Environment(EnvType.CLIENT) @@ -52,6 +65,9 @@ public static void init() { CHANNEL.clientHandle().send(new MaldingPacket(new DispatchedSubclassOne("base"))); CHANNEL.clientHandle().send(new MaldingPacket(new DispatchedSubclassTwo(20))); + + CHANNEL.clientHandle().send(new NullablePacket(null, null)); + CHANNEL.clientHandle().send(new NullablePacket("Weeee", null)); } }); } From f933a8c69c0af1db88fd5e07689bd7f7a37022b3 Mon Sep 17 00:00:00 2001 From: glisco Date: Sat, 17 Feb 2024 01:00:26 +0100 Subject: [PATCH 2/2] [endec] clean up @NullableComponent implementation --- .../network/serialization/SealedPolymorphic.java | 3 ++- .../annotations/NullableComponent.java | 15 +++++++++++++++ .../serialization/annotations/NullableField.java | 14 -------------- .../annotations/SealedPolymorphic.java | 3 +-- .../owo/serialization/endec/RecordEndec.java | 8 ++------ .../io/wispforest/uwu/network/NullablePacket.java | 5 ++--- .../wispforest/uwu/network/UwuNetworkExample.java | 4 ++-- 7 files changed, 24 insertions(+), 28 deletions(-) create mode 100644 src/main/java/io/wispforest/owo/serialization/annotations/NullableComponent.java delete mode 100644 src/main/java/io/wispforest/owo/serialization/annotations/NullableField.java diff --git a/src/main/java/io/wispforest/owo/network/serialization/SealedPolymorphic.java b/src/main/java/io/wispforest/owo/network/serialization/SealedPolymorphic.java index e1971981..19c99e35 100644 --- a/src/main/java/io/wispforest/owo/network/serialization/SealedPolymorphic.java +++ b/src/main/java/io/wispforest/owo/network/serialization/SealedPolymorphic.java @@ -6,7 +6,8 @@ import java.lang.annotation.Target; /** - * @Deprecated : Use {@link io.wispforest.owo.serialization.annotations.SealedPolymorphic} + * @deprecated Moved to {@link io.wispforest.owo.serialization.annotations.SealedPolymorphic} for consistency. This + * annotation will keep working for the time being but eventually get removed */ @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) diff --git a/src/main/java/io/wispforest/owo/serialization/annotations/NullableComponent.java b/src/main/java/io/wispforest/owo/serialization/annotations/NullableComponent.java new file mode 100644 index 00000000..4ca68541 --- /dev/null +++ b/src/main/java/io/wispforest/owo/serialization/annotations/NullableComponent.java @@ -0,0 +1,15 @@ +package io.wispforest.owo.serialization.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Indicates to the {@link io.wispforest.owo.serialization.endec.RecordEndec} that this record component + * should be treated as nullable in serialization. Importantly, this changes the serialized type of this + * component to an optional + */ +@Target(ElementType.RECORD_COMPONENT) +@Retention(RetentionPolicy.RUNTIME) +public @interface NullableComponent {} diff --git a/src/main/java/io/wispforest/owo/serialization/annotations/NullableField.java b/src/main/java/io/wispforest/owo/serialization/annotations/NullableField.java deleted file mode 100644 index 48f68fa1..00000000 --- a/src/main/java/io/wispforest/owo/serialization/annotations/NullableField.java +++ /dev/null @@ -1,14 +0,0 @@ -package io.wispforest.owo.serialization.annotations; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * Annotation used within {@link io.wispforest.owo.serialization.endec.RecordEndec} to mark a field nullable - */ -@Target({ElementType.FIELD, ElementType.RECORD_COMPONENT}) -@Retention(RetentionPolicy.RUNTIME) -public @interface NullableField { -} diff --git a/src/main/java/io/wispforest/owo/serialization/annotations/SealedPolymorphic.java b/src/main/java/io/wispforest/owo/serialization/annotations/SealedPolymorphic.java index 337c5a4b..d2e7dc11 100644 --- a/src/main/java/io/wispforest/owo/serialization/annotations/SealedPolymorphic.java +++ b/src/main/java/io/wispforest/owo/serialization/annotations/SealedPolymorphic.java @@ -7,5 +7,4 @@ @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) -public @interface SealedPolymorphic { -} +public @interface SealedPolymorphic {} diff --git a/src/main/java/io/wispforest/owo/serialization/endec/RecordEndec.java b/src/main/java/io/wispforest/owo/serialization/endec/RecordEndec.java index 339ef842..8fc026e8 100644 --- a/src/main/java/io/wispforest/owo/serialization/endec/RecordEndec.java +++ b/src/main/java/io/wispforest/owo/serialization/endec/RecordEndec.java @@ -5,9 +5,8 @@ import io.wispforest.owo.serialization.Endec; import io.wispforest.owo.serialization.Serializer; import io.wispforest.owo.serialization.StructEndec; -import io.wispforest.owo.serialization.annotations.NullableField; +import io.wispforest.owo.serialization.annotations.NullableComponent; import org.apache.commons.lang3.mutable.MutableInt; -import org.jetbrains.annotations.Nullable; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; @@ -47,10 +46,7 @@ public static RecordEndec create(Class recordClass) { var handle = lookup.unreflect(component.getAccessor()); var endec = (Endec) ReflectiveEndecBuilder.get(component.getGenericType()); - - var nullAnnotation = component.getAnnotation(NullableField.class); - - if(nullAnnotation != null) endec = endec.nullableOf(); + if(component.isAnnotationPresent(NullableComponent.class)) endec = endec.nullableOf(); fields.add(new StructField<>( component.getName(), diff --git a/src/testmod/java/io/wispforest/uwu/network/NullablePacket.java b/src/testmod/java/io/wispforest/uwu/network/NullablePacket.java index 6fd46770..2e8d3261 100644 --- a/src/testmod/java/io/wispforest/uwu/network/NullablePacket.java +++ b/src/testmod/java/io/wispforest/uwu/network/NullablePacket.java @@ -1,8 +1,7 @@ package io.wispforest.uwu.network; -import io.wispforest.owo.serialization.annotations.NullableField; +import io.wispforest.owo.serialization.annotations.NullableComponent; import java.util.List; -public record NullablePacket(@NullableField String name, @NullableField List names) { -} +public record NullablePacket(@NullableComponent String name, @NullableComponent List names) {} diff --git a/src/testmod/java/io/wispforest/uwu/network/UwuNetworkExample.java b/src/testmod/java/io/wispforest/uwu/network/UwuNetworkExample.java index 5d809569..92b4d6a8 100644 --- a/src/testmod/java/io/wispforest/uwu/network/UwuNetworkExample.java +++ b/src/testmod/java/io/wispforest/uwu/network/UwuNetworkExample.java @@ -45,8 +45,8 @@ public static void init() { } else { var text = Text.literal(""); - text.append(Text.of((message.name() == null) ? "null" : message.name())); - text.append(Text.of((message.names() == null) ? "null" : message.names().toString())); + text.append(Text.of(String.valueOf(message.name()))); + text.append(Text.of(String.valueOf(message.names()))); access.player().sendMessage(text); }