// network.c
// ============================================================================
-#include "libgame/platform.h"
-
-#if defined(NETWORK_AVALIABLE)
-
#include <signal.h>
#include <sys/time.h>
-#include "main.h"
-
#include "libgame/libgame.h"
#include "network.h"
#include "netserv.h"
+#include "main.h"
#include "game.h"
#include "tape.h"
#include "files.h"
/* 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];
static void SendBufferToServer(int size)
{
- if (!options.network)
+ if (!network.enabled)
return;
realbuffer[0] = realbuffer[1] = realbuffer[2] = 0;
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)
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)
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? */
{
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);
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()
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;
+ struct PlayerInfo *client_player = &stored_player[index_nr];
+ boolean stopped_by_remote_player = (!client_player->connected_locally);
+ 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);
/* 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;
}
SendToServer_StopPlaying(NETWORK_STOP_BY_ERROR);
}
-/* TODO */
+static char *HandleNetworkingPackets()
+{
+ while (1)
+ {
+ /* ---------- check network server for activity ---------- */
+
+ int num_active_sockets = SDLNet_CheckSockets(rfds, 1);
+
+ if (num_active_sockets < 0)
+ return "Error checking network sockets!";
+
+ if (num_active_sockets == 0)
+ break; // no active sockets, stop here
+
+ /* ---------- read packets from network server ---------- */
+
+ int num_bytes = SDLNet_TCP_Recv(sfd, readbuffer + nread, 1);
+
+ if (num_bytes < 0)
+ return "Error reading from network server!";
+
+ if (num_bytes == 0)
+ return "Connection to network server lost!";
+
+ nread += num_bytes;
+
+ HandleNetworkingMessages();
+ }
+
+ return NULL;
+}
+
+static void HandleNetworkingDisconnect()
+{
+ int i;
+
+ SDLNet_TCP_DelSocket(rfds, sfd);
+ SDLNet_TCP_Close(sfd);
+
+ network.enabled = FALSE;
+ network_playing = FALSE;
+
+ for (i = 0; i < MAX_PLAYERS; i++)
+ stored_player[i].connected_network = FALSE;
+}
void HandleNetworking()
{
- int r = 0;
+ char *error_message = HandleNetworkingPackets();
- do
+ if (error_message != NULL)
{
- if ((r = SDLNet_CheckSockets(rfds, 1)) < 0)
- Error(ERR_EXIT, "HandleNetworking(): SDLNet_CheckSockets() failed");
+ HandleNetworkingDisconnect();
- if (r > 0)
+ if (game_status == GAME_MODE_PLAYING)
{
- r = SDLNet_TCP_Recv(sfd, readbuffer + nread, 1);
-
- if (r < 0)
- Error(ERR_EXIT, "error reading from network server");
+ Request(error_message, REQ_CONFIRM | REQ_STAY_CLOSED);
- if (r == 0)
- Error(ERR_EXIT, "connection to network server lost");
+ SetGameStatus(GAME_MODE_MAIN);
- nread += r;
-
- HandleNetworkingMessages();
+ DrawMainMenu();
+ }
+ else
+ {
+ Request(error_message, REQ_CONFIRM);
}
}
- while (r > 0);
}
-
-#endif /* NETWORK_AVALIABLE */