sinbad / SPUD

Steve's Persistent Unreal Data library
MIT License
300 stars 44 forks source link

Start a new level without saving the state of the current one #39

Closed cbQB closed 2 years ago

cbQB commented 2 years ago

Assume USpudSubsystem::CurrentState is Disabled. We want to load a level (not a saved game), and start SPUD after it is loaded. We do not want to save the state of the current level. UGameplayStatics::OpenLevel is async, so if we call USpudSubsystem::NewGame immediately after it, SPUD will be activated in the current level, and will then save the state of the current level, travel to the new one, and attempt to load its state. Using a level actor's BeginPlay to call USpudSubsystem::NewGame if USpudSubsystem::CurrentState is Disabled doesn't work: the actor's BeginPlay is called before FCoreUObjectDelegates::PostLoadMapWithWorld. But USpudSubsystem::NewGame calls SubscribeAllLevelObjectEvents, and USpudSubsystem::OnPostLoadMap does it too.

My approach is to use USpudSubsystem::ForceReset() from an actor in the level (GameState), if USpudSubsystem::CurrentState is Disabled. This works, but doesn't fit the documented description of ForceReset().

Maybe this scenario should be documented?

sinbad commented 2 years ago

I assume this is because the level is a menu or a loading screen? I should probably allow a level exclusion list in the system config so that those kinds of levels are just ignored in all cases.

cbQB commented 2 years ago

That, or switching to another level, or reloading the current one. So a level exclusion list would not solve the problem. I was initially thinking of creating a flag to signal SPUD to “awake” (i.e. switch to running idle) from the post level loaded callback.

sinbad commented 2 years ago

Can you explain the circumstances where you want to call NewGame in a level you sometimes want to save? The expectation is that you'd call NewGame in a main game menu or something. I'm struggling to understand why you'd be calling it while you have a level loaded which you otherwise want to usually save, but not this time? I need to understand why this sequence arises to come up with a better way to handle it.

If it's to reset a game while you're in a normal game level, then perhaps the best way to handle it would be to automatically discard the data associated with currently loaded levels (as in, don't save them on unload) when you call NewGame. Thus when they get unloaded they don't get saved, but only that once; when re-loaded they're eligible for saving again. But if it's for another reason, then it might be worth adding an option to manually discard (not save) data for specified levels at the next unload.

[edit](I realise you found a workaround by messing with CurrentState but that's really not the intended use of it)

cbQB commented 2 years ago

Yes, for the case where the player is in a normal game level, and wants to restart (or switch to another level, without saving the current progress). My approach is to call EndGame and load the level. Then call ForceReset from the game state actor's BeginPlay, if CurrentState is Disabled (so that it doesn't abort loading). This achieves exactly what I want, the only issue is that that's not the declared usage of ForceReset.

What I want to avoid is precisely what NewGame does after calling EndGame itself - attaching to the current level, since the current level will be unloaded, with the specific desire not to save its state.

sinbad commented 2 years ago

OK, so what I suggest is that NewGame should have an option to ignore all the currently loaded levels. The current behaviour will remain the default, but if you pass an additional argument to NewGame (perhaps "bIgnoreCurrentlyLoadedLevels = true"), then it doesn't (re)subscribe to the level events of currently loaded levels, meaning when they unload they're not saved. Thus you could call NewGame before your level transition and not have to worry about doing anything post-level load yourself.

cbQB commented 2 years ago

Yes, that would do it. It's what I was about to do (see second reply). Maybe call it "bAfterLevelLoad", to avoid the use wondering what will happen if it is the current level that is re-loaded.

sinbad commented 2 years ago

Done: https://github.com/sinbad/SPUD/commit/2a06cea143ca5c1c7ce34fb1ffe0680ae4102c33

cbQB commented 2 years ago

Great, thanks!