I’m trying to add entity interpolation, and can’t really find any references and code example on how it all works together. Could someone please explain it? This is what I have for entity interpolation (I haven’t started client-side prediction as I have no idea where to begin).
Also can someone please let me know what I’m doing wrong? Here’s a brief video of how messed up it is. The box represents the current received message from the server (the player’s x and y), and the player is supposed to interpolate to that position, but instead hops.
The way my networking currently works, is the server receives messages from the clients, and adds it to a queue (dropping messages if they occur too frequently, so if you send 20 messages in the span of a second). Then the server does a tick, every 500ms, and processes those messages, and immediately dispatches it to the clients. What are some better ways of handling this? And can someone explain entity interpolation / client side prediction
Edit: I was able to find something from Valve here: https://developer.valvesoftware.com/wiki/Source_Multiplayer_Networking, but they send around 20 snapshots per second. My server sends these “messages” or packets, once every 500ms. Also I’m not sure if I’m doing it right, but my server also doesn’t send “snapshots”, I have different packets that send different information, every 500ms. So for example a packet for updating positions, a packet that updates chats, etc. Should this all be in 1 “snapshot” instead of different packets?
You’re not actually doing linear interpolation in your code snippet and you don’t take time into account.
Right now you’re moving the player closer to the target by a percentage of the remaining distance every tick, this distance get’s smaller every tick until a new position comes in, you see the problem now?
What you really want is move him at constant speed from the 2nd last to the last received point.
You can do that by taking into account the rate at which your packets come in, if packets come in every 500ms then the player must have moved from 2nd last to last position in 500ms.
When a new packet comes in you make the current position the 2nd last position and move to the new position within 500ms.
Here’s your code with the new interpolation:
private void interpolate(Player player) {
//The client needs to know his last two position-updates
//AND his current position
//AND the time since the last update from the server
Vector3 newPosition = player.getServerPosition(0);
Vector3 oldPosition = player.getServerPosition(1);
Vector3 currentPosition = player.getLastPosition();
float time = player.getTimeSinceLastPacket(); //time from 0 seconds to 0.5 seconds in your case
// we interpolate from old to new position within 500ms
currentPosition.set(oldPosition);
currentPosition.interpolate(newPosition, time/0.5f, Interpolation.linear);
}
You could also let the client continually measure the time between updates to get a better interpolation time.
To really smooth it out you need to cache 3 or more position updates so that you don’t depend on the packets coming in precisely every 500ms and you need more updates per second plus prediction for responsiveness.
Thank you for the help, I’m still trying to wrap my head around it. From my understanding the best way to approach this would be to add each new position in a Queue with a timestamp, and then interpolate through every position (using that timestamp) in order to get that smoothness with no jitter?
Also how is this problem dealt with where the interpolation ends up going through walls. Is that where you would start implementing game logic such as A Star? In that case wouldn’t the movements no longer be genuine, as the player walked a certain path, but on another persons screen, they walked a different one.
[quote]the best way to approach this would be to add each new position in a Queue with a timestamp, and then interpolate through every position (using that timestamp) in order to get that smoothness with no jitter?
[/quote]
You can do that, it’ll work. How well it works (in terms of smoothness) depends on how much jitter your connection has if you use the packet arrival times for the timestamps, otherwise you’d need to synchronize time between server and client to get really smooth interpolation, but you probably won’t need that at 2 updates per second.
[quote]interpolation ends up going through walls
[/quote]
Yes, you could use local pathfinding to patch up the path if it’s not very long, it sounds simple in theory.
You could also just transmit more movement updates per second, that would have the benefit of reducing the lag quite a bit.
I don’t have much experience with networking in games, but I think that 500ms are quite long. A more reasonable delay would be something like the 20 snapshos per second as you mentioned, so a client would get updates from the server every 50ms. Do you have a specific reason behind this rather large delay?
Could you tell me more about- what client sends to server and what server sends to client?
Best approach for this kind of problem (but hardest) is to use functions instead of snapshots (for example: player is moving with N/h speed in some vector). Server can calculate events in some cyclic manner or predict events based on set of functions. If something happens here, clients is informed by event or function change (or whatever. Depends on game). It’s hard as hell but most efficient.
" So for example a packet for updating positions, a packet that updates chats, etc. Should this all be in 1 “snapshot” instead of different packets?" -> depends on architecture. For some games it’s better the use functions, for some snapshots, for some event driven.
PS. take a look at my game:
Design is done on top of actor model (Akka). It’s event driven. Client sends commands to server, Server solves it and responds with events depends on it’s state. Client reacts to the events (reactive) same as other actors in this world (AI for example).
Still not sure what I’m doing wrong, I’ve changed the code up so it interpolates from the old server position, to the new server position. But it jumps around like crazy now.
This is my player movement packet
public boolean read(WebSocket webSocket, GamePacket gamePacket) {
UpdateMovement updateMovement = (UpdateMovement) gamePacket;
Player player = GameEngine.players.get(updateMovement.getId());
if (player == null) {
return false;
}
//Sets the last position of the player
final long milliseconds = TimeUtils.millis();
player.getLastPosition().x = player.getServerPosition().get(0).getVector3().x;
player.getLastPosition().y = player.getServerPosition().get(0).getVector3().y;
player.getServerPosition().remove(0);
player.getServerPosition().add(new Location(updateMovement.getX(), updateMovement.getY(), 0, milliseconds));
return false;
}
And with the new interpolation
private void interpolate(Player player) {
//The client needs to know his last two position-updates
//AND his current position
//AND the time since the last update from the server
Location oldPosition = player.getServerPosition().get(0);
Location newPosition = player.getServerPosition().get(1);
float time = ((newPosition.getTimeStamp() - oldPosition.getTimeStamp())) * 0.001f; //time from 0 seconds to 0.5 seconds in your case
// we interpolate from old to new position within 500ms
player.lastPosition.interpolate(newPosition.getVector3(), time, Interpolation.linear);
batch.draw(AnimationDef.getDefinition(0).getFrame(), player.lastPosition.x, player.lastPosition.y);
}
So float time = t * 0.001f, usually gives me a number between 0.49999997 to 0.501, depending on when the last packet was received. So for example. If it took, 515ms for the packet to be received, then t * 0.001f, would be 0.515 for the interpolation alpha parameter. Doing 0.49999997f / 0.5f wouldn’t produce any results.
The issue is, that no matter what the number is, it still jumps like crazy.
QDbl2j9u67A
I’ve taken a lot of inspiration from games like RuneScape, where they have high tick times, but still manage to run smooth. A higher tick time guarantees that less bandwidth is being used, this is really good for mobile where people sometimes use cellular data instead of wifi. This also causes less work for the server as the less packets it has to read and write, the better the overall performance would be. I love what VaTTeRGeR suggested, where you could possibly send positional updates at 100ms, and sending every other update at 500ms. I just can’t seem to get the interpolation part working.
At the moment the only packets sent between the client and server are positional movements, login response, chat, and a packet that notifies the client whether or not a new player has joined or left the server. These packets are all sent in a 500ms interval. And each session connected to the server can only send a max of 20 packets per 500ms, this is to make sure that if the server were to be spammed with 5,000 packets per second, only 20 of those are actually accepted by the server, while the rest are dropped. Your game looks really nice btw, love the dialogue system.
You obviously didn’t take the time to understand my very very VERY simple code snippet, this here is the last piece of advice you’re gonna get from me, get your shit together, use your brain, use google.
Here it is again with more elaborate comments:
private void interpolate(Player player) {
// This is the position we are interpolating towards, we reach it when time is 0.5
Vector3 newPosition = player.getServerPosition(0);
// This is the position we are interpolating from, we are at this position when time is 0.0
Vector3 oldPosition = player.getServerPosition(1);
// This is the position of the player, it gradually moves from player.getServerPosition(1)
// to player.getServerPosition(0) if you do everything right
Vector3 currentPosition = player.getLastPosition();
// This is the time SINCE the last packet arrived, NOT the time between packets!
// It is updated (+= 1f/framesPerSecond) every frame, when the packet just arrived it is zero and
// right before the next packet arrives it should be around 0.5
float time = player.getTimeSinceLastPacket(); //time from 0 seconds to 0.5 seconds in your case
// currentposition is set to oldPosition so that we can interpolate from old to new position
// we DO NOT want to interpolate from the players current position to newPosition
currentPosition.set(oldPosition);
currentPosition.interpolate(newPosition, time/0.5f, Interpolation.linear);
// The above now effectively does "currentPosition = oldPosition + (newPosition - oldPosition) * (time/0.5f)"
}
Not sure why you are so angry, I’m sorry if I did anything to upset you. I do use my brain, generally I come from a community where people don’t like helping each other so I ask questions while also looking for the answer myself. If someone is nice enough to respond, then that’s awesome (and helps a ton with finding answers), if they don’t I keep on researching.
With the code example, “getTimeSinceLastPacket” could have meant anything so I was just a bit confused on it since I thought it was equivalent to currentTimeMillis in Java but now I understand that it means the deltaTime it took to get from one packet to the next.
Anyway thank you again for the hospitality, and sorry again for offending you