updated contact info in source file headers
[rocksndiamonds.git] / src / netserv.c
index 10b0ab504d5615a66dc17e9221e7630f7bd60238..5ecbf1b72082c7586a3f3604ffbbd5299c5d67cc 100644 (file)
@@ -1,38 +1,49 @@
-/***********************************************************
-*  Rocks'n'Diamonds -- McDuffin Strikes Back!              *
-*----------------------------------------------------------*
-*  (c) 1995-98 Artsoft Entertainment                       *
-*              Holger Schemel                              *
-*              Oststrasse 11a                              *
-*              33604 Bielefeld                             *
-*              phone: ++49 +521 290471                     *
-*              email: aeglos@valinor.owl.de                *
-*----------------------------------------------------------*
-*  network.c                                               *
-***********************************************************/
-
-#if !defined(MSDOS) && !defined(WIN32)
+// ============================================================================
+// Rocks'n'Diamonds - McDuffin Strikes Back!
+// ----------------------------------------------------------------------------
+// (c) 1995-2014 by Artsoft Entertainment
+//                         Holger Schemel
+//                 info@artsoft.org
+//                 http://www.artsoft.org/
+// ----------------------------------------------------------------------------
+// netserv.c
+// ============================================================================
+
+#include "libgame/platform.h"
+
+#if defined(NETWORK_AVALIABLE)
 
 #include <fcntl.h>
 #include <sys/time.h>
 #include <signal.h>
-#include <sys/socket.h>
 #include <errno.h>
-#include <string.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 "misc.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;
@@ -49,16 +60,22 @@ 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 SDLNet_SocketSet fds;   /* socket set */
+#else
 static struct sockaddr_in saddr;
-static int lfd;
+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 int tcp = -1;
-
-static unsigned long ServerFrameCounter = 0;
 
-static fd_set fds;
+static unsigned int ServerFrameCounter = 0;
 
 static void addtobuffer(struct NetworkServerPlayerInfo *player,
                        unsigned char *b, int len)
@@ -75,7 +92,12 @@ static void flushuser(struct NetworkServerPlayerInfo *player)
 {
   if (player->nwrite)
   {
-    write(player->fd, player->writbuffer, player->nwrite);
+#if defined(TARGET_SDL)
+    SDLNet_TCP_Send(player->fd, player->writbuffer, player->nwrite);
+#else
+    if (write(player->fd, player->writbuffer, player->nwrite) == -1)
+      Error(ERR_WARN, "write() failed; %s", strerror(errno));
+#endif
     player->nwrite = 0;
   }
 }
@@ -87,8 +109,9 @@ static void broadcast(struct NetworkServerPlayerInfo *except,
 
   realbuffer[0] = realbuffer[1] = realbuffer[2] = 0;
   realbuffer[3] = (unsigned char)len;
-  for (player=first_player; player; player=player->next)
-    if (player != except && (player->active || !activeonly) && player->introduced)
+  for (player = first_player; player; player = player->next)
+    if (player != except && player->introduced &&
+       (player->active || !activeonly))
       addtobuffer(player, realbuffer, 4 + len);
 }
 
