In my test project I integrated units which are able to shoot at each other with instant hits. While the own player ghost unit is predicted and uses prediction spawned shots the enemy units are interpolated. Thanks to the lag compensation of ECS Netcode the player can shoot and hit other units without having to aim ahead which is really impressive. There is only one issue I have so far and that is the limitation of the history size for the lag compensation. Lag compensation uses historic collision worlds and the amount of the worlds is hard limited to 16. I am not sure on this topic, but I assume 16 means that the server can look 16 network server ticks into the past for raycast collisions and the like. In my test project this was enough for compensating a latency between client and server of approximately 130 to 140 milliseconds using the ECS Netcode simulator. On higher latencies the most aged available collision world is used which then means that enemy units are in different spots for the server-based raycast collision checks than the player will see them rendered on the client. The awesome multiplayer game play experience is blown away instantly then.
Firstly my question is: Why is there a limitation for the count of historic collision worlds used for the lag compensation at all?
And secondly at least I would like to see an considerable increased limit of historic collision worlds to enable the experience of lag compensation for higher latencies.
Unfortunately there is a limit today and it is 16.
The reasons behind this is multi-fold, but let’s say it was mostly a problem due to the fact we couldn’t store this
struct efficiently and pass them to a job was also tricky (because of the size). So we have to limit ourself a bit.
We can provide large buffer now, but it still requires some changes that will be not available for dots 1.0. But we have plans to extend that to allow you pass and configure which size you need.
I dont see how this has anything to do with non-raycast projectiles… and more than 140ms latency is IMO way past any acceptable latency for shooters anyway
Well, maybe I dont have the full picture (I’m just a hobbyist after all), but for shooters, don’t you usually have the clients do hit registration? Then if a client detects a hit, it lets the server know at which point the server will go “back in time” to fully resimulate the projectile in question. So for hitscan weapons you only need a buffer large enough to account for the lag (one way iirc, so 1/2RTT), but for physics-based projectiles you need a bigger buffer containing all collision worlds of the past 1/2RTT + bullet travel time. Otherwise, if a bullet takes a full second to reach its target, you can’t verify whether the bullet hit a wall during the first 0,5 seconds
I mean if the buffer is used to move the server Xms into the past, why would a bullet with raycasts over multiple frames need more “past” than a single raycast? Time progresses on both machines. So to rephrase that, I think you can technically add the frames the bullet simulation takes to the available buffer time. God this is hard to describe.
Sorry to only get back to this now, I haven’t been able to work on my project at all these past weeks.
I don’t think I understand what you’re saying? At least, what you’re saying makes sense to me when you’re wanting to check only against the static collision world. But if a projectile takes, say, 900ms to hit the target, the server will only be able to verify collisions against the dynamic collision world for the last 250ms - 1/2RTT of the projectile’s lifetime. That can be problematic if the projectile encountered, say, a vehicle 200ms after being fired. Since the position of the vehicle isn’t in the buffer anymore, the server will deal damage to the target even though the projectile shouldn’t have hit them
The time it takes the projectile to hit the target is thankfully irrelevant. Imagine two scenarios:
I shoot a nearby enemy. My predicted spawned bullet collides with the victim on the very next prediction loop full tick. I therefore predict the hit on my client. Later, the server uses lag compensation to rollback the victim ghost say 8 ticks (the exact number of ticks is determined by my half-ping to the server, ignoring other nuances). The server reconciles my shot, and agrees with my client prediction. The shot registers, client & server agree. Good.
I shoot a far enemy. My predicted spawned bullet travels through the air for ~900ms. Finally, I predict the hit on my client as my predicted bullet collides with the interpolated victim (who has since moved, due to the ballistic hang time). The server again uses lag compensation to rollback the victim ghost 8 ticks, not by 8 ticks + 900ms! Again, the server detects that my client hit what my client was aiming at, and awards the shot. Client & server agree, the shot lands.
The intuition is:
The server is only trying to work out what the attacker client sees at the time of the projectile impact. It doesn’t matter how long the projectile is alive. You could imagine a slow moving, high arc artillery shot. If it takes 50s to land, whatever is within its blast radius when it finally lands is the thing we should care about.
Intuitively: A player must “lead” projectiles which have hang time. Hit-scan weapons don’t require you to aim ahead (unless your ping goes above our history threshold).
Ok, I think I’m starting to understand. In effect, lag compensation is only used to verify on the server that the point of impact of the projectile as reported by the client did, in fact, have an enemy there. As opposed to what I had assumed, where the server would go “back in time”, and fully resimulate step by step the path of the projectile from the moment it was fired to the moment it hit the target.
Am I right then in saying that there’s no way to verify whether or not the shot was intercepted by other dynamic objects like players/vehicles, unless it is reported by the client? I.e. hackers wallbanging through a vehicle would be possible, barring a good anti-cheat
Oh, nah, you can check that too. Essentially, for every tick the projectile is in flight (on the server), the server can check to see if it collides with anything in the Lag Compensated physics world, using the same PhysicsWorldHistorySingleton.GetCollisionWorldFromTick API (simplified example here).
I.e. Every tick the bullet is in flight, the server uses the historic collision world to see an approximation of what that shooter client saw when the bullet was at the same spot, and checks collisions against all ghosts (like players & vehicles) to determine if the player hit anything/everything.
Thus, the server does not require the client to report a hit, it can simply benefit from that info (as verifying that the server and client agree statistically often is a good check that your lag compensation is working correctly).
Another approach is to turn the projectile into an interpolated ghost, simplifying hit detection on the server (at the expense of perceived lag in the eyes of the attacker client, after the projectile leaves the players hands/weapon). This can work for stuff like slow moving grenades & rockets, and spawned-in call-ins like artillery & stratagems etc.
Quick update: We landed a quick fix raising the max size to 32 (server default will still be 16), released expected 1.5.0 most likely. Did you have an exact value - greater than that - you needed? We may need to slightly change how it’s stored to go higher.