├── 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 |
--------------------------------------------------------------------------------