Hit Detection

TCP isn’t used because the packets are time sensitive.
The problem with TCP is that it abstracts data delivery as a reliable ordered stream. Because of this, if a packet is lost, TCP has to stop and wait for that packet to be resent. This interrupts the steady stream of packets because more recent packets must wait in a queue until the resent packet arrives, so packets are received in the same order they were sent. This adds to delay (LAG).

Hence one of if not the main reasoning behind using UDP. For reliability a custom system is layered on top of UDP. The goal of a reliability system is to know which packets arrive at the other side of the connection. This is done by sequencing, yet unlike TCP it does not resend packets with a given sequence number. This helps avoid out of order receival.

The game never stops and resends packet n if it was lost. Resends are left up to the application to compose a new packet containing the data that was lost, if necessary, and this packet gets sent with a new sequence number. Determining resend is based on non receival of an ack.

Determining loss
Because of the inherent holes in the reliability system, multiple acks must be included in each packet. Couple this with redundancy sends to ensure that both the client and the server receive acknowledgements for received packets.

An ack is an acknowledgement of receival. Consider it a receipt for a packet’s arrival and acceptance.

Using an ack bitfield (4 bytes per ack) you send multiple acks per packet, multiple times, just in case one packet with the ack doesn’t get through. For a 60 tick server you would send 63 acks per packet.

For clarity…
The server just received packet “1000” from the client. It in turn creates an ack for packet 1000. This ack will be the first ack in the ack bitfield, followed by the 62 previous acks. On the next receival it will generate an ack for the new packet, that ack will be the first ack in the bitfield, followed by the ack for packet 1000 and the previous 61 acks.

This ensures that the client/server will receive the acknowledgement. If you don’t get an ack for a packet within 1 second of projected arrival time, then it is considered as lost.

  • Each time a packet is sent, we increase the local sequence number
  • When a packet is received, the sequence number of the packet is checked against the remote sequence number. If the packet sequence is more recent, update the remote sequence number.
  • When the packet headers are composed, the local sequence becomes the sequence number of the packet, and the remote sequence becomes the ack. The ack bitfield is calculated by looking into a queue of up to 63 packets, containing sequence numbers in the range [remote sequence - 62, remote sequence]. Set bit n (in [1,62]) in ack bits to 1 if the sequence number remote sequence - n is in the received queue.
  • Additionally, when a packet is received, ack bitfield is scanned and if bit n is set, then we acknowledge sequence number packet sequence - n, if it has not been acked already.