Network input handling

Started by
123 comments, last by hplus0603 11 years, 2 months ago
I'm trying to wrap my head around how to map input into network commands to be sent to the server in an extensible way. Right now, the way input is handled locally is the input is polled every frame and systems interested in handling input can then check the state of a particular action. At startup, an XML file is loaded that maps keys to actions. Currently actions are referred to using strings since I couldn't really see a more mod-friendly way of creating new actions.

The problem is, how should I send the networked-related actions out to the server? The simple solution is to just let each input-handling system say "if IsHeld(action) then NetMessage.send(action)" but that's going to send a bunch of separate packets. I'm not a networking expert but that's probably a bad idea, right? I'm looking for a better idea that allows me to send the changed state of relevant actions across the network in 1 go.

I'm also wondering how to handle the commands on the other end. I'm trying to keep as much overlap as possible between client and server code. It's obviously not possible for everything but I think the input handling could be set up in a way that it doesn't matter if the command came in locally or is being handled on the server. This is also why I want to find a higher-level way of sending out actions to the server. If it's done in the input-handling of the system, I can't reuse that same code on the server.
Advertisement
I would say that the input handling code is different -- read from keyboard, vs read from network -- but the simulation code is the same. The simulation code just needs to know what actions/triggers are active each time step, not where they came from.

I would assign a small integer ID to each action, so that talking about actions over the network would be cheaper than sending a string. If you have less than 30 actions and they are all binary (on/off) you can even get good efficiency by just sending all actions as a bitmask, assigning them each an id from 0 through (N-1) and using the smallest integer that will fit all the actions. The client and the server then have to agree on the XML file that maps action names to integers, or that mapping has to be dynamically established during session open. (Much easier to make sure they just use the same XML file :-)

enum Bool { True, False, FileNotFound };
So you're suggesting I separate the client and server HandleInput methods? I dunno, somehow, having to separate them doesn't sit well with me. I feel like it shouldn't be too hard to abstract the input state enough so that it the handling code can be the same for both client and server.

As for assigning an ID to the actions, I suppose that will work, but I won't know how many actions there will be, since mods can add new ones. I'll have to make it load all the network related ones from the server, since any changes to the mods can change the ID mapping.
I would suggest that there is no problem here.
Firstly, let's unify the term "event". Each client may handle input with different keys mapped to each event. Therefore, it is best to have an event defined independently of the keys that triggered it. The client and server can use this definition of Event for all simulation systems. We therefore need to manage where the event comes from. This is simple. Both client and server user classes call a "get_input" event. For the client, this polls the keyboard and parses it into an Event class instance, whereas the server does the same but parsing from the network packet.

This seems akin to your suggestion. In the case of Hplus's suggestion, that would make sense for I believe he means the same as you and I have suggested.
Right now I have an InputState class which is constructed each frame with the current/previous mouse/keyboard state by the input manager and then sent out to the various systems. The systems can call IsHeld or IsTriggered to find out about the current state of an action (or event as you call it). I'm starting to think I should make an interface and then have 2 different InputState classes, one that is constructed from the mouse/keyboard state and another that is constructed from the net state. The only issue is that this InputState class also provides access to the raw input state for things that can't really be described in terms of the actions I have set up. Like rotating the screen based on the relative mouse movement. Any ideas for this?
I don't think I say so explicitly in the article, but one of the motivations for this piece was actually to solve this exact problem.

If you follow the general architecture I described there, rigging up different "sources" of input becomes trivial. You can source from keyboards, mice, joysticks, network messages, telepathy... you name it.

Wielder of the Sacred Wands
[Work - ArenaNet] [Epoch Language] [Scribblings]


So you're suggesting I separate the client and server HandleInput methods?


That depends on what they do! If they handle input type tasks, such as "read keyboard for input," then yes, that's the right seam.
If they actually processes simulation based on inputs that are read somewhere else, then no, that's the part that should be the same!
So I think we're saying the same thing. Reading keyboard vs receiving data from network is different. Updating simulation based on "cooked" input data (events) should be the same.

enum Bool { True, False, FileNotFound };
The HandleInput methods in my classes do indeed process simulation based on inputs read elsewhere. I've made some progress with this. I took the idea of ranges from Apoch's code so I could abstract the mouse X/Y/WheelDelta. I was able to remove public access to the input once I did that.

Now it's just a matter of finding a way to send the relevant actions to the server so it can construct an InputState based off that. Right now my current thought on this is to create a kind of RegisterAction method in the input manager where various systems can inform the input manager about the actions they'll be looking for and, the important part, whether or not its a network related action or not. Then I can just have the input manager check the state of the actions and send out a packet containing any that have changed state. Hopefully that'll work out
Ok, that seems to be working pretty well. The input is being automatically packed up and sent to the server. Next step is to actually feed it to the various systems, but that should be easy. The one thing I was wondering about though is if I should limit the rate input is sent to the server. Its not much of an issue with keyboard input but moving the mouse around (to rotate your character) will pretty much result in a constant stream of input messages being sent as fast as the input can be read. Is that something I should bother with or is it ok to leave it as is?
Rate limiting network traffic is always a good idea. For instance, you could have the client accumulate transmitted input a fixed 30 times a second and send that instead of spamming the server at whatever framerate the game happens to hit.

Wielder of the Sacred Wands
[Work - ArenaNet] [Epoch Language] [Scribblings]

This topic is closed to new replies.

Advertisement