2005-07-24 16:12:37 +02:00
|
|
|
/* $Id$ */
|
|
|
|
|
2008-05-06 17:11:33 +02:00
|
|
|
/** @file network.cpp Base functions for networking support. */
|
|
|
|
|
2007-01-02 18:34:03 +01:00
|
|
|
#include "../stdafx.h"
|
2008-09-30 22:51:04 +02:00
|
|
|
#include "../company_type.h"
|
2004-08-09 19:04:08 +02:00
|
|
|
|
2004-12-04 18:54:56 +01:00
|
|
|
#ifdef ENABLE_NETWORK
|
2004-08-09 19:04:08 +02:00
|
|
|
|
2007-01-02 18:34:03 +01:00
|
|
|
#include "../openttd.h"
|
2007-12-21 20:49:27 +01:00
|
|
|
#include "../strings_func.h"
|
2007-12-21 22:50:46 +01:00
|
|
|
#include "../command_func.h"
|
2007-01-02 18:34:03 +01:00
|
|
|
#include "../variables.h"
|
2007-12-26 14:50:40 +01:00
|
|
|
#include "../date_func.h"
|
2008-05-30 20:20:26 +02:00
|
|
|
#include "network_internal.h"
|
2004-12-04 18:54:56 +01:00
|
|
|
#include "network_client.h"
|
|
|
|
#include "network_server.h"
|
2009-01-17 17:53:32 +01:00
|
|
|
#include "network_content.h"
|
2004-12-04 18:54:56 +01:00
|
|
|
#include "network_udp.h"
|
|
|
|
#include "network_gamelist.h"
|
2007-01-02 18:34:03 +01:00
|
|
|
#include "core/udp.h"
|
2008-08-04 14:56:38 +02:00
|
|
|
#include "core/host.h"
|
2006-12-30 02:52:09 +01:00
|
|
|
#include "network_gui.h"
|
2008-05-24 12:15:06 +02:00
|
|
|
#include "../console_func.h"
|
2007-01-02 18:34:03 +01:00
|
|
|
#include "../md5.h"
|
2007-12-25 12:26:07 +01:00
|
|
|
#include "../core/random_func.hpp"
|
|
|
|
#include "../window_func.h"
|
2008-01-07 15:23:25 +01:00
|
|
|
#include "../string_func.h"
|
2008-09-30 22:51:04 +02:00
|
|
|
#include "../company_func.h"
|
2009-01-31 21:16:06 +01:00
|
|
|
#include "../company_base.h"
|
2008-01-13 15:37:30 +01:00
|
|
|
#include "../settings_type.h"
|
2008-05-07 11:07:19 +02:00
|
|
|
#include "../landscape_type.h"
|
2008-05-04 23:53:36 +02:00
|
|
|
#include "../rev.h"
|
2008-12-22 21:42:02 +01:00
|
|
|
#include "../core/alloc_func.hpp"
|
2008-01-05 22:42:34 +01:00
|
|
|
#ifdef DEBUG_DUMP_COMMANDS
|
2008-10-18 16:16:29 +02:00
|
|
|
#include "../fileio_func.h"
|
2008-01-11 01:30:32 +01:00
|
|
|
#endif /* DEBUG_DUMP_COMMANDS */
|
2008-01-13 02:21:35 +01:00
|
|
|
#include "table/strings.h"
|
2008-12-23 21:52:27 +01:00
|
|
|
#include "../oldpool_func.h"
|
2008-01-13 02:21:35 +01:00
|
|
|
|
2008-12-22 13:59:31 +01:00
|
|
|
DECLARE_POSTFIX_INCREMENT(ClientID);
|
|
|
|
|
2008-12-23 21:52:27 +01:00
|
|
|
typedef ClientIndex NetworkClientInfoID;
|
|
|
|
DEFINE_OLD_POOL_GENERIC(NetworkClientInfo, NetworkClientInfo);
|
|
|
|
|
2009-02-25 01:48:04 +01:00
|
|
|
bool _networking; ///< are we in networking mode?
|
2008-01-13 22:51:53 +01: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 20:20:26 +02:00
|
|
|
bool _is_network_server; ///< Does this client wants to be a network-server?
|
2008-06-03 10:04:35 +02:00
|
|
|
NetworkServerGameInfo _network_game_info;
|
2008-12-22 21:42:02 +01:00
|
|
|
NetworkCompanyState *_network_company_states = NULL;
|
2008-12-22 13:59:31 +01:00
|
|
|
ClientID _network_own_client_id;
|
|
|
|
ClientID _redirect_console_to_client;
|
2008-05-30 20:20:26 +02:00
|
|
|
bool _network_need_advertise;
|
|
|
|
uint32 _network_last_advertise_frame;
|
|
|
|
uint8 _network_reconnect;
|
2009-04-03 14:49:58 +02:00
|
|
|
StringList _network_host_list;
|
|
|
|
StringList _network_ban_list;
|
2008-05-30 20:20:26 +02:00
|
|
|
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 14:57:50 +01:00
|
|
|
uint32 _frame_counter;
|
2008-05-30 20:20:26 +02:00
|
|
|
uint32 _last_sync_frame; // Used in the server to store the last time a sync packet was sent to clients.
|
2009-04-07 21:06:36 +02:00
|
|
|
NetworkAddressList _broadcast_list;
|
2008-05-30 20:20:26 +02:00
|
|
|
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 23:18:06 +01:00
|
|
|
CompanyMask _network_company_passworded; ///< Bitmask of the password status of all companies.
|
2008-01-13 02:39:22 +01:00
|
|
|
|
2007-01-23 15:47:38 +01: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 08:05:01 +02:00
|
|
|
assert_compile((int)NETWORK_COMPANY_NAME_LENGTH == MAX_LENGTH_COMPANY_NAME_BYTES);
|
2007-01-23 15:47:38 +01:00
|
|
|
|
2007-01-12 15:30:01 +01: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 19:56:51 +01:00
|
|
|
|
2009-03-15 01:32:18 +01:00
|
|
|
/* The listen socket for the server */
|
2009-04-08 02:05:16 +02:00
|
|
|
static SocketList _listensockets;
|
2004-08-27 13:01:26 +02:00
|
|
|
|
2009-03-15 01:32:18 +01:00
|
|
|
/* The amount of clients connected */
|
2004-12-04 18:54:56 +01:00
|
|
|
static byte _network_clients_connected = 0;
|
2009-03-15 01:32:18 +01:00
|
|
|
/* The identifier counter for new clients (is never decreased) */
|
2008-12-22 13:59:31 +01:00
|
|
|
static ClientID _network_client_id = CLIENT_ID_FIRST;
|
2004-08-27 13:01:26 +02:00
|
|
|
|
2004-12-19 11:17:26 +01:00
|
|
|
/* Some externs / forwards */
|
2007-03-07 12:47:46 +01:00
|
|
|
extern void StateGameLoop();
|
2004-12-19 11:17:26 +01:00
|
|
|
|
2008-12-22 22:38:03 +01: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 12:06:52 +01:00
|
|
|
return IsValidNetworkClientInfoIndex(index) ? GetNetworkClientInfo(index) : NULL;
|
2008-12-22 22:38:03 +01: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 22:30:21 +01:00
|
|
|
NetworkClientInfo *NetworkFindClientInfoFromClientID(ClientID client_id)
|
2004-09-10 21:02:27 +02:00
|
|
|
{
|
2004-12-04 18:54:56 +01:00
|
|
|
NetworkClientInfo *ci;
|
2004-08-27 13:01:26 +02:00
|
|
|
|
2008-12-23 10:02:41 +01:00
|
|
|
FOR_ALL_CLIENT_INFOS(ci) {
|
2008-12-22 13:59:31 +01:00
|
|
|
if (ci->client_id == client_id) return ci;
|
2006-06-27 23:25:53 +02:00
|
|
|
}
|
2004-08-27 13:01:26 +02:00
|
|
|
|
2004-12-04 18:54:56 +01:00
|
|
|
return NULL;
|
2004-08-22 12:23:37 +02:00
|
|
|
}
|
|
|
|
|
2008-12-22 22:38:03 +01:00
|
|
|
/**
|
|
|
|
* Return the CI for a given IP
|
2006-01-19 16:58:57 +01:00
|
|
|
* @param ip IP of the client we are looking for. This must be in string-format
|
2008-12-22 22:38:03 +01:00
|
|
|
* @return return a pointer to the corresponding NetworkClientInfo struct or NULL when not found
|
|
|
|
*/
|
2006-01-19 16:58:57 +01:00
|
|
|
NetworkClientInfo *NetworkFindClientInfoFromIP(const char *ip)
|
|
|
|
{
|
|
|
|
NetworkClientInfo *ci;
|
2009-04-04 02:48:48 +02:00
|
|
|
NetworkAddress address(ip);
|
|
|
|
|
|
|
|
if (address.GetAddressLength() == 0) return NULL;
|
2006-01-19 16:58:57 +01:00
|
|
|
|
2008-12-23 10:02:41 +01:00
|
|
|
FOR_ALL_CLIENT_INFOS(ci) {
|
2009-04-04 02:48:48 +02:00
|
|
|
if (ci->client_address == address) return ci;
|
2006-01-19 16:58:57 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2008-12-22 22:38:03 +01:00
|
|
|
/**
|
|
|
|
* Return the client state given it's client-identifier
|
|
|
|
* @param client_id the ClientID to search for
|
2008-12-23 10:47:42 +01:00
|
|
|
* @return return a pointer to the corresponding NetworkClientSocket struct or NULL when not found
|
2008-12-22 22:38:03 +01:00
|
|
|
*/
|
2008-12-23 10:47:42 +01:00
|
|
|
NetworkClientSocket *NetworkFindClientStateFromClientID(ClientID client_id)
|
2004-12-04 18:54:56 +01:00
|
|
|
{
|
2008-12-23 10:47:42 +01:00
|
|
|
NetworkClientSocket *cs;
|
2004-08-09 19:04:08 +02:00
|
|
|
|
2008-12-23 10:02:41 +01:00
|
|
|
FOR_ALL_CLIENT_SOCKETS(cs) {
|
2008-12-22 13:59:31 +01:00
|
|
|
if (cs->client_id == client_id) return cs;
|
2006-06-27 23:25:53 +02:00
|
|
|
}
|
2004-09-07 00:46:02 +02:00
|
|
|
|
2004-12-04 18:54:56 +01:00
|
|
|
return NULL;
|
2004-09-07 00:46:02 +02:00
|
|
|
}
|
|
|
|
|
2009-03-15 01:32:18 +01: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 10:47:42 +01:00
|
|
|
void NetworkGetClientName(char *client_name, size_t size, const NetworkClientSocket *cs)
|
2004-09-08 21:20:46 +02:00
|
|
|
{
|
2008-12-23 09:39:30 +01:00
|
|
|
const NetworkClientInfo *ci = cs->GetInfo();
|
2006-06-27 23:25:53 +02:00
|
|
|
|
2008-12-23 09:39:30 +01:00
|
|
|
if (StrEmpty(ci->client_name)) {
|
2008-12-22 13:59:31 +01:00
|
|
|
snprintf(client_name, size, "Client #%4d", cs->client_id);
|
2006-06-27 23:25:53 +02:00
|
|
|
} else {
|
2006-04-22 11:46:31 +02:00
|
|
|
ttd_strlcpy(client_name, ci->client_name, size);
|
2006-06-27 23:25:53 +02:00
|
|
|
}
|
2004-09-07 00:46:02 +02:00
|
|
|
}
|
2004-09-12 16:12:33 +02:00
|
|
|
|
2007-03-07 12:47:46 +01:00
|
|
|
byte NetworkSpectatorCount()
|
2006-01-31 23:16:15 +01:00
|
|
|
{
|
2008-12-23 09:39:30 +01:00
|
|
|
const NetworkClientInfo *ci;
|
2006-01-31 23:16:15 +01:00
|
|
|
byte count = 0;
|
|
|
|
|
2008-12-23 09:39:30 +01:00
|
|
|
FOR_ALL_CLIENT_INFOS(ci) {
|
|
|
|
if (ci->client_playas == COMPANY_SPECTATOR) count++;
|
2006-01-31 23:16:15 +01:00
|
|
|
}
|
|
|
|
|
2009-01-21 11:46:21 +01:00
|
|
|
/* Don't count a dedicated server as spectator */
|
|
|
|
if (_network_dedicated) count--;
|
|
|
|
|
2006-01-31 23:16:15 +01:00
|
|
|
return count;
|
|
|
|
}
|
|
|
|
|
2009-01-23 23:18:06 +01: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);
|
|
|
|
}
|
|
|
|
|
2009-03-15 01:32:18 +01:00
|
|
|
/* This puts a text-message to the console, or in the future, the chat-box,
|
|
|
|
* (to keep it all a bit more general)
|
|
|
|
* If 'self_send' is true, this is the client who is sending the message */
|
2009-02-09 03:09:47 +01:00
|
|
|
void NetworkTextMessage(NetworkAction action, ConsoleColour colour, bool self_send, const char *name, const char *str, int64 data)
|
2004-09-08 21:20:46 +02:00
|
|
|
{
|
2004-12-04 18:54:56 +01:00
|
|
|
const int duration = 10; // Game days the messages stay visible
|
2004-08-22 12:23:37 +02:00
|
|
|
|
2008-12-29 11:37:53 +01:00
|
|
|
StringID strid;
|
2004-12-04 18:54:56 +01:00
|
|
|
switch (action) {
|
2006-10-25 00:23:08 +02:00
|
|
|
case NETWORK_ACTION_SERVER_MESSAGE:
|
2008-12-29 11:37:53 +01:00
|
|
|
/* Ignore invalid messages */
|
|
|
|
if (data >= NETWORK_SERVER_MESSAGE_END) return;
|
|
|
|
|
|
|
|
strid = STR_NETWORK_SERVER_MESSAGE;
|
2009-02-09 03:09:47 +01:00
|
|
|
colour = CC_DEFAULT;
|
2008-12-29 11:37:53 +01:00
|
|
|
data = STR_NETWORK_SERVER_MESSAGE_GAME_PAUSED_PLAYERS + data;
|
2004-12-04 18:54:56 +01:00
|
|
|
break;
|
2009-01-23 23:18:06 +01:00
|
|
|
case NETWORK_ACTION_COMPANY_SPECTATOR:
|
2009-02-09 03:09:47 +01:00
|
|
|
colour = CC_DEFAULT;
|
2009-01-23 23:18:06 +01:00
|
|
|
strid = STR_NETWORK_CLIENT_COMPANY_SPECTATE;
|
|
|
|
break;
|
|
|
|
case NETWORK_ACTION_COMPANY_JOIN:
|
2009-02-09 03:09:47 +01:00
|
|
|
colour = CC_DEFAULT;
|
2009-01-23 23:18:06 +01:00
|
|
|
strid = STR_NETWORK_CLIENT_COMPANY_JOIN;
|
|
|
|
break;
|
|
|
|
case NETWORK_ACTION_COMPANY_NEW:
|
2009-02-09 03:09:47 +01:00
|
|
|
colour = CC_DEFAULT;
|
2009-01-23 23:18:06 +01:00
|
|
|
strid = STR_NETWORK_CLIENT_COMPANY_NEW;
|
|
|
|
break;
|
2008-12-29 11:37:53 +01: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 23:48:57 +01:00
|
|
|
default: strid = STR_NETWORK_CHAT_ALL; break;
|
2004-12-04 18:54:56 +01:00
|
|
|
}
|
2004-12-19 16:14:55 +01:00
|
|
|
|
2008-12-29 11:37:53 +01:00
|
|
|
char message[1024];
|
|
|
|
SetDParamStr(0, name);
|
|
|
|
SetDParamStr(1, str);
|
|
|
|
SetDParam(2, data);
|
|
|
|
GetString(message, strid, lastof(message));
|
|
|
|
|
2008-12-29 22:50:25 +01:00
|
|
|
DEBUG(desync, 1, "msg: %d; %d; %s\n", _date, _date_fract, message);
|
2009-02-09 03:09:47 +01:00
|
|
|
IConsolePrintF(colour, "%s", message);
|
|
|
|
NetworkAddChatMessage((TextColour)colour, duration, "%s", message);
|
2004-09-11 21:34:11 +02:00
|
|
|
}
|
|
|
|
|
2009-03-15 01:32:18 +01:00
|
|
|
/* Calculate the frame-lag of a client */
|
2008-12-23 10:47:42 +01:00
|
|
|
uint NetworkCalculateLag(const NetworkClientSocket *cs)
|
2004-09-12 00:10:31 +02:00
|
|
|
{
|
2004-12-04 18:54:56 +01:00
|
|
|
int lag = cs->last_frame_server - cs->last_frame;
|
2009-03-15 01:32:18 +01:00
|
|
|
/* 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 22:21:28 +02: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-12 00:10:31 +02:00
|
|
|
|
2004-12-04 18:54:56 +01:00
|
|
|
return lag;
|
2004-09-12 00:10:31 +02:00
|
|
|
}
|
|
|
|
|
2004-12-04 18:54:56 +01:00
|
|
|
|
2009-03-15 01:32:18 +01:00
|
|
|
/* There was a non-recoverable error, drop back to the main menu with a nice
|
|
|
|
* error */
|
2005-01-22 23:47:58 +01:00
|
|
|
static void NetworkError(StringID error_string)
|
2004-08-09 19:04:08 +02:00
|
|
|
{
|
2004-12-04 18:54:56 +01:00
|
|
|
_switch_mode = SM_MENU;
|
2008-04-18 12:16:51 +02:00
|
|
|
extern StringID _switch_mode_errorstr;
|
2004-12-04 18:54:56 +01:00
|
|
|
_switch_mode_errorstr = error_string;
|
|
|
|
}
|
2004-08-09 19:04:08 +02:00
|
|
|
|
2005-01-14 20:44:42 +01:00
|
|
|
static void ServerStartError(const char *error)
|
|
|
|
{
|
2006-12-26 18:36:18 +01:00
|
|
|
DEBUG(net, 0, "[server] could not start network: %s",error);
|
2004-12-04 18:54:56 +01:00
|
|
|
NetworkError(STR_NETWORK_ERR_SERVER_START);
|
|
|
|
}
|
2004-08-09 19:04:08 +02:00
|
|
|
|
2009-01-10 01:31:47 +01:00
|
|
|
static void NetworkClientError(NetworkRecvStatus res, NetworkClientSocket *cs)
|
2006-06-27 23:25:53 +02:00
|
|
|
{
|
2009-03-15 01:32:18 +01:00
|
|
|
/* First, send a CLIENT_ERROR to the server, so he knows we are
|
|
|
|
* disconnection (and why!) */
|
2004-12-04 18:54:56 +01:00
|
|
|
NetworkErrorCode errorno;
|
2004-08-09 19:04:08 +02:00
|
|
|
|
2009-03-15 01:32:18 +01:00
|
|
|
/* We just want to close the connection.. */
|
2004-12-04 18:54:56 +01:00
|
|
|
if (res == NETWORK_RECV_STATUS_CLOSE_QUERY) {
|
2009-04-07 22:27:13 +02:00
|
|
|
cs->NetworkSocketHandler::CloseConnection();
|
2004-12-19 11:17:26 +01:00
|
|
|
NetworkCloseClient(cs);
|
2004-12-04 18:54:56 +01:00
|
|
|
_networking = false;
|
2004-09-08 21:20:46 +02:00
|
|
|
|
2004-12-04 18:54:56 +01:00
|
|
|
DeleteWindowById(WC_NETWORK_STATUS_WINDOW, 0);
|
|
|
|
return;
|
2004-08-09 19:04:08 +02:00
|
|
|
}
|
|
|
|
|
2006-02-01 08:36:15 +01:00
|
|
|
switch (res) {
|
2007-01-30 18:22:56 +01: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 19:04:08 +02:00
|
|
|
}
|
2009-03-15 01:32:18 +01:00
|
|
|
/* This means we fucked up and the server closed the connection */
|
2005-01-02 13:03:43 +01:00
|
|
|
if (res != NETWORK_RECV_STATUS_SERVER_ERROR && res != NETWORK_RECV_STATUS_SERVER_FULL &&
|
|
|
|
res != NETWORK_RECV_STATUS_SERVER_BANNED) {
|
2004-12-04 18:54:56 +01:00
|
|
|
SEND_COMMAND(PACKET_CLIENT_ERROR)(errorno);
|
|
|
|
}
|
2004-08-09 19:04:08 +02:00
|
|
|
|
2004-12-04 18:54:56 +01:00
|
|
|
_switch_mode = SM_MENU;
|
2004-12-19 11:17:26 +01:00
|
|
|
NetworkCloseClient(cs);
|
2004-12-04 18:54:56 +01:00
|
|
|
_networking = false;
|
2004-08-09 19:04:08 +02:00
|
|
|
}
|
|
|
|
|
2008-12-29 11:37:53 +01:00
|
|
|
/**
|
|
|
|
* Retrieve the string id of an internal error number
|
2006-06-27 23:25:53 +02:00
|
|
|
* @param err NetworkErrorCode
|
2008-12-29 11:37:53 +01:00
|
|
|
* @return the StringID
|
|
|
|
*/
|
|
|
|
StringID GetNetworkErrorMsg(NetworkErrorCode err)
|
2006-04-03 20:31:01 +02: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 18:22:56 +01:00
|
|
|
STR_NETWORK_ERR_CLIENT_NEWGRF_MISMATCH,
|
2006-04-03 20:31:01 +02: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 23:18:28 +02:00
|
|
|
STR_NETWORK_ERR_CLIENT_COMPANY_MISMATCH,
|
2006-04-03 20:31:01 +02:00
|
|
|
STR_NETWORK_ERR_CLIENT_KICKED,
|
|
|
|
STR_NETWORK_ERR_CLIENT_CHEATER,
|
2006-06-27 23:25:53 +02:00
|
|
|
STR_NETWORK_ERR_CLIENT_SERVER_FULL
|
2006-04-03 20:31:01 +02:00
|
|
|
};
|
|
|
|
|
2007-01-10 19:56:51 +01:00
|
|
|
if (err >= (ptrdiff_t)lengthof(network_error_strings)) err = NETWORK_ERROR_GENERAL;
|
2006-04-03 20:31:01 +02:00
|
|
|
|
2008-12-29 22:41:20 +01:00
|
|
|
return network_error_strings[err];
|
2006-04-03 20:31:01 +02:00
|
|
|
}
|
|
|
|
|
2006-10-03 18:15:34 +02:00
|
|
|
/* Count the number of active clients connected */
|
2008-09-30 22:39:50 +02:00
|
|
|
static uint NetworkCountActiveClients()
|
2006-10-03 18:15:34 +02:00
|
|
|
{
|
2008-12-23 09:39:30 +01:00
|
|
|
const NetworkClientInfo *ci;
|
2006-10-03 18:15:34 +02:00
|
|
|
uint count = 0;
|
|
|
|
|
2008-12-23 09:39:30 +01:00
|
|
|
FOR_ALL_CLIENT_INFOS(ci) {
|
2008-09-30 22:39:50 +02:00
|
|
|
if (IsValidCompanyID(ci->client_playas)) count++;
|
2006-10-03 18:15:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return count;
|
|
|
|
}
|
|
|
|
|
2008-09-30 22:39:50 +02:00
|
|
|
static bool _min_active_clients_paused = false;
|
2006-10-03 18:15:34 +02:00
|
|
|
|
2008-09-30 22:39:50 +02:00
|
|
|
/* Check if the minimum number of active clients has been reached and pause or unpause the game as appropriate */
|
2009-03-20 00:32:39 +01:00
|
|
|
static void CheckMinActiveClients()
|
2006-10-03 18:15:34 +02:00
|
|
|
{
|
|
|
|
if (!_network_dedicated) return;
|
|
|
|
|
2008-09-30 22:39:50 +02:00
|
|
|
if (NetworkCountActiveClients() < _settings_client.network.min_active_clients) {
|
|
|
|
if (_min_active_clients_paused) return;
|
2006-10-03 18:15:34 +02:00
|
|
|
|
2008-09-30 22:39:50 +02:00
|
|
|
_min_active_clients_paused = true;
|
2008-12-28 15:37:19 +01:00
|
|
|
DoCommandP(0, 1, 0, CMD_PAUSE);
|
2008-12-29 11:37:53 +01:00
|
|
|
NetworkServerSendChat(NETWORK_ACTION_SERVER_MESSAGE, DESTTYPE_BROADCAST, 0, "", CLIENT_ID_SERVER, NETWORK_SERVER_MESSAGE_GAME_PAUSED_PLAYERS);
|
2006-10-03 18:15:34 +02:00
|
|
|
} else {
|
2008-09-30 22:39:50 +02:00
|
|
|
if (!_min_active_clients_paused) return;
|
2006-10-03 18:15:34 +02:00
|
|
|
|
2008-09-30 22:39:50 +02:00
|
|
|
_min_active_clients_paused = false;
|
2008-12-28 15:37:19 +01:00
|
|
|
DoCommandP(0, 0, 0, CMD_PAUSE);
|
2008-12-29 11:37:53 +01:00
|
|
|
NetworkServerSendChat(NETWORK_ACTION_SERVER_MESSAGE, DESTTYPE_BROADCAST, 0, "", CLIENT_ID_SERVER, NETWORK_SERVER_MESSAGE_GAME_UNPAUSED_PLAYERS);
|
2006-10-03 18:15:34 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-09-30 22:39:50 +02:00
|
|
|
/** Converts a string to ip/port/company
|
|
|
|
* Format: IP#company:port
|
2007-01-17 00:01:06 +01:00
|
|
|
*
|
2008-09-30 22:39:50 +02: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-17 00:01:06 +01:00
|
|
|
* occupied by connection_string. */
|
2008-09-30 22:39:50 +02:00
|
|
|
void ParseConnectionString(const char **company, const char **port, char *connection_string)
|
2004-09-12 16:12:33 +02:00
|
|
|
{
|
2005-02-06 23:25:27 +01:00
|
|
|
char *p;
|
2004-12-04 18:54:56 +01:00
|
|
|
for (p = connection_string; *p != '\0'; p++) {
|
2007-01-17 00:01:06 +01:00
|
|
|
switch (*p) {
|
|
|
|
case '#':
|
2008-09-30 22:39:50 +02:00
|
|
|
*company = p + 1;
|
2007-01-17 00:01:06 +01:00
|
|
|
*p = '\0';
|
|
|
|
break;
|
|
|
|
case ':':
|
|
|
|
*port = p + 1;
|
|
|
|
*p = '\0';
|
|
|
|
break;
|
2004-09-11 21:34:11 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-03-15 01:32:18 +01:00
|
|
|
/* Creates a new client from a socket
|
|
|
|
* Used both by the server and the client */
|
2008-12-23 10:47:42 +01:00
|
|
|
static NetworkClientSocket *NetworkAllocClient(SOCKET s)
|
2004-09-11 21:34:11 +02:00
|
|
|
{
|
2004-12-04 18:54:56 +01:00
|
|
|
if (_network_server) {
|
2009-03-15 01:32:18 +01:00
|
|
|
/* Can we handle a new client? */
|
2006-10-18 01:34:12 +02:00
|
|
|
if (_network_clients_connected >= MAX_CLIENTS) return NULL;
|
2008-06-03 10:04:35 +02:00
|
|
|
if (_network_game_info.clients_on >= _settings_client.network.max_clients) return NULL;
|
2004-08-09 19:04:08 +02:00
|
|
|
|
2009-03-15 01:32:18 +01:00
|
|
|
/* Register the login */
|
2008-12-23 21:52:27 +01:00
|
|
|
_network_clients_connected++;
|
2004-12-04 18:54:56 +01:00
|
|
|
}
|
2004-08-09 19:04:08 +02:00
|
|
|
|
2008-12-23 21:52:27 +01:00
|
|
|
NetworkClientSocket *cs = new NetworkClientSocket(INVALID_CLIENT_ID);
|
2007-01-12 21:19:49 +01:00
|
|
|
cs->sock = s;
|
2005-03-29 21:10:13 +02:00
|
|
|
cs->last_frame = _frame_counter;
|
|
|
|
cs->last_frame_server = _frame_counter;
|
|
|
|
|
2004-12-04 18:54:56 +01:00
|
|
|
if (_network_server) {
|
2008-12-22 13:59:31 +01:00
|
|
|
cs->client_id = _network_client_id++;
|
2008-12-23 21:52:27 +01:00
|
|
|
NetworkClientInfo *ci = new NetworkClientInfo(cs->client_id);
|
|
|
|
cs->SetInfo(ci);
|
2008-09-30 22:39:50 +02:00
|
|
|
ci->client_playas = COMPANY_INACTIVE_CLIENT;
|
2004-12-04 18:54:56 +01:00
|
|
|
ci->join_date = _date;
|
2004-08-09 19:04:08 +02:00
|
|
|
|
2004-12-04 18:54:56 +01:00
|
|
|
InvalidateWindow(WC_CLIENT_LIST, 0);
|
|
|
|
}
|
2004-08-22 12:23:37 +02:00
|
|
|
|
2004-12-04 18:54:56 +01:00
|
|
|
return cs;
|
2004-08-09 19:04:08 +02:00
|
|
|
}
|
|
|
|
|
2009-03-15 01:32:18 +01:00
|
|
|
/* Close a connection */
|
2008-12-23 10:47:42 +01:00
|
|
|
void NetworkCloseClient(NetworkClientSocket *cs)
|
2004-08-09 19:04:08 +02:00
|
|
|
{
|
2009-03-08 17:14:14 +01:00
|
|
|
/*
|
|
|
|
* Sending a message just before leaving the game calls cs->Send_Packets.
|
|
|
|
* This might invoke this function, which means that when we close the
|
|
|
|
* connection after cs->Send_Packets we will close an already closed
|
|
|
|
* connection. This handles that case gracefully without having to make
|
|
|
|
* that code any more complex or more aware of the validity of the socket.
|
|
|
|
*/
|
|
|
|
if (cs->sock == INVALID_SOCKET) return;
|
2004-08-09 19:04:08 +02:00
|
|
|
|
2008-12-22 13:59:31 +01:00
|
|
|
DEBUG(net, 1, "Closed client connection %d", cs->client_id);
|
2004-08-09 19:04:08 +02:00
|
|
|
|
2009-04-07 20:23:14 +02:00
|
|
|
if (!cs->HasClientQuit() && _network_server && cs->status > STATUS_INACTIVE) {
|
2009-03-15 01:32:18 +01:00
|
|
|
/* We did not receive a leave message from this client... */
|
2006-04-22 11:46:31 +02:00
|
|
|
char client_name[NETWORK_CLIENT_NAME_LENGTH];
|
2008-12-23 10:47:42 +01:00
|
|
|
NetworkClientSocket *new_cs;
|
2004-08-09 19:04:08 +02:00
|
|
|
|
2004-12-04 18:54:56 +01:00
|
|
|
NetworkGetClientName(client_name, sizeof(client_name), cs);
|
2004-08-09 19:04:08 +02:00
|
|
|
|
2008-12-29 11:37:53 +01:00
|
|
|
NetworkTextMessage(NETWORK_ACTION_LEAVE, CC_DEFAULT, false, client_name, NULL, STR_NETWORK_ERR_CLIENT_CONNECTION_LOST);
|
2004-08-09 19:04:08 +02:00
|
|
|
|
2009-03-15 01:32:18 +01:00
|
|
|
/* Inform other clients of this... strange leaving ;) */
|
2008-12-23 10:02:41 +01:00
|
|
|
FOR_ALL_CLIENT_SOCKETS(new_cs) {
|
2004-12-04 18:54:56 +01:00
|
|
|
if (new_cs->status > STATUS_AUTH && cs != new_cs) {
|
2008-12-29 11:37:53 +01:00
|
|
|
SEND_COMMAND(PACKET_SERVER_ERROR_QUIT)(new_cs, cs->client_id, NETWORK_ERROR_CONNECTION_LOST);
|
2004-08-09 19:04:08 +02:00
|
|
|
}
|
|
|
|
}
|
2005-03-30 10:50:34 +02:00
|
|
|
}
|
2005-03-29 21:10:13 +02:00
|
|
|
|
2005-03-30 10:50:34 +02:00
|
|
|
/* When the client was PRE_ACTIVE, the server was in pause mode, so unpause */
|
2008-05-29 22:21:28 +02:00
|
|
|
if (cs->status == STATUS_PRE_ACTIVE && _settings_client.network.pause_on_join) {
|
2008-12-28 15:37:19 +01:00
|
|
|
DoCommandP(0, 0, 0, CMD_PAUSE);
|
2008-12-29 11:37:53 +01:00
|
|
|
NetworkServerSendChat(NETWORK_ACTION_SERVER_MESSAGE, DESTTYPE_BROADCAST, 0, "", CLIENT_ID_SERVER, NETWORK_SERVER_MESSAGE_GAME_UNPAUSED_CONNECT_FAIL);
|
2004-08-09 19:04:08 +02:00
|
|
|
}
|
|
|
|
|
2004-12-04 18:54:56 +01:00
|
|
|
if (_network_server) {
|
2009-03-15 01:32:18 +01:00
|
|
|
/* We just lost one client :( */
|
2007-03-20 21:11:17 +01:00
|
|
|
if (cs->status >= STATUS_AUTH) _network_game_info.clients_on--;
|
2004-12-04 18:54:56 +01:00
|
|
|
_network_clients_connected--;
|
2004-08-09 19:04:08 +02:00
|
|
|
|
2004-12-04 18:54:56 +01:00
|
|
|
InvalidateWindow(WC_CLIENT_LIST, 0);
|
2004-09-11 21:34:11 +02:00
|
|
|
}
|
|
|
|
|
2008-12-23 21:52:27 +01:00
|
|
|
delete cs->GetInfo();
|
|
|
|
delete cs;
|
2004-08-09 19:04:08 +02:00
|
|
|
}
|
|
|
|
|
2009-03-15 01:32:18 +01:00
|
|
|
/* For the server, to accept new clients */
|
2009-04-08 02:05:16 +02:00
|
|
|
static void NetworkAcceptClients(SOCKET ls)
|
2004-08-09 19:04:08 +02:00
|
|
|
{
|
2008-12-23 10:47:42 +01:00
|
|
|
NetworkClientSocket *cs;
|
2005-01-02 13:03:43 +01:00
|
|
|
bool banned;
|
2004-08-09 19:04:08 +02:00
|
|
|
|
2004-12-04 18:54:56 +01:00
|
|
|
for (;;) {
|
2009-04-04 02:47:08 +02:00
|
|
|
struct sockaddr_storage sin;
|
2009-04-03 01:03:18 +02:00
|
|
|
memset(&sin, 0, sizeof(sin));
|
2006-10-18 01:34:12 +02:00
|
|
|
socklen_t sin_len = sizeof(sin);
|
2009-04-08 02:05:16 +02:00
|
|
|
SOCKET s = accept(ls, (struct sockaddr*)&sin, &sin_len);
|
2004-08-09 19:04:08 +02:00
|
|
|
if (s == INVALID_SOCKET) return;
|
|
|
|
|
2005-02-07 10:56:16 +01:00
|
|
|
SetNonBlocking(s); // XXX error handling?
|
2004-08-09 19:04:08 +02:00
|
|
|
|
2009-04-04 02:47:08 +02:00
|
|
|
NetworkAddress address(sin, sin_len);
|
|
|
|
DEBUG(net, 1, "Client connected from %s on frame %d", address.GetHostname(), _frame_counter);
|
2004-08-09 19:04:08 +02:00
|
|
|
|
2005-02-07 10:56:16 +01:00
|
|
|
SetNoDelay(s); // XXX error handling?
|
2004-08-09 19:04:08 +02:00
|
|
|
|
2005-01-02 13:03:43 +01:00
|
|
|
/* Check if the client is banned */
|
|
|
|
banned = false;
|
2009-04-03 14:49:58 +02:00
|
|
|
for (char **iter = _network_ban_list.Begin(); iter != _network_ban_list.End(); iter++) {
|
2009-04-04 02:47:08 +02:00
|
|
|
banned = address.IsInNetmask(*iter);
|
2009-01-15 19:11:26 +01:00
|
|
|
if (banned) {
|
2007-02-01 23:30:35 +01:00
|
|
|
Packet p(PACKET_SERVER_BANNED);
|
|
|
|
p.PrepareToSend();
|
2005-01-02 13:03:43 +01:00
|
|
|
|
2009-04-03 14:49:58 +02:00
|
|
|
DEBUG(net, 1, "Banned ip tried to join (%s), refused", *iter);
|
2005-01-02 13:03:43 +01:00
|
|
|
|
2007-02-01 23:30:35 +01:00
|
|
|
send(s, (const char*)p.buffer, p.size, 0);
|
2005-01-02 13:03:43 +01:00
|
|
|
closesocket(s);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/* If this client is banned, continue with next client */
|
2006-10-18 01:34:12 +02:00
|
|
|
if (banned) continue;
|
2005-01-02 13:03:43 +01:00
|
|
|
|
2004-12-19 11:17:26 +01:00
|
|
|
cs = NetworkAllocClient(s);
|
2004-08-09 19:04:08 +02:00
|
|
|
if (cs == NULL) {
|
2009-03-15 01:32:18 +01:00
|
|
|
/* no more clients allowed?
|
|
|
|
* Send to the client that we are full! */
|
2007-02-01 23:30:35 +01:00
|
|
|
Packet p(PACKET_SERVER_FULL);
|
|
|
|
p.PrepareToSend();
|
2004-12-04 18:54:56 +01:00
|
|
|
|
2007-02-01 23:30:35 +01:00
|
|
|
send(s, (const char*)p.buffer, p.size, 0);
|
2004-08-09 19:04:08 +02:00
|
|
|
closesocket(s);
|
2004-12-04 18:54:56 +01:00
|
|
|
|
2004-08-09 19:04:08 +02:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2009-03-15 01:32:18 +01: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 */
|
2004-12-04 18:54:56 +01:00
|
|
|
cs->status = STATUS_INACTIVE;
|
|
|
|
|
2009-04-04 02:48:48 +02:00
|
|
|
cs->GetInfo()->client_address = address; // Save the IP of the client
|
2004-08-09 19:04:08 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-03-15 01:32:18 +01:00
|
|
|
/* Set up the listen socket for the server */
|
2007-03-07 12:47:46 +01:00
|
|
|
static bool NetworkListen()
|
2004-08-09 19:04:08 +02:00
|
|
|
{
|
2009-04-08 02:05:16 +02:00
|
|
|
assert(_listensockets.Length() == 0);
|
|
|
|
|
2009-04-03 03:24:52 +02:00
|
|
|
NetworkAddress address(_settings_client.network.server_bind_ip, _settings_client.network.server_port);
|
2004-08-09 19:04:08 +02:00
|
|
|
|
2009-04-08 02:05:16 +02:00
|
|
|
address.Listen(SOCK_STREAM, &_listensockets);
|
2004-08-09 19:04:08 +02:00
|
|
|
|
2009-04-08 02:05:16 +02:00
|
|
|
if (_listensockets.Length() == 0) {
|
2009-04-03 03:24:52 +02:00
|
|
|
ServerStartError("Could not create listening socket");
|
2004-12-04 18:54:56 +01:00
|
|
|
return false;
|
2004-08-09 19:04:08 +02:00
|
|
|
}
|
|
|
|
|
2004-12-04 18:54:56 +01:00
|
|
|
return true;
|
2004-08-09 19:04:08 +02:00
|
|
|
}
|
|
|
|
|
2009-03-07 23:40:47 +01:00
|
|
|
/** Resets both pools used for network clients */
|
|
|
|
static void InitializeNetworkPools()
|
|
|
|
{
|
|
|
|
_NetworkClientSocket_pool.CleanPool();
|
|
|
|
_NetworkClientSocket_pool.AddBlockToPool();
|
|
|
|
_NetworkClientInfo_pool.CleanPool();
|
|
|
|
_NetworkClientInfo_pool.AddBlockToPool();
|
|
|
|
}
|
|
|
|
|
2009-03-15 01:32:18 +01:00
|
|
|
/* Close all current connections */
|
2007-03-07 12:47:46 +01:00
|
|
|
static void NetworkClose()
|
2004-08-09 19:04:08 +02:00
|
|
|
{
|
2008-12-23 10:47:42 +01:00
|
|
|
NetworkClientSocket *cs;
|
2004-08-09 19:04:08 +02:00
|
|
|
|
2008-12-23 10:02:41 +01:00
|
|
|
FOR_ALL_CLIENT_SOCKETS(cs) {
|
2004-12-04 18:54:56 +01:00
|
|
|
if (!_network_server) {
|
2008-12-29 11:37:53 +01:00
|
|
|
SEND_COMMAND(PACKET_CLIENT_QUIT)();
|
2007-02-02 00:50:15 +01:00
|
|
|
cs->Send_Packets();
|
2004-08-09 19:04:08 +02:00
|
|
|
}
|
2004-12-19 11:17:26 +01:00
|
|
|
NetworkCloseClient(cs);
|
2004-08-09 19:04:08 +02:00
|
|
|
}
|
|
|
|
|
2004-12-04 18:54:56 +01:00
|
|
|
if (_network_server) {
|
2008-05-30 22:08:32 +02:00
|
|
|
/* We are a server, also close the listensocket */
|
2009-04-08 02:05:16 +02:00
|
|
|
for (SocketList::iterator s = _listensockets.Begin(); s != _listensockets.End(); s++) {
|
|
|
|
closesocket(s->second);
|
|
|
|
}
|
|
|
|
_listensockets.Clear();
|
2006-12-26 18:36:18 +01:00
|
|
|
DEBUG(net, 1, "Closed listener");
|
2004-08-09 19:04:08 +02:00
|
|
|
}
|
2008-05-30 22:08:32 +02:00
|
|
|
NetworkUDPCloseAll();
|
|
|
|
|
2009-01-20 12:28:18 +01:00
|
|
|
TCPConnecter::KillAll();
|
|
|
|
|
2008-05-30 22:08:32 +02:00
|
|
|
_networking = false;
|
|
|
|
_network_server = false;
|
2008-12-22 21:42:02 +01:00
|
|
|
|
2009-02-20 01:03:29 +01:00
|
|
|
NetworkFreeLocalCommandQueue();
|
|
|
|
|
2008-12-22 21:42:02 +01:00
|
|
|
free(_network_company_states);
|
|
|
|
_network_company_states = NULL;
|
2008-12-23 21:52:27 +01:00
|
|
|
|
2009-03-07 23:40:47 +01:00
|
|
|
InitializeNetworkPools();
|
2004-08-09 19:04:08 +02:00
|
|
|
}
|
|
|
|
|
2009-03-15 01:32:18 +01:00
|
|
|
/* Inits the network (cleans sockets and stuff) */
|
2007-03-07 12:47:46 +01:00
|
|
|
static void NetworkInitialize()
|
2004-08-09 19:04:08 +02:00
|
|
|
{
|
2009-03-07 23:40:47 +01:00
|
|
|
InitializeNetworkPools();
|
2004-08-22 12:23:37 +02:00
|
|
|
|
2004-12-04 18:54:56 +01:00
|
|
|
_sync_frame = 0;
|
|
|
|
_network_first_time = true;
|
2004-08-22 12:23:37 +02:00
|
|
|
|
2004-12-04 18:54:56 +01:00
|
|
|
_network_reconnect = 0;
|
2004-08-09 19:04:08 +02:00
|
|
|
}
|
|
|
|
|
2009-01-20 12:28:18 +01: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)();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2009-03-15 01:32:18 +01: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 02:32:06 +01:00
|
|
|
void NetworkTCPQueryServer(NetworkAddress address)
|
2004-08-09 19:04:08 +02:00
|
|
|
{
|
2007-02-02 22:04:50 +01:00
|
|
|
if (!_network_available) return;
|
2004-08-09 19:04:08 +02:00
|
|
|
|
2004-12-04 18:54:56 +01:00
|
|
|
NetworkDisconnect();
|
|
|
|
NetworkInitialize();
|
2004-08-09 19:04:08 +02:00
|
|
|
|
2009-01-20 12:28:18 +01:00
|
|
|
new TCPQueryConnecter(address);
|
2004-12-04 18:54:56 +01:00
|
|
|
}
|
2004-09-10 21:02:27 +02:00
|
|
|
|
2004-12-20 23:14:39 +01:00
|
|
|
/* Validates an address entered as a string and adds the server to
|
2006-01-25 20:03:50 +01:00
|
|
|
* the list. If you use this function, the games will be marked
|
2004-12-20 23:14:39 +01:00
|
|
|
* as manually added. */
|
2005-02-06 23:25:27 +01:00
|
|
|
void NetworkAddServer(const char *b)
|
2004-12-18 19:58:03 +01:00
|
|
|
{
|
|
|
|
if (*b != '\0') {
|
2005-02-06 23:25:27 +01:00
|
|
|
const char *port = NULL;
|
2008-09-30 22:39:50 +02:00
|
|
|
const char *company = NULL;
|
2005-02-06 23:25:27 +01:00
|
|
|
char host[NETWORK_HOSTNAME_LENGTH];
|
2004-12-18 19:58:03 +01:00
|
|
|
uint16 rport;
|
|
|
|
|
2008-11-02 12:20:15 +01:00
|
|
|
strecpy(host, b, lastof(host));
|
2004-12-19 11:17:26 +01:00
|
|
|
|
2008-11-02 12:20:15 +01:00
|
|
|
strecpy(_settings_client.network.connect_to_ip, b, lastof(_settings_client.network.connect_to_ip));
|
2004-12-18 19:58:03 +01:00
|
|
|
rport = NETWORK_DEFAULT_PORT;
|
|
|
|
|
2008-09-30 22:39:50 +02:00
|
|
|
ParseConnectionString(&company, &port, host);
|
2004-12-18 19:58:03 +01:00
|
|
|
if (port != NULL) rport = atoi(port);
|
|
|
|
|
2009-01-20 02:32:06 +01:00
|
|
|
NetworkUDPQueryServer(NetworkAddress(host, rport), true);
|
2004-12-20 23:14:39 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2004-12-21 17:56:43 +01:00
|
|
|
/* Generates the list of manually added hosts from NetworkGameList and
|
2004-12-20 23:14:39 +01:00
|
|
|
* dumps them into the array _network_host_list. This array is needed
|
|
|
|
* by the function that generates the config file. */
|
2007-03-07 12:47:46 +01:00
|
|
|
void NetworkRebuildHostList()
|
2004-12-20 23:14:39 +01:00
|
|
|
{
|
2009-04-03 14:49:58 +02:00
|
|
|
_network_host_list.Clear();
|
2004-12-20 23:14:39 +01:00
|
|
|
|
2009-04-03 14:49:58 +02:00
|
|
|
for (NetworkGameList *item = _network_game_list; item != NULL; item = item->next) {
|
|
|
|
if (item->manually) *_network_host_list.Append() = strdup(item->address.GetAddressAsString());
|
2004-12-18 19:58:03 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-01-20 12:28:18 +01: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();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
|
2009-03-15 01:32:18 +01:00
|
|
|
/* Used by clients, to connect to a server */
|
2009-01-20 02:32:06 +01:00
|
|
|
void NetworkClientConnectGame(NetworkAddress address)
|
2004-12-04 18:54:56 +01:00
|
|
|
{
|
2009-01-19 23:01:37 +01:00
|
|
|
if (!_network_available) return;
|
2004-08-09 19:04:08 +02:00
|
|
|
|
2009-01-20 02:32:06 +01:00
|
|
|
if (address.GetPort() == 0) return;
|
2004-08-09 19:04:08 +02:00
|
|
|
|
2009-01-20 02:32:06 +01: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 21:02:27 +02:00
|
|
|
|
2004-12-04 18:54:56 +01:00
|
|
|
NetworkDisconnect();
|
|
|
|
NetworkInitialize();
|
2004-08-09 19:04:08 +02:00
|
|
|
|
2009-01-20 12:28:18 +01:00
|
|
|
_network_join_status = NETWORK_JOIN_STATUS_CONNECTING;
|
|
|
|
ShowJoinStatusWindow();
|
2004-08-22 12:23:37 +02:00
|
|
|
|
2009-01-20 12:28:18 +01:00
|
|
|
new TCPClientConnecter(address);
|
2004-08-09 19:04:08 +02:00
|
|
|
}
|
|
|
|
|
2007-03-07 12:47:46 +01:00
|
|
|
static void NetworkInitGameInfo()
|
2004-09-12 16:12:33 +02:00
|
|
|
{
|
2008-06-03 10:04:35 +02: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 22:21:28 +02:00
|
|
|
}
|
2004-09-10 21:02:27 +02:00
|
|
|
|
2008-06-03 10:04:35 +02:00
|
|
|
/* The server is a client too */
|
|
|
|
_network_game_info.clients_on = _network_dedicated ? 0 : 1;
|
2008-05-29 17:13:28 +02:00
|
|
|
_network_game_info.start_date = ConvertYMDToDate(_settings_game.game_creation.starting_year, 0, 1);
|
2004-09-10 21:02:27 +02:00
|
|
|
|
2008-12-23 21:52:27 +01:00
|
|
|
NetworkClientInfo *ci = new NetworkClientInfo(CLIENT_ID_SERVER);
|
2008-09-30 22:39:50 +02:00
|
|
|
ci->client_playas = _network_dedicated ? COMPANY_SPECTATOR : _local_company;
|
2006-10-18 00:16:46 +02:00
|
|
|
|
2008-11-02 12:20:15 +01: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 19:04:08 +02:00
|
|
|
}
|
|
|
|
|
2007-03-07 12:47:46 +01:00
|
|
|
bool NetworkServerStart()
|
2004-09-12 16:12:33 +02:00
|
|
|
{
|
2004-12-04 18:54:56 +01:00
|
|
|
if (!_network_available) return false;
|
2004-09-10 21:02:27 +02:00
|
|
|
|
2004-12-13 18:47:21 +01: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 18:54:56 +01:00
|
|
|
NetworkInitialize();
|
2006-10-18 01:34:12 +02:00
|
|
|
if (!NetworkListen()) return false;
|
2004-08-09 19:04:08 +02:00
|
|
|
|
2009-03-15 01:32:18 +01:00
|
|
|
/* Try to start UDP-server */
|
2009-04-07 22:27:13 +02:00
|
|
|
_network_udp_server = _udp_server_socket->Listen();
|
2004-08-22 12:23:37 +02:00
|
|
|
|
2008-12-22 21:42:02 +01:00
|
|
|
_network_company_states = CallocT<NetworkCompanyState>(MAX_COMPANIES);
|
2004-12-04 18:54:56 +01:00
|
|
|
_network_server = true;
|
|
|
|
_networking = true;
|
|
|
|
_frame_counter = 0;
|
|
|
|
_frame_counter_server = 0;
|
|
|
|
_frame_counter_max = 0;
|
2005-07-16 14:59:23 +02:00
|
|
|
_last_sync_frame = 0;
|
2008-12-22 13:59:31 +01:00
|
|
|
_network_own_client_id = CLIENT_ID_SERVER;
|
2004-09-10 21:02:27 +02:00
|
|
|
|
2008-09-30 22:39:50 +02:00
|
|
|
/* Non-dedicated server will always be company #1 */
|
|
|
|
if (!_network_dedicated) _network_playas = COMPANY_FIRST;
|
2004-12-16 12:04:43 +01:00
|
|
|
|
2004-12-04 18:54:56 +01:00
|
|
|
_network_clients_connected = 0;
|
2004-08-22 12:23:37 +02:00
|
|
|
|
2004-12-04 18:54:56 +01:00
|
|
|
NetworkInitGameInfo();
|
2004-09-10 21:02:27 +02:00
|
|
|
|
2009-03-15 01:32:18 +01:00
|
|
|
/* execute server initialization script */
|
2004-12-04 18:54:56 +01:00
|
|
|
IConsoleCmdExec("exec scripts/on_server.scr 0");
|
2009-03-15 01:32:18 +01:00
|
|
|
/* if the server is dedicated ... add some other script */
|
2004-12-04 18:54:56 +01:00
|
|
|
if (_network_dedicated) IConsoleCmdExec("exec scripts/on_dedicated.scr 0");
|
2004-12-15 21:10:34 +01:00
|
|
|
|
2008-09-30 22:39:50 +02:00
|
|
|
_min_active_clients_paused = false;
|
2006-10-03 18:15:34 +02:00
|
|
|
|
2004-12-15 21:10:34 +01:00
|
|
|
/* Try to register us to the master server */
|
2006-01-19 18:50:40 +01:00
|
|
|
_network_last_advertise_frame = 0;
|
|
|
|
_network_need_advertise = true;
|
2004-12-15 21:10:34 +01:00
|
|
|
NetworkUDPAdvertise();
|
2004-12-04 18:54:56 +01:00
|
|
|
return true;
|
2004-08-09 19:04:08 +02:00
|
|
|
}
|
|
|
|
|
2009-03-15 01:32:18 +01:00
|
|
|
/* The server is rebooting...
|
|
|
|
* The only difference with NetworkDisconnect, is the packets that is sent */
|
2007-03-07 12:47:46 +01:00
|
|
|
void NetworkReboot()
|
2004-09-12 16:12:33 +02:00
|
|
|
{
|
2004-12-04 18:54:56 +01:00
|
|
|
if (_network_server) {
|
2008-12-23 10:47:42 +01:00
|
|
|
NetworkClientSocket *cs;
|
2008-12-23 10:02:41 +01:00
|
|
|
FOR_ALL_CLIENT_SOCKETS(cs) {
|
2004-12-04 18:54:56 +01:00
|
|
|
SEND_COMMAND(PACKET_SERVER_NEWGAME)(cs);
|
2007-02-02 00:50:15 +01:00
|
|
|
cs->Send_Packets();
|
2004-08-22 12:23:37 +02:00
|
|
|
}
|
2004-12-04 18:54:56 +01:00
|
|
|
}
|
2004-08-22 12:23:37 +02:00
|
|
|
|
2004-12-04 18:54:56 +01:00
|
|
|
NetworkClose();
|
2004-08-22 12:23:37 +02:00
|
|
|
}
|
|
|
|
|
2009-03-15 01:32:18 +01:00
|
|
|
/* We want to disconnect from the host/clients */
|
2007-03-07 12:47:46 +01:00
|
|
|
void NetworkDisconnect()
|
2004-09-12 16:12:33 +02:00
|
|
|
{
|
2004-12-04 18:54:56 +01:00
|
|
|
if (_network_server) {
|
2008-12-23 10:47:42 +01:00
|
|
|
NetworkClientSocket *cs;
|
2008-12-23 10:02:41 +01:00
|
|
|
FOR_ALL_CLIENT_SOCKETS(cs) {
|
2004-12-04 18:54:56 +01:00
|
|
|
SEND_COMMAND(PACKET_SERVER_SHUTDOWN)(cs);
|
2007-02-02 00:50:15 +01:00
|
|
|
cs->Send_Packets();
|
2004-12-04 18:54:56 +01:00
|
|
|
}
|
2004-09-12 16:12:33 +02:00
|
|
|
}
|
2004-08-27 13:01:26 +02:00
|
|
|
|
2008-06-09 14:17:01 +02:00
|
|
|
if (_settings_client.network.server_advertise) NetworkUDPRemoveAdvertise();
|
2004-12-22 19:56:52 +01:00
|
|
|
|
2004-12-12 14:46:10 +01:00
|
|
|
DeleteWindowById(WC_NETWORK_STATUS_WINDOW, 0);
|
|
|
|
|
2004-12-04 18:54:56 +01:00
|
|
|
NetworkClose();
|
|
|
|
}
|
2004-08-22 12:23:37 +02:00
|
|
|
|
2009-03-15 01:32:18 +01:00
|
|
|
/* Receives something from the network */
|
2007-03-07 12:47:46 +01:00
|
|
|
static bool NetworkReceive()
|
2004-12-04 18:54:56 +01:00
|
|
|
{
|
2008-12-23 10:47:42 +01:00
|
|
|
NetworkClientSocket *cs;
|
2004-12-04 18:54:56 +01:00
|
|
|
int n;
|
|
|
|
fd_set read_fd, write_fd;
|
|
|
|
struct timeval tv;
|
2004-08-22 12:23:37 +02:00
|
|
|
|
2004-12-04 18:54:56 +01:00
|
|
|
FD_ZERO(&read_fd);
|
|
|
|
FD_ZERO(&write_fd);
|
2004-08-22 12:23:37 +02:00
|
|
|
|
2008-12-23 10:02:41 +01:00
|
|
|
FOR_ALL_CLIENT_SOCKETS(cs) {
|
2007-01-12 21:19:49 +01:00
|
|
|
FD_SET(cs->sock, &read_fd);
|
|
|
|
FD_SET(cs->sock, &write_fd);
|
2004-12-04 18:54:56 +01:00
|
|
|
}
|
2004-08-22 12:23:37 +02:00
|
|
|
|
2009-03-15 01:32:18 +01:00
|
|
|
/* take care of listener port */
|
2009-04-08 02:05:16 +02:00
|
|
|
for (SocketList::iterator s = _listensockets.Begin(); s != _listensockets.End(); s++) {
|
|
|
|
FD_SET(s->second, &read_fd);
|
|
|
|
}
|
2004-08-22 12:23:37 +02:00
|
|
|
|
2004-12-04 18:54:56 +01: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 13:01:26 +02:00
|
|
|
|
2009-03-15 01:32:18 +01:00
|
|
|
/* accept clients.. */
|
2009-04-08 02:05:16 +02:00
|
|
|
for (SocketList::iterator s = _listensockets.Begin(); s != _listensockets.End(); s++) {
|
|
|
|
if (FD_ISSET(s->second, &read_fd)) NetworkAcceptClients(s->second);
|
|
|
|
}
|
2004-09-12 16:12:33 +02:00
|
|
|
|
2009-03-15 01:32:18 +01:00
|
|
|
/* read stuff from clients */
|
2008-12-23 10:02:41 +01:00
|
|
|
FOR_ALL_CLIENT_SOCKETS(cs) {
|
2007-01-12 21:19:49 +01:00
|
|
|
cs->writable = !!FD_ISSET(cs->sock, &write_fd);
|
|
|
|
if (FD_ISSET(cs->sock, &read_fd)) {
|
2006-06-27 23:25:53 +02:00
|
|
|
if (_network_server) {
|
2004-12-04 18:54:56 +01:00
|
|
|
NetworkServer_ReadPackets(cs);
|
2006-06-27 23:25:53 +02:00
|
|
|
} else {
|
2006-10-18 01:34:12 +02:00
|
|
|
NetworkRecvStatus res;
|
2006-06-27 23:25:53 +02:00
|
|
|
|
2009-03-15 01:32:18 +01:00
|
|
|
/* The client already was quiting! */
|
2009-04-07 20:23:14 +02:00
|
|
|
if (cs->HasClientQuit()) return false;
|
2006-06-27 23:25:53 +02:00
|
|
|
|
|
|
|
res = NetworkClient_ReadPackets(cs);
|
|
|
|
if (res != NETWORK_RECV_STATUS_OKAY) {
|
2009-03-15 01:32:18 +01:00
|
|
|
/* The client made an error of which we can not recover
|
|
|
|
* close the client and drop back to main menu */
|
2004-12-04 18:54:56 +01:00
|
|
|
NetworkClientError(res, cs);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
2004-09-12 16:12:33 +02:00
|
|
|
}
|
2004-08-22 12:23:37 +02:00
|
|
|
}
|
2004-12-04 18:54:56 +01:00
|
|
|
return true;
|
2004-08-22 12:23:37 +02:00
|
|
|
}
|
|
|
|
|
2009-03-15 01:32:18 +01:00
|
|
|
/* This sends all buffered commands (if possible) */
|
2007-03-07 12:47:46 +01:00
|
|
|
static void NetworkSend()
|
2004-09-13 01:35:01 +02:00
|
|
|
{
|
2008-12-23 10:47:42 +01:00
|
|
|
NetworkClientSocket *cs;
|
2008-12-23 10:02:41 +01:00
|
|
|
FOR_ALL_CLIENT_SOCKETS(cs) {
|
2004-12-04 18:54:56 +01:00
|
|
|
if (cs->writable) {
|
2007-02-02 00:50:15 +01:00
|
|
|
cs->Send_Packets();
|
2004-12-04 18:54:56 +01:00
|
|
|
|
|
|
|
if (cs->status == STATUS_MAP) {
|
2009-03-15 01:32:18 +01:00
|
|
|
/* This client is in the middle of a map-send, call the function for that */
|
2004-12-04 18:54:56 +01:00
|
|
|
SEND_COMMAND(PACKET_SERVER_MAP)(cs);
|
|
|
|
}
|
2004-09-13 01:35:01 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-03-07 12:47:46 +01:00
|
|
|
static bool NetworkDoClientLoop()
|
2004-09-12 16:12:33 +02:00
|
|
|
{
|
2004-12-04 18:54:56 +01:00
|
|
|
_frame_counter++;
|
2004-09-12 16:12:33 +02:00
|
|
|
|
2009-01-08 14:57:50 +01:00
|
|
|
NetworkExecuteLocalCommandQueue();
|
2004-09-12 16:12:33 +02:00
|
|
|
|
2004-12-04 18:54:56 +01:00
|
|
|
StateGameLoop();
|
2004-08-22 12:23:37 +02:00
|
|
|
|
2009-03-15 01:32:18 +01:00
|
|
|
/* Check if we are in sync! */
|
2004-12-04 18:54:56 +01:00
|
|
|
if (_sync_frame != 0) {
|
|
|
|
if (_sync_frame == _frame_counter) {
|
|
|
|
#ifdef NETWORK_SEND_DOUBLE_SEED
|
2008-01-29 01:27:25 +01:00
|
|
|
if (_sync_seed_1 != _random.state[0] || _sync_seed_2 != _random.state[1]) {
|
2004-12-04 18:54:56 +01:00
|
|
|
#else
|
2008-01-29 01:27:25 +01:00
|
|
|
if (_sync_seed_1 != _random.state[0]) {
|
2004-12-04 18:54:56 +01:00
|
|
|
#endif
|
|
|
|
NetworkError(STR_NETWORK_ERR_DESYNC);
|
2008-12-29 22:50:25 +01:00
|
|
|
DEBUG(desync, 1, "sync_err: %d; %d\n", _date, _date_fract);
|
2006-12-26 18:36:18 +01:00
|
|
|
DEBUG(net, 0, "Sync error detected!");
|
2008-12-23 11:42:06 +01:00
|
|
|
NetworkClientError(NETWORK_RECV_STATUS_DESYNC, GetNetworkClientSocket(0));
|
2004-12-04 18:54:56 +01:00
|
|
|
return false;
|
|
|
|
}
|
2004-08-22 12:23:37 +02:00
|
|
|
|
2009-03-15 01:32:18 +01: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! */
|
2004-12-04 18:54:56 +01:00
|
|
|
if (_network_first_time) {
|
|
|
|
_network_first_time = false;
|
|
|
|
SEND_COMMAND(PACKET_CLIENT_ACK)();
|
|
|
|
}
|
2004-08-22 12:23:37 +02:00
|
|
|
|
2004-12-04 18:54:56 +01:00
|
|
|
_sync_frame = 0;
|
|
|
|
} else if (_sync_frame < _frame_counter) {
|
2006-12-26 18:36:18 +01:00
|
|
|
DEBUG(net, 1, "Missed frame for sync-test (%d / %d)", _sync_frame, _frame_counter);
|
2004-12-04 18:54:56 +01:00
|
|
|
_sync_frame = 0;
|
2004-08-22 12:23:37 +02:00
|
|
|
}
|
2004-12-04 18:54:56 +01:00
|
|
|
}
|
2004-09-10 21:02:27 +02:00
|
|
|
|
2004-12-04 18:54:56 +01:00
|
|
|
return true;
|
2004-08-22 12:23:37 +02:00
|
|
|
}
|
|
|
|
|
2009-03-15 01:32:18 +01:00
|
|
|
/* We have to do some UDP checking */
|
2007-03-07 12:47:46 +01:00
|
|
|
void NetworkUDPGameLoop()
|
2004-09-12 16:12:33 +02:00
|
|
|
{
|
2009-01-20 17:51:55 +01:00
|
|
|
_network_content_client.SendReceive();
|
2009-01-20 12:28:18 +01:00
|
|
|
TCPConnecter::CheckCallbacks();
|
2009-01-17 17:53:32 +01:00
|
|
|
|
2004-12-22 19:42:56 +01:00
|
|
|
if (_network_udp_server) {
|
2007-01-12 15:30:01 +01:00
|
|
|
_udp_server_socket->ReceivePackets();
|
|
|
|
_udp_master_socket->ReceivePackets();
|
|
|
|
} else {
|
|
|
|
_udp_client_socket->ReceivePackets();
|
2006-06-10 10:37:41 +02:00
|
|
|
if (_network_udp_broadcast > 0) _network_udp_broadcast--;
|
2007-02-01 22:04:40 +01:00
|
|
|
NetworkGameListRequery();
|
2004-08-22 12:23:37 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-03-15 01:32:18 +01:00
|
|
|
/* The main loop called from ttd.c
|
|
|
|
* Here we also have to do StateGameLoop if needed! */
|
2007-03-07 12:47:46 +01:00
|
|
|
void NetworkGameLoop()
|
2004-09-12 16:12:33 +02:00
|
|
|
{
|
2004-12-04 18:54:56 +01:00
|
|
|
if (!_networking) return;
|
2004-08-22 12:23:37 +02:00
|
|
|
|
2004-12-04 18:54:56 +01:00
|
|
|
if (!NetworkReceive()) return;
|
2004-08-22 12:23:37 +02:00
|
|
|
|
2004-12-04 18:54:56 +01:00
|
|
|
if (_network_server) {
|
2007-07-07 12:06:10 +02: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 22:39:50 +02:00
|
|
|
_current_company = cp->company;
|
2008-12-28 15:37:19 +01:00
|
|
|
DoCommandP(cp->tile, cp->p1, cp->p2, cp->cmd, NULL, cp->text);
|
2007-07-07 12:06:10 +02:00
|
|
|
free(cp);
|
|
|
|
cp = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (cp != NULL) break;
|
|
|
|
|
|
|
|
char buff[4096];
|
|
|
|
if (fgets(buff, lengthof(buff), f) == NULL) break;
|
2008-12-29 22:50:25 +01:00
|
|
|
if (strncmp(buff, "cmd: ", 8) != 0) continue;
|
2007-07-07 12:06:10 +02:00
|
|
|
cp = MallocT<CommandPacket>(1);
|
2008-09-30 22:39:50 +02:00
|
|
|
int company;
|
2008-12-29 22:50:25 +01: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 22:39:50 +02:00
|
|
|
cp->company = (CompanyID)company;
|
2007-07-07 12:06:10 +02:00
|
|
|
}
|
2008-01-11 01:30:32 +01:00
|
|
|
#endif /* DEBUG_DUMP_COMMANDS */
|
2009-03-20 00:32:39 +01:00
|
|
|
CheckMinActiveClients();
|
2007-07-07 12:06:10 +02:00
|
|
|
|
2005-07-29 23:55:49 +02:00
|
|
|
bool send_frame = false;
|
|
|
|
|
2009-03-15 01:32:18 +01:00
|
|
|
/* We first increase the _frame_counter */
|
2004-12-04 18:54:56 +01:00
|
|
|
_frame_counter++;
|
2009-03-15 01:32:18 +01:00
|
|
|
/* Update max-frame-counter */
|
2005-07-29 23:55:49 +02:00
|
|
|
if (_frame_counter > _frame_counter_max) {
|
2008-05-29 22:21:28 +02:00
|
|
|
_frame_counter_max = _frame_counter + _settings_client.network.frame_freq;
|
2005-07-29 23:55:49 +02:00
|
|
|
send_frame = true;
|
|
|
|
}
|
2004-08-22 12:23:37 +02:00
|
|
|
|
2009-01-08 14:57:50 +01:00
|
|
|
NetworkExecuteLocalCommandQueue();
|
2004-08-22 12:23:37 +02:00
|
|
|
|
2009-03-15 01:32:18 +01:00
|
|
|
/* Then we make the frame */
|
2004-12-04 18:54:56 +01:00
|
|
|
StateGameLoop();
|
2004-09-12 16:12:33 +02:00
|
|
|
|
2008-01-29 01:27:25 +01:00
|
|
|
_sync_seed_1 = _random.state[0];
|
2004-12-04 18:54:56 +01:00
|
|
|
#ifdef NETWORK_SEND_DOUBLE_SEED
|
2008-01-29 01:27:25 +01:00
|
|
|
_sync_seed_2 = _random.state[1];
|
2004-12-04 18:54:56 +01:00
|
|
|
#endif
|
2004-08-22 12:23:37 +02:00
|
|
|
|
2005-07-29 23:55:49 +02:00
|
|
|
NetworkServer_Tick(send_frame);
|
2004-12-04 18:54:56 +01:00
|
|
|
} else {
|
2009-03-15 01:32:18 +01:00
|
|
|
/* Client */
|
2004-08-22 12:23:37 +02:00
|
|
|
|
2009-03-15 01:32:18 +01:00
|
|
|
/* Make sure we are at the frame were the server is (quick-frames) */
|
2004-12-04 18:54:56 +01:00
|
|
|
if (_frame_counter_server > _frame_counter) {
|
|
|
|
while (_frame_counter_server > _frame_counter) {
|
|
|
|
if (!NetworkDoClientLoop()) break;
|
|
|
|
}
|
|
|
|
} else {
|
2009-03-15 01:32:18 +01:00
|
|
|
/* Else, keep on going till _frame_counter_max */
|
2006-10-18 01:34:12 +02:00
|
|
|
if (_frame_counter_max > _frame_counter) NetworkDoClientLoop();
|
2004-12-04 18:54:56 +01:00
|
|
|
}
|
2004-08-22 12:23:37 +02:00
|
|
|
}
|
2004-12-04 18:54:56 +01:00
|
|
|
|
|
|
|
NetworkSend();
|
2004-08-22 12:23:37 +02:00
|
|
|
}
|
|
|
|
|
2007-03-07 12:47:46 +01:00
|
|
|
static void NetworkGenerateUniqueId()
|
2004-12-12 17:04:32 +01:00
|
|
|
{
|
2007-12-25 14:59:21 +01:00
|
|
|
Md5 checksum;
|
|
|
|
uint8 digest[16];
|
2009-01-09 23:56:28 +01:00
|
|
|
char hex_output[16 * 2 + 1];
|
2004-12-12 17:04:32 +01:00
|
|
|
char coding_string[NETWORK_NAME_LENGTH];
|
|
|
|
int di;
|
|
|
|
|
2004-12-12 18:47:50 +01:00
|
|
|
snprintf(coding_string, sizeof(coding_string), "%d%s", (uint)Random(), "OpenTTD Unique ID");
|
2004-12-12 17:04:32 +01:00
|
|
|
|
|
|
|
/* Generate the MD5 hash */
|
2007-12-25 14:59:21 +01:00
|
|
|
checksum.Append((const uint8*)coding_string, strlen(coding_string));
|
|
|
|
checksum.Finish(digest);
|
2004-12-12 17:04:32 +01:00
|
|
|
|
|
|
|
for (di = 0; di < 16; ++di)
|
|
|
|
sprintf(hex_output + di * 2, "%02x", digest[di]);
|
|
|
|
|
|
|
|
/* _network_unique_id is our id */
|
2008-05-29 22:21:28 +02:00
|
|
|
snprintf(_settings_client.network.network_id, sizeof(_settings_client.network.network_id), "%s", hex_output);
|
2004-12-12 17:04:32 +01:00
|
|
|
}
|
|
|
|
|
2009-01-20 02:32:06 +01:00
|
|
|
void NetworkStartDebugLog(NetworkAddress address)
|
2007-02-08 13:27:53 +01:00
|
|
|
{
|
|
|
|
extern SOCKET _debug_socket; // Comes from debug.c
|
|
|
|
|
2009-01-20 02:32:06 +01:00
|
|
|
DEBUG(net, 0, "Redirecting DEBUG() to %s:%d", address.GetHostname(), address.GetPort());
|
2007-02-08 13:27:53 +01:00
|
|
|
|
2009-04-03 02:33:00 +02:00
|
|
|
SOCKET s = address.Connect();
|
2007-02-08 13:27:53 +01:00
|
|
|
if (s == INVALID_SOCKET) {
|
|
|
|
DEBUG(net, 0, "Failed to open socket for redirection DEBUG()");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
_debug_socket = s;
|
|
|
|
|
|
|
|
DEBUG(net, 0, "DEBUG() is now redirected");
|
|
|
|
}
|
|
|
|
|
2007-01-04 20:12:45 +01:00
|
|
|
/** This tries to launch the network for a given OS */
|
2007-03-07 12:47:46 +01:00
|
|
|
void NetworkStartUp()
|
2004-09-12 16:12:33 +02:00
|
|
|
{
|
2006-12-26 18:36:18 +01:00
|
|
|
DEBUG(net, 3, "[core] starting network...");
|
2004-12-22 22:12:36 +01:00
|
|
|
|
2007-01-04 20:12:45 +01:00
|
|
|
/* Network is available */
|
|
|
|
_network_available = NetworkCoreInitialize();;
|
2004-12-04 18:54:56 +01:00
|
|
|
_network_dedicated = false;
|
2006-01-19 18:50:40 +01:00
|
|
|
_network_last_advertise_frame = 0;
|
|
|
|
_network_need_advertise = true;
|
2004-12-22 19:42:56 +01:00
|
|
|
_network_advertise_retries = 0;
|
2004-08-22 12:23:37 +02:00
|
|
|
|
2004-12-12 17:04:32 +01:00
|
|
|
/* Generate an unique id when there is none yet */
|
2008-05-29 22:21:28 +02:00
|
|
|
if (StrEmpty(_settings_client.network.network_id)) NetworkGenerateUniqueId();
|
2004-12-12 17:04:32 +01:00
|
|
|
|
2008-06-03 10:04:35 +02:00
|
|
|
memset(&_network_game_info, 0, sizeof(_network_game_info));
|
2004-08-22 12:23:37 +02:00
|
|
|
|
2008-12-23 21:58:03 +01:00
|
|
|
NetworkUDPInitialize();
|
2004-12-04 18:54:56 +01:00
|
|
|
NetworkInitialize();
|
2006-12-26 18:36:18 +01:00
|
|
|
DEBUG(net, 3, "[core] network online, multiplayer available");
|
2009-04-07 21:06:36 +02:00
|
|
|
NetworkFindBroadcastIPs(&_broadcast_list);
|
2004-08-22 12:23:37 +02:00
|
|
|
}
|
|
|
|
|
2007-01-04 20:12:45 +01:00
|
|
|
/** This shuts the network down */
|
2007-03-07 12:47:46 +01:00
|
|
|
void NetworkShutDown()
|
2004-09-12 16:12:33 +02:00
|
|
|
{
|
2006-10-12 16:48:36 +02:00
|
|
|
NetworkDisconnect();
|
2007-01-12 15:30:01 +01:00
|
|
|
NetworkUDPShutdown();
|
2006-10-12 16:48:36 +02:00
|
|
|
|
2006-12-26 18:36:18 +01:00
|
|
|
DEBUG(net, 3, "[core] shutting down network");
|
2004-08-22 12:23:37 +02:00
|
|
|
|
2004-12-04 18:54:56 +01:00
|
|
|
_network_available = false;
|
2004-08-22 12:23:37 +02:00
|
|
|
|
2007-01-04 19:50:40 +01:00
|
|
|
NetworkCoreShutdown();
|
2004-08-22 12:23:37 +02:00
|
|
|
}
|
|
|
|
|
2007-03-01 01:58:09 +01: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 09:46:58 +02:00
|
|
|
return strncmp(_openttd_revision, other, NETWORK_REVISION_LENGTH - 1) == 0;
|
2007-03-01 01:58:09 +01:00
|
|
|
}
|
|
|
|
|
2004-12-04 18:54:56 +01:00
|
|
|
#endif /* ENABLE_NETWORK */
|
2008-01-13 22:51:53 +01:00
|
|
|
|
|
|
|
/* NOTE: this variable needs to be always available */
|
2008-09-30 22:39:50 +02:00
|
|
|
CompanyID _network_playas;
|