File diff r25823:e260a70c7ccd → r25824:08d7cb74dd5d
src/network/core/game_info.cpp
Show inline comments
 
/*
 
 * This file is part of OpenTTD.
 
 * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
 
 * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 
 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
 
 */
 

	
 
/**
 
 * @file game_info.cpp Functions to convert NetworkGameInfo to Packet and back.
 
 */
 

	
 
#include "../../stdafx.h"
 
#include "game_info.h"
 
#include "../../core/bitmath_func.hpp"
 
#include "../../company_base.h"
 
#include "../../date_func.h"
 
#include "../../debug.h"
 
#include "../../map_func.h"
 
#include "../../game/game.hpp"
 
#include "../../game/game_info.hpp"
 
#include "../../settings_type.h"
 
#include "../../string_func.h"
 
#include "../../rev.h"
 
#include "../network_func.h"
 
#include "../network.h"
 
#include "packet.h"
 

	
 
#include "../../safeguards.h"
 

	
 

	
 
/**
 
 * How many hex digits of the git hash to include in network revision string.
 
 * Determined as 10 hex digits + 2 characters for -g/-u/-m prefix.
 
 */
 
static const uint GITHASH_SUFFIX_LEN = 12;
 

	
 
NetworkServerGameInfo _network_game_info; ///< Information about our game.
 

	
 
/**
 
 * Get the network version string used by this build.
 
 * The returned string is guaranteed to be at most NETWORK_REVISON_LENGTH bytes including '\0' terminator.
 
 */
 
std::string_view GetNetworkRevisionString()
 
{
 
	static std::string network_revision;
 

	
 
	if (network_revision.empty()) {
 
		network_revision = _openttd_revision;
 
		if (_openttd_revision_tagged) {
 
			/* Tagged; do not mangle further, though ensure it's not too long. */
 
			if (network_revision.size() >= NETWORK_REVISION_LENGTH) network_revision.resize(NETWORK_REVISION_LENGTH - 1);
 
		} else {
 
			/* Not tagged; add the githash suffix while ensuring the string does not become too long. */
 
			assert(_openttd_revision_modified < 3);
 
			std::string githash_suffix = fmt::format("-{}{}", "gum"[_openttd_revision_modified], _openttd_revision_hash);
 
			if (githash_suffix.size() > GITHASH_SUFFIX_LEN) githash_suffix.resize(GITHASH_SUFFIX_LEN);
 

	
 
			/* Where did the hash start in the original string? Overwrite from that position, unless that would create a too long string. */
 
			size_t hash_end = network_revision.find_last_of('-');
 
			if (hash_end == std::string::npos) hash_end = network_revision.size();
 
			if (hash_end + githash_suffix.size() >= NETWORK_REVISION_LENGTH) hash_end = NETWORK_REVISION_LENGTH - githash_suffix.size() - 1;
 

	
 
			/* Replace the git hash in revision string. */
 
			network_revision.replace(hash_end, std::string::npos, githash_suffix);
 
		}
 
		assert(network_revision.size() < NETWORK_REVISION_LENGTH); // size does not include terminator, constant does, hence strictly less than
 
		Debug(net, 3, "Network revision name: {}", network_revision);
 
	}
 
@@ -150,161 +152,172 @@ const NetworkServerGameInfo *GetCurrentN
 
	_network_game_info.spectators_on = NetworkSpectatorCount();
 
	_network_game_info.game_date     = _date;
 
	return &_network_game_info;
 
}
 

	
 
/**
 
 * Function that is called for every GRFConfig that is read when receiving
 
 * a NetworkGameInfo. Only grfid and md5sum are set, the rest is zero. This
 
 * function must set all appropriate fields. This GRF is later appended to
 
 * the grfconfig list of the NetworkGameInfo.
 
 * @param config the GRF to handle.
 
 */
 
static void HandleIncomingNetworkGameInfoGRFConfig(GRFConfig *config)
 
{
 
	/* Find the matching GRF file */
 
	const GRFConfig *f = FindGRFConfig(config->ident.grfid, FGCM_EXACT, config->ident.md5sum);
 
	if (f == nullptr) {
 
		/* Don't know the GRF, so mark game incompatible and the (possibly)
 
		 * already resolved name for this GRF (another server has sent the
 
		 * name of the GRF already */
 
		config->name = FindUnknownGRFName(config->ident.grfid, config->ident.md5sum, true);
 
		config->status = GCS_NOT_FOUND;
 
	} else {
 
		config->filename = f->filename;
 
		config->name = f->name;
 
		config->info = f->info;
 
		config->url = f->url;
 
	}
 
	SetBit(config->flags, GCF_COPY);
 
}
 

	
 
/**
 
 * Serializes the NetworkGameInfo struct to the packet.
 
 * @param p    the packet to write the data to.
 
 * @param info the NetworkGameInfo struct to serialize from.
 
 */
 
