Merge remote-tracking branch 'upstream/master'

This commit is contained in:
dP
2025-06-14 17:01:17 +05:00
1132 changed files with 59430 additions and 52889 deletions

View File

@@ -37,6 +37,8 @@
#include <mutex>
#include <condition_variable>
#include "table/strings.h"
#include "../citymania/cm_client_list_gui.hpp"
#include "../safeguards.h"
@@ -44,14 +46,12 @@
/* This file handles all the server-commands */
DECLARE_POSTFIX_INCREMENT(ClientID)
DECLARE_INCREMENT_DECREMENT_OPERATORS(ClientID)
/** The identifier counter for new clients (is never decreased) */
static ClientID _network_client_id = CLIENT_ID_FIRST;
/** Make very sure the preconditions given in network_type.h are actually followed */
static_assert(MAX_CLIENT_SLOTS > MAX_CLIENTS);
/** Yes... */
static_assert(NetworkClientSocketPool::MAX_SIZE == MAX_CLIENT_SLOTS);
static_assert(NetworkClientSocketPool::MAX_SIZE > MAX_CLIENTS);
/** The pool with clients. */
NetworkClientSocketPool _networkclientsocket_pool("NetworkClientSocket");
@@ -87,7 +87,7 @@ struct PacketWriter : SaveFilter {
{
std::unique_lock<std::mutex> lock(this->mutex);
if (this->cs != nullptr) this->exit_sig.wait(lock);
while (this->cs != nullptr) this->exit_sig.wait(lock);
/* This must all wait until the Destroy function is called. */
@@ -192,7 +192,6 @@ struct PacketWriter : SaveFilter {
*/
ServerNetworkGameSocketHandler::ServerNetworkGameSocketHandler(SOCKET s) : NetworkGameSocketHandler(s)
{
this->status = STATUS_INACTIVE;
this->client_id = _network_client_id++;
this->receive_limit = _settings_client.network.bytes_per_frame_burst;
@@ -261,7 +260,7 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::CloseConnection(NetworkRecvSta
}
}
/* If we were transfering a map to this client, stop the savegame creation
/* If we were transferring a map to this client, stop the savegame creation
* process and queue the next client to receive the map. */
if (this->status == STATUS_MAP) {
/* Ensure the saving of the game is stopped too. */
@@ -414,22 +413,18 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::SendNewGRFCheck()
Debug(net, 9, "client[{}] status = NEWGRFS_CHECK", this->client_id);
this->status = STATUS_NEWGRFS_CHECK;
if (_grfconfig == nullptr) {
if (_grfconfig.empty()) {
/* There are no NewGRFs, so they're welcome. */
return this->SendWelcome();
}
auto p = std::make_unique<Packet>(this, PACKET_SERVER_CHECK_NEWGRFS, TCP_MTU);
const GRFConfig *c;
uint grf_count = 0;
for (c = _grfconfig; c != nullptr; c = c->next) {
if (!HasBit(c->flags, GCF_STATIC)) grf_count++;
}
uint grf_count = std::ranges::count_if(_grfconfig, [](const auto &c){ return !c->flags.Test(GRFConfigFlag::Static); });
p->Send_uint8 (grf_count);
for (c = _grfconfig; c != nullptr; c = c->next) {
if (!HasBit(c->flags, GCF_STATIC)) SerializeGRFIdentifier(*p, c->ident);
for (const auto &c : _grfconfig) {
if (!c->flags.Test(GRFConfigFlag::Static)) SerializeGRFIdentifier(*p, c->ident);
}
this->SendPacket(std::move(p));
@@ -706,7 +701,7 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::SendChat(NetworkAction action,
* Send a chat message from external source.
* @param source Name of the source this message came from.
* @param colour TextColour to use for the message.
* @param user Name of the user who sent the messsage.
* @param user Name of the user who sent the message.
* @param msg The actual message.
*/
NetworkRecvStatus ServerNetworkGameSocketHandler::SendExternalChat(const std::string &source, TextColour colour, const std::string &user, const std::string &msg)
@@ -897,13 +892,13 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::Receive_CLIENT_IDENTIFY(Packet
Debug(sl, 2, "Using saveload preset '%s:%d' for client %d (mask %d)", this->cm_preset.format->name, (int)this->cm_preset.compression_level, (int)this->client_id, (int)savegame_formats);
/* join another company does not affect these values */
switch (playas) {
case COMPANY_NEW_COMPANY: // New company
switch (playas.base()) {
case COMPANY_NEW_COMPANY.base(): // New company
if (Company::GetNumItems() >= _settings_client.network.max_companies) {
return this->SendError(NETWORK_ERROR_FULL);
}
break;
case COMPANY_SPECTATOR: // Spectator
case COMPANY_SPECTATOR.base(): // Spectator
break;
default: // Join another company (companies 1..MAX_COMPANIES (index 0..(MAX_COMPANIES-1)))
if (!Company::IsValidHumanID(playas)) {
@@ -933,10 +928,10 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::Receive_CLIENT_IDENTIFY(Packet
NetworkClientInfo *ci = new NetworkClientInfo(this->client_id);
this->SetInfo(ci);
ci->join_date = TimerGameEconomy::date;
ci->client_name = client_name;
ci->client_name = std::move(client_name);
ci->client_playas = playas;
ci->public_key = this->peer_public_key;
Debug(desync, 1, "client: {:08x}; {:02x}; {:02x}; {:02x}", TimerGameEconomy::date, TimerGameEconomy::date_fract, (int)ci->client_playas, (int)ci->index);
Debug(desync, 1, "client: {:08x}; {:02x}; {:02x}; {:02x}", TimerGameEconomy::date, TimerGameEconomy::date_fract, ci->client_playas, ci->index);
/* Make sure companies to which people try to join are not autocleaned */
Company *c = Company::GetIfValid(playas);
@@ -948,9 +943,9 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::Receive_CLIENT_IDENTIFY(Packet
static NetworkErrorCode GetErrorForAuthenticationMethod(NetworkAuthenticationMethod method)
{
switch (method) {
case NETWORK_AUTH_METHOD_X25519_PAKE:
case NetworkAuthenticationMethod::X25519_PAKE:
return NETWORK_ERROR_WRONG_PASSWORD;
case NETWORK_AUTH_METHOD_X25519_AUTHORIZED_KEY:
case NetworkAuthenticationMethod::X25519_AuthorizedKey:
return NETWORK_ERROR_NOT_ON_ALLOW_LIST;
default:
@@ -968,13 +963,13 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::Receive_CLIENT_AUTH_RESPONSE(P
auto authentication_method = this->authentication_handler->GetAuthenticationMethod();
switch (this->authentication_handler->ReceiveResponse(p)) {
case NetworkAuthenticationServerHandler::AUTHENTICATED:
case NetworkAuthenticationServerHandler::ResponseResult::Authenticated:
break;
case NetworkAuthenticationServerHandler::RETRY_NEXT_METHOD:
case NetworkAuthenticationServerHandler::ResponseResult::RetryNextMethod:
return this->SendAuthRequest();
case NetworkAuthenticationServerHandler::NOT_AUTHENTICATED:
case NetworkAuthenticationServerHandler::ResponseResult::NotAuthenticated:
default:
return this->SendError(GetErrorForAuthenticationMethod(authentication_method));
}
@@ -1095,12 +1090,12 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::Receive_CLIENT_COMMAND(Packet
}
if ((GetCommandFlags(cp.cmd) & CMD_SERVER) && ci->client_id != CLIENT_ID_SERVER) {
if (GetCommandFlags(cp.cmd).Test(CommandFlag::Server) && ci->client_id != CLIENT_ID_SERVER) {
IConsolePrint(CC_WARNING, "Kicking client #{} (IP: {}) due to calling a server only command {}.", ci->client_id, this->GetClientIP(), cp.cmd);
return this->SendError(NETWORK_ERROR_KICKED);
}
if ((GetCommandFlags(cp.cmd) & CMD_SPECTATOR) == 0 && !Company::IsValidID(cp.company) && ci->client_id != CLIENT_ID_SERVER) {
if (!GetCommandFlags(cp.cmd).Test(CommandFlag::Spectator) && !Company::IsValidID(cp.company) && ci->client_id != CLIENT_ID_SERVER) {
IConsolePrint(CC_WARNING, "Kicking client #{} (IP: {}) due to calling a non-spectator command {}.", ci->client_id, this->GetClientIP(), cp.cmd);
return this->SendError(NETWORK_ERROR_KICKED);
}
@@ -1147,9 +1142,9 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::Receive_CLIENT_COMMAND(Packet
if (!found) return NETWORK_RECV_STATUS_OKAY;
}
if (GetCommandFlags(cp.cmd) & CMD_CLIENT_ID) NetworkReplaceCommandClientId(cp, this->client_id);
if (GetCommandFlags(cp.cmd).Test(CommandFlag::ClientID)) NetworkReplaceCommandClientId(cp, this->client_id);
this->incoming_queue.push_back(cp);
this->incoming_queue.push_back(std::move(cp));
return NETWORK_RECV_STATUS_OKAY;
}
@@ -1346,8 +1341,7 @@ void NetworkServerSendChat(NetworkAction action, DestType desttype, int dest, co
if (ci != nullptr && show_local) {
if (from_id == CLIENT_ID_SERVER) {
StringID str = Company::IsValidID(ci_to->client_playas) ? STR_COMPANY_NAME : STR_NETWORK_SPECTATORS;
SetDParam(0, ci_to->client_playas);
std::string name = GetString(str);
std::string name = GetString(str, ci_to->client_playas);
NetworkTextMessage(action, GetDrawStringCompanyColour(ci_own->client_playas), true, name, msg, data);
} else {
for (NetworkClientSocket *cs : NetworkClientSocket::Iterate()) {
@@ -1372,7 +1366,7 @@ void NetworkServerSendChat(NetworkAction action, DestType desttype, int dest, co
ci = NetworkClientInfo::GetByClientID(from_id);
if (ci != nullptr) {
NetworkTextMessage(action, GetDrawStringCompanyColour(ci->client_playas), false, ci->client_name, msg, data, "");
NetworkTextMessage(action, GetDrawStringCompanyColour(ci->client_playas), false, ci->client_name, msg, data);
}
break;
}
@@ -1382,7 +1376,7 @@ void NetworkServerSendChat(NetworkAction action, DestType desttype, int dest, co
* Send a chat message from external source.
* @param source Name of the source this message came from.
* @param colour TextColour to use for the message.
* @param user Name of the user who sent the messsage.
* @param user Name of the user who sent the message.
* @param msg The actual message.
*/
void NetworkServerSendExternalChat(const std::string &source, TextColour colour, const std::string &user, const std::string &msg)
@@ -1390,7 +1384,7 @@ void NetworkServerSendExternalChat(const std::string &source, TextColour colour,
for (NetworkClientSocket *cs : NetworkClientSocket::Iterate()) {
if (cs->status >= ServerNetworkGameSocketHandler::STATUS_AUTHORIZED) cs->SendExternalChat(source, colour, user, msg);
}
NetworkTextMessage(NETWORK_ACTION_EXTERNAL_CHAT, colour, false, user, msg, 0, source);
NetworkTextMessage(NETWORK_ACTION_EXTERNAL_CHAT, colour, false, user, msg, source);
}
NetworkRecvStatus ServerNetworkGameSocketHandler::Receive_CLIENT_CHAT(Packet &p)
@@ -1450,7 +1444,7 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::Receive_CLIENT_SET_NAME(Packet
/* Display change */
if (NetworkMakeClientNameUnique(client_name)) {
NetworkTextMessage(NETWORK_ACTION_NAME_CHANGE, CC_DEFAULT, false, ci->client_name, client_name);
ci->client_name = client_name;
ci->client_name = std::move(client_name);
NetworkUpdateClientInfo(ci->client_id);
}
}
@@ -1505,12 +1499,11 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::Receive_CLIENT_MOVE(Packet &p)
}
/**
* Populate the company stats.
* @param stats the stats to update
* Get the company stats.
*/
void NetworkPopulateCompanyStats(NetworkCompanyStats *stats)
NetworkCompanyStatsArray NetworkGetCompanyStats()
{
memset(stats, 0, sizeof(*stats) * MAX_COMPANIES);
NetworkCompanyStatsArray stats = {};
/* Go through all vehicles and count the type of vehicles */
for (const Vehicle *v : Vehicle::Iterate()) {
@@ -1531,13 +1524,15 @@ void NetworkPopulateCompanyStats(NetworkCompanyStats *stats)
if (Company::IsValidID(s->owner)) {
NetworkCompanyStats *npi = &stats[s->owner];
if (s->facilities & FACIL_TRAIN) npi->num_station[NETWORK_VEH_TRAIN]++;
if (s->facilities & FACIL_TRUCK_STOP) npi->num_station[NETWORK_VEH_LORRY]++;
if (s->facilities & FACIL_BUS_STOP) npi->num_station[NETWORK_VEH_BUS]++;
if (s->facilities & FACIL_AIRPORT) npi->num_station[NETWORK_VEH_PLANE]++;
if (s->facilities & FACIL_DOCK) npi->num_station[NETWORK_VEH_SHIP]++;
if (s->facilities.Test(StationFacility::Train)) npi->num_station[NETWORK_VEH_TRAIN]++;
if (s->facilities.Test(StationFacility::TruckStop)) npi->num_station[NETWORK_VEH_LORRY]++;
if (s->facilities.Test(StationFacility::BusStop)) npi->num_station[NETWORK_VEH_BUS]++;
if (s->facilities.Test(StationFacility::Airport)) npi->num_station[NETWORK_VEH_PLANE]++;
if (s->facilities.Test(StationFacility::Dock)) npi->num_station[NETWORK_VEH_SHIP]++;
}
}
return stats;
}
/**
@@ -1550,7 +1545,7 @@ void NetworkUpdateClientInfo(ClientID client_id)
if (ci == nullptr) return;
Debug(desync, 1, "client: {:08x}; {:02x}; {:02x}; {:04x}", TimerGameEconomy::date, TimerGameEconomy::date_fract, (int)ci->client_playas, client_id);
Debug(desync, 1, "client: {:08x}; {:02x}; {:02x}; {:04x}", TimerGameEconomy::date, TimerGameEconomy::date_fract, ci->client_playas, client_id);
for (NetworkClientSocket *cs : NetworkClientSocket::Iterate()) {
if (cs->status >= ServerNetworkGameSocketHandler::STATUS_AUTHORIZED) {
@@ -1568,25 +1563,25 @@ void NetworkUpdateClientInfo(ClientID client_id)
*/
static void NetworkAutoCleanCompanies()
{
CompanyMask has_clients = 0;
CompanyMask has_vehicles = 0;
CompanyMask has_clients{};
CompanyMask has_vehicles{};
if (!_settings_client.network.autoclean_companies) return;
/* Detect the active companies */
for (const NetworkClientInfo *ci : NetworkClientInfo::Iterate()) {
if (Company::IsValidID(ci->client_playas)) SetBit(has_clients, ci->client_playas);
if (Company::IsValidID(ci->client_playas)) has_clients.Set(ci->client_playas);
}
if (!_network_dedicated) {
const NetworkClientInfo *ci = NetworkClientInfo::GetByClientID(CLIENT_ID_SERVER);
assert(ci != nullptr);
if (Company::IsValidID(ci->client_playas)) SetBit(has_clients, ci->client_playas);
if (Company::IsValidID(ci->client_playas)) has_clients.Set(ci->client_playas);
}
if (_settings_client.network.autoclean_novehicles != 0) {
for (const Company *c : Company::Iterate()) {
if (std::any_of(std::begin(c->group_all), std::end(c->group_all), [](const GroupStatistics &gs) { return gs.num_vehicle != 0; })) SetBit(has_vehicles, c->index);
if (std::any_of(std::begin(c->group_all), std::end(c->group_all), [](const GroupStatistics &gs) { return gs.num_vehicle != 0; })) has_vehicles.Set(c->index);
}
}
@@ -1595,7 +1590,7 @@ static void NetworkAutoCleanCompanies()
/* Skip the non-active once */
if (c->is_ai) continue;
if (!HasBit(has_clients, c->index)) {
if (!has_clients.Test(c->index)) {
/* The company is empty for one month more */
if (c->months_empty != std::numeric_limits<decltype(c->months_empty)>::max()) c->months_empty++;
@@ -1606,7 +1601,7 @@ static void NetworkAutoCleanCompanies()
IConsolePrint(CC_INFO, "Auto-cleaned company #{}.", c->index + 1);
}
/* Is the company empty for autoclean_novehicles-months, and has no vehicles? */
if (_settings_client.network.autoclean_novehicles != 0 && c->months_empty > _settings_client.network.autoclean_novehicles && !HasBit(has_vehicles, c->index)) {
if (_settings_client.network.autoclean_novehicles != 0 && c->months_empty > _settings_client.network.autoclean_novehicles && !has_vehicles.Test(c->index)) {
/* Shut the company down */
Command<CMD_COMPANY_CTRL>::Post(CCA_DELETE, c->index, CRR_AUTOCLEAN, INVALID_CLIENT_ID);
IConsolePrint(CC_INFO, "Auto-cleaned company #{} with no vehicles.", c->index + 1);
@@ -1893,7 +1888,7 @@ static IntervalTimer<TimerGameEconomy> _economy_network_yearly({TimerGameEconomy
{
if (!_network_server) return;
NetworkAdminUpdate(ADMIN_FREQUENCY_ANUALLY);
NetworkAdminUpdate(AdminUpdateFrequency::Annually);
});
/** Quarterly "callback". Called whenever the economy quarter changes. */
@@ -1902,7 +1897,7 @@ static IntervalTimer<TimerGameEconomy> _network_quarterly({TimerGameEconomy::QUA
if (!_network_server) return;
NetworkAutoCleanCompanies();
NetworkAdminUpdate(ADMIN_FREQUENCY_QUARTERLY);
NetworkAdminUpdate(AdminUpdateFrequency::Quarterly);
});
/** Economy monthly "callback". Called whenever the economy month changes. */
@@ -1911,7 +1906,7 @@ static IntervalTimer<TimerGameEconomy> _network_monthly({TimerGameEconomy::MONTH
if (!_network_server) return;
NetworkAutoCleanCompanies();
NetworkAdminUpdate(ADMIN_FREQUENCY_MONTHLY);
NetworkAdminUpdate(AdminUpdateFrequency::Monthly);
});
/** Economy weekly "callback". Called whenever the economy week changes. */
@@ -1919,7 +1914,7 @@ static IntervalTimer<TimerGameEconomy> _network_weekly({TimerGameEconomy::WEEK,
{
if (!_network_server) return;
NetworkAdminUpdate(ADMIN_FREQUENCY_WEEKLY);
NetworkAdminUpdate(AdminUpdateFrequency::Weekly);
});
/** Daily "callback". Called whenever the economy date changes. */
@@ -1927,7 +1922,7 @@ static IntervalTimer<TimerGameEconomy> _economy_network_daily({TimerGameEconomy:
{
if (!_network_server) return;
NetworkAdminUpdate(ADMIN_FREQUENCY_DAILY);
NetworkAdminUpdate(AdminUpdateFrequency::Daily);
});
/**
@@ -1945,7 +1940,7 @@ void NetworkServerShowStatusToConsole()
static const char * const stat_str[] = {
"inactive",
"authorizing",
"identifing client",
"identifying client",
"checking NewGRFs",
"authorized",
"waiting",
@@ -2014,11 +2009,17 @@ void NetworkServerDoMove(ClientID client_id, CompanyID company_id)
cs->SendMove(client_id, company_id);
}
/* announce the client's move */
/* Announce the client's move. */
NetworkUpdateClientInfo(client_id);
NetworkAction action = (company_id == COMPANY_SPECTATOR) ? NETWORK_ACTION_COMPANY_SPECTATOR : NETWORK_ACTION_COMPANY_JOIN;
NetworkServerSendChat(action, DESTTYPE_BROADCAST, 0, "", client_id, company_id + 1);
if (company_id == COMPANY_SPECTATOR) {
/* The client has joined spectators. */
NetworkServerSendChat(NETWORK_ACTION_COMPANY_SPECTATOR, DESTTYPE_BROADCAST, 0, "", client_id);
} else {
/* The client has joined another company. */
std::string company_name = GetString(STR_COMPANY_NAME, company_id);
NetworkServerSendChat(NETWORK_ACTION_COMPANY_JOIN, DESTTYPE_BROADCAST, 0, company_name, client_id);
}
citymania::SetClientListDirty();
InvalidateWindowData(WC_CLIENT_LIST, 0);