Download

[Resolved] Moving Projectile To Predicted Path, Velocity Issue

Hey everyone,

I’m working on some rudimentary lag compensation for firing of projectiles. Presently, it’s 100% server-authoritative so firing projectiles takes some time with higher ping. I am planning to instantly fire a dummy projectile for the client, then when the server receives the RPC request, account for the one-way latency and move the projectile forward on its natural trajectory. This piece is working great. However, using the velocity of the last index of predictProjectilePath’s output array, the projectile goes off in some random direction after being moved. Clearly I’m misunderstanding what that velocity is, and how to utilize it to set my projectile accordingly.

Does anyone have some advice on how to update the projectile’s velocity to match what it should be at the end of the path prediction?

I haven’t used “PredictProjectilePath” before but you can get the projectiles velocity the standard way (“Get world velocity connected to forward vector”) and that will give you the velocity of the clients projectile. Then spawn/teleport the real projectile accordingly. Unless the projectile has a changing velocity after it’s been spawned then you might as well get the actual velocity then using some prediction.

I’m aware of how to get projectile velocity. Sending position data from the client to the server defeats the entire purpose (and poses the exact same problem). With a 300ms round-trip, data passed from the client to the server is 150ms old by the time the server receives it (or 300ms if the server requests it), meaning it’s no different than the original problem of the projectile spawning 150ms later than when the user fired it. That’s why server calculation is necessary for lag compensation.

As far as the server is concerned, we’re essentially looking into the future of the projectile by 150s in the above scenario (because the client has fired 150ms ago, and we are just now receiving the request to the “Fire Projectile” RPC). Asking the client where its projectile is takes 150ms to hit the client, and 150ms to receive the response (meaning we would know where the client’s projectile was 300ms ago). Sending the data from the client upon firing would simply mean we sent the exact same data as the server would have at the time of hitting the RPC for firing – a fresh, new projectile which has not traversed a single frame, yet. Also, it would not match the data of where the client presently sees the projectile.

Communication between the client and server for lag compensation is simply not feasible. That’s the problem which needs solved with calculation. Throwing more communication which suffers the same problem into the mix isn’t really going to do anyone any favors.

For anyone else potentially curious about moving a projectile “forward in time” on its natural trajectory, the only problem I faced was my own oversight. I had the logic firing off in the constructor of the projectile itself, and it is imperative that it is ran in onBeginPlay (or, at least, after onBeginPlay fires). Adjusting projectiles’ location/velocity prior to onBeginPlay will typically result in erratic behavior due to the logic that exists inside onBeginPlay. I totally spaced this when I was crafting this rudimentary lag compensation.

This solution is working exactly as intended now.

If you’re curious about lag compensation with projectiles, my setup is as follows:

As the server, upon spawning the projectile, get the player’s ping from the player state. Multiply this by 2 to get the single-trip latency at the time of spawning the projectile. Fire off a projectile path prediction method, and set the max sim time to the aforementioned latency divided by 1000. Break the result, get the last index of the path data, and set your projectile’s location + velocity to the results. Voila, you have now synced your server’s projectile with the client’s dummy projectile, within a margin of error (due to the fact that ping constantly changes, and we aren’t measuring the time THAT particular packet took in transport, which is very likely to differ from ping at least slightly, and potentially greatly due to lag spikes).

Few notes:

There is still the conundrum that after “fast forwarding” the projectile, the replicated projectile still takes time to update on the client. Therefor, for the sake of example, if you had the client fire a dummy projectile, then implemented this rudimentary lag compensation, and had the client delete their projectile to account for the server’s replicated projectile being created, the client is likely to see the projectile move back a bit. I would personally recommend simply not replicating the server’s projectile to the owning client to account for this, and leaving the client’s dummy projectile alive.

As always, relying on client data for positioning/velocity of projectiles (or nearly anything else) is a HUGE security hole. That means any random shmo with Cheat Engine (or similar) could fire projectiles from any area of the map, including 10 units in front of another player’s face, at any velocity they choose. There are tremendous amounts of incredibly intelligent people out there who would find this hole within about 5 minutes of tinkering in your game. Be cognizant of that when developing, and have your server do the leg work so you can trust the data.

This solution alone does not account for where the client sees other characters at the time of firing. This will absolutely result in clients getting direct hits with projectiles that the server sees as total misses, therefor they deal zero damage and frustrate players to no end. To truly have lag compensation, one would need to record player positions for a set duration, then have logic in place to check for hits based on where they were as opposed to where they are now. That, or have logic in place for when a client gets a direct hit with a dummy projectile that will have the server check if the player was at that position 4*ping milliseconds ago (must account for one-way latency to hit the RPC, PLUS one-way latency prior to the point of hitting the RPC). The latter would be far more light on the server.

I’m no expert on this topic, so take my findings with a grain of salt.