Recommendations for Authoritative Network Model

Started by
39 comments, last by Angus Hollands 11 years, 10 months ago
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.
Advertisement
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)
enum Bool { True, False, FileNotFound };
Ok, This makes sense. I've modified my gamestate structure to include velocities, and I've implemented your code! smile.png
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.
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.
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?

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.
enum Bool { True, False, FileNotFound };
Thanks again Hplus.
In regards to EPIC, how does it compare to spline based extrapolation?
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.
enum Bool { True, False, FileNotFound };
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
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.
enum Bool { True, False, FileNotFound };

This topic is closed to new replies.

Advertisement