shithub: choc

Download patch

ref: e15ddb2d293570992ca8b486f4b6233a648105a1
parent: 79b77612579be43566fcbac7a153da22b4cd9dd7
author: Simon Howard <fraggle@gmail.com>
date: Sat Jan 7 19:10:48 EST 2006

Move common connection code into net_common.c, shared by server
and client code.

Subversion-branch: /trunk/chocolate-doom
Subversion-revision: 263

--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -57,7 +57,8 @@
 mmus2mid.c           mmus2mid.h            \
 m_random.c           m_random.h            \
 m_swap.c             m_swap.h              \
-net_client.c                               \
+net_client.c         net_client.h          \
+net_common.c         net_common.h          \
 net_defs.h                                 \
 net_gui.c            net_gui.h             \
 net_io.c             net_io.h              \
--- a/src/net_client.c
+++ b/src/net_client.c
@@ -1,7 +1,7 @@
 // Emacs style mode select   -*- C++ -*- 
 //-----------------------------------------------------------------------------
 //
-// $Id: net_client.c 262 2006-01-07 20:08:11Z fraggle $
+// $Id: net_client.c 263 2006-01-08 00:10:48Z fraggle $
 //
 // Copyright(C) 2005 Simon Howard
 //
@@ -21,6 +21,10 @@
 // 02111-1307, USA.
 //
 // $Log$
+// Revision 1.10  2006/01/08 00:10:47  fraggle
+// Move common connection code into net_common.c, shared by server
+// and client code.
+//
 // Revision 1.9  2006/01/07 20:08:11  fraggle
 // Send player name and address in the waiting data packets.  Display these
 // on the waiting screen, and improve the waiting screen appearance.
@@ -63,6 +67,7 @@
 #include "doomstat.h"
 #include "i_system.h"
 #include "net_client.h"
+#include "net_common.h"
 #include "net_defs.h"
 #include "net_gui.h"
 #include "net_io.h"
@@ -71,10 +76,6 @@
 
 typedef enum
 {
-    // sent a syn, not received an ack yet
-
-    CLIENT_STATE_CONNECTING,
-
     // waiting for the game to start
 
     CLIENT_STATE_WAITING_START,
@@ -83,23 +84,17 @@
 
     CLIENT_STATE_IN_GAME,
 
-    // in disconnect state: sent DISCONNECT, waiting for DISCONNECT_ACK reply
-    
-    CLIENT_STATE_DISCONNECTING,
-
-    // successfully disconnected
-
-    CLIENT_STATE_DISCONNECTED,
 } net_clientstate_t;
 
+static net_connection_t client_connection;
 static net_clientstate_t client_state;
 static net_addr_t *server_addr;
 static net_context_t *client_context;
 static int last_send_time;
 
-// if TRUE, we are connected to a server
+// TRUE if the client code is in use
 
-boolean net_client_connected = false;
+boolean net_client_connected;
 
 // if TRUE, this client is the controller of the game
 
@@ -179,69 +174,6 @@
     }
 }
 
-// Received an ACK
-
-static void NET_CL_ParseACK(net_packet_t *packet)
-{
-    net_packet_t *reply;
-
-    // send an ACK back
-
-    reply = NET_NewPacket(10);
-    NET_WriteInt16(reply, NET_PACKET_TYPE_ACK);
-    NET_SendPacket(server_addr, reply);
-    NET_FreePacket(reply);
-
-    // set the client state if we havent already
- 
-    if (client_state == CLIENT_STATE_CONNECTING)
-    {
-        client_state = CLIENT_STATE_WAITING_START;
-    }
-}
-
-// parse a DISCONNECT packet
-
-static void NET_CL_ParseDisconnect(net_packet_t *packet)
-{
-    net_packet_t *reply;
-
-    // construct a DISCONNECT_ACK reply packet
-
-    reply = NET_NewPacket(10);
-    NET_WriteInt16(reply, NET_PACKET_TYPE_DISCONNECT_ACK);
-
-    // send the reply several times, in case of packet loss
-
-    NET_SendPacket(server_addr, reply);
-    NET_SendPacket(server_addr, reply);
-    NET_SendPacket(server_addr, reply);
-    NET_FreePacket(reply);
-
-    client_state = CLIENT_STATE_DISCONNECTED;
-
-    //I_Error("Disconnected from server.\n");
-    fprintf(stderr, "Disconnected from server.\n");
-
-    // Now what?
-
-    NET_CL_Disconnect();
-}
-
-// parse a DISCONNECT_ACK packet
-
-static void NET_CL_ParseDisconnectACK(net_packet_t *packet)
-{
-    if (client_state == CLIENT_STATE_DISCONNECTING)
-    {
-        // successfully disconnected from the server.
-
-        client_state = CLIENT_STATE_DISCONNECTED;
-
-        // now what?
-    }
-}
-
 // parse a received packet
 
 static void NET_CL_ParsePacket(net_packet_t *packet)