void SerializeNetworkGameInfo(Packet *p, const NetworkServerGameInfo *info)
 
{
 
	p->Send_uint8 (NETWORK_GAME_INFO_VERSION);
 

	
 
	/*
 
	 *              Please observe the order.
 
	 * The parts must be read in the same order as they are sent!
 
	 */
 

	
 
	/* Update the documentation in game_info.h on changes
 
	 * to the NetworkGameInfo wire-protocol! */
 

	
 
	/* NETWORK_GAME_INFO_VERSION = 5 */
 
	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());
 

	
 
	/* NETWORK_GAME_INFO_VERSION = 4 */
 
	{
 
		/* Only send the GRF Identification (GRF_ID and MD5 checksum) of
 
		 * the GRFs that are needed, i.e. the ones that the server has
 
		 * selected in the NewGRF GUI and not the ones that are used due
 
		 * to the fact that they are in [newgrf-static] in openttd.cfg */
 
		const GRFConfig *c;
 
		uint count = 0;
 

	
 
		/* Count number of GRFs to send information about */
 
		for (c = info->grfconfig; c != nullptr; c = c->next) {
 
			if (!HasBit(c->flags, GCF_STATIC)) count++;
 
		}
 
		p->Send_uint8 (count); // Send number of GRFs
 

	
 
		/* Send actual GRF Identifications */
 
		for (c = info->grfconfig; c != nullptr; c = c->next) {
 
			if (!HasBit(c->flags, GCF_STATIC)) SerializeGRFIdentifier(p, &c->ident);
 
		}
 
	}
 

	
 
	/* NETWORK_GAME_INFO_VERSION = 3 */
 
	p->Send_uint32(info->game_date);
 
	p->Send_uint32(info->start_date);
 

	
 
	/* NETWORK_GAME_INFO_VERSION = 2 */
 
	p->Send_uint8 (info->companies_max);
 
	p->Send_uint8 (info->companies_on);
 
	p->Send_uint8 (info->spectators_max);
 

	
 
	/* NETWORK_GAME_INFO_VERSION = 1 */
 
	p->Send_string(info->server_name);
 
	p->Send_string(info->server_revision);
 
	p->Send_uint8 (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);
 
	p->Send_bool  (info->dedicated);
 
}
 

	
 
/**
 
 * Deserializes the NetworkGameInfo struct from the packet.
 
 * @param p    the packet to read the data from.
 
 * @param info the NetworkGameInfo to deserialize into.
 
 */
 
void DeserializeNetworkGameInfo(Packet *p, NetworkGameInfo *info)
 
{
 
	static const Date MAX_DATE = ConvertYMDToDate(MAX_YEAR, 11, 31); // December is month 11
 

	
 
	byte game_info_version = p->Recv_uint8();
 

	
 
	/*
 
	 *              Please observe the order.
 
	 * The parts must be read in the same order as they are sent!
 
	 */
 

	
 
	/* Update the documentation in game_info.h on changes
 
	 * to the NetworkGameInfo wire-protocol! */
 

	
 
	switch (game_info_version) {
 
		case 5: {
 
			info->gamescript_version = (int)p->Recv_uint32();
 
			info->gamescript_name = p->Recv_string(NETWORK_NAME_LENGTH);
 
			FALLTHROUGH;
 
		}
 

	
 
		case 4: {
 
			GRFConfig **dst = &info->grfconfig;
 
			uint i;
 
			uint num_grfs = p->Recv_uint8();
 

	
 
			/* Broken/bad data. It cannot have that many NewGRFs. */
 
			if (num_grfs > NETWORK_MAX_GRF_COUNT) return;
 

	
 
			for (i = 0; i < num_grfs; i++) {
 
				GRFConfig *c = new GRFConfig();
 
				DeserializeGRFIdentifier(p, &c->ident);
 
				HandleIncomingNetworkGameInfoGRFConfig(c);
 

	
 
				/* Append GRFConfig to the list */
 
				*dst = c;
 
				dst = &c->next;
 
			}
 
			FALLTHROUGH;
 
		}
 

	
 
		case 3:
 
			info->game_date      = Clamp(p->Recv_uint32(), 0, MAX_DATE);
 
			info->start_date     = Clamp(p->Recv_uint32(), 0, MAX_DATE);
 
			FALLTHROUGH;
 

	
 
		case 2:
 
			info->companies_max  = p->Recv_uint8 ();
 
			info->companies_on   = p->Recv_uint8 ();
 
			info->spectators_max = p->Recv_uint8 ();
 
			FALLTHROUGH;
 

	
 
		case 1:
 
			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 (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->landscape      = p->Recv_uint8 ();
 
			info->dedicated      = p->Recv_bool  ();