arkparser 0.7.3__tar.gz → 0.7.4__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.
Files changed (64) hide show
  1. {arkparser-0.7.3 → arkparser-0.7.4}/PKG-INFO +1 -1
  2. {arkparser-0.7.3 → arkparser-0.7.4}/arkparser/export.py +15 -5
  3. {arkparser-0.7.3 → arkparser-0.7.4}/arkparser.egg-info/PKG-INFO +1 -1
  4. {arkparser-0.7.3 → arkparser-0.7.4}/pyproject.toml +1 -1
  5. {arkparser-0.7.3 → arkparser-0.7.4}/tests/test_export.py +34 -0
  6. {arkparser-0.7.3 → arkparser-0.7.4}/LICENSE +0 -0
  7. {arkparser-0.7.3 → arkparser-0.7.4}/README.md +0 -0
  8. {arkparser-0.7.3 → arkparser-0.7.4}/arkparser/__init__.py +0 -0
  9. {arkparser-0.7.3 → arkparser-0.7.4}/arkparser/common/__init__.py +0 -0
  10. {arkparser-0.7.3 → arkparser-0.7.4}/arkparser/common/binary_reader.py +0 -0
  11. {arkparser-0.7.3 → arkparser-0.7.4}/arkparser/common/exceptions.py +0 -0
  12. {arkparser-0.7.3 → arkparser-0.7.4}/arkparser/common/map_config.py +0 -0
  13. {arkparser-0.7.3 → arkparser-0.7.4}/arkparser/common/normalization.py +0 -0
  14. {arkparser-0.7.3 → arkparser-0.7.4}/arkparser/common/types.py +0 -0
  15. {arkparser-0.7.3 → arkparser-0.7.4}/arkparser/common/version_detection.py +0 -0
  16. {arkparser-0.7.3 → arkparser-0.7.4}/arkparser/data_models.py +0 -0
  17. {arkparser-0.7.3 → arkparser-0.7.4}/arkparser/files/__init__.py +0 -0
  18. {arkparser-0.7.3 → arkparser-0.7.4}/arkparser/files/base.py +0 -0
  19. {arkparser-0.7.3 → arkparser-0.7.4}/arkparser/files/cloud_inventory.py +0 -0
  20. {arkparser-0.7.3 → arkparser-0.7.4}/arkparser/files/profile.py +0 -0
  21. {arkparser-0.7.3 → arkparser-0.7.4}/arkparser/files/tribe.py +0 -0
  22. {arkparser-0.7.3 → arkparser-0.7.4}/arkparser/files/world_save.py +0 -0
  23. {arkparser-0.7.3 → arkparser-0.7.4}/arkparser/game_objects/__init__.py +0 -0
  24. {arkparser-0.7.3 → arkparser-0.7.4}/arkparser/game_objects/container.py +0 -0
  25. {arkparser-0.7.3 → arkparser-0.7.4}/arkparser/game_objects/game_object.py +0 -0
  26. {arkparser-0.7.3 → arkparser-0.7.4}/arkparser/game_objects/location.py +0 -0
  27. {arkparser-0.7.3 → arkparser-0.7.4}/arkparser/properties/__init__.py +0 -0
  28. {arkparser-0.7.3 → arkparser-0.7.4}/arkparser/properties/base.py +0 -0
  29. {arkparser-0.7.3 → arkparser-0.7.4}/arkparser/properties/byte_property.py +0 -0
  30. {arkparser-0.7.3 → arkparser-0.7.4}/arkparser/properties/compound.py +0 -0
  31. {arkparser-0.7.3 → arkparser-0.7.4}/arkparser/properties/primitives.py +0 -0
  32. {arkparser-0.7.3 → arkparser-0.7.4}/arkparser/properties/registry.py +0 -0
  33. {arkparser-0.7.3 → arkparser-0.7.4}/arkparser/structs/__init__.py +0 -0
  34. {arkparser-0.7.3 → arkparser-0.7.4}/arkparser/structs/base.py +0 -0
  35. {arkparser-0.7.3 → arkparser-0.7.4}/arkparser/structs/colors.py +0 -0
  36. {arkparser-0.7.3 → arkparser-0.7.4}/arkparser/structs/misc.py +0 -0
  37. {arkparser-0.7.3 → arkparser-0.7.4}/arkparser/structs/property_list.py +0 -0
  38. {arkparser-0.7.3 → arkparser-0.7.4}/arkparser/structs/registry.py +0 -0
  39. {arkparser-0.7.3 → arkparser-0.7.4}/arkparser/structs/vectors.py +0 -0
  40. {arkparser-0.7.3 → arkparser-0.7.4}/arkparser.egg-info/SOURCES.txt +0 -0
  41. {arkparser-0.7.3 → arkparser-0.7.4}/arkparser.egg-info/dependency_links.txt +0 -0
  42. {arkparser-0.7.3 → arkparser-0.7.4}/arkparser.egg-info/requires.txt +0 -0
  43. {arkparser-0.7.3 → arkparser-0.7.4}/arkparser.egg-info/top_level.txt +0 -0
  44. {arkparser-0.7.3 → arkparser-0.7.4}/setup.cfg +0 -0
  45. {arkparser-0.7.3 → arkparser-0.7.4}/tests/test_asa_header_position.py +0 -0
  46. {arkparser-0.7.3 → arkparser-0.7.4}/tests/test_asa_name_table.py +0 -0
  47. {arkparser-0.7.3 → arkparser-0.7.4}/tests/test_asa_profile_drop.py +0 -0
  48. {arkparser-0.7.3 → arkparser-0.7.4}/tests/test_ase_cluster_drift.py +0 -0
  49. {arkparser-0.7.3 → arkparser-0.7.4}/tests/test_binary_reader.py +0 -0
  50. {arkparser-0.7.3 → arkparser-0.7.4}/tests/test_binary_reader_layouts.py +0 -0
  51. {arkparser-0.7.3 → arkparser-0.7.4}/tests/test_cloud_export.py +0 -0
  52. {arkparser-0.7.3 → arkparser-0.7.4}/tests/test_cloud_inventory.py +0 -0
  53. {arkparser-0.7.3 → arkparser-0.7.4}/tests/test_cryopod_export.py +0 -0
  54. {arkparser-0.7.3 → arkparser-0.7.4}/tests/test_current_stats.py +0 -0
  55. {arkparser-0.7.3 → arkparser-0.7.4}/tests/test_data_models.py +0 -0
  56. {arkparser-0.7.3 → arkparser-0.7.4}/tests/test_game_objects.py +0 -0
  57. {arkparser-0.7.3 → arkparser-0.7.4}/tests/test_golden_exports.py +0 -0
  58. {arkparser-0.7.3 → arkparser-0.7.4}/tests/test_lazy_properties.py +0 -0
  59. {arkparser-0.7.3 → arkparser-0.7.4}/tests/test_profile.py +0 -0
  60. {arkparser-0.7.3 → arkparser-0.7.4}/tests/test_review_fixes.py +0 -0
  61. {arkparser-0.7.3 → arkparser-0.7.4}/tests/test_tribe.py +0 -0
  62. {arkparser-0.7.3 → arkparser-0.7.4}/tests/test_v13_property_layouts.py +0 -0
  63. {arkparser-0.7.3 → arkparser-0.7.4}/tests/test_version_detection.py +0 -0
  64. {arkparser-0.7.3 → arkparser-0.7.4}/tests/test_world_save.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: arkparser