@@ -253,100 +185,30 @@
         return;
     }
 
-    switch (packet_type)
+    if (NET_Conn_Packet(&client_connection, packet, packet_type))
     {
-        case NET_PACKET_TYPE_ACK:
-
-            // received an acknowledgement to the SYN we sent
-
-            NET_CL_ParseACK(packet);
-            break;
-
-        case NET_PACKET_TYPE_WAITING_DATA:
-
-            NET_CL_ParseWaitingData(packet);
-            break;
-
-        case NET_PACKET_TYPE_GAMESTART:
-            break;
-
-        case NET_PACKET_TYPE_GAMEDATA:
-            break;
-
-        case NET_PACKET_TYPE_DISCONNECT:
-            NET_CL_ParseDisconnect(packet);
-            break;
-
-        case NET_PACKET_TYPE_DISCONNECT_ACK:
-            NET_CL_ParseDisconnectACK(packet);
-            break;
-
-        default:
-            break;
+        // Packet eaten by the common connection code
     }
-}
-
-// called when we are in the "connecting" state
-
-static void NET_CL_Connecting(void)
-{
-    net_packet_t *packet;
-
-    // send a SYN packet every second
-
-    if (last_send_time < 0 || I_GetTimeMS() - last_send_time > 1000)
+    else
     {
-        // construct a SYN packet
+        switch (packet_type)
+        {
+            case NET_PACKET_TYPE_WAITING_DATA:
+                NET_CL_ParseWaitingData(packet);
+                break;
 
-        packet = NET_NewPacket(10);
+            case NET_PACKET_TYPE_GAMESTART:
+                break;
 
-        // packet type
-     
-        NET_WriteInt16(packet, NET_PACKET_TYPE_SYN);
+            case NET_PACKET_TYPE_GAMEDATA:
+                break;
 
-        // magic number
-
-        NET_WriteInt32(packet, NET_MAGIC_NUMBER);
-
-        // send to the server
-
-        NET_SendPacket(server_addr, packet);
-
-        NET_FreePacket(packet);
-
-        last_send_time = I_GetTimeMS();
+            default:
+                break;
+        }
     }
 }
 
-// Called when we are in the "disconnecting" state, disconnecting from
-// the server.
-
-static void NET_CL_Disconnecting(void)
-{
-    net_packet_t *packet;
-
-    // send a DISCONNECT packet every second
-
-    if (last_send_time < 0 || I_GetTimeMS() - last_send_time > 1000)
-    {
-        // construct packet
-
-        packet = NET_NewPacket(10);
-
-        // packet type
-     
-        NET_WriteInt16(packet, NET_PACKET_TYPE_DISCONNECT);
-
-        // send to the server
-
-        NET_SendPacket(server_addr, packet);
-
-        NET_FreePacket(packet);
-
-        last_send_time = I_GetTimeMS();
-    }
-}
-
 // "Run" the client code: check for new packets, send packets as
 // needed
 
@@ -372,19 +234,12 @@
         NET_FreePacket(packet);
     }
 
-    // send packets as needed
+    // Run the common connection code to send any packets as needed
 
