2005-07-24 14:12:37 +00:00
|
|
|
/* $Id$ */
|
|
|
|
|
2008-05-06 15:11:33 +00:00
|
|
|
/** @file network.cpp Base functions for networking support. */
|
|
|
|
|
2007-01-02 17:34:03 +00:00
|
|
|
#include "../stdafx.h"
|
2008-09-30 20:51:04 +00:00
|
|
|
#include "../company_type.h"
|
2004-08-09 17:04:08 +00:00
|
|
|
|
2004-12-04 17:54:56 +00:00
|
|
|
#ifdef ENABLE_NETWORK
|
2004-08-09 17:04:08 +00:00
|
|
|
|
2007-01-02 17:34:03 +00:00
|
|
|
#include "../openttd.h"
|
2007-12-21 19:49:27 +00:00
|
|
|
#include "../strings_func.h"
|
2007-12-21 21:50:46 +00:00
|
|
|
#include "../command_func.h"
|
2007-01-02 17:34:03 +00:00
|
|
|
#include "../variables.h"
|
2007-12-26 13:50:40 +00:00
|
|
|
#include "../date_func.h"
|
2008-05-30 18:20:26 +00:00
|
|
|
#include "network_internal.h"
|
2004-12-04 17:54:56 +00:00
|
|
|
#include "network_client.h"
|
|
|
|
#include "network_server.h"
|
2009-01-17 16:53:32 +00:00
|
|
|
#include "network_content.h"
|
2004-12-04 17:54:56 +00:00
|
|
|
#include "network_udp.h"
|
|
|
|
#include "network_gamelist.h"
|
2007-01-02 17:34:03 +00:00
|
|
|
#include "core/udp.h"
|
2008-08-04 12:56:38 +00:00
|
|
|
#include "core/host.h"
|
2006-12-30 01:52:09 +00:00
|
|
|
#include "network_gui.h"
|
2008-05-24 10:15:06 +00:00
|
|
|
#include "../console_func.h"
|
2007-01-02 17:34:03 +00:00
|
|
|
#include "../md5.h"
|
2007-12-25 11:26:07 +00:00
|
|
|
#include "../core/random_func.hpp"
|
|
|
|
#include "../window_func.h"
|
2008-01-07 14:23:25 +00:00
|
|
|
#include "../string_func.h"
|
2008-09-30 20:51:04 +00:00
|
|
|
#include "../company_func.h"
|
2009-01-31 20:16:06 +00:00
|
|
|
#include "../company_base.h"
|
2008-01-13 14:37:30 +00:00
|
|
|
#include "../settings_type.h"
|
2008-05-07 09:07:19 +00:00
|
|
|
#include "../landscape_type.h"
|
2008-05-04 21:53:36 +00:00
|
|
|
#include "../rev.h"
|
2008-12-22 20:42:02 +00:00
|
|
|
#include "../core/alloc_func.hpp"
|
2008-01-05 21:42:34 +00:00
|
|
|
#ifdef DEBUG_DUMP_COMMANDS
|
2008-10-18 14:16:29 +00:00
|
|
|
#include "../fileio_func.h"
|
2008-01-11 00:30:32 +00:00
|
|
|
#endif /* DEBUG_DUMP_COMMANDS */
|
2008-01-13 01:21:35 +00:00
|
|
|
#include "table/strings.h"
|
2008-12-23 20:52:27 +00:00
|
|
|
#include "../oldpool_func.h"
|
2008-01-13 01:21:35 +00:00
|
|
|
|
2008-12-22 12:59:31 +00:00
|
|
|
DECLARE_POSTFIX_INCREMENT(ClientID);
|
|
|
|
|
2008-12-23 20:52:27 +00:00
|
|
|
typedef ClientIndex NetworkClientInfoID;
|
|
|
|
DEFINE_OLD_POOL_GENERIC(NetworkClientInfo, NetworkClientInfo);
|
|
|
|
|
2008-01-13 21:51:53 +00:00
|
|
|
bool _network_server; ///< network-server is active
|
|
|
|
bool _network_available; ///< is network mode available?
|
|
|
|
bool _network_dedicated; ///< are we a dedicated server?
|
2008-05-30 18:20:26 +00:00
|
|
|
bool _is_network_server; ///< Does this client wants to be a network-server?
|
2008-06-03 08:04:35 +00:00
|
|
|
NetworkServerGameInfo _network_game_info;
|
2008-12-22 20:42:02 +00:00
|
|
|
NetworkCompanyState *_network_company_states = NULL;
|
2008-12-22 12:59:31 +00:00
|
|
|
ClientID _network_own_client_id;
|
|
|
|
ClientID _redirect_console_to_client;
|
2008-05-30 18:20:26 +00:00
|
|
|
bool _network_need_advertise;
|
|
|
|
uint32 _network_last_advertise_frame;
|
|
|
|
uint8 _network_reconnect;
|
|
|
|
char *_network_host_list[10];
|
|
|
|
char *_network_ban_list[25];
|
|
|
|
uint32 _frame_counter_server; // The frame_counter of the server, if in network-mode
|
|
|
|
uint32 _frame_counter_max; // To where we may go with our clients
|
2009-01-08 13:57:50 +00:00
|
|
|
uint32 _frame_counter;
|
2008-05-30 18:20:26 +00:00
|
|
|
uint32 _last_sync_frame; // Used in the server to store the last time a sync packet was sent to clients.
|
|
|
|
uint32 _broadcast_list[MAX_INTERFACES + 1];
|
|
|
|
uint32 _network_server_bind_ip;
|
|
|
|
uint32 _sync_seed_1, _sync_seed_2;
|
|
|
|
uint32 _sync_frame;
|
|
|
|
bool _network_first_time;
|
|
|
|
bool _network_udp_server;
|
|
|
|
uint16 _network_udp_broadcast;
|
|
|
|
uint8 _network_advertise_retries;
|
2009-01-23 22:18:06 +00:00
|
|
|
CompanyMask _network_company_passworded; ///< Bitmask of the password status of all companies.
|
2008-01-13 01:39:22 +00:00
|
|
|
|
2007-01-23 14:47:38 +00:00
|
|
|
/* Check whether NETWORK_NUM_LANDSCAPES is still in sync with NUM_LANDSCAPE */
|
|
|
|
assert_compile((int)NETWORK_NUM_LANDSCAPES == (int)NUM_LANDSCAPE);
|
2008-08-13 06:05:01 +00:00
|
|
|
assert_compile((int)NETWORK_COMPANY_NAME_LENGTH == MAX_LENGTH_COMPANY_NAME_BYTES);
|
2007-01-23 14:47:38 +00:00
|
|
|
|
2007-01-12 14:30:01 +00:00
|
|
|
extern NetworkUDPSocketHandler *_udp_client_socket; ///< udp client socket
|
|
|
|
extern NetworkUDPSocketHandler *_udp_server_socket; ///< udp server socket
|
|
|
|
extern NetworkUDPSocketHandler *_udp_master_socket; ///< udp master socket
|
2007-01-10 18:56:51 +00:00
|
|
|
|
2004-12-04 17:54:56 +00:00
|
|
|
// The listen socket for the server
|
|
|
|
static SOCKET _listensocket;
|
2004-08-27 11:01:26 +00:00
|
|
|
|
2004-12-04 17:54:56 +00:00
|
|
|
// The amount of clients connected
|
|
|
|
static byte _network_clients_connected = 0;
|
2008-12-22 12:59:31 +00:00
|
|
|
// The identifier counter for new clients (is never decreased)
|
|
|
|
static ClientID _network_client_id = CLIENT_ID_FIRST;
|
2004-08-27 11:01:26 +00:00
|
|
|
|
2004-12-19 10:17:26 +00:00
|
|
|
/* Some externs / forwards */
|
2007-03-07 11:47:46 +00:00
|
|
|
extern void StateGameLoop();
|
2004-12-19 10:17:26 +00:00
|
|
|
|
2008-12-22 21:38:03 +00:00
|
|
|
/**
|
|
|
|
* Return the CI given it's raw index
|
|
|
|
* @param index the index to search for
|
|
|
|
* @return return a pointer to the corresponding NetworkClientInfo struct
|
|
|
|
*/
|
|
|
|
NetworkClientInfo *NetworkFindClientInfoFromIndex(ClientIndex index)
|
|
|
|
{
|
2008-12-23 11:06:52 +00:00
|
|
|
return IsValidNetworkClientInfoIndex(index) ? GetNetworkClientInfo(index) : NULL;
|
2008-12-22 21:38:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Return the CI given it's client-identifier
|
|
|
|
* @param client_id the ClientID to search for
|
|
|
|
* @return return a pointer to the corresponding NetworkClientInfo struct or NULL when not found
|
|
|
|
*/
|
2008-12-22 21:30:21 +00:00
|
|
|
NetworkClientInfo *NetworkFindClientInfoFromClientID(ClientID client_id)
|
2004-09-10 19:02:27 +00:00
|
|
|
{
|
2004-12-04 17:54:56 +00:00
|
|
|
NetworkClientInfo *ci;
|
2004-08-27 11:01:26 +00:00
|
|
|
|
2008-12-23 09:02:41 +00:00
|
|
|
FOR_ALL_CLIENT_INFOS(ci) {
|
2008-12-22 12:59:31 +00:00
|
|
|
if (ci->client_id == client_id) return ci;
|
2006-06-27 21:25:53 +00:00
|
|
|
}
|
2004-08-27 11:01:26 +00:00
|
|
|
|
2004-12-04 17:54:56 +00:00
|
|
|
return NULL;
|
2004-08-22 10:23:37 +00:00
|
|
|
}
|
|
|
|
|
2008-12-22 21:38:03 +00:00
|
|
|
/**
|
|
|
|
* Return the CI for a given IP
|
2006-01-19 15:58:57 +00:00
|
|
|
* @param ip IP of the client we are looking for. This must be in string-format
|
2008-12-22 21:38:03 +00:00
|
|
|
* @return return a pointer to the corresponding NetworkClientInfo struct or NULL when not found
|
|
|
|
*/
|
2006-01-19 15:58:57 +00:00
|
|
|
NetworkClientInfo *NetworkFindClientInfoFromIP(const char *ip)
|
|
|
|
{
|
|
|
|
NetworkClientInfo *ci;
|
|
|
|
uint32 ip_number = inet_addr(ip);
|
|
|
|
|
2008-12-23 09:02:41 +00:00
|
|
|
FOR_ALL_CLIENT_INFOS(ci) {
|
2006-01-19 15:58:57 +00:00
|
|
|
if (ci->client_ip == ip_number) return ci;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2008-12-22 21:38:03 +00:00
|
|
|
/**
|
|
|
|
* Return the client state given it's client-identifier
|
|
|
|
* @param client_id the ClientID to search for
|
2008-12-23 09:47:42 +00:00
|
|
|
* @return return a pointer to the corresponding NetworkClientSocket struct or NULL when not found
|
2008-12-22 21:38:03 +00:00
|
|
|
*/
|
2008-12-23 09:47:42 +00:00
|
|
|
NetworkClientSocket *NetworkFindClientStateFromClientID(ClientID client_id)
|
2004-12-04 17:54:56 +00:00
|
|
|
{
|
2008-12-23 09:47:42 +00:00
|
|
|
NetworkClientSocket *cs;
|
2004-08-09 17:04:08 +00:00
|
|
|
|
2008-12-23 09:02:41 +00:00
|
|
|
FOR_ALL_CLIENT_SOCKETS(cs) {
|
2008-12-22 12:59:31 +00:00
|
|
|
if (cs->client_id == client_id) return cs;
|
2006-06-27 21:25:53 +00:00
|
|
|
}
|
2004-09-06 22:46:02 +00:00
|
|
|
|
2004-12-04 17:54:56 +00:00
|
|
|
return NULL;
|
2004-09-06 22:46:02 +00:00
|
|
|
}
|
|
|
|
|
2004-12-04 17:54:56 +00:00
|
|
|
// 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.
|
2008-12-23 09:47:42 +00:00
|
|
|
void NetworkGetClientName(char *client_name, size_t size, const NetworkClientSocket *cs)
|
2004-09-08 19:20:46 +00:00
|
|
|
{
|
2008-12-23 08:39:30 +00:00
|
|
|
const NetworkClientInfo *ci = cs->GetInfo();
|
2006-06-27 21:25:53 +00:00
|
|
|
|
2008-12-23 08:39:30 +00:00
|
|
|
if (StrEmpty(ci->client_name)) {
|
2008-12-22 12:59:31 +00:00
|
|
|
snprintf(client_name, size, "Client #%4d", cs->client_id);
|
2006-06-27 21:25:53 +00:00
|
|
|
} else {
|
2006-04-22 09:46:31 +00:00
|
|
|
ttd_strlcpy(client_name, ci->client_name, size);
|
2006-06-27 21:25:53 +00:00
|
|
|
}
|
2004-09-06 22:46:02 +00:00
|
|
|
}
|
2004-09-12 14:12:33 +00:00
|
|
|
|
2007-03-07 11:47:46 +00:00
|
|
|
byte NetworkSpectatorCount()
|
2006-01-31 22:16:15 +00:00
|
|
|
{
|
2008-12-23 08:39:30 +00:00
|
|
|
const NetworkClientInfo *ci;
|
2006-01-31 22:16:15 +00:00
|
|
|
byte count = 0;
|
|
|
|
|
2008-12-23 08:39:30 +00:00
|
|
|
FOR_ALL_CLIENT_INFOS(ci) {
|
|
|
|
if (ci->client_playas == COMPANY_SPECTATOR) count++;
|
2006-01-31 22:16:15 +00:00
|
|
|
}
|
|
|
|
|
2009-01-21 10:46:21 +00:00
|
|
|
/* Don't count a dedicated server as spectator */
|
|
|
|
if (_network_dedicated) count--;
|
|
|
|
|
2006-01-31 22:16:15 +00:00
|
|
|
return count;
|
|
|
|
}
|
|
|
|
|
2009-01-23 22:18:06 +00:00
|
|
|
/**
|
|
|
|
* Check if the company we want to join requires a password.
|
|
|
|
* @param company_id id of the company we want to check the 'passworded' flag for.
|
|
|
|
* @return true if the company requires a password.
|
|
|
|
*/
|
|
|
|
bool NetworkCompanyIsPassworded(CompanyID company_id)
|
|
|
|
{
|
|
|
|
return HasBit(_network_company_passworded, company_id);
|
|
|
|
}
|
|
|
|
|
2004-12-04 17:54:56 +00:00
|
|
|
// This puts a text-message to the console, or in the future, the chat-box,
|
|
|
|
// (to keep it all a bit more general)
|
2004-12-19 15:14:55 +00:00
|
|
|
// If 'self_send' is true, this is the client who is sending the message
|
2009-02-09 02:09:47 +00:00
|
|
|
void NetworkTextMessage(NetworkAction action, ConsoleColour colour, bool self_send, const char *name, const char *str, int64 data)
|
2004-09-08 19:20:46 +00:00
|
|
|
{
|
2004-12-04 17:54:56 +00:00
|
|
|
const int duration = 10; // Game days the messages stay visible
|
2004-08-22 10:23:37 +00:00
|
|
|
|
2008-12-29 10:37:53 +00:00
|
|
|
StringID strid;
|
2004-12-04 17:54:56 +00:00
|
|
|
switch (action) {
|
2006-10-24 22:23:08 +00:00
|
|
|
case NETWORK_ACTION_SERVER_MESSAGE:
|
2008-12-29 10:37:53 +00:00
|
|
|
/* Ignore invalid messages */
|
|
|
|
if (data >= NETWORK_SERVER_MESSAGE_END) return;
|
|
|
|
|
|
|
|
strid = STR_NETWORK_SERVER_MESSAGE;
|
2009-02-09 02:09:47 +00:00
|
|
|
colour = CC_DEFAULT;
|
2008-12-29 10:37:53 +00:00
|
|
|
data = STR_NETWORK_SERVER_MESSAGE_GAME_PAUSED_PLAYERS + data;
|
2004-12-04 17:54:56 +00:00
|
|
|
break;
|
2009-01-23 22:18:06 +00:00
|
|
|
case NETWORK_ACTION_COMPANY_SPECTATOR:
|
2009-02-09 02:09:47 +00:00
|
|
|
colour = CC_DEFAULT;
|
2009-01-23 22:18:06 +00:00
|
|
|
strid = STR_NETWORK_CLIENT_COMPANY_SPECTATE;
|
|
|
|
break;
|
|
|
|
case NETWORK_ACTION_COMPANY_JOIN:
|
2009-02-09 02:09:47 +00:00
|
|
|
colour = CC_DEFAULT;
|
2009-01-23 22:18:06 +00:00
|
|
|
strid = STR_NETWORK_CLIENT_COMPANY_JOIN;
|
|
|
|
break;
|
|
|
|
case NETWORK_ACTION_COMPANY_NEW:
|
2009-02-09 02:09:47 +00:00
|
|
|
colour = CC_DEFAULT;
|
2009-01-23 22:18:06 +00:00
|
|
|
strid = STR_NETWORK_CLIENT_COMPANY_NEW;
|
|
|
|
break;
|
2008-12-29 10:37:53 +00:00
|
|
|
case NETWORK_ACTION_JOIN: strid = STR_NETWORK_CLIENT_JOINED; break;
|
|
|
|
case NETWORK_ACTION_LEAVE: strid = STR_NETWORK_CLIENT_LEFT; break;
|
|
|
|
case NETWORK_ACTION_NAME_CHANGE: strid = STR_NETWORK_NAME_CHANGE; break;
|
|
|
|
case NETWORK_ACTION_GIVE_MONEY: strid = self_send ? STR_NETWORK_GAVE_MONEY_AWAY : STR_NETWORK_GIVE_MONEY; break;
|
|
|
|
case NETWORK_ACTION_CHAT_COMPANY: strid = self_send ? STR_NETWORK_CHAT_TO_COMPANY : STR_NETWORK_CHAT_COMPANY; break;
|
|
|
|
case NETWORK_ACTION_CHAT_CLIENT: strid = self_send ? STR_NETWORK_CHAT_TO_CLIENT : STR_NETWORK_CHAT_CLIENT; break;
|
2009-01-09 22:48:57 +00:00
|
|
|
default: strid = STR_NETWORK_CHAT_ALL; break;
|
2004-12-04 17:54:56 +00:00
|
|
|
}
|
2004-12-19 15:14:55 +00:00
|
|
|
|
2008-12-29 10:37:53 +00:00
|
|
|
char message[1024];
|
|
|
|
SetDParamStr(0, name);
|
|
|
|
SetDParamStr(1, str);
|
|
|
|
SetDParam(2, data);
|
|
|
|
GetString(message, strid, lastof(message));
|
|
|
|
|
2008-12-29 21:50:25 +00:00
|
|
|
DEBUG(desync, 1, "msg: %d; %d; %s\n", _date, _date_fract, message);
|
2009-02-09 02:09:47 +00:00
|
|
|
IConsolePrintF(colour, "%s", message);
|
|
|
|
NetworkAddChatMessage((TextColour)colour, duration, "%s", message);
|
2004-09-11 19:34:11 +00:00
|
|
|
}
|
|
|
|
|
2004-12-04 17:54:56 +00:00
|
|
|
// Calculate the frame-lag of a client
|
2008-12-23 09:47:42 +00:00
|
|
|
uint NetworkCalculateLag(const NetworkClientSocket *cs)
|
2004-09-11 22:10:31 +00:00
|
|
|
{
|
2004-12-04 17:54:56 +00:00
|
|
|
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
|
2008-05-29 20:21:28 +00:00
|
|
|
if (cs->last_frame_server + DAY_TICKS + _settings_client.network.frame_freq < _frame_counter)
|
|
|
|
lag += _frame_counter - (cs->last_frame_server + DAY_TICKS + _settings_client.network.frame_freq);
|
2004-09-11 22:10:31 +00:00
|
|
|
|
2004-12-04 17:54:56 +00:00
|
|
|
return lag;
|
2004-09-11 22:10:31 +00:00
|
|
|
}
|
|
|
|
|
2004-12-04 17:54:56 +00:00
|
|
|
|
|
|
|
// There was a non-recoverable error, drop back to the main menu with a nice
|
|
|
|
// error
|
2005-01-22 22:47:58 +00:00
|
|
|
static void NetworkError(StringID error_string)
|
2004-08-09 17:04:08 +00:00
|
|
|
{
|
2004-12-04 17:54:56 +00:00
|
|
|
_switch_mode = SM_MENU;
|
2008-04-18 10:16:51 +00:00
|
|
|
extern StringID _switch_mode_errorstr;
|
2004-12-04 17:54:56 +00:00
|
|
|
_switch_mode_errorstr = error_string;
|
|
|
|
}
|
2004-08-09 17:04:08 +00:00
|
|
|
|
2005-01-14 19:44:42 +00:00
|
|
|
static void ServerStartError(const char *error)
|
|
|
|
{
|
2006-12-26 17:36:18 +00:00
|
|
|
DEBUG(net, 0, "[server] could not start network: %s",error);
|
2004-12-04 17:54:56 +00:00
|
|
|
NetworkError(STR_NETWORK_ERR_SERVER_START);
|
|
|
|
}
|
2004-08-09 17:04:08 +00:00
|
|
|
|
2009-01-10 00:31:47 +00:00
|
|
|
static void NetworkClientError(NetworkRecvStatus res, NetworkClientSocket *cs)
|
2006-06-27 21:25:53 +00:00
|
|
|
{
|
2004-12-04 17:54:56 +00:00
|
|
|
// First, send a CLIENT_ERROR to the server, so he knows we are
|
|
|
|
// disconnection (and why!)
|
|
|
|
NetworkErrorCode errorno;
|
2004-08-09 17:04:08 +00:00
|
|
|
|
2004-12-04 17:54:56 +00:00
|
|
|
// We just want to close the connection..
|
|
|
|
if (res == NETWORK_RECV_STATUS_CLOSE_QUERY) {
|
2006-10-17 23:34:12 +00:00
|
|
|
cs->has_quit = true;
|
2004-12-19 10:17:26 +00:00
|
|
|
NetworkCloseClient(cs);
|
2004-12-04 17:54:56 +00:00
|
|
|
_networking = false;
|
2004-09-08 19:20:46 +00:00
|
|
|
|
2004-12-04 17:54:56 +00:00
|
|
|
DeleteWindowById(WC_NETWORK_STATUS_WINDOW, 0);
|
|
|
|
return;
|
2004-08-09 17:04:08 +00:00
|
|
|
}
|
|
|
|
|
2006-02-01 07:36:15 +00:00
|
|
|
switch (res) {
|
2007-01-30 17:22:56 +00:00
|
|
|
case NETWORK_RECV_STATUS_DESYNC: errorno = NETWORK_ERROR_DESYNC; break;
|
|
|
|
case NETWORK_RECV_STATUS_SAVEGAME: errorno = NETWORK_ERROR_SAVEGAME_FAILED; break;
|
|
|
|
case NETWORK_RECV_STATUS_NEWGRF_MISMATCH: errorno = NETWORK_ERROR_NEWGRF_MISMATCH; break;
|
|
|
|
default: errorno = NETWORK_ERROR_GENERAL; break;
|
2004-08-09 17:04:08 +00:00
|
|
|
}
|
2004-12-04 17:54:56 +00:00
|
|
|
// This means we fucked up and the server closed the connection
|
2005-01-02 12:03:43 +00:00
|
|
|
if (res != NETWORK_RECV_STATUS_SERVER_ERROR && res != NETWORK_RECV_STATUS_SERVER_FULL &&
|
|
|
|
res != NETWORK_RECV_STATUS_SERVER_BANNED) {
|
2004-12-04 17:54:56 +00:00
|
|
|
SEND_COMMAND(PACKET_CLIENT_ERROR)(errorno);
|
|
|
|
}
|
2004-08-09 17:04:08 +00:00
|
|
|
|
2004-12-04 17:54:56 +00:00
|
|
|
_switch_mode = SM_MENU;
|
2004-12-19 10:17:26 +00:00
|
|
|
NetworkCloseClient(cs);
|
2004-12-04 17:54:56 +00:00
|
|
|
_networking = false;
|
2004-08-09 17:04:08 +00:00
|
|
|
}
|
|
|
|
|
2008-12-29 10:37:53 +00:00
|
|
|
/**
|
|
|
|
* Retrieve the string id of an internal error number
|
2006-06-27 21:25:53 +00:00
|
|
|
* @param err NetworkErrorCode
|
2008-12-29 10:37:53 +00:00
|
|
|
* @return the StringID
|
|
|
|
*/
|
|
|
|
StringID GetNetworkErrorMsg(NetworkErrorCode err)
|
2006-04-03 18:31:01 +00:00
|
|
|
{
|
|
|
|
/* List of possible network errors, used by
|
|
|
|
* PACKET_SERVER_ERROR and PACKET_CLIENT_ERROR */
|
|
|
|
static const StringID network_error_strings[] = {
|
|
|
|
STR_NETWORK_ERR_CLIENT_GENERAL,
|
|
|
|
STR_NETWORK_ERR_CLIENT_DESYNC,
|
|
|
|
STR_NETWORK_ERR_CLIENT_SAVEGAME,
|
|
|
|
STR_NETWORK_ERR_CLIENT_CONNECTION_LOST,
|
|
|
|
STR_NETWORK_ERR_CLIENT_PROTOCOL_ERROR,
|
2007-01-30 17:22:56 +00:00
|
|
|
STR_NETWORK_ERR_CLIENT_NEWGRF_MISMATCH,
|
2006-04-03 18:31:01 +00:00
|
|
|
STR_NETWORK_ERR_CLIENT_NOT_AUTHORIZED,
|
|
|
|
STR_NETWORK_ERR_CLIENT_NOT_EXPECTED,
|
|
|
|
STR_NETWORK_ERR_CLIENT_WRONG_REVISION,
|
|
|
|
STR_NETWORK_ERR_CLIENT_NAME_IN_USE,
|
|
|
|
STR_NETWORK_ERR_CLIENT_WRONG_PASSWORD,
|
2008-09-30 21:18:28 +00:00
|
|
|
STR_NETWORK_ERR_CLIENT_COMPANY_MISMATCH,
|
2006-04-03 18:31:01 +00:00
|
|
|
STR_NETWORK_ERR_CLIENT_KICKED,
|
|
|
|
STR_NETWORK_ERR_CLIENT_CHEATER,
|
2006-06-27 21:25:53 +00:00
|
|
|
STR_NETWORK_ERR_CLIENT_SERVER_FULL
|
2006-04-03 18:31:01 +00:00
|
|
|
};
|
|
|
|
|
2007-01-10 18:56:51 +00:00
|
|
|
if (err >= (ptrdiff_t)lengthof(network_error_strings)) err = NETWORK_ERROR_GENERAL;
|
2006-04-03 18:31:01 +00:00
|
|
|
|
2008-12-29 21:41:20 +00:00
|
|
|
return network_error_strings[err];
|
2006-04-03 18:31:01 +00:00
|
|
|
}
|
|
|
|
|
2006-10-03 16:15:34 +00:00
|
|
|
/* Count the number of active clients connected */
|
2008-09-30 20:39:50 +00:00
|
|
|
static uint NetworkCountActiveClients()
|
2006-10-03 16:15:34 +00:00
|
|
|
{
|
2008-12-23 08:39:30 +00:00
|
|
|
const NetworkClientInfo *ci;
|
2006-10-03 16:15:34 +00:00
|
|
|
uint count = 0;
|
|
|
|
|
2008-12-23 08:39:30 +00:00
|
|
|
FOR_ALL_CLIENT_INFOS(ci) {
|
2008-09-30 20:39:50 +00:00
|
|
|
if (IsValidCompanyID(ci->client_playas)) count++;
|
2006-10-03 16:15:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return count;
|
|
|
|
}
|
|
|
|
|
2008-09-30 20:39:50 +00:00
|
|
|
static bool _min_active_clients_paused = false;
|
2006-10-03 16:15:34 +00:00
|
|
|
|
2008-09-30 20:39:50 +00:00
|
|
|
/* Check if the minimum number of active clients has been reached and pause or unpause the game as appropriate */
|
|
|
|
void CheckMinActiveClients()
|
2006-10-03 16:15:34 +00:00
|
|
|
{
|
|
|
|
if (!_network_dedicated) return;
|
|
|
|
|
2008-09-30 20:39:50 +00:00
|
|
|
if (NetworkCountActiveClients() < _settings_client.network.min_active_clients) {
|
|
|
|
if (_min_active_clients_paused) return;
|
2006-10-03 16:15:34 +00:00
|
|
|
|
2008-09-30 20:39:50 +00:00
|
|
|
_min_active_clients_paused = true;
|
2008-12-28 14:37:19 +00:00
|
|
|
DoCommandP(0, 1, 0, CMD_PAUSE);
|
2008-12-29 10:37:53 +00:00
|
|
|
NetworkServerSendChat(NETWORK_ACTION_SERVER_MESSAGE, DESTTYPE_BROADCAST, 0, "", CLIENT_ID_SERVER, NETWORK_SERVER_MESSAGE_GAME_PAUSED_PLAYERS);
|
2006-10-03 16:15:34 +00:00
|
|
|
} else {
|
2008-09-30 20:39:50 +00:00
|
|
|
if (!_min_active_clients_paused) return;
|
2006-10-03 16:15:34 +00:00
|
|
|
|
2008-09-30 20:39:50 +00:00
|
|
|
_min_active_clients_paused = false;
|
2008-12-28 14:37:19 +00:00
|
|
|
DoCommandP(0, 0, 0, CMD_PAUSE);
|
2008-12-29 10:37:53 +00:00
|
|
|
NetworkServerSendChat(NETWORK_ACTION_SERVER_MESSAGE, DESTTYPE_BROADCAST, 0, "", CLIENT_ID_SERVER, NETWORK_SERVER_MESSAGE_GAME_UNPAUSED_PLAYERS);
|
2006-10-03 16:15:34 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-09-30 20:39:50 +00:00
|
|
|
/** Converts a string to ip/port/company
|
|
|
|
* Format: IP#company:port
|
2007-01-16 23:01:06 +00:00
|
|
|
*
|
2008-09-30 20:39:50 +00:00
|
|
|
* connection_string will be re-terminated to seperate out the hostname, and company and port will
|
|
|
|
* be set to the company and port strings given by the user, inside the memory area originally
|
2007-01-16 23:01:06 +00:00
|
|
|
* occupied by connection_string. */
|
2008-09-30 20:39:50 +00:00
|
|
|
void ParseConnectionString(const char **company, const char **port, char *connection_string)
|
2004-09-12 14:12:33 +00:00
|
|
|
{
|
2005-02-06 22:25:27 +00:00
|
|
|
char *p;
|
2004-12-04 17:54:56 +00:00
|
|
|
for (p = connection_string; *p != '\0'; p++) {
|
2007-01-16 23:01:06 +00:00
|
|
|
switch (*p) {
|
|
|
|
case '#':
|
2008-09-30 20:39:50 +00:00
|
|
|
*company = p + 1;
|
2007-01-16 23:01:06 +00:00
|
|
|
*p = '\0';
|
|
|
|
break;
|
|
|
|
case ':':
|
|
|
|
*port = p + 1;
|
|
|
|
*p = '\0';
|
|
|
|
break;
|
2004-09-11 19:34:11 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2004-12-04 17:54:56 +00:00
|
|
|
// Creates a new client from a socket
|
|
|
|
// Used both by the server and the client
|
2008-12-23 09:47:42 +00:00
|
|
|
static NetworkClientSocket *NetworkAllocClient(SOCKET s)
|
2004-09-11 19:34:11 +00:00
|
|
|
{
|
2004-12-04 17:54:56 +00:00
|
|
|
if (_network_server) {
|
|
|
|
// Can we handle a new client?
|
2006-10-17 23:34:12 +00:00
|
|
|
if (_network_clients_connected >= MAX_CLIENTS) return NULL;
|
2008-06-03 08:04:35 +00:00
|
|
|
if (_network_game_info.clients_on >= _settings_client.network.max_clients) return NULL;
|
2004-08-09 17:04:08 +00:00
|
|
|
|
2004-12-04 17:54:56 +00:00
|
|
|
// Register the login
|
2008-12-23 20:52:27 +00:00
|
|
|
_network_clients_connected++;
|
2004-12-04 17:54:56 +00:00
|
|
|
}
|
2004-08-09 17:04:08 +00:00
|
|
|
|
2008-12-23 20:52:27 +00:00
|
|
|
NetworkClientSocket *cs = new NetworkClientSocket(INVALID_CLIENT_ID);
|
2007-01-12 20:19:49 +00:00
|
|
|
cs->sock = s;
|
2005-03-29 19:10:13 +00:00
|
|
|
cs->last_frame = _frame_counter;
|
|
|
|
cs->last_frame_server = _frame_counter;
|
|
|
|
|
2004-12-04 17:54:56 +00:00
|
|
|
if (_network_server) {
|
2008-12-22 12:59:31 +00:00
|
|
|
cs->client_id = _network_client_id++;
|
2008-12-23 20:52:27 +00:00
|
|
|
NetworkClientInfo *ci = new NetworkClientInfo(cs->client_id);
|
|
|
|
cs->SetInfo(ci);
|
2008-09-30 20:39:50 +00:00
|
|
|
ci->client_playas = COMPANY_INACTIVE_CLIENT;
|
2004-12-04 17:54:56 +00:00
|
|
|
ci->join_date = _date;
|
2004-08-09 17:04:08 +00:00
|
|
|
|
2004-12-04 17:54:56 +00:00
|
|
|
InvalidateWindow(WC_CLIENT_LIST, 0);
|
|
|
|
}
|
2004-08-22 10:23:37 +00:00
|
|
|
|
2004-12-04 17:54:56 +00:00
|
|
|
return cs;
|
2004-08-09 17:04:08 +00:00
|
|
|
}
|
|
|
|
|
2004-12-04 17:54:56 +00:00
|
|
|
// Close a connection
|
2008-12-23 09:47:42 +00:00
|
|
|
void NetworkCloseClient(NetworkClientSocket *cs)
|
2004-08-09 17:04:08 +00:00
|
|
|
{
|
2008-12-23 20:52:27 +00:00
|
|
|
assert(cs->sock != INVALID_SOCKET);
|
2004-08-09 17:04:08 +00:00
|
|
|
|
2008-12-22 12:59:31 +00:00
|
|
|
DEBUG(net, 1, "Closed client connection %d", cs->client_id);
|
2004-08-09 17:04:08 +00:00
|
|
|
|
2006-10-17 23:34:12 +00:00
|
|
|
if (!cs->has_quit && _network_server && cs->status > STATUS_INACTIVE) {
|
2004-12-04 17:54:56 +00:00
|
|
|
// We did not receive a leave message from this client...
|
2006-04-22 09:46:31 +00:00
|
|
|
char client_name[NETWORK_CLIENT_NAME_LENGTH];
|
2008-12-23 09:47:42 +00:00
|
|
|
NetworkClientSocket *new_cs;
|
2004-08-09 17:04:08 +00:00
|
|
|
|
2004-12-04 17:54:56 +00:00
|
|
|
NetworkGetClientName(client_name, sizeof(client_name), cs);
|
2004-08-09 17:04:08 +00:00
|
|
|
|
2008-12-29 10:37:53 +00:00
|
|
|
NetworkTextMessage(NETWORK_ACTION_LEAVE, CC_DEFAULT, false, client_name, NULL, STR_NETWORK_ERR_CLIENT_CONNECTION_LOST);
|
2004-08-09 17:04:08 +00:00
|
|
|
|
2004-12-04 17:54:56 +00:00
|
|
|
// Inform other clients of this... strange leaving ;)
|
2008-12-23 09:02:41 +00:00
|
|
|
FOR_ALL_CLIENT_SOCKETS(new_cs) {
|
2004-12-04 17:54:56 +00:00
|
|
|
if (new_cs->status > STATUS_AUTH && cs != new_cs) {
|
2008-12-29 10:37:53 +00:00
|
|
|
SEND_COMMAND(PACKET_SERVER_ERROR_QUIT)(new_cs, cs->client_id, NETWORK_ERROR_CONNECTION_LOST);
|
2004-08-09 17:04:08 +00:00
|
|
|
}
|
|
|
|
}
|
2005-03-30 08:50:34 +00:00
|
|
|
}
|
2005-03-29 19:10:13 +00:00
|
|
|
|
2005-03-30 08:50:34 +00:00
|
|
|
/* When the client was PRE_ACTIVE, the server was in pause mode, so unpause */
|
2008-05-29 20:21:28 +00:00
|
|
|
if (cs->status == STATUS_PRE_ACTIVE && _settings_client.network.pause_on_join) {
|
2008-12-28 14:37:19 +00:00
|
|
|
DoCommandP(0, 0, 0, CMD_PAUSE);
|
2008-12-29 10:37:53 +00:00
|
|
|
NetworkServerSendChat(NETWORK_ACTION_SERVER_MESSAGE, DESTTYPE_BROADCAST, 0, "", CLIENT_ID_SERVER, NETWORK_SERVER_MESSAGE_GAME_UNPAUSED_CONNECT_FAIL);
|
2004-08-09 17:04:08 +00:00
|
|
|
}
|
|
|
|
|
2004-12-04 17:54:56 +00:00
|
|
|
if (_network_server) {
|
|
|
|
// We just lost one client :(
|
2007-03-20 20:11:17 +00:00
|
|
|
if (cs->status >= STATUS_AUTH) _network_game_info.clients_on--;
|
2004-12-04 17:54:56 +00:00
|
|
|
_network_clients_connected--;
|
2004-08-09 17:04:08 +00:00
|
|
|
|
2004-12-04 17:54:56 +00:00
|
|
|
InvalidateWindow(WC_CLIENT_LIST, 0);
|
2004-09-11 19:34:11 +00:00
|
|
|
}
|
|
|
|
|
2008-12-23 20:52:27 +00:00
|
|
|
delete cs->GetInfo();
|
|
|
|
delete cs;
|
2006-10-03 16:15:34 +00:00
|
|
|
|
2008-09-30 20:39:50 +00:00
|
|
|
CheckMinActiveClients();
|
2004-08-09 17:04:08 +00:00
|
|
|
}
|
|
|
|
|
2004-12-04 17:54:56 +00:00
|
|
|
// For the server, to accept new clients
|
2007-03-07 11:47:46 +00:00
|
|
|
static void NetworkAcceptClients()
|
2004-08-09 17:04:08 +00:00
|
|
|
{
|
|
|
|
struct sockaddr_in sin;
|
2008-12-23 09:47:42 +00:00
|
|
|
NetworkClientSocket *cs;
|
2005-01-09 21:25:44 +00:00
|
|
|
uint i;
|
2005-01-02 12:03:43 +00:00
|
|
|
bool banned;
|
2004-08-09 17:04:08 +00:00
|
|
|
|
2004-12-04 17:54:56 +00:00
|
|
|
// Should never ever happen.. is it possible??
|
2004-08-09 17:04:08 +00:00
|
|
|
assert(_listensocket != INVALID_SOCKET);
|
|
|
|
|
2004-12-04 17:54:56 +00:00
|
|
|
for (;;) {
|
2006-10-17 23:34:12 +00:00
|
|
|
socklen_t sin_len = sizeof(sin);
|
|
|
|
SOCKET s = accept(_listensocket, (struct sockaddr*)&sin, &sin_len);
|
2004-08-09 17:04:08 +00:00
|
|
|
if (s == INVALID_SOCKET) return;
|
|
|
|
|
2005-02-07 09:56:16 +00:00
|
|
|
SetNonBlocking(s); // XXX error handling?
|
2004-08-09 17:04:08 +00:00
|
|
|
|
2006-12-26 17:36:18 +00:00
|
|
|
DEBUG(net, 1, "Client connected from %s on frame %d", inet_ntoa(sin.sin_addr), _frame_counter);
|
2004-08-09 17:04:08 +00:00
|
|
|
|
2005-02-07 09:56:16 +00:00
|
|
|
SetNoDelay(s); // XXX error handling?
|
2004-08-09 17:04:08 +00:00
|
|
|
|
2005-01-02 12:03:43 +00:00
|
|
|
/* Check if the client is banned */
|
|
|
|
banned = false;
|
|
|
|
for (i = 0; i < lengthof(_network_ban_list); i++) {
|
2006-10-17 23:34:12 +00:00
|
|
|
if (_network_ban_list[i] == NULL) continue;
|
2005-01-02 12:03:43 +00:00
|
|
|
|
2009-01-15 18:11:26 +00:00
|
|
|
/* Check for CIDR separator */
|
|
|
|
char *chr_cidr = strchr(_network_ban_list[i], '/');
|
|
|
|
if (chr_cidr != NULL) {
|
|
|
|
int cidr = atoi(chr_cidr + 1);
|
|
|
|
|
|
|
|
/* Invalid CIDR, treat as single host */
|
|
|
|
if (cidr <= 0 || cidr > 32) cidr = 32;
|
|
|
|
|
|
|
|
/* Remove and then replace the / so that inet_addr() works on the IP portion */
|
|
|
|
*chr_cidr = '\0';
|
|
|
|
uint32 ban_ip = inet_addr(_network_ban_list[i]);
|
|
|
|
*chr_cidr = '/';
|
|
|
|
|
|
|
|
/* Convert CIDR to mask in network format */
|
|
|
|
uint32 mask = htonl(-(1 << (32 - cidr)));
|
|
|
|
if ((sin.sin_addr.s_addr & mask) == (ban_ip & mask)) banned = true;
|
|
|
|
} else {
|
|
|
|
/* No CIDR used, so just perform a simple IP test */
|
|
|
|
if (sin.sin_addr.s_addr == inet_addr(_network_ban_list[i])) banned = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (banned) {
|
2007-02-01 22:30:35 +00:00
|
|
|
Packet p(PACKET_SERVER_BANNED);
|
|
|
|
p.PrepareToSend();
|
2005-01-02 12:03:43 +00:00
|
|
|
|
2006-12-26 17:36:18 +00:00
|
|
|
DEBUG(net, 1, "Banned ip tried to join (%s), refused", _network_ban_list[i]);
|
2005-01-02 12:03:43 +00:00
|
|
|
|
2007-02-01 22:30:35 +00:00
|
|
|
send(s, (const char*)p.buffer, p.size, 0);
|
2005-01-02 12:03:43 +00:00
|
|
|
closesocket(s);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/* If this client is banned, continue with next client */
|
2006-10-17 23:34:12 +00:00
|
|
|
if (banned) continue;
|
2005-01-02 12:03:43 +00:00
|
|
|
|
2004-12-19 10:17:26 +00:00
|
|
|
cs = NetworkAllocClient(s);
|
2004-08-09 17:04:08 +00:00
|
|
|
if (cs == NULL) {
|
|
|
|
// no more clients allowed?
|
2004-12-04 17:54:56 +00:00
|
|
|
// Send to the client that we are full!
|
2007-02-01 22:30:35 +00:00
|
|
|
Packet p(PACKET_SERVER_FULL);
|
|
|
|
p.PrepareToSend();
|
2004-12-04 17:54:56 +00:00
|
|
|
|
2007-02-01 22:30:35 +00:00
|
|
|
send(s, (const char*)p.buffer, p.size, 0);
|
2004-08-09 17:04:08 +00:00
|
|
|
closesocket(s);
|
2004-12-04 17:54:56 +00:00
|
|
|
|
2004-08-09 17:04:08 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2004-12-04 17:54:56 +00:00
|
|
|
// a new client has connected. We set him at inactive for now
|
|
|
|
// maybe he is only requesting server-info. Till he has sent a PACKET_CLIENT_MAP_OK
|
|
|
|
// the client stays inactive
|
|
|
|
cs->status = STATUS_INACTIVE;
|
|
|
|
|
2008-12-23 08:39:30 +00:00
|
|
|
cs->GetInfo()->client_ip = sin.sin_addr.s_addr; // Save the IP of the client
|
2004-08-09 17:04:08 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2004-12-04 17:54:56 +00:00
|
|
|
// Set up the listen socket for the server
|
2007-03-07 11:47:46 +00:00
|
|
|
static bool NetworkListen()
|
2004-08-09 17:04:08 +00:00
|
|
|
{
|
2004-12-04 17:54:56 +00:00
|
|
|
SOCKET ls;
|
|
|
|
struct sockaddr_in sin;
|
2004-08-09 17:04:08 +00:00
|
|
|
|
2008-05-29 20:21:28 +00:00
|
|
|
DEBUG(net, 1, "Listening on %s:%d", _settings_client.network.server_bind_ip, _settings_client.network.server_port);
|
2004-08-09 17:04:08 +00:00
|
|
|
|
2004-12-04 17:54:56 +00:00
|
|
|
ls = socket(AF_INET, SOCK_STREAM, 0);
|
|
|
|
if (ls == INVALID_SOCKET) {
|
|
|
|
ServerStartError("socket() on listen socket failed");
|
|
|
|
return false;
|
|
|
|
}
|
2004-09-11 19:34:11 +00:00
|
|
|
|
2004-12-04 17:54:56 +00:00
|
|
|
{ // reuse the socket
|
|
|
|
int reuse = 1;
|
|
|
|
// The (const char*) cast is needed for windows!!
|
|
|
|
if (setsockopt(ls, SOL_SOCKET, SO_REUSEADDR, (const char*)&reuse, sizeof(reuse)) == -1) {
|
|
|
|
ServerStartError("setsockopt() on listen socket failed");
|
|
|
|
return false;
|
2004-09-11 19:34:11 +00:00
|
|
|
}
|
2004-12-04 17:54:56 +00:00
|
|
|
}
|
2004-09-11 19:34:11 +00:00
|
|
|
|
2006-12-26 17:36:18 +00:00
|
|
|
if (!SetNonBlocking(ls)) DEBUG(net, 0, "Setting non-blocking mode failed"); // XXX should this be an error?
|
2004-08-22 10:23:37 +00:00
|
|
|
|
|
|
|
sin.sin_family = AF_INET;
|
2004-12-13 17:47:21 +00:00
|
|
|
sin.sin_addr.s_addr = _network_server_bind_ip;
|
2008-05-29 20:21:28 +00:00
|
|
|
sin.sin_port = htons(_settings_client.network.server_port);
|
2004-08-22 10:23:37 +00:00
|
|
|
|
2004-12-04 17:54:56 +00:00
|
|
|
if (bind(ls, (struct sockaddr*)&sin, sizeof(sin)) != 0) {
|
|
|
|
ServerStartError("bind() failed");
|
|
|
|
return false;
|
2004-08-09 17:04:08 +00:00
|
|
|
}
|
|
|
|
|
2004-12-04 17:54:56 +00:00
|
|
|
if (listen(ls, 1) != 0) {
|
|
|
|
ServerStartError("listen() failed");
|
|
|
|
return false;
|
2004-08-09 17:04:08 +00:00
|
|
|
}
|
|
|
|
|
2004-12-04 17:54:56 +00:00
|
|
|
_listensocket = ls;
|
2004-09-11 19:34:11 +00:00
|
|
|
|
2004-12-04 17:54:56 +00:00
|
|
|
return true;
|
2004-08-09 17:04:08 +00:00
|
|
|
}
|
|
|
|
|
2004-12-04 17:54:56 +00:00
|
|
|
// Close all current connections
|
2007-03-07 11:47:46 +00:00
|
|
|
static void NetworkClose()
|
2004-08-09 17:04:08 +00:00
|
|
|
{
|
2008-12-29 10:37:53 +00:00
|
|
|
/* The pool is already empty, so we already closed the connections */
|
|
|
|
if (GetNetworkClientSocketPoolSize() == 0) return;
|
|
|
|
|
2008-12-23 09:47:42 +00:00
|
|
|
NetworkClientSocket *cs;
|
2004-08-09 17:04:08 +00:00
|
|
|
|
2008-12-23 09:02:41 +00:00
|
|
|
FOR_ALL_CLIENT_SOCKETS(cs) {
|
2004-12-04 17:54:56 +00:00
|
|
|
if (!_network_server) {
|
2008-12-29 10:37:53 +00:00
|
|
|
SEND_COMMAND(PACKET_CLIENT_QUIT)();
|
2007-02-01 23:50:15 +00:00
|
|
|
cs->Send_Packets();
|
2004-08-09 17:04:08 +00:00
|
|
|
}
|
2004-12-19 10:17:26 +00:00
|
|
|
NetworkCloseClient(cs);
|
2004-08-09 17:04:08 +00:00
|
|
|
}
|
|
|
|
|
2004-12-04 17:54:56 +00:00
|
|
|
if (_network_server) {
|
2008-05-30 20:08:32 +00:00
|
|
|
/* We are a server, also close the listensocket */
|
2004-12-04 17:54:56 +00:00
|
|
|
closesocket(_listensocket);
|
|
|
|
_listensocket = INVALID_SOCKET;
|
2006-12-26 17:36:18 +00:00
|
|
|
DEBUG(net, 1, "Closed listener");
|
2004-08-09 17:04:08 +00:00
|
|
|
}
|
2008-05-30 20:08:32 +00:00
|
|
|
NetworkUDPCloseAll();
|
|
|
|
|
2009-01-20 11:28:18 +00:00
|
|
|
TCPConnecter::KillAll();
|
|
|
|
|
2008-05-30 20:08:32 +00:00
|
|
|
_networking = false;
|
|
|
|
_network_server = false;
|
2008-12-22 20:42:02 +00:00
|
|
|
|
2009-02-20 00:03:29 +00:00
|
|
|
NetworkFreeLocalCommandQueue();
|
|
|
|
|
2008-12-22 20:42:02 +00:00
|
|
|
free(_network_company_states);
|
|
|
|
_network_company_states = NULL;
|
2008-12-23 20:52:27 +00:00
|
|
|
|
|
|
|
_NetworkClientSocket_pool.CleanPool();
|
|
|
|
_NetworkClientInfo_pool.CleanPool();
|
2004-08-09 17:04:08 +00:00
|
|
|
}
|
|
|
|
|
2004-12-04 17:54:56 +00:00
|
|
|
// Inits the network (cleans sockets and stuff)
|
2007-03-07 11:47:46 +00:00
|
|
|
static void NetworkInitialize()
|
2004-08-09 17:04:08 +00:00
|
|
|
{
|
2008-12-23 20:52:27 +00:00
|
|
|
_NetworkClientSocket_pool.CleanPool();
|
|
|
|
_NetworkClientSocket_pool.AddBlockToPool();
|
|
|
|
_NetworkClientInfo_pool.CleanPool();
|
|
|
|
_NetworkClientInfo_pool.AddBlockToPool();
|
2004-08-22 10:23:37 +00:00
|
|
|
|
2004-12-04 17:54:56 +00:00
|
|
|
_sync_frame = 0;
|
|
|
|
_network_first_time = true;
|
2004-08-22 10:23:37 +00:00
|
|
|
|
2004-12-04 17:54:56 +00:00
|
|
|
_network_reconnect = 0;
|
2004-08-09 17:04:08 +00:00
|
|
|
}
|
|
|
|
|
2009-01-20 11:28:18 +00:00
|
|
|
/** Non blocking connection create to query servers */
|
|
|
|
class TCPQueryConnecter : TCPConnecter {
|
|
|
|
public:
|
|
|
|
TCPQueryConnecter(const NetworkAddress &address) : TCPConnecter(address) {}
|
|
|
|
|
|
|
|
virtual void OnFailure()
|
|
|
|
{
|
|
|
|
NetworkDisconnect();
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual void OnConnect(SOCKET s)
|
|
|
|
{
|
|
|
|
_networking = true;
|
|
|
|
NetworkAllocClient(s);
|
|
|
|
SEND_COMMAND(PACKET_CLIENT_COMPANY_INFO)();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2004-12-04 17:54:56 +00:00
|
|
|
// 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
|
2009-01-20 01:32:06 +00:00
|
|
|
void NetworkTCPQueryServer(NetworkAddress address)
|
2004-08-09 17:04:08 +00:00
|
|
|
{
|
2007-02-02 21:04:50 +00:00
|
|
|
if (!_network_available) return;
|
2004-08-09 17:04:08 +00:00
|
|
|
|
2004-12-04 17:54:56 +00:00
|
|
|
NetworkDisconnect();
|
|
|
|
NetworkInitialize();
|
2004-08-09 17:04:08 +00:00
|
|
|
|
2009-01-20 11:28:18 +00:00
|
|
|
new TCPQueryConnecter(address);
|
2004-12-04 17:54:56 +00:00
|
|
|
}
|
2004-09-10 19:02:27 +00:00
|
|
|
|
2004-12-20 22:14:39 +00:00
|
|
|
/* Validates an address entered as a string and adds the server to
|
2006-01-25 19:03:50 +00:00
|
|
|
* the list. If you use this function, the games will be marked
|
2004-12-20 22:14:39 +00:00
|
|
|
* as manually added. */
|
2005-02-06 22:25:27 +00:00
|
|
|
void NetworkAddServer(const char *b)
|
2004-12-18 18:58:03 +00:00
|
|
|
{
|
|
|
|
if (*b != '\0') {
|
2005-02-06 22:25:27 +00:00
|
|
|
const char *port = NULL;
|
2008-09-30 20:39:50 +00:00
|
|
|
const char *company = NULL;
|
2005-02-06 22:25:27 +00:00
|
|
|
char host[NETWORK_HOSTNAME_LENGTH];
|
2004-12-18 18:58:03 +00:00
|
|
|
uint16 rport;
|
|
|
|
|
2008-11-02 11:20:15 +00:00
|
|
|
strecpy(host, b, lastof(host));
|
2004-12-19 10:17:26 +00:00
|
|
|
|
2008-11-02 11:20:15 +00:00
|
|
|
strecpy(_settings_client.network.connect_to_ip, b, lastof(_settings_client.network.connect_to_ip));
|
2004-12-18 18:58:03 +00:00
|
|
|
rport = NETWORK_DEFAULT_PORT;
|
|
|
|
|
2008-09-30 20:39:50 +00:00
|
|
|
ParseConnectionString(&company, &port, host);
|
2004-12-18 18:58:03 +00:00
|
|
|
if (port != NULL) rport = atoi(port);
|
|
|
|
|
2009-01-20 01:32:06 +00:00
|
|
|
NetworkUDPQueryServer(NetworkAddress(host, rport), true);
|
2004-12-20 22:14:39 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2004-12-21 16:56:43 +00:00
|
|
|
/* Generates the list of manually added hosts from NetworkGameList and
|
2004-12-20 22:14:39 +00:00
|
|
|
* dumps them into the array _network_host_list. This array is needed
|
|
|
|
* by the function that generates the config file. */
|
2007-03-07 11:47:46 +00:00
|
|
|
void NetworkRebuildHostList()
|
2004-12-20 22:14:39 +00:00
|
|
|
{
|
2005-01-09 21:25:44 +00:00
|
|
|
uint i = 0;
|
2006-10-17 23:34:12 +00:00
|
|
|
const NetworkGameList *item = _network_game_list;
|
2005-01-02 12:03:43 +00:00
|
|
|
while (item != NULL && i != lengthof(_network_host_list)) {
|
2006-01-29 18:34:43 +00:00
|
|
|
if (item->manually) {
|
|
|
|
free(_network_host_list[i]);
|
2005-01-05 09:59:45 +00:00
|
|
|
_network_host_list[i++] = str_fmt("%s:%i", item->info.hostname, item->port);
|
2006-01-29 18:34:43 +00:00
|
|
|
}
|
2004-12-20 22:14:39 +00:00
|
|
|
item = item->next;
|
|
|
|
}
|
|
|
|
|
2005-01-02 12:03:43 +00:00
|
|
|
for (; i < lengthof(_network_host_list); i++) {
|
2006-01-29 18:34:43 +00:00
|
|
|
free(_network_host_list[i]);
|
|
|
|
_network_host_list[i] = NULL;
|
2004-12-18 18:58:03 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-01-20 11:28:18 +00:00
|
|
|
/** Non blocking connection create to actually connect to servers */
|
|
|
|
class TCPClientConnecter : TCPConnecter {
|
|
|
|
public:
|
|
|
|
TCPClientConnecter(const NetworkAddress &address) : TCPConnecter(address) {}
|
|
|
|
|
|
|
|
virtual void OnFailure()
|
|
|
|
{
|
|
|
|
NetworkError(STR_NETWORK_ERR_NOCONNECTION);
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual void OnConnect(SOCKET s)
|
|
|
|
{
|
|
|
|
_networking = true;
|
|
|
|
NetworkAllocClient(s);
|
|
|
|
IConsoleCmdExec("exec scripts/on_client.scr 0");
|
|
|
|
NetworkClient_Connected();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
|
2004-12-04 17:54:56 +00:00
|
|
|
// Used by clients, to connect to a server
|
2009-01-20 01:32:06 +00:00
|
|
|
void NetworkClientConnectGame(NetworkAddress address)
|
2004-12-04 17:54:56 +00:00
|
|
|
{
|
2009-01-19 22:01:37 +00:00
|
|
|
if (!_network_available) return;
|
2004-08-09 17:04:08 +00:00
|
|
|
|
2009-01-20 01:32:06 +00:00
|
|
|
if (address.GetPort() == 0) return;
|
2004-08-09 17:04:08 +00:00
|
|
|
|
2009-01-20 01:32:06 +00:00
|
|
|
strecpy(_settings_client.network.last_host, address.GetHostname(), lastof(_settings_client.network.last_host));
|
|
|
|
_settings_client.network.last_port = address.GetPort();
|
2004-09-10 19:02:27 +00:00
|
|
|
|
2004-12-04 17:54:56 +00:00
|
|
|
NetworkDisconnect();
|
|
|
|
NetworkInitialize();
|
2004-08-09 17:04:08 +00:00
|
|
|
|
2009-01-20 11:28:18 +00:00
|
|
|
_network_join_status = NETWORK_JOIN_STATUS_CONNECTING;
|
|
|
|
ShowJoinStatusWindow();
|
2004-08-22 10:23:37 +00:00
|
|
|
|
2009-01-20 11:28:18 +00:00
|
|
|
new TCPClientConnecter(address);
|
2004-08-09 17:04:08 +00:00
|
|
|
}
|
|
|
|
|
2007-03-07 11:47:46 +00:00
|
|
|
static void NetworkInitGameInfo()
|
2004-09-12 14:12:33 +00:00
|
|
|
{
|
2008-06-03 08:04:35 +00:00
|
|
|
if (StrEmpty(_settings_client.network.server_name)) {
|
|
|
|
snprintf(_settings_client.network.server_name, sizeof(_settings_client.network.server_name), "Unnamed Server");
|
2008-05-29 20:21:28 +00:00
|
|
|
}
|
2004-09-10 19:02:27 +00:00
|
|
|
|
2008-06-03 08:04:35 +00:00
|
|
|
/* The server is a client too */
|
|
|
|
_network_game_info.clients_on = _network_dedicated ? 0 : 1;
|
2008-05-29 15:13:28 +00:00
|
|
|
_network_game_info.start_date = ConvertYMDToDate(_settings_game.game_creation.starting_year, 0, 1);
|
2004-09-10 19:02:27 +00:00
|
|
|
|
2008-12-23 20:52:27 +00:00
|
|
|
NetworkClientInfo *ci = new NetworkClientInfo(CLIENT_ID_SERVER);
|
2008-09-30 20:39:50 +00:00
|
|
|
ci->client_playas = _network_dedicated ? COMPANY_SPECTATOR : _local_company;
|
2006-10-17 22:16:46 +00:00
|
|
|
|
2008-11-02 11:20:15 +00:00
|
|
|
strecpy(ci->client_name, _settings_client.network.client_name, lastof(ci->client_name));
|
|
|
|
strecpy(ci->unique_id, _settings_client.network.network_id, lastof(ci->unique_id));
|
2004-08-09 17:04:08 +00:00
|
|
|
}
|
|
|
|
|
2007-03-07 11:47:46 +00:00
|
|
|
bool NetworkServerStart()
|
2004-09-12 14:12:33 +00:00
|
|
|
{
|
2004-12-04 17:54:56 +00:00
|
|
|
if (!_network_available) return false;
|
2004-09-10 19:02:27 +00:00
|
|
|
|
2004-12-13 17:47:21 +00:00
|
|
|
/* Call the pre-scripts */
|
|
|
|
IConsoleCmdExec("exec scripts/pre_server.scr 0");
|
|
|
|
if (_network_dedicated) IConsoleCmdExec("exec scripts/pre_dedicated.scr 0");
|
|
|
|
|
2004-12-04 17:54:56 +00:00
|
|
|
NetworkInitialize();
|
2006-10-17 23:34:12 +00:00
|
|
|
if (!NetworkListen()) return false;
|
2004-08-09 17:04:08 +00:00
|
|
|
|
2004-12-04 17:54:56 +00:00
|
|
|
// Try to start UDP-server
|
|
|
|
_network_udp_server = true;
|
2008-05-29 20:21:28 +00:00
|
|
|
_network_udp_server = _udp_server_socket->Listen(_network_server_bind_ip, _settings_client.network.server_port, false);
|
2004-08-22 10:23:37 +00:00
|
|
|
|
2008-12-22 20:42:02 +00:00
|
|
|
_network_company_states = CallocT<NetworkCompanyState>(MAX_COMPANIES);
|
2004-12-04 17:54:56 +00:00
|
|
|
_network_server = true;
|
|
|
|
_networking = true;
|
|
|
|
_frame_counter = 0;
|
|
|
|
_frame_counter_server = 0;
|
|
|
|
_frame_counter_max = 0;
|
2005-07-16 12:59:23 +00:00
|
|
|
_last_sync_frame = 0;
|
2008-12-22 12:59:31 +00:00
|
|
|
_network_own_client_id = CLIENT_ID_SERVER;
|
2004-09-10 19:02:27 +00:00
|
|
|
|
2008-09-30 20:39:50 +00:00
|
|
|
/* Non-dedicated server will always be company #1 */
|
|
|
|
if (!_network_dedicated) _network_playas = COMPANY_FIRST;
|
2004-12-16 11:04:43 +00:00
|
|
|
|
2004-12-04 17:54:56 +00:00
|
|
|
_network_clients_connected = 0;
|
2004-08-22 10:23:37 +00:00
|
|
|
|
2004-12-04 17:54:56 +00:00
|
|
|
NetworkInitGameInfo();
|
2004-09-10 19:02:27 +00:00
|
|
|
|
2004-12-04 17:54:56 +00:00
|
|
|
// execute server initialization script
|
|
|
|
IConsoleCmdExec("exec scripts/on_server.scr 0");
|
|
|
|
// if the server is dedicated ... add some other script
|
|
|
|
if (_network_dedicated) IConsoleCmdExec("exec scripts/on_dedicated.scr 0");
|
2004-12-15 20:10:34 +00:00
|
|
|
|
2008-09-30 20:39:50 +00:00
|
|
|
_min_active_clients_paused = false;
|
|
|
|
CheckMinActiveClients();
|
2006-10-03 16:15:34 +00:00
|
|
|
|
2004-12-15 20:10:34 +00:00
|
|
|
/* Try to register us to the master server */
|
2006-01-19 17:50:40 +00:00
|
|
|
_network_last_advertise_frame = 0;
|
|
|
|
_network_need_advertise = true;
|
2004-12-15 20:10:34 +00:00
|
|
|
NetworkUDPAdvertise();
|
2004-12-04 17:54:56 +00:00
|
|
|
return true;
|
2004-08-09 17:04:08 +00:00
|
|
|
}
|
|
|
|
|
2004-12-04 17:54:56 +00:00
|
|
|
// The server is rebooting...
|
|
|
|
// The only difference with NetworkDisconnect, is the packets that is sent
|
2007-03-07 11:47:46 +00:00
|
|
|
void NetworkReboot()
|
2004-09-12 14:12:33 +00:00
|
|
|
{
|
2004-12-04 17:54:56 +00:00
|
|
|
if (_network_server) {
|
2008-12-23 09:47:42 +00:00
|
|
|
NetworkClientSocket *cs;
|
2008-12-23 09:02:41 +00:00
|
|
|
FOR_ALL_CLIENT_SOCKETS(cs) {
|
2004-12-04 17:54:56 +00:00
|
|
|
SEND_COMMAND(PACKET_SERVER_NEWGAME)(cs);
|
2007-02-01 23:50:15 +00:00
|
|
|
cs->Send_Packets();
|
2004-08-22 10:23:37 +00:00
|
|
|
}
|
2004-12-04 17:54:56 +00:00
|
|
|
}
|
2004-08-22 10:23:37 +00:00
|
|
|
|
2004-12-04 17:54:56 +00:00
|
|
|
NetworkClose();
|
2004-08-22 10:23:37 +00:00
|
|
|
}
|
|
|
|
|
2004-12-04 17:54:56 +00:00
|
|
|
// We want to disconnect from the host/clients
|
2007-03-07 11:47:46 +00:00
|
|
|
void NetworkDisconnect()
|
2004-09-12 14:12:33 +00:00
|
|
|
{
|
2004-12-04 17:54:56 +00:00
|
|
|
if (_network_server) {
|
2008-12-23 09:47:42 +00:00
|
|
|
NetworkClientSocket *cs;
|
2008-12-23 09:02:41 +00:00
|
|
|
FOR_ALL_CLIENT_SOCKETS(cs) {
|
2004-12-04 17:54:56 +00:00
|
|
|
SEND_COMMAND(PACKET_SERVER_SHUTDOWN)(cs);
|
2007-02-01 23:50:15 +00:00
|
|
|
cs->Send_Packets();
|
2004-12-04 17:54:56 +00:00
|
|
|
}
|
2004-09-12 14:12:33 +00:00
|
|
|
}
|
2004-08-27 11:01:26 +00:00
|
|
|
|
2008-06-09 12:17:01 +00:00
|
|
|
if (_settings_client.network.server_advertise) NetworkUDPRemoveAdvertise();
|
2004-12-22 18:56:52 +00:00
|
|
|
|
2004-12-12 13:46:10 +00:00
|
|
|
DeleteWindowById(WC_NETWORK_STATUS_WINDOW, 0);
|
|
|
|
|
2004-12-04 17:54:56 +00:00
|
|
|
NetworkClose();
|
|
|
|
}
|
2004-08-22 10:23:37 +00:00
|
|
|
|
2004-12-04 17:54:56 +00:00
|
|
|
// Receives something from the network
|
2007-03-07 11:47:46 +00:00
|
|
|
static bool NetworkReceive()
|
2004-12-04 17:54:56 +00:00
|
|
|
{
|
2008-12-23 09:47:42 +00:00
|
|
|
NetworkClientSocket *cs;
|
2004-12-04 17:54:56 +00:00
|
|
|
int n;
|
|
|
|
fd_set read_fd, write_fd;
|
|
|
|
struct timeval tv;
|
2004-08-22 10:23:37 +00:00
|
|
|
|
2004-12-04 17:54:56 +00:00
|
|
|
FD_ZERO(&read_fd);
|
|
|
|
FD_ZERO(&write_fd);
|
2004-08-22 10:23:37 +00:00
|
|
|
|
2008-12-23 09:02:41 +00:00
|
|
|
FOR_ALL_CLIENT_SOCKETS(cs) {
|
2007-01-12 20:19:49 +00:00
|
|
|
FD_SET(cs->sock, &read_fd);
|
|
|
|
FD_SET(cs->sock, &write_fd);
|
2004-12-04 17:54:56 +00:00
|
|
|
}
|
2004-08-22 10:23:37 +00:00
|
|
|
|
2004-12-04 17:54:56 +00:00
|
|
|
// take care of listener port
|
2006-10-17 23:34:12 +00:00
|
|
|
if (_network_server) FD_SET(_listensocket, &read_fd);
|
2004-08-22 10:23:37 +00:00
|
|
|
|
2004-12-04 17:54:56 +00:00
|
|
|
tv.tv_sec = tv.tv_usec = 0; // don't block at all.
|
|
|
|
#if !defined(__MORPHOS__) && !defined(__AMIGA__)
|
|
|
|
n = select(FD_SETSIZE, &read_fd, &write_fd, NULL, &tv);
|
|
|
|
#else
|
|
|
|
n = WaitSelect(FD_SETSIZE, &read_fd, &write_fd, NULL, &tv, NULL);
|
|
|
|
#endif
|
|
|
|
if (n == -1 && !_network_server) NetworkError(STR_NETWORK_ERR_LOSTCONNECTION);
|
2004-08-27 11:01:26 +00:00
|
|
|
|
2004-12-04 17:54:56 +00:00
|
|
|
// accept clients..
|
2006-10-17 23:34:12 +00:00
|
|
|
if (_network_server && FD_ISSET(_listensocket, &read_fd)) NetworkAcceptClients();
|
2004-09-12 14:12:33 +00:00
|
|
|
|
2004-12-04 17:54:56 +00:00
|
|
|
// read stuff from clients
|
2008-12-23 09:02:41 +00:00
|
|
|
FOR_ALL_CLIENT_SOCKETS(cs) {
|
2007-01-12 20:19:49 +00:00
|
|
|
cs->writable = !!FD_ISSET(cs->sock, &write_fd);
|
|
|
|
if (FD_ISSET(cs->sock, &read_fd)) {
|
2006-06-27 21:25:53 +00:00
|
|
|
if (_network_server) {
|
2004-12-04 17:54:56 +00:00
|
|
|
NetworkServer_ReadPackets(cs);
|
2006-06-27 21:25:53 +00:00
|
|
|
} else {
|
2006-10-17 23:34:12 +00:00
|
|
|
NetworkRecvStatus res;
|
2006-06-27 21:25:53 +00:00
|
|
|
|
2004-12-04 17:54:56 +00:00
|
|
|
// The client already was quiting!
|
2006-10-17 23:34:12 +00:00
|
|
|
if (cs->has_quit) return false;
|
2006-06-27 21:25:53 +00:00
|
|
|
|
|
|
|
res = NetworkClient_ReadPackets(cs);
|
|
|
|
if (res != NETWORK_RECV_STATUS_OKAY) {
|
2004-12-04 17:54:56 +00:00
|
|
|
// The client made an error of which we can not recover
|
|
|
|
// close the client and drop back to main menu
|
|
|
|
NetworkClientError(res, cs);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
2004-09-12 14:12:33 +00:00
|
|
|
}
|
2004-08-22 10:23:37 +00:00
|
|
|
}
|
2004-12-04 17:54:56 +00:00
|
|
|
return true;
|
2004-08-22 10:23:37 +00:00
|
|
|
}
|
|
|
|
|
2004-12-04 17:54:56 +00:00
|
|
|
// This sends all buffered commands (if possible)
|
2007-03-07 11:47:46 +00:00
|
|
|
static void NetworkSend()
|
2004-09-12 23:35:01 +00:00
|
|
|
{
|
2008-12-23 09:47:42 +00:00
|
|
|
NetworkClientSocket *cs;
|
2008-12-23 09:02:41 +00:00
|
|
|
FOR_ALL_CLIENT_SOCKETS(cs) {
|
2004-12-04 17:54:56 +00:00
|
|
|
if (cs->writable) {
|
2007-02-01 23:50:15 +00:00
|
|
|
cs->Send_Packets();
|
2004-12-04 17:54:56 +00:00
|
|
|
|
|
|
|
if (cs->status == STATUS_MAP) {
|
|
|
|
// This client is in the middle of a map-send, call the function for that
|
|
|
|
SEND_COMMAND(PACKET_SERVER_MAP)(cs);
|
|
|
|
}
|
2004-09-12 23:35:01 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-03-07 11:47:46 +00:00
|
|
|
static bool NetworkDoClientLoop()
|
2004-09-12 14:12:33 +00:00
|
|
|
{
|
2004-12-04 17:54:56 +00:00
|
|
|
_frame_counter++;
|
2004-09-12 14:12:33 +00:00
|
|
|
|
2009-01-08 13:57:50 +00:00
|
|
|
NetworkExecuteLocalCommandQueue();
|
2004-09-12 14:12:33 +00:00
|
|
|
|
2004-12-04 17:54:56 +00:00
|
|
|
StateGameLoop();
|
2004-08-22 10:23:37 +00:00
|
|
|
|
2004-12-04 17:54:56 +00:00
|
|
|
// Check if we are in sync!
|
|
|
|
if (_sync_frame != 0) {
|
|
|
|
if (_sync_frame == _frame_counter) {
|
|
|
|
#ifdef NETWORK_SEND_DOUBLE_SEED
|
2008-01-29 00:27:25 +00:00
|
|
|
if (_sync_seed_1 != _random.state[0] || _sync_seed_2 != _random.state[1]) {
|
2004-12-04 17:54:56 +00:00
|
|
|
#else
|
2008-01-29 00:27:25 +00:00
|
|
|
if (_sync_seed_1 != _random.state[0]) {
|
2004-12-04 17:54:56 +00:00
|
|
|
#endif
|
|
|
|
NetworkError(STR_NETWORK_ERR_DESYNC);
|
2008-12-29 21:50:25 +00:00
|
|
|
DEBUG(desync, 1, "sync_err: %d; %d\n", _date, _date_fract);
|
2006-12-26 17:36:18 +00:00
|
|
|
DEBUG(net, 0, "Sync error detected!");
|
2008-12-23 10:42:06 +00:00
|
|
|
NetworkClientError(NETWORK_RECV_STATUS_DESYNC, GetNetworkClientSocket(0));
|
2004-12-04 17:54:56 +00:00
|
|
|
return false;
|
|
|
|
}
|
2004-08-22 10:23:37 +00:00
|
|
|
|
2004-12-04 17:54:56 +00:00
|
|
|
// If this is the first time we have a sync-frame, we
|
|
|
|
// need to let the server know that we are ready and at the same
|
|
|
|
// frame as he is.. so we can start playing!
|
|
|
|
if (_network_first_time) {
|
|
|
|
_network_first_time = false;
|
|
|
|
SEND_COMMAND(PACKET_CLIENT_ACK)();
|
|
|
|
}
|
2004-08-22 10:23:37 +00:00
|
|
|
|
2004-12-04 17:54:56 +00:00
|
|
|
_sync_frame = 0;
|
|
|
|
} else if (_sync_frame < _frame_counter) {
|
2006-12-26 17:36:18 +00:00
|
|
|
DEBUG(net, 1, "Missed frame for sync-test (%d / %d)", _sync_frame, _frame_counter);
|
2004-12-04 17:54:56 +00:00
|
|
|
_sync_frame = 0;
|
2004-08-22 10:23:37 +00:00
|
|
|
}
|
2004-12-04 17:54:56 +00:00
|
|
|
}
|
2004-09-10 19:02:27 +00:00
|
|
|
|
2004-12-04 17:54:56 +00:00
|
|
|
return true;
|
2004-08-22 10:23:37 +00:00
|
|
|
}
|
|
|
|
|
2004-12-04 17:54:56 +00:00
|
|
|
// We have to do some UDP checking
|
2007-03-07 11:47:46 +00:00
|
|
|
void NetworkUDPGameLoop()
|
2004-09-12 14:12:33 +00:00
|
|
|
{
|
2009-01-20 16:51:55 +00:00
|
|
|
_network_content_client.SendReceive();
|
2009-01-20 11:28:18 +00:00
|
|
|
TCPConnecter::CheckCallbacks();
|
2009-01-17 16:53:32 +00:00
|
|
|
|
2004-12-22 18:42:56 +00:00
|
|
|
if (_network_udp_server) {
|
2007-01-12 14:30:01 +00:00
|
|
|
_udp_server_socket->ReceivePackets();
|
|
|
|
_udp_master_socket->ReceivePackets();
|
|
|
|
} else {
|
|
|
|
_udp_client_socket->ReceivePackets();
|
2006-06-10 08:37:41 +00:00
|
|
|
if (_network_udp_broadcast > 0) _network_udp_broadcast--;
|
2007-02-01 21:04:40 +00:00
|
|
|
NetworkGameListRequery();
|
2004-08-22 10:23:37 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2004-12-04 17:54:56 +00:00
|
|
|
// The main loop called from ttd.c
|
|
|
|
// Here we also have to do StateGameLoop if needed!
|
2007-03-07 11:47:46 +00:00
|
|
|
void NetworkGameLoop()
|
2004-09-12 14:12:33 +00:00
|
|
|
{
|
2004-12-04 17:54:56 +00:00
|
|
|
if (!_networking) return;
|
2004-08-22 10:23:37 +00:00
|
|
|
|
2004-12-04 17:54:56 +00:00
|
|
|
if (!NetworkReceive()) return;
|
2004-08-22 10:23:37 +00:00
|
|
|
|
2004-12-04 17:54:56 +00:00
|
|
|
if (_network_server) {
|
2007-07-07 10:06:10 +00:00
|
|
|
#ifdef DEBUG_DUMP_COMMANDS
|
|
|
|
static FILE *f = FioFOpenFile("commands.log", "rb", SAVE_DIR);
|
|
|
|
static Date next_date = 0;
|
|
|
|
static uint32 next_date_fract;
|
|
|
|
static CommandPacket *cp = NULL;
|
|
|
|
if (f == NULL && next_date == 0) {
|
|
|
|
printf("Cannot open commands.log\n");
|
|
|
|
next_date = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
while (f != NULL && !feof(f)) {
|
|
|
|
if (cp != NULL && _date == next_date && _date_fract == next_date_fract) {
|
2008-09-30 20:39:50 +00:00
|
|
|
_current_company = cp->company;
|
2008-12-28 14:37:19 +00:00
|
|
|
DoCommandP(cp->tile, cp->p1, cp->p2, cp->cmd, NULL, cp->text);
|
2007-07-07 10:06:10 +00:00
|
|
|
free(cp);
|
|
|
|
cp = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (cp != NULL) break;
|
|
|
|
|
|
|
|
char buff[4096];
|
|
|
|
if (fgets(buff, lengthof(buff), f) == NULL) break;
|
2008-12-29 21:50:25 +00:00
|
|
|
if (strncmp(buff, "cmd: ", 8) != 0) continue;
|
2007-07-07 10:06:10 +00:00
|
|
|
cp = MallocT<CommandPacket>(1);
|
2008-09-30 20:39:50 +00:00
|
|
|
int company;
|
2008-12-29 21:50:25 +00:00
|
|
|
sscanf(&buff[8], "%d; %d; %d; %d; %d; %d; %d; %s", &next_date, &next_date_fract, &company, &cp->tile, &cp->p1, &cp->p2, &cp->cmd, cp->text);
|
2008-09-30 20:39:50 +00:00
|
|
|
cp->company = (CompanyID)company;
|
2007-07-07 10:06:10 +00:00
|
|
|
}
|
2008-01-11 00:30:32 +00:00
|
|
|
#endif /* DEBUG_DUMP_COMMANDS */
|
2007-07-07 10:06:10 +00:00
|
|
|
|
2005-07-29 21:55:49 +00:00
|
|
|
bool send_frame = false;
|
|
|
|
|
2004-12-04 17:54:56 +00:00
|
|
|
// We first increase the _frame_counter
|
|
|
|
_frame_counter++;
|
2005-07-29 21:55:49 +00:00
|
|
|
// Update max-frame-counter
|
|
|
|
if (_frame_counter > _frame_counter_max) {
|
2008-05-29 20:21:28 +00:00
|
|
|
_frame_counter_max = _frame_counter + _settings_client.network.frame_freq;
|
2005-07-29 21:55:49 +00:00
|
|
|
send_frame = true;
|
|
|
|
}
|
2004-08-22 10:23:37 +00:00
|
|
|
|
2009-01-08 13:57:50 +00:00
|
|
|
NetworkExecuteLocalCommandQueue();
|
2004-08-22 10:23:37 +00:00
|
|
|
|
2004-12-04 17:54:56 +00:00
|
|
|
// Then we make the frame
|
|
|
|
StateGameLoop();
|
2004-09-12 14:12:33 +00:00
|
|
|
|
2008-01-29 00:27:25 +00:00
|
|
|
_sync_seed_1 = _random.state[0];
|
2004-12-04 17:54:56 +00:00
|
|
|
#ifdef NETWORK_SEND_DOUBLE_SEED
|
2008-01-29 00:27:25 +00:00
|
|
|
_sync_seed_2 = _random.state[1];
|
2004-12-04 17:54:56 +00:00
|
|
|
#endif
|
2004-08-22 10:23:37 +00:00
|
|
|
|
2005-07-29 21:55:49 +00:00
|
|
|
NetworkServer_Tick(send_frame);
|
2004-12-04 17:54:56 +00:00
|
|
|
} else {
|
|
|
|
// Client
|
2004-08-22 10:23:37 +00:00
|
|
|
|
2004-12-04 17:54:56 +00:00
|
|
|
// Make sure we are at the frame were the server is (quick-frames)
|
|
|
|
if (_frame_counter_server > _frame_counter) {
|
|
|
|
while (_frame_counter_server > _frame_counter) {
|
|
|
|
if (!NetworkDoClientLoop()) break;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// Else, keep on going till _frame_counter_max
|
2006-10-17 23:34:12 +00:00
|
|
|
if (_frame_counter_max > _frame_counter) NetworkDoClientLoop();
|
2004-12-04 17:54:56 +00:00
|
|
|
}
|
2004-08-22 10:23:37 +00:00
|
|
|
}
|
2004-12-04 17:54:56 +00:00
|
|
|
|
|
|
|
NetworkSend();
|
2004-08-22 10:23:37 +00:00
|
|
|
}
|
|
|
|
|
2007-03-07 11:47:46 +00:00
|
|
|
static void NetworkGenerateUniqueId()
|
2004-12-12 16:04:32 +00:00
|
|
|
{
|
2007-12-25 13:59:21 +00:00
|
|
|
Md5 checksum;
|
|
|
|
uint8 digest[16];
|
2009-01-09 22:56:28 +00:00
|
|
|
char hex_output[16 * 2 + 1];
|
2004-12-12 16:04:32 +00:00
|
|
|
char coding_string[NETWORK_NAME_LENGTH];
|
|
|
|
int di;
|
|
|
|
|
2004-12-12 17:47:50 +00:00
|
|
|
snprintf(coding_string, sizeof(coding_string), "%d%s", (uint)Random(), "OpenTTD Unique ID");
|
2004-12-12 16:04:32 +00:00
|
|
|
|
|
|
|
/* Generate the MD5 hash */
|
2007-12-25 13:59:21 +00:00
|
|
|
checksum.Append((const uint8*)coding_string, strlen(coding_string));
|
|
|
|
checksum.Finish(digest);
|
2004-12-12 16:04:32 +00:00
|
|
|
|
|
|
|
for (di = 0; di < 16; ++di)
|
|
|
|
sprintf(hex_output + di * 2, "%02x", digest[di]);
|
|
|
|
|
|
|
|
/* _network_unique_id is our id */
|
2008-05-29 20:21:28 +00:00
|
|
|
snprintf(_settings_client.network.network_id, sizeof(_settings_client.network.network_id), "%s", hex_output);
|
2004-12-12 16:04:32 +00:00
|
|
|
}
|
|
|
|
|
2009-01-20 01:32:06 +00:00
|
|
|
void NetworkStartDebugLog(NetworkAddress address)
|
2007-02-08 12:27:53 +00:00
|
|
|
{
|
|
|
|
extern SOCKET _debug_socket; // Comes from debug.c
|
|
|
|
SOCKET s;
|
|
|
|
struct sockaddr_in sin;
|
|
|
|
|
2009-01-20 01:32:06 +00:00
|
|
|
DEBUG(net, 0, "Redirecting DEBUG() to %s:%d", address.GetHostname(), address.GetPort());
|
2007-02-08 12:27:53 +00:00
|
|
|
|
|
|
|
s = socket(AF_INET, SOCK_STREAM, 0);
|
|
|
|
if (s == INVALID_SOCKET) {
|
|
|
|
DEBUG(net, 0, "Failed to open socket for redirection DEBUG()");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!SetNoDelay(s)) DEBUG(net, 1, "Setting TCP_NODELAY failed");
|
|
|
|
|
|
|
|
sin.sin_family = AF_INET;
|
2009-01-20 01:32:06 +00:00
|
|
|
sin.sin_addr.s_addr = address.GetIP();
|
|
|
|
sin.sin_port = htons(address.GetPort());
|
2007-02-08 12:27:53 +00:00
|
|
|
|
|
|
|
if (connect(s, (struct sockaddr *)&sin, sizeof(sin)) != 0) {
|
2009-01-20 04:01:07 +00:00
|
|
|
DEBUG(net, 0, "Failed to redirection DEBUG() to %s:%d", address.GetHostname(), address.GetPort());
|
2007-02-08 12:27:53 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!SetNonBlocking(s)) DEBUG(net, 0, "Setting non-blocking mode failed");
|
|
|
|
_debug_socket = s;
|
|
|
|
|
|
|
|
DEBUG(net, 0, "DEBUG() is now redirected");
|
|
|
|
}
|
|
|
|
|
2007-01-04 19:12:45 +00:00
|
|
|
/** This tries to launch the network for a given OS */
|
2007-03-07 11:47:46 +00:00
|
|
|
void NetworkStartUp()
|
2004-09-12 14:12:33 +00:00
|
|
|
{
|
2006-12-26 17:36:18 +00:00
|
|
|
DEBUG(net, 3, "[core] starting network...");
|
2004-12-22 21:12:36 +00:00
|
|
|
|
2007-01-04 19:12:45 +00:00
|
|
|
/* Network is available */
|
|
|
|
_network_available = NetworkCoreInitialize();;
|
2004-12-04 17:54:56 +00:00
|
|
|
_network_dedicated = false;
|
2006-01-19 17:50:40 +00:00
|
|
|
_network_last_advertise_frame = 0;
|
|
|
|
_network_need_advertise = true;
|
2004-12-22 18:42:56 +00:00
|
|
|
_network_advertise_retries = 0;
|
2004-08-22 10:23:37 +00:00
|
|
|
|
2004-12-13 17:47:21 +00:00
|
|
|
/* Load the ip from the openttd.cfg */
|
2008-05-29 20:21:28 +00:00
|
|
|
_network_server_bind_ip = inet_addr(_settings_client.network.server_bind_ip);
|
2004-12-13 17:47:21 +00:00
|
|
|
/* And put the data back in it in case it was an invalid ip */
|
2008-05-29 20:21:28 +00:00
|
|
|
snprintf(_settings_client.network.server_bind_ip, sizeof(_settings_client.network.server_bind_ip), "%s", inet_ntoa(*(struct in_addr *)&_network_server_bind_ip));
|
2004-12-13 17:47:21 +00:00
|
|
|
|
2004-12-12 16:04:32 +00:00
|
|
|
/* Generate an unique id when there is none yet */
|
2008-05-29 20:21:28 +00:00
|
|
|
if (StrEmpty(_settings_client.network.network_id)) NetworkGenerateUniqueId();
|
2004-12-12 16:04:32 +00:00
|
|
|
|
2008-06-03 08:04:35 +00:00
|
|
|
memset(&_network_game_info, 0, sizeof(_network_game_info));
|
2004-08-22 10:23:37 +00:00
|
|
|
|
2008-12-23 20:58:03 +00:00
|
|
|
NetworkUDPInitialize();
|
2004-12-04 17:54:56 +00:00
|
|
|
NetworkInitialize();
|
2006-12-26 17:36:18 +00:00
|
|
|
DEBUG(net, 3, "[core] network online, multiplayer available");
|
2008-08-04 12:56:38 +00:00
|
|
|
NetworkFindBroadcastIPs(_broadcast_list, MAX_INTERFACES);
|
2004-08-22 10:23:37 +00:00
|
|
|
}
|
|
|
|
|
2007-01-04 19:12:45 +00:00
|
|
|
/** This shuts the network down */
|
2007-03-07 11:47:46 +00:00
|
|
|
void NetworkShutDown()
|
2004-09-12 14:12:33 +00:00
|
|
|
{
|
2006-10-12 14:48:36 +00:00
|
|
|
NetworkDisconnect();
|
2007-01-12 14:30:01 +00:00
|
|
|
NetworkUDPShutdown();
|
2006-10-12 14:48:36 +00:00
|
|
|
|
2006-12-26 17:36:18 +00:00
|
|
|
DEBUG(net, 3, "[core] shutting down network");
|
2004-08-22 10:23:37 +00:00
|
|
|
|
2004-12-04 17:54:56 +00:00
|
|
|
_network_available = false;
|
2004-08-22 10:23:37 +00:00
|
|
|
|
2007-01-04 18:50:40 +00:00
|
|
|
NetworkCoreShutdown();
|
2004-08-22 10:23:37 +00:00
|
|
|
}
|
|
|
|
|
2007-03-01 00:58:09 +00:00
|
|
|
/**
|
|
|
|
* Checks whether the given version string is compatible with our version.
|
|
|
|
* @param other the version string to compare to
|
|
|
|
*/
|
|
|
|
bool IsNetworkCompatibleVersion(const char *other)
|
|
|
|
{
|
2007-07-10 07:46:58 +00:00
|
|
|
return strncmp(_openttd_revision, other, NETWORK_REVISION_LENGTH - 1) == 0;
|
2007-03-01 00:58:09 +00:00
|
|
|
}
|
|
|
|
|
2004-12-04 17:54:56 +00:00
|
|
|
#endif /* ENABLE_NETWORK */
|
2008-01-13 21:51:53 +00:00
|
|
|
|
|
|
|
/* NOTE: this variable needs to be always available */
|
2008-09-30 20:39:50 +00:00
|
|
|
CompanyID _network_playas;
|