Update to 1.11.2
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
};
|
||||
|
||||
|
||||
326
src/network/core/game_info.cpp
Normal file
326
src/network/core/game_info.cpp
Normal 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();
|
||||
}
|
||||
}
|
||||
107
src/network/core/game_info.h
Normal file
107
src/network/core/game_info.h
Normal 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 */
|
||||
174
src/network/core/os_abstraction.cpp
Normal file
174
src/network/core/os_abstraction.cpp
Normal 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);
|
||||
}
|
||||
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
90
src/network/core/tcp_content_type.h
Normal file
90
src/network/core/tcp_content_type.h
Normal 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 */
|
||||
@@ -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); }
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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" {
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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) */
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user