-    switch (client_state)
-    {
-        case CLIENT_STATE_CONNECTING:
-            NET_CL_Connecting();
-            break;
-        case CLIENT_STATE_DISCONNECTING:
-            NET_CL_Disconnecting();
-            break;
-        default:
-            break;
-    }
+    NET_Conn_Run(&client_connection);
+
+    net_waiting_for_start = client_connection.state == NET_CONN_STATE_CONNECTED
+                         && client_state == CLIENT_STATE_WAITING_START;
 }
 
 // connect to a server
@@ -410,16 +265,18 @@
     NET_AddModule(client_context, addr->module);
 
     net_client_connected = true;
-    net_waiting_for_start = true;
 
+    // Initialise connection
+
+    NET_Conn_InitClient(&client_connection, addr);
+
     // try to connect
  
-    client_state = CLIENT_STATE_CONNECTING;
     last_send_time = -1;
 
     start_time = I_GetTimeMS();
 
-    while (client_state == CLIENT_STATE_CONNECTING)
+    while (client_connection.state == NET_CONN_STATE_CONNECTING)
     {
         // time out after 5 seconds 
 
@@ -442,10 +299,12 @@
         I_Sleep(10);
     }
 
-    if (client_state != CLIENT_STATE_CONNECTING)
+    if (client_connection.state == NET_CONN_STATE_CONNECTED)
     {
         // connected ok!
 
+        client_state = CLIENT_STATE_WAITING_START;
+
         return true;
     }
     else
@@ -469,23 +328,18 @@
         return;
     }
     
-    // set the client into the DISCONNECTING state
+    NET_Conn_Disconnect(&client_connection);
 
-    if (client_state != CLIENT_STATE_DISCONNECTED)
-    {
-        client_state = CLIENT_STATE_DISCONNECTING;
-        last_send_time = -1;
-    }
-
     start_time = I_GetTimeMS();
 
