added support for protocol version check as first protocol command
[rocksndiamonds.git] / src / netserv.c
index 991fbba61ae2b94e999f4926c69958eeefc56342..4b846f9da413425db5e86999fd149111f34ea77c 100644 (file)
@@ -1,51 +1,31 @@
-/***********************************************************
-* Rocks'n'Diamonds -- McDuffin Strikes Back!               *
-*----------------------------------------------------------*
-* (c) 1995-2002 Artsoft Entertainment                      *
-*               Holger Schemel                             *
-*               Detmolder Strasse 189                      *
-*               33604 Bielefeld                            *
-*               Germany                                    *
-*               e-mail: info@artsoft.org                   *
-*----------------------------------------------------------*
-* netserv.c                                                *
-***********************************************************/
-
-#include "libgame/platform.h"
-
-#if defined(NETWORK_AVALIABLE)
+// ============================================================================
+// Rocks'n'Diamonds - McDuffin Strikes Back!
+// ----------------------------------------------------------------------------
+// (c) 1995-2014 by Artsoft Entertainment
+//                         Holger Schemel
+//                 info@artsoft.org
+//                 http://www.artsoft.org/
+// ----------------------------------------------------------------------------
+// netserv.c
+// ============================================================================
 
 #include <fcntl.h>
 #include <sys/time.h>
 #include <signal.h>
 #include <errno.h>
 
-#if defined(TARGET_SDL)
-#include "main.h"
-#else
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <netinet/tcp.h>
-#include <arpa/inet.h>
-#include <netdb.h>
-#include <sys/select.h>                        /* apparently needed for OS/2 port */
-#endif
-
 #include "libgame/libgame.h"
 
 #include "netserv.h"
+#include "main.h"
+
 
 static int clients = 0;
 static int onceonly = 0;
 
 struct NetworkServerPlayerInfo
 {
-#if defined(TARGET_SDL)
   TCPsocket fd;
-#else
-  int fd;
-#endif
-
   char player_name[16];
   unsigned char number;
   struct NetworkServerPlayerInfo *next;
@@ -62,22 +42,16 @@ static struct NetworkServerPlayerInfo *first_player = NULL;
 
 #define NEXT(player) ((player)->next ? (player)->next : first_player)
 
-#if defined(TARGET_SDL)
 /* 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 */
-#else
-static struct sockaddr_in saddr;
-static int lfd;                        /* listening socket */
-static fd_set fds;             /* socket set */
-static int tcp = -1;
-#endif
 
 static unsigned char realbuffer[512], *buffer = realbuffer + 4;
 
 static int interrupt;
 
-static unsigned long ServerFrameCounter = 0;
+static unsigned int ServerFrameCounter = 0;
 
 static void addtobuffer(struct NetworkServerPlayerInfo *player,
                        unsigned char *b, int len)
@@ -94,11 +68,8 @@ static void flushuser(struct NetworkServerPlayerInfo *player)
 {
   if (player->nwrite)
   {
-#if defined(TARGET_SDL)
     SDLNet_TCP_Send(player->fd, player->writbuffer, player->nwrite);
-#else
-    write(player->fd, player->writbuffer, player->nwrite);
-#endif
+
     player->nwrite = 0;
   }
 }
@@ -145,12 +116,8 @@ static void RemovePlayer(struct NetworkServerPlayerInfo *player)
     }
   }
 
-#if defined(TARGET_SDL)
   SDLNet_TCP_DelSocket(fds, player->fd);
   SDLNet_TCP_Close(player->fd);
-#else
-  close(player->fd);
-#endif
 
   if (player->introduced)
   {
@@ -162,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)
@@ -171,13 +139,10 @@ static void RemovePlayer(struct NetworkServerPlayerInfo *player)
     }
     exit(0);
   }
+#endif
 }
 
-#if defined(TARGET_SDL)
 static void AddPlayer(TCPsocket fd)
