# Recommendations for Authoritative Network Model

This topic is 2043 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

## Recommended Posts

Thanks Hplus!
I've taken a slightly different approach here. Firstly, I have used RLE on the entire "batch" of inputs, typically a batch consists of 6 ticks worth of input. The RLE is used on the previous six packets before the current six packets (collected between network ticks). In the event that an input is dropped, it can be retreived during the net packet, or then corrected by the server.
After effectively doubling the packet size in terms of content, the use of run length encoding compresses the packet to 80 bytes, from 300! It's rather useful.

##### Share on other sites
Question n-1:

At the moment, I have the basics working. But, It doesn't account for delay from server to client, so when i receive a gamestate (delta or snapshot) It applies it as the current gamestate.
I'm now trying to remember our discussion earlier..
Leaving input prediction until later, is this correct:

1. Store all received gamestates from the server. This is part of the networking just to set the last received gamestate
2. Calling this every frame; Extrapolate using two or three game states to determine what the server's gamestate should look like at the current tick. And store this as simulated every tick.
3. When you receive a gamestate, get the delta between the simulated gamestate for that tick and the server's gamestate, and then apply the delta.

If this is correct, should input prediction also modify the extrpolated state? I presume so.. Edited by Angus Hollands

##### Share on other sites
There are three ways to deal with corrections from the server.

1) Take the "sledgehammer" approach. If you get a correction, which should be very rare, then you snap the user back to the corrected position at the current step on the client. The server needs to remember this correction, too, and apply the same correction on the same step. This is simple, robust, and feels terrible to the user, so only do this if corrections really are rare.

2) Take a "playback log" approach. When you send input to the server, save the state you think the client has for each step. When you get a correction, correct the state at that step, and then re-play the input locally up to the current step number. This uses more RAM and more synchronization, but is less impactful to the user, and will correctly deal with simple "one packet was dropped" problems.

3) Take a "smooth into destination" approach. When you get a correction to the server, don't immediately change the client, but instead keep a value for how much the difference in position is. Then slowly apply this delta in position over some time -- say, half a second, or a second. This requires de-coupling the server-decided state, the client-decided state, and the displayed state, and is a lot more complex. It _can_ smooth out corrections more than the other options, but it's in my opinion too complex to be worth it, and the user will see "non-physical" behavior ("gliding") over more than just a single step.

##### Share on other sites
Im starting on client prediction, but i have to consider a few things. Firstly, are there any good resources for extrapolation of positions and orientations in a fast manner?

##### Share on other sites

Im starting on client prediction, but i have to consider a few things. Firstly, are there any good resources for extrapolation of positions and orientations in a fast manner?

A demo of forward-extrapolation, with code.

##### Share on other sites
I am sorry for not clarifying; I cannot read C++ at all, being almost non-identical to Python. I could create it in python if i knew how it was supposed to work :S
If that's too much to ask, then don't worry. I just don't see how it differs from typical extrpolation, though i see it is smoother.

##### Share on other sites
If you have positions and velocities at time stamps:
t0, P0, V0
t1, P1, V1
And you want to know the position at time t2 > t1, where t2 is "now" and t0, t1 are "in the past."

Then the math says:
The position at time t2 based on the t0 data is P02 = P0 + V0 * (t2 - t0).
The position at time t2 based on the t1 data is P12 = P2 + V1 * (t2 - t1).
Now, you can extrapolate the error. The error delta expressed as a velocity is Ve = (P12 - P02) / (t1 - t0).
So, take the position as we know it based on the last timestamp, forward extrapolated (P12), and add the error velocity times the extrapolation time (t2 - t1):
Pout = P12 + Ve * (t2 - t1).

Now, each time you receive a new timestamp, and shift t1 -> t0, new -> t1, you will get a discontinuity in display position. To fix this, you can extrapolate by calculating the new "target position" only once, each time you get a new update, and the move towards that position at the appropriate velocity.

Thus, at time t1 (in this case), calculate the position you'd want to be at one network packet's interval from now, plus the forward extrapolation time:
Pout = P12 + Ve * (t2 - t1 + tN)
Snapshot the current player position Pcur. Calculate the needed velocity to get from current player pos to desired target pos at that time.
Vd = (Pout - Pcur) / tN
So, the position at time tx is then simply:
Ppos = Pcur + Vd * (tx - t2)