-    while (client_state != CLIENT_STATE_DISCONNECTED)
+    while (client_connection.state != NET_CONN_STATE_DISCONNECTED
+        && client_connection.state != NET_CONN_STATE_DISCONNECTED_SLEEP)
     {
         if (I_GetTimeMS() - start_time > 5000)
         {
             // time out after 5 seconds
             
-            client_state = CLIENT_STATE_DISCONNECTED;
+            client_state = NET_CONN_STATE_DISCONNECTED;
 
             fprintf(stderr, "NET_CL_Disconnect: Timeout while disconnecting from server\n");
             break;
--- /dev/null
+++ b/src/net_common.c
@@ -1,0 +1,272 @@
+// Emacs style mode select   -*- C++ -*- 
+//-----------------------------------------------------------------------------
+//
+// $Id: net_common.c 263 2006-01-08 00:10:48Z fraggle $
+//
+// Copyright(C) 2005 Simon Howard
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+// 02111-1307, USA.
+//
+// $Log$
+// Revision 1.1  2006/01/08 00:10:48  fraggle
+// Move common connection code into net_common.c, shared by server
+// and client code.
+//
+//
+// Common code shared between the client and server
+//
+
+#include "doomdef.h"
+#include "i_system.h"
+
+#include "net_common.h"
+#include "net_io.h"
+#include "net_packet.h"
+
+// Initialise as a client connection
+
+void NET_Conn_InitClient(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;
+}
+
+// Initialise as a server connection
+
+void NET_Conn_InitServer(net_connection_t *conn, net_addr_t *addr)
+{
+    conn->state = NET_CONN_STATE_WAITING_ACK;
+    conn->last_send_time = -1;
+    conn->num_retries = 0;
+    conn->addr = addr;
+}
+
+// parse an ACK packet from a client
+
+static void NET_Conn_ParseACK(net_connection_t *conn, net_packet_t *packet)
+{
+    net_packet_t *reply;
+
+    if (conn->state == NET_CONN_STATE_CONNECTING)
+    {
+        // We are a client
+
+        // received a response from the server to our SYN
+
+        conn->state = NET_CONN_STATE_CONNECTED;
+
+        // We must send an ACK reply to the server's ACK
+
+        reply = NET_NewPacket(10);
+        NET_WriteInt16(reply, NET_PACKET_TYPE_ACK);
+        NET_SendPacket(conn->addr, reply);
+        NET_FreePacket(reply);
+    }
+    
+    if (conn->state == NET_CONN_STATE_WAITING_ACK)
+    {
+        // We are a server
+
+        // Client is connected
+        
+        conn->state = NET_CONN_STATE_CONNECTED;
+    }
+}
+
+static void NET_Conn_ParseDisconnect(net_connection_t *conn, net_packet_t *packet)
+{
+    net_packet_t *reply;
+
+    // Other end wants to disconnect
+    // Send a DISCONNECT_ACK reply.
+    
+    reply = NET_NewPacket(10);
+    NET_WriteInt16(reply, NET_PACKET_TYPE_DISCONNECT_ACK);
+    NET_SendPacket(conn->addr, reply);
+    NET_FreePacket(reply);
+
+    conn->last_send_time = I_GetTimeMS();
+    
+    conn->state = NET_CONN_STATE_DISCONNECTED_SLEEP;
+}
+
+// Parse a DISCONNECT_ACK packet
+
+static void NET_Conn_ParseDisconnectACK(net_connection_t *conn,
+                                        net_packet_t *packet)
+{
+
+    if (conn->state == NET_CONN_STATE_DISCONNECTING)
+    {
+        // We have received an acknowledgement to our disconnect
+        // request. We have been disconnected successfully.
+        
+        conn->state = NET_CONN_STATE_DISCONNECTED;
+        conn->last_send_time = -1;
+    }
+}
+
+// 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)
+{
+    //printf("Conn: %s: %i\n", NET_AddrToString(addr), packet_type);
+
+    switch (packet_type)
+    {
+        case NET_PACKET_TYPE_ACK:
+            NET_Conn_ParseACK(conn, packet);
+            break;
+        case NET_PACKET_TYPE_DISCONNECT:
+            NET_Conn_ParseDisconnect(conn, packet);
+            break;
+        case NET_PACKET_TYPE_DISCONNECT_ACK:
+            NET_Conn_ParseDisconnectACK(conn, packet);
+            break;
+        default:
+            // Not a common packet
+
+            return false;
+    }
+
+    // We found a packet that we found interesting, and ate it.
+
+    return true;
+}
+
+void NET_Conn_Disconnect(net_connection_t *conn)
+{
+    if (conn->state != NET_CONN_STATE_DISCONNECTED
+     && conn->state != NET_CONN_STATE_DISCONNECTING
+     && conn->state != NET_CONN_STATE_DISCONNECTED_SLEEP)
+    {
+        conn->state = NET_CONN_STATE_DISCONNECTING;
+        conn->last_send_time = -1;
+        conn->num_retries = 0;
+    }
+}
+
+void NET_Conn_Run(net_connection_t *conn)
+{
+    net_packet_t *packet;
+
+    if (conn->state == NET_CONN_STATE_CONNECTING)
+    {
+        if (conn->last_send_time < 0
+         || I_GetTimeMS() - conn->last_send_time > 1000)
+        {
+            // It has been a second since the last SYN was sent, and no
+            // reply.
+            
+            if (conn->num_retries < MAX_RETRIES)
+            {
+                // send another SYN
+                    
+                packet = NET_NewPacket(10);
+                NET_WriteInt16(packet, NET_PACKET_TYPE_SYN);
+                NET_WriteInt32(packet, NET_MAGIC_NUMBER);
+                NET_SendPacket(conn->addr, packet);
+                NET_FreePacket(packet);
+                conn->last_send_time = I_GetTimeMS();
+
+                ++conn->num_retries;
+            }
+            else
+            {
+                conn->state = NET_CONN_STATE_DISCONNECTED;
+            }
+        }
+    }
+    else if (conn->state == NET_CONN_STATE_WAITING_ACK)
+    {
+        if (conn->last_send_time < 0
+         || I_GetTimeMS() - conn->last_send_time > 1000)
+        {
+            // it has been a second since the last ACK was sent, and 
+            // still no reply.
+
+            if (conn->num_retries < MAX_RETRIES)
+            {
+                // send another ACK
+
+                packet = NET_NewPacket(10);
+                NET_WriteInt16(packet, NET_PACKET_TYPE_ACK);
+                NET_SendPacket(conn->addr, packet);
+                NET_FreePacket(packet);
+                conn->last_send_time = I_GetTimeMS();
+
+                ++conn->num_retries;
+            }
+            else 
+            {
+                // no more retries allowed.
+
+                conn->state = NET_CONN_STATE_DISCONNECTED;
+            }
+        }
+    }
+    else if (conn->state == NET_CONN_STATE_DISCONNECTING)
+    {
+        // Waiting for a reply to our DISCONNECT request.
+
+        if (conn->last_send_time < 0
+         || I_GetTimeMS() - conn->last_send_time > 1000)
+        {
+            // it has been a second since the last disconnect packet 
+            // was sent, and still no reply.
+
+            if (conn->num_retries < MAX_RETRIES)
+            {
+                // send another disconnect
+
+                packet = NET_NewPacket(10);
+                NET_WriteInt16(packet, NET_PACKET_TYPE_DISCONNECT);
+                NET_SendPacket(conn->addr, packet);
+                NET_FreePacket(packet);
+                conn->last_send_time = I_GetTimeMS();
+
+                ++conn->num_retries;
+            }
+            else 
+            {
+                // No more retries allowed.
+                // Force disconnect.
+
+                conn->state = NET_CONN_STATE_DISCONNECTED;
+            }
+        }
+    }
+    else if (conn->state == NET_CONN_STATE_DISCONNECTED_SLEEP)
+    {
+        // We are disconnected, waiting in case we need to send
+        // a DISCONNECT_ACK to the server again.
+
+        if (I_GetTimeMS() - conn->last_send_time > 5000)
+        {
+            // Idle for 5 seconds, switch state
+
+            conn->state = NET_CONN_STATE_DISCONNECTED;
+        }
+    }
+}
+
+
+
--- /dev/null
+++ b/src/net_common.h
@@ -1,0 +1,90 @@
+// Emacs style mode select   -*- C++ -*- 
+//-----------------------------------------------------------------------------
+//
+// $Id: net_common.h 263 2006-01-08 00:10:48Z fraggle $
+//
+// Copyright(C) 2005 Simon Howard
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+// 02111-1307, USA.
+//
+// $Log$
+// Revision 1.1  2006/01/08 00:10:48  fraggle
+// Move common connection code into net_common.c, shared by server
+// and client code.
+//
+//
+// Common code shared between the client and server
+//
+
+#ifndef NET_COMMON_H
+#define NET_COMMON_H
+
+#include "net_defs.h"
+
+typedef enum 
+{
+    // sending syn packets, waiting for an ACK reply 
+    // (client side)
+
+    NET_CONN_STATE_CONNECTING,
+
+    // received a syn, sent an ack, waiting for an ack reply
+    // (server side)
+
+    NET_CONN_STATE_WAITING_ACK,
+    
+    // successfully connected
+
+    NET_CONN_STATE_CONNECTED,
+
+    // sent a DISCONNECT packet, waiting for a DISCONNECT_ACK reply
+
+    NET_CONN_STATE_DISCONNECTING,
+
+    // client successfully disconnected
+
+    NET_CONN_STATE_DISCONNECTED,
+
+    // We are disconnected, but in a sleep state, waiting for several
+    // seconds.  This is in case the DISCONNECT_ACK we sent failed
+    // to arrive, and we need to send another one.  We keep this as
+    // a valid connection for a few seconds until we are sure that
+    // the other end has successfully disconnected as well.
+
+    NET_CONN_STATE_DISCONNECTED_SLEEP,
+
+} net_connstate_t;
+
+#define MAX_RETRIES 5
+
+typedef struct 
+{
+    net_connstate_t state;
+    net_addr_t *addr;
+    int last_send_time;
+    int num_retries;
+} net_connection_t;
+
+
+void NET_Conn_InitClient(net_connection_t *conn, net_addr_t *addr);
+void NET_Conn_InitServer(net_connection_t *conn, net_addr_t *addr);
+boolean NET_Conn_Packet(net_connection_t *conn, net_packet_t *packet,
+                        int packet_type);
+void NET_Conn_Disconnect(net_connection_t *conn);
+void NET_Conn_Run(net_connection_t *conn);
+
+#endif /* #ifndef NET_COMMON_H */
+
--- a/src/net_server.c
+++ b/src/net_server.c
@@ -1,7 +1,7 @@
 // Emacs style mode select   -*- C++ -*- 
 //-----------------------------------------------------------------------------
 //
-// $Id: net_server.c 262 2006-01-07 20:08:11Z fraggle $
+// $Id: net_server.c 263 2006-01-08 00:10:48Z fraggle $
 //
 // Copyright(C) 2005 Simon Howard
 //
@@ -21,6 +21,10 @@
 // 02111-1307, USA.
 //
 // $Log$
+// Revision 1.12  2006/01/08 00:10:48  fraggle
+// Move common connection code into net_common.c, shared by server
+// and client code.
+//
 // Revision 1.11  2006/01/07 20:08:11  fraggle
 // Send player name and address in the waiting data packets.  Display these
 // on the waiting screen, and improve the waiting screen appearance.
@@ -71,6 +75,7 @@
 #include "doomstat.h"
 #include "i_system.h"
 #include "net_client.h"
+#include "net_common.h"
 #include "net_defs.h"
 #include "net_io.h"
 #include "net_loop.h"
@@ -78,40 +83,26 @@
 #include "net_server.h"
 #include "net_sdl.h"
 
-typedef enum 
+typedef enum
 {
-    // received a syn, sent an ack, waiting for an ack reply
+    // waiting for the game to start
 
-    CLIENT_STATE_WAITING_ACK,
-    
-    // waiting for a game to start
+    SERVER_WAITING_START,
 
-    CLIENT_STATE_WAITING_START,
+    // in a game
 
-    // in game
+    SERVER_IN_GAME,
+} net_server_state_t;
 
-    CLIENT_STATE_IN_GAME,
-
-    // sent a DISCONNECT packet, waiting for a DISCONNECT_ACK reply
-
-    CLIENT_STATE_DISCONNECTING,
-
-    // client successfully disconnected
-
-    CLIENT_STATE_DISCONNECTED,
-} net_clientstate_t;
-
-#define MAX_RETRIES 5
-
 typedef struct 
 {
     boolean active;
-    net_clientstate_t state;
     net_addr_t *addr;
+    net_connection_t connection;
     int last_send_time;
-    int num_retries;
 } net_client_t;
 
+static net_server_state_t server_state;
 static boolean server_initialised = false;
 static net_client_t clients[MAXNETNODES];
 static net_context_t *server_context;
@@ -118,13 +109,9 @@
 
 static void NET_SV_DisconnectClient(net_client_t *client)
 {
-    if (client->active 
-     && client->state != CLIENT_STATE_DISCONNECTING
-     && client->state != CLIENT_STATE_DISCONNECTED)
+    if (client->active)
     {
-        client->state = CLIENT_STATE_DISCONNECTING;
-        client->num_retries = 0;
-        client->last_send_time = -1;
+        NET_Conn_Disconnect(&client->connection);
     }
 }
 
@@ -133,10 +120,8 @@
     // Check that the client is properly connected: ie. not in the 
     // process of connecting or disconnecting
 
-    return client->active
-        && client->state != CLIENT_STATE_DISCONNECTING
-        && client->state != CLIENT_STATE_DISCONNECTED
-        && client->state != CLIENT_STATE_WAITING_ACK;
+    return client->active 
+        && client->connection.state == NET_CONN_STATE_CONNECTED;
 }
 
 // returns the number of clients connected
