Update to 1.11.2

This commit is contained in:
dP
2021-05-03 22:10:57 +03:00
parent 5881c752f5
commit ac7d3eba75
103 changed files with 1631 additions and 942 deletions

View File

@@ -4,9 +4,11 @@ add_files(
config.h
core.cpp
core.h
game.h
game_info.cpp
game_info.h
host.cpp
host.h
os_abstraction.cpp
os_abstraction.h
packet.cpp
packet.h
@@ -17,6 +19,7 @@ add_files(
tcp_connect.cpp
tcp_content.cpp
tcp_content.h
tcp_content_type.h
tcp_game.cpp
tcp_game.h
tcp_http.cpp

View File

@@ -316,7 +316,7 @@ static SOCKET ConnectLoopProc(addrinfo *runp)
SOCKET sock = socket(runp->ai_family, runp->ai_socktype, runp->ai_protocol);
if (sock == INVALID_SOCKET) {
DEBUG(net, 1, "[%s] could not create %s socket: %s", type, family, strerror(errno));
DEBUG(net, 1, "[%s] could not create %s socket: %s", type, family, NetworkError::GetLast().AsString());
return INVALID_SOCKET;
}
@@ -326,12 +326,12 @@ static SOCKET ConnectLoopProc(addrinfo *runp)
#ifdef __EMSCRIPTEN__
/* Emscripten is asynchronous, and as such a connect() is still in
* progress by the time the call returns. */
if (err != 0 && errno != EINPROGRESS)
if (err != 0 && !NetworkError::GetLast().IsConnectInProgress())
#else
if (err != 0)
#endif
{
DEBUG(net, 1, "[%s] could not connect %s socket: %s", type, family, strerror(errno));
DEBUG(net, 1, "[%s] could not connect %s socket: %s", type, family, NetworkError::GetLast().AsString());
closesocket(sock);
return INVALID_SOCKET;
}
@@ -369,7 +369,7 @@ static SOCKET ListenLoopProc(addrinfo *runp)
SOCKET sock = socket(runp->ai_family, runp->ai_socktype, runp->ai_protocol);
if (sock == INVALID_SOCKET) {
DEBUG(net, 0, "[%s] could not create %s socket on port %s: %s", type, family, address, strerror(errno));
DEBUG(net, 0, "[%s] could not create %s socket on port %s: %s", type, family, address, NetworkError::GetLast().AsString());
return INVALID_SOCKET;
}
@@ -380,24 +380,24 @@ static SOCKET ListenLoopProc(addrinfo *runp)
int on = 1;
/* The (const char*) cast is needed for windows!! */
if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (const char*)&on, sizeof(on)) == -1) {
DEBUG(net, 3, "[%s] could not set reusable %s sockets for port %s: %s", type, family, address, strerror(errno));
DEBUG(net, 3, "[%s] could not set reusable %s sockets for port %s: %s", type, family, address, NetworkError::GetLast().AsString());
}
#ifndef __OS2__
if (runp->ai_family == AF_INET6 &&
setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, (const char*)&on, sizeof(on)) == -1) {
DEBUG(net, 3, "[%s] could not disable IPv4 over IPv6 on port %s: %s", type, address, strerror(errno));
DEBUG(net, 3, "[%s] could not disable IPv4 over IPv6 on port %s: %s", type, address, NetworkError::GetLast().AsString());
}
#endif
if (bind(sock, runp->ai_addr, (int)runp->ai_addrlen) != 0) {
DEBUG(net, 1, "[%s] could not bind on %s port %s: %s", type, family, address, strerror(errno));
DEBUG(net, 1, "[%s] could not bind on %s port %s: %s", type, family, address, NetworkError::GetLast().AsString());
closesocket(sock);
return INVALID_SOCKET;
}
if (runp->ai_socktype != SOCK_DGRAM && listen(sock, 1) != 0) {
DEBUG(net, 1, "[%s] could not listen at %s port %s: %s", type, family, address, strerror(errno));
DEBUG(net, 1, "[%s] could not listen at %s port %s: %s", type, family, address, NetworkError::GetLast().AsString());
closesocket(sock);
return INVALID_SOCKET;
}

View File

@@ -47,32 +47,3 @@ void NetworkCoreShutdown()
WSACleanup();
#endif
}
/**
* Serializes the GRFIdentifier (GRF ID and MD5 checksum) to the packet
* @param p the packet to write the data to
* @param grf the GRFIdentifier to serialize
*/
void NetworkSocketHandler::SendGRFIdentifier(Packet *p, const GRFIdentifier *grf)
{
uint j;
p->Send_uint32(grf->grfid);
for (j = 0; j < sizeof(grf->md5sum); j++) {
p->Send_uint8 (grf->md5sum[j]);
}
}
/**
* Deserializes the GRFIdentifier (GRF ID and MD5 checksum) from the packet
* @param p the packet to read the data from
* @param grf the GRFIdentifier to deserialize
*/
void NetworkSocketHandler::ReceiveGRFIdentifier(Packet *p, GRFIdentifier *grf)
{
uint j;
grf->grfid = p->Recv_uint32();
for (j = 0; j < sizeof(grf->md5sum); j++) {
grf->md5sum[j] = p->Recv_uint8();
}
}

View File

@@ -71,8 +71,6 @@ public:
*/
void Reopen() { this->has_quit = false; }
void SendGRFIdentifier(Packet *p, const GRFIdentifier *grf);
void ReceiveGRFIdentifier(Packet *p, GRFIdentifier *grf);
void SendCompanyInformation(Packet *p, const struct Company *c, const struct NetworkCompanyStats *stats, uint max_len = NETWORK_COMPANY_NAME_LENGTH);
};

View File

