Skip to content

Fix ECS entity id vs player_id confusion across magic, combat, effects - #2

Closed
barrelin-oss wants to merge 5 commits into
Helbreath:masterfrom
barrelin-oss:claude/fervent-cori-d737b1
Closed

Fix ECS entity id vs player_id confusion across magic, combat, effects#2
barrelin-oss wants to merge 5 commits into
Helbreath:masterfrom
barrelin-oss:claude/fervent-cori-d737b1

Conversation

@barrelin-oss

Copy link
Copy Markdown

Summary

Full src/ sweep for the recurring bug class where an ECS entity id (from the entity_manager shared with NPCs) was cast to player_id — or the reverse. The two id spaces are not interchangeable: get_player(player_id{entity.id}) returns nullptr (or the wrong player) and if (auto* p = ...)-guarded blocks are silently skipped. Standard fix: player_system::get_player_by_entity(entity) (O(1), const overload available).

Fixes

  • magic_system.cpp (21 sites): mana/HP/SP deduction, silenced/level/stat checks, range checks, safe-zone PvP checks, damage/heal scaling, SP drain, resurrection targets, debuff resist rolls. find_aoe_targets/find_line_targets no longer fabricate entity{player_id.value, 0} for hit players — they push the real p.ecs_entity (fabricated handles broke downstream combat_system::deal_damage and the spell broadcast, which resolve via the real ECS index).
  • application.cpp: periodic heal / mana_drain / mana_restore effect ticks never applied to players.
  • game_handlers_combat.cpp: on_spell_cast broadcast resolved caster/targets with the bad cast; respawn invulnerability was keyed on a fabricated entity and never matched the real defender — the 3s respawn protection was silently inert.
  • player_system::remove_player: effect cleanup used entity{id.value} instead of ecs_entity, leaking active effects on logout.
  • wave4_handlers.cpp (legacy binary path): attack/cast fabricated caster entities from ctx.player.value; now resolve the player's real ecs_entity (their spell-knowledge/cooldown lookups could never match the JSON path's keys).

Confirmed legitimate (left untouched)

  • Admin API get_player(player_id{req.player_id}) — the wire value is a real player id.
  • Inventory/trade/crafting entity_id{pid.value} keying — consistent at creation and access.
  • Spatial-index entity_id{ecs_entity.index()}.

Tests

  • test_magic.cpp, test_safe_zone.cpp, test_melee_pve.cpp updated to resolve the real ecs_entity instead of fabricating entity(pid.value) — the old form only passed because test fixtures spawn no NPCs, so the player-id and ECS-index counters coincided.
  • Full suite: 2522/2522 passing (3 pre-existing skips that need real map files).

Documented in docs/PROGRESS.md under Recent Changes.

🤖 Generated with Claude Code

barrelin-oss and others added 5 commits August 30, 2026 11:22
Adds a dedicated experience_update server->client message sent whenever
a player gains XP (solo/party NPC kill XP, crusade rewards, login reward
delivery). Carries experience_gained, new total experience and level;
on level-up also levels_gained, new max_hp/max_mp/max_sp and unspent
stat_points.

Implemented as an experience_gain callback on player_system (fired from
add_experience only when XP actually changes; silent at max level),
wired in game_handlers so every add_experience call site is covered.

Also fixes pre-existing MSVC build breaks: missing NOMINMAX include
order in three bridge translation units and unguarded POSIX time
functions (timegm/gmtime_r/localtime_r) in auth_system and a crusade
test.

Documented in docs/protocol/player.md, docs/JSON_PROTOCOL.md and
docs/protocol/items.md; PROGRESS.md updated.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…y, headless bot client

- JSON party messages (invite/accept/leave/update) + handlers and docs
- Fix loot gold credited to ECS entity id instead of player_id
- npc_registry YAML: parse gold_min/gold_max; npcs.yaml exp/gold keys fixed
- Perf: O(1) player lookup in find_aggro_target; sees_all counter gates
  the far-admin scan in get_players_who_can_see
- Case-insensitive .amd loading (ARESDEN/ELVINE were silently skipped),
  lowercase map names; characters start in their nation town
- Registration rate limit configurable (auth.max_registration_attempts)
- mapdata configs for default/aresden/elvine (spawners, merchants,
  initial points validated by tools/bot/scan-map.mjs)
- tools/bot: headless bot client (hunt/loot/shop/party), gen-bots, scanner
- posix_time_compat shim (pre-existing local build fix, included as-is)

