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.
- {arkparser-0.7.3 → arkparser-0.7.4}/PKG-INFO +1 -1
- {arkparser-0.7.3 → arkparser-0.7.4}/arkparser/export.py +15 -5
- {arkparser-0.7.3 → arkparser-0.7.4}/arkparser.egg-info/PKG-INFO +1 -1
- {arkparser-0.7.3 → arkparser-0.7.4}/pyproject.toml +1 -1
- {arkparser-0.7.3 → arkparser-0.7.4}/tests/test_export.py +34 -0
- {arkparser-0.7.3 → arkparser-0.7.4}/LICENSE +0 -0
- {arkparser-0.7.3 → arkparser-0.7.4}/README.md +0 -0
- {arkparser-0.7.3 → arkparser-0.7.4}/arkparser/__init__.py +0 -0
- {arkparser-0.7.3 → arkparser-0.7.4}/arkparser/common/__init__.py +0 -0
- {arkparser-0.7.3 → arkparser-0.7.4}/arkparser/common/binary_reader.py +0 -0
- {arkparser-0.7.3 → arkparser-0.7.4}/arkparser/common/exceptions.py +0 -0
- {arkparser-0.7.3 → arkparser-0.7.4}/arkparser/common/map_config.py +0 -0
- {arkparser-0.7.3 → arkparser-0.7.4}/arkparser/common/normalization.py +0 -0
- {arkparser-0.7.3 → arkparser-0.7.4}/arkparser/common/types.py +0 -0
- {arkparser-0.7.3 → arkparser-0.7.4}/arkparser/common/version_detection.py +0 -0
- {arkparser-0.7.3 → arkparser-0.7.4}/arkparser/data_models.py +0 -0
- {arkparser-0.7.3 → arkparser-0.7.4}/arkparser/files/__init__.py +0 -0
- {arkparser-0.7.3 → arkparser-0.7.4}/arkparser/files/base.py +0 -0
- {arkparser-0.7.3 → arkparser-0.7.4}/arkparser/files/cloud_inventory.py +0 -0
- {arkparser-0.7.3 → arkparser-0.7.4}/arkparser/files/profile.py +0 -0
- {arkparser-0.7.3 → arkparser-0.7.4}/arkparser/files/tribe.py +0 -0
- {arkparser-0.7.3 → arkparser-0.7.4}/arkparser/files/world_save.py +0 -0
- {arkparser-0.7.3 → arkparser-0.7.4}/arkparser/game_objects/__init__.py +0 -0
- {arkparser-0.7.3 → arkparser-0.7.4}/arkparser/game_objects/container.py +0 -0
- {arkparser-0.7.3 → arkparser-0.7.4}/arkparser/game_objects/game_object.py +0 -0
- {arkparser-0.7.3 → arkparser-0.7.4}/arkparser/game_objects/location.py +0 -0
- {arkparser-0.7.3 → arkparser-0.7.4}/arkparser/properties/__init__.py +0 -0
- {arkparser-0.7.3 → arkparser-0.7.4}/arkparser/properties/base.py +0 -0
- {arkparser-0.7.3 → arkparser-0.7.4}/arkparser/properties/byte_property.py +0 -0
- {arkparser-0.7.3 → arkparser-0.7.4}/arkparser/properties/compound.py +0 -0
- {arkparser-0.7.3 → arkparser-0.7.4}/arkparser/properties/primitives.py +0 -0
- {arkparser-0.7.3 → arkparser-0.7.4}/arkparser/properties/registry.py +0 -0
- {arkparser-0.7.3 → arkparser-0.7.4}/arkparser/structs/__init__.py +0 -0
- {arkparser-0.7.3 → arkparser-0.7.4}/arkparser/structs/base.py +0 -0
- {arkparser-0.7.3 → arkparser-0.7.4}/arkparser/structs/colors.py +0 -0
- {arkparser-0.7.3 → arkparser-0.7.4}/arkparser/structs/misc.py +0 -0
- {arkparser-0.7.3 → arkparser-0.7.4}/arkparser/structs/property_list.py +0 -0
- {arkparser-0.7.3 → arkparser-0.7.4}/arkparser/structs/registry.py +0 -0
- {arkparser-0.7.3 → arkparser-0.7.4}/arkparser/structs/vectors.py +0 -0
- {arkparser-0.7.3 → arkparser-0.7.4}/arkparser.egg-info/SOURCES.txt +0 -0
- {arkparser-0.7.3 → arkparser-0.7.4}/arkparser.egg-info/dependency_links.txt +0 -0
- {arkparser-0.7.3 → arkparser-0.7.4}/arkparser.egg-info/requires.txt +0 -0
- {arkparser-0.7.3 → arkparser-0.7.4}/arkparser.egg-info/top_level.txt +0 -0
- {arkparser-0.7.3 → arkparser-0.7.4}/setup.cfg +0 -0
- {arkparser-0.7.3 → arkparser-0.7.4}/tests/test_asa_header_position.py +0 -0
- {arkparser-0.7.3 → arkparser-0.7.4}/tests/test_asa_name_table.py +0 -0
- {arkparser-0.7.3 → arkparser-0.7.4}/tests/test_asa_profile_drop.py +0 -0
- {arkparser-0.7.3 → arkparser-0.7.4}/tests/test_ase_cluster_drift.py +0 -0
- {arkparser-0.7.3 → arkparser-0.7.4}/tests/test_binary_reader.py +0 -0
- {arkparser-0.7.3 → arkparser-0.7.4}/tests/test_binary_reader_layouts.py +0 -0
- {arkparser-0.7.3 → arkparser-0.7.4}/tests/test_cloud_export.py +0 -0
- {arkparser-0.7.3 → arkparser-0.7.4}/tests/test_cloud_inventory.py +0 -0
- {arkparser-0.7.3 → arkparser-0.7.4}/tests/test_cryopod_export.py +0 -0
- {arkparser-0.7.3 → arkparser-0.7.4}/tests/test_current_stats.py +0 -0
- {arkparser-0.7.3 → arkparser-0.7.4}/tests/test_data_models.py +0 -0
- {arkparser-0.7.3 → arkparser-0.7.4}/tests/test_game_objects.py +0 -0
- {arkparser-0.7.3 → arkparser-0.7.4}/tests/test_golden_exports.py +0 -0
- {arkparser-0.7.3 → arkparser-0.7.4}/tests/test_lazy_properties.py +0 -0
- {arkparser-0.7.3 → arkparser-0.7.4}/tests/test_profile.py +0 -0
- {arkparser-0.7.3 → arkparser-0.7.4}/tests/test_review_fixes.py +0 -0
- {arkparser-0.7.3 → arkparser-0.7.4}/tests/test_tribe.py +0 -0
- {arkparser-0.7.3 → arkparser-0.7.4}/tests/test_v13_property_layouts.py +0 -0
- {arkparser-0.7.3 → arkparser-0.7.4}/tests/test_version_detection.py +0 -0
- {arkparser-0.7.3 → arkparser-0.7.4}/tests/test_world_save.py +0 -0
|
@@ -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
|
-
|
|
2707
|
-
the player
|
|
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(
|
|
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)
|
|
@@ -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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|