-#else
-static void AddPlayer(int fd)
-#endif
 {
   struct NetworkServerPlayerInfo *player, *v;
   unsigned char nxn;
@@ -195,9 +160,7 @@ static void AddPlayer(int fd)
   player->action = 0;
   player->action_received = FALSE;
 
-#if defined(TARGET_SDL)
   SDLNet_TCP_AddSocket(fds, fd);
-#endif
 
   first_player = player;
 
@@ -222,11 +185,6 @@ static void AddPlayer(int fd)
   }
 
   player->number = nxn;
-#if !defined(TARGET_SDL)
-  if (options.verbose)
-    Error(ERR_NETWORK_SERVER, "client %d connecting from %s",
-         nxn, inet_ntoa(saddr.sin_addr));
-#endif
   clients++;
 
   buffer[0] = 0;
@@ -293,11 +251,11 @@ static void Handle_OP_NUMBER_WANTED(struct NetworkServerPlayerInfo *player)
       Error(ERR_NETWORK_SERVER, "client %d (%s) switches to # %d",
            player->number, player->player_name, nr_wanted);
     else if (player->number == nr_wanted)
-      Error(ERR_NETWORK_SERVER, "client %d (%s) still has # %d",
+      Error(ERR_NETWORK_SERVER, "client %d (%s) already has # %d",
            player->number, player->player_name, nr_wanted);
     else
       Error(ERR_NETWORK_SERVER,
-           "client %d (%s) cannot switch (client %d still exists)",
+           "client %d (%s) cannot switch (client %d already exists)",
            player->number, player->player_name, nr_wanted);
   }
 
@@ -375,11 +333,11 @@ static void Handle_OP_START_PLAYING(struct NetworkServerPlayerInfo *player)
 
   if (options.verbose)
     Error(ERR_NETWORK_SERVER,
-         "client %d (%s) starts game [level %d from levedir %d (%s)]",
+         "client %d (%s) starts game [level %d from leveldir %d (%s)]",
          player->number, player->player_name,
          (buffer[2] << 8) + buffer[3],
          (buffer[4] << 8) + buffer[5],
-         &buffer[6]);
+         &buffer[10]);
 
   for (w = first_player; w; w = w->next)
     if (w->introduced)
@@ -388,6 +346,8 @@ static void Handle_OP_START_PLAYING(struct NetworkServerPlayerInfo *player)
   /* reset frame counter */
   ServerFrameCounter = 0;
 
+  Error(ERR_NETWORK_SERVER, "resetting ServerFrameCounter to 0");
+
   /* reset player actions */
   for (v = first_player; v; v = v->next)
   {
@@ -416,10 +376,12 @@ static void Handle_OP_CONTINUE_PLAYING(struct NetworkServerPlayerInfo *player)
 
 static void Handle_OP_STOP_PLAYING(struct NetworkServerPlayerInfo *player)
 {
+  int cause_for_stopping = buffer[2];
+
   if (options.verbose)
-    Error(ERR_NETWORK_SERVER, "client %d (%s) stops game",
-         player->number, player->player_name);
-  broadcast(NULL, 2, 0);
+    Error(ERR_NETWORK_SERVER, "client %d (%s) stops game [%d]",
+         player->number, player->player_name, cause_for_stopping);
+  broadcast(NULL, 3, 0);
 }
 
 static void Handle_OP_MOVE_PLAYER(struct NetworkServerPlayerInfo *player)
@@ -470,7 +432,13 @@ static void Handle_OP_MOVE_PLAYER(struct NetworkServerPlayerInfo *player)
   ServerFrameCounter++;
 }
 
-#if defined(TARGET_SDL)
+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)
@@ -480,23 +448,13 @@ int NetworkServerThread(void *ptr)
   /* should never be reached */
   return 0;
 }
