Skip to content
This repository was archived by the owner on Oct 11, 2026. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions SESSION.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
## PROJET Gw2Analytics — Résumé de session (2026-08-04)

### Objectif
Parité totale du parser avec Elite Insights (EI) v3.26.0.0 sur les logs WvW (0 diff).

### État du travail
- **En cours :** investigation du surcomptage regen sur "Non Squad Player 50" (`20260125-001308`, instanceID 3089, account "Non Squad Player 50", agent 38309).
- Constat probe : notre tracker compte regen actif jusqu'à rel≈50320 (55.845 %) alors que EI l'arrête à rel=40940 (44.757 %). Dernier événement regen à rel=21118.
- **Cause probable identifiée** : les événements `remove_manual` (7 présents sur cet agent) ne sont PAS pris en charge dans `BuffStateTracker.process()` — seuls `apply`, `remove_single`, `remove_all` sont gérés. Les `remove_manual` sont donc ignorés et les stacks ne sont pas retirés → surcomptage.
- EI states : `[[0,0],[2905,1],[40940,0]]`. Règle d'uptime EI : uptime calculé sur événements/états, pas sur présence cumulée simple.

### Next Move
1. Ajouter la gestion de `kind == "remove_manual"` (et vérifier `remove_all`) dans `buff_state.py` — traiter comme un remove de la stack correspondante (par durée/stack_id), façon EI.
2. Ré-échantillonner : `uv run python scripts/ei-parity/ei_diff.py 20260125-001308 --show "buffUptimes"` et comparer inst 3089.
3. Vérifier que les "Non Squad Player" 50/58 passent de 52.2/51.895 → 44.757/43.852.

### Fichiers clés
- `libs/gw2_analytics/src/gw2_analytics/buff_state.py` — ligne ~337 (`remove_single`), ~372 (`remove_all`), ~295 (`apply`) ; **manque `remove_manual`**.
- `libs/gw2_analytics/src/gw2_analytics/ei_compare.py`
- `scripts/ei-parity/ei_diff.py`, `.tooling/ei-out/<stem>_detailed_wvw_kill.json`
- Probes `/tmp/probe_ev.py`, `/tmp/probe_tr.py`

### Probes
- `/tmp/probe_ev.py <stem> <instanceID>` : dumps événements regen ciblant un agent.
- `/tmp/probe_tr.py <stem> <instanceID>` : trace le tracker stack par événement (before/after expirations).
45 changes: 38 additions & 7 deletions libs/gw2_analytics/src/gw2_analytics/buff_state.py
Original file line number Diff line number Diff line change
Expand Up @@ -311,13 +311,24 @@ def process( # noqa: PLR0912, PLR0915
target_tracker.expirations = [expiry for expiry, _, _ in pairs]
target_tracker.stack_ids = [stack_id for _, stack_id, _ in pairs]
target_tracker.healing_scores = [healing for _, _, healing in pairs]
else:
target_tracker.expirations.append(event.duration_ms or None)
target_tracker.stack_ids.append(event.stack_id)
target_tracker.healing_scores.append(
self._healing_by_agent.get(event.source_agent_id, 0)
)
if buff_name == "regeneration" and not target_tracker.no_sort:
elif buff_name == "regeneration":
# EI BuffSimulatorDuration + HealingLogic: capacity-5
# queue, replace the lowest-heal stack on overflow
# (wasting its remaining duration), re-sort by healing
# until the first added_active apply pins no_sort, then
# activate: move the new stack to the front (or replace
# the active stack outright when it has <50 ms left).
new_duration = event.duration_ms or None
new_healing = self._healing_by_agent.get(event.source_agent_id, 0)
if len(target_tracker.expirations) >= _capacity_for(buff_name):
target_tracker.expirations[-1] = new_duration
target_tracker.stack_ids[-1] = event.stack_id
target_tracker.healing_scores[-1] = new_healing
else:
target_tracker.expirations.append(new_duration)
target_tracker.stack_ids.append(event.stack_id)
target_tracker.healing_scores.append(new_healing)
if not target_tracker.no_sort:
pairs = sorted(
zip(
target_tracker.expirations,
Expand All @@ -331,6 +342,26 @@ def process( # noqa: PLR0912, PLR0915
target_tracker.expirations = [expiry for expiry, _, _ in pairs]
target_tracker.stack_ids = [stack_id for _, stack_id, _ in pairs]
target_tracker.healing_scores = [healing for _, _, healing in pairs]
if event.added_active:
target_tracker.no_sort = True
new_index = target_tracker.stack_ids.index(event.stack_id)
expiry = target_tracker.expirations.pop(new_index)
stack_id = target_tracker.stack_ids.pop(new_index)
healing = target_tracker.healing_scores.pop(new_index)
if target_tracker.expirations and (target_tracker.expirations[0] or 0) < 50:
target_tracker.expirations[0] = expiry
target_tracker.stack_ids[0] = stack_id
target_tracker.healing_scores[0] = healing
else:
target_tracker.expirations.insert(0, expiry)
target_tracker.stack_ids.insert(0, stack_id)
target_tracker.healing_scores.insert(0, healing)
else:
target_tracker.expirations.append(event.duration_ms or None)
target_tracker.stack_ids.append(event.stack_id)
target_tracker.healing_scores.append(
self._healing_by_agent.get(event.source_agent_id, 0)
)
del target_tracker.expirations[_capacity_for(buff_name) :]
del target_tracker.stack_ids[_capacity_for(buff_name) :]
del target_tracker.healing_scores[_capacity_for(buff_name) :]
Expand Down
11 changes: 11 additions & 0 deletions libs/gw2_core/src/gw2_core/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -528,6 +528,17 @@ class because the wire shape (time_ms + source/target agent id +
)
stacks: int = Field(..., ge=0, description="Magnitude of the state change.")
stack_id: int = Field(default=0, ge=0, le=0xFFFFFFFF)
added_active: bool = Field(
default=False,
description=(
"arcdps 2025+ ``is_shields`` byte of the raw buff-apply record: "
"1 marks the apply as the currently-active stack. Elite Insights "
"uses this to activate the newly added stack (move to front + "
"``noSort``), which is required for Regeneration parity. Only the "
"arcdps 2025+ wire carries this byte; legacy records default to "
"False."
),
)
kind: Literal["apply", "remove_single", "remove_all", "remove_manual"] = Field(
default="apply",
description=(
Expand Down
1 change: 1 addition & 0 deletions libs/gw2_evtc_parser/src/gw2_evtc_parser/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -1165,6 +1165,7 @@ def parse_events(source: BinaryIO | bytes) -> Iterator[Event]: # noqa: PLR0912,
stacks=1,
stack_id=pad,
kind="apply",
added_active=bool(is_shields),
)
continue
# v0.17.0: a self-inflicted condition tick (src == dst,
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ dev = [
"alembic>=1.13,<2",
"minio>=7.2,<8",
"python-multipart>=0.0.20,<1",
"cryptography>=43.0.1,<49", # v0.10.26 CI: pip-audit → crypt>=48
"cryptography>=50,<51", # v0.10.26 CI: pip-audit → crypt>=48; v0.15.x: CVE-2026-69247/8/9 → >=50

"arq>=0.25,<1",
"redis>=5.0,<8",
Expand Down
Loading
Loading