Over 1000 posts asking for death recap

You can log Target UUID, Damage and Attacker UUID for everything pretty easily but it's impossible to be truly order accurate and it becomes exponentially more taxing the more you try. No combat log has ever been close to accurate unless it was a TCP connection.

Nothing uses TCP outside LAN situations because that protocol in itself is taxing and that's just the connection portion. When you get into really fast logging you run into tick rate problems and I don't mean server to client tick rate. I mean the engine.

Just as it's not hard to create logging. It's also not hard to overload it. The moment you have multiple things trying to resolve within the same tick the engine will fallback to stagger/buffer methods or batch processing and will just spit out nonsense in terms of action order.
"Never trust floating women." -Officer Kirac
"
"
noribin#3026 a écrit :
I say its easy as a dev.

Nope. To get a basic understanding why, ask either Gemini or Claude, "how easy or difficult would it be to create a death log for a complex ARPG like Path of Exile 2?"


How you ask the question matters.
For example:

We want an estimate of a death recap in poe 2. We do not care about 100% exact information. Will this be an easy task?

Short answer: yes—wanting only an estimate makes it easy. You can build a fast, client-side “best guess” death log without touching server code. It won’t be perfect, but it can be useful and lightweight.

What becomes feasible with “estimates”

Client-only ring buffer (2–5 s): record recent incoming damage events (as exposed to the client), DoT ticks, buff/debuff changes, player inputs, and position samples.

Heuristic attribution: pick the most likely killer using rules instead of authoritative calculations.

Confidence scoring: communicate uncertainty (“High” if a single big hit; “Low” if many overlapping sources).

Tiny footprint: 1–3 MB RAM per character, negligible CPU if you sample/coalesce events.

Minimal data you need (client-side)

Time (ms), event type (hit/DoT), damage number(s) if available, damage tags (e.g., Fire/Cold/Physical), source handle/id if available, projectile/melee/ground flags.

Player snapshot every ~100 ms: life/ES, guard/ward, active guard skill (e.g., Molten Shell), flask states, major debuffs (shock, brittle, scorch, exposure).

Context hints the client already knows: map area, party size, latency estimate.

Heuristics that work well in ARPGs

Last-Hit Dominance: If a single hit within the last 250 ms dealt ≥ X% of max EHP (say 40–60%), attribute death to that source. Confidence: High.

Burst Window: If ≥3 hits in 350 ms reduce EHP by ≥70%, attribute to “Burst” and list the top source by damage share. Confidence: Medium.

Sustained DoT: If DoT ticks account for ≥60% of EHP lost over the last 1.5 s, attribute to the latest applied DoT tag (Burning, Poison, Bleed) and its applier if known. Confidence: Medium.

Ground Effects: If the player is inside a harmful ground volume for ≥500 ms and DoT ticks match that element, prefer “Ground (element)” unless a single hit threshold triggers. Confidence: Medium.

Ailment Spike: If a high-impact ailment (Shock ≥X%, Sap, Brittle) appeared ≤500 ms before lethal damage, append “amplified by Shock (X%).”

Guard Drop: If a guard/ward buff ended ≤300 ms before lethal, add “after Guard expired” tag.

Latency Reality Check: If RTT > 100 ms and the lethal hit timestamp is earlier than the client’s last life bar update, downrank confidence by one level.

Simple decision flow (pseudo)
window = events[-2500ms:0]
ehp_lost = sum(window.damage_to_ehp)

if last_big_hit.damage >= 0.5 * max_ehp or last_big_hit.damage >= 0.7 * current_ehp:
cause = last_big_hit.source_tag
confidence = "High"
elif burst_damage_in(350ms) >= 0.7 * max_ehp:
cause = "Burst (" + top_source_in_burst + ")"
confidence = "Medium"
elif dot_share(window) >= 0.6:
cause = latest_applied_dot_type + " DoT"
confidence = "Medium"
elif in_harmful_ground and dot_share(window) >= 0.4:
cause = "Ground (" + element + ")"
confidence = "Medium"
else:
cause = top_damage_source(window) # coalesced
confidence = "Low"

if guard_ended_recently: tags.add("Guard expired")
if shock_present: tags.add("Amplified by Shock")
if rtt>100: downgrade(confidence)

Output schema (compact & readable)
{
"time": "2025-11-06T12:34:56.789Z",
"area": "Crimson Fortress",
"latency_ms": 68,
"summary": {
"cause": "Fire Projectile",
"attribution": "Rare Fanatic Marksman",
"confidence": "Medium",
"pattern": "Burst"
},
"timeline_ms": [
{"t": -820, "event": "Ignite applied", "source": "Marksman"},
{"t": -270, "event": "Projectile Hit", "dmg_est": 1420, "tags": ["Fire","Projectile"]},
{"t": -40, "event": "Ignite tick", "dmg_est": 380},
{"t": 0, "event": "Death"}
],
"player_state": {
"max_ehp": 5100, "ehp_before": 1600, "flasks": {"ruby": "cooldown"},
"buffs": ["Molten Shell (expired -310ms)"], "debuffs": ["Shock 18%"]
},
"contributors": [
{"source": "Fire Projectile", "share": 0.62},
{"source": "Ignite", "share": 0.38}
],
"notes": ["Guard expired 0.31s before death"]
}

How accurate is this in practice?

Often “good enough.” You’ll correctly flag most one-shots and many burst deaths.

Weak on edge cases: overlapping ground DoTs from multiple mobs, hidden monster mods, penetration/crit rolls you can’t see, or desyncy moments.

Mitigation: show confidence, list top alternatives (“Possible: Chaos DoT (low confidence)”).

Performance & implementation tips

Coalesce events: combine repeated ticks from same source each 50–100 ms to cut volume.

Sample inputs at 60–100 Hz, states at 10–20 Hz.

Use IDs, not names, in the buffer; resolve to strings only on display.

Bound memory: cap the ring buffer by time and count (e.g., 2,000 events or 5 s, whichever first).

Debounce spam: ignore micro-hits below 1% max EHP unless inside a burst window.

Nice UX touches

Show a one-line summary (“Killed by Fire Projectile (Rare), burst in 0.33s — Medium confidence”).

Offer “Why?” with a collapsible timeline and tags (Shock, Guard expired, Ground).

Let players copy a JSON blob for bug reports/posts.
+1
"
noribin#3026 a écrit :
wanting only an estimate makes it easy

That is standard for systems at scale. The more error you are willing to accept, the less compute you have to perform. An inaccurate log is easy to produce; an always-accurate log is barely feasible. It's a business decision how error-prone the death log would be allowed to be.

EDIT: Kudos to Xzorn who is explaining practical issues. It's the real-world side of what I'm saying at a high level.
SSF Rise of the Abyssal Ice Strike Invoker, high survivability and fast clear speed. Deprioritize armor and resistances; prioritize crafting a weapon and faster start of ES recharge. Build is shown at level 47, end of Act 3. It tore through Act 3 like butter.

https://poe.ninja/poe2/pob/ba84
Dernière édition par DistributedAutomaton#5739, le 6 nov. 2025 à 11:48:45
Spoiler
"
noribin#3026 a écrit :
"
"
noribin#3026 a écrit :
I say its easy as a dev.

Nope. To get a basic understanding why, ask either Gemini or Claude, "how easy or difficult would it be to create a death log for a complex ARPG like Path of Exile 2?"


How you ask the question matters.
For example:

We want an estimate of a death recap in poe 2. We do not care about 100% exact information. Will this be an easy task?

Short answer: yes—wanting only an estimate makes it easy. You can build a fast, client-side “best guess” death log without touching server code. It won’t be perfect, but it can be useful and lightweight.

What becomes feasible with “estimates”

Client-only ring buffer (2–5 s): record recent incoming damage events (as exposed to the client), DoT ticks, buff/debuff changes, player inputs, and position samples.

Heuristic attribution: pick the most likely killer using rules instead of authoritative calculations.

Confidence scoring: communicate uncertainty (“High” if a single big hit; “Low” if many overlapping sources).

Tiny footprint: 1–3 MB RAM per character, negligible CPU if you sample/coalesce events.

Minimal data you need (client-side)

Time (ms), event type (hit/DoT), damage number(s) if available, damage tags (e.g., Fire/Cold/Physical), source handle/id if available, projectile/melee/ground flags.

Player snapshot every ~100 ms: life/ES, guard/ward, active guard skill (e.g., Molten Shell), flask states, major debuffs (shock, brittle, scorch, exposure).

Context hints the client already knows: map area, party size, latency estimate.

Heuristics that work well in ARPGs

Last-Hit Dominance: If a single hit within the last 250 ms dealt ≥ X% of max EHP (say 40–60%), attribute death to that source. Confidence: High.

Burst Window: If ≥3 hits in 350 ms reduce EHP by ≥70%, attribute to “Burst” and list the top source by damage share. Confidence: Medium.

Sustained DoT: If DoT ticks account for ≥60% of EHP lost over the last 1.5 s, attribute to the latest applied DoT tag (Burning, Poison, Bleed) and its applier if known. Confidence: Medium.

Ground Effects: If the player is inside a harmful ground volume for ≥500 ms and DoT ticks match that element, prefer “Ground (element)” unless a single hit threshold triggers. Confidence: Medium.