-#endif
 
 void NetworkServer(int port, int serveronly)
 {
-  int sl;
   struct NetworkServerPlayerInfo *player;
   int r; 
   unsigned int len;
-#if defined(TARGET_SDL)
   IPaddress ip;
-#else
-  int i, on;
-  int is_daemon = 0;
-  struct protoent *tcpproto;
-  struct timeval tv;
-  int mfd;
-#endif
 
 #if defined(PLATFORM_UNIX) && !defined(PLATFORM_NEXT)
   struct sigaction sact;
@@ -505,14 +463,13 @@ 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;
 
-#if !defined(TARGET_SDL)
-  if ((tcpproto = getprotobyname("tcp")) != NULL)
-    tcp = tcpproto->p_proto;
-#endif
-
 #if defined(PLATFORM_UNIX)
 #if defined(PLATFORM_NEXT)
   signal(SIGPIPE, SIG_IGN);
@@ -524,67 +481,29 @@ void NetworkServer(int port, int serveronly)
 #endif
 #endif
 
-#if defined(TARGET_SDL)
-
-  /* assume that SDL is already initialized */
-#if 0
-  if (SDLNet_Init() == -1)
-    Error(ERR_EXIT_NETWORK_SERVER, "SDLNet_Init() failed");
-  atexit(SDLNet_Quit);
-#endif
-
   if (SDLNet_ResolveHost(&ip, NULL, port) == -1)
-    Error(ERR_EXIT_NETWORK_SERVER, "SDLNet_ResolveHost() failed");
-
-  lfd = SDLNet_TCP_Open(&ip);
-  if (!lfd)
-    Error(ERR_EXIT_NETWORK_SERVER, "SDLNet_TCP_Open() failed");
+    Error(ERR_EXIT_NETWORK_SERVER, "SDLNet_ResolveHost() failed: %s",
+          SDLNet_GetError());
 
-  fds = SDLNet_AllocSocketSet(MAX_PLAYERS+1);
-  SDLNet_TCP_AddSocket(fds, lfd);
+  if ((fds = SDLNet_AllocSocketSet(MAX_PLAYERS + 1 + 1)) == NULL)
+    Error(ERR_EXIT_NETWORK_SERVER, "SDLNet_AllocSocketSet() failed: %s"),
+      SDLNet_GetError();
 
-#else
-
-  if ((lfd = socket(PF_INET, SOCK_STREAM, 0)) < 0)
-    Error(ERR_EXIT_NETWORK_SERVER, "socket() failed");
-
-  saddr.sin_family = AF_INET;
-  saddr.sin_addr.s_addr = htonl(INADDR_ANY);
-  saddr.sin_port = htons(port);
-
-  on = 1;
-
-  setsockopt(lfd, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(int));
-  if (bind(lfd, (struct sockaddr *)&saddr, sizeof(saddr)) < 0)
-    Error(ERR_EXIT_NETWORK_SERVER, "bind() failed");
+  if ((lfd = SDLNet_TCP_Open(&ip)) == NULL)
+    Error(ERR_EXIT_NETWORK_SERVER, "SDLNet_TCP_Open() failed: %s"),
+      SDLNet_GetError();
 
-  listen(lfd, 5);
-#endif
+  if (SDLNet_TCP_AddSocket(fds, lfd) == -1)
+    Error(ERR_EXIT_NETWORK_SERVER, "SDLNet_TCP_AddSocket() failed: %s"),
+      SDLNet_GetError();
 
-#if !defined(TARGET_SDL)
-  if (is_daemon)
-  {
-    /* become a daemon, breaking all ties with the controlling terminal */
-    options.verbose = FALSE;
-    for (i = 0; i < 255; i++)
-    {
-      if (i != lfd)
-       close(i);
-    }
+  if ((udp = SDLNet_UDP_Open(port)) == NULL)
+    Error(ERR_EXIT_NETWORK_SERVER, "SDLNet_UDP_Open() failed: %s",
+          SDLNet_GetError());
 
-    if (fork())
-      exit(0);
-    setsid();
-    if (fork())
-      exit(0);
-    chdir("/");
-
-    /* open a fake stdin, stdout, stderr, just in case */
-    open("/dev/null", O_RDONLY);
-    open("/dev/null", O_WRONLY);
-    open("/dev/null", O_WRONLY);
-  }
-#endif
+  if (SDLNet_UDP_AddSocket(fds, udp) == -1)
+    Error(ERR_EXIT_NETWORK_SERVER, "SDLNet_TCP_AddSocket() failed: %s"),
+      SDLNet_GetError();
 
   if (options.verbose)
   {
@@ -600,47 +519,15 @@ void NetworkServer(int port, int serveronly)
     for (player = first_player; player; player = player->next)
       flushuser(player);
 
-#if defined(TARGET_SDL)
-    if ((sl = SDLNet_CheckSockets(fds, 500000)) < 1)
-    {
-      Error(ERR_NETWORK_SERVER, SDLNet_GetError());
-      perror("SDLNet_CheckSockets");
-    }
-
-#else
-
-    FD_ZERO(&fds);
-    mfd = lfd;
-    player = first_player;
-    while (player)
-    {
-      FD_SET(player->fd, &fds);
-      if (player->fd > mfd)
-       mfd = player->fd;
-      player = player->next;
-    }
-    FD_SET(lfd, &fds);
-    tv.tv_sec = 0;
-    tv.tv_usec = 500000;
-    if ((sl = select(mfd + 1, &fds, NULL, NULL, &tv)) < 0)
-    {
-      if (errno != EINTR)
-       Error(ERR_EXIT_NETWORK_SERVER, "select() failed");
-      else
-       continue;
-    }
-#endif
-
-    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 */
-#if defined(TARGET_SDL)
+    /* accept incoming TCP connections */
     if (SDLNet_SocketReady(lfd))
     {
+      Error(ERR_DEBUG, "got TCP packet");
+
       TCPsocket newsock;
 
       newsock = SDLNet_TCP_Accept(lfd);
@@ -649,50 +536,31 @@ void NetworkServer(int port, int serveronly)
        AddPlayer(newsock);
     }
 
-#else
-
-    if (FD_ISSET(lfd, &fds))
+    /* accept incoming UDP packets */
+    if (SDLNet_SocketReady(udp))
     {
-      int newfd, slen;
+      Error(ERR_DEBUG, "got UDP packet");
 
-      slen = sizeof(saddr);
-      newfd = accept(lfd, (struct sockaddr *)&saddr, &slen);
-      if (newfd < 0)
-      {
-       if (errno != EINTR)
-         Error(ERR_EXIT_NETWORK_SERVER, "accept() failed");
-      }
-      else
+      static UDPpacket packet;
+
+      int num_packets = SDLNet_UDP_Recv(udp, &packet);
+
+      if (num_packets == 1)
       {
-       if (tcp != -1)
-       {
-         on = 1;
-         setsockopt(newfd, tcp, TCP_NODELAY, (char *)&on, sizeof(int));
-       }
-       AddPlayer(newfd);
+        // bounce packet
+        SDLNet_UDP_Send(udp, -1, &packet);
       }
-      continue;
     }
-#endif
 
     player = first_player;
 
-    do
+    while (player && !interrupt)
     {
-#if defined(TARGET_SDL)
       if (SDLNet_SocketReady(player->fd))
-#else
-      if (FD_ISSET(player->fd, &fds))
-#endif
       {
-#if defined(TARGET_SDL)
        /* read only 1 byte, because SDLNet blocks when we want more than is
           in the buffer */
        r = SDLNet_TCP_Recv(player->fd, player->readbuffer + player->nread, 1);
-#else
-       r = read(player->fd, player->readbuffer + player->nread,
-                MAX_BUFFER_SIZE - player->nread);
-#endif
 
        if (r <= 0)
        {
@@ -721,17 +589,19 @@ 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;
            break;
          }
 
-         switch(buffer[1])
+         switch (buffer[1])
          {
            case OP_PLAYER_NAME:
              Handle_OP_PLAYER_NAME(player, len);
@@ -785,8 +655,5 @@ void NetworkServer(int port, int serveronly)
       if (player && !interrupt)
        player = player->next;
     }
-    while (player && !interrupt);
   }
 }
-
-#endif /* NETWORK_AVALIABLE */