Reference · Updated 2552.09.10.16.15

Profiles — PIE Checklist

The registry logic has 7 automation tests. The wiring between the registry and the per-player subsystem has none — it needs a real UGameInstance, and the headless harness builds a

The registry logic has 7 automation tests. The wiring between the registry and the per-player subsystem has none — it needs a real UGameInstance, and the headless harness builds a ULocalPlayer without one, so those paths never execute in tests. Everything below is what the tests cannot reach.

Run in PIE or Standalone. Reference: PlayerProfiles.md §8.


#⚠ PIE's "Number of Players" is the wrong tool

Each PIE client is its own UGameInstance, so each gets its own registry and claims never interact — every player would happily load the same profile and the test would pass while proving nothing.

Split-screen means extra local players in one game instance. Use the console:


DebugCreatePlayer 1     // add a second local player

DebugRemovePlayer 1     // remove them


#Setup

There is no picker yet, so creating profiles needs a temporary Blueprint hookup — one debug key on the MainMenu level Blueprint is enough:

⚠ Two subsystems, and they own different things. The registry (game instance) owns the profile names; USLPlayerProfileSubsystem (local player) owns the values and does the loading. Load Profile is on the second one — looking for it on the registry is a dead end.

NodeTargetGets you
Get Profile Registry Subsystemworld contextCreate Profile, Get Profile Names, Delete Profile
Get For Player Controller ← Get Player Controller (0)player controllerLoad Profile, Get Active Profile Name

⚠ Get Player Profile Subsystem (Widget) does NOT work in a Level Blueprint. It is DefaultToSelf on a widget, so it only resolves inside a Widget Blueprint. In a Level BP use Get For Player Controller.

Then drag off the subsystem return pin to find Load Profile — context-sensitive search hides it until there is a target of that class, which reads exactly like the function not existing.

Print Load Profile's bool return. That is the refusal signal for checks 6 and 8.

Reset to a clean state by deleting Saved/SaveGames/SLProfileIndex.sav and SLPlayerProfile_*.sav. The next boot then generates a fresh profile, which is check 1. That folder is also how you confirm what actually reached disk.


#Checks

#DoExpectFailure looks like
1Fresh state, bootA profile is generated from the name pool and loaded; the list shows exactly that oneEmpty list, or a profile called Default (that model is gone)
2CreateProfile("Beepers")Returns Valid; SLPlayerProfile_Beepers.sav appears immediately, before any setting changeNo file until you change a setting → it will be culled on next boot
3LoadProfile("Beepers"), change FOV, quit, rebootBoots into Beepers with the FOV you setBoots into Default → last-used isn't persisting
4Reboot after deleting SLPlayerProfile_Beepers.sav by handBoots into a profile that exists (generating one if that was the last); Beepers gone from the listBoots into Beepers with empty settings → reconcile isn't running
5DebugCreatePlayer 1 while P1 is on BeepersP2 lands on a different profileP2 on Beepers → both write one slot; this is the bug claiming exists to stop
6P2 LoadProfile("Beepers")Returns false, P2 stays putReturns true → the claim check isn't firing
7Change sensitivity on P2, then on P1Neither moves the other; both survive a rebootOne overwrites the other
8DebugRemovePlayer 1, then P1 LoadProfile the profile P2 heldSucceedsRefused → release isn't happening on player removal
9P2 (not primary) loads a profile, quit, rebootBoots into P1's last profile, not P2'sGuest choice is overwriting the primary's resume

#Notes

  • Check 2 is the subtle one. LoadProfileInternal only ever creates profiles in memory, so CreateProfile
  • writes the slot itself. If that regresses, a new profile survives the session and disappears at next boot — which reads as a save bug, not a creation bug.

  • Nothing is reserved. Every profile can be renamed and deleted, including a generated one. Deleting the
  • last profile is fine — the next boot generates a new one.

  • Set the name table first: Project Settings ▸ System Link ▸ System Link Profiles ▸ ProfileNameTable.
  • Without it, generated names fall back to "Player 2", "Player 3" — working, just dull.

  • Checks 5–9 are the whole point of the branch. 1–4 mostly re-confirm what the automation tests cover.

#Done when

5 through 9 pass with two pads. That is MenusAndOnline.md §9's test gate for the profile half, and it is what makes the picker (step 3) worth building.