Files
sacred_k/docs/anticheat.md
T
khallaandClaude Opus 5.5 91bbc52cf0 Anti-cheat off, weapon sets 6-7 in hero files
- The game's cheater detection killed the hero ("Hase!") whenever its
  protected stat mirror was stale; the mirror is now refreshed from the hero
  whenever it is unlocked, stream checks never punish, loadHero no longer
  sets the cheater flag (docs/anticheat.md).
- saveHero wrote the items of weapon sets 1-5 only: its loop runs over all 7
  sets, so sets 6-7 survive a world loading the hero from the hero file.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-25 01:33:43 +02:00

2.6 KiB

Anti-cheat off

Request (2026-10-25)

The Gladiator (Character03, level 106) "randomly exploded" fighting an underleveled dragon on Silber. The user: the cheater detection is not necessary, remove it.

Measured

A temporary damage log showed the hero at full health (17200) going to 0 in one tick, with the game logging cheat statBonus 0/3, cheat SkillLevel 0..7, Hase! and cObjectManager::destroy: tried to delete local hero #1!; the hero came back with 1199 health and died to the next hit.

  • The game mirrors the local hero's fields on a protected page (pointers 0x182cddc, second copy 0x182cde0): +0 hero +0x3b4, +4 hero +0x3ee (gold), +8 hero +0x3fe (u16), xor-ed with the hero's key byte (+0x38) repeated four times (the second copy: key << 1); +0xc a raw copy of hero +0x3b0..+0x49d (attributes, skill levels). Writer: 0x603d9c and other places after legitimate changes, all starting with VirtualProtect(page, 0x1000, PAGE_READWRITE) and ending with 0x201.
  • Checks: cCreature::Think about once in 101 ticks (rand() % 101 == 10, 0x5389b5), VerifyHeroMirror 0x5905f0 (callers 0x57fc71, 0x580001, 0x580331, 0x61ce22), inline copies at 0x4659f6, 0x564c6f, 0x57e09c, 0x585bd0, 0x619d33, and a trap in Think (0x5385a6: a creature whose level exceeds its experience by more than 3 gets fields raised behind the mirror's back). A mismatch resets the field (attributes 1, skill levels 0); any mismatch, or no hero at all, kills the hero ("*** Cheater detected! ***", health 0, action dying) and sets the cheater flag 0x182cdd0, saved in the hero file (record +0xf3, 0x604409) and read back by loadHero (0x6066eb).
  • Stream checks in the quest manager's hero data (0x465980): a marker 0xfea4fea4 (0x467782) and three stored values compared with the hero (0x46a60b, 0x46a64e, 0x46a696).
  • In the failing log every skill level mismatched: the mirror was stale (changes outside the game's mirror writers, e.g. the shared hero loaded into another world).

Mod (src/mods/anticheat.cpp)

  • VirtualProtect hook: when the mirror page is made writable, the mirror is rewritten from the current hero first. Checks then compare the hero with itself; writers overwrite it as before. The game's control flow is unchanged (skipping the inline checks would not be safe: code after some of them uses registers they set).
  • Patches: the stream checks' jumps past the punishment are unconditional; loadHero never sets the cheater flag (a flagged character is clean after its next save).

Confirmed by the user 2026-10-25 (attributes fine, no more exploding).