Ailment Spike: If a high-impact ailment (Shock ≥X%, Sap, Brittle) appeared ≤500 ms before lethal damage, append “amplified by Shock (X%).”

Guard Drop: If a guard/ward buff ended ≤300 ms before lethal, add “after Guard expired” tag.

Latency Reality Check: If RTT > 100 ms and the lethal hit timestamp is earlier than the client’s last life bar update, downrank confidence by one level.

Simple decision flow (pseudo)
window = events[-2500ms:0]
ehp_lost = sum(window.damage_to_ehp)

if last_big_hit.damage >= 0.5 * max_ehp or last_big_hit.damage >= 0.7 * current_ehp:
cause = last_big_hit.source_tag
confidence = "High"
elif burst_damage_in(350ms) >= 0.7 * max_ehp:
cause = "Burst (" + top_source_in_burst + ")"
confidence = "Medium"
elif dot_share(window) >= 0.6:
cause = latest_applied_dot_type + " DoT"
confidence = "Medium"
elif in_harmful_ground and dot_share(window) >= 0.4:
cause = "Ground (" + element + ")"
confidence = "Medium"
else:
cause = top_damage_source(window) # coalesced
confidence = "Low"

if guard_ended_recently: tags.add("Guard expired")
if shock_present: tags.add("Amplified by Shock")
if rtt>100: downgrade(confidence)

Output schema (compact & readable)
{
"time": "2025-11-06T12:34:56.789Z",
"area": "Crimson Fortress",
"latency_ms": 68,
"summary": {
"cause": "Fire Projectile",
"attribution": "Rare Fanatic Marksman",
"confidence": "Medium",
"pattern": "Burst"
},
"timeline_ms": [
{"t": -820, "event": "Ignite applied", "source": "Marksman"},
{"t": -270, "event": "Projectile Hit", "dmg_est": 1420, "tags": ["Fire","Projectile"]},
{"t": -40, "event": "Ignite tick", "dmg_est": 380},
{"t": 0, "event": "Death"}
],
"player_state": {
"max_ehp": 5100, "ehp_before": 1600, "flasks": {"ruby": "cooldown"},
"buffs": ["Molten Shell (expired -310ms)"], "debuffs": ["Shock 18%"]
},
"contributors": [
{"source": "Fire Projectile", "share": 0.62},
{"source": "Ignite", "share": 0.38}
],
"notes": ["Guard expired 0.31s before death"]
}

How accurate is this in practice?

Often “good enough.” You’ll correctly flag most one-shots and many burst deaths.

Weak on edge cases: overlapping ground DoTs from multiple mobs, hidden monster mods, penetration/crit rolls you can’t see, or desyncy moments.

Mitigation: show confidence, list top alternatives (“Possible: Chaos DoT (low confidence)”).

Performance & implementation tips

Coalesce events: combine repeated ticks from same source each 50–100 ms to cut volume.

Sample inputs at 60–100 Hz, states at 10–20 Hz.

Use IDs, not names, in the buffer; resolve to strings only on display.

Bound memory: cap the ring buffer by time and count (e.g., 2,000 events or 5 s, whichever first).

Debounce spam: ignore micro-hits below 1% max EHP unless inside a burst window.

Nice UX touches

Show a one-line summary (“Killed by Fire Projectile (Rare), burst in 0.33s — Medium confidence”).

Offer “Why?” with a collapsible timeline and tags (Shock, Guard expired, Ground).

Let players copy a JSON blob for bug reports/posts.


To cover extra resource cost of the beefed-up compute requirements how about they have the basic barebones version but ALSO a premium high-detail consumable mtx version that you can enable/disable/switch between chars that lasts for 10 deaths, tells you more about it, and then also plays the price is right "sad trombone" noise every time you consume a charge.

And then on top of that, an extra premium version where for $500 you get all of the above, including a private message from Jonathan himself that just says "scrub lol". For each additional level over 90 you are when you die, the font size increases 2x.

Dernière édition par karsey#2995, le 6 nov. 2025 à 12:09:47
"
"
noribin#3026 a écrit :
wanting only an estimate makes it easy

That is standard for systems at scale. The more error you are willing to accept, the less compute you have to perform. An inaccurate log is easy to produce; an always-accurate log is barely feasible. It's a business decision how error-prone the death log would be allowed to be.

EDIT: Kudos to Xzorn who is explaining practical issues. It's the real-world side of what I'm saying at a high level.


Everything I wrote is client side. Not even 1 donation will be needed to Jonathan.

Signaler

Compte à signaler :

Type de signalement

Infos supplémentaires