added support for protocol version check as first protocol command
[rocksndiamonds.git] / src / netserv.c
index 6555e0497922502843b7ef909c34cfc69be28bf1..4b846f9da413425db5e86999fd149111f34ea77c 100644 (file)
@@ -9,20 +9,16 @@
 // netserv.c
 // ============================================================================
 
-#include "libgame/platform.h"
-
-#if defined(NETWORK_AVALIABLE)
-
 #include <fcntl.h>
 #include <sys/time.h>
 #include <signal.h>
 #include <errno.h>
 
-#include "main.h"
-
 #include "libgame/libgame.h"
 
 #include "netserv.h"
+#include "main.h"
+
 
 static int clients = 0;
 static int onceonly = 0;
@@ -47,7 +43,8 @@ static struct NetworkServerPlayerInfo *first_player = NULL;
 #define NEXT(player) ((player)->next ? (player)->next : first_player)
 
 /* TODO: peer address */
-static TCPsocket lfd;          /* listening socket */
+static TCPsocket lfd;          /* listening TCP socket */
+static UDPsocket udp;          /* listening UDP socket */
 static SDLNet_SocketSet fds;   /* socket set */
 
 static unsigned char realbuffer[512], *buffer = realbuffer + 4;
@@ -132,6 +129,7 @@ static void RemovePlayer(struct NetworkServerPlayerInfo *player)
   free(player);
   clients--;
 
+#if 0  /* do not terminate network server if last player disconnected */
   if (onceonly && clients == 0)
   {
     if (options.verbose)
@@ -141,6 +139,7 @@ static void RemovePlayer(struct NetworkServerPlayerInfo *player)
     }
     exit(0);
   }
+#endif
 }
 
 static void AddPlayer(TCPsocket fd)
@@ -433,6 +432,13 @@ static void Handle_OP_MOVE_PLAYER(struct NetworkServerPlayerInfo *player)
   ServerFrameCounter++;
 }
 
+void ExitNetworkServer(int exit_value)
+{
+  Error(ERR_NETWORK_SERVER, "exiting network server");
+
+  exit(exit_value);
+}
+
 /* the following is not used for a standalone server;
    the pointer points to an integer containing the port-number */
 int NetworkServerThread(void *ptr)
