Correct practice for initialization code (messaging system)

I’ve implemented a messaging system based on the observer pattern (using it to handle input and animation at the moment, and sound effects and achievements in the future most likely), a state that wants to receive messages from another state needs to call an addObserver method in the state that is sending messages.

At the moment I’m doing this observer registration in the initialize methods of the relevant appStates but I see two problems:

  1. https://wiki.jmonkeyengine.org/legacy/doku.php/jme3:advanced:application_states states that accessing other appStates from anywhere other than the update loop is bad practice/not allowed.
  2. With the current setup I could get into issues with trying to access states that haven’t been initialized yet - i.e. if state a registers itself as an observer of state b, AND state b registers itself as an observer of state a, only the call from the last initialized one is going to work.

To fix this I think I need a second round of initialization purely for registering observers, after the proper Initialize methods but before the updates start. My current idea for a solution is to just have a second initialize method which will be called on all states from simpleInitApp() after all states have been attached to the stateManager.

What I’m asking is: is this a good solution? Are there any problems this might cause? Is there anything obvious that I’m missing?
Also, will this second set of initialize methods DEFINITELY be called after all of the first set have been called, even if some states are running on different threads?

First, you should try really hard to avoid circular dependencies as you describe. a) it’s dangerous, and b) it’s a sign that there is something wrong with your design.

Second, I even question the need for such elaborate message passing. It might help if you described what you are using the observer pattern for. In all my JME coding, I’ve never needed to do what you are describing so there may be a better way. The one time I did something even remotely similar (app states registering to watch the enabled state of other app states) I later regretted it and had to spend a bunch of time ripping it out to replace with something better.

Finally, if you really must go this route (and I’d encourage you to try alternatives) then you could always avoid sending notifications to states that aren’t initialized or at least the state’s listener could ignore messages until it is initialized. Presumably it will get the latest ‘state’ when it is initialized.

Regarding your mention of the update loop… are you really doing any of this from different threads? That’s another sign that there is something missing in your architecture.

@pinecone said: even if some states are running on different threads?

This was worth responding to separately… states should never be “running on different threads”.

Hi,

Despite of the fact that State is “somehow too generic to become anything in your application”, State SHOULD be use ONLY where they are fit! “Messaging system” is also another pattern which is “too generic” and can be spread everywhere in your code. So, my advice is before you going to use State, please structure your Application (game?) first with the Messaging system. Just review these two:

AppState side:

  • JME’s AppState has a initilize method called when StateManager put that State into work, that’s tell it to update within the update loop.
  • AppState can be access through StateManager via a Class key. So there is only ONE instance of a Class available in the StateManager’s map at a time.

Messaging system side:

  • Scope: You may like to have that Messaging system available in Application’s scope before any State is ON. Or you may like that Messaging system work in one State only, tied with that one State scope.
  • Events (Message): The types of events you want to tranfer between objects. Are they generic? Are they pure data?

I GUESS that you want a Generic messaging system to work OVER many States. So naturally, this system should be in Global scope (can be reference within Application) and available before all the State’s initilization done (before they added into StateManager).

If it’s generic, States can send each other a generic (class-based, or pure data) message, and decoupled from each other. That pattern usually call “EventBus”. The interactions (order of initialization, send, receive) between observers inside the system: within a single thread, over fibers, over channels… are usually solved by the Bus, and the way to describle how they link usually a custom syntax (annotations, external configurations, …)

That’s said, there is a lot of way to implement such System!

I usually use some libraries, which suite best for each case:

That’s quite a lot for reading. Hope this help.

Cheers!

@pspeed said: It might help if you described what you are using the observer pattern for.

Initially I had animation code tied up with my player control code, I then created a animationState component (I’m using Zay ES) and seperated my animation code from the player movement that way because my initial code was messy and needed to be seperated. THEN… I refactored again to use the observer pattern instead.

My reasoning was that I need a clean way to separate not only animations, but things like playing sound effects and unlocking achievements from the code that triggers them. Using components as flags for all of these things seems like a clumsy solution, and actually calling other AppStates looks messy and has the potential to break when I change things.

As an example of how it works, my character control code sends messages to all of it’s observers with the relevant EntityId and an enum that could be RUNNING_STARTED, RUNNING_STOPPED, and a few other things. The animation system uses these to keep track of which animations it should be playing for which entities. In the future I imagine my sound code would do a similar thing and play footstep audio, or sound effects on ATTACK.

I mostly followed this for the implementation: Observer · Design Patterns Revisited · Game Programming Patterns, I chose not to use the Observer system in the java core library because it seemed too complex for what I needed.

If I have massively overcomplicated things I’d love to be set back on the right track.

@pspeed said: This was worth responding to separately... states should never be "running on different threads".

I haven’t actually attempted to write any multithreaded code in jmonkey yet (or java in general for that matter), so forgive my ignorance there.