@@ -0,0 +1,326 @@
/*
* This file is part of OpenTTD.
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @file game_info.cpp Functions to convert NetworkGameInfo to Packet and back.
*/
#include "../../stdafx.h"
#include "game_info.h"
#include "../../core/bitmath_func.hpp"
#include "../../company_base.h"
#include "../../date_func.h"
#include "../../debug.h"
#include "../../map_func.h"
#include "../../settings_type.h"
#include "../../string_func.h"
#include "../../rev.h"
#include "../network_func.h"
#include "../network.h"
#include "packet.h"
#include "../../safeguards.h"
/**
* How many hex digits of the git hash to include in network revision string.
* Determined as 10 hex digits + 2 characters for -g/-u/-m prefix.
*/
static const uint GITHASH_SUFFIX_LEN = 12;
NetworkServerGameInfo _network_game_info; ///< Information about our game.
/**
* Get the network version string used by this build.
* The returned string is guaranteed to be at most NETWORK_REVISON_LENGTH bytes.
*/
const char *GetNetworkRevisionString()
{
/* This will be allocated on heap and never free'd, but only once so not a "real" leak. */
static char *network_revision = nullptr;
if (!network_revision) {
/* Start by taking a chance on the full revision string. */
network_revision = stredup(_openttd_revision);
/* Ensure it's not longer than the packet buffer length. */
if (strlen(network_revision) >= NETWORK_REVISION_LENGTH) network_revision[NETWORK_REVISION_LENGTH - 1] = '\0';
/* Tag names are not mangled further. */
if (_openttd_revision_tagged) {
DEBUG(net, 1, "Network revision name is '%s'", network_revision);
return network_revision;
}
/* Prepare a prefix of the git hash.
* Size is length + 1 for terminator, +2 for -g prefix. */
assert(_openttd_revision_modified < 3);
char githash_suffix[GITHASH_SUFFIX_LEN + 1] = "-";
githash_suffix[1] = "gum"[_openttd_revision_modified];
for (uint i = 2; i < GITHASH_SUFFIX_LEN; i++) {
githash_suffix[i] = _openttd_revision_hash[i-2];
}
/* Where did the hash start in the original string?
* Overwrite from that position, unless that would go past end of packet buffer length. */
ptrdiff_t hashofs = strrchr(_openttd_revision, '-') - _openttd_revision;
if (hashofs + strlen(githash_suffix) + 1 > NETWORK_REVISION_LENGTH) hashofs = strlen(network_revision) - strlen(githash_suffix);
/* Replace the git hash in revision string. */
strecpy(network_revision + hashofs, githash_suffix, network_revision + NETWORK_REVISION_LENGTH);
assert(strlen(network_revision) < NETWORK_REVISION_LENGTH); // strlen does not include terminator, constant does, hence strictly less than
DEBUG(net, 1, "Network revision name is '%s'", network_revision);
}
return network_revision;
}
/**
* Extract the git hash from the revision string.
* @param revstr The revision string (formatted as DATE-BRANCH-GITHASH).
* @return The git has part of the revision.
*/
static const char *ExtractNetworkRevisionHash(const char *revstr)
{
return strrchr(revstr, '-');
}
/**
* Checks whether the given version string is compatible with our version.
* First tries to match the full string, if that fails, attempts to compare just git hashes.
* @param other the version string to compare to
*/
bool IsNetworkCompatibleVersion(const char *other)
{
if (strncmp(GetNetworkRevisionString(), other, NETWORK_REVISION_LENGTH - 1) == 0) return true;
/* If this version is tagged, then the revision string must be a complete match,
* since there is no git hash suffix in it.
* This is needed to avoid situations like "1.9.0-beta1" comparing equal to "2.0.0-beta1". */
if (_openttd_revision_tagged) return false;
const char *hash1 = ExtractNetworkRevisionHash(GetNetworkRevisionString());
const char *hash2 = ExtractNetworkRevisionHash(other);
return hash1 != nullptr && hash2 != nullptr && strncmp(hash1, hash2, GITHASH_SUFFIX_LEN) == 0;
}
/**
* Fill a NetworkGameInfo structure with the latest information of the server.
* @param ngi the NetworkGameInfo struct to fill with data.
*/
void FillNetworkGameInfo(NetworkGameInfo &ngi)
{
/* Update some game_info */
ngi.clients_on = _network_game_info.clients_on;
ngi.start_date = ConvertYMDToDate(_settings_game.game_creation.starting_year, 0, 1);
ngi.server_lang = _settings_client.network.server_lang;
ngi.use_password = !StrEmpty(_settings_client.network.server_password);
ngi.clients_max = _settings_client.network.max_clients;
ngi.companies_on = (byte)Company::GetNumItems();
ngi.companies_max = _settings_client.network.max_companies;
ngi.spectators_on = NetworkSpectatorCount();
ngi.spectators_max = _settings_client.network.max_spectators;
ngi.game_date = _date;
ngi.map_width = MapSizeX();
ngi.map_height = MapSizeY();
ngi.map_set = _settings_game.game_creation.landscape;
ngi.dedicated = _network_dedicated;
ngi.grfconfig = _grfconfig;
strecpy(ngi.map_name, _network_game_info.map_name, lastof(ngi.map_name));
strecpy(ngi.server_name, _settings_client.network.server_name, lastof(ngi.server_name));
strecpy(ngi.server_revision, GetNetworkRevisionString(), lastof(ngi.server_revision));
}
/**
* Function that is called for every GRFConfig that is read when receiving
* a NetworkGameInfo. Only grfid and md5sum are set, the rest is zero. This
* function must set all appropriate fields. This GRF is later appended to
* the grfconfig list of the NetworkGameInfo.
* @param config the GRF to handle.
*/
static void HandleIncomingNetworkGameInfoGRFConfig(GRFConfig *config)
{
/* Find the matching GRF file */
const GRFConfig *f = FindGRFConfig(config->ident.grfid, FGCM_EXACT, config->ident.md5sum);
if (f == nullptr) {
/* Don't know the GRF, so mark game incompatible and the (possibly)
* already resolved name for this GRF (another server has sent the
* name of the GRF already */
config->name = FindUnknownGRFName(config->ident.grfid, config->ident.md5sum, true);
config->status = GCS_NOT_FOUND;
} else {
config->filename = f->filename;
config->name = f->name;
config->info = f->info;
config->url = f->url;
}
SetBit(config->flags, GCF_COPY);
}
/**
* Serializes the NetworkGameInfo struct to the packet.
* @param p the packet to write the data to.
* @param info the NetworkGameInfo struct to serialize from.
*/
void SerializeNetworkGameInfo(Packet *p, const NetworkGameInfo *info)
{
p->Send_uint8 (NETWORK_GAME_INFO_VERSION);
/*
* Please observe the order.
* The parts must be read in the same order as they are sent!
*/
/* Update the documentation in game_info.h on changes
* to the NetworkGameInfo wire-protocol! */
/* NETWORK_GAME_INFO_VERSION = 4 */
{
/* Only send the GRF Identification (GRF_ID and MD5 checksum) of
* the GRFs that are needed, i.e. the ones that the server has
* selected in the NewGRF GUI and not the ones that are used due
* to the fact that they are in [newgrf-static] in openttd.cfg */
const GRFConfig *c;
uint count = 0;
/* Count number of GRFs to send information about */
for (c = info->grfconfig; c != nullptr; c = c->next) {
if (!HasBit(c->flags, GCF_STATIC)) count++;
}
p->Send_uint8 (count); // Send number of GRFs
/* Send actual GRF Identifications */
for (c = info->grfconfig; c != nullptr; c = c->next) {
if (!HasBit(c->flags, GCF_STATIC)) SerializeGRFIdentifier(p, &c->ident);
}
}
/* NETWORK_GAME_INFO_VERSION = 3 */
p->Send_uint32(info->game_date);
p->Send_uint32(info->start_date);
/* NETWORK_GAME_INFO_VERSION = 2 */
p->Send_uint8 (info->companies_max);
p->Send_uint8 (info->companies_on);
p->Send_uint8 (info->spectators_max);
/* NETWORK_GAME_INFO_VERSION = 1 */
p->Send_string(info->server_name);
p->Send_string(info->server_revision);
p->Send_uint8 (info->server_lang);
p->Send_bool (info->use_password);
p->Send_uint8 (info->clients_max);
p->Send_uint8 (info->clients_on);
p->Send_uint8 (info->spectators_on);
p->Send_string(info->map_name);
p->Send_uint16(info->map_width);
p->Send_uint16(info->map_height);
p->Send_uint8 (info->map_set);
p->Send_bool (info->dedicated);
}
/**
* Deserializes the NetworkGameInfo struct from the packet.
* @param p the packet to read the data from.
* @param info the NetworkGameInfo to deserialize into.
*/
void DeserializeNetworkGameInfo(Packet *p, NetworkGameInfo *info)
{
static const Date MAX_DATE = ConvertYMDToDate(MAX_YEAR, 11, 31); // December is month 11
info->game_info_version = p->Recv_uint8();
/*
* Please observe the order.
* The parts must be read in the same order as they are sent!
*/
/* Update the documentation in game_info.h on changes
* to the NetworkGameInfo wire-protocol! */
switch (info->game_info_version) {
case 4: {
GRFConfig **dst = &info->grfconfig;
uint i;
uint num_grfs = p->Recv_uint8();
/* Broken/bad data. It cannot have that many NewGRFs. */
if (num_grfs > NETWORK_MAX_GRF_COUNT) return;
for (i = 0; i < num_grfs; i++) {
GRFConfig *c = new GRFConfig();
DeserializeGRFIdentifier(p, &c->ident);
HandleIncomingNetworkGameInfoGRFConfig(c);
/* Append GRFConfig to the list */
*dst = c;
dst = &c->next;
}
FALLTHROUGH;
}
case 3:
info->game_date = Clamp(p->Recv_uint32(), 0, MAX_DATE);
info->start_date = Clamp(p->Recv_uint32(), 0, MAX_DATE);
FALLTHROUGH;
case 2:
info->companies_max = p->Recv_uint8 ();
info->companies_on = p->Recv_uint8 ();
info->spectators_max = p->Recv_uint8 ();
FALLTHROUGH;
case 1:
p->Recv_string(info->server_name, sizeof(info->server_name));
p->Recv_string(info->server_revision, sizeof(info->server_revision));
info->server_lang = p->Recv_uint8 ();
info->use_password = p->Recv_bool ();
info->clients_max = p->Recv_uint8 ();
info->clients_on = p->Recv_uint8 ();
info->spectators_on = p->Recv_uint8 ();
if (info->game_info_version < 3) { // 16 bits dates got scrapped and are read earlier
info->game_date = p->Recv_uint16() + DAYS_TILL_ORIGINAL_BASE_YEAR;
info->start_date = p->Recv_uint16() + DAYS_TILL_ORIGINAL_BASE_YEAR;
}
p->Recv_string(info->map_name, sizeof(info->map_name));
info->map_width = p->Recv_uint16();
info->map_height = p->Recv_uint16();
info->map_set = p->Recv_uint8 ();
info->dedicated = p->Recv_bool ();
if (info->server_lang >= NETWORK_NUM_LANGUAGES) info->server_lang = 0;
if (info->map_set >= NETWORK_NUM_LANDSCAPES) info->map_set = 0;
}
}
/**
* Serializes the GRFIdentifier (GRF ID and MD5 checksum) to the packet
* @param p the packet to write the data to.
* @param grf the GRFIdentifier to serialize.
*/
void SerializeGRFIdentifier(Packet *p, const GRFIdentifier *grf)
{
uint j;
p->Send_uint32(grf->grfid);
for (j = 0; j < sizeof(grf->md5sum); j++) {
p->Send_uint8(grf->md5sum[j]);
}
}
/**
* Deserializes the GRFIdentifier (GRF ID and MD5 checksum) from the packet
* @param p the packet to read the data from.
* @param grf the GRFIdentifier to deserialize.
*/
void DeserializeGRFIdentifier(Packet *p, GRFIdentifier *grf)
{
uint j;
grf->grfid = p->Recv_uint32();
for (j = 0; j < sizeof(grf->md5sum); j++) {
grf->md5sum[j] = p->Recv_uint8();
}
}

View File

@@ -0,0 +1,107 @@
/*
* This file is part of OpenTTD.
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @file game_info.h Convert NetworkGameInfo to Packet and back.
*/
#ifndef NETWORK_CORE_GAME_INFO_H
#define NETWORK_CORE_GAME_INFO_H
#include "config.h"
#include "core.h"
#include "../../newgrf_config.h"
#include "../../date_type.h"
/*
* NetworkGameInfo has several revisions which we still need to support on the
* wire. The table below shows the version and size for each field of the
* serialized NetworkGameInfo.
*
* Version: Bytes: Description:
* all 1 the version of this packet's structure
*
* 4+ 1 number of GRFs attached (n)
* 4+ n * 20 unique identifier for GRF files. Consists of:
* - one 4 byte variable with the GRF ID
* - 16 bytes (sent sequentially) for the MD5 checksum
* of the GRF
*
* 3+ 4 current game date in days since 1-1-0 (DMY)
* 3+ 4 game introduction date in days since 1-1-0 (DMY)
*
* 2+ 1 maximum number of companies allowed on the server
* 2+ 1 number of companies on the server
* 2+ 1 maximum number of spectators allowed on the server
*
* 1+ var string with the name of the server
* 1+ var string with the revision of the server
* 1+ 1 the language run on the server
* (0 = any, 1 = English, 2 = German, 3 = French)
* 1+ 1 whether the server uses a password (0 = no, 1 = yes)
* 1+ 1 maximum number of clients allowed on the server
* 1+ 1 number of clients on the server
* 1+ 1 number of spectators on the server
* 1 & 2 2 current game date in days since 1-1-1920 (DMY)
* 1 & 2 2 game introduction date in days since 1-1-1920 (DMY)
* 1+ var string with the name of the map
* 1+ 2 width of the map in tiles
* 1+ 2 height of the map in tiles
* 1+ 1 type of map:
* (0 = temperate, 1 = arctic, 2 = desert, 3 = toyland)
* 1+ 1 whether the server is dedicated (0 = no, 1 = yes)
*/
/**
* The game information that is not generated on-the-fly and has to
* be sent to the clients.
*/
struct NetworkServerGameInfo {
char map_name[NETWORK_NAME_LENGTH]; ///< Map which is played ["random" for a randomized map]
byte clients_on; ///< Current count of clients on server
};
/**
* The game information that is sent from the server to the clients.
*/
struct NetworkGameInfo : NetworkServerGameInfo {
GRFConfig *grfconfig; ///< List of NewGRF files used
Date start_date; ///< When the game started
Date game_date; ///< Current date
uint16 map_width; ///< Map width
uint16 map_height; ///< Map height
char server_name[NETWORK_NAME_LENGTH]; ///< Server name
char hostname[NETWORK_HOSTNAME_LENGTH]; ///< Hostname of the server (if any)
char server_revision[NETWORK_REVISION_LENGTH]; ///< The version number the server is using (e.g.: 'r304' or 0.5.0)
bool dedicated; ///< Is this a dedicated server?
bool version_compatible; ///< Can we connect to this server or not? (based on server_revision)
bool compatible; ///< Can we connect to this server or not? (based on server_revision _and_ grf_match
bool use_password; ///< Is this server passworded?
byte game_info_version; ///< Version of the game info
byte server_lang; ///< Language of the server (we should make a nice table for this)
byte clients_max; ///< Max clients allowed on server
byte companies_on; ///< How many started companies do we have
byte companies_max; ///< Max companies allowed on server
byte spectators_on; ///< How many spectators do we have?
byte spectators_max; ///< Max spectators allowed on server
byte map_set; ///< Graphical set
};
extern NetworkServerGameInfo _network_game_info;
const char *GetNetworkRevisionString();
bool IsNetworkCompatibleVersion(const char *other);
void FillNetworkGameInfo(NetworkGameInfo &ngi);
void DeserializeGRFIdentifier(Packet *p, GRFIdentifier *grf);
void SerializeGRFIdentifier(Packet *p, const GRFIdentifier *grf);
void DeserializeNetworkGameInfo(Packet *p, NetworkGameInfo *info);
void SerializeNetworkGameInfo(Packet *p, const NetworkGameInfo *info);
#endif /* NETWORK_CORE_GAME_INFO_H */