@@ -233,9 +218,6 @@
             if (!clients[i].active)
             {
                 client = &clients[i];
-                client->active = true;
-                client->addr = addr;
-                client->state = CLIENT_STATE_DISCONNECTED;
                 break;
             }
         }
@@ -245,102 +227,35 @@
             return;
         }
     }
-
-    // Set into the correct state if necessary
-    // Allow immediate reconnects from clients which just disconnected.
-
-    if (client->state == CLIENT_STATE_DISCONNECTED)
+    else
     {
-        client->state = CLIENT_STATE_WAITING_ACK;
-        client->num_retries = 0;
-    }
+        // If this is a recently-disconnected client, deactivate
+        // to allow immediate reconnection
 
-    if (client->state == CLIENT_STATE_WAITING_ACK)
-    {
-        // force an acknowledgement
-
-        client->last_send_time = -1;
+        if (client->connection.state == NET_CONN_STATE_DISCONNECTED)
+        {
+            client->active = false;
+        }
     }
-}
 
-// parse an ACK packet from a client
+    // New client?
 
-static void NET_SV_ParseACK(net_packet_t *packet, net_client_t *client)
-{
-    if (client == NULL)
+    if (!client->active)
     {
-        return;
-    }
-
-    if (client->state == CLIENT_STATE_WAITING_ACK)
-    {
-        // now waiting for the game to start
-
-        client->state = CLIENT_STATE_WAITING_START;
-
-        // force a waiting data packet to be sent immediately
-
+        // Activate, initialise connection
+        client->active = true;
+        NET_Conn_InitServer(&client->connection, addr);
+        client->addr = addr;
         client->last_send_time = -1;
     }
-}
 