WIP checkpoint requested by worktree session relaxed-bell-f49dbe for merge.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The modernized magic_type enum was missing six values used by the HBX
Magic.cfg data (14, 19, 21, 25, 26, 27), so 15 spells were skipped at
load as invalid magic_type - including all high-circle attack magic
(Blizzard, Meteor-Strike, Lightning-Strike, Bloody-Shock-Wave).

- Add create_dynamic(14), damage_linear(19), damage_area_no_center(21),
  damage_area_sp_down(25), armor_break(26), ice_linear(27) to the enum
- Implement line targeting (find_line_targets): Bresenham trace from
  caster toward target up to 12 tiles, faction and safe-zone filtered
- damage_area_sp_down drains SP from player targets (effect2 dice,
  parsed into new sp_drain field)
- armor_break deals pure damage (ignores_defense now honored via
  deal_pure_damage)
- Cancellation becomes an offensive dispel (debuff category, removes
  the target effects) instead of hitting the utility stub
- Fix 4 placeholder rows in magic.yaml copied from Lightning-Strike in
  the original cfg: Cancellation type 28, Resurrection 32,
  Illusion-Movement and Mass-Illusion-Movement 16
- create_dynamic spells (Spike-Field, Ice-Storm, Cloud-Kill) stay
  skipped with an info log until the dynamic ground-object subsystem
  exists (tracked in PROGRESS.md)
- New registry test covering the legacy types; 2522 tests pass

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
loot_tables.yaml and shops.yaml were authored against a different item
numbering than items.yaml - 22 referenced IDs did not exist (375+
validation warnings per boot) and several existing IDs pointed at the
wrong item entirely (bosses dropping Tomato/Hoe/Garlic/Carrot as
placeholders for Ice/Merien gear; potion shops selling a MagicNecklace;
blacksmiths selling Dagger variants labeled ShortSword/MainGauche).

Audited every reference against items.yaml using the intent recorded in
the line comments:
- Remapped 16 references whose intended item exists under another ID
  (BlackShadowSword 926, The_Devastator 923, BarbarianHammer 928,
  KlonessAxe 929, StormBringer 924, GiantSword 46, Flameberge+1 55,
  MagicWand(MS20) 256, KnecklaceOfStoneGolem 647, SapphireRing 336 ...)
- Removed 32 pool entries whose intended item does not exist in this
  item set (AncientTablets, CritCandy, SSS/E.S.W/I.M.C manuals,
  XelimaCap/Hat/Helm, NecklaceOfXelima, DragonWand MS40, HolyBlade,
  GiantBattleHammer) plus the vegetable placeholders
- Rewrote ~50 stale comments to the real item names (no behavior change)
- shops.yaml: ShopKeeper-E/W sell RedPotion/BluePotion/GreenPotion
  (91/93/95); Gandlf/William sell Dagger/ShortSword/MainGauche (1/8/12)

All references now resolve; no in-pool duplicates; 2522 tests pass.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Entity ids from the shared entity_manager are not player ids; casting one
to the other made get_player() return nullptr (or the wrong player) and
silently skipped guarded blocks. Standard fix: get_player_by_entity().

- magic_system: 21 lookup sites (mana/HP/SP costs, silenced/level/stat
  checks, range and safe-zone checks, damage/heal scaling, SP drain,
  resurrection, debuff resist); AOE/line target finders now push the real
  p.ecs_entity instead of fabricating entity{player_id.value, 0}
- application: periodic heal/mana_drain/mana_restore effect ticks never
  applied to players
- game_handlers_combat: spell-cast broadcast target resolution; respawn
  invulnerability was keyed on a fabricated entity and never matched
- player_system::remove_player: effect cleanup used entity{id.value},
  leaking active effects on logout
- wave4 legacy handlers: attack/cast fabricated caster entities from
  player_id, so spell knowledge/cooldowns could never match the JSON path
- tests: fixtures now resolve the real ecs_entity (old fabricated form
  only passed because fixtures spawn no NPCs, keeping counters aligned)

Legitimate uses kept: admin API player_id from the wire, inventory/trade
entity_id{pid.value} keying (consistent), spatial entity_id{index}.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@barrelin-oss

Copy link
Copy Markdown
Author

Closing in favor of #1: the local master fast-forwarded to this branch's merge commit (536af07) and the fork master mirrored in #1 now includes the full audit content. Re-validated post-merge: clean build, 2531/2531 tests passing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant