Please stay with me as I explain my question.
I'm creating a multiplayer game. My design pattern follows broadly thoughts described in the post here.
The idea is that each actor whether be it a (local) player or a npc does what a
GameAction attached to this actor tells it to do. These GameActions are obtained by reacting to a received
GameEvent. This enables only sending GameEvents through network and each client logic will obtain own GameActions for e.g. moving local copies of remote players themselves and also getting sanity checks from time to time.
How should these actions be composed then? Who takes responsibility stopping actions?
My problem right now is that if a
LocalInputHandler produces on
CHARACTER_INPUT event it will be delivered to and consumed by a parent node the local player is living in. The parent node obtains a
CHARACTER_MOVE GameAction and attaches it to the specific local player. On
update() the player/character excecutes the GameAction. It moves, by itself. Like a small script interpreter. Isn't that awesome? No it isn't because if I didn't release the button (aka holding it) no new
CHARACTER_INPUT will be emitted and the player excecuted it's action and nothing happens.
To solve this problem I came up with 6 classifications or types of GameActions:
- Unstoppable - loops forever, can only be stopped by itself
- OneTimeLooper - loops until getting stopped, always finishes task before getting interrupted
- Looper - loops until getting stopped, can be interrupted any time
- OneTimer - performs action only once and can't be interrupted while doing it
- WeakWorker - Performs action once but can always be interrupted
- Stopper - AntiAction: interrupts all types except unstoppables. Is a OneTimer
This would lead having a
CHARACTER_MOVE action be a
OneTimeLooper. It moves the attached actor until a
CHARACTER_MOVE_STOP action tells it to do, being created because of an event originating from a
But this solution smells bad to me, because then each Character/Actor/Player has to have more than one GameAction it can hold to. A Stopper must have access to it's counterpart to know if it is allowed to stop. Also shouldn't higher order classes like parent nodes have the power to stop an GameAction since they also created them? Why not give LocalInputHandler power? How to change input style if you have a menu opened on top of the screen representing everything that happens in your world?
Clarifications and optional text:
LocalInputHandler and localPlayer are coupled by handing over LocalInputHandler the ID (represented by UUIDs, which I'm still not sure using because they are so big for networking applications) of localPlayer in construction. Each InputHandler whether be it a remote or ai InputHandler will only now by ID which actor they are handling and will refer to this ID when creating a
CHARACTER_INPUT event by writing
recepient = actorID into the event. The message of this event is a
int keymap which holds information about which key is held down. keymap is a member variable of a InputHandler It could be a boolean but for now it's an int array. Parentnode of an actor takes these informations (recepientID and keymap) and packs a
CHARACTER_MOVE GameAction into the event passing it down to the actor for consumption. In this process the keymap is being copied by calling
.copy()on keymap because this will prevent opening a channel of direct communication from the InputHandler to the GameAction by not letting it just copy a reference but the array as a whole.