-static void NET_SV_ParseDisconnect(net_packet_t *packet, net_client_t *client)
-{
-    net_packet_t *reply;
-
-    // sanity check
-
-    if (client == NULL)
+    if (client->connection.state == NET_CONN_STATE_WAITING_ACK)
     {
-        return;
+        // force an acknowledgement
+        client->connection.last_send_time = -1;
     }
-
-    // This client wants to disconnect from the server.
-    // Send a DISCONNECT_ACK reply.
-    
-    reply = NET_NewPacket(10);
-    NET_WriteInt16(reply, NET_PACKET_TYPE_DISCONNECT_ACK);
-    NET_SendPacket(client->addr, reply);
-    NET_FreePacket(reply);
-
-    client->last_send_time = I_GetTimeMS();
-    
-    // Do not set to inactive immediately.  Instead, set to the 
-    // DISCONNECTED state.  This is in case our acknowledgement is
-    // not received and another must be sent.
-    //
-    // After a few seconds, the client will get properly removed
-    // and cleaned up from the clients list.
-
-    client->state = CLIENT_STATE_DISCONNECTED;
-
-    //printf("SV: %s: client disconnected\n", NET_AddrToString(client->addr));
 }
 
-// Parse a DISCONNECT_ACK packet
-
-static void NET_SV_ParseDisconnectACK(net_packet_t *packet, 
-                                      net_client_t *client)
-{
-    // Sanity check
-  
-    if (client == NULL)
-    {
-        return;
-    }
-
-    if (client->state == CLIENT_STATE_DISCONNECTING)
-    {
-        // We have received an acknowledgement to our disconnect
-        // request. Client has been disconnected successfully.
-        
-        // Place into the DISCONNECTED state to allow for cleanup.
-
-        client->state = CLIENT_STATE_DISCONNECTED;
-        client->last_send_time = -1;
-    }
-}
-
 // Process a packet received by the server
 
 static void NET_SV_Packet(net_packet_t *packet, net_addr_t *addr)
