[ENet-discuss] fps networking

Nuno Silva ghostmanzero at lutherlink.de
Thu Mar 20 01:37:05 PDT 2008


Games like unreal use their "replication system" to send over  
variables to the server and back, you dont send a message if you're  
still pressing the 'up' arrow to move.

Here's a page on Unreal's networking architecture.

http://unreal.epicgames.com/Network.htm

Quoting Jacky J <flamuss at gmail.com>:

> What are you babbling about? Games like Unreal don't send input?...
> weird... so how does the server know how the client moves? Yeah...
>
> Can anyone answer the question? What happens when the clients drops to
> a low frame and tries to send input? Lee? I mean come on...
>
> Don't mean to sound like an ass but come on i asked a simple question.
>
> Looks like i'm on my own again..... like always
>
>
> Steve Williams wrote:
>> Games like Unreal and many other FPS games do not generally send   
>> inputs over the network.  They send a requested position update,   
>> which the server processes, then the server sends back the actual   
>> position (after taking into account the requested position updates   
>> of all clients and AI characters).  The client takes the actual   
>> position, moves its player to that position, and continues.
>>
>> Client prediction involves the client assuming that the server will  
>>  agree with what the client thinks might happen, so while the  
>> server  is processing the requested position update, the client is  
>> moving  its player to where it thinks it will end up.  When the  
>> actual  position is received from the server, the client adjusts  
>> its  players position to match the position the server said it was   
>> supposed to be.
>>
>> This also allows dropped network packets to be handled correctly.    
>> You may see the player jump or snap to the actual position as given  
>>  by the server after a dropped packet or two, but the positional   
>> updates take care of missed packets.
>>
>> --
>> Sly
>>
>>
>> -----Original Message-----
>> From: enet-discuss-bounces at cubik.org   
>> [mailto:enet-discuss-bounces at cubik.org] On Behalf Of Jacky J
>> Sent: Monday, 17 March 2008 11:56 AM
>> To: Discussion of the ENet library
>> Subject: Re: [ENet-discuss] fps networking
>>
>> Hey Lee i appreciate all the info.  I'm still a bit confused about the
>> client timestep:
>>
>> For simplicity, let's say we have a game that has only 8 directions (no
>> mouse, jumping, etc).
>>
>> Does the client timestep contain just one move command or does it
>> contain multiple?  "move command" meaning one of 8 directions that are
>> applied during a frame.  So for example our game runs at 60 fps, and
>> we're sending at 20hz. If the server wanted to exactly recreate a
>> sequence of moves that the client does, it would have to know all of the
>> directions that were applied at every frame.  That means in our example,
>> in an ideal world, each client->send() function would send 3 directions
>> with it. Does this sound right?
>>
>> Now of course if the client frame rate dropped to 20 fps, it would only
>> be sending one direction per timestep. But the client wants to be
>> traveling as much distance as it would if it were running at 60fps, so
>> locally it would apply some physics update multiple times to make up for
>> the lost frames.  How does the server account for this?
>>
>> It seems like in this article they do one move command per timestep:
>> http://unreal.epicgames.com/Network.htm
>>
>> Do you have any good examples of source code i could look at?  I looked
>> a cube but it seems like it was doing the client-side physics method
>> rather than client/server cosimulation (correct me if i'm wrong).
>>
>> Thanks again.
>>
>>
>> Lee Salzman wrote:
>>
>>> FPS is a spectrum of things you can do, more than a set way. The ways,
>>> however, are far different from, say, a MMORPG where latency is just
>>> usually accepted by players: in a twitch FPS game, latency must be
>>> destroyed at all costs - latency is totally evil, no exceptions. But
>>> even within the spectrum of FPS networking, each  way comes with its own
>>> trade-off. They more or less boil down to the following:
>>>
>>> Trade-off #1: fixed rate physics, or variable rate physics. You need to
>>> select a rate at which to run the physics simulation, i.e. 50 Hz, 100
>>> Hz, etc. Now if  the rendering FPS is higher than the physics rate,
>>> then you will need to interpolate between two physics steps to have the
>>> game  not  look all jerky, since if the game is rendering at 100  FPS
>>> and physics is only simulated at 50 Hz, your view point is only changing
>>> 50 times a second, so half of those 100 FPS are going to waste. If you
>>> use variable rate, you still choose a minimum rate at which to run the
>>> physics, but if the FPS happens to be higher,  then you run physics at a
>>> higher rate. For example, say, you settle on 50 Hz, meaning each physics
>>> step is 20 milliseconds. If one frame of rendering took up, say, 102
>>> milliseconds, then you would do 5 physics steps, and if doing fixed rate
>>> physics, you would bank those 2 leftover milliseconds for "credit" on
>>> the next frame. If you are doing variable rate physics, you just go
>>> ahead and do an extra time step using those leftover 2 milliseconds
>>> immediately. Fixed rate is probably better if you can just afford to run
>>> the physics at a high rate these days, and variable rate kinda requires
>>> the client to be authoritative on physics simulation.
>>>
>>> Trade-off #2: co-simulation, totally client-side physics, or lock-step
>>> simulation.
>>>
>>> - In a lock-step simulation, you would just send player input reliably
>>> to the server, the server would simulate physics, and tell the player
>>> where everything is at. This is a great evil that should never EVER be
>>> used in an FPS game because everything requires a silly round-trip that
>>> destroys twitch gameplay on even modest pings. I only mention it because
>>> you should NEVER use it. ;)
>>>
>>> - Totally client-side physics. Each client just runs its own physics,
>>> and broadcasts its position at a fixed rate to all other clients (either
>>> P2P or by sending it through the server which just broadcasts it - the
>>> server is just a dumb simple broadcaster in this case), i.e. you send
>>> out your position to other clients say 20 times a second as unreliable
>>> data. You don't really care if the position gets lost at all, since
>>> another one is coming behind it right away. On the receiving end, each
>>> client needs to smooth out the positions it is receiving from other
>>> clients, since it is much less the rendering FPS. You can do this by
>>> either buffering one or two steps worth of positions, and interpolating
>>> between them - i.e. you wait till you've gotten at least two position
>>> updates from a client, then over some time period (say 50 milliseconds
>>> if updates are happening at 20 Hz), you interpolate the position between
>>> them. Another approach is to just send necessary physics simulation data
>>> (like player velocity), and keep simulating the client locally starting
>>> from the last position/velocity update you got. These two things can be
>>> combined, for instance just always simulate the player locally from the
>>> last time you got an update, however when an update comes in, record the
>>> different in between the update and the current position (the "snap"),
>>> and instead of applying the snap immediately, smooth it out over the
>>> next 50 milliseconds or so. Keep in mind clients are authoritative, so
>>> you need to take care of cheating by non-technical means (i.e. player
>>> moderator system).
>>>
>>> - Co-simulation. The server and client each run their own corresponding
>>> loosely coupled simulations. The client runs on the ASSUMPTION its
>>> simulation is always right i.e. when I shoot, the client assumes I hit
>>> what I actually did, or if I try to move, the client just moves locally.
>>> For EACH client time step the client is sending all the input (i.e.
>>> player movement directions and mouse look) to the server, so the server
>>> can exactly recreate each time step. This should be done via delta
>>> compression of an unreliable packet stream to avoid the cost of reliable
>>> packets. First choose a fixed rate, i.e. 20 Hz. Now every time step is
>>> numbered, so they form an ever-continuing sequence. So when you send a
>>> time step to the server, the server knows the sequence number of the
>>> last time step it got. It sends this sequence number back as a periodic
>>> ack (unreliable, of course, but best piggy-backed on other
>>> server->client updates) so the client knows the last sequence number the
>>> server received (or at least some sequence number less than that in case
>>> the ack gets lost in transit). Every time the client sends an update to
>>> the server, it sends all time steps starting at the last sequence number
>>> the server verified receiving (via that ack the server sent to the
>>> client), up to the most current time step.  So the client must buffer
>>> all time steps it is sending to the server, until it has verified the
>>> server has received them, and is basically just sending this buffer at a
>>> fixed rate (again i.e. 20 Hz) to the server, removing stuff from the
>>> front of the buffer as it gets verification the server actually got it.
>>> If this buffer grows unreasonably large (i.e. some threshold like a few
>>> KB or more where sending it 20 times a second until the server gets it
>>> becomes stupidity), you can just "bail out" at the cost of a possible
>>> round-trip timeout stall by sending the client->server update as a
>>> reliable packet, and just clearing the buffer (since you know the update
>>> will get there). You just don't want to use the bail out option on every
>>> packet, since you want to avoid the latency of reliable traffic at all
>>> costs. Smart encoding of a time step with a simple run-length scheme and
>>> you can get the average size of a time step in transit down to only a
>>> few bits since you may only have one of 8 compass directions, 2D mouse
>>> coords, and maybe some boolean modifiers like jump/crouch, and certain
>>> aspects like the direction don't change very fast. Various events like
>>> shooting , picking up items, etc. should be properly sequenced into this
>>> same stream as well (but encoded via some exceptional means/special
>>> prefix since they are uncommon). You just put these in a server-side
>>> queue for each player, which the server dequeues and runs for each
>>> player at each of its time steps. If it doesn't have any time step info
>>> for a client at a particular server time-step, you can either keep the
>>> client moving in whichever direction he was going, or just have him
>>> stand there - whatever seems most reasonable, but you give the client a
>>> "credit" for that time step, so that when more time steps come in over
>>> the net, you apply them immediately so long as the client has credits.
>>>
>>> Now the tricky part. The server then runs its simulation at whatever
>>> fixed rate you decided along with the client. The server must then send
>>> out server->client updates on positions/velocities of other clients in
>>> the world. You can do this by jumping through hoops to do the whole
>>> delta compression of each other client's input stream to get it from the
>>> server->client, but this just becomes stupidly complex and hoggish of
>>> bandwidth (call that Trade-off #3). You are better off just sending out
>>> the updates from server->client much as you would in the "totally
>>> client-side" case, i.e. just a simple unreliable update containing the
>>> positions/velocities of everything, again at some fixed rate like 20 Hz.
>>> If the update gets lost in transit, you don't care since another one is
>>> coming soon. However, you want to tag each of these server->client
>>> updates with a sequence number. So when the client gets an update, it
>>> knows the sequence number of the last one it got. The client just
>>> locally moves the physics ahead using its own fixed rate simulation
>>> (that hopefully works in the same way as the server's, unless the client
>>> is cheating by modifying it). Now when the client interacts with an
>>> object, i.e. aims at it and shoots it, it can tell the server ("Okay, I
>>> shot player Bob, who was at the position stated in server->client update
>>> #42, from 60 milliseconds had elapsed since that updated, so I had moved
>>> Bob ahead locally 3 time steps from that position.") The server must
>>> buffer the results of each time step of its simulation up to a
>>> reasonable amount of time (say 1 second). So when the server receives
>>> your shot request, it looks in its buffer for physics update #42 (or if
>>> this is a time step > 1 second old, just takes the oldest from the
>>> buffer instead), find Bob's position in this buffered physics update,
>>> predicts him ahead 60 milliseconds/3 time steps in the SAME EXACT WAY
>>> the client would have had it got no more updates during those 60
>>> milliseconds, and then applies your shot to Bob at that position. If you
>>> are quantizing/truncating numbers to send them from server->client in
>>> the updates, you must simulate this on the server when pulling Bob's
>>> position out of the update as well. This way aiming/shooting is
>>> completely WYSIWYG, no disgusting having-to-lead-your-shots-ahead type
>>> of gameplay like in various Quakes.
>>>
>>> Now there can be some small round-off differences from processor to
>>> processor, so the simulation between the client and server may drift
>>> over time. So every  so often the client must either send  what position
>>> it is at, or the server sends what position it has the client at to the
>>> client. In either case, you check if they differ by a substantial
>>> amount, and if so the server sends all the raw/unquantized physics info
>>> needed to the client for them to sync back up (causing an ugly snap, of
>>> which the only sane way to hide is interpolation). If you can manage to
>>> implement the physics entirely without floating point such that there
>>> will never be any drift and hence no snaps, go for that instead (but
>>> seems largely impossible in this day and age with more complicated  
>>>  physics).
>>>
>>> Hopefully this all adequately confuses you. :)
>>>
>>> Lee
>>>
>>> Jacky J wrote:
>>>
>>>
>>>> I'm having a hard time getting my head around some concepts used in
>>>> first person shooter style games, namely sending user input.  I
>>>> understand pretty much everything else: client side prediction, object
>>>> replication, etc.
>>>>
>>>> So here are my questions and thoughts:
>>>>
>>>> 1. The client continuously sends its input to the server.  This is a
>>>> packet that might have a bit field for each button or key. For example
>>>> WASD might take 4 bits, and another few for jump or fire.
>>>> So how often do i send these packets? Is it on a timer or do i send it
>>>> as much as the clients presses those buttons?
>>>>
>>>> 2.  My biggest concern: What if the client is bogged down to 5 fps,
>>>> meaning in the best case, the server is receiving those inputs every 200
>>>> ms.  Surely the server needs to update the client based on the client's
>>>> own framerate, because if you're moving the player 1 unit per input, the
>>>> player will move a lot slower if it's updating less per unit time.
>>>>
>>>> My initial thought was to send some sort of lastsendtime to scale the
>>>> player's input, but then it seems like you could cheat and just send
>>>> really large values to make it seem like you're running at a low fps.
>>>>
>>>> What all should i be sending to the server, and what actions should the
>>>> server be taking based on those inputs?
>>>>
>>>> I have a simple techdemo/game setup using enet called godmode.
>>>> Everything is pretty much set up except for sending client inputs
>>>> correctly.
>>>>
>>>> http://code.google.com/p/godmode/
>>>>
>>>> Thanks
>>>> _______________________________________________
>>>> ENet-discuss mailing list
>>>> ENet-discuss at cubik.org
>>>> http://lists.cubik.org/mailman/listinfo/enet-discuss
>>>>
>>>>
>>>>
>>>>
>>> _______________________________________________
>>> ENet-discuss mailing list
>>> ENet-discuss at cubik.org
>>> http://lists.cubik.org/mailman/listinfo/enet-discuss
>>>
>>>
>>
>> _______________________________________________
>> ENet-discuss mailing list
>> ENet-discuss at cubik.org
>> http://lists.cubik.org/mailman/listinfo/enet-discuss
>>
>>
>>
>>
>> This message and its attachments may contain legally privileged or   
>> confidential information. This message is intended for the use of   
>> the individual or entity to which it is addressed. If you are not   
>> the addressee indicated in this message, or the employee or agent   
>> responsible for delivering the message to the intended recipient,   
>> you may not copy or deliver this message or its attachments to   
>> anyone. Rather, you should permanently delete this message and its   
>> attachments and kindly notify the sender by reply e-mail. Any   
>> content of this message and its attachments, which does not relate   
>> to the official business of the sending company must be taken not   
>> to have been sent or endorsed by the sending company or any of its   
>> related entities. No warranty is made that the e-mail or   
>> attachment(s) are free from computer virus or other defect.
>>
>> _______________________________________________
>> ENet-discuss mailing list
>> ENet-discuss at cubik.org
>> http://lists.cubik.org/mailman/listinfo/enet-discuss
>>



-- 
~Zero



More information about the ENet-discuss mailing list