├── logo.png ├── README.md └── addons └── Seriously └── Seriously.gd /logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/freehuntx/godot-seriously/HEAD/logo.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Seriously - A de-/serializer for godot 2 | There are some things i didnt like about the godot serialization api. So i came up with an own one. 3 | While its not faster than the godot serialization api, it produces smaller byte arrays thus uses less traffic. 4 | Plus you dont have the security issue of functions executing when you want to serialize objects. 5 | 6 | **Tested on Godot 4.2** 7 | 8 | ## Installation 9 | - Copy addons folder into your godot project 10 | 11 | ## API 12 | 13 | - Seriously 14 | - pack_to_bytes (value): PoolByteArray 15 | - unpack_from_bytes (bytes: PackedByteArray) 16 | - pack (value): StreamPeerBuffer 17 | - unpack (stream: StreamPeerBuffer) 18 | 19 | ## Example 20 | 21 | ### Serialization 22 | 23 | ``` 24 | var bytes := Seriously.pack_to_bytes([ "example", true, false, 1, 1.3, {}, Object.new() ]) 25 | ``` 26 | 27 | ### Deserialization 28 | 29 | ``` 30 | var bytes := Seriously.pack_to_bytes([ "example", true, false, 1, 1.3, {}, Object.new() ]) 31 | var data = Seriously.unpack_from_bytes(bytes) 32 | ``` 33 | ## Test 34 |
35 | Test code 36 | 37 | ``` 38 | func _init(): 39 | var test_data = { 40 | "some_key": { 41 | "some_other_key": [ 42 | 1, 43 | false, 44 | 0.1, 45 | Color.RED, 46 | Vector4.ZERO, 47 | Plane(10, 10, 10, 10) 48 | ], 49 | "yet_another_key": SceneMultiplayer.new() 50 | } 51 | } 52 | 53 | # Godot serialization api 54 | var start_time = Time.get_ticks_usec() 55 | print("# Godot serialization api") 56 | var bytes = var_to_bytes(test_data) 57 | print() 58 | print("## Bytes (%s)" % [bytes.size()]) 59 | print(bytes) 60 | print() 61 | print("## Parsed") 62 | print(bytes_to_var(bytes)) 63 | print() 64 | print("## Result") 65 | print(float(Time.get_ticks_usec() - start_time) / 1000.0, "m/s") 66 | print("--------------------") 67 | # Seriously api 68 | start_time = Time.get_ticks_usec() 69 | print("# Seriously api") 70 | bytes = Seriously.pack_to_bytes(test_data) 71 | print() 72 | print("## Bytes (%s)" % [bytes.size()]) 73 | print(bytes) 74 | print() 75 | print("## Parsed") 76 | print(Seriously.unpack_from_bytes(bytes)) 77 | print() 78 | print("## Result") 79 | print(float(Time.get_ticks_usec() - start_time) / 1000.0, "m/s") 80 | ``` 81 |
82 |
83 |
84 | Result 85 | 86 | ``` 87 | # Godot serialization api 88 | 89 | ## Bytes (188) 90 | [27, 0, 0, 0, 1, 0, 0, 0, 4, 0, 0, 0, 8, 0, 0, 0, 115, 111, 109, 101, 95, 107, 101, 121, 27, 0, 0, 0, 2, 0, 0, 0, 4, 0, 0, 0, 14, 0, 0, 0, 115, 111, 109, 101, 95, 111, 116, 104, 101, 114, 95, 107, 101, 121, 0, 0, 28, 0, 0, 0, 6, 0, 0, 0, 2, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 3, 0, 1, 0, 154, 153, 153, 153, 153, 153, 185, 63, 20, 0, 0, 0, 0, 0, 128, 63, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 63, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 0, 0, 0, 0, 0, 32, 65, 0, 0, 32, 65, 0, 0, 32, 65, 0, 0, 32, 65, 4, 0, 0, 0, 15, 0, 0, 0, 121, 101, 116, 95, 97, 110, 111, 116, 104, 101, 114, 95, 107, 101, 121, 0, 24, 0, 1, 0, 197, 4, 0, 144, 5, 0, 0, 128] 91 | 92 | ## Parsed 93 | { "some_key": { "some_other_key": [1, false, 0.1, (1, 0, 0, 1), (0, 0, 0, 0), [N: (10, 10, 10), D: 10]], "yet_another_key": } } 94 | 95 | ## Result 96 | 0.3m/s 97 | -------------------- 98 | # Seriously api 99 | 100 | ## Bytes (107) 101 | [27, 1, 0, 8, 0, 115, 111, 109, 101, 95, 107, 101, 121, 27, 2, 0, 14, 0, 115, 111, 109, 101, 95, 111, 116, 104, 101, 114, 95, 107, 101, 121, 28, 6, 0, 50, 1, 1, 0, 3, 154, 153, 153, 153, 153, 153, 185, 63, 20, 255, 0, 0, 255, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 0, 0, 32, 65, 0, 0, 32, 65, 0, 0, 32, 65, 0, 0, 32, 65, 15, 0, 121, 101, 116, 95, 97, 110, 111, 116, 104, 101, 114, 95, 107, 101, 121, 24, 0, 0] 102 | 103 | ## Parsed 104 | { "some_key": { "some_other_key": [1, false, 0.1, (1, 0, 0, 1), (0, 0, 0, 0), [N: (10, 10, 10), D: 10]], "yet_another_key": } } 105 | 106 | ## Result 107 | 0.973m/s 108 | ``` 109 |
-------------------------------------------------------------------------------- /addons/Seriously/Seriously.gd: -------------------------------------------------------------------------------- 1 | class_name Seriously extends RefCounted 2 | 3 | const OBJECT_AS_DICT := false # Should objects be serialized as dict? (more performance?) 4 | const INT_MAX = 9223372036854775807 5 | const INT_MIN = -9223372036854775808 6 | 7 | enum CustomTypes { 8 | # Ensure the values dont overlap with an existing TYPE_* 9 | UINT8 = 50, 10 | INT8 = 51, 11 | UINT16 = 52, 12 | INT16 = 53, 13 | UINT32 = 54, 14 | INT32 = 55, 15 | UINT64 = 56, 16 | INT64 = 57, 17 | TYPED_ARRAY = 58 18 | } 19 | 20 | static var _serializer = { 21 | TYPE_NIL: { 22 | "pack": func(_value, stream: StreamPeerBuffer) -> StreamPeerBuffer: 23 | return stream, 24 | "unpack": func(_stream: StreamPeerBuffer): 25 | return null, 26 | }, 27 | TYPE_BOOL: { 28 | "pack": func(value, stream: StreamPeerBuffer) -> StreamPeerBuffer: 29 | stream.put_u8(1 if value else 0) 30 | return stream, 31 | "unpack": func(stream: StreamPeerBuffer) -> bool: 32 | return stream.get_u8() > 0, 33 | }, 34 | TYPE_INT: { 35 | "pack": func(value: int, stream: StreamPeerBuffer) -> StreamPeerBuffer: 36 | stream.put_32(value) 37 | return stream, 38 | "unpack": func(stream: StreamPeerBuffer) -> int: 39 | return stream.get_32(), 40 | }, 41 | CustomTypes.UINT8: { 42 | "pack": func(value: int, stream: StreamPeerBuffer) -> StreamPeerBuffer: 43 | stream.put_u8(value) 44 | return stream, 45 | "unpack": func(stream: StreamPeerBuffer) -> int: 46 | return stream.get_u8(), 47 | }, 48 | CustomTypes.INT8: { 49 | "pack": func(value: int, stream: StreamPeerBuffer) -> StreamPeerBuffer: 50 | stream.put_8(value) 51 | return stream, 52 | "unpack": func(stream: StreamPeerBuffer) -> int: 53 | return stream.get_8(), 54 | }, 55 | CustomTypes.UINT16: { 56 | "pack": func(value: int, stream: StreamPeerBuffer) -> StreamPeerBuffer: 57 | stream.put_u16(value) 58 | return stream, 59 | "unpack": func(stream: StreamPeerBuffer) -> int: 60 | return stream.get_u16(), 61 | }, 62 | CustomTypes.INT16: { 63 | "pack": func(value: int, stream: StreamPeerBuffer) -> StreamPeerBuffer: 64 | stream.put_16(value) 65 | return stream, 66 | "unpack": func(stream: StreamPeerBuffer) -> int: 67 | return stream.get_16(), 68 | }, 69 | CustomTypes.UINT32: { 70 | "pack": func(value: int, stream: StreamPeerBuffer) -> StreamPeerBuffer: 71 | stream.put_u32(value) 72 | return stream, 73 | "unpack": func(stream: StreamPeerBuffer) -> int: 74 | return stream.get_u32(), 75 | }, 76 | CustomTypes.INT32: { 77 | "pack": func(value: int, stream: StreamPeerBuffer) -> StreamPeerBuffer: 78 | stream.put_32(value) 79 | return stream, 80 | "unpack": func(stream: StreamPeerBuffer) -> int: 81 | return stream.get_32(), 82 | }, 83 | CustomTypes.UINT64: { 84 | "pack": func(value: int, stream: StreamPeerBuffer) -> StreamPeerBuffer: 85 | stream.put_u64(value) 86 | return stream, 87 | "unpack": func(stream: StreamPeerBuffer) -> int: 88 | return stream.get_u64(), 89 | }, 90 | CustomTypes.INT64: { 91 | "pack": func(value: int, stream: StreamPeerBuffer) -> StreamPeerBuffer: 92 | stream.put_64(value) 93 | return stream, 94 | "unpack": func(stream: StreamPeerBuffer) -> int: 95 | return stream.get_64(), 96 | }, 97 | TYPE_FLOAT: { 98 | "pack": func(value: float, stream: StreamPeerBuffer) -> StreamPeerBuffer: 99 | stream.put_double(value) 100 | return stream, 101 | "unpack": func(stream: StreamPeerBuffer) -> float: 102 | return stream.get_double(), 103 | }, 104 | TYPE_STRING: { 105 | "pack": func(value: String, stream: StreamPeerBuffer) -> StreamPeerBuffer: 106 | stream.put_u16(value.length()) 107 | stream.put_data(value.to_utf8_buffer()) 108 | return stream, 109 | "unpack": func(stream: StreamPeerBuffer) -> String: 110 | return stream.get_utf8_string(stream.get_u16()), 111 | }, 112 | TYPE_VECTOR2: { 113 | "pack": func(value: Vector2, stream: StreamPeerBuffer) -> StreamPeerBuffer: 114 | stream.put_float(value.x) 115 | stream.put_float(value.y) 116 | return stream, 117 | "unpack": func(stream: StreamPeerBuffer) -> Vector2: 118 | return Vector2(stream.get_float(), stream.get_float()), 119 | }, 120 | TYPE_VECTOR2I: { 121 | "pack": func(value: Vector2i, stream: StreamPeerBuffer) -> StreamPeerBuffer: 122 | stream.put_32(value.x) 123 | stream.put_32(value.y) 124 | return stream, 125 | "unpack": func(stream: StreamPeerBuffer) -> Vector2i: 126 | return Vector2i(stream.get_32(), stream.get_32()), 127 | }, 128 | TYPE_RECT2: { 129 | "pack": func(value: Rect2, stream: StreamPeerBuffer) -> StreamPeerBuffer: 130 | stream.put_float(value.position.x) 131 | stream.put_float(value.position.y) 132 | stream.put_float(value.size.x) 133 | stream.put_float(value.size.y) 134 | return stream, 135 | "unpack": func(stream: StreamPeerBuffer) -> Rect2: 136 | return Rect2(stream.get_float(), stream.get_float(), stream.get_float(), stream.get_float()), 137 | }, 138 | TYPE_RECT2I: { 139 | "pack": func(value: Rect2i, stream: StreamPeerBuffer) -> StreamPeerBuffer: 140 | stream.put_32(value.position.x) 141 | stream.put_32(value.position.y) 142 | stream.put_32(value.size.x) 143 | stream.put_32(value.size.y) 144 | return stream, 145 | "unpack": func(stream: StreamPeerBuffer) -> Rect2i: 146 | return Rect2i(stream.get_32(), stream.get_32(), stream.get_32(), stream.get_32()), 147 | }, 148 | TYPE_VECTOR3: { 149 | "pack": func(value: Vector3, stream: StreamPeerBuffer) -> StreamPeerBuffer: 150 | stream.put_float(value.x) 151 | stream.put_float(value.y) 152 | stream.put_float(value.z) 153 | return stream, 154 | "unpack": func(stream: StreamPeerBuffer) -> Vector3: 155 | return Vector3(stream.get_float(), stream.get_float(), stream.get_float()), 156 | }, 157 | TYPE_VECTOR3I: { 158 | "pack": func(value: Vector3i, stream: StreamPeerBuffer) -> StreamPeerBuffer: 159 | stream.put_32(value.x) 160 | stream.put_32(value.y) 161 | stream.put_32(value.z) 162 | return stream, 163 | "unpack": func(stream: StreamPeerBuffer) -> Vector3i: 164 | return Vector3i(stream.get_32(), stream.get_32(), stream.get_32()), 165 | }, 166 | TYPE_TRANSFORM2D: { 167 | "pack": func(value: Transform2D, stream: StreamPeerBuffer) -> StreamPeerBuffer: 168 | pack(value.x, stream, TYPE_VECTOR2) 169 | pack(value.y, stream, TYPE_VECTOR2) 170 | pack(value.origin, stream, TYPE_VECTOR2) 171 | return stream, 172 | "unpack": func(stream: StreamPeerBuffer) -> Transform2D: 173 | return Transform2D(unpack(stream, TYPE_VECTOR2), unpack(stream, TYPE_VECTOR2), unpack(stream, TYPE_VECTOR2)), 174 | }, 175 | TYPE_VECTOR4: { 176 | "pack": func(value: Vector4, stream: StreamPeerBuffer) -> StreamPeerBuffer: 177 | stream.put_float(value.x) 178 | stream.put_float(value.y) 179 | stream.put_float(value.z) 180 | stream.put_float(value.w) 181 | return stream, 182 | "unpack": func(stream: StreamPeerBuffer) -> Vector4: 183 | return Vector4(stream.get_float(), stream.get_float(), stream.get_float(), stream.get_float()), 184 | }, 185 | TYPE_VECTOR4I: { 186 | "pack": func(value: Vector4i, stream: StreamPeerBuffer) -> StreamPeerBuffer: 187 | stream.put_32(value.x) 188 | stream.put_32(value.y) 189 | stream.put_32(value.z) 190 | stream.put_32(value.w) 191 | return stream, 192 | "unpack": func(stream: StreamPeerBuffer) -> Vector4i: 193 | return Vector4i(stream.get_32(), stream.get_32(), stream.get_32(), stream.get_32()), 194 | }, 195 | TYPE_PLANE: { 196 | "pack": func(value: Plane, stream: StreamPeerBuffer) -> StreamPeerBuffer: 197 | pack(value.normal, stream, TYPE_VECTOR3) 198 | stream.put_float(value.d) 199 | return stream, 200 | "unpack": func(stream: StreamPeerBuffer) -> Plane: 201 | return Plane(unpack(stream, TYPE_VECTOR3), stream.get_float()), 202 | }, 203 | TYPE_QUATERNION: { 204 | "pack": func(value: Quaternion, stream: StreamPeerBuffer) -> StreamPeerBuffer: 205 | stream.put_float(value.x) 206 | stream.put_float(value.y) 207 | stream.put_float(value.z) 208 | stream.put_float(value.w) 209 | return stream, 210 | "unpack": func(stream: StreamPeerBuffer) -> Quaternion: 211 | return Quaternion(stream.get_float(), stream.get_float(), stream.get_float(), stream.get_float()), 212 | }, 213 | TYPE_AABB: { 214 | "pack": func(value: AABB, stream: StreamPeerBuffer) -> StreamPeerBuffer: 215 | pack(value.position, stream, TYPE_VECTOR3) 216 | pack(value.size, stream, TYPE_VECTOR3) 217 | return stream, 218 | "unpack": func(stream: StreamPeerBuffer) -> AABB: 219 | return AABB(unpack(stream, TYPE_VECTOR3), unpack(stream, TYPE_VECTOR3)), 220 | }, 221 | TYPE_BASIS: { 222 | "pack": func(value: Basis, stream: StreamPeerBuffer) -> StreamPeerBuffer: 223 | pack(value.x, stream, TYPE_VECTOR3) 224 | pack(value.y, stream, TYPE_VECTOR3) 225 | pack(value.z, stream, TYPE_VECTOR3) 226 | return stream, 227 | "unpack": func(stream: StreamPeerBuffer) -> Basis: 228 | return Basis(unpack(stream, TYPE_VECTOR3), unpack(stream, TYPE_VECTOR3), unpack(stream, TYPE_VECTOR3)), 229 | }, 230 | TYPE_TRANSFORM3D: { 231 | "pack": func(value: Transform3D, stream: StreamPeerBuffer) -> StreamPeerBuffer: 232 | pack(value.basis, stream, TYPE_BASIS) 233 | pack(value.origin, stream, TYPE_VECTOR3) 234 | return stream, 235 | "unpack": func(stream: StreamPeerBuffer) -> Transform3D: 236 | return Transform3D(unpack(stream, TYPE_BASIS), unpack(stream, TYPE_VECTOR3)), 237 | }, 238 | TYPE_PROJECTION: { 239 | "pack": func(value: Projection, stream: StreamPeerBuffer) -> StreamPeerBuffer: 240 | pack(value.x, stream, TYPE_VECTOR4) 241 | pack(value.y, stream, TYPE_VECTOR4) 242 | pack(value.z, stream, TYPE_VECTOR4) 243 | pack(value.w, stream, TYPE_VECTOR4) 244 | return stream, 245 | "unpack": func(stream: StreamPeerBuffer) -> Projection: 246 | return Projection(unpack(stream, TYPE_VECTOR4), unpack(stream, TYPE_VECTOR4), unpack(stream, TYPE_VECTOR4), unpack(stream, TYPE_VECTOR4)), 247 | }, 248 | TYPE_COLOR: { 249 | "pack": func(value: Color, stream: StreamPeerBuffer) -> StreamPeerBuffer: 250 | stream.put_u32(value.to_rgba32()) 251 | return stream, 252 | "unpack": func(stream: StreamPeerBuffer) -> Color: 253 | return Color(stream.get_u32()), 254 | }, 255 | TYPE_STRING_NAME: { 256 | "pack": func(value: StringName, stream: StreamPeerBuffer) -> StreamPeerBuffer: 257 | pack(str(value), stream, TYPE_STRING) 258 | return stream, 259 | "unpack": func(stream: StreamPeerBuffer) -> StringName: 260 | return StringName(unpack(stream, TYPE_STRING)), 261 | }, 262 | TYPE_NODE_PATH: { 263 | "pack": func(value: NodePath, stream: StreamPeerBuffer) -> StreamPeerBuffer: 264 | pack(str(value), stream, TYPE_STRING) 265 | return stream, 266 | "unpack": func(stream: StreamPeerBuffer) -> NodePath: 267 | return NodePath(unpack(stream, TYPE_STRING)), 268 | }, 269 | TYPE_RID: { 270 | "pack": func(_value: RID, _stream: StreamPeerBuffer) -> StreamPeerBuffer: 271 | push_error("[Seriously] TYPE_RID is not supported") 272 | return null, 273 | "unpack": func(_stream: StreamPeerBuffer): 274 | push_error("[Seriously] TYPE_RID is not supported") 275 | return null, 276 | }, 277 | TYPE_OBJECT: { 278 | "pack": func(value: Object, stream: StreamPeerBuffer) -> StreamPeerBuffer: 279 | var prop_names = value.get_property_list().filter(func(p): return p.usage & PROPERTY_USAGE_SCRIPT_VARIABLE).map(func(p): return p.name) 280 | 281 | stream.put_u16(prop_names.size()) 282 | 283 | for name in prop_names: 284 | pack(name, stream, TYPE_STRING) 285 | pack(value.get(name), stream) 286 | 287 | return stream, 288 | "unpack": func(stream: StreamPeerBuffer): 289 | var object_size := stream.get_u16() 290 | var dict := {} 291 | 292 | for j in object_size: 293 | var name = unpack(stream, TYPE_STRING) 294 | dict[name] = unpack(stream) 295 | 296 | if OBJECT_AS_DICT: 297 | return dict 298 | 299 | # Create dynamic object (bad performance?) 300 | var source_code := "extends RefCounted\n" 301 | 302 | for name in dict.keys(): 303 | source_code += "var %s\n" % [name] 304 | 305 | var dynamic_object := GDScript.new() 306 | dynamic_object.source_code = source_code 307 | dynamic_object.reload() 308 | 309 | var object = dynamic_object.new() 310 | for name in dict.keys(): 311 | object.set(name, dict[name]) 312 | 313 | return object, 314 | }, 315 | TYPE_CALLABLE: { 316 | "pack": func(_value: Callable, _stream: StreamPeerBuffer) -> StreamPeerBuffer: 317 | push_error("[Seriously] TYPE_CALLABLE type pack requested. This is not possible!") 318 | return null, 319 | "unpack": func(_stream: StreamPeerBuffer): 320 | push_error("[Seriously] TYPE_CALLABLE type unpack requested. This is not possible!") 321 | return null, 322 | }, 323 | TYPE_SIGNAL: { 324 | "pack": func(_value: Signal, _stream: StreamPeerBuffer) -> StreamPeerBuffer: 325 | push_error("[Seriously] TYPE_SIGNAL type pack requested. This is not possible!") 326 | return null, 327 | "unpack": func(_stream: StreamPeerBuffer): 328 | push_error("[Seriously] TYPE_SIGNAL type unpack requested. This is not possible!") 329 | return null, 330 | }, 331 | TYPE_DICTIONARY: { 332 | "pack": func(value: Dictionary, stream: StreamPeerBuffer) -> StreamPeerBuffer: 333 | stream.put_u16(value.size()) 334 | 335 | for key in value.keys(): 336 | pack(key, stream, TYPE_STRING) 337 | pack(value[key], stream) 338 | 339 | return stream, 340 | "unpack": func(stream: StreamPeerBuffer) -> Dictionary: 341 | var dictionary_size = stream.get_u16() 342 | var dictionary = {} 343 | 344 | for j in dictionary_size: 345 | var name = unpack(stream, TYPE_STRING) 346 | dictionary[name] = unpack(stream) 347 | 348 | return dictionary, 349 | }, 350 | TYPE_ARRAY: { 351 | "pack": func(value: Array, stream: StreamPeerBuffer) -> StreamPeerBuffer: 352 | var array_size := value.size() 353 | 354 | stream.put_u16(array_size) 355 | 356 | for i in array_size: 357 | pack(value[i], stream) 358 | 359 | return stream, 360 | "unpack": func(stream: StreamPeerBuffer) -> Array: 361 | var array_size = stream.get_u16() 362 | var array = [] 363 | 364 | for i in array_size: 365 | array.append(unpack(stream)) 366 | 367 | return array, 368 | }, 369 | TYPE_PACKED_BYTE_ARRAY: { 370 | "pack": func(value: PackedByteArray, stream: StreamPeerBuffer) -> StreamPeerBuffer: 371 | stream.put_u16(value.size()) 372 | stream.put_data(value) 373 | return stream, 374 | "unpack": func(stream: StreamPeerBuffer) -> PackedByteArray: 375 | var array_size := stream.get_u16() 376 | var data = stream.get_data(array_size)[1] 377 | return PackedByteArray(data), 378 | }, 379 | TYPE_PACKED_INT32_ARRAY: { 380 | "pack": func(value: PackedInt32Array, stream: StreamPeerBuffer) -> StreamPeerBuffer: 381 | var buffer = value.to_byte_array() 382 | 383 | stream.put_u16(buffer.size()) 384 | stream.put_data(buffer) 385 | 386 | return stream, 387 | "unpack": func(stream: StreamPeerBuffer) -> PackedInt32Array: 388 | return PackedByteArray(stream.get_data(stream.get_u16())).to_int32_array(), 389 | }, 390 | TYPE_PACKED_INT64_ARRAY: { 391 | "pack": func(value: PackedInt64Array, stream: StreamPeerBuffer) -> StreamPeerBuffer: 392 | var buffer = value.to_byte_array() 393 | 394 | stream.put_u16(buffer.size()) 395 | stream.put_data(buffer) 396 | 397 | return stream, 398 | "unpack": func(stream: StreamPeerBuffer) -> PackedInt64Array: 399 | return PackedByteArray(stream.get_data(stream.get_u16())).to_int64_array(), 400 | }, 401 | TYPE_PACKED_FLOAT32_ARRAY: { 402 | "pack": func(value: PackedFloat32Array, stream: StreamPeerBuffer) -> StreamPeerBuffer: 403 | var buffer = value.to_byte_array() 404 | 405 | stream.put_u16(buffer.size()) 406 | stream.put_data(buffer) 407 | 408 | return stream, 409 | "unpack": func(stream: StreamPeerBuffer) -> PackedFloat32Array: 410 | return PackedByteArray(stream.get_data(stream.get_u16())).to_float32_array(), 411 | }, 412 | TYPE_PACKED_FLOAT64_ARRAY: { 413 | "pack": func(value: PackedFloat64Array, stream: StreamPeerBuffer) -> StreamPeerBuffer: 414 | var buffer = value.to_byte_array() 415 | 416 | stream.put_u16(buffer.size()) 417 | stream.put_data(buffer) 418 | 419 | return stream, 420 | "unpack": func(stream: StreamPeerBuffer) -> PackedFloat64Array: 421 | return PackedByteArray(stream.get_data(stream.get_u16())).to_float64_array(), 422 | }, 423 | TYPE_PACKED_STRING_ARRAY: { 424 | "pack": func(value: PackedStringArray, stream: StreamPeerBuffer) -> StreamPeerBuffer: 425 | stream.put_u16(value.size()) 426 | 427 | for string in value: 428 | pack(string, stream, TYPE_STRING) 429 | 430 | return stream, 431 | "unpack": func(stream: StreamPeerBuffer) -> PackedStringArray: 432 | var array_size := stream.get_u16() 433 | var array := PackedStringArray() 434 | 435 | for i in array_size: 436 | array.append(unpack(stream, TYPE_STRING)) 437 | 438 | return array, 439 | }, 440 | TYPE_PACKED_VECTOR2_ARRAY: { 441 | "pack": func(value: PackedVector2Array, stream: StreamPeerBuffer) -> StreamPeerBuffer: 442 | stream.put_u16(value.size()) 443 | 444 | for vector in value: 445 | pack(vector, stream, TYPE_VECTOR2) 446 | 447 | return stream, 448 | "unpack": func(stream: StreamPeerBuffer) -> PackedVector2Array: 449 | var array_size := stream.get_u16() 450 | var array := PackedVector2Array() 451 | 452 | for i in array_size: 453 | array.append(unpack(stream, TYPE_VECTOR2)) 454 | 455 | return array, 456 | }, 457 | TYPE_PACKED_VECTOR3_ARRAY: { 458 | "pack": func(value: PackedVector3Array, stream: StreamPeerBuffer) -> StreamPeerBuffer: 459 | stream.put_u16(value.size()) 460 | 461 | for vector in value: 462 | pack(vector, stream, TYPE_VECTOR3) 463 | 464 | return stream, 465 | "unpack": func(stream: StreamPeerBuffer) -> PackedVector3Array: 466 | var array_size := stream.get_u16() 467 | var array := PackedVector3Array() 468 | 469 | for i in array_size: 470 | array.append(unpack(stream, TYPE_VECTOR3)) 471 | 472 | return array, 473 | }, 474 | TYPE_PACKED_COLOR_ARRAY: { 475 | "pack": func(value: PackedColorArray, stream: StreamPeerBuffer) -> StreamPeerBuffer: 476 | stream.put_u16(value.size()) 477 | 478 | for vector in value: 479 | pack(vector, stream, TYPE_COLOR) 480 | 481 | return stream, 482 | "unpack": func(stream: StreamPeerBuffer) -> PackedColorArray: 483 | var array_size := stream.get_u16() 484 | var array := PackedColorArray() 485 | 486 | for i in array_size: 487 | array.append(unpack(stream, TYPE_COLOR)) 488 | 489 | return array, 490 | }, 491 | CustomTypes.TYPED_ARRAY: { 492 | "pack": func(array: Array, stream: StreamPeerBuffer) -> StreamPeerBuffer: 493 | var item_type: int = TYPE_NIL 494 | 495 | if _can_array_be_typed(array): 496 | item_type = typeof(array[0]) 497 | 498 | stream.put_u16(array.size()) 499 | stream.put_u8(item_type) 500 | 501 | for item in array: 502 | pack(item, stream, item_type) 503 | 504 | return stream, 505 | "unpack": func(stream: StreamPeerBuffer): 506 | var array_size = stream.get_u16() 507 | var item_type = stream.get_u8() 508 | 509 | if item_type == TYPE_NIL: 510 | return null 511 | 512 | var array = [] 513 | for i in array_size: 514 | array.append(unpack(stream, item_type)) 515 | 516 | return array, 517 | } 518 | } 519 | 520 | static func pack_to_bytes(value) -> PackedByteArray: 521 | return pack(value).data_array 522 | 523 | static func unpack_from_bytes(bytes: PackedByteArray): 524 | var stream := StreamPeerBuffer.new() 525 | stream.data_array = bytes 526 | return unpack(stream) 527 | 528 | static func pack(value, stream:=StreamPeerBuffer.new(), type:=-1, add_type_prefix:=false) -> StreamPeerBuffer: 529 | add_type_prefix = add_type_prefix or type == -1 # If type is unknown we add a type prefix to identify it 530 | 531 | if type == -1: 532 | type = typeof(value) # Check the generic type of the value 533 | 534 | # If the type is an array, lets try to make it typed (to save data) 535 | if type == TYPE_ARRAY and _can_array_be_typed(value): 536 | type = CustomTypes.TYPED_ARRAY 537 | if type == TYPE_INT: 538 | type = _get_int_type(value) 539 | if type == TYPE_OBJECT and value == null: 540 | type = TYPE_NIL 541 | 542 | if not type in _serializer: 543 | push_error("[Seriously] Unknown type: ", type) 544 | return null 545 | 546 | if add_type_prefix: 547 | stream.put_u8(type) 548 | 549 | return _serializer[type].pack.call(value, stream) 550 | 551 | static func unpack(stream: StreamPeerBuffer, type:=-1): 552 | if type == -1: # If we dont define a type we try to read a type prefix 553 | type = stream.get_u8() 554 | 555 | if not type in _serializer: 556 | push_error("[Seriously] Unknown type: ", type) 557 | return null 558 | 559 | return _serializer[type].unpack.call(stream) 560 | 561 | # Private methods 562 | static func _can_array_be_typed(array: Array) -> bool: 563 | if array.size() == 0: # Typing empty arrays makes no sense 564 | return false 565 | if array.size() == 1: 566 | return true # If an array has just 1 entry thats 1 type. So it CAN be typed 567 | 568 | var array_type: int = typeof(array[0]) # Lets check if all other entries have the same type 569 | for entry in array: 570 | if typeof(entry) != array_type: return false # This array cannot be typed because there is a mismatch 571 | 572 | return true 573 | 574 | static func _get_int_type(value: int) -> int: 575 | var unsigned = value >= 0 576 | var bit_size = 8 577 | if abs(value) <= 0xFF: 578 | bit_size = 8 579 | elif abs(value) <= 0xFFFF: 580 | bit_size = 16 581 | elif abs(value) <= 0xFFFFFFFF: 582 | bit_size = 32 583 | elif value >= INT_MIN and value <= INT_MAX: 584 | bit_size = 64 585 | else: 586 | push_error("[Seriously] Unsupported integer: ", value) 587 | return -1 588 | 589 | var type_name := "%sINT%s" % ["U" if unsigned else "", bit_size] 590 | if not type_name in CustomTypes: 591 | push_error("[Seriously] Unsupported integer: ", value) 592 | return -1 593 | 594 | return CustomTypes[type_name] 595 | --------------------------------------------------------------------------------