Merge tag '12.0-beta1' into jgrpp-beta

# Conflicts:
#	CMakeLists.txt
#	bin/ai/CMakeLists.txt
#	bin/game/CMakeLists.txt
#	src/build_vehicle_gui.cpp
#	src/console_cmds.cpp
#	src/core/overflowsafe_type.hpp
#	src/fios.cpp
#	src/lang/english.txt
#	src/lang/german.txt
#	src/lang/korean.txt
#	src/lang/polish.txt
#	src/network/core/game_info.cpp
#	src/network/core/game_info.h
#	src/network/core/tcp_game.cpp
#	src/network/core/tcp_game.h
#	src/network/network.cpp
#	src/network/network_client.cpp
#	src/network/network_client.h
#	src/network/network_coordinator.cpp
#	src/network/network_gui.cpp
#	src/network/network_server.cpp
#	src/network/network_server.h
#	src/newgrf_engine.cpp
#	src/openttd.cpp
#	src/rev.cpp.in
#	src/settings_type.h
#	src/train.h
#	src/train_cmd.cpp
This commit is contained in:
Jonathan G Rennison
2021-11-02 17:51:38 +00:00
146 changed files with 2391 additions and 2807 deletions

View File

@@ -26,6 +26,8 @@ add_files(
network_server.h
network_stun.cpp
network_stun.h
network_turn.cpp
network_turn.h
network_type.h
network_udp.cpp
network_udp.h

View File

@@ -30,6 +30,8 @@ add_files(
tcp_listen.h
tcp_stun.cpp
tcp_stun.h
tcp_turn.cpp
tcp_turn.h
udp.cpp
udp.h
)

View File

@@ -22,6 +22,7 @@ static const char * const NETWORK_CONTENT_MIRROR_URL = "/bananas";
static const uint16 NETWORK_COORDINATOR_SERVER_PORT = 3976; ///< The default port of the Game Coordinator server (TCP)
static const uint16 NETWORK_STUN_SERVER_PORT = 3975; ///< The default port of the STUN server (TCP)
static const uint16 NETWORK_TURN_SERVER_PORT = 3974; ///< The default port of the TURN server (TCP)
static const uint16 NETWORK_CONTENT_SERVER_PORT = 3978; ///< The default port of the content server (TCP)
static const uint16 NETWORK_CONTENT_MIRROR_PORT = 80; ///< The default port of the content mirror (TCP)
static const uint16 NETWORK_DEFAULT_PORT = 3979; ///< The default port of the game server (TCP & UDP)
@@ -48,9 +49,9 @@ static const uint16 TCP_MTU = 32767; ///< Numbe
static const uint16 COMPAT_MTU = 1460; ///< Number of bytes we can pack in a single packet for backward compatibility
static const byte NETWORK_GAME_ADMIN_VERSION = 1; ///< What version of the admin network do we use?
static const byte NETWORK_GAME_INFO_VERSION = 5; ///< What version of game-info do we use?
static const byte NETWORK_GAME_INFO_VERSION = 6; ///< What version of game-info do we use?
static const byte NETWORK_COMPANY_INFO_VERSION = 6; ///< What version of company info is this?
static const byte NETWORK_COORDINATOR_VERSION = 3; ///< What version of game-coordinator-protocol do we use?
static const byte NETWORK_COORDINATOR_VERSION = 5; ///< What version of game-coordinator-protocol do we use?
static const uint NETWORK_NAME_LENGTH = 80; ///< The maximum length of the server name and map name, in bytes including '\0'
static const uint NETWORK_COMPANY_NAME_LENGTH = 128; ///< The maximum length of the company name, in bytes including '\0'
@@ -95,14 +96,4 @@ static const uint NETWORK_GRF_NAME_LENGTH = 80; ///< Maxim
*/
static const uint NETWORK_MAX_GRF_COUNT = 255;
/**
* The number of landscapes in OpenTTD.
* This number must be equal to NUM_LANDSCAPE, but as this number is used
* within the network code and that the network code is shared with the
* masterserver/updater, it has to be declared in here too. In network.cpp
* there is a compile assertion to check that this NUM_LANDSCAPE is equal
* to NETWORK_NUM_LANDSCAPES.
*/
static const uint NETWORK_NUM_LANDSCAPES = 4;
#endif /* NETWORK_CORE_CONFIG_H */

View File

@@ -71,8 +71,6 @@ public:
* Reopen the socket so we can send/receive stuff again.
*/
void Reopen() { this->has_quit = false; }
void SendCompanyInformation(Packet *p, const struct Company *c, const struct NetworkCompanyStats *stats, uint max_len = NETWORK_COMPANY_NAME_LENGTH);
};
#endif /* NETWORK_CORE_CORE_H */

View File

@@ -134,7 +134,6 @@ void FillStaticNetworkServerGameInfo()
_network_game_info.start_date = ConvertYMDToDate(_settings_game.game_creation.starting_year, 0, 1);
_network_game_info.clients_max = _settings_client.network.max_clients;
_network_game_info.companies_max = _settings_client.network.max_companies;
_network_game_info.spectators_max = _settings_client.network.max_spectators;
_network_game_info.map_width = MapSizeX();
_network_game_info.map_height = MapSizeY();
_network_game_info.landscape = _settings_game.game_creation.landscape;
@@ -167,17 +166,15 @@ const NetworkServerGameInfo *GetCurrentNetworkServerGameInfo()
* 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.
* @param config The GRF to handle.
* @param name The name of the NewGRF, empty when unknown.
*/
static void HandleIncomingNetworkGameInfoGRFConfig(GRFConfig *config)
static void HandleIncomingNetworkGameInfoGRFConfig(GRFConfig *config, std::string name)
{
/* 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);
AddGRFTextToList(config->name, name.empty() ? GetString(STR_CONFIG_ERROR_INVALID_GRF_UNKNOWN) : name);
config->status = GCS_NOT_FOUND;
} else {
config->filename = f->filename;
@@ -193,7 +190,7 @@ static void HandleIncomingNetworkGameInfoGRFConfig(GRFConfig *config)
* @param p the packet to write the data to.
* @param info the NetworkGameInfo struct to serialize from.
*/
void SerializeNetworkGameInfo(Packet *p, const NetworkServerGameInfo *info)
void SerializeNetworkGameInfo(Packet *p, const NetworkServerGameInfo *info, bool send_newgrf_names)
{
p->Send_uint8 (NETWORK_GAME_INFO_VERSION);
@@ -205,6 +202,9 @@ void SerializeNetworkGameInfo(Packet *p, const NetworkServerGameInfo *info)
/* Update the documentation in game_info.h on changes
* to the NetworkGameInfo wire-protocol! */
/* NETWORK_GAME_INFO_VERSION = 6 */
p->Send_uint8(send_newgrf_names ? NST_GRFID_MD5_NAME : NST_GRFID_MD5);
/* NETWORK_GAME_INFO_VERSION = 5 */
GameInfo *game_info = Game::GetInfo();
p->Send_uint32(game_info == nullptr ? -1 : (uint32)game_info->GetVersion());
@@ -226,16 +226,11 @@ void SerializeNetworkGameInfo(Packet *p, const NetworkServerGameInfo *info)
p->Send_uint8(std::min<uint>(count, NETWORK_MAX_GRF_COUNT)); // Send number of GRFs
/* Send actual GRF Identifications */
uint index = 0;
for (c = info->grfconfig; c != nullptr; c = c->next) {
if (!HasBit(c->flags, GCF_STATIC)) {
if (index >= NETWORK_MAX_GRF_COUNT) {
break;
} else {
SerializeGRFIdentifier(p, &c->ident);
}
index++;
}
if (HasBit(c->flags, GCF_STATIC)) continue;
SerializeGRFIdentifier(p, &c->ident);
if (send_newgrf_names) p->Send_string(c->GetName());
}
}
@@ -246,17 +241,15 @@ void SerializeNetworkGameInfo(Packet *p, const NetworkServerGameInfo *info)
/* NETWORK_GAME_INFO_VERSION = 2 */
p->Send_uint8 (info->companies_max);
p->Send_uint8 (info->companies_on);
p->Send_uint8 (info->spectators_max);
p->Send_uint8 (info->clients_max); // Used to be max-spectators
/* NETWORK_GAME_INFO_VERSION = 1 */
p->Send_string(info->server_name);
p->Send_string(info->server_revision);
p->Send_uint8 (0); // Used to be 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(""); // Used to be map-name.
p->Send_uint16(info->map_width);
p->Send_uint16(info->map_height);
p->Send_uint8 (info->landscape);
@@ -268,15 +261,17 @@ void SerializeNetworkGameInfo(Packet *p, const NetworkServerGameInfo *info)
* @param p the packet to write the data to
* @param info the NetworkGameInfo struct to serialize
*/
void SerializeNetworkGameInfoExtended(Packet *p, const NetworkServerGameInfo *info, uint16 flags, uint16 version)
void SerializeNetworkGameInfoExtended(Packet *p, const NetworkServerGameInfo *info, uint16 flags, uint16 version, bool send_newgrf_names)
{
p->Send_uint8(0); // version num
version = std::max<uint16>(version, 1); // Version 1 is the max supported
p->Send_uint8(version); // version num
p->Send_uint32(info->game_date);
p->Send_uint32(info->start_date);
p->Send_uint8 (info->companies_max);
p->Send_uint8 (info->companies_on);
p->Send_uint8 (info->spectators_max);
p->Send_uint8 (info->clients_max); // Used to be max-spectators
p->Send_string(info->server_name);
p->Send_string(info->server_revision);
p->Send_uint8 (0); // Used to be server-lang.
@@ -290,6 +285,14 @@ void SerializeNetworkGameInfoExtended(Packet *p, const NetworkServerGameInfo *in
p->Send_uint8 (info->landscape);
p->Send_bool (info->dedicated);
if (version >= 1) {
GameInfo *game_info = Game::GetInfo();
p->Send_uint32(game_info == nullptr ? -1 : (uint32)game_info->GetVersion());
p->Send_string(game_info == nullptr ? "" : game_info->GetName());
p->Send_uint8(send_newgrf_names ? NST_GRFID_MD5_NAME : NST_GRFID_MD5);
}
{
/* Only send the GRF Identification (GRF_ID and MD5 checksum) of
* the GRFs that are needed, i.e. the ones that the server has
@@ -306,9 +309,10 @@ void SerializeNetworkGameInfoExtended(Packet *p, const NetworkServerGameInfo *in
/* Send actual GRF Identifications */
for (c = info->grfconfig; c != nullptr; c = c->next) {
if (!HasBit(c->flags, GCF_STATIC)) {
SerializeGRFIdentifier(p, &c->ident);
}
if (HasBit(c->flags, GCF_STATIC)) continue;
SerializeGRFIdentifier(p, &c->ident);
if (send_newgrf_names && version >= 1) p->Send_string(c->GetName());
}
}
}
@@ -318,11 +322,12 @@ void SerializeNetworkGameInfoExtended(Packet *p, const NetworkServerGameInfo *in
* @param p the packet to read the data from.
* @param info the NetworkGameInfo to deserialize into.
*/
void DeserializeNetworkGameInfo(Packet *p, NetworkGameInfo *info)
void DeserializeNetworkGameInfo(Packet *p, NetworkGameInfo *info, const GameInfoNewGRFLookupTable *newgrf_lookup_table)
{
static const Date MAX_DATE = ConvertYMDToDate(MAX_YEAR, 11, 31); // December is month 11
byte game_info_version = p->Recv_uint8();
NewGRFSerializationType newgrf_serialisation = NST_GRFID_MD5;
/*
* Please observe the order.
@@ -333,6 +338,11 @@ void DeserializeNetworkGameInfo(Packet *p, NetworkGameInfo *info)
* to the NetworkGameInfo wire-protocol! */
switch (game_info_version) {
case 6:
newgrf_serialisation = (NewGRFSerializationType)p->Recv_uint8();
if (newgrf_serialisation >= NST_END) return;
FALLTHROUGH;
case 5: {
info->gamescript_version = (int)p->Recv_uint32();
info->gamescript_name = p->Recv_string(NETWORK_NAME_LENGTH);
@@ -348,9 +358,31 @@ void DeserializeNetworkGameInfo(Packet *p, NetworkGameInfo *info)
if (num_grfs > NETWORK_MAX_GRF_COUNT) return;
for (i = 0; i < num_grfs; i++) {
NamedGRFIdentifier grf;
switch (newgrf_serialisation) {
case NST_GRFID_MD5:
DeserializeGRFIdentifier(p, &grf.ident);
break;
case NST_GRFID_MD5_NAME:
DeserializeGRFIdentifierWithName(p, &grf);
break;
case NST_LOOKUP_ID: {
if (newgrf_lookup_table == nullptr) return;
auto it = newgrf_lookup_table->find(p->Recv_uint32());
if (it == newgrf_lookup_table->end()) return;
grf = it->second;
break;
}
default:
NOT_REACHED();
}
GRFConfig *c = new GRFConfig();
DeserializeGRFIdentifier(p, &c->ident);
HandleIncomingNetworkGameInfoGRFConfig(c);
c->ident = grf.ident;
HandleIncomingNetworkGameInfoGRFConfig(c, grf.name);
/* Append GRFConfig to the list */
*dst = c;
@@ -367,13 +399,13 @@ void DeserializeNetworkGameInfo(Packet *p, NetworkGameInfo *info)
case 2:
info->companies_max = p->Recv_uint8 ();
info->companies_on = p->Recv_uint8 ();
info->spectators_max = p->Recv_uint8 ();
p->Recv_uint8(); // Used to contain max-spectators.
FALLTHROUGH;
case 1:
info->server_name = p->Recv_string(NETWORK_NAME_LENGTH);
info->server_revision = p->Recv_string(NETWORK_REVISION_LENGTH);
p->Recv_uint8 (); // Used to contain server-lang.
if (game_info_version < 6) p->Recv_uint8 (); // Used to contain server-lang.
info->use_password = p->Recv_bool ();
info->clients_max = p->Recv_uint8 ();
info->clients_on = p->Recv_uint8 ();
@@ -382,13 +414,13 @@ void DeserializeNetworkGameInfo(Packet *p, NetworkGameInfo *info)
info->game_date = p->Recv_uint16() + DAYS_TILL_ORIGINAL_BASE_YEAR;
info->start_date = p->Recv_uint16() + DAYS_TILL_ORIGINAL_BASE_YEAR;
}
while (p->Recv_uint8() != 0) {} // Used to contain the map-name.
if (game_info_version < 6) while (p->Recv_uint8() != 0) {} // Used to contain the map-name.
info->map_width = p->Recv_uint16();
info->map_height = p->Recv_uint16();
info->landscape = p->Recv_uint8 ();
info->dedicated = p->Recv_bool ();
if (info->landscape >= NETWORK_NUM_LANDSCAPES) info->landscape = 0;
if (info->landscape >= NUM_LANDSCAPE) info->landscape = 0;
}
}
@@ -402,13 +434,15 @@ void DeserializeNetworkGameInfoExtended(Packet *p, NetworkGameInfo *info)
static const Date MAX_DATE = ConvertYMDToDate(MAX_YEAR, 11, 31); // December is month 11
const uint8 version = p->Recv_uint8();
if (version > 0) return; // Unknown version
if (version > 1) return; // Unknown version
NewGRFSerializationType newgrf_serialisation = NST_GRFID_MD5;
info->game_date = Clamp(p->Recv_uint32(), 0, MAX_DATE);
info->start_date = Clamp(p->Recv_uint32(), 0, MAX_DATE);
info->companies_max = p->Recv_uint8 ();
info->companies_on = p->Recv_uint8 ();
info->spectators_max = p->Recv_uint8 ();
p->Recv_uint8(); // Used to contain max-spectators.
info->server_name = p->Recv_string(NETWORK_NAME_LENGTH);
info->server_revision = p->Recv_string(NETWORK_LONG_REVISION_LENGTH);
p->Recv_uint8 (); // Used to contain server-lang.
@@ -420,8 +454,17 @@ void DeserializeNetworkGameInfoExtended(Packet *p, NetworkGameInfo *info)
info->map_width = p->Recv_uint32();
info->map_height = p->Recv_uint32();
info->landscape = p->Recv_uint8 ();
if (info->landscape >= NUM_LANDSCAPE) info->landscape = 0;
info->dedicated = p->Recv_bool ();
if (version >= 1) {
info->gamescript_version = (int)p->Recv_uint32();
info->gamescript_name = p->Recv_string(NETWORK_NAME_LENGTH);
newgrf_serialisation = (NewGRFSerializationType)p->Recv_uint8();
if (newgrf_serialisation >= NST_END) return;
}
{
GRFConfig **dst = &info->grfconfig;
uint i;
@@ -431,17 +474,34 @@ void DeserializeNetworkGameInfoExtended(Packet *p, NetworkGameInfo *info)
if (num_grfs > MAX_NON_STATIC_GRF_COUNT) return;
for (i = 0; i < num_grfs; i++) {
NamedGRFIdentifier grf;
switch (newgrf_serialisation) {
case NST_GRFID_MD5:
DeserializeGRFIdentifier(p, &grf.ident);
break;
case NST_GRFID_MD5_NAME:
DeserializeGRFIdentifierWithName(p, &grf);
break;
case NST_LOOKUP_ID: {
DEBUG(net, 0, "Unexpected NST_LOOKUP_ID in DeserializeNetworkGameInfoExtended");
return;
}
default:
NOT_REACHED();
}
GRFConfig *c = new GRFConfig();
DeserializeGRFIdentifier(p, &c->ident);
HandleIncomingNetworkGameInfoGRFConfig(c);
c->ident = grf.ident;
HandleIncomingNetworkGameInfoGRFConfig(c, grf.name);
/* Append GRFConfig to the list */
*dst = c;
dst = &c->next;
}
}
if (info->landscape >= NETWORK_NUM_LANDSCAPES) info->landscape = 0;
}
/**
@@ -471,3 +531,14 @@ void DeserializeGRFIdentifier(Packet *p, GRFIdentifier *grf)
grf->md5sum[j] = p->Recv_uint8();
}
}
/**
* Deserializes the NamedGRFIdentifier (GRF ID, MD5 checksum and name) from the packet
* @param p the packet to read the data from.
* @param grf the NamedGRFIdentifier to deserialize.
*/
void DeserializeGRFIdentifierWithName(Packet *p, NamedGRFIdentifier *grf)
{
DeserializeGRFIdentifier(p, &grf->ident);
grf->name = p->Recv_string(NETWORK_GRF_NAME_LENGTH);
}

View File

@@ -17,6 +17,8 @@
#include "../../newgrf_config.h"
#include "../../date_type.h"
#include <unordered_map>
/*
* 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
@@ -25,11 +27,32 @@
* 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
* 6+ 1 type of storage for the NewGRFs below:
* 0 = NewGRF ID and MD5 checksum.
* Used as default for version 5 and below, and for
* later game updates to the Game Coordinator.
* 1 = NewGRF ID, MD5 checksum and name.
* Used for direct requests and the first game
* update to Game Coordinator.
* 2 = Index in NewGRF lookup table.
* Used for sending server listing from the Game
* Coordinator to the clients.
*
* 5+ 4 version number of the Game Script (-1 is case none is selected).
* 5+ var string with the name of the Game Script.
*
* 4+ 1 number of GRFs attached (n).
* 4+ n * var identifiers for GRF files. Consists of:
* Note: the 'vN' refers to packet version and 'type'
* refers to the v6+ type of storage for the NewGRFs.
* - 4 byte variable with the GRF ID.
* For v4, v5, and v6+ in case of type 0 and/or type 1.
* - 16 bytes with the MD5 checksum of the GRF.
* For v4, v5, and v6+ in case of type 0 and/or type 1.
* - string with name of NewGRF.
* For v6+ in case of type 1.
* - 4 byte lookup table index.
* For v6+ in case of type 2.
*
* 3+ 4 current game date in days since 1-1-0 (DMY)
* 3+ 4 game introduction date in days since 1-1-0 (DMY)
@@ -40,7 +63,7 @@
*
* 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
* 1 - 5 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
@@ -48,7 +71,7 @@
* 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 - 5 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:
@@ -56,6 +79,14 @@
* 1+ 1 whether the server is dedicated (0 = no, 1 = yes)
*/
/** The different types/ways a NewGRF can be serialized in the GameInfo since version 6. */
enum NewGRFSerializationType {
NST_GRFID_MD5 = 0, ///< Unique GRF ID and MD5 checksum.
NST_GRFID_MD5_NAME = 1, ///< Unique GRF ID, MD5 checksum and name.
NST_LOOKUP_ID = 2, ///< Unique ID into a lookup table that is sent before.
NST_END ///< The end of the list (period).
};
/**
* The game information that is sent from the server to the client.
*/
@@ -74,7 +105,6 @@ struct NetworkServerGameInfo {
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 landscape; ///< The used landscape
int gamescript_version; ///< Version of the gamescript.
std::string gamescript_name; ///< Name of the gamescript.
@@ -89,6 +119,17 @@ struct NetworkGameInfo : NetworkServerGameInfo {
bool compatible; ///< Can we connect to this server or not? (based on server_revision _and_ grf_match
};
/**
* Container to hold the GRF identifier (GRF ID + MD5 checksum) and the name
* associated with that NewGRF.
*/
struct NamedGRFIdentifier {
GRFIdentifier ident; ///< The unique identifier of the NewGRF.
std::string name; ///< The name of the NewGRF.
};
/** Lookup table for the GameInfo in case of #NST_LOOKUP_ID. */
typedef std::unordered_map<uint32, NamedGRFIdentifier> GameInfoNewGRFLookupTable;
extern NetworkServerGameInfo _network_game_info;
const char *GetNetworkRevisionString();
@@ -99,11 +140,12 @@ void FillStaticNetworkServerGameInfo();
const NetworkServerGameInfo *GetCurrentNetworkServerGameInfo();
void DeserializeGRFIdentifier(Packet *p, GRFIdentifier *grf);
void DeserializeGRFIdentifierWithName(Packet *p, NamedGRFIdentifier *grf);
void SerializeGRFIdentifier(Packet *p, const GRFIdentifier *grf);
void DeserializeNetworkGameInfo(Packet *p, NetworkGameInfo *info);
void DeserializeNetworkGameInfo(Packet *p, NetworkGameInfo *info, const GameInfoNewGRFLookupTable *newgrf_lookup_table = nullptr);
void DeserializeNetworkGameInfoExtended(Packet *p, NetworkGameInfo *info);
void SerializeNetworkGameInfo(Packet *p, const NetworkServerGameInfo *info);
void SerializeNetworkGameInfoExtended(Packet *p, const NetworkServerGameInfo *info, uint16 flags, uint16 version);
void SerializeNetworkGameInfo(Packet *p, const NetworkServerGameInfo *info, bool send_newgrf_names = true);
void SerializeNetworkGameInfoExtended(Packet *p, const NetworkServerGameInfo *info, uint16 flags, uint16 version, bool send_newgrf_names = true);
#endif /* NETWORK_CORE_GAME_INFO_H */

View File

@@ -42,6 +42,8 @@ bool NetworkCoordinatorSocketHandler::HandlePacket(Packet *p)
case PACKET_COORDINATOR_GC_STUN_REQUEST: return this->Receive_GC_STUN_REQUEST(p);
case PACKET_COORDINATOR_SERCLI_STUN_RESULT: return this->Receive_SERCLI_STUN_RESULT(p);
case PACKET_COORDINATOR_GC_STUN_CONNECT: return this->Receive_GC_STUN_CONNECT(p);
case PACKET_COORDINATOR_GC_NEWGRF_LOOKUP: return this->Receive_GC_NEWGRF_LOOKUP(p);
case PACKET_COORDINATOR_GC_TURN_CONNECT: return this->Receive_GC_TURN_CONNECT(p);
default:
DEBUG(net, 0, "[tcp/coordinator] Received invalid packet type %u", type);
@@ -99,3 +101,5 @@ bool NetworkCoordinatorSocketHandler::Receive_GC_DIRECT_CONNECT(Packet *p) { ret
bool NetworkCoordinatorSocketHandler::Receive_GC_STUN_REQUEST(Packet *p) { return this->ReceiveInvalidPacket(PACKET_COORDINATOR_GC_STUN_REQUEST); }
bool NetworkCoordinatorSocketHandler::Receive_SERCLI_STUN_RESULT(Packet *p) { return this->ReceiveInvalidPacket(PACKET_COORDINATOR_SERCLI_STUN_RESULT); }
bool NetworkCoordinatorSocketHandler::Receive_GC_STUN_CONNECT(Packet *p) { return this->ReceiveInvalidPacket(PACKET_COORDINATOR_GC_STUN_CONNECT); }
bool NetworkCoordinatorSocketHandler::Receive_GC_NEWGRF_LOOKUP(Packet *p) { return this->ReceiveInvalidPacket(PACKET_COORDINATOR_GC_NEWGRF_LOOKUP); }
bool NetworkCoordinatorSocketHandler::Receive_GC_TURN_CONNECT(Packet *p) { return this->ReceiveInvalidPacket(PACKET_COORDINATOR_GC_TURN_CONNECT); }

View File

@@ -41,6 +41,8 @@ enum PacketCoordinatorType {
PACKET_COORDINATOR_GC_STUN_REQUEST, ///< Game Coordinator tells client/server to initiate a STUN request.
PACKET_COORDINATOR_SERCLI_STUN_RESULT, ///< Client/server informs the Game Coordinator of the result of the STUN request.
PACKET_COORDINATOR_GC_STUN_CONNECT, ///< Game Coordinator tells client/server to connect() reusing the STUN local address.
PACKET_COORDINATOR_GC_NEWGRF_LOOKUP, ///< Game Coordinator informs client about NewGRF lookup table updates needed for GC_LISTING.
PACKET_COORDINATOR_GC_TURN_CONNECT, ///< Game Coordinator tells client/server to connect to a specific TURN server.
PACKET_COORDINATOR_END, ///< Must ALWAYS be on the end of this list!! (period)
};
@@ -52,6 +54,7 @@ enum ConnectionType {
CONNECTION_TYPE_ISOLATED, ///< The Game Coordinator failed to find a way to connect to your server. Nobody will be able to join.
CONNECTION_TYPE_DIRECT, ///< The Game Coordinator can directly connect to your server.
CONNECTION_TYPE_STUN, ///< The Game Coordinator can connect to your server via a STUN request.
CONNECTION_TYPE_TURN, ///< The Game Coordinator needs you to connect to a relay.
};
/**
@@ -125,6 +128,7 @@ protected:
* uint8 Game Coordinator protocol version.
* uint8 Game-info version used by this client.
* string Revision of the client.
* uint32 (Game Coordinator protocol >= 4) Cursor as received from GC_NEWGRF_LOOKUP, or zero.
*
* @param p The packet that was just received.
* @return True upon success, otherwise false.
@@ -263,6 +267,43 @@ protected:
*/
virtual bool Receive_GC_STUN_CONNECT(Packet *p);
/**
* Game Coordinator informs the client of updates for the NewGRFs lookup table
* as used by the NewGRF deserialization in GC_LISTING.
* This packet is sent after a CLIENT_LISTING request, but before GC_LISTING.
*
* uint32 Lookup table cursor.
* uint16 Number of NewGRFs in the packet, with for each of the NewGRFs:
* uint32 Lookup table index for the NewGRF.
* uint32 Unique NewGRF ID.
* byte[16] MD5 checksum of the NewGRF
* string Name of the NewGRF.
*
* The lookup table built using these packets are used by the deserialisation
* of the NewGRFs for servers in the GC_LISTING. These updates are additive,
* i.e. each update will add NewGRFs but never remove them. However, this
* lookup table is specific to the connection with the Game Coordinator, and
* should be considered invalid after disconnecting from the Game Coordinator.
*
* @param p The packet that was just received.
* @return True upon success, otherwise false.
*/
virtual bool Receive_GC_NEWGRF_LOOKUP(Packet *p);
/**
* Game Coordinator requests that we make a connection to the indicated
* peer, which is a TURN server.
*
* string Token to track the current connect request.
* uint8 Tracking number to track current connect request.
* string Ticket to hand over to the TURN server.
* string Connection string of the TURN server.
*
* @param p The packet that was just received.
* @return True upon success, otherwise false.
*/
virtual bool Receive_GC_TURN_CONNECT(Packet *p);
bool HandlePacket(Packet *p);
public:
/**

View File

@@ -112,7 +112,7 @@ NetworkRecvStatus NetworkGameSocketHandler::CloseConnection(bool error)
_networking = false;
ShowErrorMessage(STR_NETWORK_ERROR_LOSTCONNECTION, INVALID_STRING_ID, WL_CRITICAL);
return NETWORK_RECV_STATUS_CLIENT_QUIT;
return this->CloseConnection(NETWORK_RECV_STATUS_CLIENT_QUIT);
}
return this->CloseConnection(NETWORK_RECV_STATUS_CONNECTION_LOST);
@@ -141,8 +141,6 @@ NetworkRecvStatus NetworkGameSocketHandler::HandlePacket(Packet *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_SERVER_GAME_INFO_EXTENDED: return this->Receive_SERVER_GAME_INFO_EXTENDED(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);
case PACKET_SERVER_NEED_GAME_PASSWORD: return this->Receive_SERVER_NEED_GAME_PASSWORD(p);
case PACKET_SERVER_NEED_COMPANY_PASSWORD: return this->Receive_SERVER_NEED_COMPANY_PASSWORD(p);
@@ -234,8 +232,6 @@ NetworkRecvStatus NetworkGameSocketHandler::Receive_SERVER_ERROR(Packet *p) { re
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_SERVER_GAME_INFO_EXTENDED(Packet *p) { return this->ReceiveInvalidPacket(PACKET_SERVER_GAME_INFO_EXTENDED); }
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); }
NetworkRecvStatus NetworkGameSocketHandler::Receive_SERVER_NEED_GAME_PASSWORD(Packet *p) { return this->ReceiveInvalidPacket(PACKET_SERVER_NEED_GAME_PASSWORD); }
NetworkRecvStatus NetworkGameSocketHandler::Receive_SERVER_NEED_COMPANY_PASSWORD(Packet *p) { return this->ReceiveInvalidPacket(PACKET_SERVER_NEED_COMPANY_PASSWORD); }

View File

@@ -39,9 +39,9 @@ enum PacketGameType {
PACKET_CLIENT_JOIN, ///< The client telling the server it wants to join.
PACKET_SERVER_ERROR, ///< Server sending an error message to the client.
/* Packets used for the pre-game lobby. */
PACKET_CLIENT_COMPANY_INFO, ///< Request information about all companies.
PACKET_SERVER_COMPANY_INFO, ///< Information about a single company.
/* Unused packet types, formerly used for the pre-game lobby. */
PACKET_CLIENT_UNUSED, ///< Unused.
PACKET_SERVER_UNUSED, ///< Unused.
/* Packets used to get the game info. */
PACKET_SERVER_GAME_INFO, ///< Information about the server.
@@ -221,40 +221,6 @@ protected:
*/
virtual NetworkRecvStatus Receive_SERVER_GAME_INFO_EXTENDED(Packet *p);
/**
* Request company information (in detail).
* @param p The packet that was just received.
*/
virtual NetworkRecvStatus Receive_CLIENT_COMPANY_INFO(Packet *p);
/**
* Sends information about the companies (one packet per company):
* uint8 Version of the structure of this packet (NETWORK_COMPANY_INFO_VERSION).
* bool Contains data (false marks the end of updates).
* uint8 ID of the company.
* string Name of the company.
* uint32 Year the company was inaugurated.
* uint64 Value.
* uint64 Money.
* uint64 Income.
* uint16 Performance (last quarter).
* bool Company is password protected.
* uint16 Number of trains.
* uint16 Number of lorries.
* uint16 Number of busses.
* uint16 Number of planes.
* uint16 Number of ships.
* uint16 Number of train stations.
* uint16 Number of lorry stations.
* uint16 Number of bus stops.
* uint16 Number of airports and heliports.
* uint16 Number of harbours.
* bool Company is an AI.
* string Client names (comma separated list)
* @param p The packet that was just received.
*/
virtual NetworkRecvStatus Receive_SERVER_COMPANY_INFO(Packet *p);
/**
* Send information about a client:
* uint32 ID of the client (always unique on a server. 1 = server, 0 is invalid).

View File

@@ -0,0 +1,70 @@
/*
* 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_turn.cpp Basic functions to receive and send TURN packets.
*/
#include "../../stdafx.h"
#include "../../date_func.h"
#include "../../debug.h"
#include "tcp_turn.h"
#include "../../safeguards.h"
/**
* Handle the given packet, i.e. pass it to the right
* parser receive command.
* @param p the packet to handle
* @return true if we should immediately handle further packets, false otherwise
*/
bool NetworkTurnSocketHandler::HandlePacket(Packet *p)
{
PacketTurnType type = (PacketTurnType)p->Recv_uint8();
switch (type) {
case PACKET_TURN_TURN_ERROR: return this->Receive_TURN_ERROR(p);
case PACKET_TURN_SERCLI_CONNECT: return this->Receive_SERCLI_CONNECT(p);
case PACKET_TURN_TURN_CONNECTED: return this->Receive_TURN_CONNECTED(p);
default:
DEBUG(net, 0, "[tcp/turn] Received invalid packet type %u", type);
return false;
}
}
/**
* Receive a packet at TCP level
* @return Whether at least one packet was received.
*/
bool NetworkTurnSocketHandler::ReceivePackets()
{
std::unique_ptr<Packet> p;
static const int MAX_PACKETS_TO_RECEIVE = 4;
int i = MAX_PACKETS_TO_RECEIVE;
while (--i != 0 && (p = this->ReceivePacket()) != nullptr) {
bool cont = this->HandlePacket(p.get());
if (!cont) return true;
}
return i != MAX_PACKETS_TO_RECEIVE - 1;
}
/**
* Helper for logging receiving invalid packets.
* @param type The received packet type.
* @return Always false, as it's an error.
*/
bool NetworkTurnSocketHandler::ReceiveInvalidPacket(PacketTurnType type)
{
DEBUG(net, 0, "[tcp/turn] Received illegal packet type %u", type);
return false;
}
bool NetworkTurnSocketHandler::Receive_TURN_ERROR(Packet *p) { return this->ReceiveInvalidPacket(PACKET_TURN_TURN_ERROR); }
bool NetworkTurnSocketHandler::Receive_SERCLI_CONNECT(Packet *p) { return this->ReceiveInvalidPacket(PACKET_TURN_SERCLI_CONNECT); }
bool NetworkTurnSocketHandler::Receive_TURN_CONNECTED(Packet *p) { return this->ReceiveInvalidPacket(PACKET_TURN_TURN_CONNECTED); }

View File

@@ -0,0 +1,79 @@
/*
* 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_turn.h Basic functions to receive and send TCP packets to/from the TURN server.
*/
#ifndef NETWORK_CORE_TCP_TURN_H
#define NETWORK_CORE_TCP_TURN_H
#include "os_abstraction.h"
#include "tcp.h"
#include "packet.h"
#include "game_info.h"
/** Enum with all types of TCP TURN packets. The order MUST not be changed. **/
enum PacketTurnType {
PACKET_TURN_TURN_ERROR, ///< TURN server is unable to relay.
PACKET_TURN_SERCLI_CONNECT, ///< Client or server is connecting to the TURN server.
PACKET_TURN_TURN_CONNECTED, ///< TURN server indicates the socket is now being relayed.
PACKET_TURN_END, ///< Must ALWAYS be on the end of this list!! (period)
};
/** Base socket handler for all TURN TCP sockets. */
class NetworkTurnSocketHandler : public NetworkTCPSocketHandler {
protected:
bool ReceiveInvalidPacket(PacketTurnType type);
/**
* TURN server was unable to connect the client or server based on the
* token. Most likely cause is an invalid token or the other side that
* hasn't connected in a reasonable amount of time.
*
* @param p The packet that was just received.
* @return True upon success, otherwise false.
*/
virtual bool Receive_TURN_ERROR(Packet *p);
/**
* Client or servers wants to connect to the TURN server (on request by
* the Game Coordinator).
*
* uint8 Game Coordinator protocol version.
* string Token to track the current TURN request.
*
* @param p The packet that was just received.
* @return True upon success, otherwise false.
*/
virtual bool Receive_SERCLI_CONNECT(Packet *p);
/**
* TURN server has connected client and server together and will now relay
* all packets to each other. No further TURN packets should be send over
* this socket, and the socket should be handed over to the game protocol.
*
* string Hostname of the peer. This can be used to check if a client is not banned etc.
*
* @param p The packet that was just received.
* @return True upon success, otherwise false.
*/
virtual bool Receive_TURN_CONNECTED(Packet *p);
bool HandlePacket(Packet *p);
public:
/**
* Create a new cs socket handler for a given cs.
* @param s the socket we are connected with.
* @param address IP etc. of the client.
*/
NetworkTurnSocketHandler(SOCKET s = INVALID_SOCKET) : NetworkTCPSocketHandler(s) {}
bool ReceivePackets();
};
#endif /* NETWORK_CORE_TCP_TURN_H */

View File

@@ -84,8 +84,6 @@ uint8 _last_sync_tick_skip_counter; ///< "
bool _network_first_time; ///< Whether we have finished joining or not.
CompanyMask _network_company_passworded; ///< Bitmask of the password status of all companies.
/* Check whether NETWORK_NUM_LANDSCAPES is still in sync with NUM_LANDSCAPE */
static_assert((int)NETWORK_NUM_LANDSCAPES == (int)NUM_LANDSCAPE);
static_assert((int)NETWORK_COMPANY_NAME_LENGTH == MAX_LENGTH_COMPANY_NAME_CHARS * MAX_CHAR_LENGTH);
/** The amount of clients connected */
@@ -621,7 +619,7 @@ void NetworkClose(bool close_admins)
MyClient::my_client->CloseConnection(NETWORK_RECV_STATUS_CLIENT_QUIT);
}
_network_coordinator_client.CloseAllTokens();
_network_coordinator_client.CloseAllConnections();
}
TCPConnecter::KillAll();
@@ -672,7 +670,7 @@ public:
{
_networking = true;
new ClientNetworkGameSocketHandler(s, this->connection_string);
MyClient::SendInformationQuery(false);
MyClient::SendInformationQuery();
}
};
@@ -689,42 +687,6 @@ void NetworkQueryServer(const std::string &connection_string)
new TCPQueryConnecter(connection_string);
}
/** Non blocking connection to query servers for their game and company info. */
class TCPLobbyQueryConnecter : TCPServerConnecter {
private:
std::string connection_string;
public:
TCPLobbyQueryConnecter(const std::string &connection_string) : TCPServerConnecter(connection_string, NETWORK_DEFAULT_PORT), connection_string(connection_string) {}
void OnFailure() override
{
DeleteWindowById(WC_NETWORK_WINDOW, WN_NETWORK_WINDOW_LOBBY);
ShowErrorMessage(STR_NETWORK_ERROR_NOCONNECTION, INVALID_STRING_ID, WL_ERROR);
}
void OnConnect(SOCKET s) override
{
_networking = true;
new ClientNetworkGameSocketHandler(s, this->connection_string);
MyClient::SendInformationQuery(true);
}
};
/**
* Query a server to fetch the game-info for the lobby.
* @param connection_string the address to query.
*/
void NetworkQueryLobbyServer(const std::string &connection_string)
{
if (!_network_available) return;
NetworkInitialize();
new TCPLobbyQueryConnecter(connection_string);
}
/**
* Validates an address entered as a string and adds the server to
* the list. If you use this function, the games will be marked

View File

@@ -140,8 +140,8 @@ void ClientNetworkEmergencySave()
if (!_networking) return;
if (!ClientNetworkGameSocketHandler::EmergencySavePossible()) return;
static int _netsave_ctr = 0;
DoAutoOrNetsave(_netsave_ctr, true);
static FiosNumberedSaveName _netsave_ctr("netsave");
DoAutoOrNetsave(_netsave_ctr, false);
}
@@ -180,29 +180,24 @@ ClientNetworkGameSocketHandler::~ClientNetworkGameSocketHandler()
NetworkRecvStatus ClientNetworkGameSocketHandler::CloseConnection(NetworkRecvStatus status)
{
assert(status != NETWORK_RECV_STATUS_OKAY);
/*
* Sending a message just before leaving the game calls cs->SendPackets.
* This might invoke this function, which means that when we close the
* connection after cs->SendPackets we will close an already closed
* connection. This handles that case gracefully without having to make
* that code any more complex or more aware of the validity of the socket.
*/
if (this->sock == INVALID_SOCKET) return status;
assert(this->sock != INVALID_SOCKET);
if (this->status == STATUS_CLOSING) return status;
DEBUG(net, 3, "Shutting down client connection %d", this->client_id);
if (!this->HasClientQuit()) {
DEBUG(net, 3, "Closed client connection %d", this->client_id);
SetBlocking(this->sock);
SetBlocking(this->sock);
this->SendPackets(true);
this->SendPackets(true);
ShutdownSocket(this->sock, false, true, 2);
ShutdownSocket(this->sock, false, true, 2);
/* Wait a number of ticks so our leave message can reach the server.
* This is especially needed for Windows servers as they seem to get
* the "socket is closed" message before receiving our leave message,
* which would trigger the server to close the connection as well. */
CSleep(3 * MILLISECONDS_PER_TICK);
/* Wait a number of ticks so our leave message can reach the server.
* This is especially needed for Windows servers as they seem to get
* the "socket is closed" message before receiving our leave message,
* which would trigger the server to close the connection as well. */
CSleep(3 * MILLISECONDS_PER_TICK);
}
DEBUG(net, 1, "Shutdown client connection %d", this->client_id);
@@ -291,7 +286,7 @@ void ClientNetworkGameSocketHandler::ClientError(NetworkRecvStatus res)
/* static */ void ClientNetworkGameSocketHandler::Send()
{
my_client->SendPackets();
my_client->CheckConnection();
if (my_client != nullptr) my_client->CheckConnection();
}
/**
@@ -385,8 +380,8 @@ static std::string _password_server_id;
/** Maximum number of companies of the currently joined server. */
static uint8 _network_server_max_companies;
/** Maximum number of spectators of the currently joined server. */
static uint8 _network_server_max_spectators;
/** The current name of the server you are on. */
std::string _network_server_name;
/** Information about the game to join to. */
NetworkJoinInfo _network_join;
@@ -402,7 +397,7 @@ static_assert(NETWORK_SERVER_ID_LENGTH == 16 * 2 + 1);
/**
* Query the server for server information.
*/
NetworkRecvStatus ClientNetworkGameSocketHandler::SendInformationQuery(bool request_company_info)
NetworkRecvStatus ClientNetworkGameSocketHandler::SendInformationQuery()
{
my_client->status = STATUS_GAME_INFO;
@@ -413,14 +408,6 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::SendInformationQuery(bool requ
p->Send_uint16(0); // version
my_client->SendPacket(p);
if (request_company_info) {
my_client->status = STATUS_COMPANY_INFO;
_network_join_status = NETWORK_JOIN_STATUS_GETTING_COMPANY_INFO;
SetWindowDirty(WC_NETWORK_STATUS_WINDOW, WN_NETWORK_STATUS_WINDOW_JOIN);
my_client->SendPacket(new Packet(PACKET_CLIENT_COMPANY_INFO));
}
return NETWORK_RECV_STATUS_OKAY;
}
@@ -696,13 +683,9 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_BANNED(Packet *
NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_GAME_INFO(Packet *p)
{
if (this->status != STATUS_COMPANY_INFO && this->status != STATUS_GAME_INFO) return NETWORK_RECV_STATUS_MALFORMED_PACKET;
if (this->status != STATUS_GAME_INFO) return NETWORK_RECV_STATUS_MALFORMED_PACKET;
NetworkGameList *item = GetLobbyGameInfo();
if (item == nullptr) {
/* This is not the lobby, so add it to the game list. */
item = NetworkGameListAddItem(this->connection_string);
}
NetworkGameList *item = NetworkGameListAddItem(this->connection_string);
/* Clear any existing GRFConfig chain. */
ClearGRFConfigList(&item->info.grfconfig);
@@ -713,20 +696,16 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_GAME_INFO(Packe
/* Ensure we consider the server online. */
item->online = true;
/* It could be either window, but only one is open, so redraw both. */
UpdateNetworkGameWindow();
SetWindowDirty(WC_NETWORK_WINDOW, WN_NETWORK_WINDOW_LOBBY);
/* We will receive company info next, so keep connection open. */
if (this->status == STATUS_COMPANY_INFO) return NETWORK_RECV_STATUS_OKAY;
return NETWORK_RECV_STATUS_CLOSE_QUERY;
}
NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_GAME_INFO_EXTENDED(Packet *p)
{
if (this->status != STATUS_COMPANY_INFO && this->status != STATUS_INACTIVE) return NETWORK_RECV_STATUS_MALFORMED_PACKET;
if (this->status != STATUS_GAME_INFO) return NETWORK_RECV_STATUS_MALFORMED_PACKET;
NetworkGameList *item = GetLobbyGameInfo();
NetworkGameList *item = NetworkGameListAddItem(this->connection_string);
/* Clear any existing GRFConfig chain. */
ClearGRFConfigList(&item->info.grfconfig);
@@ -737,50 +716,7 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_GAME_INFO_EXTEN
/* Ensure we consider the server online. */
item->online = true;
SetWindowDirty(WC_NETWORK_WINDOW, WN_NETWORK_WINDOW_LOBBY);
/* We will receive company info next, so keep connection open. */
if (this->status == STATUS_COMPANY_INFO) return NETWORK_RECV_STATUS_OKAY;
return NETWORK_RECV_STATUS_CLOSE_QUERY;
}
NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_COMPANY_INFO(Packet *p)
{
if (this->status != STATUS_COMPANY_INFO) return NETWORK_RECV_STATUS_MALFORMED_PACKET;
byte company_info_version = p->Recv_uint8();
if (!this->HasClientQuit() && company_info_version == NETWORK_COMPANY_INFO_VERSION) {
/* We have received all data... (there are no more packets coming) */
if (!p->Recv_bool()) return NETWORK_RECV_STATUS_CLOSE_QUERY;
CompanyID current = (Owner)p->Recv_uint8();
if (current >= MAX_COMPANIES) return NETWORK_RECV_STATUS_CLOSE_QUERY;
NetworkCompanyInfo *company_info = GetLobbyCompanyInfo(current);
if (company_info == nullptr) return NETWORK_RECV_STATUS_CLOSE_QUERY;
company_info->company_name = p->Recv_string(NETWORK_COMPANY_NAME_LENGTH);
company_info->inaugurated_year = p->Recv_uint32();
company_info->company_value = p->Recv_uint64();
company_info->money = p->Recv_uint64();
company_info->income = p->Recv_uint64();
company_info->performance = p->Recv_uint16();
company_info->use_password = p->Recv_bool();
for (uint i = 0; i < NETWORK_VEH_END; i++) {
company_info->num_vehicle[i] = p->Recv_uint16();
}
for (uint i = 0; i < NETWORK_VEH_END; i++) {
company_info->num_station[i] = p->Recv_uint16();
}
company_info->ai = p->Recv_bool();
company_info->clients = p->Recv_string(NETWORK_CLIENTS_LENGTH);
SetWindowDirty(WC_NETWORK_WINDOW, WN_NETWORK_WINDOW_LOBBY);
return NETWORK_RECV_STATUS_OKAY;
}
UpdateNetworkGameWindow();
return NETWORK_RECV_STATUS_CLOSE_QUERY;
}
@@ -877,7 +813,7 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_ERROR(Packet *p
* NETWORK_ERROR_NOT_EXPECTED on requesting the game info. Show a special
* error popup in that case.
*/
if (error == NETWORK_ERROR_NOT_EXPECTED && (this->status == STATUS_GAME_INFO || this->status == STATUS_COMPANY_INFO)) {
if (error == NETWORK_ERROR_NOT_EXPECTED && this->status == STATUS_GAME_INFO) {
ShowErrorMessage(STR_NETWORK_ERROR_SERVER_TOO_OLD, INVALID_STRING_ID, WL_CRITICAL);
return NETWORK_RECV_STATUS_CLOSE_QUERY;
}
@@ -1084,6 +1020,8 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_MAP_DONE(Packet
/* Say we received the map and loaded it correctly! */
SendMapOk();
ShowClientList();
/* New company/spectator (invalid company) or company we want to join is not active
* Switch local company to spectator and await the server's judgement */
if (_network_join.company == COMPANY_NEW_COMPANY || !Company::IsValidID(_network_join.company)) {
@@ -1373,7 +1311,7 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_CONFIG_UPDATE(P
if (this->status < STATUS_ACTIVE) return NETWORK_RECV_STATUS_MALFORMED_PACKET;
_network_server_max_companies = p->Recv_uint8();
_network_server_max_spectators = p->Recv_uint8();
_network_server_name = p->Recv_string(NETWORK_NAME_LENGTH);
return NETWORK_RECV_STATUS_OKAY;
}
@@ -1433,7 +1371,6 @@ const char *ClientNetworkGameSocketHandler::GetServerStatusName(ServerStatus sta
static const char* _server_status_names[] {
"INACTIVE",
"GAME_INFO",
"COMPANY_INFO",
"JOIN",
"NEWGRFS_CHECK",
"AUTH_GAME",
@@ -1643,12 +1580,3 @@ bool NetworkMaxCompaniesReached()
{
return Company::GetNumItems() >= (_network_server ? _settings_client.network.max_companies : _network_server_max_companies);
}
/**
* Check if max_spectatos has been reached on the server (local check only).
* @return true if the max value has been reached or exceeded, false otherwise.
*/
bool NetworkMaxSpectatorsReached()
{
return NetworkSpectatorCount() >= (_network_server ? _settings_client.network.max_spectators : _network_server_max_spectators);
}

View File

@@ -23,7 +23,6 @@ private:
enum ServerStatus {
STATUS_INACTIVE, ///< The client is not connected nor active.
STATUS_GAME_INFO, ///< We are trying to get the game information.
STATUS_COMPANY_INFO, ///< We are trying to get company information.
STATUS_JOIN, ///< We are trying to join a server.
STATUS_NEWGRFS_CHECK, ///< Last action was checking NewGRFs.
STATUS_AUTH_GAME, ///< Last action was requesting game (server) password.
@@ -54,7 +53,6 @@ protected:
NetworkRecvStatus Receive_SERVER_ERROR(Packet *p) override;
NetworkRecvStatus Receive_SERVER_GAME_INFO(Packet *p) override;
NetworkRecvStatus Receive_SERVER_GAME_INFO_EXTENDED(Packet *p) override;
NetworkRecvStatus Receive_SERVER_COMPANY_INFO(Packet *p) override;
NetworkRecvStatus Receive_SERVER_CLIENT_INFO(Packet *p) override;
NetworkRecvStatus Receive_SERVER_NEED_GAME_PASSWORD(Packet *p) override;
NetworkRecvStatus Receive_SERVER_NEED_COMPANY_PASSWORD(Packet *p) override;
@@ -94,7 +92,7 @@ public:
std::string GetDebugInfo() const override;
static NetworkRecvStatus SendInformationQuery(bool request_company_info);
static NetworkRecvStatus SendInformationQuery();
static NetworkRecvStatus SendJoin();
static NetworkRecvStatus SendCommand(const CommandPacket *cp);

View File

@@ -18,6 +18,7 @@
#include "network.h"
#include "network_coordinator.h"
#include "network_gamelist.h"
#include "network_gui.h"
#include "network_internal.h"
#include "network_server.h"
#include "network_stun.h"
@@ -144,7 +145,11 @@ bool ClientNetworkCoordinatorSocketHandler::Receive_GC_ERROR(Packet *p)
return false;
case NETWORK_COORDINATOR_ERROR_INVALID_INVITE_CODE: {
this->CloseToken(detail);
auto connecter_pre_it = this->connecter_pre.find(detail);
if (connecter_pre_it != this->connecter_pre.end()) {
connecter_pre_it->second->SetFailure();
this->connecter_pre.erase(connecter_pre_it);
}
/* Mark the server as offline. */
NetworkGameList *item = NetworkGameListAddItem(detail);
@@ -189,6 +194,7 @@ bool ClientNetworkCoordinatorSocketHandler::Receive_GC_REGISTER_ACK(Packet *p)
case CONNECTION_TYPE_ISOLATED: connection_type = "Remote players can't connect"; break;
case CONNECTION_TYPE_DIRECT: connection_type = "Public"; break;
case CONNECTION_TYPE_STUN: connection_type = "Behind NAT"; break;
case CONNECTION_TYPE_TURN: connection_type = "Via relay"; break;
case CONNECTION_TYPE_UNKNOWN: // Never returned from Game Coordinator.
default: connection_type = "Unknown"; break; // Should never happen, but don't fail if it does.
@@ -231,7 +237,7 @@ bool ClientNetworkCoordinatorSocketHandler::Receive_GC_LISTING(Packet *p)
/* Read the NetworkGameInfo from the packet. */
NetworkGameInfo ngi = {};
DeserializeNetworkGameInfo(p, &ngi);
DeserializeNetworkGameInfo(p, &ngi, &this->newgrf_lookup_table);
/* Now we know the connection string, we can add it to our list. */
NetworkGameList *item = NetworkGameListAddItem(connection_string);
@@ -258,15 +264,15 @@ bool ClientNetworkCoordinatorSocketHandler::Receive_GC_CONNECTING(Packet *p)
std::string invite_code = p->Recv_string(NETWORK_INVITE_CODE_LENGTH);
/* Find the connecter based on the invite code. */
auto connecter_it = this->connecter_pre.find(invite_code);
if (connecter_it == this->connecter_pre.end()) {
auto connecter_pre_it = this->connecter_pre.find(invite_code);
if (connecter_pre_it == this->connecter_pre.end()) {
this->CloseConnection();
return false;
}
/* Now store it based on the token. */
this->connecter[token] = connecter_it->second;
this->connecter_pre.erase(connecter_it);
this->connecter[token] = connecter_pre_it->second;
this->connecter_pre.erase(connecter_pre_it);
return true;
}
@@ -274,14 +280,6 @@ bool ClientNetworkCoordinatorSocketHandler::Receive_GC_CONNECTING(Packet *p)
bool ClientNetworkCoordinatorSocketHandler::Receive_GC_CONNECT_FAILED(Packet *p)
{
std::string token = p->Recv_string(NETWORK_TOKEN_LENGTH);
auto connecter_it = this->connecter.find(token);
if (connecter_it != this->connecter.end()) {
connecter_it->second->SetFailure();
this->connecter.erase(connecter_it);
}
/* Close all remaining connections. */
this->CloseToken(token);
return true;
@@ -347,6 +345,62 @@ bool ClientNetworkCoordinatorSocketHandler::Receive_GC_STUN_CONNECT(Packet *p)
return true;
}
bool ClientNetworkCoordinatorSocketHandler::Receive_GC_NEWGRF_LOOKUP(Packet *p)
{
this->newgrf_lookup_table_cursor = p->Recv_uint32();
uint16 newgrfs = p->Recv_uint16();
for (; newgrfs> 0; newgrfs--) {
uint32 index = p->Recv_uint32();
DeserializeGRFIdentifierWithName(p, &this->newgrf_lookup_table[index]);
}
return true;
}
bool ClientNetworkCoordinatorSocketHandler::Receive_GC_TURN_CONNECT(Packet *p)
{
std::string token = p->Recv_string(NETWORK_TOKEN_LENGTH);
uint8 tracking_number = p->Recv_uint8();
std::string ticket = p->Recv_string(NETWORK_TOKEN_LENGTH);
std::string connection_string = p->Recv_string(NETWORK_HOSTNAME_PORT_LENGTH);
/* Ensure all other pending connection attempts are killed. */
if (this->game_connecter != nullptr) {
this->game_connecter->Kill();
this->game_connecter = nullptr;
}
this->turn_handlers[token] = ClientNetworkTurnSocketHandler::Turn(token, tracking_number, ticket, connection_string);
if (!_network_server) {
switch (_settings_client.network.use_relay_service) {
case URS_NEVER:
this->ConnectFailure(token, 0);
break;
case URS_ASK:
ShowNetworkAskRelay(connection_string, token);
break;
case URS_ALLOW:
this->StartTurnConnection(token);
break;
}
} else {
this->StartTurnConnection(token);
}
return true;
}
void ClientNetworkCoordinatorSocketHandler::StartTurnConnection(std::string &token)
{
auto turn_it = this->turn_handlers.find(token);
if (turn_it == this->turn_handlers.end()) return;
turn_it->second->Connect();
}
void ClientNetworkCoordinatorSocketHandler::Connect()
{
/* We are either already connected or are trying to connect. */
@@ -370,7 +424,7 @@ NetworkRecvStatus ClientNetworkCoordinatorSocketHandler::CloseConnection(bool er
_network_server_connection_type = CONNECTION_TYPE_UNKNOWN;
this->next_update = {};
this->CloseAllTokens();
this->CloseAllConnections();
SetWindowDirty(WC_CLIENT_LIST, 0);
@@ -410,13 +464,14 @@ void ClientNetworkCoordinatorSocketHandler::Register()
void ClientNetworkCoordinatorSocketHandler::SendServerUpdate()
{
DEBUG(net, 6, "Sending server update to Game Coordinator");
this->next_update = std::chrono::steady_clock::now() + NETWORK_COORDINATOR_DELAY_BETWEEN_UPDATES;
Packet *p = new Packet(PACKET_COORDINATOR_SERVER_UPDATE, TCP_MTU);
p->Send_uint8(NETWORK_COORDINATOR_VERSION);
SerializeNetworkGameInfo(p, GetCurrentNetworkServerGameInfo());
SerializeNetworkGameInfo(p, GetCurrentNetworkServerGameInfo(), this->next_update.time_since_epoch() != std::chrono::nanoseconds::zero());
this->SendPacket(p);
this->next_update = std::chrono::steady_clock::now() + NETWORK_COORDINATOR_DELAY_BETWEEN_UPDATES;
}
/**
@@ -432,6 +487,7 @@ void ClientNetworkCoordinatorSocketHandler::GetListing()
p->Send_uint8(NETWORK_COORDINATOR_VERSION);
p->Send_uint8(NETWORK_GAME_INFO_VERSION);
p->Send_string(_openttd_revision);
p->Send_uint32(this->newgrf_lookup_table_cursor);
this->SendPacket(p);
}
@@ -510,11 +566,14 @@ void ClientNetworkCoordinatorSocketHandler::ConnectSuccess(const std::string &to
p->Send_string(token);
this->SendPacket(p);
/* Find the connecter; it can happen it no longer exist, in cases where
* we aborted the connect but the Game Coordinator was already in the
* processes of connecting us. */
auto connecter_it = this->connecter.find(token);
assert(connecter_it != this->connecter.end());
connecter_it->second->SetConnected(sock);
this->connecter.erase(connecter_it);
if (connecter_it != this->connecter.end()) {
connecter_it->second->SetConnected(sock);
this->connecter.erase(connecter_it);
}
}
/* Close all remaining connections. */
@@ -538,6 +597,11 @@ void ClientNetworkCoordinatorSocketHandler::StunResult(const std::string &token,
this->SendPacket(p);
}
/**
* Close the STUN handler.
* @param token The token used for the STUN handlers.
* @param family The family of STUN handlers to close. AF_UNSPEC to close all STUN handlers for this token.
*/
void ClientNetworkCoordinatorSocketHandler::CloseStunHandler(const std::string &token, uint8 family)
{
auto stun_it = this->stun_handlers.find(token);
@@ -561,20 +625,34 @@ void ClientNetworkCoordinatorSocketHandler::CloseStunHandler(const std::string &
}
}
/**
* Close the TURN handler.
* @param token The token used for the TURN handler.
*/
void ClientNetworkCoordinatorSocketHandler::CloseTurnHandler(const std::string &token)
{
DeleteWindowByClass(WC_NETWORK_ASK_RELAY);
auto turn_it = this->turn_handlers.find(token);
if (turn_it == this->turn_handlers.end()) return;
turn_it->second->CloseConnection();
turn_it->second->CloseSocket();
/* We don't remove turn_handler here, as we can be called from within that
* turn_handler instance, so our object cannot be free'd yet. Instead, we
* check later if the connection is closed, and free the object then. */
}
/**
* Close everything related to this connection token.
* @param token The connection token to close.
*/
void ClientNetworkCoordinatorSocketHandler::CloseToken(const std::string &token)
{
/* Ensure all other pending connection attempts are also killed. */
if (this->game_connecter != nullptr) {
this->game_connecter->Kill();
this->game_connecter = nullptr;
}
/* Close all remaining STUN connections. */
/* Close all remaining STUN / TURN connections. */
this->CloseStunHandler(token);
this->CloseTurnHandler(token);
/* Close the caller of the connection attempt. */
auto connecter_it = this->connecter.find(token);
@@ -582,17 +660,12 @@ void ClientNetworkCoordinatorSocketHandler::CloseToken(const std::string &token)
connecter_it->second->SetFailure();
this->connecter.erase(connecter_it);
}
auto connecter_pre_it = this->connecter_pre.find(token);
if (connecter_pre_it != this->connecter_pre.end()) {
connecter_pre_it->second->SetFailure();
this->connecter_pre.erase(connecter_pre_it);
}
}
/**
* Close all pending connection tokens.
*/
void ClientNetworkCoordinatorSocketHandler::CloseAllTokens()
void ClientNetworkCoordinatorSocketHandler::CloseAllConnections()
{
/* Ensure all other pending connection attempts are also killed. */
if (this->game_connecter != nullptr) {
@@ -603,12 +676,20 @@ void ClientNetworkCoordinatorSocketHandler::CloseAllTokens()
/* Mark any pending connecters as failed. */
for (auto &[token, it] : this->connecter) {
this->CloseStunHandler(token);
this->CloseTurnHandler(token);
it->SetFailure();
/* Inform the Game Coordinator he can stop trying to connect us to the server. */
this->ConnectFailure(token, 0);
}
this->stun_handlers.clear();
this->turn_handlers.clear();
this->connecter.clear();
/* Also close any pending invite-code requests. */
for (auto &[invite_code, it] : this->connecter_pre) {
it->SetFailure();
}
this->connecter.clear();
this->connecter_pre.clear();
}
@@ -684,4 +765,17 @@ void ClientNetworkCoordinatorSocketHandler::SendReceive()
stun_handler->SendReceive();
}
}
/* Check for handlers that are not connecting nor connected. Destroy those objects. */
for (auto turn_it = this->turn_handlers.begin(); turn_it != this->turn_handlers.end(); /* nothing */) {
if (turn_it->second->connect_started && turn_it->second->connecter == nullptr && !turn_it->second->IsConnected()) {
turn_it = this->turn_handlers.erase(turn_it);
} else {
turn_it++;
}
}
for (const auto &[token, turn_handler] : this->turn_handlers) {
turn_handler->SendReceive();
}
}

View File

@@ -12,6 +12,7 @@
#include "core/tcp_coordinator.h"
#include "network_stun.h"
#include "network_turn.h"
#include <map>
/**
@@ -42,6 +43,10 @@
* - a) Server/client connect, client sends CLIENT_CONNECTED to Game Coordinator.
* - b) Server/client connect fails, both send SERCLI_CONNECT_FAILED to Game Coordinator.
* - Game Coordinator tries other combination if available.
* 3) TURN?
* - Game Coordinator sends GC_TURN_CONNECT to server/client.
* - a) Server/client connect, client sends CLIENT_CONNECTED to Game Coordinator.
* - b) Server/client connect fails, both send SERCLI_CONNECT_FAILED to Game Coordinator.
* - If all fails, Game Coordinator sends GC_CONNECT_FAILED to indicate no connection is possible.
*/
@@ -52,8 +57,12 @@ private:
std::map<std::string, TCPServerConnecter *> connecter; ///< Based on tokens, the current connecters that are pending.
std::map<std::string, TCPServerConnecter *> connecter_pre; ///< Based on invite codes, the current connecters that are pending.
std::map<std::string, std::map<int, std::unique_ptr<ClientNetworkStunSocketHandler>>> stun_handlers; ///< All pending STUN handlers, stored by token:family.
std::map<std::string, std::unique_ptr<ClientNetworkTurnSocketHandler>> turn_handlers; ///< Pending TURN handler (if any), stored by token.
TCPConnecter *game_connecter = nullptr; ///< Pending connecter to the game server.
uint32 newgrf_lookup_table_cursor = 0; ///< Last received cursor for the #GameInfoNewGRFLookupTable updates.
GameInfoNewGRFLookupTable newgrf_lookup_table; ///< Table to look up NewGRFs in the GC_LISTING packets.
protected:
bool Receive_GC_ERROR(Packet *p) override;
bool Receive_GC_REGISTER_ACK(Packet *p) override;
@@ -63,6 +72,8 @@ protected:
bool Receive_GC_DIRECT_CONNECT(Packet *p) override;
bool Receive_GC_STUN_REQUEST(Packet *p) override;
bool Receive_GC_STUN_CONNECT(Packet *p) override;
bool Receive_GC_NEWGRF_LOOKUP(Packet *p) override;
bool Receive_GC_TURN_CONNECT(Packet *p) override;
public:
/** The idle timeout; when to close the connection because it's idle. */
@@ -82,14 +93,16 @@ public:
void Connect();
void CloseToken(const std::string &token);
void CloseAllTokens();
void CloseAllConnections();
void CloseStunHandler(const std::string &token, uint8 family = AF_UNSPEC);
void CloseTurnHandler(const std::string &token);
void Register();
void SendServerUpdate();
void GetListing();
void ConnectToServer(const std::string &invite_code, TCPServerConnecter *connecter);
void StartTurnConnection(std::string &token);
};
extern ClientNetworkCoordinatorSocketHandler _network_coordinator_client;

View File

@@ -62,7 +62,6 @@ void NetworkClientSendDesyncMsg(const char *msg);
bool NetworkClientPreferTeamChat(const NetworkClientInfo *cio);
bool NetworkCompanyIsPassworded(CompanyID company_id);
bool NetworkMaxCompaniesReached();
bool NetworkMaxSpectatorsReached();
void NetworkPrintClients();
void NetworkHandlePauseChange(PauseMode prev_mode, PauseMode changed_mode);

View File

@@ -125,10 +125,7 @@ void NetworkAfterNewGRFScan()
const GRFConfig *f = FindGRFConfig(c->ident.grfid, FGCM_EXACT, c->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. */
c->name = FindUnknownGRFName(c->ident.grfid, c->ident.md5sum, true);
/* Don't know the GRF (anymore), so mark game incompatible. */
c->status = GCS_NOT_FOUND;
/* If we miss a file, we're obviously incompatible. */

View File

@@ -55,7 +55,6 @@
#include "../safeguards.h"
static void ShowNetworkStartServerWindow();
static void ShowNetworkLobbyWindow(NetworkGameList *ngl);
static const int NETWORK_LIST_REFRESH_DELAY = 30; ///< Time, in seconds, between updates of the network list.
@@ -498,7 +497,7 @@ public:
this->filter_editbox.cancel_button = QueryString::ACTION_CLEAR;
this->SetFocusedWidget(WID_NG_FILTER);
/* As the master-server doesn't support "websocket" servers yet, we
/* As the Game Coordinator doesn't support "websocket" servers yet, we
* let "os/emscripten/pre.js" hardcode a list of servers people can
* join. This means the serverlist is curated for now, but it is the
* best we can offer. */
@@ -785,7 +784,7 @@ public:
case WID_NG_JOIN: // Join Game
if (this->server != nullptr) {
ShowNetworkLobbyWindow(this->server);
NetworkClientConnectGame(this->server->connection_string, COMPANY_SPECTATOR);
}
break;
@@ -1005,7 +1004,6 @@ static WindowDesc _network_game_window_desc(
void ShowNetworkGameWindow()
{
static bool first = true;
DeleteWindowById(WC_NETWORK_WINDOW, WN_NETWORK_WINDOW_LOBBY);
DeleteWindowById(WC_NETWORK_WINDOW, WN_NETWORK_WINDOW_START);
/* Only show once */
@@ -1048,10 +1046,6 @@ struct NetworkStartServerWindow : public Window {
case WID_NSS_COMPANIES_TXT:
SetDParam(0, _settings_client.network.max_companies);
break;
case WID_NSS_SPECTATORS_TXT:
SetDParam(0, _settings_client.network.max_spectators);
break;
}
}
@@ -1094,7 +1088,6 @@ struct NetworkStartServerWindow : public Window {
case WID_NSS_CLIENTS_BTND: case WID_NSS_CLIENTS_BTNU: // Click on up/down button for number of clients
case WID_NSS_COMPANIES_BTND: case WID_NSS_COMPANIES_BTNU: // Click on up/down button for number of companies
case WID_NSS_SPECTATORS_BTND: case WID_NSS_SPECTATORS_BTNU: // Click on up/down button for number of spectators
/* Don't allow too fast scrolling. */
if (!(this->flags & WF_TIMEOUT) || this->timeout_timer <= 1) {
this->HandleButtonClick(widget);
@@ -1107,9 +1100,6 @@ struct NetworkStartServerWindow : public Window {
case WID_NSS_COMPANIES_BTND: case WID_NSS_COMPANIES_BTNU:
_settings_client.network.max_companies = Clamp(_settings_client.network.max_companies + widget - WID_NSS_COMPANIES_TXT, 1, MAX_COMPANIES);
break;
case WID_NSS_SPECTATORS_BTND: case WID_NSS_SPECTATORS_BTNU:
_settings_client.network.max_spectators = Clamp(_settings_client.network.max_spectators + widget - WID_NSS_SPECTATORS_TXT, 0, MAX_CLIENTS);
break;
}
}
_left_button_clicked = false;
@@ -1127,12 +1117,6 @@ struct NetworkStartServerWindow : public Window {
ShowQueryString(STR_JUST_INT, STR_NETWORK_START_SERVER_NUMBER_OF_COMPANIES, 3, this, CS_NUMERAL, QSF_NONE);
break;
case WID_NSS_SPECTATORS_TXT: // Click on number of spectators
this->widget_id = WID_NSS_SPECTATORS_TXT;
SetDParam(0, _settings_client.network.max_spectators);
ShowQueryString(STR_JUST_INT, STR_NETWORK_START_SERVER_NUMBER_OF_SPECTATORS, 4, this, CS_NUMERAL, QSF_NONE);
break;
case WID_NSS_GENERATE_GAME: // Start game
if (!CheckServerName()) return;
_is_network_server = true;
@@ -1187,7 +1171,7 @@ struct NetworkStartServerWindow : public Window {
void OnTimeout() override
{
static const int raise_widgets[] = {WID_NSS_CLIENTS_BTND, WID_NSS_CLIENTS_BTNU, WID_NSS_COMPANIES_BTND, WID_NSS_COMPANIES_BTNU, WID_NSS_SPECTATORS_BTND, WID_NSS_SPECTATORS_BTNU, WIDGET_LIST_END};
static const int raise_widgets[] = {WID_NSS_CLIENTS_BTND, WID_NSS_CLIENTS_BTNU, WID_NSS_COMPANIES_BTND, WID_NSS_COMPANIES_BTNU, WIDGET_LIST_END};
for (const int *widget = raise_widgets; *widget != WIDGET_LIST_END; widget++) {
if (this->IsWidgetLowered(*widget)) {
this->RaiseWidget(*widget);
@@ -1209,7 +1193,6 @@ struct NetworkStartServerWindow : public Window {
default: NOT_REACHED();
case WID_NSS_CLIENTS_TXT: _settings_client.network.max_clients = Clamp(value, 2, MAX_CLIENTS); break;
case WID_NSS_COMPANIES_TXT: _settings_client.network.max_companies = Clamp(value, 1, MAX_COMPANIES); break;
case WID_NSS_SPECTATORS_TXT: _settings_client.network.max_spectators = Clamp(value, 0, MAX_CLIENTS); break;
}
}
@@ -1261,15 +1244,6 @@ static const NWidgetPart _nested_network_start_server_window_widgets[] = {
NWidget(WWT_IMGBTN, COLOUR_LIGHT_BLUE, WID_NSS_COMPANIES_BTNU), SetMinimalSize(12, 12), SetFill(0, 1), SetDataTip(SPR_ARROW_UP, STR_NETWORK_START_SERVER_NUMBER_OF_COMPANIES_TOOLTIP),
EndContainer(),
EndContainer(),
NWidget(NWID_VERTICAL), SetPIP(0, 1, 0),
NWidget(WWT_TEXT, COLOUR_LIGHT_BLUE, WID_NSS_SPECTATORS_LABEL), SetFill(1, 0), SetDataTip(STR_NETWORK_START_SERVER_NUMBER_OF_SPECTATORS, STR_NULL),
NWidget(NWID_HORIZONTAL),
NWidget(WWT_IMGBTN, COLOUR_LIGHT_BLUE, WID_NSS_SPECTATORS_BTND), SetMinimalSize(12, 12), SetFill(0, 1), SetDataTip(SPR_ARROW_DOWN, STR_NETWORK_START_SERVER_NUMBER_OF_SPECTATORS_TOOLTIP),
NWidget(WWT_PUSHTXTBTN, COLOUR_LIGHT_BLUE, WID_NSS_SPECTATORS_TXT), SetFill(1, 0), SetDataTip(STR_NETWORK_START_SERVER_SPECTATORS_SELECT, STR_NETWORK_START_SERVER_NUMBER_OF_SPECTATORS_TOOLTIP),
NWidget(WWT_IMGBTN, COLOUR_LIGHT_BLUE, WID_NSS_SPECTATORS_BTNU), SetMinimalSize(12, 12), SetFill(0, 1), SetDataTip(SPR_ARROW_UP, STR_NETWORK_START_SERVER_NUMBER_OF_SPECTATORS_TOOLTIP),
EndContainer(),
EndContainer(),
EndContainer(),
/* 'generate game' and 'load game' buttons */
@@ -1305,330 +1279,10 @@ static void ShowNetworkStartServerWindow()
if (!NetworkValidateOurClientName()) return;
DeleteWindowById(WC_NETWORK_WINDOW, WN_NETWORK_WINDOW_GAME);
DeleteWindowById(WC_NETWORK_WINDOW, WN_NETWORK_WINDOW_LOBBY);
new NetworkStartServerWindow(&_network_start_server_window_desc);
}
struct NetworkLobbyWindow : public Window {
CompanyID company; ///< Selected company
NetworkGameList *server; ///< Selected server
NetworkCompanyInfo company_info[MAX_COMPANIES];
Scrollbar *vscroll;
NetworkLobbyWindow(WindowDesc *desc, NetworkGameList *ngl) :
Window(desc), company(INVALID_COMPANY), server(ngl)
{
this->CreateNestedTree();
this->vscroll = this->GetScrollbar(WID_NL_SCROLLBAR);
this->FinishInitNested(WN_NETWORK_WINDOW_LOBBY);
}
CompanyID NetworkLobbyFindCompanyIndex(byte pos) const
{
/* Scroll through all this->company_info and get the 'pos' item that is not empty. */
for (CompanyID i = COMPANY_FIRST; i < MAX_COMPANIES; i++) {
if (!this->company_info[i].company_name.empty()) {
if (pos-- == 0) return i;
}
}
return COMPANY_FIRST;
}
void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize) override
{
switch (widget) {
case WID_NL_HEADER:
size->height = WD_MATRIX_TOP + FONT_HEIGHT_NORMAL + WD_MATRIX_BOTTOM;
break;
case WID_NL_MATRIX:
resize->height = WD_MATRIX_TOP + std::max<uint>(std::max(GetSpriteSize(SPR_LOCK).height, GetSpriteSize(SPR_PROFIT_LOT).height), FONT_HEIGHT_NORMAL) + WD_MATRIX_BOTTOM;
size->height = 10 * resize->height;
break;
case WID_NL_DETAILS:
size->height = 30 + 11 * FONT_HEIGHT_NORMAL;
break;
}
}
void SetStringParameters(int widget) const override
{
switch (widget) {
case WID_NL_TEXT:
SetDParamStr(0, this->server->info.server_name);
break;
}
}
void DrawWidget(const Rect &r, int widget) const override
{
switch (widget) {
case WID_NL_DETAILS:
this->DrawDetails(r);
break;
case WID_NL_MATRIX:
this->DrawMatrix(r);
break;
}
}
void OnPaint() override
{
const NetworkGameInfo *gi = &this->server->info;
/* Join button is disabled when no company is selected and for AI companies. */
this->SetWidgetDisabledState(WID_NL_JOIN, this->company == INVALID_COMPANY || GetLobbyCompanyInfo(this->company)->ai);
/* Cannot start new company if there are too many. */
this->SetWidgetDisabledState(WID_NL_NEW, gi->companies_on >= gi->companies_max);
/* Cannot spectate if there are too many spectators. */
this->SetWidgetDisabledState(WID_NL_SPECTATE, gi->spectators_on >= gi->spectators_max);
this->vscroll->SetCount(gi->companies_on);
/* Draw window widgets */
this->DrawWidgets();
}
void DrawMatrix(const Rect &r) const
{
bool rtl = _current_text_dir == TD_RTL;
uint left = r.left + WD_FRAMERECT_LEFT;
uint right = r.right - WD_FRAMERECT_RIGHT;
uint text_offset = (this->resize.step_height - WD_MATRIX_TOP - WD_MATRIX_BOTTOM - FONT_HEIGHT_NORMAL) / 2 + WD_MATRIX_TOP;
Dimension lock_size = GetSpriteSize(SPR_LOCK);
int lock_width = lock_size.width;
int lock_y_offset = (this->resize.step_height - WD_MATRIX_TOP - WD_MATRIX_BOTTOM - lock_size.height) / 2 + WD_MATRIX_TOP;
Dimension profit_size = GetSpriteSize(SPR_PROFIT_LOT);
int profit_width = lock_size.width;
int profit_y_offset = (this->resize.step_height - WD_MATRIX_TOP - WD_MATRIX_BOTTOM - profit_size.height) / 2 + WD_MATRIX_TOP;
uint text_left = left + (rtl ? lock_width + profit_width + 4 : 0);
uint text_right = right - (rtl ? 0 : lock_width + profit_width + 4);
uint profit_left = rtl ? left : right - profit_width;
uint lock_left = rtl ? left + profit_width + 2 : right - profit_width - lock_width - 2;
int y = r.top;
/* Draw company list */
int pos = this->vscroll->GetPosition();
while (pos < this->server->info.companies_on) {
byte company = NetworkLobbyFindCompanyIndex(pos);
bool income = false;
if (this->company == company) {
GfxFillRect(r.left + WD_BEVEL_LEFT, y + 1, r.right - WD_BEVEL_RIGHT, y + this->resize.step_height - 2, PC_GREY); // show highlighted item with a different colour
}
DrawString(text_left, text_right, y + text_offset, this->company_info[company].company_name, TC_BLACK);
if (this->company_info[company].use_password != 0) DrawSprite(SPR_LOCK, PAL_NONE, lock_left, y + lock_y_offset);
/* If the company's income was positive puts a green dot else a red dot */
if (this->company_info[company].income >= 0) income = true;
DrawSprite(income ? SPR_PROFIT_LOT : SPR_PROFIT_NEGATIVE, PAL_NONE, profit_left, y + profit_y_offset);
pos++;
y += this->resize.step_height;
if (pos >= this->vscroll->GetPosition() + this->vscroll->GetCapacity()) break;
}
}
void DrawDetails(const Rect &r) const
{
const int detail_height = 12 + FONT_HEIGHT_NORMAL + 12;
/* Draw info about selected company when it is selected in the left window. */
GfxFillRect(r.left + 1, r.top + 1, r.right - 1, r.top + detail_height - 1, PC_DARK_BLUE);
DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, r.top + 12, STR_NETWORK_GAME_LOBBY_COMPANY_INFO, TC_FROMSTRING, SA_HOR_CENTER);
if (this->company == INVALID_COMPANY || this->company_info[this->company].company_name.empty()) return;
int y = r.top + detail_height + 4;
const NetworkGameInfo *gi = &this->server->info;
SetDParam(0, gi->clients_on);
SetDParam(1, gi->clients_max);
SetDParam(2, gi->companies_on);
SetDParam(3, gi->companies_max);
DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, STR_NETWORK_SERVER_LIST_CLIENTS);
y += FONT_HEIGHT_NORMAL;
SetDParamStr(0, this->company_info[this->company].company_name);
DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, STR_NETWORK_GAME_LOBBY_COMPANY_NAME);
y += FONT_HEIGHT_NORMAL;
SetDParam(0, this->company_info[this->company].inaugurated_year);
DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, STR_NETWORK_GAME_LOBBY_INAUGURATION_YEAR); // inauguration year
y += FONT_HEIGHT_NORMAL;
SetDParam(0, this->company_info[this->company].company_value);
DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, STR_NETWORK_GAME_LOBBY_VALUE); // company value
y += FONT_HEIGHT_NORMAL;
SetDParam(0, this->company_info[this->company].money);
DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, STR_NETWORK_GAME_LOBBY_CURRENT_BALANCE); // current balance
y += FONT_HEIGHT_NORMAL;
SetDParam(0, this->company_info[this->company].income);
DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, STR_NETWORK_GAME_LOBBY_LAST_YEARS_INCOME); // last year's income
y += FONT_HEIGHT_NORMAL;
SetDParam(0, this->company_info[this->company].performance);
DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, STR_NETWORK_GAME_LOBBY_PERFORMANCE); // performance
y += FONT_HEIGHT_NORMAL;
SetDParam(0, this->company_info[this->company].num_vehicle[NETWORK_VEH_TRAIN]);
SetDParam(1, this->company_info[this->company].num_vehicle[NETWORK_VEH_LORRY]);
SetDParam(2, this->company_info[this->company].num_vehicle[NETWORK_VEH_BUS]);
SetDParam(3, this->company_info[this->company].num_vehicle[NETWORK_VEH_SHIP]);
SetDParam(4, this->company_info[this->company].num_vehicle[NETWORK_VEH_PLANE]);
DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, STR_NETWORK_GAME_LOBBY_VEHICLES); // vehicles
y += FONT_HEIGHT_NORMAL;
SetDParam(0, this->company_info[this->company].num_station[NETWORK_VEH_TRAIN]);
SetDParam(1, this->company_info[this->company].num_station[NETWORK_VEH_LORRY]);
SetDParam(2, this->company_info[this->company].num_station[NETWORK_VEH_BUS]);
SetDParam(3, this->company_info[this->company].num_station[NETWORK_VEH_SHIP]);
SetDParam(4, this->company_info[this->company].num_station[NETWORK_VEH_PLANE]);
DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, STR_NETWORK_GAME_LOBBY_STATIONS); // stations
y += FONT_HEIGHT_NORMAL;
SetDParamStr(0, this->company_info[this->company].clients);
DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, STR_NETWORK_GAME_LOBBY_PLAYERS); // players
}
void OnClick(Point pt, int widget, int click_count) override
{
switch (widget) {
case WID_NL_CANCEL: // Cancel button
ShowNetworkGameWindow();
break;
case WID_NL_MATRIX: { // Company list
uint id_v = this->vscroll->GetScrolledRowFromWidget(pt.y, this, WID_NL_MATRIX);
this->company = (id_v >= this->server->info.companies_on) ? INVALID_COMPANY : NetworkLobbyFindCompanyIndex(id_v);
this->SetDirty();
/* FIXME the disabling should go into some InvalidateData, which is called instead of the SetDirty */
if (click_count > 1 && !this->IsWidgetDisabled(WID_NL_JOIN)) this->OnClick(pt, WID_NL_JOIN, 1);
break;
}
case WID_NL_JOIN: // Join company
/* Button can be clicked only when it is enabled. */
NetworkClientConnectGame(this->server->connection_string, this->company);
break;
case WID_NL_NEW: // New company
NetworkClientConnectGame(this->server->connection_string, COMPANY_NEW_COMPANY);
break;
case WID_NL_SPECTATE: // Spectate game
NetworkClientConnectGame(this->server->connection_string, COMPANY_SPECTATOR);
break;
case WID_NL_REFRESH: // Refresh
/* Clear the information so removed companies don't remain */
for (auto &company : this->company_info) company = {};
NetworkQueryLobbyServer(this->server->connection_string);
break;
}
}
void OnResize() override
{
this->vscroll->SetCapacityFromWidget(this, WID_NL_MATRIX);
}
};
static const NWidgetPart _nested_network_lobby_window_widgets[] = {
NWidget(NWID_HORIZONTAL),
NWidget(WWT_CLOSEBOX, COLOUR_LIGHT_BLUE),
NWidget(WWT_CAPTION, COLOUR_LIGHT_BLUE), SetDataTip(STR_NETWORK_GAME_LOBBY_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
EndContainer(),
NWidget(WWT_PANEL, COLOUR_LIGHT_BLUE, WID_NL_BACKGROUND),
NWidget(WWT_TEXT, COLOUR_LIGHT_BLUE, WID_NL_TEXT), SetDataTip(STR_NETWORK_GAME_LOBBY_PREPARE_TO_JOIN, STR_NULL), SetResize(1, 0), SetPadding(10, 10, 0, 10),
NWidget(NWID_SPACER), SetMinimalSize(0, 3),
NWidget(NWID_HORIZONTAL), SetPIP(10, 0, 10),
/* Company list. */
NWidget(NWID_VERTICAL),
NWidget(WWT_PANEL, COLOUR_WHITE, WID_NL_HEADER), SetMinimalSize(146, 0), SetResize(1, 0), SetFill(1, 0), EndContainer(),
NWidget(WWT_MATRIX, COLOUR_LIGHT_BLUE, WID_NL_MATRIX), SetMinimalSize(146, 0), SetResize(1, 1), SetFill(1, 1), SetMatrixDataTip(1, 0, STR_NETWORK_GAME_LOBBY_COMPANY_LIST_TOOLTIP), SetScrollbar(WID_NL_SCROLLBAR),
EndContainer(),
NWidget(NWID_VSCROLLBAR, COLOUR_LIGHT_BLUE, WID_NL_SCROLLBAR),
NWidget(NWID_SPACER), SetMinimalSize(5, 0), SetResize(0, 1),
/* Company info. */
NWidget(WWT_PANEL, COLOUR_LIGHT_BLUE, WID_NL_DETAILS), SetMinimalSize(232, 0), SetResize(1, 1), SetFill(1, 1), EndContainer(),
EndContainer(),
NWidget(NWID_SPACER), SetMinimalSize(0, 9),
/* Buttons. */
NWidget(NWID_HORIZONTAL, NC_EQUALSIZE), SetPIP(10, 3, 10),
NWidget(NWID_VERTICAL, NC_EQUALSIZE), SetPIP(0, 3, 0),
NWidget(WWT_PUSHTXTBTN, COLOUR_WHITE, WID_NL_JOIN), SetResize(1, 0), SetFill(1, 0), SetDataTip(STR_NETWORK_GAME_LOBBY_JOIN_COMPANY, STR_NETWORK_GAME_LOBBY_JOIN_COMPANY_TOOLTIP),
NWidget(WWT_PUSHTXTBTN, COLOUR_WHITE, WID_NL_NEW), SetResize(1, 0), SetFill(1, 0), SetDataTip(STR_NETWORK_GAME_LOBBY_NEW_COMPANY, STR_NETWORK_GAME_LOBBY_NEW_COMPANY_TOOLTIP),
EndContainer(),
NWidget(NWID_VERTICAL, NC_EQUALSIZE), SetPIP(0, 3, 0),
NWidget(WWT_PUSHTXTBTN, COLOUR_WHITE, WID_NL_SPECTATE), SetResize(1, 0), SetFill(1, 0), SetDataTip(STR_NETWORK_GAME_LOBBY_SPECTATE_GAME, STR_NETWORK_GAME_LOBBY_SPECTATE_GAME_TOOLTIP),
NWidget(WWT_PUSHTXTBTN, COLOUR_WHITE, WID_NL_REFRESH), SetResize(1, 0), SetFill(1, 0), SetDataTip(STR_NETWORK_SERVER_LIST_REFRESH, STR_NETWORK_SERVER_LIST_REFRESH_TOOLTIP),
EndContainer(),
NWidget(NWID_VERTICAL, NC_EQUALSIZE), SetPIP(0, 3, 0),
NWidget(WWT_PUSHTXTBTN, COLOUR_WHITE, WID_NL_CANCEL), SetResize(1, 0), SetFill(1, 0), SetDataTip(STR_BUTTON_CANCEL, STR_NULL),
NWidget(NWID_SPACER), SetFill(1, 1),
EndContainer(),
EndContainer(),
NWidget(NWID_SPACER), SetMinimalSize(0, 8),
EndContainer(),
};
static WindowDesc _network_lobby_window_desc(
WDP_CENTER, nullptr, 0, 0,
WC_NETWORK_WINDOW, WC_NONE,
0,
_nested_network_lobby_window_widgets, lengthof(_nested_network_lobby_window_widgets)
);
/**
* Show the networklobbywindow with the selected server.
* @param ngl Selected game pointer which is passed to the new window.
*/
static void ShowNetworkLobbyWindow(NetworkGameList *ngl)
{
if (!NetworkValidateOurClientName()) return;
DeleteWindowById(WC_NETWORK_WINDOW, WN_NETWORK_WINDOW_START);
DeleteWindowById(WC_NETWORK_WINDOW, WN_NETWORK_WINDOW_GAME);
_settings_client.network.last_joined = ngl->connection_string;
NetworkQueryLobbyServer(ngl->connection_string);
new NetworkLobbyWindow(&_network_lobby_window_desc, ngl);
}
/**
* Get the company information of a given company to fill for the lobby.
* @param company the company to get the company info struct from.
* @return the company info struct to write the (downloaded) data to.
*/
NetworkCompanyInfo *GetLobbyCompanyInfo(CompanyID company)
{
NetworkLobbyWindow *lobby = dynamic_cast<NetworkLobbyWindow*>(FindWindowById(WC_NETWORK_WINDOW, WN_NETWORK_WINDOW_LOBBY));
return (lobby != nullptr && company < MAX_COMPANIES) ? &lobby->company_info[company] : nullptr;
}
/**
* Get the game information for the lobby.
* @return the game info struct to write the (downloaded) data to.
*/
NetworkGameList *GetLobbyGameInfo()
{
NetworkLobbyWindow *lobby = dynamic_cast<NetworkLobbyWindow *>(FindWindowById(WC_NETWORK_WINDOW, WN_NETWORK_WINDOW_LOBBY));
return lobby != nullptr ? lobby->server : nullptr;
}
/* The window below gives information about the connected clients
* and also makes able to kick them (if server) and stuff like that. */
@@ -1642,28 +1296,30 @@ static const NWidgetPart _nested_client_list_widgets[] = {
NWidget(WWT_STICKYBOX, COLOUR_GREY),
EndContainer(),
NWidget(WWT_PANEL, COLOUR_GREY),
NWidget(NWID_SELECTION, INVALID_COLOUR, WID_CL_SERVER_SELECTOR),
NWidget(WWT_FRAME, COLOUR_GREY), SetDataTip(STR_NETWORK_CLIENT_LIST_SERVER, STR_NULL), SetPadding(4, 4, 0, 4), SetPIP(0, 2, 0),
NWidget(NWID_HORIZONTAL), SetPIP(0, 3, 0),
NWidget(WWT_TEXT, COLOUR_GREY), SetMinimalTextLines(1, 0), SetDataTip(STR_NETWORK_CLIENT_LIST_SERVER_NAME, STR_NULL),
NWidget(NWID_SPACER), SetMinimalSize(10, 0),
NWidget(WWT_TEXT, COLOUR_GREY, WID_CL_SERVER_NAME), SetFill(1, 0), SetMinimalTextLines(1, 0), SetResize(1, 0), SetDataTip(STR_BLACK_RAW_STRING, STR_NETWORK_CLIENT_LIST_SERVER_NAME_TOOLTIP), SetAlignment(SA_VERT_CENTER | SA_RIGHT),
NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_CL_SERVER_NAME_EDIT), SetMinimalSize(12, 14), SetDataTip(SPR_RENAME, STR_NETWORK_CLIENT_LIST_SERVER_NAME_EDIT_TOOLTIP),
EndContainer(),
NWidget(NWID_HORIZONTAL), SetPIP(0, 3, 0),
NWidget(WWT_TEXT, COLOUR_GREY), SetMinimalTextLines(1, 0), SetDataTip(STR_NETWORK_CLIENT_LIST_SERVER_VISIBILITY, STR_NULL),
NWidget(NWID_SPACER), SetMinimalSize(10, 0), SetFill(1, 0), SetResize(1, 0),
NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_CL_SERVER_VISIBILITY), SetDataTip(STR_BLACK_STRING, STR_NETWORK_CLIENT_LIST_SERVER_VISIBILITY_TOOLTIP),
EndContainer(),
NWidget(NWID_HORIZONTAL), SetPIP(0, 3, 0),
NWidget(WWT_TEXT, COLOUR_GREY), SetMinimalTextLines(1, 0), SetDataTip(STR_NETWORK_CLIENT_LIST_SERVER_INVITE_CODE, STR_NULL),
NWidget(NWID_SPACER), SetMinimalSize(10, 0),
NWidget(WWT_TEXT, COLOUR_GREY, WID_CL_SERVER_INVITE_CODE), SetFill(1, 0), SetMinimalTextLines(1, 0), SetResize(1, 0), SetDataTip(STR_BLACK_RAW_STRING, STR_NETWORK_CLIENT_LIST_SERVER_INVITE_CODE_TOOLTIP), SetAlignment(SA_VERT_CENTER | SA_RIGHT),
EndContainer(),
NWidget(NWID_HORIZONTAL), SetPIP(0, 3, 0),
NWidget(WWT_TEXT, COLOUR_GREY), SetMinimalTextLines(1, 0), SetDataTip(STR_NETWORK_CLIENT_LIST_SERVER_CONNECTION_TYPE, STR_NULL),
NWidget(NWID_SPACER), SetMinimalSize(10, 0),
NWidget(WWT_TEXT, COLOUR_GREY, WID_CL_SERVER_CONNECTION_TYPE), SetFill(1, 0), SetMinimalTextLines(1, 0), SetResize(1, 0), SetDataTip(STR_BLACK_STRING, STR_NETWORK_CLIENT_LIST_SERVER_CONNECTION_TYPE_TOOLTIP), SetAlignment(SA_VERT_CENTER | SA_RIGHT),
NWidget(WWT_FRAME, COLOUR_GREY), SetDataTip(STR_NETWORK_CLIENT_LIST_SERVER, STR_NULL), SetPadding(4, 4, 0, 4), SetPIP(0, 2, 0),
NWidget(NWID_HORIZONTAL), SetPIP(0, 3, 0),
NWidget(WWT_TEXT, COLOUR_GREY), SetMinimalTextLines(1, 0), SetDataTip(STR_NETWORK_CLIENT_LIST_SERVER_NAME, STR_NULL),
NWidget(NWID_SPACER), SetMinimalSize(10, 0),
NWidget(WWT_TEXT, COLOUR_GREY, WID_CL_SERVER_NAME), SetFill(1, 0), SetMinimalTextLines(1, 0), SetResize(1, 0), SetDataTip(STR_BLACK_RAW_STRING, STR_NETWORK_CLIENT_LIST_SERVER_NAME_TOOLTIP), SetAlignment(SA_VERT_CENTER | SA_RIGHT),
NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_CL_SERVER_NAME_EDIT), SetMinimalSize(12, 14), SetDataTip(SPR_RENAME, STR_NETWORK_CLIENT_LIST_SERVER_NAME_EDIT_TOOLTIP),
EndContainer(),
NWidget(NWID_SELECTION, INVALID_COLOUR, WID_CL_SERVER_SELECTOR),
NWidget(NWID_VERTICAL),
NWidget(NWID_HORIZONTAL), SetPIP(0, 3, 0),
NWidget(WWT_TEXT, COLOUR_GREY), SetMinimalTextLines(1, 0), SetDataTip(STR_NETWORK_CLIENT_LIST_SERVER_VISIBILITY, STR_NULL),
NWidget(NWID_SPACER), SetMinimalSize(10, 0), SetFill(1, 0), SetResize(1, 0),
NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_CL_SERVER_VISIBILITY), SetDataTip(STR_BLACK_STRING, STR_NETWORK_CLIENT_LIST_SERVER_VISIBILITY_TOOLTIP),
EndContainer(),
NWidget(NWID_HORIZONTAL), SetPIP(0, 3, 0),
NWidget(WWT_TEXT, COLOUR_GREY), SetMinimalTextLines(1, 0), SetDataTip(STR_NETWORK_CLIENT_LIST_SERVER_INVITE_CODE, STR_NULL),
NWidget(NWID_SPACER), SetMinimalSize(10, 0),
NWidget(WWT_TEXT, COLOUR_GREY, WID_CL_SERVER_INVITE_CODE), SetFill(1, 0), SetMinimalTextLines(1, 0), SetResize(1, 0), SetDataTip(STR_BLACK_RAW_STRING, STR_NETWORK_CLIENT_LIST_SERVER_INVITE_CODE_TOOLTIP), SetAlignment(SA_VERT_CENTER | SA_RIGHT),
EndContainer(),
NWidget(NWID_HORIZONTAL), SetPIP(0, 3, 0),
NWidget(WWT_TEXT, COLOUR_GREY), SetMinimalTextLines(1, 0), SetDataTip(STR_NETWORK_CLIENT_LIST_SERVER_CONNECTION_TYPE, STR_NULL),
NWidget(NWID_SPACER), SetMinimalSize(10, 0),
NWidget(WWT_TEXT, COLOUR_GREY, WID_CL_SERVER_CONNECTION_TYPE), SetFill(1, 0), SetMinimalTextLines(1, 0), SetResize(1, 0), SetDataTip(STR_BLACK_STRING, STR_NETWORK_CLIENT_LIST_SERVER_CONNECTION_TYPE_TOOLTIP), SetAlignment(SA_VERT_CENTER | SA_RIGHT),
EndContainer(),
EndContainer(),
EndContainer(),
EndContainer(),
@@ -1938,15 +1594,15 @@ private:
/**
* Part of RebuildList() to create the information for a single company.
* @param company_id The company to build the list for.
* @param own_ci The NetworkClientInfo of the client itself.
* @param client_playas The company the client is joined as.
*/
void RebuildListCompany(CompanyID company_id, const NetworkClientInfo *own_ci)
void RebuildListCompany(CompanyID company_id, CompanyID client_playas)
{
ButtonCommon *chat_button = new CompanyButton(SPR_CHAT, company_id == COMPANY_SPECTATOR ? STR_NETWORK_CLIENT_LIST_CHAT_SPECTATOR_TOOLTIP : STR_NETWORK_CLIENT_LIST_CHAT_COMPANY_TOOLTIP, COLOUR_ORANGE, company_id, &NetworkClientListWindow::OnClickCompanyChat);
if (_network_server) this->buttons[line_count].emplace_back(new CompanyButton(SPR_ADMIN, STR_NETWORK_CLIENT_LIST_ADMIN_COMPANY_TOOLTIP, COLOUR_RED, company_id, &NetworkClientListWindow::OnClickCompanyAdmin, company_id == COMPANY_SPECTATOR));
this->buttons[line_count].emplace_back(chat_button);
if (own_ci->client_playas != company_id) this->buttons[line_count].emplace_back(new CompanyButton(SPR_JOIN, STR_NETWORK_CLIENT_LIST_JOIN_TOOLTIP, COLOUR_ORANGE, company_id, &NetworkClientListWindow::OnClickCompanyJoin, company_id != COMPANY_SPECTATOR && Company::Get(company_id)->is_ai));
if (client_playas != company_id) this->buttons[line_count].emplace_back(new CompanyButton(SPR_JOIN, STR_NETWORK_CLIENT_LIST_JOIN_TOOLTIP, COLOUR_ORANGE, company_id, &NetworkClientListWindow::OnClickCompanyJoin, company_id != COMPANY_SPECTATOR && Company::Get(company_id)->is_ai));
this->line_count += 1;
@@ -1977,6 +1633,7 @@ private:
void RebuildList()
{
const NetworkClientInfo *own_ci = NetworkClientInfo::GetByClientID(_network_own_client_id);
CompanyID client_playas = own_ci == nullptr ? COMPANY_SPECTATOR : own_ci->client_playas;
this->buttons.clear();
this->line_count = 0;
@@ -1984,24 +1641,24 @@ private:
this->player_self_index = -1;
/* As spectator, show a line to create a new company. */
if (own_ci->client_playas == COMPANY_SPECTATOR && !NetworkMaxCompaniesReached()) {
if (client_playas == COMPANY_SPECTATOR && !NetworkMaxCompaniesReached()) {
this->buttons[line_count].emplace_back(new CompanyButton(SPR_JOIN, STR_NETWORK_CLIENT_LIST_NEW_COMPANY_TOOLTIP, COLOUR_ORANGE, COMPANY_SPECTATOR, &NetworkClientListWindow::OnClickCompanyNew));
this->line_count += 1;
}
if (own_ci->client_playas != COMPANY_SPECTATOR) {
this->RebuildListCompany(own_ci->client_playas, own_ci);
if (client_playas != COMPANY_SPECTATOR) {
this->RebuildListCompany(client_playas, client_playas);
}
/* Companies */
for (const Company *c : Company::Iterate()) {
if (c->index == own_ci->client_playas) continue;
if (c->index == client_playas) continue;
this->RebuildListCompany(c->index, own_ci);
this->RebuildListCompany(c->index, client_playas);
}
/* Spectators */
this->RebuildListCompany(COMPANY_SPECTATOR, own_ci);
this->RebuildListCompany(COMPANY_SPECTATOR, client_playas);
this->vscroll->SetCount(this->line_count);
}
@@ -2058,6 +1715,7 @@ public:
/* Currently server information is not sync'd to clients, so we cannot show it on clients. */
this->GetWidget<NWidgetStacked>(WID_CL_SERVER_SELECTOR)->SetDisplayedPlane(_network_server ? 0 : SZSP_HORIZONTAL);
this->SetWidgetDisabledState(WID_CL_SERVER_NAME_EDIT, !_network_server);
}
void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize) override
@@ -2092,7 +1750,7 @@ public:
{
switch (widget) {
case WID_CL_SERVER_NAME:
SetDParamStr(0, _settings_client.network.server_name);
SetDParamStr(0, _network_server ? _settings_client.network.server_name : _network_server_name);
break;
case WID_CL_SERVER_VISIBILITY:
@@ -2421,16 +2079,18 @@ public:
}
NetworkClientInfo *own_ci = NetworkClientInfo::GetByClientID(_network_own_client_id);
if (own_ci->client_playas == COMPANY_SPECTATOR && !NetworkMaxCompaniesReached()) {
CompanyID client_playas = own_ci == nullptr ? COMPANY_SPECTATOR : own_ci->client_playas;
if (client_playas == COMPANY_SPECTATOR && !NetworkMaxCompaniesReached()) {
this->DrawCompany(COMPANY_NEW_COMPANY, r.left, r.right, r.top, line);
}
if (own_ci->client_playas != COMPANY_SPECTATOR) {
this->DrawCompany(own_ci->client_playas, r.left, r.right, r.top, line);
if (client_playas != COMPANY_SPECTATOR) {
this->DrawCompany(client_playas, r.left, r.right, r.top, line);
}
for (const Company *c : Company::Iterate()) {
if (own_ci->client_playas == c->index) continue;
if (client_playas == c->index) continue;
this->DrawCompany(c->index, r.left, r.right, r.top, line);
}
@@ -2714,3 +2374,105 @@ void ShowNetworkCompanyPasswordWindow(Window *parent)
new NetworkCompanyPasswordWindow(&_network_company_password_window_desc, parent);
}
/**
* Window used for asking the user if he is okay using a TURN server.
*/
struct NetworkAskRelayWindow : public Window {
std::string connection_string; ///< The TURN server we want to connect to.
std::string token; ///< The token for this connection.
NetworkAskRelayWindow(WindowDesc *desc, Window *parent, const std::string &connection_string, const std::string &token) : Window(desc), connection_string(connection_string), token(token)
{
this->parent = parent;
this->InitNested(0);
}
void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize) override
{
if (widget == WID_NAR_TEXT) {
*size = GetStringBoundingBox(STR_NETWORK_ASK_RELAY_TEXT);
size->height = GetStringHeight(STR_NETWORK_ASK_RELAY_TEXT, size->width - WD_FRAMETEXT_LEFT - WD_FRAMETEXT_RIGHT) + WD_FRAMETEXT_BOTTOM + WD_FRAMETEXT_TOP;
}
}
void DrawWidget(const Rect &r, int widget) const override
{
if (widget == WID_NAR_TEXT) {
DrawStringMultiLine(r.left + WD_FRAMETEXT_LEFT, r.right - WD_FRAMETEXT_RIGHT, r.top + WD_FRAMETEXT_TOP, r.bottom - WD_FRAMETEXT_BOTTOM, STR_NETWORK_ASK_RELAY_TEXT, TC_FROMSTRING, SA_CENTER);
}
}
void FindWindowPlacementAndResize(int def_width, int def_height) override
{
/* Position query window over the calling window, ensuring it's within screen bounds. */
this->left = Clamp(parent->left + (parent->width / 2) - (this->width / 2), 0, _screen.width - this->width);
this->top = Clamp(parent->top + (parent->height / 2) - (this->height / 2), 0, _screen.height - this->height);
this->SetDirty();
}
void SetStringParameters(int widget) const override
{
switch (widget) {
case WID_NAR_TEXT:
SetDParamStr(0, this->connection_string);
break;
}
}
void OnClick(Point pt, int widget, int click_count) override
{
switch (widget) {
case WID_NAR_NO:
_network_coordinator_client.ConnectFailure(this->token, 0);
delete this;
break;
case WID_NAR_YES_ONCE:
_network_coordinator_client.StartTurnConnection(this->token);
delete this;
break;
case WID_NAR_YES_ALWAYS:
_settings_client.network.use_relay_service = URS_ALLOW;
_network_coordinator_client.StartTurnConnection(this->token);
delete this;
break;
}
}
};
static const NWidgetPart _nested_network_ask_relay_widgets[] = {
NWidget(NWID_HORIZONTAL),
NWidget(WWT_CLOSEBOX, COLOUR_RED),
NWidget(WWT_CAPTION, COLOUR_RED, WID_NAR_CAPTION), SetDataTip(STR_NETWORK_ASK_RELAY_CAPTION, STR_NULL),
EndContainer(),
NWidget(WWT_PANEL, COLOUR_RED), SetPIP(0, 0, 8),
NWidget(WWT_TEXT, COLOUR_RED, WID_NAR_TEXT), SetAlignment(SA_HOR_CENTER), SetFill(1, 1),
NWidget(NWID_HORIZONTAL, NC_EQUALSIZE), SetPIP(10, 15, 10),
NWidget(WWT_PUSHTXTBTN, COLOUR_YELLOW, WID_NAR_NO), SetMinimalSize(71, 12), SetFill(1, 1), SetDataTip(STR_NETWORK_ASK_RELAY_NO, STR_NULL),
NWidget(WWT_PUSHTXTBTN, COLOUR_YELLOW, WID_NAR_YES_ONCE), SetMinimalSize(71, 12), SetFill(1, 1), SetDataTip(STR_NETWORK_ASK_RELAY_YES_ONCE, STR_NULL),
NWidget(WWT_PUSHTXTBTN, COLOUR_YELLOW, WID_NAR_YES_ALWAYS), SetMinimalSize(71, 12), SetFill(1, 1), SetDataTip(STR_NETWORK_ASK_RELAY_YES_ALWAYS, STR_NULL),
EndContainer(),
EndContainer(),
};
static WindowDesc _network_ask_relay_desc(
WDP_CENTER, nullptr, 0, 0,
WC_NETWORK_ASK_RELAY, WC_NONE,
WDF_MODAL,
_nested_network_ask_relay_widgets, lengthof(_nested_network_ask_relay_widgets)
);
/**
* Show a modal confirmation window with "no" / "yes, once" / "yes, always" buttons.
* @param connection_string The relay server we want to connect to.
* @param token The token for this connection.
*/
void ShowNetworkAskRelay(const std::string &connection_string, const std::string &token)
{
DeleteWindowByClass(WC_NETWORK_ASK_RELAY);
Window *parent = FindWindowById(WC_MAIN_WINDOW, 0);
new NetworkAskRelayWindow(&_network_ask_relay_desc, parent, connection_string, token);
}

View File

@@ -38,7 +38,6 @@ struct NetworkCompanyInfo : NetworkCompanyStats {
std::string clients; ///< The clients that control this company (Name1, name2, ..)
};
NetworkCompanyInfo *GetLobbyCompanyInfo(CompanyID company);
NetworkGameList *GetLobbyGameInfo();
void ShowNetworkAskRelay(const std::string &connection_string, const std::string &token);
#endif /* NETWORK_GUI_H */

View File

@@ -93,12 +93,14 @@ extern uint32 _network_join_bytes_total;
extern ConnectionType _network_server_connection_type;
extern std::string _network_server_invite_code;
/* Variable available for clients. */
extern std::string _network_server_name;
extern uint8 _network_reconnect;
extern CompanyMask _network_company_passworded;
void NetworkQueryServer(const std::string &connection_string);
void NetworkQueryLobbyServer(const std::string &connection_string);
void GetBindAddresses(NetworkAddressList *addresses, uint16 port);
struct NetworkGameList *NetworkAddServer(const std::string &connection_string, bool manually = true, bool never_expire = false);

View File

@@ -378,66 +378,6 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::SendGameInfoExtended(PacketGam
return NETWORK_RECV_STATUS_OKAY;
}
/** Send the client information about the companies. */
NetworkRecvStatus ServerNetworkGameSocketHandler::SendCompanyInfo()
{
/* Fetch the latest version of the stats */
NetworkCompanyStats company_stats[MAX_COMPANIES];
NetworkPopulateCompanyStats(company_stats);
/* Make a list of all clients per company */
std::string clients[MAX_COMPANIES];
/* Add the local player (if not dedicated) */
const NetworkClientInfo *ci = NetworkClientInfo::GetByClientID(CLIENT_ID_SERVER);
if (ci != nullptr && Company::IsValidID(ci->client_playas)) {
clients[ci->client_playas] = ci->client_name;
}
for (NetworkClientSocket *csi : NetworkClientSocket::Iterate()) {
char client_name[NETWORK_CLIENT_NAME_LENGTH];
((ServerNetworkGameSocketHandler*)csi)->GetClientName(client_name, lastof(client_name));
ci = csi->GetInfo();
if (ci != nullptr && Company::IsValidID(ci->client_playas)) {
if (!clients[ci->client_playas].empty()) {
clients[ci->client_playas] += ", ";
}
clients[ci->client_playas] += client_name;
}
}
/* Now send the data */
Packet *p;
for (const Company *company : Company::Iterate()) {
p = new Packet(PACKET_SERVER_COMPANY_INFO, SHRT_MAX);
p->Send_uint8 (NETWORK_COMPANY_INFO_VERSION);
p->Send_bool (true);
this->SendCompanyInformation(p, company, &company_stats[company->index]);
if (clients[company->index].empty()) {
p->Send_string("<none>");
} else {
p->Send_string(clients[company->index]);
}
this->SendPacket(p);
}
p = new Packet(PACKET_SERVER_COMPANY_INFO, SHRT_MAX);
p->Send_uint8 (NETWORK_COMPANY_INFO_VERSION);
p->Send_bool (false);
this->SendPacket(p);
return NETWORK_RECV_STATUS_OKAY;
}
/**
* Send an error to the client, and close its connection.
* @param error The error to disconnect for.
@@ -864,7 +804,7 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::SendConfigUpdate()
Packet *p = new Packet(PACKET_SERVER_CONFIG_UPDATE, SHRT_MAX);
p->Send_uint8(_settings_client.network.max_companies);
p->Send_uint8(_settings_client.network.max_spectators);
p->Send_string(_settings_client.network.server_name);
this->SendPacket(p);
return NETWORK_RECV_STATUS_OKAY;
}
@@ -895,11 +835,6 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::Receive_CLIENT_GAME_INFO(Packe
}
}
NetworkRecvStatus ServerNetworkGameSocketHandler::Receive_CLIENT_COMPANY_INFO(Packet *p)
{
return this->SendCompanyInfo();
}
NetworkRecvStatus ServerNetworkGameSocketHandler::Receive_CLIENT_NEWGRFS_CHECKED(Packet *p)
{
if (this->status != STATUS_NEWGRFS_CHECK) {
@@ -950,9 +885,6 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::Receive_CLIENT_JOIN(Packet *p)
}
break;
case COMPANY_SPECTATOR: // Spectator
if (NetworkSpectatorCount() >= _settings_client.network.max_spectators) {
return this->SendError(NETWORK_ERROR_FULL);
}
break;
default: // Join another company (companies 1-8 (index 0-7))
if (!Company::IsValidHumanID(playas)) {
@@ -1624,58 +1556,6 @@ std::string ServerNetworkGameSocketHandler::GetDebugInfo() const
return stdstr_fmt("status: %d (%s)", this->status, GetClientStatusName(this->status));
}
/**
* Package some generic company information into a packet.
* @param p The packet that will contain the data.
* @param c The company to put the of into the packet.
* @param stats The statistics to put in the packet.
* @param max_len The maximum length of the company name.
*/
void NetworkSocketHandler::SendCompanyInformation(Packet *p, const Company *c, const NetworkCompanyStats *stats, uint max_len)
{
/* Grab the company name */
char company_name[NETWORK_COMPANY_NAME_LENGTH];
SetDParam(0, c->index);
assert(max_len <= lengthof(company_name));
GetString(company_name, STR_COMPANY_NAME, company_name + max_len - 1);
/* Get the income */
Money income = 0;
if (_cur_year - 1 == c->inaugurated_year) {
/* The company is here just 1 year, so display [2], else display[1] */
for (uint i = 0; i < lengthof(c->yearly_expenses[2]); i++) {
income -= c->yearly_expenses[2][i];
}
} else {
for (uint i = 0; i < lengthof(c->yearly_expenses[1]); i++) {
income -= c->yearly_expenses[1][i];
}
}
/* Send the information */
p->Send_uint8 (c->index);
p->Send_string(company_name);
p->Send_uint32(c->inaugurated_year);
p->Send_uint64(c->old_economy[0].company_value);
p->Send_uint64(c->money);
p->Send_uint64(income);
p->Send_uint16(c->old_economy[0].performance_history);
/* Send 1 if there is a password for the company else send 0 */
p->Send_bool (!_network_company_states[c->index].password.empty());
for (uint i = 0; i < NETWORK_VEH_END; i++) {
p->Send_uint16(stats->num_vehicle[i]);
}
for (uint i = 0; i < NETWORK_VEH_END; i++) {
p->Send_uint16(stats->num_station[i]);
}
p->Send_bool(c->is_ai);
}
/**
* Populate the company stats.
* @param stats the stats to update

View File

@@ -25,7 +25,6 @@ class ServerNetworkGameSocketHandler : public NetworkClientSocketPool::PoolItem<
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;
NetworkRecvStatus Receive_CLIENT_SETTINGS_PASSWORD(Packet *p) override;
@@ -46,7 +45,6 @@ protected:
NetworkRecvStatus SendGameInfo();
NetworkRecvStatus SendGameInfoExtended(PacketGameType reply_type, uint16 flags, uint16 version);
NetworkRecvStatus SendCompanyInfo();
NetworkRecvStatus SendNewGRFCheck();
NetworkRecvStatus SendWelcome();
NetworkRecvStatus SendNeedGamePassword();

View File

@@ -0,0 +1,135 @@
/*
* 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 network_turn.cpp TURN sending/receiving part of the network protocol. */
#include "../stdafx.h"
#include "../debug.h"
#include "../error.h"
#include "../strings_func.h"
#include "network_coordinator.h"
#include "network_turn.h"
#include "table/strings.h"
#include "../safeguards.h"
/** Connect to the TURN server. */
class NetworkTurnConnecter : public TCPConnecter {
private:
ClientNetworkTurnSocketHandler *handler;
public:
/**
* Initiate the connecting.
* @param connection_string The address of the TURN server.
*/
NetworkTurnConnecter(ClientNetworkTurnSocketHandler *handler, const std::string &connection_string) : TCPConnecter(connection_string, NETWORK_TURN_SERVER_PORT), handler(handler) {}
void OnFailure() override
{
this->handler->connecter = nullptr;
this->handler->ConnectFailure();
}
void OnConnect(SOCKET s) override
{
this->handler->connecter = nullptr;
handler->sock = s;
}
};
bool ClientNetworkTurnSocketHandler::Receive_TURN_ERROR(Packet *p)
{
this->ConnectFailure();
return false;
}
bool ClientNetworkTurnSocketHandler::Receive_TURN_CONNECTED(Packet *p)
{
std::string hostname = p->Recv_string(NETWORK_HOSTNAME_LENGTH);
/* Act like we no longer have a socket, as we are handing it over to the
* game handler. */
SOCKET game_sock = this->sock;
this->sock = INVALID_SOCKET;
NetworkAddress address = NetworkAddress(hostname, NETWORK_DEFAULT_PORT);
_network_coordinator_client.ConnectSuccess(this->token, game_sock, address);
return false;
}
/**
* Connect to the TURN server.
*/
void ClientNetworkTurnSocketHandler::Connect()
{
this->connect_started = true;
this->connecter = new NetworkTurnConnecter(this, this->connection_string);
}
/**
* Prepare a TURN connection.
* Not until you run Connect() on the resulting instance will it start setting
* up the TURN connection.
* @param token The token as received from the Game Coordinator.
* @param tracking_number The tracking number as recieved from the Game Coordinator.
* @param ticket The ticket as received from the Game Coordinator.
* @param connection_string Connection string of the TURN server.
* @return The handler for this TURN connection.
*/
/* static */ std::unique_ptr<ClientNetworkTurnSocketHandler> ClientNetworkTurnSocketHandler::Turn(const std::string &token, uint8 tracking_number, const std::string &ticket, const std::string &connection_string)
{
auto turn_handler = std::make_unique<ClientNetworkTurnSocketHandler>(token, tracking_number, connection_string);
Packet *p = new Packet(PACKET_TURN_SERCLI_CONNECT);
p->Send_uint8(NETWORK_COORDINATOR_VERSION);
p->Send_string(ticket);
turn_handler->SendPacket(p);
return turn_handler;
}
void ClientNetworkTurnSocketHandler::ConnectFailure()
{
_network_coordinator_client.ConnectFailure(this->token, this->tracking_number);
}
NetworkRecvStatus ClientNetworkTurnSocketHandler::CloseConnection(bool error)
{
NetworkTurnSocketHandler::CloseConnection(error);
/* If our connecter is still pending, shut it down too. Otherwise the
* callback of the connecter can call into us, and our object is most
* likely about to be destroyed. */
if (this->connecter != nullptr) {
this->connecter->Kill();
this->connecter = nullptr;
}
return NETWORK_RECV_STATUS_OKAY;
}
/**
* Check whether we received/can send some data from/to the TURN server and
* when that's the case handle it appropriately
*/
void ClientNetworkTurnSocketHandler::SendReceive()
{
if (this->sock == INVALID_SOCKET) return;
if (this->CanSendReceive()) {
this->ReceivePackets();
}
this->SendPackets();
}

View File

@@ -0,0 +1,41 @@
/*
* 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 network_turn.h Part of the network protocol handling TURN requests. */
#ifndef NETWORK_TURN_H
#define NETWORK_TURN_H
#include "core/tcp_turn.h"
/** Class for handling the client side of the TURN connection. */
class ClientNetworkTurnSocketHandler : public NetworkTurnSocketHandler {
private:
std::string token; ///< Token of this connection.
uint8 tracking_number; ///< Tracking number of this connection.
std::string connection_string; ///< The connection string of the TURN server we are connecting to.
protected:
bool Receive_TURN_ERROR(Packet *p) override;
bool Receive_TURN_CONNECTED(Packet *p) override;
public:
TCPConnecter *connecter = nullptr; ///< Connecter instance.
bool connect_started = false; ///< Whether we started the connection.
ClientNetworkTurnSocketHandler(const std::string &token, uint8 tracking_number, const std::string &connection_string) : token(token), tracking_number(tracking_number), connection_string(connection_string) {}
NetworkRecvStatus CloseConnection(bool error = true) override;
void SendReceive();
void Connect();
void ConnectFailure();
static std::unique_ptr<ClientNetworkTurnSocketHandler> Turn(const std::string &token, uint8 tracking_number, const std::string &ticket, const std::string &connection_string);
};
#endif /* NETWORK_TURN_H */