Have the client send a vector on move events, and a rotation on turn events.
Move events are clicks, rotations are movement with left and right keys.
The vector is 2 points… for mouse movement, it’s easy, use a start and target location. For a keyboard vector, set the target point X meters in front of the character’s current position.
Rotation events are similar, you just need 2 values sent to the server, representing the speed of your rotation and the direction… 0 for left, 1 for right or something like that.
It’s a simple operation for the server to combine those two variables and predict the ending location, or a pathing node, in order to relay correct information to the other clients. Also, your players get the benefits of both point and click movement and WASD movement.
The server should be the final arbiter of speed, as well. The client can calculate little things like startup and slowdown speed… as far as the server is concerned, however, you could just do a simple single rate that is slightly slower than what the player sees on screen.
Also, because RPG’s like to have variable movement speeds (centaurs are faster than humans, unless humans have a run buff, etc., ) keep a list of absolute run values to scale a players movement on. That way, the client never has to send it’s runspeed, it just has to send a trigger to the server to alert that a change in the runspeed has been made. If the server verifies the change, then it sends back a flag to client which lets it know that it’s Flash Gordon time.
You could also look into optimization routines based on location within your world… people will generally run on paths and roads moreso than across terrain. If you offer a benefit to running on roads as opposed through thick grass, that ensures that you have a very specific trail the player will most likely follow. In those instances, you can have the server predict the entire route, in disparate, overlapping vectors. If the player follows the entire path, then no further calculation needs to be done and the server can use cached static data to accomplish it’s updates. If the player leaves the beaten path, however, you can resume normal path prediction based on immediate player input.
Since the input method is event driven, you want to have a limiter on the client(so someone’s not allowed to send a million clicks through to the server in a one minute period) and a queue on the server so it can prioritize and steward it’s CPU time in a reasonable manner.
Ideally, you should have an extremely minimal packet size, and once a degree of synchronization is achieved with the server, it can prioritize client communications. Nobody really cares if the facing of other players in the game is 100% accurate. If it’s accurate within a degree or ten, it’s good enough to look and feel real. Adjust your prediction routines accordingly… in those situations where sloppiness can be ignored or completely overlooked, then focus more CPU time on the situations that matter, like collision detection, or realtime clashes of swords, armored mounts, and spells exploding enemies into little bits.
More lag between clients means you need to gear your prediction algorithms’ priority to serving the slower clients. Higher bandwidth and less lag for a client means you have to do less prediction and can assign more real-time updates.
Even on a 56k, you should be able to send that data easily, so long as you format it correctly.
Even with a single kilobit, you have 1000 bits with which to send data. If you cannot create a packet structure with 1000 bits (1000 on/off flags, 100 10-bit flags, etc.), then you’re probably sending too much data. Use logic to structure the flags as much as possible. You could then realistically shoot for a .5 second refresh rate, even on slower connections (depending on how good your prediction algorithms end up being.) note: I don’t include chat with this… chat can eat up a much larger chunk of bandwidth, but you can use a more efficient queue system with less of a refresh rate to deal with load balancing for chat.
Then… once you’ve optimized your packet structure based on all the possible flags and triggers the client and server needs for communication, implement a cyclic redundancy check to verify the data sent.
Basically, you create a checksum that creates a semi-unique signature of the data in question. If the client performs the checksum and the checksum doesnt match, it means one of two things… the data was corrupted or wrong, or the checksum was corrupted or wrong. First, recalculate the checksum, and if it turns up wrong again, send a small notice to the server, like “Packet #XXXX failed”. The server should have it’s own set up for handling such errors. If it was simply a movement or ambience update, non-critical to gameplay, then the next packet sent will do just as well to update the client. If the client missed a significant packet, such as being attacked, taking damage, someone opening a trade, or some other important event, then the next packet sent needs to be adjusted to re-send that data.
And so on, and so forth… error correction, optimization by only sending gameplay important information, and minimalist interfaces can give you a good structure and a good usage of bandwidth.