@@ -111,7 +134,7 @@ static void RemovePlayer(struct NetworkServerPlayerInfo *player)
     first_player = player->next;
   else
   {
-    for (v=first_player; v; v=v->next)
+    for (v = first_player; v; v = v->next)
     {
       if (v->next && v->next == player)
       {
@@ -120,7 +143,13 @@ 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)
   {
@@ -143,10 +172,15 @@ static void RemovePlayer(struct NetworkServerPlayerInfo *player)
   }
 }
 
+#if defined(TARGET_SDL)
+static void AddPlayer(TCPsocket fd)
+#else
 static void AddPlayer(int fd)
+#endif
 {
   struct NetworkServerPlayerInfo *player, *v;
   unsigned char nxn;
+  boolean again = TRUE;
 
   player = checked_malloc(sizeof (struct NetworkServerPlayerInfo));
 
@@ -160,26 +194,38 @@ 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;
 
   nxn = 1;
 
- again:
-  v = player->next;
-  while(v)
+  while (again)
   {
-    if (v->number == nxn)
+    again = FALSE;
+    v = player->next;
+
+    while (v)
     {
-      nxn++;
-      goto again;
+      if (v->number == nxn)
+      {
+       nxn++;
+
+       again = TRUE;
+       break;
+      }
+      v = v->next;
     }
-    v = v->next;
   }
 
   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;
@@ -231,7 +277,7 @@ static void Handle_OP_NUMBER_WANTED(struct NetworkServerPlayerInfo *player)
       Error(ERR_NETWORK_SERVER, "client %d (%s) wants to switch to # %d",
            player->number, player->player_name, nr_wanted);
 
-  for (v=first_player; v; v=v->next)
+  for (v = first_player; v; v = v->next)
   {
     if (v->number == nr_wanted)
     {
@@ -246,11 +292,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);
   }
 
@@ -279,7 +325,7 @@ static void Handle_OP_PLAYER_NAME(struct NetworkServerPlayerInfo *player,
     len=16;
   memcpy(player->player_name, &buffer[2], len-2);
   player->player_name[len-2] = 0;
-  for (i=0; i<len-2; i++)
+  for (i = 0; i < len - 2; i++)
   {
     if (player->player_name[i] < ' ' || 
        ((unsigned char)(player->player_name[i]) > 0x7e &&
@@ -305,7 +351,7 @@ static void Handle_OP_PLAYER_NAME(struct NetworkServerPlayerInfo *player,
 
   if (!player->introduced)
   {
-    for (v=first_player; v; v=v->next)
+    for (v = first_player; v; v = v->next)
     {
       if (v != player && v->introduced)
       {
@@ -328,21 +374,23 @@ 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)
+  for (w = first_player; w; w = w->next)
     if (w->introduced)
       w->active = 1;
 
   /* reset frame counter */
   ServerFrameCounter = 0;
 
+  Error(ERR_NETWORK_SERVER, "resetting ServerFrameCounter to 0");
+
   /* reset player actions */
-  for (v=first_player; v; v=v->next)
+  for (v = first_player; v; v = v->next)
   {
     v->action = 0;
     v->action_received = FALSE;
@@ -369,20 +417,22 @@ 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_FIGURE(struct NetworkServerPlayerInfo *player)
+static void Handle_OP_MOVE_PLAYER(struct NetworkServerPlayerInfo *player)
 {
   struct NetworkServerPlayerInfo *v;
   int last_client_nr = 0;
   int i;
 
   /* store player action */
-  for (v=first_player; v; v=v->next)
+  for (v = first_player; v; v = v->next)
   {
     if (v->number == player->number)
     {
@@ -392,7 +442,7 @@ static void Handle_OP_MOVE_FIGURE(struct NetworkServerPlayerInfo *player)
   }
 
   /* check if server received action from each player */
-  for (v=first_player; v; v=v->next)
+  for (v = first_player; v; v = v->next)
   {
     if (!v->action_received)
       return;
@@ -402,11 +452,11 @@ static void Handle_OP_MOVE_FIGURE(struct NetworkServerPlayerInfo *player)
   }
 
   /* initialize all player actions to zero */
-  for (i=0; i<last_client_nr; i++)
+  for (i = 0; i < last_client_nr; i++)
     buffer[6 + i] = 0;
 
   /* broadcast actions of all players to all players */
-  for (v=first_player; v; v=v->next)
+  for (v = first_player; v; v = v->next)
   {
     buffer[6 + v->number-1] = v->action;
     v->action = 0;
@@ -420,21 +470,43 @@ static void Handle_OP_MOVE_FIGURE(struct NetworkServerPlayerInfo *player)
 
   broadcast(NULL, 6 + last_client_nr, 0);
 
+#if 0
+  Error(ERR_NETWORK_SERVER, "sending ServerFrameCounter value %d",
+       ServerFrameCounter);
+#endif
+
   ServerFrameCounter++;
 }
 
+#if defined(TARGET_SDL)
+/* the following is not used for a standalone server;
+   the pointer points to an integer containing the port-number */
+int NetworkServerThread(void *ptr)
+{
+  NetworkServer(*((int *) ptr), 0);
+
+  /* should never be reached */
+  return 0;
+}
+#endif
+
 void NetworkServer(int port, int serveronly)
 {
-  int i, sl, on;
+  int sl;
   struct NetworkServerPlayerInfo *player;
-  int mfd;
   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 is_daemon = 0;
+  int mfd;
+#endif
 
-#ifndef NeXT
+#if defined(PLATFORM_UNIX) && !defined(PLATFORM_NEXT)
   struct sigaction sact;
 #endif
 
@@ -444,10 +516,13 @@ void NetworkServer(int port, int serveronly)
   if (!serveronly)
     onceonly = 1;
 
+#if !defined(TARGET_SDL)
   if ((tcpproto = getprotobyname("tcp")) != NULL)
     tcp = tcpproto->p_proto;
+#endif
 
-#ifdef NeXT
+#if defined(PLATFORM_UNIX)
+#if defined(PLATFORM_NEXT)
   signal(SIGPIPE, SIG_IGN);
 #else
   sact.sa_handler = SIG_IGN;
@@ -455,7 +530,28 @@ void NetworkServer(int port, int serveronly)
   sact.sa_flags = 0;
   sigaction(SIGPIPE, &sact, NULL);
 #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");
+
+  fds = SDLNet_AllocSocketSet(MAX_PLAYERS+1);
+  SDLNet_TCP_AddSocket(fds, lfd);
+
+#else
 
   if ((lfd = socket(PF_INET, SOCK_STREAM, 0)) < 0)
     Error(ERR_EXIT_NETWORK_SERVER, "socket() failed");
@@ -471,12 +567,14 @@ void NetworkServer(int port, int serveronly)
     Error(ERR_EXIT_NETWORK_SERVER, "bind() failed");
 
   listen(lfd, 5);
+#endif
 
+#if !defined(TARGET_SDL)
   if (is_daemon)
   {
     /* become a daemon, breaking all ties with the controlling terminal */
-    options.verbose = 0;
-    for (i=0; i<255; i++)
+    options.verbose = FALSE;
+    for (i = 0; i < 255; i++)
     {
       if (i != lfd)
        close(i);
@@ -487,13 +585,15 @@ void NetworkServer(int port, int serveronly)
     setsid();
     if (fork())
       exit(0);
-    chdir("/");
+    if (chdir("/") == -1)
+      Error(ERR_WARN, "chdir() failed; %s", strerror(errno));
 
     /* 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 (options.verbose)
   {
@@ -502,13 +602,23 @@ void NetworkServer(int port, int serveronly)
          PROTOCOL_VERSION_1, PROTOCOL_VERSION_2, PROTOCOL_VERSION_3);
   }
 
-  while(1)
+  while (1)
   {
     interrupt = 0;
 
-    for (player=first_player; player; player=player->next)
+    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_CheckSockets failed: %s",
+           SDLNet_GetError());
+      perror("SDLNet_CheckSockets");
+    }
+
+#else
+
     FD_ZERO(&fds);
     mfd = lfd;
     player = first_player;
@@ -529,6 +639,7 @@ void NetworkServer(int port, int serveronly)
       else
        continue;
     }
+#endif
 
     if (sl < 0)
       continue;
@@ -536,9 +647,24 @@ void NetworkServer(int port, int serveronly)
     if (sl == 0)
       continue;
 
+    /* accept incoming connections */
+#if defined(TARGET_SDL)
+    if (SDLNet_SocketReady(lfd))
+    {
+      TCPsocket newsock;
+
+      newsock = SDLNet_TCP_Accept(lfd);
+
+      if (newsock)
+       AddPlayer(newsock);
+    }
+
+#else
+
     if (FD_ISSET(lfd, &fds))
     {
-      int newfd, slen;
+      int newfd;
+      socklen_t slen;
 
       slen = sizeof(saddr);
       newfd = accept(lfd, (struct sockaddr *)&saddr, &slen);
@@ -558,14 +684,27 @@ void NetworkServer(int port, int serveronly)
       }
       continue;
     }
+#endif
 
     player = first_player;
 
     do
     {
+#if defined(TARGET_SDL)
+      if (SDLNet_SocketReady(player->fd))
+#else
       if (FD_ISSET(player->fd, &fds))
+#endif
       {
-       r = read(player->fd, player->readbuffer + player->nread, MAX_BUFFER_SIZE - player->nread);
+#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)
        {
          if (options.verbose)
@@ -603,7 +742,7 @@ void NetworkServer(int port, int serveronly)
            break;
          }
 
-         switch(buffer[1])
+         switch (buffer[1])
          {
            case OP_PLAYER_NAME:
              Handle_OP_PLAYER_NAME(player, len);
@@ -633,8 +772,8 @@ void NetworkServer(int port, int serveronly)
              Handle_OP_STOP_PLAYING(player);
              break;
 
-           case OP_MOVE_FIGURE:
-             Handle_OP_MOVE_FIGURE(player);
+           case OP_MOVE_PLAYER:
+             Handle_OP_MOVE_PLAYER(player);
              break;
 
            case OP_BROADCAST_MESSAGE:
@@ -661,4 +800,4 @@ void NetworkServer(int port, int serveronly)
   }
 }
 
-#endif /* !MSDOS && !WIN32 */
+#endif /* NETWORK_AVALIABLE */