arkparser 0.4.6__tar.gz → 0.5.0__tar.gz
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.
- {arkparser-0.4.6 → arkparser-0.5.0}/PKG-INFO +11 -13
- {arkparser-0.4.6 → arkparser-0.5.0}/README.md +10 -12
- {arkparser-0.4.6 → arkparser-0.5.0}/arkparser/__init__.py +1 -1
- {arkparser-0.4.6 → arkparser-0.5.0}/arkparser/data_models.py +8 -0
- {arkparser-0.4.6 → arkparser-0.5.0}/arkparser/export.py +747 -280
- {arkparser-0.4.6 → arkparser-0.5.0}/arkparser/files/profile.py +19 -3
- {arkparser-0.4.6 → arkparser-0.5.0}/arkparser/files/world_save.py +17 -34
- {arkparser-0.4.6 → arkparser-0.5.0}/arkparser/properties/registry.py +18 -5
- {arkparser-0.4.6 → arkparser-0.5.0}/arkparser.egg-info/PKG-INFO +11 -13
- {arkparser-0.4.6 → arkparser-0.5.0}/pyproject.toml +1 -1
- {arkparser-0.4.6 → arkparser-0.5.0}/tests/test_review_fixes.py +2 -1
- {arkparser-0.4.6 → arkparser-0.5.0}/LICENSE +0 -0
- {arkparser-0.4.6 → arkparser-0.5.0}/arkparser/common/__init__.py +0 -0
- {arkparser-0.4.6 → arkparser-0.5.0}/arkparser/common/binary_reader.py +0 -0
- {arkparser-0.4.6 → arkparser-0.5.0}/arkparser/common/exceptions.py +0 -0
- {arkparser-0.4.6 → arkparser-0.5.0}/arkparser/common/map_config.py +0 -0
- {arkparser-0.4.6 → arkparser-0.5.0}/arkparser/common/normalization.py +0 -0
- {arkparser-0.4.6 → arkparser-0.5.0}/arkparser/common/types.py +0 -0
- {arkparser-0.4.6 → arkparser-0.5.0}/arkparser/common/version_detection.py +0 -0
- {arkparser-0.4.6 → arkparser-0.5.0}/arkparser/files/__init__.py +0 -0
- {arkparser-0.4.6 → arkparser-0.5.0}/arkparser/files/base.py +0 -0
- {arkparser-0.4.6 → arkparser-0.5.0}/arkparser/files/cloud_inventory.py +0 -0
- {arkparser-0.4.6 → arkparser-0.5.0}/arkparser/files/tribe.py +0 -0
- {arkparser-0.4.6 → arkparser-0.5.0}/arkparser/game_objects/__init__.py +0 -0
- {arkparser-0.4.6 → arkparser-0.5.0}/arkparser/game_objects/container.py +0 -0
- {arkparser-0.4.6 → arkparser-0.5.0}/arkparser/game_objects/game_object.py +0 -0
- {arkparser-0.4.6 → arkparser-0.5.0}/arkparser/game_objects/location.py +0 -0
- {arkparser-0.4.6 → arkparser-0.5.0}/arkparser/properties/__init__.py +0 -0
- {arkparser-0.4.6 → arkparser-0.5.0}/arkparser/properties/base.py +0 -0
- {arkparser-0.4.6 → arkparser-0.5.0}/arkparser/properties/byte_property.py +0 -0
- {arkparser-0.4.6 → arkparser-0.5.0}/arkparser/properties/compound.py +0 -0
- {arkparser-0.4.6 → arkparser-0.5.0}/arkparser/properties/primitives.py +0 -0
- {arkparser-0.4.6 → arkparser-0.5.0}/arkparser/structs/__init__.py +0 -0
- {arkparser-0.4.6 → arkparser-0.5.0}/arkparser/structs/base.py +0 -0
- {arkparser-0.4.6 → arkparser-0.5.0}/arkparser/structs/colors.py +0 -0
- {arkparser-0.4.6 → arkparser-0.5.0}/arkparser/structs/misc.py +0 -0
- {arkparser-0.4.6 → arkparser-0.5.0}/arkparser/structs/property_list.py +0 -0
- {arkparser-0.4.6 → arkparser-0.5.0}/arkparser/structs/registry.py +0 -0
- {arkparser-0.4.6 → arkparser-0.5.0}/arkparser/structs/vectors.py +0 -0
- {arkparser-0.4.6 → arkparser-0.5.0}/arkparser.egg-info/SOURCES.txt +0 -0
- {arkparser-0.4.6 → arkparser-0.5.0}/arkparser.egg-info/dependency_links.txt +0 -0
- {arkparser-0.4.6 → arkparser-0.5.0}/arkparser.egg-info/requires.txt +0 -0
- {arkparser-0.4.6 → arkparser-0.5.0}/arkparser.egg-info/top_level.txt +0 -0
- {arkparser-0.4.6 → arkparser-0.5.0}/setup.cfg +0 -0
- {arkparser-0.4.6 → arkparser-0.5.0}/tests/test_asa_header_position.py +0 -0
- {arkparser-0.4.6 → arkparser-0.5.0}/tests/test_asa_name_table.py +0 -0
- {arkparser-0.4.6 → arkparser-0.5.0}/tests/test_ase_cluster_drift.py +0 -0
- {arkparser-0.4.6 → arkparser-0.5.0}/tests/test_binary_reader.py +0 -0
- {arkparser-0.4.6 → arkparser-0.5.0}/tests/test_binary_reader_layouts.py +0 -0
- {arkparser-0.4.6 → arkparser-0.5.0}/tests/test_cloud_export.py +0 -0
- {arkparser-0.4.6 → arkparser-0.5.0}/tests/test_cloud_inventory.py +0 -0
- {arkparser-0.4.6 → arkparser-0.5.0}/tests/test_cryopod_export.py +0 -0
- {arkparser-0.4.6 → arkparser-0.5.0}/tests/test_current_stats.py +0 -0
- {arkparser-0.4.6 → arkparser-0.5.0}/tests/test_data_models.py +0 -0
- {arkparser-0.4.6 → arkparser-0.5.0}/tests/test_export.py +0 -0
- {arkparser-0.4.6 → arkparser-0.5.0}/tests/test_game_objects.py +0 -0
- {arkparser-0.4.6 → arkparser-0.5.0}/tests/test_profile.py +0 -0
- {arkparser-0.4.6 → arkparser-0.5.0}/tests/test_tribe.py +0 -0
- {arkparser-0.4.6 → arkparser-0.5.0}/tests/test_v13_property_layouts.py +0 -0
- {arkparser-0.4.6 → arkparser-0.5.0}/tests/test_version_detection.py +0 -0
- {arkparser-0.4.6 → arkparser-0.5.0}/tests/test_world_save.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: arkparser
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.5.0
|
|
4
4
|
Summary: Pure Python parser for ARK: Survival Evolved and ARK: Survival Ascended save files
|
|
5
5
|
Author: Vertyco
|
|
6
6
|
License-Expression: MIT
|
|
@@ -56,7 +56,7 @@ A pure-Python library for parsing ARK: Survival Evolved (ASE) and ARK: Survival
|
|
|
56
56
|
- **Cloud Inventory / Obelisk**: uploaded creatures, items, cryopod contents
|
|
57
57
|
- **World Saves** (`.ark`): full map state (creatures, structures, items, players)
|
|
58
58
|
- **Dual format**: automatic ASE (v5-12) / ASA (v13-14+, SQLite) detection
|
|
59
|
-
- **Legacy-parity export**: drop-in JSON output matching `ASVExport.exe` schema, plus parser-only extras under
|
|
59
|
+
- **Legacy-parity export**: drop-in JSON output matching `ASVExport.exe` schema, plus parser-only extras under descriptive snake_case keys (no namespace prefix; never overloading a legacy key)
|
|
60
60
|
- **Fast**: pure-Python `BinaryReader` (`int.from_bytes` + `struct.Struct` unpackers, slots-based dataclasses), a 30 MB ASE save (65k objects) loads in ~3s on CPython 3.14
|
|
61
61
|
|
|
62
62
|
## Installation
|
|
@@ -297,11 +297,8 @@ For the richest output, hand `export_players` **both**, assemble a wrapper for e
|
|
|
297
297
|
| `tribe` | legacy | tribe name |
|
|
298
298
|
| `sex` | legacy | `"Female"` / `"Male"` |
|
|
299
299
|
| `lvl` | legacy | `BaseCharacterLevel + ExtraCharacterLevel` |
|
|
300
|
-
| `lat`, `lon` | legacy | `0.0` (profile/cluster files carry no in-world location) |
|
|
301
300
|
| `hp` .. `craft`, `fort` | legacy | `NumberOfLevelUpPointsApplied[i]` (10 visible stats) |
|
|
302
301
|
| `torp`, `temp` | added | same source, indices legacy never emitted |
|
|
303
|
-
| `active` | legacy | last-active datetime; currently `null` (placeholder for parity) |
|
|
304
|
-
| `ccc` | legacy | `"0 0 0"` (no in-world position from profile/cluster source) |
|
|
305
302
|
| `achievements` | legacy | reserved array (currently empty for parity) |
|
|
306
303
|
| `netAddress` | legacy (now populated) | Last client IP ARK persisted (`SavedNetworkAddress` in profile `MyData`). Legacy ASVExport reads the same field (ContentPlayer.cs:157 ASE / :341 ASA). `""` when the profile lacks it (e.g. never-played placeholders). ASA stores a clean IPv4/IPv6 string; some ASE saves store an engine-truncated value (e.g. `"[2001"`) reproduced verbatim, matching legacy. |
|
|
307
304
|
| `steamid`, `dataFile` | legacy | platform net id and `{steamid}.arkprofile` filename |
|
|
@@ -325,12 +322,14 @@ For the richest output, hand `export_players` **both**, assemble a wrapper for e
|
|
|
325
322
|
|
|
326
323
|
#### `ASV_Tribes` schema
|
|
327
324
|
|
|
325
|
+
`ASV_Tribes` (and `ASV_TribeLogs` / `ASV_Players`, which iterate the same list) is a **superset** of the loaded `.arktribe` files, mirroring legacy `ContentContainer`: it seeds two sentinels (`[ASV Unclaimed]` id `2000000000`, `[ASV Abandoned]` id `-2147483648`), adds every file-backed tribe, then synthesizes a stub tribe for each player profile not already in a tribe (`Tribe of <name>`, id = `PlayerDataID`), each distinct structure `TargetingTeam` (`>= 50000`), and each distinct in-world tame `TargetingTeam` — deduped by id. Cross-server tribes that exist **only** in cluster cloud-inventory files (no map presence) are not synthesized.
|
|
326
|
+
|
|
328
327
|
| Field | Origin | Source / formula |
|
|
329
328
|
|---|---|---|
|
|
330
|
-
| `tribeid` | legacy | `TribeID` / parser `Tribe.tribe_id
|
|
331
|
-
| `tribe` | legacy | tribe name |
|
|
332
|
-
| `players` | legacy | member count |
|
|
333
|
-
| `members` | legacy | list of `{ign, lvl, playerid, playername, steamid}
|
|
329
|
+
| `tribeid` | legacy | `TribeID` / parser `Tribe.tribe_id`, or a synthesized stub/sentinel id (see above) |
|
|
330
|
+
| `tribe` | legacy | tribe name (file `TribeName`; for stubs, `OwnerName`/`TamerString`; or `Tribe of <character>` for a solo) |
|
|
331
|
+
| `players` | legacy | count of players **allocated** to this tribe (profiles whose explicit team / membership / solo id resolves here, plus member back-fill), matching legacy `Players.Count` — not the raw `.arktribe` member count |
|
|
332
|
+
| `members` | legacy | list of `{ign, lvl, playerid, playername, steamid}` built from the allocated players. `lvl` and `steamid` populate from the matching `.arkprofile`; members with no profile (back-filled from the tribe's member list) carry `lvl=0`, `steamid=""`. |
|
|
334
333
|
| `tames`, `structures` | legacy | counts derived from `WorldSave` (creatures + structures whose `TargetingTeam` matches) |
|
|
335
334
|
| `uploadedTames` | legacy | reserved (currently `0`) |
|
|
336
335
|
| `active` | legacy | ISO 8601 datetime of the most recent tribe log entry, converted from in-game "Day N, HH:MM:SS" via the save's anchor. `null` when no parseable log entries or anchors are missing. |
|
|
@@ -352,9 +351,9 @@ For the richest output, hand `export_players` **both**, assemble a wrapper for e
|
|
|
352
351
|
| Field | Origin | Source / formula |
|
|
353
352
|
|---|---|---|
|
|
354
353
|
| `id` | added | `GameObject.id`, internal numeric identifier. Cross-references the values in other records' `linked_structures` / `saddle_structures` / `attached_dino_id` lists. |
|
|
355
|
-
| `tribeid` | legacy | `TargetingTeam` |
|
|
356
|
-
| `tribe` | legacy | `OwnerName` |
|
|
357
|
-
| `struct` | legacy | `GameObject.class_name` |
|
|
354
|
+
| `tribeid` | legacy | `TargetingTeam` for player-owned structures (`>= 50000`). Unowned structures fall to the synthetic `[ASV Abandoned]` tribe id `-2147483648` (legacy `int.MinValue`), mirroring `ContentContainer.cs`. |
|
|
355
|
+
| `tribe` | legacy | resolved owning-tribe name: the loaded `.arktribe` `TribeName`, else the structure's `OwnerName` / `TamerString`; `"[ASV Abandoned]"` for unowned. (Legacy emits the resolved tribe name, not the raw `OwnerName`.) |
|
|
356
|
+
| `struct` | legacy | `GameObject.class_name`. Unowned map elements / crates / debug actors (`Button_*`, `*Vein_*`, `*Nest_*`, `*Beaver*`, `BeeHive_C`, `ArtifactCrate_*`, `TributeTerminal_*`, `SupplyCrate_*`) are excluded — surfaced via `ASV_MapStructures` instead, matching legacy's abandoned-structure filter. |
|
|
358
357
|
| `name` | legacy | `BoxName` (empty when it matches the class name, mirroring legacy's no-rename strip) |
|
|
359
358
|
| `locked` | legacy | `bIsPinLocked` or `bIsLocked` |
|
|
360
359
|
| `created` | legacy (richer) | ISO 8601 datetime with the local TZ of the parser machine, computed `save.file_mtime + (OriginalCreationTime - game_time)` (mirrors legacy `ContentContainer.GetApproxDateTimeOf`). `null` when the anchors are missing. |
|
|
@@ -362,7 +361,6 @@ For the richest output, hand `export_players` **both**, assemble a wrapper for e
|
|
|
362
361
|
| `lat`, `lon`, `ccc` | legacy | location via `MapConfig`, **rounded to 2 decimals** (parser-only nicety, not legacy parity) |
|
|
363
362
|
| `isSwitchedOn` | legacy | `bContainerActivated`, emitted only when the structure is powered (`bIsPowered` or `bHasFuel`); omitted otherwise. Mirrors legacy `ContentStructure.cs` / `ContentPack.cs` (`IsSwitchedOn.HasValue`). |
|
|
364
363
|
| `decay_reset` | added | `bHasResetDecayTime` |
|
|
365
|
-
| `last_ally_in_range_seconds` | added | raw `LastInAllyRangeTime` / `LastInAllyRangeTimeSerialized` / `LastInAllyRangeSerialized` (in-game seconds, float) |
|
|
366
364
|
| `last_ally_in_range` | added | ISO 8601 datetime with local TZ. `null` when the save lacks the anchors. |
|
|
367
365
|
| `painting_id` | added | `UniquePaintingId` |
|
|
368
366
|
| `feeding_inclusions` | added | `FeedingDinoList` class names when `DinoFeedingListType == 1` (ASA feeding troughs) |
|
|
@@ -22,7 +22,7 @@ A pure-Python library for parsing ARK: Survival Evolved (ASE) and ARK: Survival
|
|
|
22
22
|
- **Cloud Inventory / Obelisk**: uploaded creatures, items, cryopod contents
|
|
23
23
|
- **World Saves** (`.ark`): full map state (creatures, structures, items, players)
|
|
24
24
|
- **Dual format**: automatic ASE (v5-12) / ASA (v13-14+, SQLite) detection
|
|
25
|
-
- **Legacy-parity export**: drop-in JSON output matching `ASVExport.exe` schema, plus parser-only extras under
|
|
25
|
+
- **Legacy-parity export**: drop-in JSON output matching `ASVExport.exe` schema, plus parser-only extras under descriptive snake_case keys (no namespace prefix; never overloading a legacy key)
|
|
26
26
|
- **Fast**: pure-Python `BinaryReader` (`int.from_bytes` + `struct.Struct` unpackers, slots-based dataclasses), a 30 MB ASE save (65k objects) loads in ~3s on CPython 3.14
|
|
27
27
|
|
|
28
28
|
## Installation
|
|
@@ -263,11 +263,8 @@ For the richest output, hand `export_players` **both**, assemble a wrapper for e
|
|
|
263
263
|
| `tribe` | legacy | tribe name |
|
|
264
264
|
| `sex` | legacy | `"Female"` / `"Male"` |
|
|
265
265
|
| `lvl` | legacy | `BaseCharacterLevel + ExtraCharacterLevel` |
|
|
266
|
-
| `lat`, `lon` | legacy | `0.0` (profile/cluster files carry no in-world location) |
|
|
267
266
|
| `hp` .. `craft`, `fort` | legacy | `NumberOfLevelUpPointsApplied[i]` (10 visible stats) |
|
|
268
267
|
| `torp`, `temp` | added | same source, indices legacy never emitted |
|
|
269
|
-
| `active` | legacy | last-active datetime; currently `null` (placeholder for parity) |
|
|
270
|
-
| `ccc` | legacy | `"0 0 0"` (no in-world position from profile/cluster source) |
|
|
271
268
|
| `achievements` | legacy | reserved array (currently empty for parity) |
|
|
272
269
|
| `netAddress` | legacy (now populated) | Last client IP ARK persisted (`SavedNetworkAddress` in profile `MyData`). Legacy ASVExport reads the same field (ContentPlayer.cs:157 ASE / :341 ASA). `""` when the profile lacks it (e.g. never-played placeholders). ASA stores a clean IPv4/IPv6 string; some ASE saves store an engine-truncated value (e.g. `"[2001"`) reproduced verbatim, matching legacy. |
|
|
273
270
|
| `steamid`, `dataFile` | legacy | platform net id and `{steamid}.arkprofile` filename |
|
|
@@ -291,12 +288,14 @@ For the richest output, hand `export_players` **both**, assemble a wrapper for e
|
|
|
291
288
|
|
|
292
289
|
#### `ASV_Tribes` schema
|
|
293
290
|
|
|
291
|
+
`ASV_Tribes` (and `ASV_TribeLogs` / `ASV_Players`, which iterate the same list) is a **superset** of the loaded `.arktribe` files, mirroring legacy `ContentContainer`: it seeds two sentinels (`[ASV Unclaimed]` id `2000000000`, `[ASV Abandoned]` id `-2147483648`), adds every file-backed tribe, then synthesizes a stub tribe for each player profile not already in a tribe (`Tribe of <name>`, id = `PlayerDataID`), each distinct structure `TargetingTeam` (`>= 50000`), and each distinct in-world tame `TargetingTeam` — deduped by id. Cross-server tribes that exist **only** in cluster cloud-inventory files (no map presence) are not synthesized.
|
|
292
|
+
|
|
294
293
|
| Field | Origin | Source / formula |
|
|
295
294
|
|---|---|---|
|
|
296
|
-
| `tribeid` | legacy | `TribeID` / parser `Tribe.tribe_id
|
|
297
|
-
| `tribe` | legacy | tribe name |
|
|
298
|
-
| `players` | legacy | member count |
|
|
299
|
-
| `members` | legacy | list of `{ign, lvl, playerid, playername, steamid}
|
|
295
|
+
| `tribeid` | legacy | `TribeID` / parser `Tribe.tribe_id`, or a synthesized stub/sentinel id (see above) |
|
|
296
|
+
| `tribe` | legacy | tribe name (file `TribeName`; for stubs, `OwnerName`/`TamerString`; or `Tribe of <character>` for a solo) |
|
|
297
|
+
| `players` | legacy | count of players **allocated** to this tribe (profiles whose explicit team / membership / solo id resolves here, plus member back-fill), matching legacy `Players.Count` — not the raw `.arktribe` member count |
|
|
298
|
+
| `members` | legacy | list of `{ign, lvl, playerid, playername, steamid}` built from the allocated players. `lvl` and `steamid` populate from the matching `.arkprofile`; members with no profile (back-filled from the tribe's member list) carry `lvl=0`, `steamid=""`. |
|
|
300
299
|
| `tames`, `structures` | legacy | counts derived from `WorldSave` (creatures + structures whose `TargetingTeam` matches) |
|
|
301
300
|
| `uploadedTames` | legacy | reserved (currently `0`) |
|
|
302
301
|
| `active` | legacy | ISO 8601 datetime of the most recent tribe log entry, converted from in-game "Day N, HH:MM:SS" via the save's anchor. `null` when no parseable log entries or anchors are missing. |
|
|
@@ -318,9 +317,9 @@ For the richest output, hand `export_players` **both**, assemble a wrapper for e
|
|
|
318
317
|
| Field | Origin | Source / formula |
|
|
319
318
|
|---|---|---|
|
|
320
319
|
| `id` | added | `GameObject.id`, internal numeric identifier. Cross-references the values in other records' `linked_structures` / `saddle_structures` / `attached_dino_id` lists. |
|
|
321
|
-
| `tribeid` | legacy | `TargetingTeam` |
|
|
322
|
-
| `tribe` | legacy | `OwnerName` |
|
|
323
|
-
| `struct` | legacy | `GameObject.class_name` |
|
|
320
|
+
| `tribeid` | legacy | `TargetingTeam` for player-owned structures (`>= 50000`). Unowned structures fall to the synthetic `[ASV Abandoned]` tribe id `-2147483648` (legacy `int.MinValue`), mirroring `ContentContainer.cs`. |
|
|
321
|
+
| `tribe` | legacy | resolved owning-tribe name: the loaded `.arktribe` `TribeName`, else the structure's `OwnerName` / `TamerString`; `"[ASV Abandoned]"` for unowned. (Legacy emits the resolved tribe name, not the raw `OwnerName`.) |
|
|
322
|
+
| `struct` | legacy | `GameObject.class_name`. Unowned map elements / crates / debug actors (`Button_*`, `*Vein_*`, `*Nest_*`, `*Beaver*`, `BeeHive_C`, `ArtifactCrate_*`, `TributeTerminal_*`, `SupplyCrate_*`) are excluded — surfaced via `ASV_MapStructures` instead, matching legacy's abandoned-structure filter. |
|
|
324
323
|
| `name` | legacy | `BoxName` (empty when it matches the class name, mirroring legacy's no-rename strip) |
|
|
325
324
|
| `locked` | legacy | `bIsPinLocked` or `bIsLocked` |
|
|
326
325
|
| `created` | legacy (richer) | ISO 8601 datetime with the local TZ of the parser machine, computed `save.file_mtime + (OriginalCreationTime - game_time)` (mirrors legacy `ContentContainer.GetApproxDateTimeOf`). `null` when the anchors are missing. |
|
|
@@ -328,7 +327,6 @@ For the richest output, hand `export_players` **both**, assemble a wrapper for e
|
|
|
328
327
|
| `lat`, `lon`, `ccc` | legacy | location via `MapConfig`, **rounded to 2 decimals** (parser-only nicety, not legacy parity) |
|
|
329
328
|
| `isSwitchedOn` | legacy | `bContainerActivated`, emitted only when the structure is powered (`bIsPowered` or `bHasFuel`); omitted otherwise. Mirrors legacy `ContentStructure.cs` / `ContentPack.cs` (`IsSwitchedOn.HasValue`). |
|
|
330
329
|
| `decay_reset` | added | `bHasResetDecayTime` |
|
|
331
|
-
| `last_ally_in_range_seconds` | added | raw `LastInAllyRangeTime` / `LastInAllyRangeTimeSerialized` / `LastInAllyRangeSerialized` (in-game seconds, float) |
|
|
332
330
|
| `last_ally_in_range` | added | ISO 8601 datetime with local TZ. `null` when the save lacks the anchors. |
|
|
333
331
|
| `painting_id` | added | `UniquePaintingId` |
|
|
334
332
|
| `feeding_inclusions` | added | `FeedingDinoList` class names when `DinoFeedingListType == 1` (ASA feeding troughs) |
|
|
@@ -364,6 +364,14 @@ class CryopodCreature:
|
|
|
364
364
|
props_dict[key] = p.value
|
|
365
365
|
obj["properties"] = props_dict
|
|
366
366
|
except Exception:
|
|
367
|
+
# Don't let one drifted object silently zero out: log it so
|
|
368
|
+
# cryopod decode regressions surface instead of returning
|
|
369
|
+
# empty stats with no diagnostic (was a blind swallow).
|
|
370
|
+
logger.debug(
|
|
371
|
+
"Failed to parse cryopod object properties at offset %s",
|
|
372
|
+
obj.get("props_offset"),
|
|
373
|
+
exc_info=True,
|
|
374
|
+
)
|
|
367
375
|
obj["properties"] = {}
|
|
368
376
|
|
|
369
377
|
# Find creature object (first one) and status component
|