View File

@@ -0,0 +1,174 @@
/*
* This file is part of OpenTTD.
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @file os_abstraction.cpp OS specific implementations of functions of the OS abstraction layer for network stuff.
*
* The general idea is to have simple abstracting functions for things that
* require different implementations for different environments.
* In here the functions, and their documentation, are defined only once
* and the implementation contains the #ifdefs to change the implementation.
* Since Windows is usually different that is usually the first case, after
* that the behaviour is usually Unix/BSD-like with occasional variation.
*/
#include "stdafx.h"
#include "os_abstraction.h"
#include "../../string_func.h"
#include <mutex>
#include "../../safeguards.h"
/**
* Construct the network error with the given error code.
* @param error The error code.
*/
NetworkError::NetworkError(int error) : error(error)
{
}
/**
* Check whether this error describes that the operation would block.
* @return True iff the operation would block.
*/
bool NetworkError::WouldBlock() const
{
#if defined(_WIN32)
return this->error == WSAEWOULDBLOCK;
#else
/* Usually EWOULDBLOCK and EAGAIN are the same, but sometimes they are not
* and the POSIX.1 specification states that either should be checked. */
return this->error == EWOULDBLOCK || this->error == EAGAIN;
#endif
}
/**
* Check whether this error describes a connection reset.
* @return True iff the connection is reset.
*/
bool NetworkError::IsConnectionReset() const
{
#if defined(_WIN32)
return this->error == WSAECONNRESET;
#else
return this->error == ECONNRESET;
#endif
}
/**
* Check whether this error describes a connect is in progress.
* @return True iff the connect is already in progress.
*/
bool NetworkError::IsConnectInProgress() const
{
#if defined(_WIN32)
return this->error == WSAEWOULDBLOCK;
#else
return this->error == EINPROGRESS;
#endif
}
/**
* Get the string representation of the error message.
* @return The string representation that will get overwritten by next calls.
*/
const char *NetworkError::AsString() const
{
if (this->message.empty()) {
#if defined(_WIN32)
char buffer[512];
if (FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, this->error,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), buffer, sizeof(buffer), NULL) == 0) {
seprintf(buffer, lastof(buffer), "Unknown error %d", this->error);
}
this->message.assign(buffer);
#else
/* Make strerror thread safe by locking access to it. There is a thread safe strerror_r, however
* the non-POSIX variant is available due to defining _GNU_SOURCE meaning it is not portable.
* The problem with the non-POSIX variant is that it does not necessarily fill the buffer with
* the error message but can also return a pointer to a static bit of memory, whereas the POSIX
* variant always fills the buffer. This makes the behaviour too erratic to work with. */
static std::mutex mutex;
std::lock_guard<std::mutex> guard(mutex);
this->message.assign(strerror(this->error));
#endif
}
return this->message.c_str();
}
/**
* Check whether an error was actually set.
* @return True iff an error was set.
*/
bool NetworkError::HasError() const
{
return this->error != 0;
}
/**
* Get the last network error.
* @return The network error.
*/
/* static */ NetworkError NetworkError::GetLast()
{
#if defined(_WIN32)
return NetworkError(WSAGetLastError());
#elif defined(__OS2__)
return NetworkError(sock_errno());
#else
return NetworkError(errno);
#endif
}
/**
* Try to set the socket into non-blocking mode.
* @param d The socket to set the non-blocking more for.
* @return True if setting the non-blocking mode succeeded, otherwise false.
*/
bool SetNonBlocking(SOCKET d)
{
#if defined(_WIN32)
u_long nonblocking = 1;
return ioctlsocket(d, FIONBIO, &nonblocking) == 0;
#elif defined __EMSCRIPTEN__
return true;
#else
int nonblocking = 1;
return ioctl(d, FIONBIO, &nonblocking) == 0;
#endif
}
/**
* Try to set the socket to not delay sending.
* @param d The socket to disable the delaying for.
* @return True if disabling the delaying succeeded, otherwise false.
*/
bool SetNoDelay(SOCKET d)
{
#ifdef __EMSCRIPTEN__
return true;
#else
int flags = 1;
/* The (const char*) cast is needed for windows */
return setsockopt(d, IPPROTO_TCP, TCP_NODELAY, (const char *)&flags, sizeof(flags)) == 0;
#endif
}
/**
* Get the error from a socket, if any.
* @param d The socket to get the error from.
* @return The errno on the socket.
*/
NetworkError GetSocketError(SOCKET d)
{
int err;
socklen_t len = sizeof(err);
getsockopt(d, SOL_SOCKET, SO_ERROR, (char *)&err, &len);
return NetworkError(err);
}

View File

@@ -14,6 +14,28 @@
#ifndef NETWORK_CORE_OS_ABSTRACTION_H
#define NETWORK_CORE_OS_ABSTRACTION_H
#include <string>
/**
* Abstraction of a network error where all implementation details of the
* error codes are encapsulated in this class and the abstraction layer.
*/
class NetworkError {
private:
int error; ///< The underlying error number from errno or WSAGetLastError.
mutable std::string message; ///< The string representation of the error (set on first call to #AsString).
public:
NetworkError(int error);
bool HasError() const;
bool WouldBlock() const;
bool IsConnectionReset() const;
bool IsConnectInProgress() const;
const char *AsString() const;
static NetworkError GetLast();
};
/* Include standard stuff per OS */
/* Windows stuff */
@@ -23,9 +45,6 @@
#include <ws2tcpip.h>
#include <windows.h>
#define GET_LAST_ERROR() WSAGetLastError()
#undef EWOULDBLOCK
#define EWOULDBLOCK WSAEWOULDBLOCK
/* Windows has some different names for some types */
typedef unsigned long in_addr_t;
@@ -49,9 +68,7 @@ typedef unsigned long in_addr_t;
# endif
# define SOCKET int
# define INVALID_SOCKET -1
# define ioctlsocket ioctl
# define closesocket close
# define GET_LAST_ERROR() (errno)
/* Need this for FIONREAD on solaris */
# define BSD_COMP
@@ -99,9 +116,7 @@ typedef unsigned long in_addr_t;
#if defined(__OS2__)
# define SOCKET int
# define INVALID_SOCKET -1
# define ioctlsocket ioctl
# define closesocket close
# define GET_LAST_ERROR() (sock_errno())
/* Includes needed for OS/2 systems */
# include <types.h>
@@ -173,41 +188,10 @@ static inline socklen_t FixAddrLenForEmscripten(struct sockaddr_storage &address
}
#endif
/**
* Try to set the socket into non-blocking mode.
* @param d The socket to set the non-blocking more for.
* @return True if setting the non-blocking mode succeeded, otherwise false.
*/
static inline bool SetNonBlocking(SOCKET d)
{
#ifdef __EMSCRIPTEN__
return true;
#else
# ifdef _WIN32
u_long nonblocking = 1;
# else
int nonblocking = 1;
# endif
return ioctlsocket(d, FIONBIO, &nonblocking) == 0;
#endif
}
/**
* Try to set the socket to not delay sending.
* @param d The socket to disable the delaying for.
* @return True if disabling the delaying succeeded, otherwise false.
*/
static inline bool SetNoDelay(SOCKET d)
{
#ifdef __EMSCRIPTEN__
return true;
#else
/* XXX should this be done at all? */
int b = 1;
/* The (const char*) cast is needed for windows */
return setsockopt(d, IPPROTO_TCP, TCP_NODELAY, (const char*)&b, sizeof(b)) == 0;
#endif
}
bool SetNonBlocking(SOCKET d);
bool SetNoDelay(SOCKET d);
NetworkError GetSocketError(SOCKET d);
/* Make sure these structures have the size we expect them to be */
static_assert(sizeof(in_addr) == 4); ///< IPv4 addresses should be 4 bytes.

View File

