Sign in to follow this  
Hayer

Multiplayer networking newbie

Recommended Posts

here is how I do it atm;

When the client presses the move-left-key it sends a byte to the server. The server then updates the position of the client and 1 / 30 sec it sends a update with the position to all the players.

I can now see that this wont work over the inet - as im only testing locally atm. So how does the pro's do this?

Im using Lidgren by the way..

Share this post


Link to post
Share on other sites
What kind of player count are you planing on handling? That kind of send player actions + lag compensation is typical of FPS games with 16 to 32 players.

Share this post


Link to post
Share on other sites
Well, its a pretty simple game in logic-game code..

There is a team of 1-8 players on a map against some 8-10 different types of zombies.
And there will be a max of I guess 40-100 zombies.

The zombie limit is only limited by the net code - so I want some good net-code but I also want to keep it simple. So I guess I need to balance it between quality / simplicity.

Share this post


Link to post
Share on other sites
You can have 10,000 zombies with close to zero impact on netcode. You do this by spawning the zombies according to known locations, and then simulating them using deterministic simulation on each machine separately. Because computers can be deterministic (assuming you have no bugs), they will simulate the same value on all machines.

Read the "1,500 archers" article about Age of Empires, linked from the Forum FAQ.

Share this post


Link to post
Share on other sites
You can use randomly generated numbers too, you just have to be careful not to let the pseudo-random state get out of sync. The Age of Empires game had some random movement in it, e.g. grazing animals. IIRC one of the bugs they had (from that article) was traced back to these animals not behaving deterministically, leading to a subtle but increasing divergence of state between peers.

Share this post


Link to post
Share on other sites
You will want to look up Dead Reckoning. Basically, once you push left, you can assume that the left key is still being pushed until the client says that the key has been released.

By making such assumptions you can make the same calculations on the server. Further, you can simulate the same on all other clients as this is as much as they know.

Hope that helps you consider some different approaches.

Share this post


Link to post
Share on other sites
The problem is that there is so many ways to do it!
I only want a simple way that still gives a good result compared to how hard/long time it takes me.

Okey; atm im testing this:

1. The user sends a byte when *he presses a key
2. the server starts moving the object and keeps moving it until it gets a "kill-button"-byte
3. the user lets go of the button and sends the "kill-button"-byte

As you see the server still does all the moving - so how would I add some prediction to this?
And does prediction have some math-formulas? I can't seem to find them.. Or maybe its just me that needs to skill up my google'r-brain

Share this post


Link to post
Share on other sites
I am a newbie too but here is my 5 cents: you should always checking the ping time for all clients the server and keep them in a list.
Prediction comes right here, when server gets a button-press byte it assumes player has already gone pingtime*playerspeed and 'teleports' the player a little bit before making it move.
And same goes for clients if client gets a move-byte of another client it assumes player has already gone (mypingtime+hispingtime)*hisspeed and 'teleports' .
You can ofcourse have better approaches like sliding instead of teleporting.
I am currently trying to do some online zombie game too :). I'll just copy paste the ai code from server to clients and let them predict but once they get an update packet from server they will obey. And ill let clients delete bullets for example on collusion but i wont let them to decide damage or kill anything, at least in the beta.

Share this post


Link to post
Share on other sites
What engine and network library are you using?

Im using C# + XNA + Lidgren 3.

Well, gonna give those a little try. What did you mean with "sliding" instead of "teleporting"?

Share this post


Link to post
Share on other sites
I have done what I said in c++ with networking library ENET.
Imagine your latency is too high like 1 second then you'll see other clients teleporting because the actual position and predicted position is 'very' different you can add something like offset variable which is a point. And instead of teleporting add the teleporting distance we have found to this offset.
When uptading, with addition to speed add i.e. 10 percent or 5 percent or 50 percent of this offset to players position :) i am not sure but you can experiment. I never had to 'slide' teleporting worked fine for me because i only tested it with 3 players pings were okey.

Share this post


Link to post
Share on other sites
Another question;

You say; when the client receives a client-move byte from another client - you mean client->server->another_client, right?

But, when the server receives a "kill-move"-byte I have to calculate the latency for that to? And then the player will "snap" a few pixels back?

Share this post


Link to post
Share on other sites
Quote:
Original post by stonemetal
What kind of player count are you planing on handling? That kind of send player actions + lag compensation is typical of FPS games with 16 to 32 players.


You must be an experienced programmer if you use "round" numbers such as 16 and 32 ;-)

Share this post


Link to post
Share on other sites
Yes I meant that. There are two or three ways I can think of, most basic is to let every client know everyone elses pingtime with the server. So you can add your and his pingtime product with his speed, add to offset if he started and substract from the offset if he stopped.

Other method would be, which I had used, is very precise synchronising the timers for all clients & server. This way clients dont have to care about pingtimes of all other clients.which is actually 'dumb'. Then You can just timestamp events and you get the difference between timestamp-read and current time. But the hard work here is precise synchronising many clients with server. 100ms difference may ruin your game. I guess.

I can try to explain how to synchronise if you cant find it

Share this post


Link to post
Share on other sites
Okey, so I've done some of my homework now - I read up on the "Valve Source Multiplayer Networking"

So I'll just post what I wrote as "notes" to my self - It would be nice if anyone could confirm my notes ;p

// START OF COPYPASTE

* ticks per sec - simulations per sec (server)
* each tick
- process incoming user commands[1]
- run physical simulation
- check game logic / rules
- update all object states

[1] User command
The client creates user commands from sampling input devices with the same tick rate that the server is running.

NOTE:
Instead of sending a new packet to the server for each user command the client sends command packets at a certain rate of packets per sec(30[2] standard) -> two or more user commands are transmitted within the same packet.

[2] Update rate
Client-side variable(called cl_cmdrate in Source Engine) decides how many samplings per sec. Higher cmdrate -> more out-bandwitdh needed.

Snapshots sent to client per sec i decided by the client variable(cl_updaterate in Source Engine) witch is sent to the server
NORE: For dev. and keeping it simple this is set to 20.

The server keeps the last 100 snapshots so when a client fire-command is received it that rewind back in the snapshots and see if the bullet hit anyone.

// END OF COPYPASTE

One of the main things I'm wondering about is; how should I save the 100 last snapshots? That would be pretty large array if I saved ALL object.

( sizeof ( player ) * number of players + sizeof ( zombie ) * number of zombies ) * 100?

Share this post


Link to post
Share on other sites
Quote:
The problem is that there is so many ways to do it!
I only want a simple way that still gives a good result compared to how hard/long time it takes me.


Instead of "networked game," compare "strike it rich."

There are many ways to do it. All I want is a simple way that still gives good results without having to work too hard/long. :-)

In reality, this is *hard*, no matter how you do it.

"Source" (or "Quake" or "Unreal" or "Entity State") style networking is great for FPS-style games with a small number of actors.
"Age of Empires" (or "Starcraft" or "Input Synchronous") style networking is great for minimizing networking when you have tons of actors, but is more complex to get right.

You have to look at your goals (N entities, M updates per second) and your available resources (Q megabytes of memory, Z bandwidth per player) and choose one that fits your requirements.
For example, 100 snapshots for 1,000 entities isn't that much, if each snapshot is 100 bytes. It's just 10 MB. On a PC or modern console, that's nothing, and you might even be able to afford it on the bigger cell phones.

Share this post


Link to post
Share on other sites
I was thinking of using entity state for the players and and simplified RTS approach for the zombies.

Or is that just a bad idea?

My main problem is understanding what formulas should be used at the server and what formulas that should be used in the client to synchronize them as good as possible.
I understand mostly of the theory(at least I think so), but I don't know how to code it.
I guess I'll just have to get to work and try different things.

One thing that came to my mind is;
in the source engine they "rewind" the player positions by saving the 100 last ticks - how would I do that with the zombies? only save they're position and unique ID in a array? That way the array will only take ( sizeof( Vector3 ) + sizeof( Int ) ) * 100

Share this post


Link to post
Share on other sites
Okey; did some simple testing and math now;

And I tought; okey, if I only store the players and the zombies positions for the last 100 snapshots and I got a total of 200(players + zombies) that would be;

((sizeof(float)*3 + sizeof(int))*100*200

sizeof(float)*3 = Vector2 + the rotation vector.

That would be 320mb for only the 100 last snapshots. If I then add 75mb for a map and 25mb for other things I havnt tought about yet that would be 420mb for a server.

Thats right.. or? >_>

I wouldn't call that much memory for a game server - or am I totally lost?

Erlap;
You would mind explaining some more about how you synchronize the timers and stuff for all clients and the server?

And; ping is never constant, its always changing - so going with your first approach you.. do what? tell the ping to every client every 10sec or something like that?

Share this post


Link to post
Share on other sites
I didn't get what you are calculating.. aren't you making a 2d-game?
you can store 2 bytes for x location 2 bytes for y location and one byte for rotation. I don't think you can see the difference for 360/256 degrees, which will be your max round-up error with rotation. anyway in case you want to be more precise store 2 bytes for it too and all should be OK if I am not mistaken.

You can even send ping-time to clients every second.. Or observe the oscilating clients and check them more often while omiting the constant-staying ones, if you want to optimize bandwidth.
Suppose you have 10 players, 2 bytes is enough for one ping-time, which shouldn't be over 6500 hopefully :P. every player will get 10 ping-times, which is 20 bytes and TCP header which is 20 bytes too. 40 bytes/sec is just too small for me. and for server it is 400 bytes/sec upload. Oh 400 is much for just ping-times. You can either switch sending ping-times rarely or using synchronised timers.

I'd explain how I did it but you started to get complex for me with that storing snapshots idea.Anyway you can synchronise all the timers like this:

Suppose A is server and B,C,D... are clients respectively.

1)Set A to synchronising state.
2)Start the timer on A and send all the clients a ping-packet(which can be either empty or equal to avarage size of the packets you'll use during your game)
3)Now clients will start getting packets, as soon as each one gets, resend the data to server and start their timers.
4)As soon as server gets the data resend the packet again..
5)As soon as a client now gets a data increase their ticks by 50 percent and wait till the synchronising state is over.

This works because the timer-difference at the beginning is packet travel time (difference between 2 and 3). After starting the timer on a client you ping-pong a packet and thats a whole 2 ping-time. And you add the difference which is only the half of that.I don't know if its the best solution but it has WORKED for me :)

Share this post


Link to post
Share on other sites
There aren't any synchronization algos server side. The server is authoritative. The clients could just do whatever the server tells it the current game state is. This will cause warping and may cause stutter. So the client "pulls" towards where it should be with linear interpolation and warps if it becomes to far out of true. The client can also do prediction so that if a ping spike causes an update to be late the game doesn't jerk quite as much. Prediction can be as simple as keep doing what you were doing until the server says otherwise. This assumes you are transmitting full player state from server to client.


Quote:
You must be an experienced programmer if you use "round" numbers such as 16 and 32 ;-)
It has been a while, but back in the old days those where networking limits on actual shipped titles.

Share this post


Link to post
Share on other sites
Okey; I'll try to get some spare time to try out Erlap's idea.

Isnt there any good books on this topic? Tried to search at amazon and looked at the GameDev->Books but couldn't find any book that had some chapters on this topic.

And, yes - its a 2d game. Dunno why I was doing some of the sizemath with vec3.. I must have been sleepy or hungry :3

Share this post


Link to post
Share on other sites
Quote:
Original post by Hayer
And I tought; okey, if I only store the players and the zombies positions for the last 100 snapshots and I got a total of 200(players + zombies) that would be;

((sizeof(float)*3 + sizeof(int))*100*200

sizeof(float)*3 = Vector2 + the rotation vector.

That would be 320mb for only the 100 last snapshots.


That would be ~320 KILObytes or 3200 bytes per snapshot. With delta compression or just plain LZW, snapshots can probably be sent once per second without much problem.

Using ints for position might be better and simpler for calculating deltas and perhaps even using deterministic simulation.

Share this post


Link to post
Share on other sites
Antheus; thanks for the correction.

If someone got a simple(if anything in this topic can be called simple) example of the snapshot-technique it would be really helpful if they could share it here :)

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this