Beat me like a dead horse, nothing ventured, nothing gained.
Digital Combat Simulator
A mid-air collision can shear off wings, destroy control surfaces, or trigger a catastrophic explosion for both players depending on the angle and speed of impact.
IL-2 Sturmovik Series (Great Battles / 1946)
If two players collide during a dogfight or formation flight, the sim calculates the structural impact on both airframes. You will routinely see both planes lose wing sections, trade paint, or go down in flames together.
War Thunder (Simulator Battles Mode)
"Ramming" is completely physical. Ramming or accidentally clipping another player mid-air will break the wings, tails, or propellers of both participating aircraft.
Aces High uses Netcode
Netcode Variables: Because Aces High relies on server-side authority to determine real-time positioning, a slight latency difference can occasionally make a collision look strange on your screen (e.g., seeming like you barely touched but taking fatal damage). However, mathematically, the engine always applies the kinetic energy and structural impact limits to both hitboxes.
To make netcode produce mutual damage during a mid-air plane collision, you must implement deterministic collision resolution that overrides the local authority of a single client. Because network latency creates "network lag" (where each player sees themselves ahead of the enemy), relying on a simple "if I hit you, you take damage" system will often result in only one plane exploding or delayed, unfair deaths.
Here is how to structure netcode to ensure both planes take damage reliably.
1. Server-Authoritative Verification (The Gold Standard)
The most robust solution is to let the server make the final decision based on a synchronized timeline.
Client-Side Prediction: Both clients fly smoothly and detect the collision locally for immediate visual feedback.
Timestamped RPCs: When a client detects a collision, it sends a Remote Procedure Call (RPC) to the server containing the precise simulation tick or timestamp and the IDs of both planes.
Server Lag Compensation: The server rewinds its simulation history to that specific timestamp. If both planes were close enough to collide at that exact moment in game-time, the server applies mutual damage to both entities and broadcasts the explosion to everyone.
2. Handling the "Dead Man Flying" Problem
A common bug in mutual collision netcode is when Client A kills Client B a split-second beforethe collision happens on Client B's screen. On Client B's screen, they rammed a dead plane but took no damage because the server already registered the enemy as destroyed.
To fix this, implement a "Grace Period" for Latency:
If a plane is destroyed by projectiles or a collision, keep its physical hitbox active on the server for an extra 100–150ms (matching the average network round-trip time).
Even if Plane A is technically "dead," if Plane B rams into its burning wreckage within that tiny window, the server rules it a mutual collision and destroys Plane B as well.
Implementation Breakdown
Feature How It Helps Mutual Damage
Deterministic Ticks: Ensures both clients and the server agree on when the planes occupied the same coordinates.
Lenient Hitboxes: Slightly inflating the collision boundary only for ramming calculations helps account for minor positional network sync errors.
Local Visuals, Delayed Logic
Explode the plane locally immediately so it feels responsive, but let the network health sync happen a few milliseconds later.
A useful rule is:
if server_collision(A, B):
relativeVelocity = A.velocity - B.velocity
impactSpeed = length(relativeVelocity)
damageA = calculateDamage(A, B, impactSpeed)
damageB = calculateDamage(B, A, impactSpeed)
applyDamage(A, damageA)
applyDamage(B, damageB)
broadcast(A, damageA)
broadcast(B, damageB)
Preventing common multiplayer problems
Don't do this:
Client A detects collision
→ damages B
Client B detects collision
→ damages A
With latency, one client might see the collision before the other, or both might register it at slightly different positions/times.
Instead, use a single server collision event:
collisionId = generateCollisionId(A, B)
if collisionId not in processedCollisions:
processedCollisions.add(collisionId)
damage(A)
damage(B)