3
- Version: 0.7.3
3
+ Version: 0.7.4
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
@@ -2699,12 +2699,18 @@ def _allocate_profiles(
2699
2699
  profiles: list[Profile],
2700
2700
  file_tribe_ids: set[int],
2701
2701
  member_index: dict[int, int],
2702
+ tribe_members: dict[int, set[int]],
2702
2703
  ) -> tuple[dict[int, int], dict[int, list[int]]]:
2703
2704
  """Allocate each profile to one tribe id (ContentContainer.cs:760-790).
2704
2705
 
2705
- Priority: explicit profile tribe id when it names an existing tribe ->
2706
- the tribe whose member list contains the player -> a solo tribe keyed on
2707
- the player id. Returns ``(player_id -> tribe_id, tribe_id -> [player_id])``.
2706
+ Priority: explicit profile tribe id when it names an existing tribe AND
2707
+ that tribe's member roster still lists the player -> the tribe whose
2708
+ member list contains the player -> a solo tribe keyed on the player id.
2709
+ The roster check diverges from live legacy on purpose: the game leaves
2710
+ ``TribeID`` stale in the profile of a member removed while offline, and
2711
+ honoring it unconditionally re-attaches ex-members (rosters then exceed
2712
+ the server tribe cap). The ``.arktribe`` member list is authoritative.
2713
+ Returns ``(player_id -> tribe_id, tribe_id -> [player_id])``.
2708
2714
  """
2709
2715
  profile_tribeid: dict[int, int] = {}
2710
2716
  players_by_tribe: dict[int, list[int]] = {}
