PyREUser3 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
pyreuser3/pack/plan.py ADDED
@@ -0,0 +1,566 @@
1
+ """JSON 到 RSZ 实例表的规划逻辑。
2
+
3
+ 规划阶段负责把 JSON 树转换为线性的实例列表(``self.instances``)和字段中间
4
+ 表示,并在此过程中分配稳定的实例编号、校验引用、按模板字段顺序补默认值。
5
+ 它分两种输入:完整实例表封包文档(保持原实例编号)和 readable/手写 JSON
6
+ (按遍历顺序新建实例编号)。
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ from typing import Any
12
+
13
+ from .models import PACK_JSON_FORMAT, InstanceRef, InstanceSpec, PackError, StructValue
14
+ from ..schema import ClassDef, FieldDef
15
+
16
+
17
+ class PackerPlanMixin:
18
+ """负责把 JSON 树转换成待写入的实例和字段值。"""
19
+
20
+ def _is_pack_document(self, data: Any) -> bool:
21
+ """判断输入是否为完整实例表封包文档。
22
+
23
+ 参数:
24
+ data (Any): 待判定的 JSON 数据。
25
+
26
+ 返回:
27
+ bool: 含正确 ``_format`` 且 ``_instances`` 为对象时返回 ``True``。
28
+ """
29
+ return (
30
+ isinstance(data, dict)
31
+ and data.get("_format") == PACK_JSON_FORMAT
32
+ and isinstance(data.get("_instances"), dict)
33
+ )
34
+
35
+ def _plan_pack_document(self, data: dict[str, Any]) -> list[int]:
36
+ """按完整实例表文档规划实例,保持原实例编号稳定。
37
+
38
+ 参数:
39
+ data (dict[str, Any]): 完整实例表封包文档。
40
+
41
+ 返回:
42
+ list[int]: 根实例编号列表。
43
+
44
+ 异常:
45
+ PackError: 当文档含不支持的数据段、实例编号不连续、缺类、引用悬空,
46
+ 或对象字段内联了对象数据时抛出。
47
+ """
48
+ unsupported = data.get("_unsupported", [])
49
+ if unsupported:
50
+ if not isinstance(unsupported, list):
51
+ raise PackError("pack JSON _unsupported must be an array")
52
+ raise PackError(
53
+ "pack JSON contains original data sections that the current "
54
+ f"writer cannot rebuild: {unsupported}"
55
+ )
56
+
57
+ instances_raw = data.get("_instances")
58
+ if not isinstance(instances_raw, dict):
59
+ raise PackError("pack JSON must contain an _instances object")
60
+
61
+ ids = self._parse_pack_instance_ids(instances_raw)
62
+ if not ids or ids[0] != 0:
63
+ raise PackError("pack JSON _instances must include null instance 0")
64
+ # 实例编号必须从 0 开始稠密连续,否则写出的实例表会错位。
65
+ expected = list(range(ids[-1] + 1))
66
+ if ids != expected:
67
+ missing = sorted(set(expected) - set(ids))
68
+ raise PackError(f"pack JSON instance ids must be dense; missing: {missing}")
69
+
70
+ roots = self._parse_pack_roots(data.get("_roots"), set(ids))
71
+ self._validate_pack_references(instances_raw, set(ids))
72
+ self.instances = [None for _ in ids]
73
+
74
+ # 第一遍:建立每个实例的类型规格,但暂不准备字段。
75
+ for idx in ids[1:]:
76
+ entry = instances_raw[str(idx)]
77
+ if not isinstance(entry, dict):
78
+ raise PackError(f"instance {idx} must be an object")
79
+ if entry.get("_unparsed"):
80
+ reason = entry.get("reason", "unparsed")
81
+ raise PackError(
82
+ f"instance {idx} is unparsed and cannot be packed: {reason}"
83
+ )
84
+ if entry.get("_kind") == "userdata_reference":
85
+ raise PackError(
86
+ f"instance {idx} is an external userdata reference; "
87
+ "the current writer cannot rebuild RSZ userdata tables"
88
+ )
89
+ class_name = entry.get("_class")
90
+ if not isinstance(class_name, str) or not class_name:
91
+ raise PackError(f"instance {idx} is missing _class")
92
+ class_hash = self.typedb.name_to_hash.get(class_name)
93
+ if class_hash is None:
94
+ raise PackError(
95
+ f"class not found in schema for instance {idx}: {class_name}"
96
+ )
97
+ class_def = self.typedb.get_class(class_hash)
98
+ if class_def is None:
99
+ raise PackError(
100
+ f"class hash not found in schema for instance {idx}: {class_name}"
101
+ )
102
+ self._validate_declared_hash(idx, entry, class_hash, class_def.crc)
103
+ self.instances[idx] = InstanceSpec(
104
+ class_hash=class_hash, class_def=class_def
105
+ )
106
+
107
+ # 第二遍:在所有实例规格就绪后再准备字段,确保引用编号已稳定。
108
+ for idx in ids[1:]:
109
+ entry = instances_raw[str(idx)]
110
+ spec = self.instances[idx]
111
+ if spec is None:
112
+ continue
113
+ fields = entry.get("fields", {})
114
+ if not isinstance(fields, dict):
115
+ raise PackError(f"instance {idx} fields must be an object")
116
+ self._validate_known_fields(idx, spec.class_def, fields)
117
+ before_count = len(self.instances)
118
+ spec.fields = self._prepare_fields(spec.class_def, fields)
119
+ if len(self.instances) != before_count:
120
+ # 完整实例表模式不允许内联对象数据,必须使用 ref_instance_id 引用。
121
+ raise PackError(
122
+ f"instance {idx} contains embedded object data; "
123
+ "pack JSON object fields must use ref_instance_id"
124
+ )
125
+ return roots
126
+
127
+ def _parse_pack_instance_ids(self, instances_raw: dict[str, Any]) -> list[int]:
128
+ """解析并排序完整实例表中的实例编号。
129
+
130
+ 参数:
131
+ instances_raw (dict[str, Any]): ``_instances`` 对象(键为字符串编号)。
132
+
133
+ 返回:
134
+ list[int]: 升序排列的实例编号列表。
135
+
136
+ 异常:
137
+ PackError: 当键不是有效的非负整数时抛出。
138
+ """
139
+ ids: list[int] = []
140
+ for key in instances_raw:
141
+ try:
142
+ idx = int(key)
143
+ except (TypeError, ValueError) as exc:
144
+ raise PackError(f"invalid instance id: {key!r}") from exc
145
+ if idx < 0:
146
+ raise PackError(f"instance id must be non-negative: {idx}")
147
+ ids.append(idx)
148
+ return sorted(ids)
149
+
150
+ def _parse_pack_roots(self, raw_roots: Any, known_ids: set[int]) -> list[int]:
151
+ """解析并校验完整实例表中的根实例编号。
152
+
153
+ 参数:
154
+ raw_roots (Any): ``_roots`` 字段,应为整数列表。
155
+ known_ids (set[int]): 已知的全部实例编号集合。
156
+
157
+ 返回:
158
+ list[int]: 校验通过的根实例编号列表。
159
+
160
+ 异常:
161
+ PackError: 当 ``_roots`` 不是数组、元素非整数或引用了不存在的实例时抛出。
162
+ """
163
+ if not isinstance(raw_roots, list):
164
+ raise PackError("pack JSON must contain a _roots array")
165
+ roots: list[int] = []
166
+ for raw_root in raw_roots:
167
+ if not isinstance(raw_root, int):
168
+ raise PackError(f"root instance id must be int: {raw_root!r}")
169
+ if raw_root not in known_ids:
170
+ raise PackError(f"root references missing instance: {raw_root}")
171
+ roots.append(raw_root)
172
+ return roots
173
+
174
+ def _validate_pack_references(
175
+ self, instances_raw: dict[str, Any], known_ids: set[int]
176
+ ) -> None:
177
+ """校验所有 ref_instance_id 是否能在完整实例表中找到。
178
+
179
+ 参数:
180
+ instances_raw (dict[str, Any]): ``_instances`` 对象。
181
+ known_ids (set[int]): 已知的全部实例编号集合。
182
+
183
+ 返回:
184
+ None: 仅做校验;发现问题时抛出异常。
185
+ """
186
+ for idx, entry in instances_raw.items():
187
+ self._validate_ref_value(entry, known_ids, f"_instances.{idx}")
188
+
189
+ def _validate_ref_value(self, value: Any, known_ids: set[int], path: str) -> None:
190
+ """递归校验引用对象,避免静默写入悬空引用。
191
+
192
+ 参数:
193
+ value (Any): 当前校验的节点(dict / list / 标量)。
194
+ known_ids (set[int]): 已知的全部实例编号集合。
195
+ path (str): 当前节点的路径字符串,用于错误信息定位。
196
+
197
+ 返回:
198
+ None: 仅做校验;发现问题时抛出异常。
199
+
200
+ 异常:
201
+ PackError: 当 ``ref_instance_id`` 非整数、夹带其它键,或指向不存在的实例时抛出。
202
+ """
203
+ if isinstance(value, dict):
204
+ if "ref_instance_id" in value:
205
+ ref_id = value.get("ref_instance_id")
206
+ if not isinstance(ref_id, int):
207
+ raise PackError(f"{path}.ref_instance_id must be int")
208
+ extra = sorted(k for k in value if k != "ref_instance_id")
209
+ if extra:
210
+ raise PackError(
211
+ f"{path} has ref_instance_id plus ignored keys: {extra}"
212
+ )
213
+ if ref_id not in known_ids:
214
+ raise PackError(f"{path} references missing instance: {ref_id}")
215
+ return
216
+ for key, child in value.items():
217
+ self._validate_ref_value(child, known_ids, f"{path}.{key}")
218
+ return
219
+ if isinstance(value, list):
220
+ for i, child in enumerate(value):
221
+ self._validate_ref_value(child, known_ids, f"{path}[{i}]")
222
+
223
+ def _validate_declared_hash(
224
+ self, idx: int, entry: dict[str, Any], class_hash: int, crc: int
225
+ ) -> None:
226
+ """如果 pack JSON 中带有 hash/crc,则与模板解析结果比对。
227
+
228
+ 参数:
229
+ idx (int): 实例编号,用于错误信息。
230
+ entry (dict[str, Any]): 实例条目,可能包含 ``_hash`` / ``_crc``。
231
+ class_hash (int): 模板解析出的类型哈希。
232
+ crc (int): 模板解析出的 CRC。
233
+
234
+ 返回:
235
+ None: 仅做校验;不一致时抛出异常。
236
+
237
+ 异常:
238
+ PackError: 当声明的 ``_hash`` 或 ``_crc`` 与模板不一致时抛出。
239
+ """
240
+ declared_hash = self._parse_optional_u32(entry.get("_hash"))
241
+ if declared_hash is not None and declared_hash != class_hash:
242
+ raise PackError(
243
+ f"instance {idx} _hash does not match schema class: "
244
+ f"0x{declared_hash:08x} != 0x{class_hash:08x}"
245
+ )
246
+ declared_crc = self._parse_optional_u32(entry.get("_crc"))
247
+ if declared_crc is not None and declared_crc != (crc & 0xFFFFFFFF):
248
+ raise PackError(
249
+ f"instance {idx} _crc does not match schema class: "
250
+ f"0x{declared_crc:08x} != 0x{crc & 0xFFFFFFFF:08x}"
251
+ )
252
+
253
+ def _parse_optional_u32(self, value: Any) -> int | None:
254
+ """解析可选的十六进制/十进制 32 位整数。
255
+
256
+ 参数:
257
+ value (Any): ``None``、整数或数字字符串(支持 ``0x`` 前缀)。
258
+
259
+ 返回:
260
+ int | None: 解析出的无符号 32 位整数;输入为 ``None`` 或空串时返回 ``None``。
261
+
262
+ 异常:
263
+ PackError: 当值既不是整数也不是合法数字字符串时抛出。
264
+ """
265
+ if value is None:
266
+ return None
267
+ if isinstance(value, int):
268
+ return value & 0xFFFFFFFF
269
+ if isinstance(value, str):
270
+ text = value.strip()
271
+ if not text:
272
+ return None
273
+ return int(text, 0) & 0xFFFFFFFF
274
+ raise PackError(f"expected integer or hex string, got {value!r}")
275
+
276
+ def _validate_known_fields(
277
+ self, idx: int, class_def: ClassDef, raw_fields: dict[str, Any]
278
+ ) -> None:
279
+ """在完整实例表模式下拒绝会被静默忽略的未知字段。
280
+
281
+ 参数:
282
+ idx (int): 实例编号,用于错误信息。
283
+ class_def (ClassDef): 实例对应的类型定义。
284
+ raw_fields (dict[str, Any]): 待写入的字段名到值映射。
285
+
286
+ 返回:
287
+ None: 仅做校验;存在未知字段时抛出异常。
288
+
289
+ 异常:
290
+ PackError: 当字段名不在模板字段集合内时抛出。
291
+ """
292
+ allowed = {field.name or "unnamed" for field in class_def.fields}
293
+ unknown = sorted(key for key in raw_fields if key not in allowed)
294
+ if unknown:
295
+ raise PackError(
296
+ f"instance {idx} ({class_def.name}) contains unknown fields: {unknown}"
297
+ )
298
+
299
+ def _normalize_roots(self, data: Any) -> list[Any]:
300
+ """把顶层 JSON 统一成根对象列表。
301
+
302
+ 参数:
303
+ data (Any): 顶层 JSON,单个对象或对象列表。
304
+
305
+ 返回:
306
+ list[Any]: 根对象列表(单对象会被包成单元素列表)。
307
+
308
+ 异常:
309
+ PackError: 当顶层既不是对象也不是对象列表时抛出。
310
+ """
311
+ if isinstance(data, list):
312
+ return data
313
+ if isinstance(data, dict):
314
+ return [data]
315
+ raise PackError("top-level JSON must be an object or a list of objects")
316
+
317
+ def _plan_node(self, node: Any, expected_class: str | None = None) -> int:
318
+ """把一个 JSON 节点规划为 RSZ 实例,并返回实例编号。
319
+
320
+ 参数:
321
+ node (Any): 类名包裹对象,或在已知 ``expected_class`` 时的字段对象。
322
+ expected_class (str | None): 对象字段声明的预期类名,用于免去外层类名包裹。
323
+
324
+ 返回:
325
+ int: 新建实例在 ``self.instances`` 中的编号。
326
+
327
+ 异常:
328
+ PackError: 当无法推断类名或类名不在模板中时抛出。
329
+ """
330
+ class_name, fields = self._unwrap_node(node, expected_class)
331
+ class_hash = self.typedb.name_to_hash.get(class_name)
332
+ if class_hash is None:
333
+ raise PackError(f"class not found in schema: {class_name}")
334
+ class_def = self.typedb.get_class(class_hash)
335
+ if class_def is None:
336
+ raise PackError(f"class hash not found in schema: {class_name}")
337
+
338
+ spec = InstanceSpec(class_hash=class_hash, class_def=class_def)
339
+ spec.fields = self._prepare_fields(class_def, fields)
340
+ instance_id = len(self.instances)
341
+ # 先加入实例表,再由字段准备阶段递归规划子对象,保持引用编号稳定。
342
+ self.instances.append(spec)
343
+ return instance_id
344
+
345
+ def _unwrap_node(self, node: Any, expected_class: str | None) -> tuple[str, Any]:
346
+ """从类名包裹对象中取出类名和字段对象。
347
+
348
+ 参数:
349
+ node (Any): 待解包的节点。
350
+ expected_class (str | None): 对象字段声明的预期类名。
351
+
352
+ 返回:
353
+ tuple[str, Any]: ``(类名, 字段对象)`` 二元组。
354
+
355
+ 异常:
356
+ PackError: 当既无法识别类名包裹、又没有 ``expected_class`` 时抛出。
357
+ """
358
+ if isinstance(node, dict):
359
+ class_keys = [
360
+ k
361
+ for k in node.keys()
362
+ if isinstance(k, str) and k in self.typedb.name_to_hash
363
+ ]
364
+ if len(class_keys) == 1 and len(node) == 1:
365
+ key = class_keys[0]
366
+ return key, node[key]
367
+ if expected_class:
368
+ # 对象字段有明确声明类型时,允许用户直接传字段值而不再包一层类名。
369
+ return expected_class, node
370
+ raise PackError(f"cannot infer class for node: {node!r}")
371
+
372
+ def _prepare_fields(self, class_def: ClassDef, raw_fields: Any) -> dict[str, Any]:
373
+ """按模板字段顺序准备一个实例的字段值。
374
+
375
+ 参数:
376
+ class_def (ClassDef): 实例对应的类型定义。
377
+ raw_fields (Any): JSON 中的字段对象;枚举/包装类型也可能是裸值。
378
+
379
+ 返回:
380
+ dict[str, Any]: 字段名到“写入器中间表示”的映射,缺失字段已补默认值。
381
+
382
+ 异常:
383
+ PackError: 当非字典输入无法还原为单值字段时抛出。
384
+ """
385
+ if not isinstance(raw_fields, dict):
386
+ value_fields = [
387
+ f for f in class_def.fields if f.name in {"_Value", "value__"}
388
+ ]
389
+ if len(value_fields) == 1:
390
+ # 枚举或简单包装类型经常导出为纯值,这里还原到真实字段名。
391
+ raw_fields = {value_fields[0].name: raw_fields}
392
+ else:
393
+ raise PackError(f"class {class_def.name} expects object fields")
394
+
395
+ prepared: dict[str, Any] = {}
396
+ for field_def in class_def.fields:
397
+ key = field_def.name or "unnamed"
398
+ # JSON 中缺失的字段按类型填默认值,避免手工编辑后无法封包。
399
+ raw_value = raw_fields.get(key, self._default_value(field_def))
400
+ prepared[key] = self._prepare_field_value(field_def, raw_value)
401
+ return prepared
402
+
403
+ def _prepare_field_value(self, field_def: FieldDef, raw_value: Any) -> Any:
404
+ """把 JSON 字段值转换为写入器需要的中间表示。
405
+
406
+ 参数:
407
+ field_def (FieldDef): 字段定义。
408
+ raw_value (Any): JSON 中的原始字段值。
409
+
410
+ 返回:
411
+ Any: 写入器可消费的中间表示(标量原样、对象转 :class:`InstanceRef`、
412
+ 结构体转 :class:`StructValue`、数组转其元素列表)。
413
+ """
414
+ if field_def.is_array:
415
+ items = raw_value if isinstance(raw_value, list) else []
416
+ non_array = FieldDef(
417
+ name=field_def.name,
418
+ field_type=field_def.field_type,
419
+ original_type=field_def.original_type,
420
+ size=field_def.size,
421
+ align=field_def.align,
422
+ is_array=False,
423
+ )
424
+ return [self._prepare_field_value(non_array, item) for item in items]
425
+
426
+ if field_def.field_type in {"Object", "UserData"}:
427
+ # 对象字段需要先规划目标实例,再写入引用编号。
428
+ return self._prepare_object_ref(field_def, raw_value)
429
+ if field_def.field_type == "Struct":
430
+ # 结构体按自己的 ClassDef 递归准备字段。
431
+ return self._prepare_struct_value(field_def, raw_value)
432
+ return raw_value
433
+
434
+ def _prepare_object_ref(self, field_def: FieldDef, raw_value: Any) -> InstanceRef:
435
+ """把对象字段值转换为实例引用。
436
+
437
+ 参数:
438
+ field_def (FieldDef): 对象/用户数据字段定义。
439
+ raw_value (Any): JSON 值:``None``、已有引用、类名包裹对象或裸字段对象。
440
+
441
+ 返回:
442
+ InstanceRef: 指向目标实例的引用;``None`` 值返回指向 0 的空引用。
443
+
444
+ 异常:
445
+ PackError: 当无法推断对象字段应使用的类时抛出。
446
+ """
447
+ if raw_value is None:
448
+ return InstanceRef(0)
449
+ if isinstance(raw_value, dict) and isinstance(
450
+ raw_value.get("ref_instance_id"), int
451
+ ):
452
+ # 用户保留导出的引用编号时直接复用,不展开新实例。
453
+ return InstanceRef(raw_value["ref_instance_id"])
454
+
455
+ expected_class = self._resolve_object_class(field_def.original_type)
456
+ if isinstance(raw_value, dict):
457
+ class_keys = [
458
+ k
459
+ for k in raw_value.keys()
460
+ if isinstance(k, str) and k in self.typedb.name_to_hash
461
+ ]
462
+ if len(class_keys) == 1 and len(raw_value) == 1:
463
+ # 已经是 `{类名: 字段}` 形状时直接规划该子对象。
464
+ return InstanceRef(self._plan_node(raw_value))
465
+ if expected_class:
466
+ return InstanceRef(self._plan_node(raw_value, expected_class))
467
+
468
+ if expected_class:
469
+ return InstanceRef(self._plan_node(raw_value, expected_class))
470
+ raise PackError(
471
+ f"cannot encode object field {field_def.name!r} of type {field_def.original_type!r}"
472
+ )
473
+
474
+ def _resolve_object_class(self, original_type: str) -> str | None:
475
+ """根据字段原始类型推断对象字段应使用的类名。
476
+
477
+ 参数:
478
+ original_type (str): 字段的原始类型名。
479
+
480
+ 返回:
481
+ str | None: 命中模板的类名;无法推断时返回 ``None``。
482
+ """
483
+ if original_type in self.typedb.name_to_hash:
484
+ return original_type
485
+ if original_type.endswith("_Fixed"):
486
+ # 固定枚举字段常对应一个 `xxx_Serializable` 包装类型。
487
+ candidate = f"{original_type[:-6]}_Serializable"
488
+ if candidate in self.typedb.name_to_hash:
489
+ return candidate
490
+ return None
491
+
492
+ def _prepare_struct_value(self, field_def: FieldDef, raw_value: Any) -> Any:
493
+ """准备结构体字段的中间表示。
494
+
495
+ 参数:
496
+ field_def (FieldDef): 结构体字段定义。
497
+ raw_value (Any): JSON 值;可能是字段对象,或保留原始字节的 dict。
498
+
499
+ 返回:
500
+ Any: :class:`StructValue` 中间表示;当 JSON 保留了原始字节时直接返回该
501
+ dict 交给写入器原样写回。
502
+
503
+ 异常:
504
+ PackError: 当模板能解析结构体哈希、却找不到对应类定义时抛出。
505
+ """
506
+ if isinstance(raw_value, dict) and isinstance(raw_value.get("raw"), str):
507
+ # 导出器在结构体过深或模板缺失时会保留原始字节;
508
+ # 这里直接交给 writer 原样写回,避免默认字段覆盖未知内容。
509
+ return raw_value
510
+ struct_hash = self.typedb.resolve_struct_hash(field_def.original_type)
511
+ if struct_hash is None:
512
+ # 模板无法解析结构体时,尽量按原始字节原样写回。
513
+ return StructValue(
514
+ class_def=ClassDef(field_def.original_type, 0, []),
515
+ fields={"raw": raw_value},
516
+ declared_size=field_def.size,
517
+ )
518
+ class_def = self.typedb.get_class(struct_hash)
519
+ if class_def is None:
520
+ raise PackError(f"struct class not found: {field_def.original_type}")
521
+ fields = raw_value if isinstance(raw_value, dict) else {}
522
+ return StructValue(
523
+ class_def, self._prepare_fields(class_def, fields), field_def.size
524
+ )
525
+
526
+ def _default_value(self, field_def: FieldDef) -> Any:
527
+ """根据字段类型生成缺省值。
528
+
529
+ 参数:
530
+ field_def (FieldDef): 字段定义。
531
+
532
+ 返回:
533
+ Any: 与字段类型匹配的默认值(数组为 ``[]``,布尔为 ``False``,浮点为
534
+ ``0.0``,字符串为 ``""``,GUID 为全零文本,对象为 ``None``,向量为零向量,
535
+ 其余整数为 ``0``)。
536
+ """
537
+ if field_def.is_array:
538
+ return []
539
+ if field_def.field_type in {"Bool"}:
540
+ return False
541
+ if field_def.field_type in {"F32", "F64"}:
542
+ return 0.0
543
+ if field_def.field_type in {"String", "Resource", "C8"}:
544
+ return ""
545
+ if field_def.field_type in {"Guid", "GameObjectRef", "Uri"}:
546
+ return "00000000-0000-0000-0000-000000000000"
547
+ if field_def.field_type in {"Object", "UserData"}:
548
+ return None
549
+ if field_def.field_type in {
550
+ "Float2",
551
+ "Float3",
552
+ "Float4",
553
+ "Vec2",
554
+ "Vec3",
555
+ "Vec4",
556
+ "Quaternion",
557
+ "Color",
558
+ "AABB",
559
+ "Capsule",
560
+ "OBB",
561
+ "Mat3",
562
+ "Mat4",
563
+ "Position",
564
+ }:
565
+ return [0.0 for _ in range(max(field_def.size // 4, 1))]
566
+ return 0