AppState life cycle question

Hi there! I noticed some minor changes with major consequences in the AppStateManager in the last months.

New is for me, that the manager calls the cleanup() method on states when they are detached.



When the state lifecycle where not clearly defined, I used to attach/detach them on a level change. But now this calls misbehaviour, because they are cleaned up on detach and re-initialized on attach.



I noticed also that the initialized-flag is reset on cleanup.



What is the recommended way to handle state changes?

  1. Attach all states in disabled state and just enable the states that I need / disable idle states. This may cause that many states are attached at the same time and could lead to leaked states that will never be detached.


  2. Attach states that are needed, detach states that are in idle mode. AppStates have to initialize their view again every time they are attached / become visible and clean up their view stuff on every detach. This causes much overhead.



    So what’s the developers vision?

The problem was that before there was no way to clean up a state on detach. And really, not cleaning up on detach is dangerous as it would lead to leaks like you mention in 1. It doesn’t matter if the state is detached or not if it never cleans up its mess, after all.



The old way was very random and most people would have strange behavior that they could never explain… basically, attaching an app state during update could cause all kinds of issues since it could be initialized in a place that caused “scene graph cannot be modified” errors.



The AppStateManager Javadoc is pretty clear about the life cycle now because it was fairly random before:

http://hub.jmonkeyengine.org/javadoc/com/jme3/app/state/AppStateManager.html



I attach and detach, enable and disable app states all the time and in the old way this was totally unsafe to do.



If you have app states that are really expensive to initialize and terminate but you don’t mind them leaving that garbage around when not needed then leave them attached and just enable/disable them. However, if they hold onto a lot of resources that hog the memory of your game then attach them and detach them.



For example, game levels are a perfectly reasonable reason to detach and attach states. It would seem like a bad idea to have all of the levels loaded all the time… and managing the transition from one state to another is pretty straight forward (I use an interim loading state for example).



On the other hand, things like a pop-up GUI could be attached once and then just enabled/disabled as needed.

1 Like

And note: the javadoc’s mention of ONCE means once per attach/detach cycle. Before initialize would be called until isInitialized() returned true… and it would be called several times per frame often in a place where it could get into trouble if it modified the scene graph.



Also, I would have added a terminate() method instead of cleanup but it would have broken everyone’s application… and really there is no point of cleanup when there is a terminate() and vice-versa. If you don’t clean up when detaching then when would you clean up? The state manager won’t call anything on detached states… not even cleanup.



So your cleanup() was probably never called before on any states you detached (more leakage).

1 Like

I used to understand the initialize() method as the one-off creation of the states scene and the cleanup() as the final disposal of the scene.



So the concept is now, that the initialize() couples the model (game logic) with the view and the cleanup() decouples both, leaving the option to re-couple both?



The cleanup of BulletAppState is very expensive, it shuts down the executor and destroys the physics space. So I think it should only be disabled.



My application needs to switch between different views, so I think enabling/disabling should be sufficient. I implemented it before, when the AppStateManager did not care if states are enabled/disabled. So I decided to attach/detach them. The adaption is now a little bit difficult.

As long as I’ve been using JME, AppStateManager has always paid attention to enabled/disabled. It’s when it would call initialize that was unpredictable.



…and prior to my changes, there was no safe way to clean up on detach. None at all.



And detached states would never be cleaned up. So cleanup was useless for any app states that were detached.



Regarding initialize, state manager played fast and loose with this one. You could get whatever behavior you wanted (and most users got exactly what they didn’t want) just be manually controlling isInitialized.



The life cycle is much more predictable and sensible now… and safe.

@pspeed said:
As long as I've been using JME, AppStateManager has _always_ paid attention to enabled/disabled.


Referring to an older topic, I proposed to implement setters/getters to enable/disable states here.

But you're right, they didn't get cleaned up before, so I used to call cleanup() explicitly.

Clearly, the life cycle is much easier to handle now. I just need to adapt my code to work correctly with it.
@3H said:
Referring to an older topic, I proposed to implement setters/getters to enable/disable states here.


Ahah. That explains it. I didn't come back into the JME fold until February of last year... after that thread. :)

I fixed it. I replaced all attaching/detaching by setEnabled() and moved the delegate code for sub-states from the overridden stateAttached/stateDetached methods to setEnabled().

It works fine now. Thank you!