@pinecone said: Initially I had animation code tied up with my player control code, I then created a animationState component (I'm using Zay ES) and seperated my animation code from the player movement that way because my initial code was messy and needed to be seperated. THEN... I refactored again to use the observer pattern instead.

My reasoning was that I need a clean way to separate not only animations, but things like playing sound effects and unlocking achievements from the code that triggers them. Using components as flags for all of these things seems like a clumsy solution, and actually calling other AppStates looks messy and has the potential to break when I change things.

As an example of how it works, my character control code sends messages to all of it’s observers with the relevant EntityId and an enum that could be RUNNING_STARTED, RUNNING_STOPPED, and a few other things. The animation system uses these to keep track of which animations it should be playing for which entities. In the future I imagine my sound code would do a similar thing and play footstep audio, or sound effects on ATTACK.

I mostly followed this for the implementation: Observer · Design Patterns Revisited · Game Programming Patterns, I chose not to use the Observer system in the java core library because it seemed too complex for what I needed.

If I have massively overcomplicated things I’d love to be set back on the right track.

Yeah, kind of.

The observer pattern is one way to decouple tightly coupled ‘things’ but as you’ve discovered, it isn’t really decoupling them that much. There are still some inherent dependencies that happen.

…versus the ES, where everything is 100% independent and any coupling is only notional.

The sound system has an EntitySet for anything with a Position and a Sound component. It uses that to play sound and it’s really simple. From that point on, anything with a position and a sound will automatically have sound played with no additional code written. The player walks, they get a “footsteps” sound attached to them. The drop a book on the floor, an entity is created at that position with the Position and Sound. Persistent sounds are covered, moving sounds are covered, temporary sounds are covered… all from one dirt-simple system.

It’s also possible to couple the sound to the animation but I always think of good reasons not to do that. (I may want animation without sound and sound without animation, for example. Character animation is where it gets a little trickier.)

Character animation need not be a component at all but purely reactive based on other state. After all, it’s a purely visual thing based on something else. When the player is walking, the player has some state that indicates he is walking. It kind of has to otherwise the systems that move him wouldn’t know to move him. In that case, a system could be watching for this state and play an animation as appropriate. That being said, things can be even simpler having an animation component and you could cover non-character animation. Character animation is a weird one, though, because generally you’d want to have the animation linked directly with movement. It’s not like a windmill spinning in the corner. The walk animation may need to speed up or slow down to keep the feet from sliding, etc… But if you already have animation components for other things then you could go a long way using that until you can implement better character animation.

Especially in an ES based application, you should not need game-level events like this. Moreover, they will likely cause you issues because now you will end up having chicken/egg problems with entity set updates and so on. One system may receive an event for something before it’s had a chance to update and process its EntitySet. An ES is supposed to prevent the need for these event objects flying all over the place anyway.

1 Like
@pspeed said: _Especially_ in an ES based application, you should not need game-level events like this. Moreover, they will likely cause you issues because now you will end up having chicken/egg problems with entity set updates and so on. One system may receive an event for something before it's had a chance to update and process its EntitySet. An ES is supposed to prevent the need for these event objects flying all over the place anyway.

Shit, I didn’t think of that. Everything is going to break horribly! Hooray!
Thank you for the very detailed help though, time for me to rethink my design.

EDIT: Actually, I’m storing events upon receiving them and only processing them during the proper update method, after the entityset has been updated, so maybe I won’t have issues there, but I can still think of problems that could arise.

@pinecone said: Shit, I didn't think of that. Everything is going to break horribly! Hooray! Thank you for the very detailed help though, time for me to rethink my design.

EDIT: Actually, I’m storing events upon receiving them and only processing them during the proper update method, after the entityset has been updated, so maybe I won’t have issues there, but I can still think of problems that could arise.

Yeah, because another problem with events is that you will get and process all of them regardless of if they are stale by then or not. Versus the ES way of just grabbing the current snapshot.

Events is push design, where ES is mainly pull/poll design. Paul provide good advices.
Anyway if you want to continue with events, this my tips, about using events with jme. Often event system use asynchronous and multi-thread, so if you have to do stuff in the jme’Render Thread as reaction to an event, you should use the method Application.enqueue(Callable).

example (I use rxjava as events/react system + jdk8 for the ease of creating closure) :
[java]
// async “pipeline” creation, introducing a delay because state processing can change the current state before all observers receive notification in the right order
// by default rxjava process event synchronously
final BehaviorSubject<State> stateReq = BehaviorSubject.create(State.hidden);
final Observable<State> state = stateReq.distinctUntilChanged().delay(1,TimeUnit.MILLISECONDS);

// subcription
target.state.subscribe(this)

// send state change :
target.stateReq.onNext(InfoCube.State.waiting);

// process state change :
@Override
public void onNext(InfoCube.State v) {
if (onExit != null) {
try {
onExit.call(v);
} catch (Exception e) {
log.warn(“onExit”, e);
}
onExit = null;
}
log.info(“Enter in {}”, v);
switch(v) {
case hidden :
jme.enqueue(() -> {
gp.remove(target.node);
efactory.unas(target.node);
return true;
});
target.stateReq.onNext(InfoCube.State.generating);
break;
case generating : {
jme.enqueue(() -> {
target.translateNext.apply(target);
efactory.asCube(target.node);
gp.add(target.node);
AnimControl ac = Spatials.findAnimControl(target.node);
ac.addListener(animListener);
animator.play(target.node, “generating”);
return true;
});
break;
}
case waiting :
jme.enqueue(() -> {
animator.playLoop(target.node, “waiting”);
return true;
});
break;
case grabbed :
jme.enqueue(() -> {
animator.play(target.node, “grabbed”);
return true;
});
break;
case exiting :
jme.enqueue(() -> {
animator.play(target.node, “exiting”);
return true;
});
break;
}
}
[/java]

1 Like