hi jmonkeyengine community,
i've started to develop a few little games so far (finished none^^), and one had basic multiplayer support. i encountered a few serious problems there and want to prevent my current game from having the same ones.
right now, i'm using a small interface (setAttack(boolean), setJump(boolean)) to control characters. this way, the controlling "entity" could be a keyboard, an ai or someone at a remote location.
until here, everything is fine. the server gets commands from all clients and the server's world runs according to the received commands. the other way round, each client receives updates about movements, actions, state changes of objects and new objects.
this leads to the first problem: the client's world will differ from the server's world. even after a few frames - if a character moves forward and turns left every frame, it will get to a different location at different frame rates. 90 units forward and 90 degrees to the left, forward and to the left again will make the character walk in a L-like movement. at 90 fps (180* 1 forward, 1 degree to the left) the character will walk in a half circle and end up in a different location.
this means i have to send updates at constant intervals (from server to all clients). and this will lead either to:
- the client's world will always be a snapshop of the server's world, having a limited amount of fps (but perfectly in sync)
- the client will run on it's own, and sometimes, objects will be moved from a to b, their health/ammo/mana will be changed for no reason at all (from the clients viewpoint)
how do or did yu solve this? how to other games solve it?
You should never do it that way for the very problems you are experiencing.
The way JGN / jME-Networking offers to do this is that the logic of the game controlled from the client and the client simply sends updates of positional information to the server. The server in-turn can validate that the client isn't trying to do something it's not allowed to do (like jump all the way across the map), but for the most part just accepts whatever the client sends it. In JGN you have a message type of RealtimeMessage that all the synchronization messages implement. This type of message will never stack up. If an incoming or outgoing message queue contains more than one for any object it will delete the old one in favor of the new one. This reduces lag potential, increasing performance, and even reduces bandwidth usage since you're not sending a bunch of messages that have already expired. Further, since it tells exactly where they should be in that message it doesn't need to rely on any previous message, so you can use UDP without any trouble since you don't really care if you drop a couple messages here and there since ever message has everything it needs to synchronize the position, rotation, etc. of the player.
The server in-turn can validate that the client isn't trying to do something it's not allowed to do
this leads to (almost) unlimited complexity. in my long 5 years of software development, i learned that
"build it in a way that cannot go wrong" is better than "do it now, check it later". it avoids lots of complexity.
the client simply sends updates of positional information to the server
in my case, this is not enough - everything except positions and orientations would get out of sync. the other clients have to know WHY object X is moving, not only where it is right now. (has it been beamed? if so, commit suicide if you touch it. is it moving quickly? subtract some health and calculate how hard you are supposed to be pushed away)
I'd have to say "build it in a way that cannot go wrong" won't work for networked game development, especially using UDP (whose packets could be dropped constantly). In regards to positional updates, there are a couple ways to do this, and darkfrog pointed out one that's a good solution. You could put a bit more than just position in your data that the clients are sending, if that'll be what works for you.
For my networked games, I'm going the route where I maintain the world state and communicate the position, velocity, etc to the client, and predict on the client where things will be. I'm always a bit concerned about security and cheaters, so I try to keep the client control to a bare minimum. The client won't actually tell the server where it is, but rather what it wants to do, and start moving based on the information it has. So, basically, the client starts moving on its own, and if the server sends it position information that's a certain amount off (I plan on adding the ability to factor into the client prediction how much latency is being seen between the client and server) then the client will have to adjust, or maybe even "jump" the user to where they're supposed to be (like lag you'd occasionally see in a multiplayer shooter).
Of course, this isn't necessarily what's usually used in the networked gaming world, but…it all depends on what tradeoffs you want. In a shooter-like game, it might be a bit too difficult for the server to do all the processing, so a lot needs to be offloaded onto clients, who will be trusted with providing correct information.
Just my 2 cents…sorry I ranted.
I'd have to say "build it in a way that cannot go wrong" won't work for networked game development,
that's not what i meant.
what i meant was: do everything asap. if you split anything up to do something later, this "something" *will* cause additional complexity at this "later" point. if you handle a command completely, it will do whatever it's supposed to do. if you extract the effect and apply it, and check if it's ok later, you need to handle every possible error, re-organize all neccesary information, maybe you even have to calculate what the exact situation was before the command was executed etc.
if the server simply executes the commands (in my case, a set of flags), the result is guaranteed to be valid and cheat free, no matter what the content is.
Sorry for not clarifying this better. I suggest things like position, rotation, velocity, etc be done via synchronization like I stated above. Other things would be managed like you stated. You would send a TCP message to tell the server "hey, I just switched to my death-ray", or "I'm firing my rocket launcher". It's typically best for the client to do as much work as possible and the server to only do some validations to make sure they are playing nice. If you don't care about cheating you still should keep a synchronized copy of the game (perhaps in some watered down state) running on the server for questions of synchronization. For example, if player a fires the deathray at player b. Now because of lag player b's client dodged, but from client a's perspective he should have been totally melted. Someone has to make that decision and having a system to determine who makes that decision can often be more complicated than simply providing the logic in the server to be the "end-all-be-all" of what's happening.
Also, apart from cheating it's a good idea to have the server know positional information just in case a client lags big-time and then all of a sudden thinks he's all the way across the map and that's just plain wrong from the server's perspective. In JGN there's a validation state that occurs when a synchronization message is received. You provide your own validation and then you simply return a boolean whether it's valid. If you've determined it is not valid the message is disregarded and a new message is created from the server and sent to the client to tell them where they actually should be. If the server accepts the message it simply gets applied and then spammed out to all the other connected players.