@@ -361,30 +276,31 @@
         return;
     }
 
-    //printf("SV: %s: %i\n", NET_AddrToString(addr), packet_type);
-
-    switch (packet_type)
+    if (packet_type == NET_PACKET_TYPE_SYN)
     {
-        case NET_PACKET_TYPE_SYN:
-            NET_SV_ParseSYN(packet, client, addr);
-            break;
-        case NET_PACKET_TYPE_ACK:
-            NET_SV_ParseACK(packet, client);
-            break;
-        case NET_PACKET_TYPE_GAMESTART:
-            break;
-        case NET_PACKET_TYPE_GAMEDATA:
-            break;
-        case NET_PACKET_TYPE_DISCONNECT:
-            NET_SV_ParseDisconnect(packet, client);
-            break;
-        case NET_PACKET_TYPE_DISCONNECT_ACK:
-            NET_SV_ParseDisconnectACK(packet, client);
-            break;
-        default:
-            // unknown packet type
+        NET_SV_ParseSYN(packet, client, addr);
+    }
+    else if (client == NULL)
+    {
+        // Must come from a valid client; ignore otherwise
+    }
+    else if (NET_Conn_Packet(&client->connection, packet, packet_type))
+    {
+        // Packet was eaten by the common connection code
+    }
+    else
+    { 
+        //printf("SV: %s: %i\n", NET_AddrToString(addr), packet_type);
 
-            break;
+        switch (packet_type)
+        {
+            case NET_PACKET_TYPE_GAMESTART:
+                break;
+            default:
+                // unknown packet type
+
+                break;
+        }
     }
 
     // If this address is not in the list of clients, be sure to
