Body
arkparser version: 0.7.2
Game: ARK: Survival Ascended (dedicated PvE cluster)
Map tested: custom map ("Astraeos_WP"), but this looks structural, not map-specific
Summary
On my ASA server, WorldSave.iter_cryopod_creatures() returns 0 results even though the server has ~15,000 filled dinoballs. I tracked this down to two separate bugs that stack on top of each other, plus a third, deeper issue once the first two are fixed (cryopodded creatures decode with empty stat dicts).
Bug 1 — CRYOPOD_CLASS_PATTERNS casing mismatch
arkparser/common/types.py:
CRYOPOD_CLASS_PATTERNS: tuple[str, ...] = (
"Cryopod",
"SoulTrap",
"Vivarium",
"DinoBall", # capital B
)
The actual dinoball item class on this ASA server is ItemDinoball_C — lowercase "b". Since the match is a case-sensitive substring check (any(p in cn for p in CRYOPOD_CLASS_PATTERNS)), it never matches. I found 14,975 ItemDinoball_C objects in one save, 0 of which were recognized as cryopod-class items.
Dumping every "cryo"-ish class name in the same save also turned up a few related items, for reference:
14975 ItemDinoball_C (matches "DinoBall"? NO — casing)
195 DinoballStorageBox_C
195 InventoryDinoballStorageBox_C
133 PrimalItem_WeaponEmptyCryopod_C
57 ItemDinoballStack_C
32 ItemDinoballStorageBox_C
31 ItemStructureDinoballDisplayCase_C
22 VivariumV2_CS_C
22 PrimalInventory_Vivarium_CS_C
9 PrimalItemStructure_Vivarium_CS_C
Suggested fix: add "Dinoball" (lowercase b) to CRYOPOD_CLASS_PATTERNS alongside the existing "DinoBall" entry (I left the original as-is in case some ASE save legitimately uses that casing).
Bug 2 — CustomDataName mismatch in iter_cryopod_creatures() / _decode_inventory_cryopod()
Even after fixing bug 1, iter_cryopod_creatures() still returned 0. Both WorldSave.iter_cryopod_creatures() (files/world_save.py) and export.py's _decode_inventory_cryopod() only accept an embedded CustomItemDatas entry when:
entry.get("CustomDataName") != "Dino"
On this ASA server, the entry holding the actual creature data is named DinoballItemData, not "Dino". There's also a sibling entry named DinoballTooltipData (display text only, not the creature blob) that should stay ignored.
Once I widened the check to entry.get("CustomDataName") not in ("Dino", "DinoballItemData"), CryopodCreature.from_asa_cryopod_data(entry) (which was already implemented and already handles this exact strings/floats shape!) successfully decoded name/species/level/colors for every sample I checked.
Suggested fix: accept "DinoballItemData" as an additional valid entry name in both call sites, alongside "Dino".
Combined impact of bugs 1+2
After patching both locally, iter_cryopod_creatures() went from 0 to 14,931 decoded creatures on the same save, correctly attributed to the right tribes via the existing owner-lookup fallback (verified one tribe: 12,491 of its cryopodded dinos correctly resolved to the right tribe id). So the fix itself works well for identity/tribe/name/level/species — this is a big deal for anyone running ASA where cryo is the majority of a tribe's roster (as your own docstring on iter_cryopod_creatures already notes: "2,158 of 2,518 on-map tames on the live SE PvE reference" were in cryo — that's presumably the same class of bug hitting ASE saves too if any of them use similarly-cased item classes).
Bug 3 (unresolved on my end) — stat data (HP/Stam/Melee/etc.) is empty for ASA cryopods
Once decoded via from_asa_cryopod_data(), the resulting CryopodCreature has current_stats={}, max_stats={}, base_stats={}, level_ups_wild={}, level_ups_tamed={} — only identity fields (class_name/name/species/level/colors) come through. For my use case (an ARK server-manager web app) this means cryopodded dinos show up with correct name/tribe/level but 0 for every wild/mutated/tamed stat point.
Investigating further: CustomDataBytes.ByteArrays[0].Bytes (the same blob from_cryopod_bytes() is meant to parse) is not the plain ASE format that function expects. On this ASA server it's:
- 12-byte header: 3x little-endian int32. First value is constant (
1031) across every sample I checked; the second value exactly equals the decompressed payload length; the third is unclear (roughly ~1.3x the decompressed length).
- Followed immediately by a standard zlib stream (
\x78\x9c magic at byte offset 12), which decompresses cleanly with plain zlib.decompress().
The decompressed payload is not in the format from_cryopod_bytes() expects either (no leading GUID + obj_count int32 — reading it that way gives nonsense). It does contain readable, correctly-spelled property/level names like BaseCharacterLevel, NumberOfLevelUpPointsApplied, NumberOfMutationsAppliedTamed, DinoImprintingQuality, DoubleProperty, DinoAncestors, ImprinterPlayerUniqueNetId, plus a full soft-object path (/Game/ASA/Dinos/<Species>/<Species>_Character_BP.<Species>_Character_BP_C) near the front, and what looks like a level/world name and tamer/tribe name as separate strings — so the stat values are clearly present somewhere in there, just in a layout I haven't reverse-engineered (looks structurally closer to the object/property-tag layout used in the cloud/obelisk inventory format than to the plain from_cryopod_bytes() layout, but isn't identical to that either — e.g. the class name here is a full path, not the short name _read_asa_object_header reads).
I didn't want to guess further at an undocumented binary layout without a way to verify correctness against ground truth, so I'm flagging it here rather than shipping a possibly-wrong decoder. Happy to share a redacted/anonymized sample blob if that'd help, since I can't attach live save data directly.
What I changed locally (happy to open a PR instead, if preferred)
arkparser/common/types.py: added "Dinoball" to CRYOPOD_CLASS_PATTERNS.
arkparser/files/world_save.py (iter_cryopod_creatures): accept "DinoballItemData" alongside "Dino" for CustomDataName.
arkparser/export.py (_decode_inventory_cryopod): same CustomDataName widening, for cryopods discovered inside other inventories (cryofridges, vaults, dedicated storage, etc.).
Thanks for the great library — legacy-parity plus the cryopod-recovery feature has been a huge upgrade over the dead ASVExport.exe pipeline for us.
Body
arkparser version: 0.7.2
Game: ARK: Survival Ascended (dedicated PvE cluster)
Map tested: custom map ("Astraeos_WP"), but this looks structural, not map-specific
Summary
On my ASA server,
WorldSave.iter_cryopod_creatures()returns 0 results even though the server has ~15,000 filled dinoballs. I tracked this down to two separate bugs that stack on top of each other, plus a third, deeper issue once the first two are fixed (cryopodded creatures decode with empty stat dicts).Bug 1 —
CRYOPOD_CLASS_PATTERNScasing mismatcharkparser/common/types.py:The actual dinoball item class on this ASA server is
ItemDinoball_C— lowercase "b". Since the match is a case-sensitive substring check (any(p in cn for p in CRYOPOD_CLASS_PATTERNS)), it never matches. I found 14,975ItemDinoball_Cobjects in one save, 0 of which were recognized as cryopod-class items.Dumping every "cryo"-ish class name in the same save also turned up a few related items, for reference:
Suggested fix: add
"Dinoball"(lowercase b) toCRYOPOD_CLASS_PATTERNSalongside the existing"DinoBall"entry (I left the original as-is in case some ASE save legitimately uses that casing).Bug 2 —
CustomDataNamemismatch initer_cryopod_creatures()/_decode_inventory_cryopod()Even after fixing bug 1,
iter_cryopod_creatures()still returned 0. BothWorldSave.iter_cryopod_creatures()(files/world_save.py) andexport.py's_decode_inventory_cryopod()only accept an embeddedCustomItemDatasentry when:On this ASA server, the entry holding the actual creature data is named
DinoballItemData, not"Dino". There's also a sibling entry namedDinoballTooltipData(display text only, not the creature blob) that should stay ignored.Once I widened the check to
entry.get("CustomDataName") not in ("Dino", "DinoballItemData"),CryopodCreature.from_asa_cryopod_data(entry)(which was already implemented and already handles this exact strings/floats shape!) successfully decoded name/species/level/colors for every sample I checked.Suggested fix: accept
"DinoballItemData"as an additional valid entry name in both call sites, alongside"Dino".Combined impact of bugs 1+2
After patching both locally,
iter_cryopod_creatures()went from 0 to 14,931 decoded creatures on the same save, correctly attributed to the right tribes via the existing owner-lookup fallback (verified one tribe: 12,491 of its cryopodded dinos correctly resolved to the right tribe id). So the fix itself works well for identity/tribe/name/level/species — this is a big deal for anyone running ASA where cryo is the majority of a tribe's roster (as your own docstring oniter_cryopod_creaturesalready notes: "2,158 of 2,518 on-map tames on the live SE PvE reference" were in cryo — that's presumably the same class of bug hitting ASE saves too if any of them use similarly-cased item classes).Bug 3 (unresolved on my end) — stat data (HP/Stam/Melee/etc.) is empty for ASA cryopods
Once decoded via
from_asa_cryopod_data(), the resultingCryopodCreaturehascurrent_stats={},max_stats={},base_stats={},level_ups_wild={},level_ups_tamed={}— only identity fields (class_name/name/species/level/colors) come through. For my use case (an ARK server-manager web app) this means cryopodded dinos show up with correct name/tribe/level but 0 for every wild/mutated/tamed stat point.Investigating further:
CustomDataBytes.ByteArrays[0].Bytes(the same blobfrom_cryopod_bytes()is meant to parse) is not the plain ASE format that function expects. On this ASA server it's:1031) across every sample I checked; the second value exactly equals the decompressed payload length; the third is unclear (roughly ~1.3x the decompressed length).\x78\x9cmagic at byte offset 12), which decompresses cleanly with plainzlib.decompress().The decompressed payload is not in the format
from_cryopod_bytes()expects either (no leading GUID +obj_countint32 — reading it that way gives nonsense). It does contain readable, correctly-spelled property/level names likeBaseCharacterLevel,NumberOfLevelUpPointsApplied,NumberOfMutationsAppliedTamed,DinoImprintingQuality,DoubleProperty,DinoAncestors,ImprinterPlayerUniqueNetId, plus a full soft-object path (/Game/ASA/Dinos/<Species>/<Species>_Character_BP.<Species>_Character_BP_C) near the front, and what looks like a level/world name and tamer/tribe name as separate strings — so the stat values are clearly present somewhere in there, just in a layout I haven't reverse-engineered (looks structurally closer to the object/property-tag layout used in the cloud/obelisk inventory format than to the plainfrom_cryopod_bytes()layout, but isn't identical to that either — e.g. the class name here is a full path, not the short name_read_asa_object_headerreads).I didn't want to guess further at an undocumented binary layout without a way to verify correctness against ground truth, so I'm flagging it here rather than shipping a possibly-wrong decoder. Happy to share a redacted/anonymized sample blob if that'd help, since I can't attach live save data directly.
What I changed locally (happy to open a PR instead, if preferred)
arkparser/common/types.py: added"Dinoball"toCRYOPOD_CLASS_PATTERNS.arkparser/files/world_save.py(iter_cryopod_creatures): accept"DinoballItemData"alongside"Dino"forCustomDataName.arkparser/export.py(_decode_inventory_cryopod): sameCustomDataNamewidening, for cryopods discovered inside other inventories (cryofridges, vaults, dedicated storage, etc.).Thanks for the great library — legacy-parity plus the cryopod-recovery feature has been a huge upgrade over the dead ASVExport.exe pipeline for us.