From ae7adeeda69cc61ed4e9409b0a4d3d13b5d40332 Mon Sep 17 00:00:00 2001 From: Simon Howard Date: Tue, 10 Jan 2006 19:59:26 +0000 Subject: Reliable packet transport mechanism Subversion-branch: /trunk/chocolate-doom Subversion-revision: 279 --- src/net_common.c | 195 ++++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 186 insertions(+), 9 deletions(-) (limited to 'src/net_common.c') diff --git a/src/net_common.c b/src/net_common.c index a89b1bba..92c050e1 100644 --- a/src/net_common.c +++ b/src/net_common.c @@ -1,7 +1,7 @@ // Emacs style mode select -*- C++ -*- //----------------------------------------------------------------------------- // -// $Id: net_common.c 268 2006-01-08 04:52:26Z fraggle $ +// $Id: net_common.c 279 2006-01-10 19:59:26Z fraggle $ // // Copyright(C) 2005 Simon Howard // @@ -21,6 +21,9 @@ // 02111-1307, USA. // // $Log$ +// Revision 1.4 2006/01/10 19:59:26 fraggle +// Reliable packet transport mechanism +// // Revision 1.3 2006/01/08 04:52:26 fraggle // Allow the server to reject clients // @@ -36,6 +39,8 @@ // Common code shared between the client and server // +#include + #include "doomdef.h" #include "i_system.h" @@ -51,24 +56,40 @@ #define KEEPALIVE_PERIOD 1 -// Initialise as a client connection +// reliable packet that is guaranteed to reach its destination -void NET_Conn_InitClient(net_connection_t *conn, net_addr_t *addr) +struct net_reliable_packet_s +{ + net_packet_t *packet; + int last_send_time; + int seq; + net_reliable_packet_t *next; +}; + +static void NET_Conn_Init(net_connection_t *conn, net_addr_t *addr) { - conn->state = NET_CONN_STATE_CONNECTING; conn->last_send_time = -1; conn->num_retries = 0; conn->addr = addr; + conn->reliable_packets = NULL; + conn->reliable_send_seq = 0; + conn->reliable_recv_seq = 0; +} + +// Initialise as a client connection + +void NET_Conn_InitClient(net_connection_t *conn, net_addr_t *addr) +{ + NET_Conn_Init(conn, addr); + conn->state = NET_CONN_STATE_CONNECTING; } // Initialise as a server connection void NET_Conn_InitServer(net_connection_t *conn, net_addr_t *addr) { + NET_Conn_Init(conn, addr); conn->state = NET_CONN_STATE_WAITING_ACK; - conn->last_send_time = -1; - conn->num_retries = 0; - conn->addr = addr; } // Send a packet to a connection @@ -158,16 +179,117 @@ static void NET_Conn_ParseReject(net_connection_t *conn, net_packet_t *packet) } } +static void NET_Conn_ParseReliableACK(net_connection_t *conn, net_packet_t *packet) +{ + unsigned int seq; + + if (!NET_ReadInt8(packet, &seq)) + { + return; + } + + if (conn->reliable_packets == NULL) + { + return; + } + + // Is this an acknowledgement for the first packet in the list? + + if (seq == ((conn->reliable_packets->seq + 1) & 0xff)) + { + net_reliable_packet_t *rp; + + // Discard it, then. + // Unlink from the list. + + rp = conn->reliable_packets; + conn->reliable_packets = rp->next; + + NET_FreePacket(rp->packet); + free(rp); + } +} + +// Process the header of a reliable packet +// +// Returns true if the packet should be discarded (incorrect sequence) + +static boolean NET_Conn_ReliablePacket(net_connection_t *conn, + net_packet_t *packet) +{ + unsigned int seq; + net_packet_t *reply; + boolean result; + + // Read the sequence number + + if (!NET_ReadInt8(packet, &seq)) + { + return true; + } + + if (seq != (conn->reliable_recv_seq & 0xff)) + { + // This is not the next expected packet in the sequence! + // + // Discard the packet. If we were smart, we would use a proper + // sliding window protocol to do this, but I'm lazy. + + result = true; + } + else + { + // Now we can receive the next packet in the sequence. + + conn->reliable_recv_seq = (conn->reliable_recv_seq + 1) & 0xff; + + result = false; + } + + // Send an acknowledgement + + // Note: this is braindead. It would be much more sensible to + // include this in the next packet, rather than the overhead of + // sending a complete packet just for one byte of information. + + reply = NET_NewPacket(10); + + NET_WriteInt16(reply, NET_PACKET_TYPE_RELIABLE_ACK); + NET_WriteInt8(reply, conn->reliable_recv_seq & 0xff); + + NET_Conn_SendPacket(conn, reply); + + NET_FreePacket(reply); + + return result; +} + // Process a packet received by the server // // Returns true if eaten by common code boolean NET_Conn_Packet(net_connection_t *conn, net_packet_t *packet, - int packet_type) + unsigned int *packet_type) { conn->keepalive_recv_time = I_GetTimeMS(); + + // Is this a reliable packet? + + if (*packet_type & NET_RELIABLE_PACKET) + { + if (NET_Conn_ReliablePacket(conn, packet)) + { + // Invalid packet: eat it. + + return true; + } + + // Remove the reliable bit + + *packet_type &= ~NET_RELIABLE_PACKET; + } - switch (packet_type) + switch (*packet_type) { case NET_PACKET_TYPE_ACK: NET_Conn_ParseACK(conn, packet); @@ -184,6 +306,9 @@ boolean NET_Conn_Packet(net_connection_t *conn, net_packet_t *packet, case NET_PACKET_TYPE_REJECTED: NET_Conn_ParseReject(conn, packet); break; + case NET_PACKET_TYPE_RELIABLE_ACK: + NET_Conn_ParseReliableACK(conn, packet); + break; default: // Not a common packet @@ -236,6 +361,21 @@ void NET_Conn_Run(net_connection_t *conn) NET_Conn_SendPacket(conn, packet); NET_FreePacket(packet); } + + // Check the reliable packet list. Has the first packet in the + // list timed out? + // + // NB. This is braindead, we have a fixed time of one second. + + if (conn->reliable_packets != NULL + && (conn->reliable_packets->last_send_time < 0 + || nowtime - conn->reliable_packets->last_send_time > 1000)) + { + // Packet timed out, time to resend + + NET_Conn_SendPacket(conn, conn->reliable_packets->packet); + conn->reliable_packets->last_send_time = nowtime; + } } else if (conn->state == NET_CONN_STATE_WAITING_ACK) { @@ -310,5 +450,42 @@ void NET_Conn_Run(net_connection_t *conn) } } +net_packet_t *NET_Conn_NewReliable(net_connection_t *conn, int packet_type) +{ + net_packet_t *packet; + net_reliable_packet_t *rp; + net_reliable_packet_t **listend; + + // Generate a packet with the right header + + packet = NET_NewPacket(100); + NET_WriteInt16(packet, packet_type | NET_RELIABLE_PACKET); + + // write the low byte of the send sequence number + + NET_WriteInt8(packet, conn->reliable_send_seq & 0xff); + + // Add to the list of reliable packets + + rp = malloc(sizeof(net_reliable_packet_t)); + rp->packet = packet; + rp->next = NULL; + rp->seq = conn->reliable_send_seq; + rp->last_send_time = -1; + + for (listend = &conn->reliable_packets; + *listend != NULL; + listend = &((*listend)->next)); + + *listend = rp; + + // Count along the sequence + + conn->reliable_send_seq = (conn->reliable_send_seq + 1) & 0xff; + + // Finished + + return packet; +} -- cgit v1.2.3