added separate network info structure for network options and setup values
[rocksndiamonds.git] / src / network.c
index 56d6bc66226452bbdff72c886630fe425263f44b..742cea5792e6a0cd639b851b12a4c92f2916c329 100644 (file)
@@ -44,7 +44,8 @@ static struct NetworkClientPlayerInfo first_player =
 
 /* server stuff */
 
-static TCPsocket sfd;          /* server socket */
+static TCPsocket sfd;          /* TCP server socket */
+static UDPsocket udp;          /* UDP server socket */
 static SDLNet_SocketSet rfds;  /* socket set */
 
 static byte realbuffer[512];
@@ -55,7 +56,7 @@ static boolean stop_network_game = FALSE;
 
 static void SendBufferToServer(int size)
 {
-  if (!options.network)
+  if (!network.enabled)
     return;
 
   realbuffer[0] = realbuffer[1] = realbuffer[2] = 0;
@@ -121,25 +122,99 @@ static void StartNetworkServer(int port)
 boolean ConnectToServer(char *hostname, int port)
 {
   IPaddress ip;
+  int server_host = 0;
   int i;
 
   if (port == 0)
     port = DEFAULT_SERVER_PORT;
 
+  if (hostname == NULL)
+  {
+    // if no hostname given, try to auto-detect network server in local network
+    // by doing a UDP broadcast on the network server port and wait for answer
+
+    SDLNet_SocketSet udp_socket_set = SDLNet_AllocSocketSet(1);
+    if (!udp_socket_set)
+      Error(ERR_EXIT, "SDLNet_AllocSocketSet() failed: %s"), SDLNet_GetError();
+
+    udp = SDLNet_UDP_Open(0);
+    if(!udp)
+      Error(ERR_EXIT, "SDLNet_UDP_Open() failed: %s", SDLNet_GetError());
+
+    if (SDLNet_UDP_AddSocket(udp_socket_set, udp) == -1)
+      Error(ERR_EXIT_NETWORK_SERVER, "SDLNet_TCP_AddSocket() failed: %s"),
+        SDLNet_GetError();
+
+    char *data_ptr = "network server UDB broadcast";
+    int data_len = strlen(data_ptr) + 1;
+    IPaddress ip_address;
+
+    SDLNet_Write32(0xffffffff, &ip_address.host);      /* 255.255.255.255 */
+    SDLNet_Write16(port,       &ip_address.port);
+
+    UDPpacket packet =
+    {
+      -1,
+      (Uint8 *)data_ptr,
+      data_len,
+      data_len,
+      0,
+      ip_address
+    };
+
+    SDLNet_UDP_Send(udp, -1, &packet);
+
+    Error(ERR_DEBUG, "doing UDP broadcast for local network server ...");
+
+    if (SDLNet_CheckSockets(udp_socket_set, 1000) == 1)
+    {
+      int num_packets = SDLNet_UDP_Recv(udp, &packet);
+
+      if (num_packets == 1)
+      {
+        Error(ERR_DEBUG, "network server found");
+
+        server_host = SDLNet_Read32(&packet.address.host);
+      }
+      else
+      {
+       Error(ERR_DEBUG, "no answer from network server");
+      }
+    }
+    else
+    {
+      Error(ERR_DEBUG, "no network server found");
+    }
+  }
+
   rfds = SDLNet_AllocSocketSet(1);
 
   if (hostname)
   {
     SDLNet_ResolveHost(&ip, hostname, port);
+
     if (ip.host == INADDR_NONE)
       Error(ERR_EXIT, "cannot locate host '%s'", hostname);
+    else
+      server_host = SDLNet_Read32(&ip.host);
   }
   else
   {
-    SDLNet_Write32(0x7f000001, &ip.host);      /* 127.0.0.1 */
-    SDLNet_Write16(port, &ip.port);
+    // if no hostname was given and no network server was auto-detected in the
+    // local network, try to connect to a network server at the local host
+    if (server_host == 0)
+      server_host = 0x7f000001;                        /* 127.0.0.1 */
+
+    SDLNet_Write32(server_host, &ip.host);
+    SDLNet_Write16(port,        &ip.port);
   }
 
+  Error(ERR_DEBUG, "trying to connect to network server at %d.%d.%d.%d ...",
+        (server_host >> 24) & 0xff,
+        (server_host >> 16) & 0xff,
+        (server_host >>  8) & 0xff,
+        (server_host >>  0) & 0xff);
+
   sfd = SDLNet_TCP_Open(&ip);
 
   if (sfd)
@@ -275,11 +350,15 @@ static void Handle_OP_YOUR_NUMBER()
 
   if (old_local_player != new_local_player)
   {
-    /* copy existing player settings and change to new player */
+    /* set relevant player settings and change to new player */
 
-    *new_local_player = *old_local_player;
-    old_local_player->connected = FALSE;
     local_player = new_local_player;
+
+    old_local_player->connected_locally = FALSE;
+    new_local_player->connected_locally = TRUE;
+
+    old_local_player->connected_network = FALSE;
+    new_local_player->connected_network = TRUE;
   }
 
   if (first_player.nr > MAX_PLAYERS)
@@ -313,17 +392,22 @@ static void Handle_OP_NUMBER_WANTED()
 
     if (old_client_nr != new_client_nr)
     {
-      /* copy existing player settings and change to new player */
+      /* set relevant player settings and change to new player */
 
-      *new_player = *old_player;
-      old_player->connected = FALSE;
+      old_player->connected_network = FALSE;
+      new_player->connected_network = TRUE;
     }
 
     player = getNetworkPlayer(old_client_nr);
     player->nr = new_client_nr;
 
     if (old_player == local_player)            /* local player switched */
+    {
       local_player = new_player;
+
+      old_player->connected_locally = FALSE;
+      new_player->connected_locally = TRUE;
+    }
   }
   else if (old_client_nr == first_player.nr)   /* failed -- local player? */
   {
@@ -375,13 +459,14 @@ static void Handle_OP_PLAYER_CONNECTED()
   player->name[0] = '\0';
   player->next = NULL;
 
-  stored_player[new_index_nr].connected = TRUE;
+  stored_player[new_index_nr].connected_network = TRUE;
 }
 
 static void Handle_OP_PLAYER_DISCONNECTED()
 {
   struct NetworkClientPlayerInfo *player, *player_disconnected;
   int player_nr = (int)buffer[0];
+  int index_nr = player_nr - 1;
 
   printf("OP_PLAYER_DISCONNECTED: %d\n", player_nr);
   player_disconnected = getNetworkPlayer(player_nr);
@@ -392,6 +477,9 @@ static void Handle_OP_PLAYER_DISCONNECTED()
     if (player->next == player_disconnected)
       player->next = player_disconnected->next;
   free(player_disconnected);
+
+  stored_player[index_nr].connected_locally = FALSE;
+  stored_player[index_nr].connected_network = FALSE;
 }
 
 static void Handle_OP_START_PLAYING()
@@ -456,12 +544,17 @@ static void Handle_OP_STOP_PLAYING()
 
   if (game_status == GAME_MODE_PLAYING)
   {
-    if (buffer[2] == NETWORK_STOP_BY_PLAYER)
-      Request("Network game stopped by player!", REQ_CONFIRM);
-    else if (buffer[2] == NETWORK_STOP_BY_ERROR)
-      Request("Network game stopped due to internal error!", REQ_CONFIRM);
-    else
-      Request("Network game stopped!", REQ_CONFIRM);
+    int client_nr = buffer[0];
+    int index_nr = client_nr - 1;
+    boolean stopped_by_remote_player = (index_nr != local_player->index_nr);
+    char *message = (buffer[2] == NETWORK_STOP_BY_PLAYER ?
+                    "Network game stopped by player!" :
+                    buffer[2] == NETWORK_STOP_BY_ERROR ?
+                    "Network game stopped due to internal error!" :
+                    "Network game stopped!");
+
+    if (buffer[2] != NETWORK_STOP_BY_PLAYER || stopped_by_remote_player)
+      Request(message, REQ_CONFIRM | REQ_STAY_CLOSED);
   }
 
   SetGameStatus(GAME_MODE_MAIN);
@@ -495,7 +588,7 @@ static void Handle_OP_MOVE_PLAYER(unsigned int len)
   /* copy valid player actions */
   for (i = 0; i < MAX_PLAYERS; i++)
     stored_player[i].effective_action =
-      (i < len - 6 && stored_player[i].active ? buffer[6 + i] : 0);
+      (i < len - 6 ? buffer[6 + i] : 0);
 
   network_player_action_received = TRUE;
 }