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:
@@ -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
|
||||
|
@@ -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
|
||||
)
|
||||
|
@@ -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 */
|
||||
|
@@ -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 */
|
||||
|
@@ -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);
|
||||
}
|
||||
|
@@ -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 */
|
||||
|
@@ -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); }
|
||||
|
@@ -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:
|
||||
/**
|
||||
|
@@ -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); }
|
||||
|
@@ -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).
|
||||
|
70
src/network/core/tcp_turn.cpp
Normal file
70
src/network/core/tcp_turn.cpp
Normal 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); }
|
79
src/network/core/tcp_turn.h
Normal file
79
src/network/core/tcp_turn.h
Normal 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 */
|
@@ -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
|
||||
|
@@ -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);
|
||||
}
|
||||
|
@@ -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);
|
||||
|
@@ -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();
|
||||
}
|
||||
}
|
||||
|
@@ -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;
|
||||
|
@@ -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);
|
||||
|
||||
|
@@ -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. */
|
||||
|
@@ -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);
|
||||
}
|
||||
|
@@ -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 */
|
||||
|
@@ -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);
|
||||
|
@@ -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
|
||||
|
@@ -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();
|
||||
|
135
src/network/network_turn.cpp
Normal file
135
src/network/network_turn.cpp
Normal 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();
|
||||
}
|
41
src/network/network_turn.h
Normal file
41
src/network/network_turn.h
Normal 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 */
|
Reference in New Issue
Block a user