@@ -439,10 +355,6 @@
 
     NET_SendPacket(client->addr, packet);
     NET_FreePacket(packet);
-    
-    // update time
-
-    client->last_send_time = I_GetTimeMS();
 }
 
 // Perform any needed action on a client
@@ -449,38 +361,28 @@
 
 static void NET_SV_RunClient(net_client_t *client)
 {
-    net_packet_t *packet;
+    // Run common code
 
-    if (client->state == CLIENT_STATE_WAITING_ACK)
+    NET_Conn_Run(&client->connection);
+    
+    // Is this client disconnected?
+
+    if (client->connection.state == NET_CONN_STATE_DISCONNECTED)
     {
-        if (client->last_send_time < 0
-         || I_GetTimeMS() - client->last_send_time > 1000)
-        {
-            // it has been a second since the last ACK was sent, and 
-            // still no reply.
+        // deactivate and free back 
 
-            if (client->num_retries < MAX_RETRIES)
-            {
-                // send another ACK
+        client->active = false;
+        NET_FreeAddress(client->addr);
+    }
+    
+    if (!ClientConnected(client))
+    {
+        // client has not yet finished connecting
 
-                packet = NET_NewPacket(10);
-                NET_WriteInt16(packet, NET_PACKET_TYPE_ACK);
-                NET_SendPacket(client->addr, packet);
-                NET_FreePacket(packet);
-                client->last_send_time = I_GetTimeMS();
-
-                ++client->num_retries;
-            }
-            else 
-            {
-                // no more retries allowed.
-
-                client->active = false;
-                NET_FreeAddress(client->addr);
-            }
-        }
+        return;
     }
-    else if (client->state == CLIENT_STATE_WAITING_START)
+
+    if (server_state == SERVER_WAITING_START)
     {
         // Waiting for the game to start
 
@@ -490,57 +392,9 @@
          || I_GetTimeMS() - client->last_send_time > 1000)
         {
             NET_SV_SendWaitingData(client);
+            client->last_send_time = I_GetTimeMS();
         }
     }
-    else if (client->state == CLIENT_STATE_DISCONNECTING)
-    {
-        // Waiting for a reply to our DISCONNECT request.
-
-        if (client->last_send_time < 0
-         || I_GetTimeMS() - client->last_send_time > 1000)
-        {
-            // it has been a second since the last disconnect packet 
-            // was sent, and still no reply.
-
-            if (client->num_retries < MAX_RETRIES)
-            {
-                // send another disconnect
-
-                packet = NET_NewPacket(10);
-                NET_WriteInt16(packet, NET_PACKET_TYPE_DISCONNECT);
-                NET_SendPacket(client->addr, packet);
-                NET_FreePacket(packet);
-                client->last_send_time = I_GetTimeMS();
-
-                ++client->num_retries;
-            }
-            else 
-            {
-                // No more retries allowed.
-                // Force disconnect.
-
-                client->active = false;
-                NET_FreeAddress(client->addr);
-            }
-        }
-
-    }
-    else if (client->state == CLIENT_STATE_DISCONNECTED)
-    {
-        // Client has disconnected.  
-        //
-        // See NET_SV_ParseDisconnect() above.
-
-        // Remove from the list after five seconds
-
-        if (client->last_send_time < 0
-         || I_GetTimeMS() - client->last_send_time > 5000)
-        {
-            //printf("SV: %s: deactivated\n", NET_AddrToString(client->addr));
-            client->active = false;
-            NET_FreeAddress(client->addr);
-        }
-    }
 }
 
 // Initialise server and wait for connections
@@ -564,6 +418,7 @@
         clients[i].active = false;
     }
 
+    server_state = SERVER_WAITING_START;
     server_initialised = true;
 }