##### Share on other sites
Ok, This makes sense. I've modified my gamestate structure to include velocities, and I've implemented your code!
The problem i have now (there's always a problem!) Is that occaisionally it seems to break when clients jump - the client starts "drifting" downwards, ignoring collisions. This would be fine but it also ignores the actual Z position.
Could it be that the gravity of the Physics engine is affecting it?
I actually get this with or without input prediction, so I can't think of what's causing it. Edited by Angus Hollands

##### Share on other sites
And, another conceptual question!
With rotation of players, Mouselook that was purely server side would be laggy and almost unplayable. Should it be purely client side and transmitted? or would you use something like epic? - I can recreate that for rotations.

##### Share on other sites
Oh darn this.
I thought it would implement correctly, but it doesn't seem to work - every so often it starts to throw wierd position offsets...

Here is my python interpretation:
 # Updated every network recv tick_rate = 60 network_packet = 6 #tick_0 = ... tick_1 = ... current_tick == ... position_0, orientation_0, velocity_0 = state_0 position_1, orientation_1, velocity_1 = state_1 position_now = user.worldPosition.copy() # Extrapolated (current) position according to tick 0 position_acc_tick_0 = position_0 + velocity_0 * ((tick_now - tick_0) / tick_rate) # Extrapolated (current) position according to tick 1 position_acc_tick_1 = position_1 + velocity_1 * ((tick_now - tick_1) / tick_rate) # Get the error between two predictions as a velocity vector velocity_err = (position_acc_tick_1 - position_acc_tick_0) / ((tick_1 - tick_0) / tick_rate) # Get the estimated position the client needs to be at in one network ticks time, accounting for error position_out = position_acc_tick_1 + velocity_err * ( (tick_now - tick_1 + network_packet) / tick_rate ) # Get's velocity needed to move from current position to target position within one network tick needed_velocity = (position_out - position_now) / (network_packet / tick_rate) # Updated every frame new_velocity = needed_velocity * ((current_tick - packet_tick) / tick_rate) 

Can you see any problems with this? The only other problem i can foresee is a problem with the arrival times.

EDIT;

I beleive it was caused by a side effect of having the logic processed before physics; linv didn't account for friction.
However, my question still remains about orientation and mouselook - where is it handled? Edited by Angus Hollands

##### Share on other sites

And, another conceptual question!
With rotation of players, Mouselook that was purely server side would be laggy and almost unplayable. Should it be purely client side and transmitted? or would you use something like epic? - I can recreate that for rotations.

Typically, an FPS will make mouselook client authoritative. One input from the client is the heading(yaw) and pitch of the forward vector.
Note that doing this makes aimbots easier to write, but that's usually an OK trade-off, because you have to find cheaters through external means anyway.
Thus, the input state from a client is "movement and action keys + mouselook direction."

EPIC can only forward extrapolate based on past data, it's not typically something you use for the local player.

##### Share on other sites
Thanks again Hplus.
In regards to EPIC, how does it compare to spline based extrapolation?

##### Share on other sites
Splines may make for softer turns, but also have all kinds of speed derivative problems during interpolation. Epic prefers to make a sharper change in speed at each snapshot, and be linear in between.
Personally, I don't much like the spline and polynomial based methods, because when they go wrong, they tend to go *REALLY* wrong.

##### Share on other sites
Haha, i see. That does make some sense, considering their nature.
I've really hit a snag now and I could use some help.
Basically, I have the delta updates working fine, and full updates. I am using a physics engine for movement on the server, and for client side prediction.
However, I am correcting the errors between prediction and official server values, and it is horribly juddery.
I don't think it is looking at mismatching data, and i just think that it is a small variation between server and client physics.
The other part i beleive to be the issue is that the server updates in "Bursts" whereby after receiving a packet containing 6 inputs or so, it idles and thus it would show a temporary decrease in velocity.
To be honest, here's my query:
How can I smoothly use both client side and official information from the server? When i apply correction information at the moment, it just dies, especially when going up slopes

Here is my correction code, called every update from server
 def correct_errors(self, gamestate_tick): '''Correct error between prediction and server state @param gamestate_tick: tick to correct from''' # Iterate through all predictions, update to reflect corrections simulated_states = self.simulated_states received_states = self.received_states predicted_state = simulated_states.get(gamestate_tick) received_state = received_states.get(gamestate_tick ) username = self.username # Try to get the user prediction and server determined gamestates try: user_prediction = predicted_state[username] server_data = received_state[username] # If this fails, then return as both states are required except (TypeError, KeyError) as err: return predicted_position, predicted_orientation, predicted_velocity = user_prediction server_position, server_orientation, server_velocity, server_states = server_data # Applies the modification to the predictions position_error = server_position - predicted_position velocity_error = server_velocity - predicted_velocity #position_error # Prevents local and remote collision errors # Update existing user position to reflect packet difference (error) user = self.users.get_user(username) user.object.worldPosition += position_error # Modify predicted state data for tick, state in list(simulated_states.items()): # Ignore & remove old predictions before this gamestate if tick < gamestate_tick: self.simulated_states.pop(tick) continue # Get the data of the local user from the state try: state_position, state_orientation, state_velocity = state[username] except: continue # Get prediction errors corrected_position = state_position + position_error corrected_velocity = state_velocity + velocity_error # Modify the predicted state to reflect corrections state[username] = corrected_position, state_orientation, corrected_velocity 

##### Share on other sites
You should clock the server at the same rate as the client. If you send 6 inputs in one packet, the last input should be processed the 6th tick after the packet is received. This means you need a queue of input events, and new events from the client get added to the end of this queue. Typically, you also want to time-stamp each input even with which tick it was generated for.

##### Share on other sites
A little update. For some reason the user prediction is really jittery.
I think i've determined the causes;
Whenever the client receives an update, it gets the delta between the prediction for that tick and the actual server copy.
I was sending the last received delta update, and the client then set that as the update to apply the delta to, however due to the upstream latency
this wasn't acknowledged by the server until too late.
Why is using a delta advantageous? Apparently float sizes are the same. Edited by Angus Hollands