Skip to content

Add domain clone helpers for user patches#569

Closed
CaseyLabs wants to merge 1 commit intofix/mapper-js-lintfrom
security/user-admin-clone-helpers
Closed

Add domain clone helpers for user patches#569
CaseyLabs wants to merge 1 commit intofix/mapper-js-lintfrom
security/user-admin-clone-helpers

Conversation

@CaseyLabs
Copy link
Copy Markdown
Collaborator

@CaseyLabs CaseyLabs commented May 6, 2026

Why This PR Exists

  • This PR adds safe copy helpers for user-owned game data.

    • The next PR in the stack edits user records through the admin API.
    • That API needs to try changes before saving them.
    • It should not accidentally change the live user object while validating input.
  • In Go, a plain struct copy is only partly independent.

    • Simple fields like int, bool, and string copy cleanly.
    • Slices still point at the same backing array.
    • Maps still point at the same hash table.
    • Pointers still point at the same object.
  • That matters for UserRecord and Character.

    • A user has maps such as macros, aliases, config options, and completed tips.
    • A character has slices such as inventory, adjectives, room history, and buffs.
    • Items, pets, equipment, shops, kill/death stats, room visit data, and buffs can also contain nested mutable data.
    • A shallow copy could let a rejected edit leak into the original data.

Maintainability Approach

  • This PR keeps clone rules close to the types that own the data.

    • Character.Clone() still coordinates the full character copy.
    • Smaller types now own their own details through their own Clone() methods.
    • This reduces the number of places that need to know about nested fields.
  • This does not try to use reflection or serialization for cloning.

    • Reflection would hide important copy rules from reviewers.
    • Serialization would drop runtime-only fields and depend on YAML behavior.
    • Explicit clone methods are easier to test and safer for game state.
  • The pattern is now easier to extend.

    • If ShopItem later gains a mutable field, update ShopItem.Clone().
    • If ItemSpec later gains a mutable field, update ItemSpec.Clone().
    • If PetAbility later gains a mutable field, update PetAbility.Clone().
    • Callers should keep using the owning type's Clone() method.

What Changed

  • Added top-level user and character clone helpers.

    • UserRecord.Clone() copies user account state and nested character state.
    • Character.Clone() copies player identity, gameplay state, runtime maps, inventory, equipment, pet data, buffs, shops, room visits, and combat tracking.
  • Added clone helpers for character-owned support types.

    • Shop.Clone() and ShopItem.Clone() handle merchant stock data.
    • Cooldowns.Clone() handles cooldown maps.
    • KDStats.Clone() handles kill/death maps.
    • MobMasteries.Clone() handles mastery maps.
    • RoomBitset.Clone() handles visited-room bitsets.
  • Added clone helpers for item-owned state.

    • Item.Clone() copies item instance data.
    • ItemSpec.Clone() copies item override specs.
    • Damage.Clone() copies crit buff ids.
  • Added clone helpers for pet-owned state.

    • Pet.Clone() copies pet inventory and abilities.
    • PetAbility.Clone() copies ability damage, stat mods, and buff ids.
    • Pet ability caches are cleared on the clone so they rebuild from clone-owned ability data.
  • Added clone helpers for user logs.

    • UserLog.Clone() copies the event log slice.

Go Concepts Used

  • maps.Clone(...) copies a map.

    • The new map can add, remove, or replace keys without changing the old map.
    • This is a shallow map copy.
    • It is enough when the map values are simple values.
  • slices.Clone(...) copies a slice.

    • The new slice has its own backing array.
    • Appending or replacing elements will not rewrite the old slice.
    • Nested mutable fields still need their own clone step.
  • Pointer fields need explicit handling.

    • Copying a pointer copies the address.
    • Both structs would still point at the same object.
    • This PR copies pointed-to data where sharing would be unsafe.
  • Cached pointers are intentionally reset.

    • A cache is derived data.
    • It can be rebuilt when needed.
    • Resetting it avoids pointing from a clone back into source-owned data.

Tests Added

  • Added focused clone independence tests.

    • internal/users/clone_test.go
    • internal/characters/clone_test.go
    • internal/items/clone_test.go
    • internal/pets/clone_test.go
  • The tests mutate cloned data and check that the original data stays unchanged.

    • User maps and event logs.
    • Character shops, cooldowns, kill/death maps, mastery maps, room visits, and equipment items.
    • Item override specs.
    • Pet inventory and pet abilities.

Why This Is Its Own PR

  • The next PR uses these helpers for admin API hardening.

    • That next layer is about request validation and allowed fields.
    • This layer is only about safe copying.
    • Splitting them lets reviewers check the copy behavior separately.
  • The changes are mechanical but security-relevant.

    • The code does not add new API routes.
    • It does not change user-facing gameplay behavior by itself.
    • It reduces the risk of accidental in-memory mutation in later code.

Stack

Validation

  • Ran after this update.
    • go test ./internal/users ./internal/characters ./internal/items ./internal/pets ./internal/buffs
    • go test ./internal/web ./internal/users ./internal/characters ./internal/items ./internal/pets ./internal/buffs

@CaseyLabs CaseyLabs force-pushed the security/user-admin-clone-helpers branch from 3bc018c to 4045ace Compare May 6, 2026 13:55
@CaseyLabs CaseyLabs closed this May 6, 2026
@CaseyLabs CaseyLabs deleted the security/user-admin-clone-helpers branch May 6, 2026 14:15
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