Last active
April 22, 2026 23:50
-
-
Save theraot/31515e28e2d8bfea33f6c6d5bcd852f6 to your computer and use it in GitHub Desktop.
BinarySerializer for Godot 4.6+
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
| @tool | |
| class_name BinarySerializer # RefCounted (Static) | |
| ## Comprehensive binary serialization system for built-in data types. | |
| const _BITS08 := 1 | |
| const _BITS16 := 2 | |
| const _BITS32 := 4 | |
| const _BITS64 := 8 | |
| const _TYPE_BOOL_SIZE := _BITS08 | |
| const _TYPE_INT_SIZE := _BITS64 | |
| const _TYPE_FLOAT_SIZE := _BITS32 | |
| const _TYPE_VECTOR2_SIZE := _TYPE_FLOAT_SIZE * 2 | |
| const _TYPE_VECTOR2I_SIZE := _TYPE_INT_SIZE * 2 | |
| const _TYPE_RECT2_SIZE := _TYPE_VECTOR2_SIZE * 2 | |
| const _TYPE_RECT2I_SIZE := _TYPE_VECTOR2I_SIZE * 2 | |
| const _TYPE_VECTOR3_SIZE := _TYPE_FLOAT_SIZE * 3 | |
| const _TYPE_VECTOR3I_SIZE := _TYPE_INT_SIZE * 3 | |
| const _TYPE_TRANSFORM2D_SIZE := _TYPE_VECTOR2_SIZE * 3 | |
| const _TYPE_VECTOR4_SIZE := _TYPE_FLOAT_SIZE * 4 | |
| const _TYPE_VECTOR4I_SIZE := _TYPE_INT_SIZE * 4 | |
| const _TYPE_PLANE_SIZE := _TYPE_VECTOR3_SIZE + _TYPE_FLOAT_SIZE | |
| const _TYPE_QUATERNION_SIZE := _TYPE_FLOAT_SIZE * 4 | |
| const _TYPE_AABB_SIZE := _TYPE_VECTOR3_SIZE * 2 | |
| const _TYPE_BASIS_SIZE := _TYPE_VECTOR3_SIZE * 3 | |
| const _TYPE_TRANSFORM3D_SIZE := _TYPE_VECTOR3_SIZE * 4 | |
| const _TYPE_PROJECTION_SIZE := _TYPE_VECTOR4_SIZE * 4 | |
| const _TYPE_COLOR_SIZE := _BITS16 * 4 | |
| const _RESOURCE_PROPERTIES := [ | |
| &"resource_local_to_scene", &"resource_name", &"resource_path", &"resource_scene_unique_id" | |
| ] | |
| static func skip_byte_count(buffer: StreamPeerBuffer, count: int) -> void: | |
| assert(count >= 0) | |
| var new_position := buffer.get_position() + count | |
| buffer.seek(new_position) | |
| static func save_dictionary_of( | |
| buffer: StreamPeerBuffer, input: Dictionary, keys_to_skip: Array = [] | |
| ) -> void: | |
| var key_type := input.get_typed_key_builtin() | |
| var value_type := input.get_typed_value_builtin() | |
| var sub_buffer := StreamPeerBuffer.new() | |
| for key: Variant in input.keys(): | |
| if keys_to_skip.has(key): | |
| continue | |
| var value: Variant = input[key] | |
| save_value(sub_buffer, key, key_type, {}) | |
| save_value(sub_buffer, value, value_type, {}) | |
| save_byte_array(buffer, sub_buffer.data_array) | |
| static func load_dictionary_to( | |
| buffer: StreamPeerBuffer, output: Dictionary, keys_to_skip: Array = [] | |
| ) -> void: | |
| var key_type := output.get_typed_key_builtin() | |
| var value_type := output.get_typed_value_builtin() | |
| var sub_buffer := StreamPeerBuffer.new() | |
| sub_buffer.data_array = load_byte_array(buffer) | |
| while sub_buffer.get_available_bytes() > 0: | |
| var key: Variant = load_value(sub_buffer, key_type, {}) | |
| if keys_to_skip.has(key): | |
| skip_value(sub_buffer, value_type, {}) | |
| continue | |
| var value: Variant = load_value(sub_buffer, value_type, {}) | |
| output[key] = value | |
| #region BOOL | |
| static func save_bool(buffer: StreamPeerBuffer, input: bool) -> void: | |
| buffer.put_u8(0b11111111 if input else 0) | |
| static func load_bool(buffer: StreamPeerBuffer) -> bool: | |
| return buffer.get_u8() != 0 | |
| static func skip_bool(buffer: StreamPeerBuffer) -> void: | |
| skip_byte_count(buffer, _TYPE_BOOL_SIZE) | |
| #endregion | |
| #region INT | |
| static func save_int(buffer: StreamPeerBuffer, input: int) -> void: | |
| buffer.put_64(input) | |
| static func load_int(buffer: StreamPeerBuffer) -> int: | |
| return buffer.get_64() | |
| static func skip_int(buffer: StreamPeerBuffer) -> void: | |
| skip_byte_count(buffer, _TYPE_INT_SIZE) | |
| #endregion | |
| #region FLOAT | |
| static func save_float(buffer: StreamPeerBuffer, input: float) -> void: | |
| buffer.put_float(input) | |
| static func load_float(buffer: StreamPeerBuffer) -> float: | |
| return buffer.get_float() | |
| static func skip_float(buffer: StreamPeerBuffer) -> void: | |
| skip_byte_count(buffer, _TYPE_FLOAT_SIZE) | |
| #endregion | |
| #region STRING | |
| static func save_string(buffer: StreamPeerBuffer, input: String) -> void: | |
| save_byte_array(buffer, input.to_utf8_buffer()) | |
| static func load_string(buffer: StreamPeerBuffer) -> String: | |
| var utf8 := load_byte_array(buffer) | |
| return utf8.get_string_from_utf8() | |
| static func skip_string(buffer: StreamPeerBuffer) -> void: | |
| skip_byte_array(buffer) | |
| #endregion | |
| #region VECTOR2 | |
| static func save_vector2(buffer: StreamPeerBuffer, input: Vector2) -> void: | |
| save_float(buffer, input.x) | |
| save_float(buffer, input.y) | |
| static func load_vector2(buffer: StreamPeerBuffer) -> Vector2: | |
| return Vector2(load_float(buffer), load_float(buffer)) | |
| static func skip_vector2(buffer: StreamPeerBuffer) -> void: | |
| skip_byte_count(buffer, _TYPE_VECTOR2_SIZE) | |
| #endregion | |
| #region VECTOR2I | |
| static func save_vector2i(buffer: StreamPeerBuffer, input: Vector2i) -> void: | |
| buffer.put_32(input.x) | |
| buffer.put_32(input.y) | |
| static func load_vector2i(buffer: StreamPeerBuffer) -> Vector2i: | |
| return Vector2i(buffer.get_32(), buffer.get_32()) | |
| static func skip_vector2i(buffer: StreamPeerBuffer) -> void: | |
| skip_byte_count(buffer, _TYPE_VECTOR2I_SIZE) | |
| #endregion | |
| #region RECT2 | |
| static func save_rect2(buffer: StreamPeerBuffer, input: Rect2) -> void: | |
| save_vector2(buffer, input.position) | |
| save_vector2(buffer, input.size) | |
| static func load_rect2(buffer: StreamPeerBuffer) -> Rect2: | |
| return Rect2(load_vector2(buffer), load_vector2(buffer)) | |
| static func skip_rect2(buffer: StreamPeerBuffer) -> void: | |
| skip_byte_count(buffer, _TYPE_RECT2_SIZE) | |
| #endregion | |
| #region RECT2I | |
| static func save_rect2i(buffer: StreamPeerBuffer, input: Rect2i) -> void: | |
| save_vector2i(buffer, input.position) | |
| save_vector2i(buffer, input.size) | |
| static func load_rect2i(buffer: StreamPeerBuffer) -> Rect2i: | |
| return Rect2i(load_vector2i(buffer), load_vector2i(buffer)) | |
| static func skip_rect2i(buffer: StreamPeerBuffer) -> void: | |
| skip_byte_count(buffer, _TYPE_RECT2I_SIZE) | |
| #endregion | |
| #region VECTOR3 | |
| static func save_vector3(buffer: StreamPeerBuffer, input: Vector3) -> void: | |
| save_float(buffer, input.x) | |
| save_float(buffer, input.y) | |
| save_float(buffer, input.z) | |
| static func load_vector3(buffer: StreamPeerBuffer) -> Vector3: | |
| return Vector3(load_float(buffer), load_float(buffer), load_float(buffer)) | |
| static func skip_vector3(buffer: StreamPeerBuffer) -> void: | |
| skip_byte_count(buffer, _TYPE_VECTOR3_SIZE) | |
| #endregion | |
| #region VECTOR3I | |
| static func save_vector3i(buffer: StreamPeerBuffer, input: Vector3i) -> void: | |
| buffer.put_32(input.x) | |
| buffer.put_32(input.y) | |
| buffer.put_32(input.z) | |
| static func load_vector3i(buffer: StreamPeerBuffer) -> Vector3i: | |
| return Vector3i(buffer.get_32(), buffer.get_32(), buffer.get_32()) | |
| static func skip_vector3i(buffer: StreamPeerBuffer) -> void: | |
| skip_byte_count(buffer, _TYPE_VECTOR3I_SIZE) | |
| #endregion | |
| #region TRANSFORM2D | |
| static func save_transform2d(buffer: StreamPeerBuffer, input: Transform2D) -> void: | |
| save_vector2(buffer, input.x) | |
| save_vector2(buffer, input.y) | |
| save_vector2(buffer, input.origin) | |
| static func load_transform2d(buffer: StreamPeerBuffer) -> Transform2D: | |
| return Transform2D(load_vector2(buffer), load_vector2(buffer), load_vector2(buffer)) | |
| static func skip_transform2d(buffer: StreamPeerBuffer) -> void: | |
| skip_byte_count(buffer, _TYPE_TRANSFORM2D_SIZE) | |
| #endregion | |
| #region VECTOR4 | |
| static func save_vector4(buffer: StreamPeerBuffer, input: Vector4) -> void: | |
| save_float(buffer, input.x) | |
| save_float(buffer, input.y) | |
| save_float(buffer, input.z) | |
| save_float(buffer, input.w) | |
| static func load_vector4(buffer: StreamPeerBuffer) -> Vector4: | |
| return Vector4(load_float(buffer), load_float(buffer), load_float(buffer), load_float(buffer)) | |
| static func skip_vector4(buffer: StreamPeerBuffer) -> void: | |
| skip_byte_count(buffer, _TYPE_VECTOR4_SIZE) | |
| #endregion | |
| #region VECTOR4I | |
| static func save_vector4i(buffer: StreamPeerBuffer, input: Vector4i) -> void: | |
| buffer.put_32(input.x) | |
| buffer.put_32(input.y) | |
| buffer.put_32(input.z) | |
| buffer.put_32(input.w) | |
| static func load_vector4i(buffer: StreamPeerBuffer) -> Vector4i: | |
| return Vector4i(buffer.get_32(), buffer.get_32(), buffer.get_32(), buffer.get_32()) | |
| static func skip_vector4i(buffer: StreamPeerBuffer) -> void: | |
| skip_byte_count(buffer, _TYPE_VECTOR4I_SIZE) | |
| #endregion | |
| #region PLANE | |
| static func save_plane(buffer: StreamPeerBuffer, input: Plane) -> void: | |
| save_vector3(buffer, input.normal) | |
| save_float(buffer, input.d) | |
| static func load_plane(buffer: StreamPeerBuffer) -> Plane: | |
| return Plane(load_vector3(buffer), load_float(buffer)) | |
| static func skip_plane(buffer: StreamPeerBuffer) -> void: | |
| skip_byte_count(buffer, _TYPE_PLANE_SIZE) | |
| #endregion | |
| #region QUATERNION | |
| static func save_quaternion(buffer: StreamPeerBuffer, input: Quaternion) -> void: | |
| save_float(buffer, input.x) | |
| save_float(buffer, input.y) | |
| save_float(buffer, input.z) | |
| save_float(buffer, input.w) | |
| static func load_quaternion(buffer: StreamPeerBuffer) -> Quaternion: | |
| return Quaternion( | |
| load_float(buffer), load_float(buffer), load_float(buffer), load_float(buffer) | |
| ) | |
| static func skip_quaternion(buffer: StreamPeerBuffer) -> void: | |
| skip_byte_count(buffer, _TYPE_QUATERNION_SIZE) | |
| #endregion | |
| #region AABB | |
| static func save_aabb(buffer: StreamPeerBuffer, input: AABB) -> void: | |
| save_vector3(buffer, input.position) | |
| save_vector3(buffer, input.size) | |
| static func load_aabb(buffer: StreamPeerBuffer) -> AABB: | |
| return AABB(load_vector3(buffer), load_vector3(buffer)) | |
| static func skip_aabb(buffer: StreamPeerBuffer) -> void: | |
| skip_byte_count(buffer, _TYPE_AABB_SIZE) | |
| #endregion | |
| #region BASIS | |
| static func save_basis(buffer: StreamPeerBuffer, input: Basis) -> void: | |
| save_vector3(buffer, input.x) | |
| save_vector3(buffer, input.y) | |
| save_vector3(buffer, input.z) | |
| static func load_basis(buffer: StreamPeerBuffer) -> Basis: | |
| return Basis(load_vector3(buffer), load_vector3(buffer), load_vector3(buffer)) | |
| static func skip_basis(buffer: StreamPeerBuffer) -> void: | |
| skip_byte_count(buffer, _TYPE_BASIS_SIZE) | |
| #endregion | |
| #region TRANSFORM3D | |
| static func save_transform3d(buffer: StreamPeerBuffer, input: Transform3D) -> void: | |
| save_vector3(buffer, input.basis.x) | |
| save_vector3(buffer, input.basis.y) | |
| save_vector3(buffer, input.basis.z) | |
| save_vector3(buffer, input.origin) | |
| static func load_transform3d(buffer: StreamPeerBuffer) -> Transform3D: | |
| return Transform3D( | |
| load_vector3(buffer), load_vector3(buffer), load_vector3(buffer), load_vector3(buffer) | |
| ) | |
| static func skip_transform3d(buffer: StreamPeerBuffer) -> void: | |
| skip_byte_count(buffer, _TYPE_TRANSFORM3D_SIZE) | |
| #endregion | |
| #region PROJECTION | |
| static func save_projection(buffer: StreamPeerBuffer, input: Projection) -> void: | |
| save_vector4(buffer, input.x) | |
| save_vector4(buffer, input.y) | |
| save_vector4(buffer, input.z) | |
| save_vector4(buffer, input.w) | |
| static func load_projection(buffer: StreamPeerBuffer) -> Projection: | |
| return Projection( | |
| load_vector4(buffer), load_vector4(buffer), load_vector4(buffer), load_vector4(buffer) | |
| ) | |
| static func skip_projection(buffer: StreamPeerBuffer) -> void: | |
| skip_byte_count(buffer, _TYPE_PROJECTION_SIZE) | |
| #endregion | |
| #region COLOR | |
| static func save_color(buffer: StreamPeerBuffer, input: Color) -> void: | |
| buffer.put_half(input.r) | |
| buffer.put_half(input.g) | |
| buffer.put_half(input.b) | |
| buffer.put_half(input.a) | |
| static func load_color(buffer: StreamPeerBuffer) -> Color: | |
| return Color(buffer.get_half(), buffer.get_half(), buffer.get_half(), buffer.get_half()) | |
| static func skip_color(buffer: StreamPeerBuffer) -> void: | |
| skip_byte_count(buffer, _TYPE_COLOR_SIZE) | |
| #endregion | |
| #region STRING_NAME | |
| static func save_string_name(buffer: StreamPeerBuffer, input: StringName) -> void: | |
| save_string(buffer, str(input)) | |
| static func load_string_name(buffer: StreamPeerBuffer) -> StringName: | |
| return StringName(load_string(buffer)) | |
| static func skip_string_name(buffer: StreamPeerBuffer) -> void: | |
| skip_byte_array(buffer) | |
| #endregion | |
| #region NODE_PATH | |
| static func save_node_path(buffer: StreamPeerBuffer, input: NodePath) -> void: | |
| save_string(buffer, str(input)) | |
| static func load_node_path(buffer: StreamPeerBuffer) -> NodePath: | |
| return NodePath(load_string(buffer)) | |
| static func skip_node_path(buffer: StreamPeerBuffer) -> void: | |
| skip_byte_array(buffer) | |
| #endregion | |
| #region CALLABLE | |
| static func save_callable( | |
| buffer: StreamPeerBuffer, input: Callable, saved_objects: Dictionary[int, Object] | |
| ) -> void: | |
| save_string(buffer, input.get_method()) | |
| save_array(buffer, input.get_bound_arguments(), saved_objects) | |
| save_object(buffer, input.get_object(), saved_objects) | |
| static func load_callable( | |
| buffer: StreamPeerBuffer, saved_objects: Dictionary[int, Object] | |
| ) -> Callable: | |
| var method := load_string(buffer) | |
| var arguments := load_array(buffer, saved_objects) | |
| var object := load_object(buffer, saved_objects) | |
| return Callable(object, method).bindv(arguments) | |
| static func skip_callable(buffer: StreamPeerBuffer, saved_objects: Dictionary[int, Object]) -> void: | |
| skip_string(buffer) | |
| skip_array(buffer, saved_objects) | |
| skip_object(buffer, saved_objects) | |
| #endregion | |
| #region SIGNAL | |
| static func save_signal( | |
| buffer: StreamPeerBuffer, input: Signal, saved_objects: Dictionary[int, Object] | |
| ) -> void: | |
| save_string(buffer, input.get_name()) | |
| save_object(buffer, input.get_object(), saved_objects) | |
| static func load_signal(buffer: StreamPeerBuffer, saved_objects: Dictionary[int, Object]) -> Signal: | |
| var name := load_string(buffer) | |
| var object := load_object(buffer, saved_objects) | |
| return Signal(object, name) | |
| static func skip_signal(buffer: StreamPeerBuffer, saved_objects: Dictionary[int, Object]) -> void: | |
| skip_string(buffer) | |
| skip_object(buffer, saved_objects) | |
| #endregion | |
| #region OBJECT | |
| static func save_object( | |
| buffer: StreamPeerBuffer, input: Object, saved_objects: Dictionary[int, Object] | |
| ) -> void: | |
| if input == null: | |
| buffer.put_32(0) | |
| return | |
| var id := input.get_instance_id() | |
| buffer.put_32(id) | |
| if saved_objects.has(id): | |
| save_bool(buffer, true) | |
| return | |
| saved_objects[id] = input | |
| save_bool(buffer, false) | |
| var sub_buffer := StreamPeerBuffer.new() | |
| var class_string := InstanceHelper.get_class_by_instance(input) | |
| save_string(sub_buffer, class_string) | |
| var properties := input.get_property_list() | |
| for property in properties: | |
| var value_type: Variant.Type = property["type"] | |
| if value_type == TYPE_NIL: | |
| continue | |
| var usage: PropertyUsageFlags = property["usage"] | |
| if usage & PROPERTY_USAGE_STORAGE == 0: | |
| continue | |
| var name := str(property["name"]) | |
| if name == &"script" or (input is Resource and name in _RESOURCE_PROPERTIES): | |
| continue | |
| var value: Variant = input.get(name) | |
| save_string(sub_buffer, name) | |
| sub_buffer.put_8(value_type) | |
| save_value(sub_buffer, value, value_type, saved_objects) | |
| save_byte_array(buffer, sub_buffer.data_array) | |
| static func load_object( | |
| buffer: StreamPeerBuffer, loaded_objects: Dictionary[int, Object] | |
| ) -> Object: | |
| var id := buffer.get_32() | |
| if id == 0: | |
| return null | |
| var is_placeholder := load_bool(buffer) | |
| if is_placeholder: | |
| if loaded_objects.has(id): | |
| return loaded_objects[id] | |
| push_error("Missing object id %d" % str(id)) | |
| return null | |
| if loaded_objects.has(id): | |
| push_error("Duplicate object id %d" % str(id)) | |
| return null | |
| var sub_buffer := StreamPeerBuffer.new() | |
| sub_buffer.data_array = load_byte_array(buffer) | |
| if sub_buffer.get_size() == 0: | |
| loaded_objects[id] = null | |
| return null | |
| var class_string := load_string(sub_buffer) | |
| var output: Object = InstanceHelper.instantiate_class(class_string) | |
| loaded_objects[id] = output | |
| var seen_properties := [] | |
| while sub_buffer.get_available_bytes() > 0: | |
| var name := load_string(sub_buffer) | |
| var value_type := sub_buffer.get_8() | |
| seen_properties.append(name) | |
| if ( | |
| name not in output | |
| or name == &"script" | |
| or (output is Resource and name in _RESOURCE_PROPERTIES) | |
| ): | |
| skip_value(sub_buffer, value_type, loaded_objects) | |
| else: | |
| var value: Variant = load_value(sub_buffer, value_type, loaded_objects) | |
| output.set(name, value) | |
| return output | |
| static func skip_object(buffer: StreamPeerBuffer, loaded_objects: Dictionary[int, Object]) -> void: | |
| @warning_ignore("return_value_discarded") | |
| load_object(buffer, loaded_objects) | |
| #endregion | |
| #region DICTIONARY | |
| static func save_dictionary( | |
| buffer: StreamPeerBuffer, | |
| input: Dictionary, | |
| saved_objects: Dictionary[int, Object], | |
| keys_to_skip: Array = [] | |
| ) -> void: | |
| var sub_buffer := StreamPeerBuffer.new() | |
| var key_type := input.get_typed_key_builtin() | |
| var value_type := input.get_typed_value_builtin() | |
| sub_buffer.put_8(key_type) | |
| sub_buffer.put_8(value_type) | |
| for key: Variant in input.keys(): | |
| if keys_to_skip.has(key): | |
| continue | |
| var value: Variant = input[key] | |
| save_value(sub_buffer, key, key_type, saved_objects) | |
| save_value(sub_buffer, value, value_type, saved_objects) | |
| save_byte_array(buffer, sub_buffer.data_array) | |
| static func load_dictionary( | |
| buffer: StreamPeerBuffer, loaded_objects: Dictionary[int, Object], keys_to_skip: Array = [] | |
| ) -> Dictionary: | |
| var output := Dictionary() | |
| var sub_buffer := StreamPeerBuffer.new() | |
| sub_buffer.data_array = load_byte_array(buffer) | |
| var key_type := sub_buffer.get_8() | |
| var value_type := sub_buffer.get_8() | |
| while sub_buffer.get_available_bytes() > 0: | |
| var key: Variant = load_value(sub_buffer, key_type, loaded_objects) | |
| if keys_to_skip.has(key): | |
| skip_value(sub_buffer, value_type, loaded_objects) | |
| continue | |
| var value: Variant = load_value(sub_buffer, value_type, loaded_objects) | |
| output[key] = value | |
| return output | |
| static func skip_dictionary( | |
| buffer: StreamPeerBuffer, loaded_objects: Dictionary[int, Object] | |
| ) -> void: | |
| @warning_ignore("return_value_discarded") | |
| load_dictionary(buffer, loaded_objects) | |
| #endregion | |
| #region ARRAY | |
| static func save_array( | |
| buffer: StreamPeerBuffer, input: Array, saved_objects: Dictionary[int, Object] | |
| ) -> void: | |
| var sub_buffer := StreamPeerBuffer.new() | |
| var value_type := input.get_typed_builtin() | |
| sub_buffer.put_8(value_type) | |
| if value_type == TYPE_OBJECT: | |
| save_string(sub_buffer, input.get_typed_class_name()) | |
| var script: GDScript = input.get_typed_script() | |
| save_string(sub_buffer, ScriptHelper.get_class_by_script_or_empty(script)) | |
| for value: Variant in input: | |
| save_value(sub_buffer, value, value_type, saved_objects) | |
| save_byte_array(buffer, sub_buffer.data_array) | |
| static func load_array(buffer: StreamPeerBuffer, loaded_objects: Dictionary[int, Object]) -> Array: | |
| var sub_buffer := StreamPeerBuffer.new() | |
| sub_buffer.data_array = load_byte_array(buffer) | |
| var value_type := sub_buffer.get_8() | |
| var class_string := &"" | |
| var script: GDScript = null | |
| if value_type == TYPE_OBJECT: | |
| class_string = load_string(sub_buffer) | |
| var class_script := load_string(sub_buffer) | |
| script = ScriptHelper.get_script_by_class_or_empty(class_script) | |
| var output := Array([], value_type, class_string, script) | |
| while sub_buffer.get_available_bytes() > 0: | |
| var value: Variant = load_value(sub_buffer, value_type, loaded_objects) | |
| output.append(value) | |
| return output | |
| static func skip_array(buffer: StreamPeerBuffer, loaded_objects: Dictionary[int, Object]) -> void: | |
| @warning_ignore("return_value_discarded") | |
| load_array(buffer, loaded_objects) | |
| #endregion | |
| #region PACKED_BYTE_ARRAY | |
| static func save_byte_array(buffer: StreamPeerBuffer, input: PackedByteArray) -> void: | |
| buffer.put_32(input.size()) | |
| var err := buffer.put_data(input) | |
| if err != OK: | |
| push_error("Error saving data. Error: %s" % error_string(err)) | |
| static func load_byte_array(buffer: StreamPeerBuffer) -> PackedByteArray: | |
| var size := mini(buffer.get_32(), buffer.get_available_bytes()) | |
| var position := buffer.get_position() | |
| if position >= buffer.data_array.size(): | |
| return PackedByteArray() | |
| var output := buffer.data_array.slice(position, position + size) | |
| buffer.seek(position + size) | |
| return output | |
| static func skip_byte_array(buffer: StreamPeerBuffer) -> void: | |
| var size := mini(buffer.get_32(), buffer.get_available_bytes()) | |
| skip_byte_count(buffer, size) | |
| #endregion | |
| #region PACKED_INT32_ARRAY | |
| static func save_int32_array(buffer: StreamPeerBuffer, input: PackedInt32Array) -> void: | |
| buffer.put_32(input.size() * _BITS32) | |
| for input_entry in input: | |
| buffer.put_32(input_entry) | |
| static func load_int32_array(buffer: StreamPeerBuffer) -> PackedInt32Array: | |
| var sub_buffer := StreamPeerBuffer.new() | |
| sub_buffer.data_array = load_byte_array(buffer) | |
| var output := PackedInt32Array() | |
| @warning_ignore("integer_division") | |
| var err := output.resize(sub_buffer.data_array.size() / _BITS32) | |
| if err != OK: | |
| push_error("Error loading data. Error: %s" % error_string(err)) | |
| for index in output.size(): | |
| output[index] = sub_buffer.get_32() | |
| return output | |
| static func skip_int32_array(buffer: StreamPeerBuffer) -> void: | |
| skip_byte_array(buffer) | |
| #endregion | |
| #region PACKED_INT64_ARRAY | |
| static func save_int64_array(buffer: StreamPeerBuffer, input: PackedInt64Array) -> void: | |
| buffer.put_32(input.size() * _BITS64) | |
| for input_entry in input: | |
| buffer.put_64(input_entry) | |
| static func load_int64_array(buffer: StreamPeerBuffer) -> PackedInt64Array: | |
| var sub_buffer := StreamPeerBuffer.new() | |
| sub_buffer.data_array = load_byte_array(buffer) | |
| var output := PackedInt64Array() | |
| @warning_ignore("integer_division") | |
| var err := output.resize(sub_buffer.data_array.size() / _BITS64) | |
| if err != OK: | |
| push_error("Error loading data. Error: %s" % error_string(err)) | |
| for index in output.size(): | |
| output[index] = sub_buffer.get_64() | |
| return output | |
| static func skip_int64_array(buffer: StreamPeerBuffer) -> void: | |
| skip_byte_array(buffer) | |
| #endregion | |
| #region PACKED_FLOAT32_ARRAY | |
| static func save_float32_array(buffer: StreamPeerBuffer, input: PackedFloat32Array) -> void: | |
| buffer.put_32(input.size() * _BITS32) | |
| for input_entry in input: | |
| buffer.put_float(input_entry) | |
| static func load_float32_array(buffer: StreamPeerBuffer) -> PackedFloat32Array: | |
| var sub_buffer := StreamPeerBuffer.new() | |
| sub_buffer.data_array = load_byte_array(buffer) | |
| var output := PackedFloat32Array() | |
| @warning_ignore("integer_division") | |
| var err := output.resize(sub_buffer.data_array.size() / _BITS32) | |
| if err != OK: | |
| push_error("Error loading data. Error: %s" % error_string(err)) | |
| for index in output.size(): | |
| output[index] = sub_buffer.get_float() | |
| return output | |
| static func skip_float32_array(buffer: StreamPeerBuffer) -> void: | |
| skip_byte_array(buffer) | |
| #endregion | |
| #region PACKED_FLOAT64_ARRAY | |
| static func save_float64_array(buffer: StreamPeerBuffer, input: PackedFloat64Array) -> void: | |
| buffer.put_32(input.size() * _BITS64) | |
| for input_entry in input: | |
| buffer.put_double(input_entry) | |
| static func load_float64_array(buffer: StreamPeerBuffer) -> PackedFloat64Array: | |
| var sub_buffer := StreamPeerBuffer.new() | |
| sub_buffer.data_array = load_byte_array(buffer) | |
| var output := PackedFloat64Array() | |
| @warning_ignore("integer_division") | |
| var err := output.resize(sub_buffer.data_array.size() / _BITS64) | |
| if err != OK: | |
| push_error("Error loading data. Error: %s" % error_string(err)) | |
| for index in output.size(): | |
| output[index] = sub_buffer.get_double() | |
| return output | |
| static func skip_float64_array(buffer: StreamPeerBuffer) -> void: | |
| skip_byte_array(buffer) | |
| #endregion | |
| #region PACKED_STRING_ARRAY | |
| static func save_string_array(buffer: StreamPeerBuffer, input: PackedStringArray) -> void: | |
| var sub_buffer := StreamPeerBuffer.new() | |
| for input_entry in input: | |
| save_string(sub_buffer, str(input_entry)) | |
| save_byte_array(buffer, sub_buffer.data_array) | |
| static func load_string_array(buffer: StreamPeerBuffer) -> PackedStringArray: | |
| var sub_buffer := StreamPeerBuffer.new() | |
| sub_buffer.data_array = load_byte_array(buffer) | |
| var output := PackedStringArray() | |
| while sub_buffer.get_available_bytes() > 0: | |
| if output.append(load_string(sub_buffer)): | |
| push_error("Error loading data. Error: %s" % error_string(ERR_OUT_OF_MEMORY)) | |
| return output | |
| static func skip_string_array(buffer: StreamPeerBuffer) -> void: | |
| skip_byte_array(buffer) | |
| #endregion | |
| #region PACKED_VECTOR2_ARRAY | |
| static func save_vector2_array(buffer: StreamPeerBuffer, input: PackedVector2Array) -> void: | |
| buffer.put_32(input.size() * _TYPE_VECTOR2_SIZE) | |
| for input_entry in input: | |
| save_vector2(buffer, input_entry) | |
| static func load_vector2_array(buffer: StreamPeerBuffer) -> PackedVector2Array: | |
| var sub_buffer := StreamPeerBuffer.new() | |
| sub_buffer.data_array = load_byte_array(buffer) | |
| var output := PackedVector2Array() | |
| @warning_ignore("integer_division") | |
| var err := output.resize(sub_buffer.data_array.size() / _TYPE_VECTOR2_SIZE) | |
| if err != OK: | |
| push_error("Error loading data. Error: %s" % error_string(err)) | |
| for index in output.size(): | |
| output[index] = load_vector2(sub_buffer) | |
| return output | |
| static func skip_vector2_array(buffer: StreamPeerBuffer) -> void: | |
| skip_byte_array(buffer) | |
| #endregion | |
| #region PACKED_VECTOR3_ARRAY | |
| static func save_vector3_array(buffer: StreamPeerBuffer, input: PackedVector3Array) -> void: | |
| buffer.put_32(input.size() * _TYPE_VECTOR3_SIZE) | |
| for input_entry in input: | |
| save_vector3(buffer, input_entry) | |
| static func load_vector3_array(buffer: StreamPeerBuffer) -> PackedVector3Array: | |
| var sub_buffer := StreamPeerBuffer.new() | |
| sub_buffer.data_array = load_byte_array(buffer) | |
| var output := PackedVector3Array() | |
| @warning_ignore("integer_division") | |
| var err := output.resize(sub_buffer.data_array.size() / _TYPE_VECTOR3_SIZE) | |
| if err != OK: | |
| push_error("Error loading data. Error: %s" % error_string(err)) | |
| for index in output.size(): | |
| output[index] = load_vector3(sub_buffer) | |
| return output | |
| static func skip_vector3_array(buffer: StreamPeerBuffer) -> void: | |
| skip_byte_array(buffer) | |
| #endregion | |
| #region PACKED_COLOR_ARRAY | |
| static func save_color_array(buffer: StreamPeerBuffer, input: PackedColorArray) -> void: | |
| buffer.put_32(input.size() * _TYPE_COLOR_SIZE) | |
| for input_entry in input: | |
| save_color(buffer, input_entry) | |
| static func load_color_array(buffer: StreamPeerBuffer) -> PackedColorArray: | |
| var sub_buffer := StreamPeerBuffer.new() | |
| sub_buffer.data_array = load_byte_array(buffer) | |
| var output := PackedColorArray() | |
| @warning_ignore("integer_division") | |
| var err := output.resize(sub_buffer.data_array.size() / _TYPE_COLOR_SIZE) | |
| if err != OK: | |
| push_error("Error loading data. Error: %s" % error_string(err)) | |
| for index in output.size(): | |
| output[index] = load_color(sub_buffer) | |
| return output | |
| static func skip_color_array(buffer: StreamPeerBuffer) -> void: | |
| skip_byte_array(buffer) | |
| #endregion | |
| #region PACKED_VECTOR4_ARRAY | |
| static func save_vector4_array(buffer: StreamPeerBuffer, input: PackedVector4Array) -> void: | |
| buffer.put_32(input.size() * _TYPE_VECTOR4_SIZE) | |
| for input_entry in input: | |
| save_vector4(buffer, input_entry) | |
| static func load_vector4_array(buffer: StreamPeerBuffer) -> PackedVector4Array: | |
| var sub_buffer := StreamPeerBuffer.new() | |
| sub_buffer.data_array = load_byte_array(buffer) | |
| var output := PackedVector4Array() | |
| @warning_ignore("integer_division") | |
| var err := output.resize(sub_buffer.data_array.size() / _TYPE_VECTOR4_SIZE) | |
| if err != OK: | |
| push_error("Error loading data. Error: %s" % error_string(err)) | |
| for index in output.size(): | |
| output[index] = load_vector4(buffer) | |
| return output | |
| static func skip_vector4_array(buffer: StreamPeerBuffer) -> void: | |
| skip_byte_array(buffer) | |
| #endregion | |
| #region VARIANT | |
| static func save_variant( | |
| buffer: StreamPeerBuffer, input: Variant, saved_objects: Dictionary[int, Object] | |
| ) -> void: | |
| save_value(buffer, input, TYPE_NIL, saved_objects) | |
| static func load_variant( | |
| buffer: StreamPeerBuffer, loaded_objects: Dictionary[int, Object] | |
| ) -> Variant: | |
| return load_value(buffer, TYPE_NIL, loaded_objects) | |
| static func skip_variant(buffer: StreamPeerBuffer, loaded_objects: Dictionary[int, Object]) -> void: | |
| @warning_ignore("return_value_discarded") | |
| load_variant(buffer, loaded_objects) | |
| #endregion | |
| static func save_simple_value( | |
| buffer: StreamPeerBuffer, input: Variant, known_type: Variant.Type | |
| ) -> void: | |
| var type := typeof(input) | |
| if known_type == TYPE_NIL: | |
| buffer.put_8(type) | |
| if known_type == TYPE_OBJECT and type == TYPE_NIL: | |
| type = TYPE_OBJECT | |
| elif type != known_type: | |
| push_error( | |
| ( | |
| "BinarySerializer.save_value(): Type mismatch. Expected: %s, Got: %s" | |
| % [str(known_type), str(type)] | |
| ) | |
| ) | |
| match type: | |
| TYPE_NIL: | |
| push_error("BinarySerializer.save_value(): TYPE_NIL is not supported.") | |
| TYPE_BOOL: | |
| var value: bool = input | |
| save_bool(buffer, value) | |
| TYPE_INT: | |
| var value: int = input | |
| save_int(buffer, value) | |
| TYPE_FLOAT: | |
| var value: float = input | |
| save_float(buffer, value) | |
| TYPE_STRING: | |
| var value: String = input | |
| save_string(buffer, value) | |
| TYPE_VECTOR2: | |
| var value: Vector2 = input | |
| save_vector2(buffer, value) | |
| TYPE_VECTOR2I: | |
| var value: Vector2i = input | |
| save_vector2i(buffer, value) | |
| TYPE_RECT2: | |
| var value: Rect2 = input | |
| save_rect2(buffer, value) | |
| TYPE_RECT2I: | |
| var value: Rect2i = input | |
| save_rect2i(buffer, value) | |
| TYPE_VECTOR3: | |
| var value: Vector3 = input | |
| save_vector3(buffer, value) | |
| TYPE_VECTOR3I: | |
| var value: Vector3i = input | |
| save_vector3i(buffer, value) | |
| TYPE_TRANSFORM2D: | |
| var value: Transform2D = input | |
| save_transform2d(buffer, value) | |
| TYPE_VECTOR4: | |
| var value: Vector4 = input | |
| save_vector4(buffer, value) | |
| TYPE_VECTOR4I: | |
| var value: Vector4i = input | |
| save_vector4i(buffer, value) | |
| TYPE_PLANE: | |
| var value: Plane = input | |
| save_plane(buffer, value) | |
| TYPE_QUATERNION: | |
| var value: Quaternion = input | |
| save_quaternion(buffer, value) | |
| TYPE_AABB: | |
| var value: AABB = input | |
| save_aabb(buffer, value) | |
| TYPE_BASIS: | |
| var value: Basis = input | |
| save_basis(buffer, value) | |
| TYPE_TRANSFORM3D: | |
| var value: Transform3D = input | |
| save_transform3d(buffer, value) | |
| TYPE_PROJECTION: | |
| var value: Projection = input | |
| save_projection(buffer, value) | |
| TYPE_COLOR: | |
| var value: Color = input | |
| save_color(buffer, value) | |
| TYPE_STRING_NAME: | |
| var value: StringName = input | |
| save_string_name(buffer, value) | |
| TYPE_NODE_PATH: | |
| var value: NodePath = input | |
| save_node_path(buffer, value) | |
| TYPE_RID: | |
| push_error("BinarySerializer.save_value(): TYPE_RID is not supported.") | |
| TYPE_PACKED_BYTE_ARRAY: | |
| var value: PackedByteArray = input | |
| save_byte_array(buffer, value) | |
| TYPE_PACKED_INT32_ARRAY: | |
| var value: PackedInt32Array = input | |
| save_int32_array(buffer, value) | |
| TYPE_PACKED_INT64_ARRAY: | |
| var value: PackedInt64Array = input | |
| save_int64_array(buffer, value) | |
| TYPE_PACKED_FLOAT32_ARRAY: | |
| var value: PackedFloat32Array = input | |
| save_float32_array(buffer, value) | |
| TYPE_PACKED_FLOAT64_ARRAY: | |
| var value: PackedFloat64Array = input | |
| save_float64_array(buffer, value) | |
| TYPE_PACKED_STRING_ARRAY: | |
| var value: PackedStringArray = input | |
| save_string_array(buffer, value) | |
| TYPE_PACKED_VECTOR2_ARRAY: | |
| var value: PackedVector2Array = input | |
| save_vector2_array(buffer, value) | |
| TYPE_PACKED_VECTOR3_ARRAY: | |
| var value: PackedVector3Array = input | |
| save_vector3_array(buffer, value) | |
| TYPE_PACKED_COLOR_ARRAY: | |
| var value: PackedColorArray = input | |
| save_color_array(buffer, value) | |
| TYPE_PACKED_VECTOR4_ARRAY: | |
| var value: PackedVector4Array = input | |
| save_vector4_array(buffer, value) | |
| _: | |
| push_error("BinarySerializer.save_value(): Unsupported type: %s" % str(type)) | |
| static func load_simple_value(buffer: StreamPeerBuffer, known_type: Variant.Type) -> Variant: | |
| var type := TYPE_NIL | |
| if known_type == TYPE_NIL: | |
| type = buffer.get_8() as Variant.Type | |
| else: | |
| type = known_type | |
| match type: | |
| TYPE_NIL: | |
| push_error("BinarySerializer.load_value(): TYPE_NIL is not supported.") | |
| return null | |
| TYPE_BOOL: | |
| return load_bool(buffer) | |
| TYPE_INT: | |
| return load_int(buffer) | |
| TYPE_FLOAT: | |
| return load_float(buffer) | |
| TYPE_STRING: | |
| return load_string(buffer) | |
| TYPE_VECTOR2: | |
| return load_vector2(buffer) | |
| TYPE_VECTOR2I: | |
| return load_vector2i(buffer) | |
| TYPE_RECT2: | |
| return load_rect2(buffer) | |
| TYPE_RECT2I: | |
| return load_rect2i(buffer) | |
| TYPE_VECTOR3: | |
| return load_vector3(buffer) | |
| TYPE_VECTOR3I: | |
| return load_vector3i(buffer) | |
| TYPE_TRANSFORM2D: | |
| return load_transform2d(buffer) | |
| TYPE_VECTOR4: | |
| return load_vector4(buffer) | |
| TYPE_VECTOR4I: | |
| return load_vector4i(buffer) | |
| TYPE_PLANE: | |
| return load_plane(buffer) | |
| TYPE_QUATERNION: | |
| return load_quaternion(buffer) | |
| TYPE_AABB: | |
| return load_aabb(buffer) | |
| TYPE_BASIS: | |
| return load_basis(buffer) | |
| TYPE_TRANSFORM3D: | |
| return load_transform3d(buffer) | |
| TYPE_PROJECTION: | |
| return load_projection(buffer) | |
| TYPE_COLOR: | |
| return load_color(buffer) | |
| TYPE_STRING_NAME: | |
| return load_string_name(buffer) | |
| TYPE_NODE_PATH: | |
| return load_node_path(buffer) | |
| TYPE_RID: | |
| push_error("BinarySerializer.load_value(): TYPE_RID is not supported.") | |
| return RID() | |
| TYPE_PACKED_BYTE_ARRAY: | |
| return load_byte_array(buffer) | |
| TYPE_PACKED_INT32_ARRAY: | |
| return load_int32_array(buffer) | |
| TYPE_PACKED_INT64_ARRAY: | |
| return load_int64_array(buffer) | |
| TYPE_PACKED_FLOAT32_ARRAY: | |
| return load_float32_array(buffer) | |
| TYPE_PACKED_FLOAT64_ARRAY: | |
| return load_float64_array(buffer) | |
| TYPE_PACKED_STRING_ARRAY: | |
| return load_string_array(buffer) | |
| TYPE_PACKED_VECTOR2_ARRAY: | |
| return load_vector2_array(buffer) | |
| TYPE_PACKED_VECTOR3_ARRAY: | |
| return load_vector3_array(buffer) | |
| TYPE_PACKED_COLOR_ARRAY: | |
| return load_color_array(buffer) | |
| TYPE_PACKED_VECTOR4_ARRAY: | |
| return load_vector4_array(buffer) | |
| _: | |
| push_error("BinarySerializer.load_value(): Unsupported type: %s" % type) | |
| return null | |
| static func skip_simple_value(buffer: StreamPeerBuffer, known_type: Variant.Type) -> void: | |
| var type := TYPE_NIL | |
| if known_type == TYPE_NIL: | |
| type = buffer.get_8() as Variant.Type | |
| else: | |
| type = known_type | |
| match type: | |
| TYPE_NIL: | |
| return | |
| TYPE_BOOL: | |
| skip_bool(buffer) | |
| TYPE_INT: | |
| skip_int(buffer) | |
| TYPE_FLOAT: | |
| skip_float(buffer) | |
| TYPE_STRING: | |
| skip_string(buffer) | |
| TYPE_VECTOR2: | |
| skip_vector2(buffer) | |
| TYPE_VECTOR2I: | |
| skip_vector2i(buffer) | |
| TYPE_RECT2: | |
| skip_rect2(buffer) | |
| TYPE_RECT2I: | |
| skip_rect2i(buffer) | |
| TYPE_VECTOR3: | |
| skip_vector3(buffer) | |
| TYPE_VECTOR3I: | |
| skip_vector3i(buffer) | |
| TYPE_TRANSFORM2D: | |
| skip_transform2d(buffer) | |
| TYPE_VECTOR4: | |
| skip_vector4(buffer) | |
| TYPE_VECTOR4I: | |
| skip_vector4i(buffer) | |
| TYPE_PLANE: | |
| skip_plane(buffer) | |
| TYPE_QUATERNION: | |
| skip_quaternion(buffer) | |
| TYPE_AABB: | |
| skip_aabb(buffer) | |
| TYPE_BASIS: | |
| skip_basis(buffer) | |
| TYPE_TRANSFORM3D: | |
| skip_transform3d(buffer) | |
| TYPE_PROJECTION: | |
| skip_projection(buffer) | |
| TYPE_COLOR: | |
| skip_color(buffer) | |
| TYPE_STRING_NAME: | |
| skip_string_name(buffer) | |
| TYPE_NODE_PATH: | |
| skip_node_path(buffer) | |
| TYPE_RID: | |
| return | |
| TYPE_PACKED_BYTE_ARRAY: | |
| skip_byte_array(buffer) | |
| TYPE_PACKED_INT32_ARRAY: | |
| skip_int32_array(buffer) | |
| TYPE_PACKED_INT64_ARRAY: | |
| skip_int64_array(buffer) | |
| TYPE_PACKED_FLOAT32_ARRAY: | |
| skip_float32_array(buffer) | |
| TYPE_PACKED_FLOAT64_ARRAY: | |
| skip_float64_array(buffer) | |
| TYPE_PACKED_STRING_ARRAY: | |
| skip_string_array(buffer) | |
| TYPE_PACKED_VECTOR2_ARRAY: | |
| skip_vector2_array(buffer) | |
| TYPE_PACKED_VECTOR3_ARRAY: | |
| skip_vector3_array(buffer) | |
| TYPE_PACKED_COLOR_ARRAY: | |
| skip_color_array(buffer) | |
| TYPE_PACKED_VECTOR4_ARRAY: | |
| skip_vector4_array(buffer) | |
| _: | |
| return | |
| static func save_value( | |
| buffer: StreamPeerBuffer, | |
| input: Variant, | |
| known_type: Variant.Type, | |
| saved_objects: Dictionary[int, Object] | |
| ) -> void: | |
| var type := typeof(input) | |
| if known_type == TYPE_NIL: | |
| buffer.put_8(type) | |
| elif known_type == TYPE_OBJECT and type == TYPE_NIL: | |
| type = TYPE_OBJECT | |
| elif type != known_type: | |
| push_error( | |
| ( | |
| "BinarySerializer.save_value(): Type mismatch. Expected: %s, got: %s" | |
| % [str(known_type), str(type)] | |
| ) | |
| ) | |
| match type: | |
| TYPE_NIL: | |
| push_error("BinarySerializer.save_value(): TYPE_NIL is not supported.") | |
| TYPE_OBJECT: | |
| var value: Object = null | |
| if is_instance_valid(input): | |
| value = input | |
| save_object(buffer, value, saved_objects) | |
| TYPE_CALLABLE: | |
| var value: Callable = input | |
| save_callable(buffer, value, saved_objects) | |
| TYPE_SIGNAL: | |
| var value: Signal = input | |
| save_signal(buffer, value, saved_objects) | |
| TYPE_DICTIONARY: | |
| var value: Dictionary = input | |
| save_dictionary(buffer, value, saved_objects) | |
| TYPE_ARRAY: | |
| var value: Array = input | |
| save_array(buffer, value, saved_objects) | |
| _: | |
| save_simple_value(buffer, input, type) | |
| static func load_value( | |
| buffer: StreamPeerBuffer, known_type: Variant.Type, loaded_objects: Dictionary[int, Object] | |
| ) -> Variant: | |
| var type := TYPE_NIL | |
| if known_type == TYPE_NIL: | |
| type = buffer.get_8() as Variant.Type | |
| else: | |
| type = known_type | |
| match type: | |
| TYPE_NIL: | |
| push_error("BinarySerializer.load_value(): TYPE_NIL is not supported.") | |
| return null | |
| TYPE_OBJECT: | |
| return load_object(buffer, loaded_objects) | |
| TYPE_CALLABLE: | |
| return load_callable(buffer, loaded_objects) | |
| TYPE_SIGNAL: | |
| return load_signal(buffer, loaded_objects) | |
| TYPE_DICTIONARY: | |
| return load_dictionary(buffer, loaded_objects) | |
| TYPE_ARRAY: | |
| return load_array(buffer, loaded_objects) | |
| _: | |
| return load_simple_value(buffer, type) | |
| static func skip_value( | |
| buffer: StreamPeerBuffer, known_type: Variant.Type, loaded_objects: Dictionary[int, Object] | |
| ) -> void: | |
| var type := TYPE_NIL | |
| if known_type == TYPE_NIL: | |
| type = buffer.get_8() as Variant.Type | |
| else: | |
| type = known_type | |
| match type: | |
| TYPE_NIL: | |
| return | |
| TYPE_OBJECT: | |
| skip_object(buffer, loaded_objects) | |
| TYPE_CALLABLE: | |
| skip_callable(buffer, loaded_objects) | |
| TYPE_SIGNAL: | |
| skip_signal(buffer, loaded_objects) | |
| TYPE_DICTIONARY: | |
| skip_dictionary(buffer, loaded_objects) | |
| TYPE_ARRAY: | |
| skip_array(buffer, loaded_objects) | |
| _: | |
| skip_simple_value(buffer, type) |
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
| @tool | |
| class_name ScriptHelper # RefCounted (Static) | |
| ## Utility functions for scripts, including inheritance checks, and global name resolution. | |
| static var _script_path_by_class: Dictionary[StringName, StringName] = {} | |
| static var _root_scripts: Array[StringName] = [] | |
| static func _static_init() -> void: | |
| _rebuild_cache() | |
| static func _rebuild_cache() -> void: | |
| _script_path_by_class.clear() | |
| _root_scripts.clear() | |
| for global_class in ProjectSettings.get_global_class_list(): | |
| var found_class: String = global_class["class"] | |
| var found_path: String = global_class["path"] | |
| assert(not found_class.is_empty()) | |
| assert(not found_path.is_empty()) | |
| _script_path_by_class[found_class] = found_path | |
| if found_class.ends_with("Root"): | |
| _root_scripts.append(found_class) | |
| static func _get_script_by_path(script_path: String) -> Script: | |
| assert(not script_path.is_empty()) | |
| assert(ResourceLoader.exists(script_path, "Script")) | |
| var script: Script = ResourceLoader.load(script_path, "Script") as Script | |
| assert(script != null, "Failed to load script") | |
| return script | |
| static func _get_script_by_class(not_build_in_class_string: StringName) -> Script: | |
| assert(not not_build_in_class_string.is_empty()) | |
| var script_path := "" | |
| if is_resource_path(not_build_in_class_string): | |
| script_path = not_build_in_class_string | |
| else: | |
| script_path = _script_path_by_class.get(not_build_in_class_string, &"") | |
| if ( | |
| Engine.is_editor_hint() | |
| and ( | |
| script_path.is_empty() | |
| or not ResourceLoader.exists(script_path, "Script") | |
| ) | |
| ): | |
| _rebuild_cache() | |
| script_path = _script_path_by_class.get(not_build_in_class_string, "") | |
| if script_path.is_empty() or not ResourceLoader.exists(script_path, "Script"): | |
| push_error("Not found script for class %s" % not_build_in_class_string) | |
| return null | |
| if script_path.is_empty() or not ResourceLoader.exists(script_path, "Script"): | |
| return null | |
| return _get_script_by_path(script_path) | |
| static func is_resource_path(string: String) -> bool: | |
| if string.length() <= 6: | |
| return false | |
| var check_a := string.substr(3, 3) | |
| if check_a != "://": | |
| return false | |
| var check_b := string.substr(0, 3) | |
| if check_b not in ["res", "uid"]: | |
| return false | |
| return true | |
| static func get_script_by_path(script_path: String) -> Script: | |
| if script_path.is_empty() or not ResourceLoader.exists(script_path, "Script"): | |
| return null | |
| return _get_script_by_path(script_path) | |
| static func get_script_by_class_name(name_of_class: String) -> Script: | |
| assert(not name_of_class.is_empty()) | |
| var script_path: String = _script_path_by_class.get(name_of_class, "") | |
| if ( | |
| Engine.is_editor_hint() | |
| and ( | |
| script_path.is_empty() | |
| or not ResourceLoader.exists(script_path, "Script") | |
| ) | |
| ): | |
| _rebuild_cache() | |
| script_path = _script_path_by_class.get(name_of_class, "") | |
| if script_path.is_empty() or not ResourceLoader.exists(script_path, "Script"): | |
| push_error("Not found script for class %s" % name_of_class) | |
| return null | |
| if script_path.is_empty() or not ResourceLoader.exists(script_path, "Script"): | |
| return null | |
| return _get_script_by_path(script_path) | |
| static func get_script_by_class(class_string: StringName) -> Script: | |
| assert(not class_string.is_empty()) | |
| assert(not ClassDB.class_exists(class_string)) | |
| return _get_script_by_class(class_string) | |
| static func get_script_by_class_or_empty(class_string: StringName) -> Script: | |
| if class_string.is_empty() or ClassDB.class_exists(class_string): | |
| return null | |
| return _get_script_by_class(class_string) | |
| static func get_class_by_script(script: Script) -> String: | |
| assert(script != null) | |
| var global_name := script.get_global_name() | |
| if not global_name.is_empty(): | |
| return global_name | |
| return script.resource_path | |
| static func get_class_by_script_or_empty(script: Script) -> String: | |
| if script == null: | |
| return "" | |
| var global_name := script.get_global_name() | |
| if not global_name.is_empty(): | |
| return global_name | |
| return script.resource_path | |
| static func is_node_class(class_string: StringName) -> bool: | |
| assert(not class_string.is_empty()) | |
| if ClassDB.class_exists(class_string): | |
| return class_string == &"Node" or ClassDB.is_parent_class(class_string, &"Node") | |
| var class_script := _get_script_by_class(class_string) | |
| if class_script == null: | |
| return false | |
| var found_class_string: StringName = class_script.get_instance_base_type() | |
| return found_class_string == &"Node" or ClassDB.is_parent_class(found_class_string, &"Node") | |
| static func is_class_derived(class_string: StringName, base_class_string: StringName) -> bool: | |
| assert(not class_string.is_empty()) | |
| assert(not base_class_string.is_empty()) | |
| if ClassDB.class_exists(class_string): | |
| if ClassDB.class_exists(base_class_string): | |
| return ( | |
| class_string == base_class_string | |
| or ClassDB.is_parent_class(class_string, base_class_string) | |
| ) | |
| return false | |
| var class_script := _get_script_by_class(class_string) | |
| if class_script == null: | |
| return false | |
| if ClassDB.class_exists(base_class_string): | |
| var found_class_string: StringName = class_script.get_instance_base_type() | |
| return ( | |
| found_class_string == base_class_string | |
| or ClassDB.is_parent_class(found_class_string, base_class_string) | |
| ) | |
| return is_script_derived(class_script, _get_script_by_class(base_class_string)) | |
| static func is_script_derived(script: Script, base_script: Script) -> bool: | |
| assert(base_script != null) | |
| if script == null: | |
| return false | |
| while script != null: | |
| if script == base_script: | |
| return true | |
| script = script.get_base_script() | |
| return false | |
| static func get_all_script_classes() -> Array[StringName]: | |
| return _script_path_by_class.keys() | |
| static func get_root_script_classes() -> Array[StringName]: | |
| return _root_scripts | |
| static func get_static_method_callable( | |
| name_of_class: StringName, method_name: StringName | |
| ) -> Callable: | |
| if name_of_class.is_empty(): | |
| return Callable() | |
| if method_name.is_empty(): | |
| return Callable() | |
| var script := _get_script_by_class(name_of_class) | |
| if script == null: | |
| return Callable() | |
| if not script.has_method(method_name): | |
| return Callable() | |
| return Callable(script, method_name) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment