|
@@ -32,12 +32,16 @@ static Patches network_tmp_patches;
|
|
|
|
|
|
// The amount of clients connected
|
|
|
static byte _network_clients_connected = 0;
|
|
|
// The index counter for new clients (is never decreased)
|
|
|
static uint16 _network_client_index = NETWORK_SERVER_INDEX + 1;
|
|
|
|
|
|
/* Some externs / forwards */
|
|
|
extern void ShowJoinStatusWindow();
|
|
|
extern void StateGameLoop();
|
|
|
|
|
|
// Function that looks up the CI for a given client-index
|
|
|
NetworkClientInfo *NetworkFindClientInfoFromIndex(uint16 client_index)
|
|
|
{
|
|
|
NetworkClientInfo *ci;
|
|
|
|
|
|
for (ci = _network_client_info; ci != &_network_client_info[MAX_CLIENT_INFO]; ci++)
|
|
@@ -45,26 +49,26 @@ NetworkClientInfo *NetworkFindClientInfo
|
|
|
return ci;
|
|
|
|
|
|
return NULL;
|
|
|
}
|
|
|
|
|
|
// Function that looks up the CS for a given client-index
|
|
|
ClientState *NetworkFindClientStateFromIndex(uint16 client_index)
|
|
|
NetworkClientState *NetworkFindClientStateFromIndex(uint16 client_index)
|
|
|
{
|
|
|
ClientState *cs;
|
|
|
NetworkClientState *cs;
|
|
|
|
|
|
for (cs = _clients; cs != &_clients[MAX_CLIENT_INFO]; cs++)
|
|
|
if (cs->index == client_index)
|
|
|
return cs;
|
|
|
|
|
|
return NULL;
|
|
|
}
|
|
|
|
|
|
// NetworkGetClientName is a server-safe function to get the name of the client
|
|
|
// if the user did not send it yet, Client #<no> is used.
|
|
|
void NetworkGetClientName(char *client_name, size_t size, ClientState *cs)
|
|
|
void NetworkGetClientName(char *client_name, size_t size, const NetworkClientState *cs)
|
|
|
{
|
|
|
NetworkClientInfo *ci = DEREF_CLIENT_INFO(cs);
|
|
|
if (ci->client_name[0] == '\0')
|
|
|
snprintf(client_name, size, "Client #%d", cs->index);
|
|
|
else
|
|
|
snprintf(client_name, size, "%s", ci->client_name);
|
|
@@ -116,13 +120,13 @@ void CDECL NetworkTextMessage(NetworkAct
|
|
|
AddTextMessage(color, duration, "[All] %s: %s", name, buf);
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// Calculate the frame-lag of a client
|
|
|
uint NetworkCalculateLag(const ClientState *cs)
|
|
|
uint NetworkCalculateLag(const NetworkClientState *cs)
|
|
|
{
|
|
|
int lag = cs->last_frame_server - cs->last_frame;
|
|
|
// This client has missed his ACK packet after 1 DAY_TICKS..
|
|
|
// so we increase his lag for every frame that passes!
|
|
|
// The packet can be out by a max of _net_frame_freq
|
|
|
if (cs->last_frame_server + DAY_TICKS + _network_frame_freq < _frame_counter)
|
|
@@ -147,21 +151,21 @@ void ClientStartError(char *error) {
|
|
|
|
|
|
void ServerStartError(char *error) {
|
|
|
DEBUG(net, 0)("[NET] Server could not start network: %s",error);
|
|
|
NetworkError(STR_NETWORK_ERR_SERVER_START);
|
|
|
}
|
|
|
|
|
|
void NetworkClientError(byte res, ClientState *cs) {
|
|
|
static void NetworkClientError(byte res, NetworkClientState *cs) {
|
|
|
// First, send a CLIENT_ERROR to the server, so he knows we are
|
|
|
// disconnection (and why!)
|
|
|
NetworkErrorCode errorno;
|
|
|
|
|
|
// We just want to close the connection..
|
|
|
if (res == NETWORK_RECV_STATUS_CLOSE_QUERY) {
|
|
|
cs->quited = true;
|
|
|
CloseClient(cs);
|
|
|
NetworkCloseClient(cs);
|
|
|
_networking = false;
|
|
|
|
|
|
DeleteWindowById(WC_NETWORK_STATUS_WINDOW, 0);
|
|
|
return;
|
|
|
}
|
|
|
|
|
@@ -176,18 +180,18 @@ void NetworkClientError(byte res, Client
|
|
|
|
|
|
// Dequeue all commands before closing the socket
|
|
|
NetworkSend_Packets(DEREF_CLIENT(0));
|
|
|
}
|
|
|
|
|
|
_switch_mode = SM_MENU;
|
|
|
CloseClient(cs);
|
|
|
NetworkCloseClient(cs);
|
|
|
_networking = false;
|
|
|
}
|
|
|
|
|
|
// Find all IP-aliases for this host
|
|
|
void NetworkFindIPs(void)
|
|
|
static void NetworkFindIPs(void)
|
|
|
{
|
|
|
int i, last;
|
|
|
|
|
|
#if defined(BEOS_NET_SERVER) /* doesn't have neither getifaddrs or net/if.h */
|
|
|
/* Based on Andrew Bachmann's netstat+.c. Big thanks to him! */
|
|
|
int _netstat(int fd, char **output, int verbose);
|
|
@@ -385,15 +389,15 @@ void ParseConnectionString(const byte **
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// Creates a new client from a socket
|
|
|
// Used both by the server and the client
|
|
|
static ClientState *AllocClient(SOCKET s)
|
|
|
static NetworkClientState *NetworkAllocClient(SOCKET s)
|
|
|
{
|
|
|
ClientState *cs;
|
|
|
NetworkClientState *cs;
|
|
|
NetworkClientInfo *ci;
|
|
|
byte client_no;
|
|
|
|
|
|
client_no = 0;
|
|
|
|
|
|
if (_network_server) {
|
|
@@ -426,26 +430,26 @@ static ClientState *AllocClient(SOCKET s
|
|
|
}
|
|
|
|
|
|
return cs;
|
|
|
}
|
|
|
|
|
|
// Close a connection
|
|
|
void CloseClient(ClientState *cs)
|
|
|
void NetworkCloseClient(NetworkClientState *cs)
|
|
|
{
|
|
|
NetworkClientInfo *ci;
|
|
|
// Socket is already dead
|
|
|
if (cs->socket == INVALID_SOCKET) return;
|
|
|
|
|
|
DEBUG(net, 1) ("[NET] Closed client connection");
|
|
|
|
|
|
if (!cs->quited && _network_server && cs->status > STATUS_INACTIVE) {
|
|
|
// We did not receive a leave message from this client...
|
|
|
NetworkErrorCode errorno = NETWORK_ERROR_CONNECTION_LOST;
|
|
|
char str1[100], str2[100];
|
|
|
char client_name[NETWORK_NAME_LENGTH];
|
|
|
ClientState *new_cs;
|
|
|
NetworkClientState *new_cs;
|
|
|
|
|
|
NetworkGetClientName(client_name, sizeof(client_name), cs);
|
|
|
|
|
|
GetString(str1, STR_NETWORK_ERR_LEFT);
|
|
|
GetString(str2, STR_NETWORK_ERR_CLIENT_GENERAL + errorno);
|
|
|
|
|
@@ -500,16 +504,14 @@ void CloseClient(ClientState *cs)
|
|
|
cs->socket = INVALID_SOCKET;
|
|
|
cs->status = STATUS_INACTIVE;
|
|
|
cs->index = NETWORK_EMPTY_INDEX;
|
|
|
ci->client_index = NETWORK_EMPTY_INDEX;
|
|
|
}
|
|
|
|
|
|
extern void ShowJoinStatusWindow();
|
|
|
|
|
|
// A client wants to connect to a server
|
|
|
bool NetworkConnect(const char *hostname, int port)
|
|
|
static bool NetworkConnect(const char *hostname, int port)
|
|
|
{
|
|
|
SOCKET s;
|
|
|
struct sockaddr_in sin;
|
|
|
|
|
|
DEBUG(net, 1) ("[NET] Connecting to %s %d", hostname, port);
|
|
|
|
|
@@ -547,13 +549,13 @@ bool NetworkConnect(const char *hostname
|
|
|
if (ioctlsocket(s, FIONBIO, &blocking) != 0)
|
|
|
#endif
|
|
|
DEBUG(net, 0)("[NET] Setting non-blocking failed"); /* XXX should this be an error? */
|
|
|
}
|
|
|
|
|
|
// in client mode, only the first client field is used. it's pointing to the server.
|
|
|
AllocClient(s);
|
|
|
NetworkAllocClient(s);
|
|
|
|
|
|
ShowJoinStatusWindow();
|
|
|
|
|
|
memcpy(&network_tmp_patches, &_patches, sizeof(_patches));
|
|
|
|
|
|
return true;
|
|
@@ -561,13 +563,13 @@ bool NetworkConnect(const char *hostname
|
|
|
|
|
|
// For the server, to accept new clients
|
|
|
static void NetworkAcceptClients(void)
|
|
|
{
|
|
|
struct sockaddr_in sin;
|
|
|
SOCKET s;
|
|
|
ClientState *cs;
|
|
|
NetworkClientState *cs;
|
|
|
#ifndef __MORPHOS__
|
|
|
int sin_len;
|
|
|
#else
|
|
|
LONG sin_len; // for some reason we need a 'LONG' under MorphOS
|
|
|
#endif
|
|
|
|
|
@@ -591,13 +593,13 @@ static void NetworkAcceptClients(void)
|
|
|
// set nodelay
|
|
|
#if !defined(BEOS_NET_SERVER) // not implemented on BeOS net_server...
|
|
|
// The (const char*) cast is needed for windows!!
|
|
|
{int b = 1; setsockopt(s, IPPROTO_TCP, TCP_NODELAY, (const char*)&b, sizeof(b));}
|
|
|
#endif
|
|
|
|
|
|
cs = AllocClient(s);
|
|
|
cs = NetworkAllocClient(s);
|
|
|
if (cs == NULL) {
|
|
|
// no more clients allowed?
|
|
|
// Send to the client that we are full!
|
|
|
Packet *p = NetworkSend_Init(PACKET_SERVER_FULL);
|
|
|
|
|
|
p->buffer[0] = p->size & 0xFF;
|
|
@@ -623,13 +625,13 @@ static void NetworkAcceptClients(void)
|
|
|
ci->client_ip = sin.sin_addr.s_addr;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// Set up the listen socket for the server
|
|
|
bool NetworkListen(void)
|
|
|
static bool NetworkListen(void)
|
|
|
{
|
|
|
SOCKET ls;
|
|
|
struct sockaddr_in sin;
|
|
|
int port;
|
|
|
|
|
|
port = _network_server_port;
|
|
@@ -679,37 +681,37 @@ bool NetworkListen(void)
|
|
|
_listensocket = ls;
|
|
|
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
// Close all current connections
|
|
|
void NetworkClose(void)
|
|
|
static void NetworkClose(void)
|
|
|
{
|
|
|
ClientState *cs;
|
|
|
NetworkClientState *cs;
|
|
|
|
|
|
FOR_ALL_CLIENTS(cs) {
|
|
|
if (!_network_server) {
|
|
|
SEND_COMMAND(PACKET_CLIENT_QUIT)("leaving");
|
|
|
NetworkSend_Packets(cs);
|
|
|
}
|
|
|
CloseClient(cs);
|
|
|
NetworkCloseClient(cs);
|
|
|
}
|
|
|
|
|
|
if (_network_server) {
|
|
|
// We are a server, also close the listensocket
|
|
|
closesocket(_listensocket);
|
|
|
_listensocket = INVALID_SOCKET;
|
|
|
DEBUG(net, 1) ("[NET] Closed listener");
|
|
|
NetworkUDPClose();
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// Inits the network (cleans sockets and stuff)
|
|
|
void NetworkInitialize(void)
|
|
|
static void NetworkInitialize(void)
|
|
|
{
|
|
|
ClientState *cs;
|
|
|
NetworkClientState *cs;
|
|
|
uint i;
|
|
|
|
|
|
_local_command_queue = NULL;
|
|
|
|
|
|
// Clean all client-sockets
|
|
|
memset(_clients, 0, sizeof(_clients));
|
|
@@ -732,13 +734,13 @@ void NetworkInitialize(void)
|
|
|
|
|
|
NetworkUDPInitialize();
|
|
|
|
|
|
// add all servers from the config file to our list
|
|
|
for (i=0; i != lengthof(_network_server_list); i++) {
|
|
|
if (_network_server_list[i] == NULL) break;
|
|
|
AddServer(_network_server_list[i]);
|
|
|
NetworkAddServer(_network_server_list[i]);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// Query a server to fetch his game-info
|
|
|
// If game_info is true, only the gameinfo is fetched,
|
|
|
// else only the client_info is fetched
|
|
@@ -772,28 +774,31 @@ void NetworkQueryServer(const byte* host
|
|
|
// No networking, close everything down again
|
|
|
NetworkDisconnect();
|
|
|
}
|
|
|
|
|
|
// validates an address entered as a string and adds the server to
|
|
|
// the list
|
|
|
void AddServer(byte *b)
|
|
|
void NetworkAddServer(const byte *b)
|
|
|
{
|
|
|
if (*b != '\0') {
|
|
|
const byte *port = NULL;
|
|
|
const byte *player = NULL;
|
|
|
byte host[NETWORK_HOSTNAME_LENGTH];
|
|
|
uint16 rport;
|
|
|
|
|
|
ttd_strlcpy(host, b, lengthof(host));
|
|
|
|
|
|
ttd_strlcpy(_network_default_ip, b, lengthof(_network_default_ip));
|
|
|
rport = NETWORK_DEFAULT_PORT;
|
|
|
|
|
|
ParseConnectionString(&player, &port, b);
|
|
|
ParseConnectionString(&player, &port, host);
|
|
|
|
|
|
if (player != NULL) _network_playas = atoi(player);
|
|
|
if (port != NULL) rport = atoi(port);
|
|
|
|
|
|
NetworkQueryServer(b, rport, true);
|
|
|
NetworkQueryServer(host, rport, true);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// Used by clients, to connect to a server
|
|
|
bool NetworkClientConnectGame(const byte* host, unsigned short port)
|
|
|
{
|
|
@@ -820,13 +825,13 @@ bool NetworkClientConnectGame(const byte
|
|
|
NetworkError(STR_NETWORK_ERR_NOCONNECTION);
|
|
|
}
|
|
|
|
|
|
return _networking;
|
|
|
}
|
|
|
|
|
|
void NetworkInitGameInfo(void)
|
|
|
static void NetworkInitGameInfo(void)
|
|
|
{
|
|
|
NetworkClientInfo *ci;
|
|
|
|
|
|
ttd_strlcpy(_network_game_info.server_name, _network_server_name, sizeof(_network_game_info.server_name));
|
|
|
if (_network_game_info.server_name[0] == '\0')
|
|
|
snprintf(_network_game_info.server_name, sizeof(_network_game_info.server_name), "Unnamed Server");
|
|
@@ -910,13 +915,13 @@ bool NetworkServerStart(void)
|
|
|
|
|
|
// The server is rebooting...
|
|
|
// The only difference with NetworkDisconnect, is the packets that is sent
|
|
|
void NetworkReboot(void)
|
|
|
{
|
|
|
if (_network_server) {
|
|
|
ClientState *cs;
|
|
|
NetworkClientState *cs;
|
|
|
FOR_ALL_CLIENTS(cs) {
|
|
|
SEND_COMMAND(PACKET_SERVER_NEWGAME)(cs);
|
|
|
NetworkSend_Packets(cs);
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -934,13 +939,13 @@ void NetworkReboot(void)
|
|
|
}
|
|
|
|
|
|
// We want to disconnect from the host/clients
|
|
|
void NetworkDisconnect(void)
|
|
|
{
|
|
|
if (_network_server) {
|
|
|
ClientState *cs;
|
|
|
NetworkClientState *cs;
|
|
|
FOR_ALL_CLIENTS(cs) {
|
|
|
SEND_COMMAND(PACKET_SERVER_SHUTDOWN)(cs);
|
|
|
NetworkSend_Packets(cs);
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -961,15 +966,15 @@ void NetworkDisconnect(void)
|
|
|
|
|
|
_networking = false;
|
|
|
_network_server = false;
|
|
|
}
|
|
|
|
|
|
// Receives something from the network
|
|
|
bool NetworkReceive(void)
|
|
|
static bool NetworkReceive(void)
|
|
|
{
|
|
|
ClientState *cs;
|
|
|
NetworkClientState *cs;
|
|
|
int n;
|
|
|
fd_set read_fd, write_fd;
|
|
|
struct timeval tv;
|
|
|
|
|
|
FD_ZERO(&read_fd);
|
|
|
FD_ZERO(&write_fd);
|
|
@@ -1019,13 +1024,13 @@ bool NetworkReceive(void)
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
// This sends all buffered commands (if possible)
|
|
|
static void NetworkSend(void)
|
|
|
{
|
|
|
ClientState *cs;
|
|
|
NetworkClientState *cs;
|
|
|
FOR_ALL_CLIENTS(cs) {
|
|
|
if (cs->writable) {
|
|
|
NetworkSend_Packets(cs);
|
|
|
|
|
|
if (cs->status == STATUS_MAP) {
|
|
|
// This client is in the middle of a map-send, call the function for that
|
|
@@ -1033,13 +1038,13 @@ static void NetworkSend(void)
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// Handle the local-command-queue
|
|
|
void NetworkHandleLocalQueue(void)
|
|
|
static void NetworkHandleLocalQueue(void)
|
|
|
{
|
|
|
if (_local_command_queue != NULL) {
|
|
|
CommandPacket *cp;
|
|
|
CommandPacket *cp_prev;
|
|
|
|
|
|
cp = _local_command_queue;
|
|
@@ -1068,16 +1073,13 @@ void NetworkHandleLocalQueue(void)
|
|
|
cp = cp->next;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
extern void StateGameLoop();
|
|
|
|
|
|
bool NetworkDoClientLoop(void)
|
|
|
static bool NetworkDoClientLoop(void)
|
|
|
{
|
|
|
_frame_counter++;
|
|
|
|
|
|
NetworkHandleLocalQueue();
|
|
|
|
|
|
StateGameLoop();
|
|
@@ -1165,13 +1167,13 @@ void NetworkGameLoop(void)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
NetworkSend();
|
|
|
}
|
|
|
|
|
|
void NetworkGenerateUniqueId()
|
|
|
static void NetworkGenerateUniqueId(void)
|
|
|
{
|
|
|
md5_state_t state;
|
|
|
md5_byte_t digest[16];
|
|
|
char hex_output[16*2 + 1];
|
|
|
char coding_string[NETWORK_NAME_LENGTH];
|
|
|
int di;
|