• Announcements

    • khawk

      Download the Game Design and Indie Game Marketing Freebook   07/19/17

      GameDev.net and CRC Press have teamed up to bring a free ebook of content curated from top titles published by CRC Press. The freebook, Practices of Game Design & Indie Game Marketing, includes chapters from The Art of Game Design: A Book of Lenses, A Practical Guide to Indie Game Marketing, and An Architectural Approach to Level Design. The GameDev.net FreeBook is relevant to game designers, developers, and those interested in learning more about the challenges in game development. We know game development can be a tough discipline and business, so we picked several chapters from CRC Press titles that we thought would be of interest to you, the GameDev.net audience, in your journey to design, develop, and market your next game. The free ebook is available through CRC Press by clicking here. The Curated Books The Art of Game Design: A Book of Lenses, Second Edition, by Jesse Schell Presents 100+ sets of questions, or different lenses, for viewing a game’s design, encompassing diverse fields such as psychology, architecture, music, film, software engineering, theme park design, mathematics, anthropology, and more. Written by one of the world's top game designers, this book describes the deepest and most fundamental principles of game design, demonstrating how tactics used in board, card, and athletic games also work in video games. It provides practical instruction on creating world-class games that will be played again and again. View it here. A Practical Guide to Indie Game Marketing, by Joel Dreskin Marketing is an essential but too frequently overlooked or minimized component of the release plan for indie games. A Practical Guide to Indie Game Marketing provides you with the tools needed to build visibility and sell your indie games. With special focus on those developers with small budgets and limited staff and resources, this book is packed with tangible recommendations and techniques that you can put to use immediately. As a seasoned professional of the indie game arena, author Joel Dreskin gives you insight into practical, real-world experiences of marketing numerous successful games and also provides stories of the failures. View it here. An Architectural Approach to Level Design This is one of the first books to integrate architectural and spatial design theory with the field of level design. The book presents architectural techniques and theories for level designers to use in their own work. It connects architecture and level design in different ways that address the practical elements of how designers construct space and the experiential elements of how and why humans interact with this space. Throughout the text, readers learn skills for spatial layout, evoking emotion through gamespaces, and creating better levels through architectural theory. View it here. Learn more and download the ebook by clicking here. Did you know? GameDev.net and CRC Press also recently teamed up to bring GDNet+ Members up to a 20% discount on all CRC Press books. Learn more about this and other benefits here.
Sign in to follow this  
Followers 0
Angus Hollands

Recommendations for Authoritative Network Model

40 posts in this topic

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.
0

Share this post


Link to post
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:[list=1]
[*]Store all received gamestates from the server. This is part of the networking just to set the last received gamestate
[*]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.
[*]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.
[/list]
If this is correct, should input prediction also modify the extrpolated state? I presume so.. Edited by Angus Hollands
0

Share this post


Link to post
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.

1

Share this post


Link to post
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?
0

Share this post


Link to post
Share on other sites
[quote name='Angus Hollands' timestamp='1339333160' post='4947905']
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?
[/quote]

[url=http://www.mindcontrol.org/~hplus/epic/]A demo of forward-extrapolation, with code[/url].
0

Share this post


Link to post
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.
0

Share this post


Link to post
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)
0

Share this post


Link to post
Share on other sites
Ok, This makes sense. I've modified my gamestate structure to include velocities, and I've implemented your code! [img]http://public.gamedev.net//public/style_emoticons/default/smile.png[/img]
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
0

Share this post


Link to post
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.
0

Share this post


Link to post
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:
[CODE]
# 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)
[/CODE]

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
0

Share this post


Link to post
Share on other sites
[quote name='Angus Hollands' timestamp='1339605724' post='4948851']
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.
[/quote]

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.
1

Share this post


Link to post
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.
1

Share this post


Link to post
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
[CODE]
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
[/CODE]
0

Share this post


Link to post
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.
0

Share this post


Link to post
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
0

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  
Followers 0