Created
April 15, 2025 08:39
-
-
Save Cretezy/ae9d66c5ddd5efc582feec9824ed84c8 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class SerializerTest: | |
const _NATIVE_SERIALIZABLE_TYPES = [ | |
TYPE_VECTOR2, | |
TYPE_VECTOR2I, | |
TYPE_VECTOR3, | |
TYPE_VECTOR3I, | |
TYPE_VECTOR4, | |
TYPE_VECTOR4I, | |
TYPE_RECT2, | |
TYPE_RECT2I, | |
TYPE_BASIS, | |
TYPE_TRANSFORM2D, | |
TYPE_TRANSFORM3D, | |
TYPE_PLANE, | |
TYPE_QUATERNION, | |
TYPE_AABB, | |
TYPE_PROJECTION, | |
TYPE_COLOR, | |
TYPE_PACKED_BYTE_ARRAY, # Could be more efficient (base64) | |
TYPE_PACKED_INT32_ARRAY, | |
TYPE_PACKED_INT64_ARRAY, | |
TYPE_PACKED_FLOAT32_ARRAY, | |
TYPE_PACKED_FLOAT64_ARRAY, | |
TYPE_PACKED_STRING_ARRAY, | |
TYPE_PACKED_VECTOR2_ARRAY, | |
TYPE_PACKED_VECTOR3_ARRAY, | |
TYPE_PACKED_COLOR_ARRAY, | |
TYPE_PACKED_VECTOR4_ARRAY, | |
] | |
static var type_field := "._type" | |
static var _object_prefix := "Object_" | |
static var _registry: Dictionary[String, ObjectRegistryEntry] | |
static func register(script: Script, name: StringName = "") -> void: | |
var script_name := _get_script_name(script, name) | |
assert(script_name, "Script must have name\n" + script.source_code) | |
var entry := ObjectRegistryEntry.new() | |
entry.script_type = script | |
entry.type = _object_prefix + script_name | |
_registry[entry.type] = entry | |
static func serialize(value: Variant) -> Variant: | |
match typeof(value): | |
TYPE_OBJECT: | |
var name: StringName = value.get_script().get_global_name() | |
var object_entry := _get_entry(name, value.get_script()) | |
if !object_entry: | |
assert(false, "Could not find type (%s) in registry\n%s" % [name if name else "no name", value.get_script().source_code]); | |
return object_entry.serialize(value) | |
TYPE_ARRAY: | |
return value.map(SerializerTest.serialize) | |
TYPE_DICTIONARY: | |
var result := {} | |
for i: Variant in value: | |
result[i] = SerializerTest.serialize(value[i]) | |
return result | |
if _NATIVE_SERIALIZABLE_TYPES.has(typeof(value)): | |
return { | |
SerializerTest.type_field: type_string(typeof(value)), | |
"_": JSON.from_native(value)["args"] | |
} | |
return value; | |
static func deserialize(value: Variant) -> Variant: | |
match typeof(value): | |
TYPE_DICTIONARY: | |
if value.has(type_field): | |
var type: String = value.get(type_field) | |
if type.begins_with(_object_prefix): | |
var entry := _get_entry(type) | |
if !entry: | |
assert(false, "Could not find type (%s) in registry" % type); | |
return entry.deserialize(value) | |
elif _NATIVE_SERIALIZABLE_TYPES.any( | |
func(native_type: Variant.Type) -> bool: return type_string(native_type) == type | |
): | |
return JSON.to_native({ | |
"type": type, | |
"args": value["_"] | |
}) | |
var result := {} | |
for i: Variant in value: | |
result[i] = SerializerTest.deserialize(value[i]) | |
return result | |
TYPE_ARRAY: | |
return value.map(SerializerTest.deserialize) | |
return value | |
static func _get_script_name(script: Script, name: StringName = "") -> StringName: | |
if name: | |
return name | |
if script.resource_name: | |
return script.resource_name | |
if script.get_global_name(): | |
return script.get_global_name() | |
return "" | |
static func _get_entry(name: StringName = "", script: Script = null) -> ObjectRegistryEntry: | |
if name: | |
var entry: ObjectRegistryEntry = _registry.get(name) | |
if entry: | |
return entry | |
if script: | |
for i: String in _registry: | |
var entry: ObjectRegistryEntry = _registry.get(i) | |
if entry: | |
if script == entry.script_type: | |
return entry | |
return null | |
class ObjectRegistryEntry: | |
var type: String | |
var script_type: Script | |
func serialize(value: Variant) -> Variant: | |
var result := { | |
SerializerTest.type_field: type | |
} | |
for property: Dictionary in value.get_property_list(): | |
if property.usage & PROPERTY_USAGE_SCRIPT_VARIABLE: | |
result[property.name] = SerializerTest.serialize(value.get(property.name)) | |
return result | |
func deserialize(value: Variant) -> Variant: | |
var instance: Variant = script_type.new() | |
for key: String in value: | |
if key == SerializerTest.type_field: | |
continue | |
var key_value: Variant = SerializerTest.deserialize(value[key]) | |
match typeof(key_value): | |
TYPE_DICTIONARY: | |
instance[key].assign(key_value) | |
TYPE_ARRAY: | |
instance[key].assign(key_value) | |
_: | |
instance[key] = key_value | |
return instance | |
# Example | |
class Data: | |
# Custom handling | |
var nested: Data | |
var array: Array | |
var array_nested: Array[Data] | |
var dictionary: Dictionary | |
var dictionary_nested: Dictionary[String, Data] | |
# JSON types | |
var string: String | |
var boolean: bool | |
var integer: int | |
var floating_point: float | |
# Godot types | |
var vector2: Vector2 | |
var vector2i: Vector2i | |
var vector3: Vector3 | |
var vector3i: Vector3i | |
var vector4: Vector4 | |
var vector4i: Vector4i | |
var rect2: Rect2 | |
var rect2i: Rect2i | |
var transform2d: Transform2D | |
var transform3d: Transform3D | |
var basis: Basis | |
var plane: Plane | |
var quaternion: Quaternion | |
var aabb: AABB | |
var projection: Projection | |
var color: Color | |
var packed_byte_array: PackedByteArray | |
var packed_int32_array: PackedInt32Array | |
var packed_int64_array: PackedInt64Array | |
var packed_float32_array: PackedFloat32Array | |
var packed_float64_array: PackedFloat64Array | |
var packed_string_array: PackedStringArray | |
var packed_vector2_array: PackedVector2Array | |
var packed_vector3_array: PackedVector3Array | |
var packed_vector4_array: PackedVector4Array | |
var packed_color_array: PackedColorArray | |
func _build_data(string: String) -> Data: | |
var data := Data.new() | |
data.string = string | |
return data | |
# Tests | |
func _init() -> void: | |
# Register objects | |
SerializerTest.register(Data, "Data") | |
# Build test data | |
var data := Data.new() | |
data.nested = _build_data("nested") | |
data.array = [1, "a", Vector2(1,2)] | |
data.array_nested = [_build_data("array nested")] | |
data.dictionary = { "int": 1, "vector": Vector2(1, 2) } | |
data.dictionary_nested = { "data": _build_data("dictionary nested") } | |
data.string = "a" | |
data.boolean = true | |
data.integer = 1 | |
data.floating_point = 2.3 | |
data.vector2 = Vector2(1.2, 3.4) | |
data.vector2i = Vector2i(1, 2) | |
data.vector3 = Vector3(1.2, 3.4, 5.6) | |
data.vector3i = Vector3(1, 2, 3) | |
data.vector4 = Vector4(1.2, 3.4, 5.6, 7.8) | |
data.vector4i = Vector4i(1, 2, 3, 4) | |
data.rect2 = Rect2(1.2, 3.4, 5.6, 7.8) | |
data.rect2i = Rect2(1, 2, 3, 4) | |
data.transform2d = Transform2D(Vector2(1, 2), Vector2(3, 4), Vector2(5, 6)) | |
data.transform3d = Transform3D(Vector3(1, 2, 3), Vector3(4, 5, 6), Vector3(7, 8, 9), Vector3(10, 11, 12)) | |
data.basis = Basis(Vector3(1, 2, 3), Vector3(4, 5, 6), Vector3(7, 8, 9)) | |
data.plane = Plane(Vector3(1, 2, 3), Vector3(4, 5, 6), Vector3(7, 8, 9)) | |
data.quaternion = Quaternion(Vector3(1, 2, 3), 2) | |
data.aabb = AABB(Vector3(1, 2, 3), Vector3(4, 5, 6)) | |
data.projection = Projection(Vector4(1, 2, 3, 4), Vector4(1, 2, 3, 4), Vector4(1, 2, 3, 4), Vector4(1, 2, 3, 4)) | |
data.color = Color(0.1, 0.2, 0.3, 0.4) | |
data.packed_byte_array = PackedByteArray([1, 2, 3]) | |
data.packed_int32_array = PackedInt32Array([1, 2, 3]) | |
data.packed_int64_array = PackedInt64Array([1, 2, 3]) | |
data.packed_float32_array = PackedFloat32Array([1.2, 2.3]) | |
data.packed_float64_array = PackedFloat64Array([1.2, 2.3]) | |
data.packed_string_array = PackedStringArray(["a", "b"]) | |
data.packed_vector2_array = PackedVector2Array([Vector2(1, 2), Vector2(3, 4)]) | |
data.packed_vector3_array = PackedVector3Array([Vector3(1, 2, 3), Vector3(4, 5, 6)]) | |
data.packed_vector4_array = PackedVector4Array([Vector4(1, 2, 3, 4), Vector4(5, 6, 7, 8)]) | |
data.packed_color_array = PackedColorArray([Color(0.1, 0.2, 0.3, 0.4), Color(0.5, 0.6, 0.7, 0.8)]) | |
# Serialize | |
var serialized: Variant = SerializerTest.serialize(data) | |
print(JSON.stringify(serialized, " ")) | |
# Verify | |
var deserialized: Data = SerializerTest.deserialize(serialized) | |
assert(data.nested.string == deserialized.nested.string, "nested different") | |
assert(data.array == deserialized.array, "array different") | |
assert(data.array_nested[0].string == deserialized.array_nested[0].string, "array nested different") | |
assert(data.dictionary == deserialized.dictionary, "dictionary different") | |
assert(data.dictionary_nested["data"].string == deserialized.dictionary_nested["data"].string, "dictionary nested different") | |
assert(data.string == deserialized.string, "string different") | |
assert(data.integer == deserialized.integer, "integer different") | |
assert(data.floating_point == deserialized.floating_point, "floating_point different") | |
assert(data.vector2 == deserialized.vector2, "vector2 different") | |
assert(data.vector2i == deserialized.vector2i, "vector2i different") | |
assert(data.vector3 == deserialized.vector3, "vector3 different") | |
assert(data.vector3i == deserialized.vector3i, "vector3i different") | |
assert(data.vector4 == deserialized.vector4, "vector4 different") | |
assert(data.vector4i == deserialized.vector4i, "vector4i different") | |
assert(data.rect2 == deserialized.rect2, "rect2 different") | |
assert(data.rect2i == deserialized.rect2i, "rect2i different") | |
assert(data.transform2d == deserialized.transform2d, "transform2d different") | |
assert(data.transform3d == deserialized.transform3d, "transform3d different") | |
assert(data.basis == deserialized.basis, "basis different") | |
assert(data.plane == deserialized.plane, "plane different") | |
assert(data.quaternion == deserialized.quaternion, "quaternion different") | |
assert(data.aabb == deserialized.aabb, "aabb different") | |
assert(data.projection == deserialized.projection, "projection different") | |
assert(data.color == deserialized.color, "color different") | |
assert(data.packed_byte_array == deserialized.packed_byte_array, "packed_byte_array different") | |
assert(data.packed_int32_array == deserialized.packed_int32_array , "packed_int32_array different") | |
assert(data.packed_int64_array == deserialized.packed_int64_array, "packed_int64_array different") | |
assert(data.packed_float32_array == deserialized.packed_float32_array, "packed_float32_array different") | |
assert(data.packed_float64_array == deserialized.packed_float64_array, "packed_float64_array different") | |
assert(data.packed_string_array == deserialized.packed_string_array, "packed_string_array different") | |
assert(data.packed_vector2_array == deserialized.packed_vector2_array, "packed_vector2_array different") | |
assert(data.packed_vector3_array == deserialized.packed_vector3_array, "packed_vector3_array different") | |
assert(data.packed_vector4_array == deserialized.packed_vector4_array, "packed_vector4_array different") | |
assert(data.packed_color_array == deserialized.packed_color_array, "packed_color_array different") | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment