Merge commit 'f1dfa661a1898cde06a38ab4cb230c95912b245b' into jgrpp-beta

# Conflicts:
#	src/lang/estonian.txt
#	src/lang/hungarian.txt
#	src/network/core/game_info.cpp
#	src/network/core/game_info.h
#	src/network/core/packet.h
#	src/network/network.cpp
#	src/network/network_client.cpp
#	src/network/network_server.cpp
#	src/network/network_udp.cpp
#	src/openttd.cpp
#	src/string_func.h
This commit is contained in:
Jonathan G Rennison
2021-09-18 22:10:04 +01:00
47 changed files with 525 additions and 350 deletions

View File

@@ -23,11 +23,13 @@ static const int DEFAULT_CONNECT_TIMEOUT_SECONDS = 3; ///< Allow connect() three
*/
const char *NetworkAddress::GetHostname()
{
if (StrEmpty(this->hostname) && this->address.ss_family != AF_UNSPEC) {
if (this->hostname.empty() && this->address.ss_family != AF_UNSPEC) {
assert(this->address_length != 0);
getnameinfo((struct sockaddr *)&this->address, this->address_length, this->hostname, sizeof(this->hostname), nullptr, 0, NI_NUMERICHOST);
char buffer[NETWORK_HOSTNAME_LENGTH];
getnameinfo((struct sockaddr *)&this->address, this->address_length, buffer, sizeof(buffer), nullptr, 0, NI_NUMERICHOST);
this->hostname = buffer;
}
return this->hostname;
return this->hostname.c_str();
}
/**
@@ -244,32 +246,32 @@ SOCKET NetworkAddress::Resolve(int family, int socktype, int flags, SocketList *
/* Setting both hostname to nullptr and port to 0 is not allowed.
* As port 0 means bind to any port, the other must mean that
* we want to bind to 'all' IPs. */
if (StrEmpty(this->hostname) && this->address_length == 0 && this->GetPort() == 0) {
if (this->hostname.empty() && this->address_length == 0 && this->GetPort() == 0) {
reset_hostname = true;
int fam = this->address.ss_family;
if (fam == AF_UNSPEC) fam = family;
strecpy(this->hostname, fam == AF_INET ? "0.0.0.0" : "::", lastof(this->hostname));
this->hostname = fam == AF_INET ? "0.0.0.0" : "::";
}
static bool _resolve_timeout_error_message_shown = false;
auto start = std::chrono::steady_clock::now();
int e = getaddrinfo(StrEmpty(this->hostname) ? nullptr : this->hostname, port_name, &hints, &ai);
int e = getaddrinfo(this->hostname.empty() ? nullptr : this->hostname.c_str(), port_name, &hints, &ai);
auto end = std::chrono::steady_clock::now();
std::chrono::seconds duration = std::chrono::duration_cast<std::chrono::seconds>(end - start);
if (!_resolve_timeout_error_message_shown && duration >= std::chrono::seconds(5)) {
DEBUG(net, 0, "getaddrinfo for hostname \"%s\", port %s, address family %s and socket type %s took %i seconds",
this->hostname, port_name, AddressFamilyAsString(family), SocketTypeAsString(socktype), (int)duration.count());
this->hostname.c_str(), port_name, AddressFamilyAsString(family), SocketTypeAsString(socktype), (int)duration.count());
DEBUG(net, 0, " this is likely an issue in the DNS name resolver's configuration causing it to time out");
_resolve_timeout_error_message_shown = true;
}
if (reset_hostname) strecpy(this->hostname, "", lastof(this->hostname));
if (reset_hostname) this->hostname.clear();
if (e != 0) {
if (func != ResolveLoopProc) {
DEBUG(net, 0, "getaddrinfo for hostname \"%s\", port %s, address family %s and socket type %s failed: %s",
this->hostname, port_name, AddressFamilyAsString(family), SocketTypeAsString(socktype), FS2OTTD(gai_strerror(e)).c_str());
this->hostname.c_str(), port_name, AddressFamilyAsString(family), SocketTypeAsString(socktype), FS2OTTD(gai_strerror(e)).c_str());
}
return INVALID_SOCKET;
}
@@ -452,11 +454,11 @@ void NetworkAddress::Listen(int socktype, SocketList *sockets)
{
assert(sockets != nullptr);
/* Setting both hostname to nullptr and port to 0 is not allowed.
/* Setting both hostname to "" and port to 0 is not allowed.
* As port 0 means bind to any port, the other must mean that
* we want to bind to 'all' IPs. */
if (this->address_length == 0 && this->address.ss_family == AF_UNSPEC &&
StrEmpty(this->hostname) && this->GetPort() == 0) {
this->hostname.empty() && this->GetPort() == 0) {
this->Resolve(AF_INET, socktype, AI_ADDRCONFIG | AI_PASSIVE, sockets, ListenLoopProc);
this->Resolve(AF_INET6, socktype, AI_ADDRCONFIG | AI_PASSIVE, sockets, ListenLoopProc);
} else {

View File

@@ -28,10 +28,10 @@ typedef SmallMap<NetworkAddress, SOCKET> SocketList; ///< Type for a mapping
*/
class NetworkAddress {
private:
char hostname[NETWORK_HOSTNAME_LENGTH]; ///< The hostname
int address_length; ///< The length of the resolved address
sockaddr_storage address; ///< The resolved address
bool resolved; ///< Whether the address has been (tried to be) resolved
std::string hostname; ///< The hostname
int address_length; ///< The length of the resolved address
sockaddr_storage address; ///< The resolved address
bool resolved; ///< Whether the address has been (tried to be) resolved
/**
* Helper function to resolve something to a socket.
@@ -52,7 +52,6 @@ public:
address(address),
resolved(address_length != 0)
{
*this->hostname = '\0';
}
/**
@@ -64,7 +63,6 @@ public:
address_length(address_length),
resolved(address_length != 0)
{
*this->hostname = '\0';
memset(&this->address, 0, sizeof(this->address));
memcpy(&this->address, address, address_length);
}
@@ -75,16 +73,15 @@ public:
* @param port the port
* @param family the address family
*/
NetworkAddress(const char *hostname = "", uint16 port = 0, int family = AF_UNSPEC) :
NetworkAddress(std::string_view hostname = "", uint16 port = 0, int family = AF_UNSPEC) :
address_length(0),
resolved(false)
{
/* Also handle IPv6 bracket enclosed hostnames */
if (StrEmpty(hostname)) hostname = "";
if (*hostname == '[') hostname++;
strecpy(this->hostname, StrEmpty(hostname) ? "" : hostname, lastof(this->hostname));
char *tmp = strrchr(this->hostname, ']');
if (tmp != nullptr) *tmp = '\0';
if (!hostname.empty() && hostname.front() == '[' && hostname.back() == ']') {
hostname.remove_prefix(1);
hostname.remove_suffix(1);
}
this->hostname = hostname;
memset(&this->address, 0, sizeof(this->address));
this->address.ss_family = family;

View File

@@ -113,7 +113,7 @@ bool IsNetworkCompatibleVersion(const char *other, bool extended)
void CheckGameCompatibility(NetworkGameInfo &ngi, bool extended)
{
/* Check if we are allowed on this server based on the revision-check. */
ngi.version_compatible = IsNetworkCompatibleVersion(ngi.server_revision, extended);
ngi.version_compatible = IsNetworkCompatibleVersion(ngi.server_revision.c_str(), extended);
ngi.compatible = ngi.version_compatible;
/* Check if we have all the GRFs on the client-system too. */
@@ -123,31 +123,37 @@ void CheckGameCompatibility(NetworkGameInfo &ngi, bool extended)
}
/**
* Fill a NetworkGameInfo structure with the latest information of the server.
* @param ngi the NetworkGameInfo struct to fill with data.
* Fill a NetworkServerGameInfo structure with the static content, or things
* that are so static they can be updated on request from a settings change.
*/
void FillNetworkGameInfo(NetworkGameInfo &ngi)
void FillStaticNetworkServerGameInfo()
{
/* Update some game_info */
ngi.clients_on = _network_game_info.clients_on;
ngi.start_date = ConvertYMDToDate(_settings_game.game_creation.starting_year, 0, 1);
_network_game_info.use_password = !StrEmpty(_settings_client.network.server_password);
_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;
_network_game_info.dedicated = _network_dedicated;
_network_game_info.grfconfig = _grfconfig;
ngi.use_password = !StrEmpty(_settings_client.network.server_password);
ngi.clients_max = _settings_client.network.max_clients;
ngi.companies_on = (byte)Company::GetNumItems();
ngi.companies_max = _settings_client.network.max_companies;
ngi.spectators_on = NetworkSpectatorCount();
ngi.spectators_max = _settings_client.network.max_spectators;
ngi.game_date = _date;
ngi.map_width = MapSizeX();
ngi.map_height = MapSizeY();
ngi.map_set = _settings_game.game_creation.landscape;
ngi.dedicated = _network_dedicated;
ngi.grfconfig = _grfconfig;
_network_game_info.server_name = _settings_client.network.server_name;
_network_game_info.server_revision = GetNetworkRevisionString();
}
strecpy(ngi.server_name, _settings_client.network.server_name, lastof(ngi.server_name));
strecpy(ngi.server_revision, GetNetworkRevisionString(), lastof(ngi.server_revision));
strecpy(ngi.short_server_revision, _openttd_revision, lastof(ngi.short_server_revision));
/**
* Get the NetworkServerGameInfo structure with the latest information of the server.
* @return The current NetworkServerGameInfo.
*/
const NetworkServerGameInfo *GetCurrentNetworkServerGameInfo()
{
/* Client_on is used as global variable to keep track on the number of clients. */
_network_game_info.companies_on = (byte)Company::GetNumItems();
_network_game_info.spectators_on = NetworkSpectatorCount();
_network_game_info.game_date = _date;
return &_network_game_info;
}
/**
@@ -181,7 +187,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 NetworkGameInfo *info)
void SerializeNetworkGameInfo(Packet *p, const NetworkServerGameInfo *info)
{
p->Send_uint8 (NETWORK_GAME_INFO_VERSION);
@@ -247,7 +253,7 @@ void SerializeNetworkGameInfo(Packet *p, const NetworkGameInfo *info)
p->Send_string(""); // Used to be map-name.
p->Send_uint16(info->map_width);
p->Send_uint16(info->map_height);
p->Send_uint8 (info->map_set);
p->Send_uint8 (info->landscape);
p->Send_bool (info->dedicated);
}
@@ -256,7 +262,7 @@ void SerializeNetworkGameInfo(Packet *p, const NetworkGameInfo *info)
* @param p the packet to write the data to
* @param info the NetworkGameInfo struct to serialize
*/
void SerializeNetworkGameInfoExtended(Packet *p, const NetworkGameInfo *info, uint16 flags, uint16 version)
void SerializeNetworkGameInfoExtended(Packet *p, const NetworkServerGameInfo *info, uint16 flags, uint16 version)
{
p->Send_uint8(0); // version num
@@ -275,7 +281,7 @@ void SerializeNetworkGameInfoExtended(Packet *p, const NetworkGameInfo *info, ui
p->Send_string(""); // Used to be map-name.
p->Send_uint32(info->map_width);
p->Send_uint32(info->map_height);
p->Send_uint8 (info->map_set);
p->Send_uint8 (info->landscape);
p->Send_bool (info->dedicated);
{
@@ -310,7 +316,7 @@ void DeserializeNetworkGameInfo(Packet *p, NetworkGameInfo *info)
{
static const Date MAX_DATE = ConvertYMDToDate(MAX_YEAR, 11, 31); // December is month 11
info->game_info_version = p->Recv_uint8();
byte game_info_version = p->Recv_uint8();
/*
* Please observe the order.
@@ -320,7 +326,7 @@ void DeserializeNetworkGameInfo(Packet *p, NetworkGameInfo *info)
/* Update the documentation in game_info.h on changes
* to the NetworkGameInfo wire-protocol! */
switch (info->game_info_version) {
switch (game_info_version) {
case 4: {
GRFConfig **dst = &info->grfconfig;
uint i;
@@ -353,24 +359,24 @@ void DeserializeNetworkGameInfo(Packet *p, NetworkGameInfo *info)
FALLTHROUGH;
case 1:
p->Recv_string(info->server_name, sizeof(info->server_name));
p->Recv_string(info->server_revision, sizeof(info->server_revision));
info->server_name = p->Recv_string(NETWORK_NAME_LENGTH);
info->server_revision = p->Recv_string(NETWORK_REVISION_LENGTH);
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 ();
info->spectators_on = p->Recv_uint8 ();
if (info->game_info_version < 3) { // 16 bits dates got scrapped and are read earlier
if (game_info_version < 3) { // 16 bits dates got scrapped and are read earlier
info->game_date = p->Recv_uint16() + DAYS_TILL_ORIGINAL_BASE_YEAR;
info->start_date = p->Recv_uint16() + DAYS_TILL_ORIGINAL_BASE_YEAR;
}
while (p->Recv_uint8() != 0) {} // Used to contain the map-name.
info->map_width = p->Recv_uint16();
info->map_height = p->Recv_uint16();
info->map_set = p->Recv_uint8 ();
info->landscape = p->Recv_uint8 ();
info->dedicated = p->Recv_bool ();
if (info->map_set >= NETWORK_NUM_LANDSCAPES) info->map_set = 0;
if (info->landscape >= NETWORK_NUM_LANDSCAPES) info->landscape = 0;
}
}
@@ -386,15 +392,13 @@ void DeserializeNetworkGameInfoExtended(Packet *p, NetworkGameInfo *info)
const uint8 version = p->Recv_uint8();
if (version > 0) return; // Unknown version
info->game_info_version = 255;
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_string(info->server_name, sizeof(info->server_name));
p->Recv_string(info->server_revision, sizeof(info->server_revision));
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.
info->use_password = p->Recv_bool ();
info->clients_max = p->Recv_uint8 ();
@@ -403,7 +407,7 @@ void DeserializeNetworkGameInfoExtended(Packet *p, NetworkGameInfo *info)
while (p->Recv_uint8() != 0) {} // Used to contain the map-name.
info->map_width = p->Recv_uint32();
info->map_height = p->Recv_uint32();
info->map_set = p->Recv_uint8 ();
info->landscape = p->Recv_uint8 ();
info->dedicated = p->Recv_bool ();
{
@@ -425,7 +429,7 @@ void DeserializeNetworkGameInfoExtended(Packet *p, NetworkGameInfo *info)
}
}
if (info->map_set >= NETWORK_NUM_LANDSCAPES) info->map_set = 0;
if (info->landscape >= NETWORK_NUM_LANDSCAPES) info->landscape = 0;
}
/**

View File

@@ -57,36 +57,34 @@
*/
/**
* The game information that is not generated on-the-fly and has to
* be sent to the clients.
* The game information that is sent from the server to the client.
*/
struct NetworkServerGameInfo {
byte clients_on; ///< Current count of clients on server
GRFConfig *grfconfig; ///< List of NewGRF files used
Date start_date; ///< When the game started
Date game_date; ///< Current date
uint32 map_width; ///< Map width
uint32 map_height; ///< Map height
std::string server_name; ///< Server name
std::string server_revision; ///< The version number the server is using (e.g.: 'r304' or 0.5.0)
bool dedicated; ///< Is this a dedicated server?
bool use_password; ///< Is this server passworded?
byte clients_on; ///< Current count of clients on server
byte clients_max; ///< Max clients allowed on server
byte companies_on; ///< How many started companies do we have
byte companies_max; ///< Max companies allowed on server
byte spectators_on; ///< How many spectators do we have?
byte spectators_max; ///< Max spectators allowed on server
byte landscape; ///< The used landscape
};
/**
* The game information that is sent from the server to the clients.
* The game information that is sent from the server to the clients
* with extra information only required at the client side.
*/
struct NetworkGameInfo : NetworkServerGameInfo {
GRFConfig *grfconfig; ///< List of NewGRF files used
Date start_date; ///< When the game started
Date game_date; ///< Current date
uint32 map_width; ///< Map width
uint32 map_height; ///< Map height
char server_name[NETWORK_NAME_LENGTH]; ///< Server name
char short_server_revision[NETWORK_REVISION_LENGTH]; ///< The version number the server is using (e.g.: 'r304' or 0.5.0) (truncated)
char server_revision[NETWORK_LONG_REVISION_LENGTH]; ///< The version number the server is using (e.g.: 'r304' or 0.5.0)
bool dedicated; ///< Is this a dedicated server?
bool version_compatible; ///< Can we connect to this server or not? (based on server_revision)
bool compatible; ///< Can we connect to this server or not? (based on server_revision _and_ grf_match
bool use_password; ///< Is this server passworded?
byte game_info_version; ///< Version of the game info
byte clients_max; ///< Max clients allowed on server
byte companies_on; ///< How many started companies do we have
byte companies_max; ///< Max companies allowed on server
byte spectators_on; ///< How many spectators do we have?
byte spectators_max; ///< Max spectators allowed on server
byte map_set; ///< Graphical set
};
extern NetworkServerGameInfo _network_game_info;
@@ -95,14 +93,15 @@ const char *GetNetworkRevisionString();
bool IsNetworkCompatibleVersion(const char *other, bool extended = false);
void CheckGameCompatibility(NetworkGameInfo &ngi, bool extended = false);
void FillNetworkGameInfo(NetworkGameInfo &ngi);
void FillStaticNetworkServerGameInfo();
const NetworkServerGameInfo *GetCurrentNetworkServerGameInfo();
void DeserializeGRFIdentifier(Packet *p, GRFIdentifier *grf);
void SerializeGRFIdentifier(Packet *p, const GRFIdentifier *grf);
void DeserializeNetworkGameInfo(Packet *p, NetworkGameInfo *info);
void DeserializeNetworkGameInfoExtended(Packet *p, NetworkGameInfo *info);
void SerializeNetworkGameInfo(Packet *p, const NetworkGameInfo *info);
void SerializeNetworkGameInfoExtended(Packet *p, const NetworkGameInfo *info, uint16 flags, uint16 version);
void SerializeNetworkGameInfo(Packet *p, const NetworkServerGameInfo *info);
void SerializeNetworkGameInfoExtended(Packet *p, const NetworkServerGameInfo *info, uint16 flags, uint16 version);
#endif /* NETWORK_CORE_GAME_INFO_H */

View File

@@ -168,12 +168,11 @@ void Packet::Send_uint64(uint64 data)
* the string + '\0'. No size-byte or something.
* @param data The string to send
*/
void Packet::Send_string(const char *data)
void Packet::Send_string(const std::string_view data)
{
assert(data != nullptr);
/* Length of the string + 1 for the '\0' termination. */
assert(this->CanWriteToPacket(strlen(data) + 1));
while (this->buffer.emplace_back(*data++) != '\0') {}
assert(this->CanWriteToPacket(data.size() + 1));
this->buffer.insert(this->buffer.end(), data.begin(), data.end());
this->buffer.emplace_back('\0');
}
/**
@@ -404,6 +403,35 @@ void Packet::Recv_string(char *buffer, size_t size, StringValidationSettings set
str_validate(bufp, last, settings);
}
/**
* Reads characters (bytes) from the packet until it finds a '\0', or reaches a
* maximum of \c length characters.
* When the '\0' has not been reached in the first \c length read characters,
* more characters are read from the packet until '\0' has been reached. However,
* these characters will not end up in the returned string.
* The length of the returned string will be at most \c length - 1 characters.
* @param length The maximum length of the string including '\0'.
* @param settings The string validation settings.
* @return The validated string.
*/
std::string Packet::Recv_string(size_t length, StringValidationSettings settings)
{
assert(length > 1);
/* Both loops with Recv_uint8 terminate when reading past the end of the
* packet as Recv_uint8 then closes the connection and returns 0. */
std::string str;
char character;
while (--length > 0 && (character = this->Recv_uint8()) != '\0') str.push_back(character);
if (length == 0) {
/* The string in the packet was longer. Read until the termination. */
while (this->Recv_uint8() != '\0') {}
}
return str_validate(str, settings);
}
/**
* Get the amount of bytes that are still available for the Transfer functions.
* @return The number of bytes that still have to be transfered.

View File

@@ -69,7 +69,7 @@ public:
void Send_uint16(uint16 data);
void Send_uint32(uint32 data);
void Send_uint64(uint64 data);
void Send_string(const char *data);
void Send_string(const std::string_view data);
size_t Send_bytes (const byte *begin, const byte *end);
void Send_binary(const char *data, const size_t size);
@@ -88,6 +88,7 @@ public:
uint32 Recv_uint32();
uint64 Recv_uint64();
void Recv_string(char *buffer, size_t size, StringValidationSettings settings = SVS_REPLACE_WITH_QUESTION_MARK);
std::string Recv_string(size_t length, StringValidationSettings settings = SVS_REPLACE_WITH_QUESTION_MARK);
void Recv_string(std::string &buffer, StringValidationSettings settings = SVS_REPLACE_WITH_QUESTION_MARK);
void Recv_binary(char *buffer, size_t size);
void Recv_binary(std::string &buffer, size_t size);

View File

@@ -87,7 +87,7 @@ protected:
NetworkAddress address;
public:
TCPConnecter(const NetworkAddress &address);
TCPConnecter(const std::string &connection_string, uint16 default_port);
/** Silence the warnings */
virtual ~TCPConnecter() {}

View File

@@ -13,6 +13,7 @@
#include "../../thread.h"
#include "tcp.h"
#include "../network_internal.h"
#include "../../safeguards.h"
@@ -21,15 +22,16 @@ static std::vector<TCPConnecter *> _tcp_connecters;
/**
* Create a new connecter for the given address
* @param address the (un)resolved address to connect to
* @param connection_string the address to connect to
*/
TCPConnecter::TCPConnecter(const NetworkAddress &address) :
TCPConnecter::TCPConnecter(const std::string &connection_string, uint16 default_port) :
connected(false),
aborted(false),
killed(false),
sock(INVALID_SOCKET),
address(address)
sock(INVALID_SOCKET)
{
this->address = ParseConnectionString(connection_string, default_port);
_tcp_connecters.push_back(this);
if (!StartNewThread(nullptr, "ottd:tcp", &TCPConnecter::ThreadEntry, this)) {
this->Connect();

View File

@@ -203,11 +203,9 @@ int NetworkHTTPSocketHandler::HandleHeader()
*url = '\0';
NetworkAddress address = ParseConnectionString(hname, 80);
/* Restore the URL. */
*url = '/';
new NetworkHTTPContentConnecter(address, callback, url, data, depth);
new NetworkHTTPContentConnecter(hname, callback, url, data, depth);
return 0;
}

View File

@@ -81,16 +81,14 @@ class NetworkHTTPContentConnecter : TCPConnecter {
public:
/**
* Start the connecting.
* @param address the address to connect to
* @param callback the callback for HTTP retrieval
* @param url the url at the server
* @param data the data to send
* @param depth the depth (redirect recursion) of the queries
* @param connection_string The address to connect to.
* @param callback The callback for HTTP retrieval.
* @param url The url at the server.
* @param data The data to send.
* @param depth The depth (redirect recursion) of the queries.
*/
NetworkHTTPContentConnecter(const NetworkAddress &address,
HTTPCallback *callback, const char *url,
const char *data = nullptr, int depth = 0) :
TCPConnecter(address),
NetworkHTTPContentConnecter(const std::string &connection_string, HTTPCallback *callback, const char *url, const char *data = nullptr, int depth = 0) :
TCPConnecter(connection_string, 80),
callback(callback),
url(stredup(url)),
data(data),

View File

@@ -28,11 +28,11 @@ NetworkUDPSocketHandler::NetworkUDPSocketHandler(NetworkAddressList *bind)
this->bind.push_back(addr);
}
} else {
/* As hostname nullptr and port 0/nullptr don't go well when
/* As an empty hostname and port 0 don't go well when
* resolving it we need to add an address for each of
* the address families we support. */
this->bind.emplace_back(nullptr, 0, AF_INET);
this->bind.emplace_back(nullptr, 0, AF_INET6);
this->bind.emplace_back("", 0, AF_INET);
this->bind.emplace_back("", 0, AF_INET6);
}
this->fragment_token = ((uint64) InteractiveRandom()) | (((uint64) InteractiveRandom()) << 32);

View File

@@ -34,6 +34,7 @@
#include "../gfx_func.h"
#include "../error.h"
#include "../core/checksum_func.hpp"
#include <charconv>
#include "../safeguards.h"
@@ -479,36 +480,51 @@ static void CheckPauseOnJoin()
* Converts a string to ip/port/company
* Format: IP:port#company
*
* connection_string will be re-terminated to separate out the hostname, port will
* be set to the port strings given by the user, inside the memory area originally
* occupied by connection_string. Similar for company, if set.
* Returns the IP part as a string view into the passed string. This view is
* valid as long the passed connection string is valid. If there is no port
* present in the connection string, the port reference will not be touched.
* When there is no company ID present in the connection string or company_id
* is nullptr, then company ID will not be touched.
*
* @param connection_string The string with the connection data.
* @param port The port reference to set.
* @param company_id The company ID to set, if available.
* @return A std::string_view into the connection string with the (IP) address part.
*/
void ParseFullConnectionString(const char **company, const char **port, char *connection_string)
std::string_view ParseFullConnectionString(const std::string &connection_string, uint16 &port, CompanyID *company_id)
{
bool ipv6 = (strchr(connection_string, ':') != strrchr(connection_string, ':'));
for (char *p = connection_string; *p != '\0'; p++) {
switch (*p) {
case '[':
ipv6 = true;
break;
std::string_view ip = connection_string;
if (company_id != nullptr) {
size_t offset = ip.find_last_of('#');
if (offset != std::string::npos) {
std::string_view company_string = ip.substr(offset + 1);
ip = ip.substr(0, offset);
case ']':
ipv6 = false;
break;
case '#':
if (company == nullptr) continue;
*company = p + 1;
*p = '\0';
break;
case ':':
if (ipv6) break;
*port = p + 1;
*p = '\0';
break;
uint8 company_value;
auto [_, err] = std::from_chars(company_string.data(), company_string.data() + company_string.size(), company_value);
if (err == std::errc()) {
if (company_value != COMPANY_NEW_COMPANY && company_value != COMPANY_SPECTATOR) {
if (company_value > MAX_COMPANIES || company_value == 0) {
*company_id = COMPANY_SPECTATOR;
} else {
/* "#1" means the first company, which has index 0. */
*company_id = (CompanyID)(company_value - 1);
}
} else {
*company_id = (CompanyID)company_value;
}
}
}
}
size_t port_offset = ip.find_last_of(':');
size_t ipv6_close = ip.find_last_of(']');
if (port_offset != std::string::npos && (ipv6_close == std::string::npos || ipv6_close < port_offset)) {
std::string_view port_string = ip.substr(port_offset + 1);
ip = ip.substr(0, port_offset);
std::from_chars(port_string.data(), port_string.data() + port_string.size(), port);
}
return ip;
}
/**
@@ -519,16 +535,11 @@ void ParseFullConnectionString(const char **company, const char **port, char *co
* @param default_port The default port to set port to if not in connection_string.
* @return A valid NetworkAddress of the parsed information.
*/
NetworkAddress ParseConnectionString(const std::string &connection_string, int default_port)
NetworkAddress ParseConnectionString(const std::string &connection_string, uint16 default_port)
{
char internal_connection_string[NETWORK_HOSTNAME_PORT_LENGTH];
strecpy(internal_connection_string, connection_string.c_str(), lastof(internal_connection_string));
const char *port = nullptr;
ParseFullConnectionString(nullptr, &port, internal_connection_string);
int rport = port != nullptr ? atoi(port) : default_port;
return NetworkAddress(internal_connection_string, rport);
uint16 port = default_port;
std::string_view ip = ParseFullConnectionString(connection_string, port);
return NetworkAddress(ip, port);
}
/**
@@ -536,24 +547,16 @@ NetworkAddress ParseConnectionString(const std::string &connection_string, int d
* NetworkAddress, where the string can be postfixed with "#company" to
* indicate the requested company.
*
* @param company Pointer to the company variable to set iff indicted.
* @param connection_string The string to parse.
* @param default_port The default port to set port to if not in connection_string.
* @param company Pointer to the company variable to set iff indicted.
* @return A valid NetworkAddress of the parsed information.
*/
NetworkAddress ParseGameConnectionString(CompanyID *company, const std::string &connection_string, int default_port)
static NetworkAddress ParseGameConnectionString(const std::string &connection_string, uint16 default_port, CompanyID *company)
{
char internal_connection_string[NETWORK_HOSTNAME_PORT_LENGTH + 4]; // 4 extra for the "#" and company
strecpy(internal_connection_string, connection_string.c_str(), lastof(internal_connection_string));
const char *port_s = nullptr;
const char *company_s = nullptr;
ParseFullConnectionString(&company_s, &port_s, internal_connection_string);
if (company_s != nullptr) *company = (CompanyID)atoi(company_s);
int port = port_s != nullptr ? atoi(port_s) : default_port;
return NetworkAddress(internal_connection_string, port);
uint16 port = default_port;
std::string_view ip = ParseFullConnectionString(connection_string, port, company);
return NetworkAddress(ip, port);
}
/**
@@ -637,9 +640,10 @@ static void NetworkInitialize(bool close_admins = true)
class TCPQueryConnecter : TCPConnecter {
private:
bool request_company_info;
std::string connection_string;
public:
TCPQueryConnecter(const NetworkAddress &address, bool request_company_info) : TCPConnecter(address), request_company_info(request_company_info) {}
TCPQueryConnecter(const std::string &connection_string, bool request_company_info) : TCPConnecter(connection_string, NETWORK_DEFAULT_PORT), request_company_info(request_company_info), connection_string(connection_string) {}
void OnFailure() override
{
@@ -649,24 +653,24 @@ public:
void OnConnect(SOCKET s) override
{
_networking = true;
new ClientNetworkGameSocketHandler(s, address);
new ClientNetworkGameSocketHandler(s, this->connection_string);
MyClient::SendInformationQuery(request_company_info);
}
};
/**
* Query a server to fetch his game-info.
* @param address the address to query.
* @param connection_string the address to query.
* @param request_company_info Whether to request company info too.
*/
void NetworkTCPQueryServer(NetworkAddress address, bool request_company_info)
void NetworkTCPQueryServer(const std::string &connection_string, bool request_company_info)
{
if (!_network_available) return;
NetworkDisconnect();
NetworkInitialize();
new TCPQueryConnecter(address, request_company_info);
new TCPQueryConnecter(connection_string, request_company_info);
}
/**
@@ -680,20 +684,18 @@ NetworkGameList *NetworkAddServer(const std::string &connection_string)
{
if (connection_string.empty()) return nullptr;
NetworkAddress address = ParseConnectionString(connection_string, NETWORK_DEFAULT_PORT);
/* Ensure the item already exists in the list */
NetworkGameList *item = NetworkGameListAddItem(address);
if (StrEmpty(item->info.server_name)) {
NetworkGameList *item = NetworkGameListAddItem(connection_string);
if (item->info.server_name.empty()) {
ClearGRFConfigList(&item->info.grfconfig);
address.GetAddressAsString(item->info.server_name, lastof(item->info.server_name));
item->info.server_name = connection_string;
item->manually = true;
NetworkRebuildHostList();
UpdateNetworkGameWindow();
}
NetworkTCPQueryServer(address);
NetworkTCPQueryServer(connection_string);
return item;
}
@@ -723,14 +725,17 @@ void NetworkRebuildHostList()
_network_host_list.clear();
for (NetworkGameList *item = _network_game_list; item != nullptr; item = item->next) {
if (item->manually) _network_host_list.emplace_back(NetworkAddressDumper().GetAddressAsString(&(item->address), false));
if (item->manually) _network_host_list.emplace_back(item->connection_string);
}
}
/** Non blocking connection create to actually connect to servers */
class TCPClientConnecter : TCPConnecter {
private:
std::string connection_string;
public:
TCPClientConnecter(const NetworkAddress &address) : TCPConnecter(address) {}
TCPClientConnecter(const std::string &connection_string) : TCPConnecter(connection_string, NETWORK_DEFAULT_PORT), connection_string(connection_string) {}
void OnFailure() override
{
@@ -740,7 +745,7 @@ public:
void OnConnect(SOCKET s) override
{
_networking = true;
new ClientNetworkGameSocketHandler(s, this->address);
new ClientNetworkGameSocketHandler(s, this->connection_string);
IConsoleCmdExec("exec scripts/on_client.scr 0");
NetworkClient_Connected();
}
@@ -766,34 +771,12 @@ public:
bool NetworkClientConnectGame(const std::string &connection_string, CompanyID default_company, const char *join_server_password, const char *join_company_password)
{
CompanyID join_as = default_company;
NetworkAddress address = ParseGameConnectionString(&join_as, connection_string, NETWORK_DEFAULT_PORT);
std::string resolved_connection_string = ParseGameConnectionString(connection_string, NETWORK_DEFAULT_PORT, &join_as).GetAddressAsString(false);
if (join_as != COMPANY_NEW_COMPANY && join_as != COMPANY_SPECTATOR) {
join_as--;
if (join_as >= MAX_COMPANIES) {
return false;
}
}
return NetworkClientConnectGame(address, join_as, join_server_password, join_company_password);
}
/**
* Join a client to the server at the given address.
* See the overloaded NetworkClientConnectGame for more details.
*
* @param address The network address of the server to join to.
* @param join_as The company number to join as.
* @param join_server_password The password for the server.
* @param join_company_password The password for the company.
* @return Whether the join has started.
*/
bool NetworkClientConnectGame(NetworkAddress &address, CompanyID join_as, const char *join_server_password, const char *join_company_password)
{
if (!_network_available) return false;
if (!NetworkValidateClientName()) return false;
_network_join.address = address;
_network_join.connection_string = resolved_connection_string;
_network_join.company = join_as;
_network_join.server_password = join_server_password;
_network_join.company_password = join_company_password;
@@ -822,11 +805,11 @@ void NetworkClientJoinGame()
NetworkDisconnect();
NetworkInitialize();
strecpy(_settings_client.network.last_joined, _network_join.address.GetAddressAsString(false).c_str(), lastof(_settings_client.network.last_joined));
strecpy(_settings_client.network.last_joined, _network_join.connection_string.c_str(), lastof(_settings_client.network.last_joined));
_network_join_status = NETWORK_JOIN_STATUS_CONNECTING;
ShowJoinStatusWindow();
new TCPClientConnecter(_network_join.address);
new TCPClientConnecter(_network_join.connection_string);
}
static void NetworkInitGameInfo()
@@ -835,6 +818,7 @@ static void NetworkInitGameInfo()
strecpy(_settings_client.network.server_name, "Unnamed Server", lastof(_settings_client.network.server_name));
}
FillStaticNetworkServerGameInfo();
/* The server is a client too */
_network_game_info.clients_on = _network_dedicated ? 0 : 1;
@@ -1243,7 +1227,7 @@ void NetworkStartUp()
/* Generate an server id when there is none yet */
if (StrEmpty(_settings_client.network.network_id)) NetworkGenerateServerId();
memset(&_network_game_info, 0, sizeof(_network_game_info));
_network_game_info = {};
NetworkInitialize();
DEBUG(net, 3, "[core] network online, multiplayer available");

View File

@@ -150,9 +150,9 @@ void ClientNetworkEmergencySave()
* Create a new socket for the client side of the game connection.
* @param s The socket to connect with.
*/
ClientNetworkGameSocketHandler::ClientNetworkGameSocketHandler (SOCKET s, NetworkAddress address)
ClientNetworkGameSocketHandler::ClientNetworkGameSocketHandler (SOCKET s, std::string connection_string)
: NetworkGameSocketHandler(s),
address(address), savegame(nullptr), token(0), status(STATUS_INACTIVE)
connection_string(std::move(connection_string)), savegame(nullptr), token(0), status(STATUS_INACTIVE)
{
assert(ClientNetworkGameSocketHandler::my_client == nullptr);
ClientNetworkGameSocketHandler::my_client = this;
@@ -702,7 +702,7 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_GAME_INFO(Packe
NetworkGameList *item = GetLobbyGameInfo();
if (item == nullptr) {
/* This is not the lobby, so add it to the game list. */
item = NetworkGameListAddItem(this->address);
item = NetworkGameListAddItem(this->connection_string);
}
/* Clear any existing GRFConfig chain. */

View File

@@ -15,7 +15,7 @@
/** Class for handling the client side of the game connection. */
class ClientNetworkGameSocketHandler : public NetworkGameSocketHandler {
private:
NetworkAddress address; ///< Address we are connected to.
std::string connection_string; ///< Address we are connected to.
struct PacketReader *savegame; ///< Packet reader for reading the savegame.
byte token; ///< The token we need to send back to the server to prove we're the right client.
@@ -86,7 +86,7 @@ protected:
static NetworkRecvStatus SendMapOk();
void CheckConnection();
public:
ClientNetworkGameSocketHandler(SOCKET s, NetworkAddress address);
ClientNetworkGameSocketHandler(SOCKET s, std::string connection_string);
~ClientNetworkGameSocketHandler();
NetworkRecvStatus CloseConnection(NetworkRecvStatus status) override;
@@ -132,10 +132,10 @@ void NetworkClientSetCompanyPassword(const char *password);
/** Information required to join a server. */
struct NetworkJoinInfo {
NetworkJoinInfo() : company(COMPANY_SPECTATOR), server_password(nullptr), company_password(nullptr) {}
NetworkAddress address; ///< The address of the server to join.
CompanyID company; ///< The company to join.
const char *server_password; ///< The password of the server to join.
const char *company_password; ///< The password of the company to join.
std::string connection_string; ///< The address of the server to join.
CompanyID company; ///< The company to join.
const char *server_password; ///< The password of the server to join.
const char *company_password; ///< The password of the company to join.
};
extern NetworkJoinInfo _network_join;

View File

@@ -367,8 +367,7 @@ void ClientNetworkContentSocketHandler::DownloadSelectedContentHTTP(const Conten
this->http_response_index = -1;
NetworkAddress address(NETWORK_CONTENT_MIRROR_HOST, NETWORK_CONTENT_MIRROR_PORT);
new NetworkHTTPContentConnecter(address, this, NETWORK_CONTENT_MIRROR_URL, content_request);
new NetworkHTTPContentConnecter(NETWORK_CONTENT_MIRROR_HOST, this, NETWORK_CONTENT_MIRROR_URL, content_request);
/* NetworkHTTPContentConnecter takes over freeing of content_request! */
}
@@ -774,7 +773,7 @@ public:
* Initiate the connecting.
* @param address The address of the server.
*/
NetworkContentConnecter(const NetworkAddress &address) : TCPConnecter(address) {}
NetworkContentConnecter(const std::string &connection_string) : TCPConnecter(connection_string, NETWORK_CONTENT_SERVER_PORT) {}
void OnFailure() override
{
@@ -800,7 +799,7 @@ void ClientNetworkContentSocketHandler::Connect()
{
if (this->sock != INVALID_SOCKET || this->isConnecting) return;
this->isConnecting = true;
new NetworkContentConnecter(NetworkAddress(NETWORK_CONTENT_SERVER_HOST, NETWORK_CONTENT_SERVER_PORT, AF_UNSPEC));
new NetworkContentConnecter(NETWORK_CONTENT_SERVER_HOST);
}
/**

View File

@@ -45,7 +45,7 @@ void NetworkReboot();
void NetworkDisconnect(bool blocking = false, bool close_admins = true);
void NetworkGameLoop();
void NetworkBackgroundLoop();
void ParseFullConnectionString(const char **company, const char **port, char *connection_string);
std::string_view ParseFullConnectionString(const std::string &connection_string, uint16 &port, CompanyID *company_id = nullptr);
void NetworkStartDebugLog(const std::string &connection_string);
void NetworkPopulateCompanyStats(NetworkCompanyStats *stats);
@@ -70,6 +70,7 @@ void NetworkServerDailyLoop();
void NetworkServerMonthlyLoop();
void NetworkServerYearlyLoop();
void NetworkServerSendConfigUpdate();
void NetworkServerUpdateGameInfo();
void NetworkServerShowStatusToConsole();
bool NetworkServerStart();
void NetworkServerNewCompany(const Company *company, NetworkClientInfo *ci);

View File

@@ -44,20 +44,20 @@ static void NetworkGameListHandleDelayedInsert()
while (ins_item != nullptr && !_network_game_delayed_insertion_list.compare_exchange_weak(ins_item, ins_item->next, std::memory_order_acq_rel)) {}
if (ins_item == nullptr) break; // No item left.
NetworkGameList *item = NetworkGameListAddItem(ins_item->address);
NetworkGameList *item = NetworkGameListAddItem(ins_item->connection_string);
if (item != nullptr) {
if (StrEmpty(item->info.server_name)) {
if (item->info.server_name.empty()) {
ClearGRFConfigList(&item->info.grfconfig);
memset(&item->info, 0, sizeof(item->info));
strecpy(item->info.server_name, ins_item->info.server_name, lastof(item->info.server_name));
item->info = {};
item->info.server_name = ins_item->info.server_name;
item->online = false;
}
item->manually |= ins_item->manually;
if (item->manually) NetworkRebuildHostList();
UpdateNetworkGameWindow();
}
free(ins_item);
delete ins_item;
}
}
@@ -67,19 +67,20 @@ static void NetworkGameListHandleDelayedInsert()
* @param address the address of the to-be added item
* @return a point to the newly added or already existing item
*/
NetworkGameList *NetworkGameListAddItem(NetworkAddress address)
NetworkGameList *NetworkGameListAddItem(const std::string &connection_string)
{
NetworkGameList *item, *prev_item;
/* Parse the connection string to ensure the default port is there. */
const std::string resolved_connection_string = ParseConnectionString(connection_string, NETWORK_DEFAULT_PORT).GetAddressAsString(false);
prev_item = nullptr;
for (item = _network_game_list; item != nullptr; item = item->next) {
if (item->address == address) return item;
if (item->connection_string == resolved_connection_string) return item;
prev_item = item;
}
item = CallocT<NetworkGameList>(1);
item->next = nullptr;
item->address = address;
item = new NetworkGameList(resolved_connection_string);
if (prev_item == nullptr) {
_network_game_list = item;
@@ -109,8 +110,7 @@ void NetworkGameListRemoveItem(NetworkGameList *remove)
/* Remove GRFConfig information */
ClearGRFConfigList(&remove->info.grfconfig);
free(remove);
remove = nullptr;
delete remove;
DEBUG(net, 4, "[gamelist] removed server from list");
NetworkRebuildHostList();
@@ -141,7 +141,7 @@ void NetworkGameListRequery()
/* item gets mostly zeroed by NetworkUDPQueryServer */
uint8 retries = item->retries;
NetworkUDPQueryServer(item->address);
NetworkUDPQueryServer(item->connection_string);
item->retries = (retries >= REFRESH_GAMEINFO_X_REQUERIES) ? 0 : retries;
}
}

View File

@@ -16,19 +16,24 @@
/** Structure with information shown in the game list (GUI) */
struct NetworkGameList {
NetworkGameInfo info; ///< The game information of this server
NetworkAddress address; ///< The connection info of the game server
bool online; ///< False if the server did not respond (default status)
bool manually; ///< True if the server was added manually
uint8 retries; ///< Number of retries (to stop requerying)
NetworkGameList *next; ///< Next pointer to make a linked game list
NetworkGameList(const std::string &connection_string, bool manually = false) :
connection_string(connection_string), manually(manually)
{
}
NetworkGameInfo info = {}; ///< The game information of this server
std::string connection_string; ///< Address of the server
bool online = false; ///< False if the server did not respond (default status)
bool manually = false; ///< True if the server was added manually
uint8 retries = 0; ///< Number of retries (to stop requerying)
NetworkGameList *next = nullptr; ///< Next pointer to make a linked game list
};
/** Game list of this client */
extern NetworkGameList *_network_game_list;
void NetworkGameListAddItemDelayed(NetworkGameList *item);
NetworkGameList *NetworkGameListAddItem(NetworkAddress address);
NetworkGameList *NetworkGameListAddItem(const std::string &connection_string);
void NetworkGameListRemoveItem(NetworkGameList *remove);
void NetworkGameListRequery();

View File

@@ -286,8 +286,10 @@ protected:
/** Sort servers by name. */
static bool NGameNameSorter(NetworkGameList * const &a, NetworkGameList * const &b)
{
int r = strnatcmp(a->info.server_name, b->info.server_name, true); // Sort by name (natural sorting).
return r == 0 ? a->address.CompareTo(b->address) < 0: r < 0;
int r = strnatcmp(a->info.server_name.c_str(), b->info.server_name.c_str(), true); // Sort by name (natural sorting).
if (r == 0) r = a->connection_string.compare(b->connection_string);
return r < 0;
}
/**
@@ -337,7 +339,7 @@ protected:
static bool NGameAllowedSorter(NetworkGameList * const &a, NetworkGameList * const &b)
{
/* The servers we do not know anything about (the ones that did not reply) should be at the bottom) */
int r = StrEmpty(a->info.server_revision) - StrEmpty(b->info.server_revision);
int r = a->info.server_revision.empty() - b->info.server_revision.empty();
/* Reverse default as we are interested in version-compatible clients first */
if (r == 0) r = b->info.version_compatible - a->info.version_compatible;
@@ -374,7 +376,7 @@ protected:
assert((*item) != nullptr);
sf.ResetState();
sf.AddLine((*item)->info.server_name);
sf.AddLine((*item)->info.server_name.c_str());
return sf.GetState();
}
@@ -648,7 +650,7 @@ public:
DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, STR_NETWORK_SERVER_LIST_CLIENTS);
y += FONT_HEIGHT_NORMAL;
SetDParam(0, STR_CHEAT_SWITCH_CLIMATE_TEMPERATE_LANDSCAPE + sel->info.map_set);
SetDParam(0, STR_CHEAT_SWITCH_CLIMATE_TEMPERATE_LANDSCAPE + sel->info.landscape);
DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, STR_NETWORK_SERVER_LIST_LANDSCAPE); // landscape
y += FONT_HEIGHT_NORMAL;
@@ -661,8 +663,7 @@ public:
DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, STR_NETWORK_SERVER_LIST_SERVER_VERSION); // server version
y += FONT_HEIGHT_NORMAL;
std::string address = sel->address.GetAddressAsString();
SetDParamStr(0, address.c_str());
SetDParamStr(0, sel->connection_string.c_str());
DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, STR_NETWORK_SERVER_LIST_SERVER_ADDRESS); // server address
y += FONT_HEIGHT_NORMAL;
@@ -766,7 +767,7 @@ public:
break;
case WID_NG_REFRESH: // Refresh
if (this->server != nullptr) NetworkTCPQueryServer(this->server->address);
if (this->server != nullptr) NetworkTCPQueryServer(this->server->connection_string);
break;
case WID_NG_NEWGRF: // NewGRF Settings
@@ -1486,22 +1487,22 @@ struct NetworkLobbyWindow : public Window {
case WID_NL_JOIN: // Join company
/* Button can be clicked only when it is enabled. */
NetworkClientConnectGame(this->server->address, this->company);
NetworkClientConnectGame(this->server->connection_string, this->company);
break;
case WID_NL_NEW: // New company
NetworkClientConnectGame(this->server->address, COMPANY_NEW_COMPANY);
NetworkClientConnectGame(this->server->connection_string, COMPANY_NEW_COMPANY);
break;
case WID_NL_SPECTATE: // Spectate game
NetworkClientConnectGame(this->server->address, COMPANY_SPECTATOR);
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 = {};
NetworkTCPQueryServer(this->server->address, true);
NetworkTCPQueryServer(this->server->connection_string, true);
break;
}
}
@@ -1569,9 +1570,9 @@ static void ShowNetworkLobbyWindow(NetworkGameList *ngl)
DeleteWindowById(WC_NETWORK_WINDOW, WN_NETWORK_WINDOW_START);
DeleteWindowById(WC_NETWORK_WINDOW, WN_NETWORK_WINDOW_GAME);
strecpy(_settings_client.network.last_joined, ngl->address.GetAddressAsString(false).c_str(), lastof(_settings_client.network.last_joined));
strecpy(_settings_client.network.last_joined, ngl->connection_string.c_str(), lastof(_settings_client.network.last_joined));
NetworkTCPQueryServer(ngl->address, true);
NetworkTCPQueryServer(ngl->connection_string, true);
new NetworkLobbyWindow(&_network_lobby_window_desc, ngl);
}

View File

@@ -94,7 +94,7 @@ extern uint8 _network_reconnect;
extern CompanyMask _network_company_passworded;
void NetworkTCPQueryServer(NetworkAddress address, bool request_company_info = false);
void NetworkTCPQueryServer(const std::string &connection_string, bool request_company_info = false);
void GetBindAddresses(NetworkAddressList *addresses, uint16 port);
struct NetworkGameList *NetworkAddServer(const std::string &connection_string);
@@ -126,8 +126,6 @@ StringID GetNetworkErrorMsg(NetworkErrorCode err);
bool NetworkFindName(char *new_name, const char *last);
const char *GenerateCompanyPasswordHash(const char *password, const char *password_server_id, uint32 password_game_seed);
bool NetworkClientConnectGame(NetworkAddress &address, CompanyID join_as, const char *join_server_password = nullptr, const char *join_company_password = nullptr);
NetworkAddress ParseConnectionString(const std::string &connection_string, int default_port);
NetworkAddress ParseGameConnectionString(CompanyID *company, const std::string &connection_string, int default_port);
NetworkAddress ParseConnectionString(const std::string &connection_string, uint16 default_port);
#endif /* NETWORK_INTERNAL_H */

View File

@@ -360,11 +360,8 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::SendClientInfo(NetworkClientIn
/** Send the client information about the server. */
NetworkRecvStatus ServerNetworkGameSocketHandler::SendGameInfo()
{
NetworkGameInfo ngi;
FillNetworkGameInfo(ngi);
Packet *p = new Packet(PACKET_SERVER_GAME_INFO, SHRT_MAX);
SerializeNetworkGameInfo(p, &ngi);
SerializeNetworkGameInfo(p, GetCurrentNetworkServerGameInfo());
this->SendPacket(p);
@@ -373,11 +370,8 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::SendGameInfo()
NetworkRecvStatus ServerNetworkGameSocketHandler::SendGameInfoExtended(PacketGameType reply_type, uint16 flags, uint16 version)
{
NetworkGameInfo ngi;
FillNetworkGameInfo(ngi);
Packet *p = new Packet(reply_type, SHRT_MAX);
SerializeNetworkGameInfoExtended(p, &ngi, flags, version);
SerializeNetworkGameInfoExtended(p, GetCurrentNetworkServerGameInfo(), flags, version);
this->SendPacket(p);
@@ -2166,6 +2160,12 @@ void NetworkServerSendConfigUpdate()
}
}
/** Update the server's NetworkServerGameInfo due to changes in settings. */
void NetworkServerUpdateGameInfo()
{
if (_network_server) FillStaticNetworkServerGameInfo();
}
/**
* Tell that a particular company is (not) passworded.
* @param company_id The company that got/removed the password.

View File

@@ -102,35 +102,34 @@ static Packet PrepareUdpClientFindServerPacket()
/**
* Helper function doing the actual work for querying the server.
* @param address The address of the server.
* @param connection_string The address of the server.
* @param needs_mutex Whether we need to acquire locks when sending the packet or not.
* @param manually Whether the address was entered manually.
*/
static void DoNetworkUDPQueryServer(NetworkAddress &address, bool needs_mutex, bool manually)
static void DoNetworkUDPQueryServer(const std::string &connection_string, bool needs_mutex, bool manually)
{
/* Clear item in gamelist */
NetworkGameList *item = CallocT<NetworkGameList>(1);
address.GetAddressAsString(item->info.server_name, lastof(item->info.server_name));
item->address = address;
item->manually = manually;
NetworkGameList *item = new NetworkGameList(connection_string, manually);
item->info.server_name = connection_string;
NetworkGameListAddItemDelayed(item);
std::unique_lock<std::mutex> lock(_udp_client.mutex, std::defer_lock);
if (needs_mutex) lock.lock();
/* Init the packet */
NetworkAddress address = NetworkAddress(ParseConnectionString(connection_string, NETWORK_DEFAULT_PORT));
Packet p = PrepareUdpClientFindServerPacket();
if (_udp_client.socket != nullptr) _udp_client.socket->SendPacket(&p, &address);
}
/**
* Query a specific server.
* @param address The address of the server.
* @param connection_string The address of the server.
* @param manually Whether the address was entered manually.
*/
void NetworkUDPQueryServer(NetworkAddress address, bool manually)
void NetworkUDPQueryServer(const std::string &connection_string, bool manually)
{
if (address.IsResolved() || !StartNewThread(nullptr, "ottd:udp-query", &DoNetworkUDPQueryServer, std::move(address), true, std::move(manually))) {
DoNetworkUDPQueryServer(address, true, manually);
if (!StartNewThread(nullptr, "ottd:udp-query", &DoNetworkUDPQueryServer, std::move(connection_string), true, std::move(manually))) {
DoNetworkUDPQueryServer(connection_string, true, manually);
}
}
@@ -173,7 +172,7 @@ protected:
void Receive_CLIENT_FIND_SERVER(Packet *p, NetworkAddress *client_addr) override;
void Receive_CLIENT_DETAIL_INFO(Packet *p, NetworkAddress *client_addr) override;
void Receive_CLIENT_GET_NEWGRFS(Packet *p, NetworkAddress *client_addr) override;
void Reply_CLIENT_FIND_SERVER_extended(Packet *p, NetworkAddress *client_addr, NetworkGameInfo *ngi);
void Reply_CLIENT_FIND_SERVER_extended(Packet *p, NetworkAddress *client_addr, const NetworkServerGameInfo *ngi);
public:
/**
* Create the socket.
@@ -190,16 +189,13 @@ void ServerNetworkUDPSocketHandler::Receive_CLIENT_FIND_SERVER(Packet *p, Networ
return;
}
NetworkGameInfo ngi;
FillNetworkGameInfo(ngi);
if (p->CanReadFromPacket(8) && p->Recv_uint32() == FIND_SERVER_EXTENDED_TOKEN) {
this->Reply_CLIENT_FIND_SERVER_extended(p, client_addr, &ngi);
this->Reply_CLIENT_FIND_SERVER_extended(p, client_addr, GetCurrentNetworkServerGameInfo());
return;
}
Packet packet(PACKET_UDP_SERVER_RESPONSE);
SerializeNetworkGameInfo(&packet, &ngi);
SerializeNetworkGameInfo(&packet, GetCurrentNetworkServerGameInfo());
/* Let the client know that we are here */
this->SendPacket(&packet, client_addr);
@@ -207,7 +203,7 @@ void ServerNetworkUDPSocketHandler::Receive_CLIENT_FIND_SERVER(Packet *p, Networ
DEBUG(net, 2, "[udp] queried from %s", client_addr->GetHostname());
}
void ServerNetworkUDPSocketHandler::Reply_CLIENT_FIND_SERVER_extended(Packet *p, NetworkAddress *client_addr, NetworkGameInfo *ngi)
void ServerNetworkUDPSocketHandler::Reply_CLIENT_FIND_SERVER_extended(Packet *p, NetworkAddress *client_addr, const NetworkServerGameInfo *ngi)
{
uint16 flags = p->Recv_uint16();
uint16 version = p->Recv_uint16();
@@ -396,7 +392,7 @@ void ClientNetworkUDPSocketHandler::Receive_SERVER_RESPONSE_Common(Packet *p, Ne
DEBUG(net, 4, "[udp]%s server response from %s", extended ? " extended" : "", NetworkAddressDumper().GetAddressAsString(client_addr));
/* Find next item */
item = NetworkGameListAddItem(*client_addr);
item = NetworkGameListAddItem(client_addr->GetAddressAsString(false));
/* Clear any existing GRFConfig chain. */
ClearGRFConfigList(&item->info.grfconfig);
@@ -434,7 +430,8 @@ void ClientNetworkUDPSocketHandler::Receive_SERVER_RESPONSE_Common(Packet *p, Ne
SerializeGRFIdentifier(&packet, &in_request[i]->ident);
}
this->SendPacket(&packet, &item->address);
NetworkAddress address = NetworkAddress(ParseConnectionString(item->connection_string, NETWORK_DEFAULT_PORT));
this->SendPacket(&packet, &address);
DEBUG(net, 4, "[udp] sent newgrf data request (%u) to %s", in_request_count, NetworkAddressDumper().GetAddressAsString(client_addr));
@@ -453,7 +450,7 @@ void ClientNetworkUDPSocketHandler::Receive_SERVER_RESPONSE_Common(Packet *p, Ne
}
if (client_addr->GetAddress()->ss_family == AF_INET6) {
strecat(item->info.server_name, " (IPv6)", lastof(item->info.server_name));
item->info.server_name.append(" (IPv6)");
}
UpdateNetworkGameWindow();
@@ -489,7 +486,7 @@ void ClientNetworkUDPSocketHandler::Receive_MASTER_RESPONSE_LIST(Packet *p, Netw
/* Somehow we reached the end of the packet */
if (this->HasClientQuit()) return;
DoNetworkUDPQueryServer(addr, false, false);
DoNetworkUDPQueryServer(addr.GetAddressAsString(false), false, false);
}
}
}

View File

@@ -15,7 +15,7 @@
void NetworkUDPInitialize();
void NetworkUDPSearchGame();
void NetworkUDPQueryMasterServer();
void NetworkUDPQueryServer(NetworkAddress address, bool manually = false);
void NetworkUDPQueryServer(const std::string &connection_string, bool manually = false);
void NetworkUDPAdvertise();
void NetworkUDPRemoveAdvertise(bool blocking);
void NetworkUDPClose();