@@ -105,11 +105,11 @@ SendPacketsState NetworkTCPSocketHandler::SendPackets(bool closing_down)
while (p != nullptr) {
res = send(this->sock, (const char*)p->buffer + p->pos, p->size - p->pos, 0);
if (res == -1) {
int err = GET_LAST_ERROR();
if (err != EWOULDBLOCK) {
NetworkError err = NetworkError::GetLast();
if (!err.WouldBlock()) {
/* Something went wrong.. close client! */
if (!closing_down) {
DEBUG(net, 0, "send failed with error %d", err);
DEBUG(net, 0, "send failed with error %s", err.AsString());
this->CloseConnection();
}
return SPS_CLOSED;
@@ -160,10 +160,10 @@ Packet *NetworkTCPSocketHandler::ReceivePacket()
/* Read the size of the packet */
res = recv(this->sock, (char*)p->buffer + p->pos, sizeof(PacketSize) - p->pos, 0);
if (res == -1) {
int err = GET_LAST_ERROR();
if (err != EWOULDBLOCK) {
/* Something went wrong... (104 is connection reset by peer) */
if (err != 104) DEBUG(net, 0, "recv failed with error %d", err);
NetworkError err = NetworkError::GetLast();
if (!err.WouldBlock()) {
/* Something went wrong... */
if (!err.IsConnectionReset()) DEBUG(net, 0, "recv failed with error %s", err.AsString());
this->CloseConnection();
return nullptr;
}
@@ -191,10 +191,10 @@ Packet *NetworkTCPSocketHandler::ReceivePacket()
while (p->pos < p->size) {
res = recv(this->sock, (char*)p->buffer + p->pos, p->size - p->pos, 0);
if (res == -1) {
int err = GET_LAST_ERROR();
if (err != EWOULDBLOCK) {
/* Something went wrong... (104 is connection reset by peer) */
if (err != 104) DEBUG(net, 0, "recv failed with error %d", err);
NetworkError err = NetworkError::GetLast();
if (!err.WouldBlock()) {
/* Something went wrong... */
if (!err.IsConnectionReset()) DEBUG(net, 0, "recv failed with error %s", err.AsString());
this->CloseConnection();
return nullptr;
}

View File

@@ -16,81 +16,7 @@
#include "tcp.h"
#include "packet.h"
#include "../../debug.h"
/** The values in the enum are important; they are used as database 'keys' */
enum ContentType {
CONTENT_TYPE_BEGIN = 1, ///< Helper to mark the begin of the types
CONTENT_TYPE_BASE_GRAPHICS = 1, ///< The content consists of base graphics
CONTENT_TYPE_NEWGRF = 2, ///< The content consists of a NewGRF
CONTENT_TYPE_AI = 3, ///< The content consists of an AI
CONTENT_TYPE_AI_LIBRARY = 4, ///< The content consists of an AI library
CONTENT_TYPE_SCENARIO = 5, ///< The content consists of a scenario
CONTENT_TYPE_HEIGHTMAP = 6, ///< The content consists of a heightmap
CONTENT_TYPE_BASE_SOUNDS = 7, ///< The content consists of base sounds
CONTENT_TYPE_BASE_MUSIC = 8, ///< The content consists of base music
CONTENT_TYPE_GAME = 9, ///< The content consists of a game script
CONTENT_TYPE_GAME_LIBRARY = 10, ///< The content consists of a GS library
CONTENT_TYPE_END, ///< Helper to mark the end of the types
};
/** Enum with all types of TCP content packets. The order MUST not be changed **/
enum PacketContentType {
PACKET_CONTENT_CLIENT_INFO_LIST, ///< Queries the content server for a list of info of a given content type
PACKET_CONTENT_CLIENT_INFO_ID, ///< Queries the content server for information about a list of internal IDs
PACKET_CONTENT_CLIENT_INFO_EXTID, ///< Queries the content server for information about a list of external IDs
PACKET_CONTENT_CLIENT_INFO_EXTID_MD5, ///< Queries the content server for information about a list of external IDs and MD5
PACKET_CONTENT_SERVER_INFO, ///< Reply of content server with information about content
PACKET_CONTENT_CLIENT_CONTENT, ///< Request a content file given an internal ID
PACKET_CONTENT_SERVER_CONTENT, ///< Reply with the content of the given ID
PACKET_CONTENT_END, ///< Must ALWAYS be on the end of this list!! (period)
};
/** Unique identifier for the content. */
enum ContentID {
INVALID_CONTENT_ID = UINT32_MAX, ///< Sentinel for invalid content.
};
/** Container for all important information about a piece of content. */
struct ContentInfo {
/** The state the content can be in. */
enum State {
UNSELECTED, ///< The content has not been selected
SELECTED, ///< The content has been manually selected
AUTOSELECTED, ///< The content has been selected as dependency
ALREADY_HERE, ///< The content is already at the client side
DOES_NOT_EXIST, ///< The content does not exist in the content system
INVALID, ///< The content's invalid
};
ContentType type; ///< Type of content
ContentID id; ///< Unique (server side) ID for the content
uint32 filesize; ///< Size of the file
char filename[48]; ///< Filename (for the .tar.gz; only valid on download)
char name[32]; ///< Name of the content
char version[16]; ///< Version of the content
char url[96]; ///< URL related to the content
char description[512]; ///< Description of the content
uint32 unique_id; ///< Unique ID; either GRF ID or shortname
byte md5sum[16]; ///< The MD5 checksum
uint8 dependency_count; ///< Number of dependencies
ContentID *dependencies; ///< Malloced array of dependencies (unique server side ids)
uint8 tag_count; ///< Number of tags
char (*tags)[32]; ///< Malloced array of tags (strings)
State state; ///< Whether the content info is selected (for download)
bool upgrade; ///< This item is an upgrade
ContentInfo();
~ContentInfo();
void TransferFrom(ContentInfo *other);
size_t Size() const;
bool IsSelected() const;
bool IsValid() const;
#ifndef OPENTTD_MSU
const char *GetTextfile(TextfileType type) const;
#endif /* OPENTTD_MSU */
};
#include "tcp_content_type.h"
/** Base socket handler for all Content TCP sockets */
class NetworkContentSocketHandler : public NetworkTCPSocketHandler {

View File

@@ -0,0 +1,90 @@
/*
* This file is part of OpenTTD.
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @file tcp_content_type.h Basic types related to the content on the content server.
*/
#ifndef NETWORK_CORE_TCP_CONTENT_TYPE_H
#define NETWORK_CORE_TCP_CONTENT_TYPE_H
/** The values in the enum are important; they are used as database 'keys' */
enum ContentType {
CONTENT_TYPE_BEGIN = 1, ///< Helper to mark the begin of the types
CONTENT_TYPE_BASE_GRAPHICS = 1, ///< The content consists of base graphics
CONTENT_TYPE_NEWGRF = 2, ///< The content consists of a NewGRF
CONTENT_TYPE_AI = 3, ///< The content consists of an AI
CONTENT_TYPE_AI_LIBRARY = 4, ///< The content consists of an AI library
CONTENT_TYPE_SCENARIO = 5, ///< The content consists of a scenario
CONTENT_TYPE_HEIGHTMAP = 6, ///< The content consists of a heightmap
CONTENT_TYPE_BASE_SOUNDS = 7, ///< The content consists of base sounds
CONTENT_TYPE_BASE_MUSIC = 8, ///< The content consists of base music
CONTENT_TYPE_GAME = 9, ///< The content consists of a game script
CONTENT_TYPE_GAME_LIBRARY = 10, ///< The content consists of a GS library
CONTENT_TYPE_END, ///< Helper to mark the end of the types
};
/** Enum with all types of TCP content packets. The order MUST not be changed **/
enum PacketContentType {
PACKET_CONTENT_CLIENT_INFO_LIST, ///< Queries the content server for a list of info of a given content type
PACKET_CONTENT_CLIENT_INFO_ID, ///< Queries the content server for information about a list of internal IDs
PACKET_CONTENT_CLIENT_INFO_EXTID, ///< Queries the content server for information about a list of external IDs
PACKET_CONTENT_CLIENT_INFO_EXTID_MD5, ///< Queries the content server for information about a list of external IDs and MD5
PACKET_CONTENT_SERVER_INFO, ///< Reply of content server with information about content
PACKET_CONTENT_CLIENT_CONTENT, ///< Request a content file given an internal ID
PACKET_CONTENT_SERVER_CONTENT, ///< Reply with the content of the given ID
PACKET_CONTENT_END, ///< Must ALWAYS be on the end of this list!! (period)
};
/** Unique identifier for the content. */
enum ContentID {
INVALID_CONTENT_ID = UINT32_MAX, ///< Sentinel for invalid content.
};
/** Container for all important information about a piece of content. */
struct ContentInfo {
/** The state the content can be in. */
enum State {
UNSELECTED, ///< The content has not been selected
SELECTED, ///< The content has been manually selected
AUTOSELECTED, ///< The content has been selected as dependency
ALREADY_HERE, ///< The content is already at the client side
DOES_NOT_EXIST, ///< The content does not exist in the content system
INVALID, ///< The content's invalid
};
ContentType type; ///< Type of content
ContentID id; ///< Unique (server side) ID for the content
uint32 filesize; ///< Size of the file
char filename[48]; ///< Filename (for the .tar.gz; only valid on download)
char name[32]; ///< Name of the content
char version[16]; ///< Version of the content
char url[96]; ///< URL related to the content
char description[512]; ///< Description of the content
uint32 unique_id; ///< Unique ID; either GRF ID or shortname
byte md5sum[16]; ///< The MD5 checksum
uint8 dependency_count; ///< Number of dependencies
ContentID *dependencies; ///< Malloced array of dependencies (unique server side ids)
uint8 tag_count; ///< Number of tags
char (*tags)[32]; ///< Malloced array of tags (strings)
State state; ///< Whether the content info is selected (for download)
bool upgrade; ///< This item is an upgrade
ContentInfo();
~ContentInfo();
void TransferFrom(ContentInfo *other);
size_t Size() const;
bool IsSelected() const;
bool IsValid() const;
#ifndef OPENTTD_MSU
const char *GetTextfile(TextfileType type) const;
#endif /* OPENTTD_MSU */
};
#endif /* NETWORK_CORE_TCP_CONTENT_TYPE_H */

View File

@@ -71,6 +71,8 @@ NetworkRecvStatus NetworkGameSocketHandler::HandlePacket(Packet *p)
case PACKET_SERVER_BANNED: return this->Receive_SERVER_BANNED(p);
case PACKET_CLIENT_JOIN: return this->Receive_CLIENT_JOIN(p);
case PACKET_SERVER_ERROR: return this->Receive_SERVER_ERROR(p);
case PACKET_CLIENT_GAME_INFO: return this->Receive_CLIENT_GAME_INFO(p);
case PACKET_SERVER_GAME_INFO: return this->Receive_SERVER_GAME_INFO(p);
case PACKET_CLIENT_COMPANY_INFO: return this->Receive_CLIENT_COMPANY_INFO(p);
case PACKET_SERVER_COMPANY_INFO: return this->Receive_SERVER_COMPANY_INFO(p);
case PACKET_SERVER_CLIENT_INFO: return this->Receive_SERVER_CLIENT_INFO(p);
@@ -157,6 +159,8 @@ NetworkRecvStatus NetworkGameSocketHandler::Receive_SERVER_FULL(Packet *p) { ret
NetworkRecvStatus NetworkGameSocketHandler::Receive_SERVER_BANNED(Packet *p) { return this->ReceiveInvalidPacket(PACKET_SERVER_BANNED); }
NetworkRecvStatus NetworkGameSocketHandler::Receive_CLIENT_JOIN(Packet *p) { return this->ReceiveInvalidPacket(PACKET_CLIENT_JOIN); }
NetworkRecvStatus NetworkGameSocketHandler::Receive_SERVER_ERROR(Packet *p) { return this->ReceiveInvalidPacket(PACKET_SERVER_ERROR); }
NetworkRecvStatus NetworkGameSocketHandler::Receive_CLIENT_GAME_INFO(Packet *p) { return this->ReceiveInvalidPacket(PACKET_CLIENT_GAME_INFO); }
NetworkRecvStatus NetworkGameSocketHandler::Receive_SERVER_GAME_INFO(Packet *p) { return this->ReceiveInvalidPacket(PACKET_SERVER_GAME_INFO); }
NetworkRecvStatus NetworkGameSocketHandler::Receive_CLIENT_COMPANY_INFO(Packet *p) { return this->ReceiveInvalidPacket(PACKET_CLIENT_COMPANY_INFO); }
NetworkRecvStatus NetworkGameSocketHandler::Receive_SERVER_COMPANY_INFO(Packet *p) { return this->ReceiveInvalidPacket(PACKET_SERVER_COMPANY_INFO); }
NetworkRecvStatus NetworkGameSocketHandler::Receive_SERVER_CLIENT_INFO(Packet *p) { return this->ReceiveInvalidPacket(PACKET_SERVER_CLIENT_INFO); }

View File

@@ -24,7 +24,7 @@
*/
enum PacketGameType {
/*
* These first three pair of packets (thus six in
* These first four pair of packets (thus eight in
* total) must remain in this order for backward
* and forward compatibility between clients that
* are trying to join directly.
@@ -42,6 +42,10 @@ enum PacketGameType {
PACKET_CLIENT_COMPANY_INFO, ///< Request information about all companies.
PACKET_SERVER_COMPANY_INFO, ///< Information about a single company.
/* Packets used to get the game info. */
PACKET_SERVER_GAME_INFO, ///< Information about the server.
PACKET_CLIENT_GAME_INFO, ///< Request information about the server.
/*
* Packets after here assume that the client
* and server are running the same version. As
@@ -183,6 +187,19 @@ protected:
*/
virtual NetworkRecvStatus Receive_SERVER_ERROR(Packet *p);
/**
* Request game information.
* @param p The packet that was just received.
*/
virtual NetworkRecvStatus Receive_CLIENT_GAME_INFO(Packet *p);
/**
* Sends information about the game.
* Serialized NetworkGameInfo. See game_info.h for details.
* @param p The packet that was just received.
*/
virtual NetworkRecvStatus Receive_SERVER_GAME_INFO(Packet *p);
/**
* Request company information (in detail).
* @param p The packet that was just received.

View File

@@ -13,6 +13,7 @@
#include "../../debug.h"
#include "../../rev.h"
#include "../network_func.h"
#include "game_info.h"
#include "tcp_http.h"
@@ -230,10 +231,10 @@ int NetworkHTTPSocketHandler::Receive()
for (;;) {
ssize_t res = recv(this->sock, (char *)this->recv_buffer + this->recv_pos, lengthof(this->recv_buffer) - this->recv_pos, 0);
if (res == -1) {
int err = GET_LAST_ERROR();
if (err != EWOULDBLOCK) {
/* Something went wrong... (104 is connection reset by peer) */
if (err != 104) DEBUG(net, 0, "recv failed with error %d", err);
NetworkError err = NetworkError::GetLast();
if (!err.WouldBlock()) {
/* Something went wrong... */
if (!err.IsConnectionReset()) DEBUG(net, 0, "recv failed with error %s", err.AsString());
return -1;
}
/* Connection would block, so stop for now */

View File

@@ -64,7 +64,7 @@ public:
DEBUG(net, 1, "[%s] Banned ip tried to join (%s), refused", Tsocket::GetName(), entry.c_str());
if (send(s, (const char*)p.buffer, p.size, 0) < 0) {
DEBUG(net, 0, "send failed with error %d", GET_LAST_ERROR());
DEBUG(net, 0, "send failed with error %s", NetworkError::GetLast().AsString());
}
closesocket(s);
break;
@@ -81,7 +81,7 @@ public:
p.PrepareToSend();
if (send(s, (const char*)p.buffer, p.size, 0) < 0) {
DEBUG(net, 0, "send failed with error %d", GET_LAST_ERROR());
DEBUG(net, 0, "send failed with error %s", NetworkError::GetLast().AsString());
}
closesocket(s);
@@ -151,7 +151,7 @@ public:
if (sockets.size() == 0) {
DEBUG(net, 0, "[server] could not start network: could not create listening socket");
NetworkError(STR_NETWORK_ERROR_SERVER_START);
ShowNetworkError(STR_NETWORK_ERROR_SERVER_START);
return false;
}

View File

@@ -12,6 +12,7 @@
#include "../../stdafx.h"
#include "../../date_func.h"
#include "../../debug.h"
#include "game_info.h"
#include "udp.h"
#include "../../safeguards.h"
@@ -94,7 +95,7 @@ void NetworkUDPSocketHandler::SendPacket(Packet *p, NetworkAddress *recv, bool a
/* Enable broadcast */
unsigned long val = 1;
if (setsockopt(s.second, SOL_SOCKET, SO_BROADCAST, (char *) &val, sizeof(val)) < 0) {
DEBUG(net, 1, "[udp] setting broadcast failed with: %i", GET_LAST_ERROR());
DEBUG(net, 1, "[udp] setting broadcast failed with: %s", NetworkError::GetLast().AsString());
}
}
@@ -103,7 +104,7 @@ void NetworkUDPSocketHandler::SendPacket(Packet *p, NetworkAddress *recv, bool a
DEBUG(net, 7, "[udp] sendto(%s)", send.GetAddressAsString().c_str());
/* Check for any errors, but ignore it otherwise */
if (res == -1) DEBUG(net, 1, "[udp] sendto(%s) failed with: %i", send.GetAddressAsString().c_str(), GET_LAST_ERROR());
if (res == -1) DEBUG(net, 1, "[udp] sendto(%s) failed with: %s", send.GetAddressAsString().c_str(), NetworkError::GetLast().AsString());
if (!all) break;
}
@@ -149,143 +150,6 @@ void NetworkUDPSocketHandler::ReceivePackets()
}
}
/**
* Serializes the NetworkGameInfo struct to the packet
* @param p the packet to write the data to
* @param info the NetworkGameInfo struct to serialize
*/
void NetworkUDPSocketHandler::SendNetworkGameInfo(Packet *p, const NetworkGameInfo *info)
{
p->Send_uint8 (NETWORK_GAME_INFO_VERSION);
/*
* Please observe the order.
* The parts must be read in the same order as they are sent!
*/
/* Update the documentation in udp.h on changes
* to the NetworkGameInfo wire-protocol! */
/* NETWORK_GAME_INFO_VERSION = 4 */
{
/* Only send the GRF Identification (GRF_ID and MD5 checksum) of
* the GRFs that are needed, i.e. the ones that the server has
* selected in the NewGRF GUI and not the ones that are used due
* to the fact that they are in [newgrf-static] in openttd.cfg */
const GRFConfig *c;
uint count = 0;
/* Count number of GRFs to send information about */
for (c = info->grfconfig; c != nullptr; c = c->next) {
if (!HasBit(c->flags, GCF_STATIC)) count++;
}
p->Send_uint8 (count); // Send number of GRFs
/* Send actual GRF Identifications */
for (c = info->grfconfig; c != nullptr; c = c->next) {
if (!HasBit(c->flags, GCF_STATIC)) this->SendGRFIdentifier(p, &c->ident);
}
}
/* NETWORK_GAME_INFO_VERSION = 3 */
p->Send_uint32(info->game_date);
p->Send_uint32(info->start_date);
/* NETWORK_GAME_INFO_VERSION = 2 */
p->Send_uint8 (info->companies_max);
p->Send_uint8 (info->companies_on);
p->Send_uint8 (info->spectators_max);
/* NETWORK_GAME_INFO_VERSION = 1 */
p->Send_string(info->server_name);
p->Send_string(info->server_revision);
p->Send_uint8 (info->server_lang);
p->Send_bool (info->use_password);
p->Send_uint8 (info->clients_max);
p->Send_uint8 (info->clients_on);
p->Send_uint8 (info->spectators_on);
p->Send_string(info->map_name);
p->Send_uint16(info->map_width);
p->Send_uint16(info->map_height);
p->Send_uint8 (info->map_set);
p->Send_bool (info->dedicated);
}
/**
* Deserializes the NetworkGameInfo struct from the packet
* @param p the packet to read the data from
* @param info the NetworkGameInfo to deserialize into
*/
void NetworkUDPSocketHandler::ReceiveNetworkGameInfo(Packet *p, NetworkGameInfo *info)
{
static const Date MAX_DATE = ConvertYMDToDate(MAX_YEAR, 11, 31); // December is month 11
info->game_info_version = p->Recv_uint8();
/*
* Please observe the order.
* The parts must be read in the same order as they are sent!
*/
/* Update the documentation in udp.h on changes
* to the NetworkGameInfo wire-protocol! */
switch (info->game_info_version) {
case 4: {
GRFConfig **dst = &info->grfconfig;
uint i;
uint num_grfs = p->Recv_uint8();
/* Broken/bad data. It cannot have that many NewGRFs. */
if (num_grfs > NETWORK_MAX_GRF_COUNT) return;
for (i = 0; i < num_grfs; i++) {
GRFConfig *c = new GRFConfig();
this->ReceiveGRFIdentifier(p, &c->ident);
this->HandleIncomingNetworkGameInfoGRFConfig(c);
/* Append GRFConfig to the list */
*dst = c;
dst = &c->next;
}
FALLTHROUGH;
}
case 3:
info->game_date = Clamp(p->Recv_uint32(), 0, MAX_DATE);
info->start_date = Clamp(p->Recv_uint32(), 0, MAX_DATE);
FALLTHROUGH;
case 2:
info->companies_max = p->Recv_uint8 ();
info->companies_on = p->Recv_uint8 ();
info->spectators_max = p->Recv_uint8 ();
FALLTHROUGH;
case 1:
p->Recv_string(info->server_name, sizeof(info->server_name));
p->Recv_string(info->server_revision, sizeof(info->server_revision));
info->server_lang = p->Recv_uint8 ();
info->use_password = p->Recv_bool ();
info->clients_max = p->Recv_uint8 ();
info->clients_on = p->Recv_uint8 ();
info->spectators_on = p->Recv_uint8 ();
if (info->game_info_version < 3) { // 16 bits dates got scrapped and are read earlier
info->game_date = p->Recv_uint16() + DAYS_TILL_ORIGINAL_BASE_YEAR;
info->start_date = p->Recv_uint16() + DAYS_TILL_ORIGINAL_BASE_YEAR;
}
p->Recv_string(info->map_name, sizeof(info->map_name));
info->map_width = p->Recv_uint16();
info->map_height = p->Recv_uint16();
info->map_set = p->Recv_uint8 ();
info->dedicated = p->Recv_bool ();
if (info->server_lang >= NETWORK_NUM_LANGUAGES) info->server_lang = 0;
if (info->map_set >= NETWORK_NUM_LANDSCAPES) info->map_set = 0;
}
}
/**
* Handle an incoming packets by sending it to the correct function.
* @param p the received packet

View File

@@ -13,7 +13,6 @@
#define NETWORK_CORE_UDP_H
#include "address.h"
#include "game.h"
#include "packet.h"
/** Enum with all types of UDP packets. The order MUST not be changed **/
@@ -63,40 +62,7 @@ protected:
/**
* Return of server information to the client.
* This packet has several legacy versions, so we list the version and size of each "field":
*
* Version: Bytes: Description:
* all 1 the version of this packet's structure
*
* 4+ 1 number of GRFs attached (n)
* 4+ n * 20 unique identifier for GRF files. Consists of:
* - one 4 byte variable with the GRF ID
* - 16 bytes (sent sequentially) for the MD5 checksum
* of the GRF
*
* 3+ 4 current game date in days since 1-1-0 (DMY)
* 3+ 4 game introduction date in days since 1-1-0 (DMY)
*
* 2+ 1 maximum number of companies allowed on the server
* 2+ 1 number of companies on the server
* 2+ 1 maximum number of spectators allowed on the server
*
* 1+ var string with the name of the server
* 1+ var string with the revision of the server
* 1+ 1 the language run on the server
* (0 = any, 1 = English, 2 = German, 3 = French)
* 1+ 1 whether the server uses a password (0 = no, 1 = yes)
* 1+ 1 maximum number of clients allowed on the server
* 1+ 1 number of clients on the server
* 1+ 1 number of spectators on the server
* 1 & 2 2 current game date in days since 1-1-1920 (DMY)
* 1 & 2 2 game introduction date in days since 1-1-1920 (DMY)
* 1+ var string with the name of the map
* 1+ 2 width of the map in tiles
* 1+ 2 height of the map in tiles
* 1+ 1 type of map:
* (0 = temperate, 1 = arctic, 2 = desert, 3 = toyland)
* 1+ 1 whether the server is dedicated (0 = no, 1 = yes)
* Serialized NetworkGameInfo. See game_info.h for details.
* @param p The received packet.
* @param client_addr The origin of the packet.
*/
@@ -217,15 +183,6 @@ protected:
virtual void Receive_MASTER_SESSION_KEY(Packet *p, NetworkAddress *client_addr);
void HandleUDPPacket(Packet *p, NetworkAddress *client_addr);
/**
* Function that is called for every GRFConfig that is read when receiving
* a NetworkGameInfo. Only grfid and md5sum are set, the rest is zero. This
* function must set all appropriate fields. This GRF is later appended to
* the grfconfig list of the NetworkGameInfo.
* @param config the GRF to handle
*/
virtual void HandleIncomingNetworkGameInfoGRFConfig(GRFConfig *config) { NOT_REACHED(); }
public:
NetworkUDPSocketHandler(NetworkAddressList *bind = nullptr);
@@ -237,9 +194,6 @@ public:
void SendPacket(Packet *p, NetworkAddress *recv, bool all = false, bool broadcast = false);
void ReceivePackets();
void SendNetworkGameInfo(Packet *p, const NetworkGameInfo *info);
void ReceiveNetworkGameInfo(Packet *p, NetworkGameInfo *info);
};
#endif /* NETWORK_CORE_UDP_H */

View File

@@ -54,7 +54,6 @@ bool _network_server; ///< network-server is active
bool _network_available; ///< is network mode available?
bool _network_dedicated; ///< are we a dedicated server?
bool _is_network_server; ///< Does this client wants to be a network-server?
NetworkServerGameInfo _network_game_info; ///< Information about our game.
NetworkCompanyState *_network_company_states = nullptr; ///< Statistics about some companies.
ClientID _network_own_client_id; ///< Our client identifier.
ClientID _redirect_console_to_client; ///< If not invalid, redirect the console output to a client.
@@ -177,12 +176,15 @@ const char *GenerateCompanyPasswordHash(const char *password, const char *passwo
if (StrEmpty(password)) return password;
char salted_password[NETWORK_SERVER_ID_LENGTH];
size_t password_length = strlen(password);
size_t password_server_id_length = strlen(password_server_id);
memset(salted_password, 0, sizeof(salted_password));
seprintf(salted_password, lastof(salted_password), "%s", password);
/* Add the game seed and the server's ID as the salt. */
for (uint i = 0; i < NETWORK_SERVER_ID_LENGTH - 1; i++) {
salted_password[i] ^= password_server_id[i] ^ (password_game_seed >> (i % 32));
char password_char = (i < password_length ? password[i] : 0);
char server_id_char = (i < password_server_id_length ? password_server_id[i] : 0);
char seed_char = password_game_seed >> (i % 32);
salted_password[i] = password_char ^ server_id_char ^ seed_char;
}
Md5 checksum;
@@ -278,7 +280,7 @@ uint NetworkCalculateLag(const NetworkClientSocket *cs)
/* There was a non-recoverable error, drop back to the main menu with a nice
* error */
void NetworkError(StringID error_string)
void ShowNetworkError(StringID error_string)
{
_switch_mode = SM_MENU;
ShowErrorMessage(error_string, INVALID_STRING_ID, WL_CRITICAL);
@@ -572,9 +574,10 @@ public:
}
};
/* 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 */
/**
* Query a server to fetch his game-info.
* @param address the address to query.
*/
void NetworkTCPQueryServer(NetworkAddress address)
{
if (!_network_available) return;
@@ -644,7 +647,7 @@ public:
void OnFailure() override
{
NetworkError(STR_NETWORK_ERROR_NOCONNECTION);
ShowNetworkError(STR_NETWORK_ERROR_NOCONNECTION);
}
void OnConnect(SOCKET s) override
@@ -658,25 +661,45 @@ public:
/* Used by clients, to connect to a server */
void NetworkClientConnectGame(NetworkAddress address, CompanyID join_as, const char *join_server_password, const char *join_company_password)
void NetworkClientConnectGame(const char *hostname, uint16 port, CompanyID join_as, const char *join_server_password, const char *join_company_password)
{
if (!_network_available) return;
if (address.GetPort() == 0) return;
if (port == 0) return;
strecpy(_settings_client.network.last_host, address.GetHostname(), lastof(_settings_client.network.last_host));
_settings_client.network.last_port = address.GetPort();
strecpy(_settings_client.network.last_host, hostname, lastof(_settings_client.network.last_host));
_settings_client.network.last_port = port;
_network_join_as = join_as;
_network_join_server_password = join_server_password;
_network_join_company_password = join_company_password;
if (_game_mode == GM_MENU) {
/* From the menu we can immediately continue with the actual join. */
NetworkClientJoinGame();
} else {
/* When already playing a game, first go back to the main menu. This
* disconnects the user from the current game, meaning we can safely
* load in the new. After all, there is little point in continueing to
* play on a server if we are connecting to another one.
*/
_switch_mode = SM_JOIN_GAME;
}
}
/**
* Actually perform the joining to the server. Use #NetworkClientConnectGame
* when you want to connect to a specific server/company. This function
* assumes _network_join is already fully set up.
*/
void NetworkClientJoinGame()
{
NetworkDisconnect();
NetworkInitialize();
_network_join_status = NETWORK_JOIN_STATUS_CONNECTING;
ShowJoinStatusWindow();
new TCPClientConnecter(address);
new TCPClientConnecter(NetworkAddress(_settings_client.network.last_host, _settings_client.network.last_port));
}
static void NetworkInitGameInfo()
@@ -1027,12 +1050,13 @@ static void NetworkGenerateServerId()
seprintf(_settings_client.network.network_id, lastof(_settings_client.network.network_id), "%s", hex_output);
}
void NetworkStartDebugLog(NetworkAddress address)
void NetworkStartDebugLog(const char *hostname, uint16 port)
{
extern SOCKET _debug_socket; // Comes from debug.c
DEBUG(net, 0, "Redirecting DEBUG() to %s:%d", address.GetHostname(), address.GetPort());
DEBUG(net, 0, "Redirecting DEBUG() to %s:%d", hostname, port);
NetworkAddress address(hostname, port);
SOCKET s = address.Connect();
if (s == INVALID_SOCKET) {
DEBUG(net, 0, "Failed to open socket for redirection DEBUG()");
@@ -1077,79 +1101,6 @@ void NetworkShutDown()
NetworkCoreShutdown();
}
/**
* How many hex digits of the git hash to include in network revision string.
* Determined as 10 hex digits + 2 characters for -g/-u/-m prefix.
*/
static const uint GITHASH_SUFFIX_LEN = 12;
/**
* Get the network version string used by this build.
* The returned string is guaranteed to be at most NETWORK_REVISON_LENGTH bytes.
*/
const char * GetNetworkRevisionString()
{
/* This will be allocated on heap and never free'd, but only once so not a "real" leak. */
static char *network_revision = nullptr;
if (!network_revision) {
/* Start by taking a chance on the full revision string. */
network_revision = stredup(_openttd_revision);
/* Ensure it's not longer than the packet buffer length. */
if (strlen(network_revision) >= NETWORK_REVISION_LENGTH) network_revision[NETWORK_REVISION_LENGTH - 1] = '\0';
/* Tag names are not mangled further. */
if (_openttd_revision_tagged) {
DEBUG(net, 1, "Network revision name is '%s'", network_revision);
return network_revision;
}
/* Prepare a prefix of the git hash.
* Size is length + 1 for terminator, +2 for -g prefix. */
assert(_openttd_revision_modified < 3);
char githash_suffix[GITHASH_SUFFIX_LEN + 1] = "-";
githash_suffix[1] = "gum"[_openttd_revision_modified];
for (uint i = 2; i < GITHASH_SUFFIX_LEN; i++) {
githash_suffix[i] = _openttd_revision_hash[i-2];
}
/* Where did the hash start in the original string?
* Overwrite from that position, unless that would go past end of packet buffer length. */
ptrdiff_t hashofs = strrchr(_openttd_revision, '-') - _openttd_revision;
if (hashofs + strlen(githash_suffix) + 1 > NETWORK_REVISION_LENGTH) hashofs = strlen(network_revision) - strlen(githash_suffix);
/* Replace the git hash in revision string. */
strecpy(network_revision + hashofs, githash_suffix, network_revision + NETWORK_REVISION_LENGTH);
assert(strlen(network_revision) < NETWORK_REVISION_LENGTH); // strlen does not include terminator, constant does, hence strictly less than
DEBUG(net, 1, "Network revision name is '%s'", network_revision);
}
return network_revision;
}
static const char *ExtractNetworkRevisionHash(const char *revstr)
{
return strrchr(revstr, '-');
}
/**
* Checks whether the given version string is compatible with our version.
* First tries to match the full string, if that fails, attempts to compare just git hashes.
* @param other the version string to compare to
*/
bool IsNetworkCompatibleVersion(const char *other)
{
if (strncmp(GetNetworkRevisionString(), other, NETWORK_REVISION_LENGTH - 1) == 0) return true;
/* If this version is tagged, then the revision string must be a complete match,
* since there is no git hash suffix in it.
* This is needed to avoid situations like "1.9.0-beta1" comparing equal to "2.0.0-beta1". */
if (_openttd_revision_tagged) return false;
const char *hash1 = ExtractNetworkRevisionHash(GetNetworkRevisionString());
const char *hash2 = ExtractNetworkRevisionHash(other);
return hash1 && hash2 && (strncmp(hash1, hash2, GITHASH_SUFFIX_LEN) == 0);
}
#ifdef __EMSCRIPTEN__
extern "C" {

View File

@@ -10,6 +10,7 @@
#include "../stdafx.h"
#include "../strings_func.h"
#include "../date_func.h"
#include "core/game_info.h"
#include "network_admin.h"
#include "network_base.h"
#include "network_server.h"

View File

@@ -14,6 +14,7 @@
#include "core/address.h"
#include "../core/pool_type.hpp"
#include "../company_type.h"
#include "../date_type.h"
/** Type for the pool with client information. */
typedef Pool<NetworkClientInfo, ClientIndex, 8, MAX_CLIENT_SLOTS, PT_NCLIENT> NetworkClientInfoPool;

View File

@@ -23,6 +23,7 @@
#include "../gfx_func.h"
#include "../error.h"
#include "../rev.h"
#include "core/game_info.h"
#include "network.h"
#include "network_base.h"
#include "network_client.h"
@@ -280,7 +281,7 @@ void ClientNetworkGameSocketHandler::ClientError(NetworkRecvStatus res)
#else
if (_sync_seed_1 != _random.state[0]) {
#endif
NetworkError(STR_NETWORK_ERROR_DESYNC);
ShowNetworkError(STR_NETWORK_ERROR_DESYNC);
DEBUG(desync, 1, "sync_err: %08x; %02x", _date, _date_fract);
DEBUG(net, 0, "Sync error detected!");
my_client->ClientError(NETWORK_RECV_STATUS_DESYNC);
@@ -723,7 +724,7 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_CHECK_NEWGRFS(P
/* Check all GRFs */
for (; grf_count > 0; grf_count--) {
GRFIdentifier c;
this->ReceiveGRFIdentifier(p, &c);
DeserializeGRFIdentifier(p, &c);
/* Check whether we know this GRF */
const GRFConfig *f = FindGRFConfig(c.grfid, FGCM_EXACT, c.md5sum);

View File

@@ -17,14 +17,13 @@
// #define DEBUG_DUMP_COMMANDS
// #define DEBUG_FAILED_DUMP_COMMANDS
#include "core/address.h"
#include "network_type.h"
#include "../console_type.h"
#include "../gfx_type.h"
#include "../openttd.h"
#include "../company_type.h"
#include "../string_type.h"
extern NetworkServerGameInfo _network_game_info;
extern NetworkCompanyState *_network_company_states;
extern ClientID _network_own_client_id;
@@ -44,12 +43,13 @@ void NetworkDisconnect(bool blocking = false, bool close_admins = true);
void NetworkGameLoop();
void NetworkBackgroundLoop();
void ParseConnectionString(const char **company, const char **port, char *connection_string);
void NetworkStartDebugLog(NetworkAddress address);
void NetworkStartDebugLog(const char *hostname, uint16 port);
void NetworkPopulateCompanyStats(NetworkCompanyStats *stats);
void NetworkUpdateClientInfo(ClientID client_id);
void NetworkClientsToSpectators(CompanyID cid);
void NetworkClientConnectGame(NetworkAddress address, CompanyID join_as, const char *join_server_password = nullptr, const char *join_company_password = nullptr);
void NetworkClientConnectGame(const char *hostname, uint16 port, CompanyID join_as, const char *join_server_password = nullptr, const char *join_company_password = nullptr);
void NetworkClientJoinGame();
void NetworkClientRequestMove(CompanyID company, const char *pass = "");
void NetworkClientSendRcon(const char *password, const char *command);
void NetworkClientSendChat(NetworkAction action, DestType type, int dest, const char *msg, int64 data = 0);

View File

@@ -11,6 +11,7 @@
#define NETWORK_GAMELIST_H
#include "core/address.h"
#include "core/game_info.h"
#include "network_type.h"
/** Structure with information shown in the game list (GUI) */

View File

@@ -1536,15 +1536,15 @@ struct NetworkLobbyWindow : public Window {
case WID_NL_JOIN: // Join company
/* Button can be clicked only when it is enabled. */
NetworkClientConnectGame(NetworkAddress(_settings_client.network.last_host, _settings_client.network.last_port), this->company);
NetworkClientConnectGame(_settings_client.network.last_host, _settings_client.network.last_port, this->company);
break;
case WID_NL_NEW: // New company
NetworkClientConnectGame(NetworkAddress(_settings_client.network.last_host, _settings_client.network.last_port), COMPANY_NEW_COMPANY);
NetworkClientConnectGame(_settings_client.network.last_host, _settings_client.network.last_port, COMPANY_NEW_COMPANY);
break;
case WID_NL_SPECTATE: // Spectate game
NetworkClientConnectGame(NetworkAddress(_settings_client.network.last_host, _settings_client.network.last_port), COMPANY_SPECTATOR);
NetworkClientConnectGame(_settings_client.network.last_host, _settings_client.network.last_port, COMPANY_SPECTATOR);
break;
case WID_NL_REFRESH: // Refresh

View File

@@ -11,6 +11,7 @@
#define NETWORK_GUI_H
#include "../company_type.h"
#include "../date_type.h"
#include "../economy_type.h"
#include "../window_type.h"
#include "network_type.h"

View File

@@ -135,8 +135,6 @@ void NetworkAddServer(const char *b);
void NetworkRebuildHostList();
void UpdateNetworkGameWindow();
bool IsNetworkCompatibleVersion(const char *version);
/* From network_command.cpp */
/**
* Everything we need to know about a command to be able to execute it.
@@ -155,7 +153,7 @@ void NetworkExecuteLocalCommandQueue();
void NetworkFreeLocalCommandQueue();
void NetworkSyncCommandQueue(NetworkClientSocket *cs);
void NetworkError(StringID error_string);
void ShowNetworkError(StringID error_string);
void NetworkTextMessage(NetworkAction action, TextColour colour, bool self_send, const char *name, const char *str = "", int64 data = 0);
uint NetworkCalculateLag(const NetworkClientSocket *cs);
StringID GetNetworkErrorMsg(NetworkErrorCode err);

View File

@@ -10,6 +10,7 @@
#include "../stdafx.h"
#include "../strings_func.h"
#include "../date_func.h"
#include "core/game_info.h"
#include "network_admin.h"
#include "network_server.h"
#include "network_udp.h"
@@ -363,6 +364,20 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::SendClientInfo(NetworkClientIn
return NETWORK_RECV_STATUS_OKAY;
}
/** Send the client information about the server. */
NetworkRecvStatus ServerNetworkGameSocketHandler::SendGameInfo()
{
NetworkGameInfo ngi;
FillNetworkGameInfo(ngi);
Packet *p = new Packet(PACKET_SERVER_GAME_INFO);
SerializeNetworkGameInfo(p, &ngi);
this->SendPacket(p);
return NETWORK_RECV_STATUS_OKAY;
}
/** Send the client information about the companies. */
NetworkRecvStatus ServerNetworkGameSocketHandler::SendCompanyInfo()
{
@@ -488,7 +503,7 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::SendNewGRFCheck()
p->Send_uint8 (grf_count);
for (c = _grfconfig; c != nullptr; c = c->next) {
if (!HasBit(c->flags, GCF_STATIC)) this->SendGRFIdentifier(p, &c->ident);
if (!HasBit(c->flags, GCF_STATIC)) SerializeGRFIdentifier(p, &c->ident);
}
this->SendPacket(p);
@@ -605,7 +620,7 @@ void ServerNetworkGameSocketHandler::CheckNextClientToSendMap(NetworkClientSocke
/** This sends the map to the client */
NetworkRecvStatus ServerNetworkGameSocketHandler::SendMap()
{
static uint sent_packets; // How many packets we did send successfully last time
static uint16 sent_packets; // How many packets we did send successfully last time
if (this->status < STATUS_AUTHORIZED) {
/* Illegal call, return error and ignore the packet */
@@ -665,8 +680,10 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::SendMap()
return NETWORK_RECV_STATUS_CONN_LOST;
case SPS_ALL_SENT:
/* All are sent, increase the sent_packets */
if (has_packets) sent_packets *= 2;
/* All are sent, increase the sent_packets but do not overflow! */
if (has_packets && sent_packets < std::numeric_limits<decltype(sent_packets)>::max() / 2) {
sent_packets *= 2;
}
break;
case SPS_PARTLY_SENT:
@@ -875,6 +892,11 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::SendConfigUpdate()
* DEF_SERVER_RECEIVE_COMMAND has parameter: NetworkClientSocket *cs, Packet *p
************/
NetworkRecvStatus ServerNetworkGameSocketHandler::Receive_CLIENT_GAME_INFO(Packet *p)
{
return this->SendGameInfo();
}
NetworkRecvStatus ServerNetworkGameSocketHandler::Receive_CLIENT_COMPANY_INFO(Packet *p)
{
return this->SendCompanyInfo();

View File

@@ -24,6 +24,7 @@ extern NetworkClientSocketPool _networkclientsocket_pool;
class ServerNetworkGameSocketHandler : public NetworkClientSocketPool::PoolItem<&_networkclientsocket_pool>, public NetworkGameSocketHandler, public TCPListenHandler<ServerNetworkGameSocketHandler, PACKET_SERVER_FULL, PACKET_SERVER_BANNED> {
protected:
NetworkRecvStatus Receive_CLIENT_JOIN(Packet *p) override;
NetworkRecvStatus Receive_CLIENT_GAME_INFO(Packet *p) override;
NetworkRecvStatus Receive_CLIENT_COMPANY_INFO(Packet *p) override;
NetworkRecvStatus Receive_CLIENT_GAME_PASSWORD(Packet *p) override;
NetworkRecvStatus Receive_CLIENT_COMPANY_PASSWORD(Packet *p) override;
@@ -40,6 +41,7 @@ protected:
NetworkRecvStatus Receive_CLIENT_NEWGRFS_CHECKED(Packet *p) override;
NetworkRecvStatus Receive_CLIENT_MOVE(Packet *p) override;
NetworkRecvStatus SendGameInfo();
NetworkRecvStatus SendCompanyInfo();
NetworkRecvStatus SendNewGRFCheck();
NetworkRecvStatus SendWelcome();

View File

@@ -10,7 +10,7 @@
#ifndef NETWORK_TYPE_H
#define NETWORK_TYPE_H
#include "core/game.h"
#include "core/config.h"
/** How many clients can we have */
static const uint MAX_CLIENTS = 255;

View File

@@ -16,6 +16,7 @@
#include "../date_func.h"
#include "../map_func.h"
#include "../debug.h"
#include "core/game_info.h"
#include "network_gamelist.h"
#include "network_internal.h"
#include "network_udp.h"
@@ -171,31 +172,10 @@ void ServerNetworkUDPSocketHandler::Receive_CLIENT_FIND_SERVER(Packet *p, Networ
}
NetworkGameInfo ngi;
/* Update some game_info */
ngi.clients_on = _network_game_info.clients_on;
ngi.start_date = ConvertYMDToDate(_settings_game.game_creation.starting_year, 0, 1);
ngi.server_lang = _settings_client.network.server_lang;
ngi.use_password = !StrEmpty(_settings_client.network.server_password);
ngi.clients_max = _settings_client.network.max_clients;
ngi.companies_on = (byte)Company::GetNumItems();
ngi.companies_max = _settings_client.network.max_companies;
ngi.spectators_on = NetworkSpectatorCount();
ngi.spectators_max = _settings_client.network.max_spectators;
ngi.game_date = _date;
ngi.map_width = MapSizeX();
ngi.map_height = MapSizeY();
ngi.map_set = _settings_game.game_creation.landscape;
ngi.dedicated = _network_dedicated;
ngi.grfconfig = _grfconfig;
strecpy(ngi.map_name, _network_game_info.map_name, lastof(ngi.map_name));
strecpy(ngi.server_name, _settings_client.network.server_name, lastof(ngi.server_name));
strecpy(ngi.server_revision, GetNetworkRevisionString(), lastof(ngi.server_revision));
FillNetworkGameInfo(ngi);
Packet packet(PACKET_UDP_SERVER_RESPONSE);
this->SendNetworkGameInfo(&packet, &ngi);
SerializeNetworkGameInfo(&packet, &ngi);
/* Let the client know that we are here */
this->SendPacket(&packet, client_addr);
@@ -286,7 +266,7 @@ void ServerNetworkUDPSocketHandler::Receive_CLIENT_GET_NEWGRFS(Packet *p, Networ
GRFIdentifier c;
const GRFConfig *f;
this->ReceiveGRFIdentifier(p, &c);
DeserializeGRFIdentifier(p, &c);
/* Find the matching GRF file */
f = FindGRFConfig(c.grfid, FGCM_EXACT, c.md5sum);
@@ -313,7 +293,7 @@ void ServerNetworkUDPSocketHandler::Receive_CLIENT_GET_NEWGRFS(Packet *p, Networ
/* The name could be an empty string, if so take the filename */
strecpy(name, in_reply[i]->GetName(), lastof(name));
this->SendGRFIdentifier(&packet, &in_reply[i]->ident);
SerializeGRFIdentifier(&packet, &in_reply[i]->ident);
packet.Send_string(name);
}
@@ -328,7 +308,6 @@ protected:
void Receive_SERVER_RESPONSE(Packet *p, NetworkAddress *client_addr) override;
void Receive_MASTER_RESPONSE_LIST(Packet *p, NetworkAddress *client_addr) override;
void Receive_SERVER_NEWGRFS(Packet *p, NetworkAddress *client_addr) override;
void HandleIncomingNetworkGameInfoGRFConfig(GRFConfig *config) override;
public:
virtual ~ClientNetworkUDPSocketHandler() {}
};
@@ -346,7 +325,7 @@ void ClientNetworkUDPSocketHandler::Receive_SERVER_RESPONSE(Packet *p, NetworkAd
item = NetworkGameListAddItem(*client_addr);
ClearGRFConfigList(&item->info.grfconfig);
this->ReceiveNetworkGameInfo(p, &item->info);
DeserializeNetworkGameInfo(p, &item->info);
item->info.compatible = true;
{
@@ -375,7 +354,7 @@ void ClientNetworkUDPSocketHandler::Receive_SERVER_RESPONSE(Packet *p, NetworkAd
packet.Send_uint8(in_request_count);
for (i = 0; i < in_request_count; i++) {
this->SendGRFIdentifier(&packet, &in_request[i]->ident);
SerializeGRFIdentifier(&packet, &in_request[i]->ident);
}
this->SendPacket(&packet, &item->address);
@@ -449,7 +428,7 @@ void ClientNetworkUDPSocketHandler::Receive_SERVER_NEWGRFS(Packet *p, NetworkAdd
char name[NETWORK_GRF_NAME_LENGTH];
GRFIdentifier c;
this->ReceiveGRFIdentifier(p, &c);
DeserializeGRFIdentifier(p, &c);
p->Recv_string(name, sizeof(name));
/* An empty name is not possible under normal circumstances
@@ -466,25 +445,6 @@ void ClientNetworkUDPSocketHandler::Receive_SERVER_NEWGRFS(Packet *p, NetworkAdd
}
}
void ClientNetworkUDPSocketHandler::HandleIncomingNetworkGameInfoGRFConfig(GRFConfig *config)
{
/* Find the matching GRF file */
const GRFConfig *f = FindGRFConfig(config->ident.grfid, FGCM_EXACT, config->ident.md5sum);
if (f == nullptr) {
/* Don't know the GRF, so mark game incompatible and the (possibly)
* already resolved name for this GRF (another server has sent the
* name of the GRF already */
config->name = FindUnknownGRFName(config->ident.grfid, config->ident.md5sum, true);
config->status = GCS_NOT_FOUND;
} else {
config->filename = f->filename;
config->name = f->name;
config->info = f->info;
config->url = f->url;
}
SetBit(config->flags, GCF_COPY);
}
/** Broadcast to all ips */
static void NetworkUDPBroadCast(NetworkUDPSocketHandler *socket)
{