@@ -2713,7 +2719,7 @@ def _allocate_profiles(
2713
2719
  if not pid:
2714
2720
  continue
2715
2721
  explicit = _int(prof.raw_tribe_id)
2716
- if explicit and explicit in file_tribe_ids:
2722
+ if explicit and explicit in file_tribe_ids and pid in tribe_members.get(explicit, ()):
2717
2723
  target = explicit
2718
2724
  elif pid in member_index:
2719
2725
  target = member_index[pid]
@@ -2754,6 +2760,7 @@ def _assemble_tribes(save: t.Any) -> dict[str, t.Any]:
2754
2760
  logs: dict[int, list[str]] = {}
2755
2761
  member_index: dict[int, int] = {}
2756
2762
  member_name: dict[int, str] = {}
2763
+ tribe_members: dict[int, set[int]] = {}
2757
2764
  order: list[int] = []
2758
2765
  for sid, sname in (
2759
2766
  (_UNCLAIMED_TRIBE_ID, "[ASV Unclaimed]"),
@@ -2773,12 +2780,15 @@ def _assemble_tribes(save: t.Any) -> dict[str, t.Any]:
2773
2780
  logs[tid] = log_entries
2774
2781
  for pid, mname in members:
2775
2782
  member_index.setdefault(pid, tid)
2783
+ tribe_members.setdefault(tid, set()).add(pid)
2776
2784
  if mname:
2777
2785
  member_name.setdefault(pid, mname)
2778
2786
  file_tribe_ids = set(rich) | {_UNCLAIMED_TRIBE_ID, _ABANDONED_TRIBE_ID}
2779
2787
  profiles = _profile_entries(save)
2780
2788
  profile_active = _profile_actives(profiles, save)
2781
- profile_tribeid, players_by_tribe = _allocate_profiles(profiles, file_tribe_ids, member_index)
2789
+ profile_tribeid, players_by_tribe = _allocate_profiles(
2790
+ profiles, file_tribe_ids, member_index, tribe_members
2791
+ )
2782
2792
  for prof in profiles:
2783
2793
  pid = _int(prof.player_id)
2784
2794
  target = profile_tribeid.get(pid)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: arkparser
3
- Version: 0.7.3
3
+ Version: 0.7.4
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
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "arkparser"
7
- version = "0.7.3"
7
+ version = "0.7.4"
8
8
  description = "Pure Python parser for ARK: Survival Evolved and ARK: Survival Ascended save files"
9
9
  readme = "README.md"
10
10
  license = "MIT"
@@ -3,6 +3,7 @@
3
3
  import datetime as dt
4
4
  import os
5
5
  import shutil
6
+ import typing as t
6
7
  from pathlib import Path
7
8
 
8
9
  import pytest
@@ -157,6 +158,39 @@ def test_export_tribes_active_never_future() -> None:
157
158
  assert rec["active"] is None
158
159
 
159
160
 
161
+ def test_allocate_profiles_ignores_stale_profile_tribe_id() -> None:
162
+ """A profile whose TribeID names a tribe that no longer lists the player
163
+ in its member roster must NOT be allocated to that tribe (the game leaves
164
+ TribeID stale in profiles of members removed while offline). It falls
165
+ through to a solo tribe keyed on the player id."""
166
+ from arkparser.export import _allocate_profiles
167
+
168
+ current = t.cast(Profile, type("P", (), {"player_id": 1, "raw_tribe_id": 555})())
169
+ stale = t.cast(Profile, type("P", (), {"player_id": 987, "raw_tribe_id": 555})())
170
+ member_index = {1: 555, 2: 555}
171
+ tribe_members = {555: {1, 2}}
172
+ profile_tribeid, players_by_tribe = _allocate_profiles(
173
+ [current, stale], {555}, member_index, tribe_members
174
+ )
175
+ assert profile_tribeid[1] == 555
176
+ assert profile_tribeid[987] == 987
177
+ assert players_by_tribe[555] == [1]
178
+ assert players_by_tribe[987] == [987]
179
+
180
+
181
+ def test_allocate_profiles_member_list_still_wins_without_explicit_id() -> None:
182
+ """A profile with no usable TribeID but present in a tribe's roster is
183
+ still allocated to that tribe via the member-list fallback."""
184
+ from arkparser.export import _allocate_profiles
185
+
186
+ prof = t.cast(Profile, type("P", (), {"player_id": 2, "raw_tribe_id": 0})())
187
+ profile_tribeid, players_by_tribe = _allocate_profiles(
188
+ [prof], {555}, {2: 555}, {555: {2}}
189
+ )
190
+ assert profile_tribeid[2] == 555
191
+ assert players_by_tribe[555] == [2]
192
+
193
+
160
194
  def test_export_tribe_logs_uses_tribe_parser(ase_tribe_path: Path) -> None:
161
195
  tribe = Tribe.load(ase_tribe_path)
162
196
  exported = export_tribe_logs(type("Holder", (), {"tribes": [tribe]})())
File without changes
File without changes
File without changes
File without changes