@@ -445,7 +451,6 @@ int NetworkServerThread(void *ptr)
 
 void NetworkServer(int port, int serveronly)
 {
-  int sl;
   struct NetworkServerPlayerInfo *player;
   int r; 
   unsigned int len;
@@ -458,6 +463,10 @@ void NetworkServer(int port, int serveronly)
   if (port == 0)
     port = DEFAULT_SERVER_PORT;
 
+  // if only running the network server, exit on Ctrl-C
+  if (serveronly)
+    signal(SIGINT, ExitNetworkServer);
+
   if (!serveronly)
     onceonly = 1;
 
@@ -473,14 +482,28 @@ void NetworkServer(int port, int serveronly)
 #endif
 
   if (SDLNet_ResolveHost(&ip, NULL, port) == -1)
-    Error(ERR_EXIT_NETWORK_SERVER, "SDLNet_ResolveHost() failed");
+    Error(ERR_EXIT_NETWORK_SERVER, "SDLNet_ResolveHost() failed: %s",
+          SDLNet_GetError());
+
+  if ((fds = SDLNet_AllocSocketSet(MAX_PLAYERS + 1 + 1)) == NULL)
+    Error(ERR_EXIT_NETWORK_SERVER, "SDLNet_AllocSocketSet() failed: %s"),
+      SDLNet_GetError();
+
+  if ((lfd = SDLNet_TCP_Open(&ip)) == NULL)
+    Error(ERR_EXIT_NETWORK_SERVER, "SDLNet_TCP_Open() failed: %s"),
+      SDLNet_GetError();
+
+  if (SDLNet_TCP_AddSocket(fds, lfd) == -1)
+    Error(ERR_EXIT_NETWORK_SERVER, "SDLNet_TCP_AddSocket() failed: %s"),
+      SDLNet_GetError();
 
-  lfd = SDLNet_TCP_Open(&ip);
-  if (!lfd)
-    Error(ERR_EXIT_NETWORK_SERVER, "SDLNet_TCP_Open() failed");
+  if ((udp = SDLNet_UDP_Open(port)) == NULL)
+    Error(ERR_EXIT_NETWORK_SERVER, "SDLNet_UDP_Open() failed: %s",
+          SDLNet_GetError());
 
-  fds = SDLNet_AllocSocketSet(MAX_PLAYERS+1);
-  SDLNet_TCP_AddSocket(fds, lfd);
+  if (SDLNet_UDP_AddSocket(fds, udp) == -1)
+    Error(ERR_EXIT_NETWORK_SERVER, "SDLNet_TCP_AddSocket() failed: %s"),
+      SDLNet_GetError();
 
   if (options.verbose)
   {
@@ -496,22 +519,15 @@ void NetworkServer(int port, int serveronly)
     for (player = first_player; player; player = player->next)
       flushuser(player);
 
-    if ((sl = SDLNet_CheckSockets(fds, 500000)) < 1)
-    {
-      Error(ERR_NETWORK_SERVER, "SDLNet_CheckSockets failed: %s",
-           SDLNet_GetError());
-      perror("SDLNet_CheckSockets");
-    }
-
-    if (sl < 0)
-      continue;
-    
-    if (sl == 0)
+    // wait for 100 ms for activity on open network sockets
+    if (SDLNet_CheckSockets(fds, 100) < 1)
       continue;
 
-    /* accept incoming connections */
+    /* accept incoming TCP connections */
     if (SDLNet_SocketReady(lfd))
     {
+      Error(ERR_DEBUG, "got TCP packet");
+
       TCPsocket newsock;
 
       newsock = SDLNet_TCP_Accept(lfd);
@@ -520,9 +536,25 @@ void NetworkServer(int port, int serveronly)
        AddPlayer(newsock);
     }
 
+    /* accept incoming UDP packets */
+    if (SDLNet_SocketReady(udp))
+    {
+      Error(ERR_DEBUG, "got UDP packet");
+
+      static UDPpacket packet;
+
+      int num_packets = SDLNet_UDP_Recv(udp, &packet);
+
+      if (num_packets == 1)
+      {
+        // bounce packet
+        SDLNet_UDP_Send(udp, -1, &packet);
+      }
+    }
+
     player = first_player;
 
-    do
+    while (player && !interrupt)
     {
       if (SDLNet_SocketReady(player->fd))
       {
@@ -557,10 +589,12 @@ void NetworkServer(int port, int serveronly)
          memmove(player->readbuffer, player->readbuffer + 4 + len, player->nread);
 
          buffer[0] = player->number;
-         if (!player->introduced && buffer[1] != OP_PLAYER_NAME)
+         if (!player->introduced &&
+             buffer[1] != OP_PLAYER_NAME &&
+             buffer[1] != OP_PROTOCOL_VERSION)
          {
            if (options.verbose)
-             Error(ERR_NETWORK_SERVER, "!(client %d)->introduced && buffer[1]==%d (expected OP_PLAYER_NAME)", buffer[0], buffer[1]);
+             Error(ERR_NETWORK_SERVER, "!(client %d)->introduced && buffer[1]==%d (expected OP_PLAYER_NAME or OP_PROTOCOL_VERSION)", buffer[0], buffer[1]);
 
            RemovePlayer(player);
            interrupt = 1;
@@ -621,8 +655,5 @@ void NetworkServer(int port, int serveronly)
       if (player && !interrupt)
        player = player->next;
     }
-    while (player && !interrupt);
   }
 }
-
-#endif /* NETWORK_AVALIABLE */