Allow 256 NewGRFs in multiplayer

Add extended network format for server info
Add general UDP packet fragmentation system
Fix map dimensions >= 64k
Increase length of server revision string
Maintain backwards compatibility with trunk for advertisement/server listing
pull/59/head
Jonathan G Rennison 6 years ago
parent 9b42ae954c
commit 6342099c4d

@ -44,6 +44,7 @@ static const uint NETWORK_COMPANY_NAME_LENGTH = 128; ///< The maxim
static const uint NETWORK_HOSTNAME_LENGTH = 80; ///< The maximum length of the host name, in bytes including '\0'
static const uint NETWORK_SERVER_ID_LENGTH = 33; ///< The maximum length of the network id of the servers, in bytes including '\0'
static const uint NETWORK_REVISION_LENGTH = 15; ///< The maximum length of the revision, in bytes including '\0'
static const uint NETWORK_LONG_REVISION_LENGTH = 64; ///< The maximum length of the revision, in bytes including '\0'
static const uint NETWORK_PASSWORD_LENGTH = 33; ///< The maximum length of the password, in bytes including '\0' (must be >= NETWORK_SERVER_ID_LENGTH)
static const uint NETWORK_CLIENTS_LENGTH = 200; ///< The maximum length for the list of clients that controls a company, in bytes including '\0'
static const uint NETWORK_CLIENT_NAME_LENGTH = 25; ///< The maximum length of a client's name, in bytes including '\0'

@ -37,11 +37,12 @@ struct NetworkGameInfo : NetworkServerGameInfo {
GRFConfig *grfconfig; ///< List of NewGRF files used
Date start_date; ///< When the game started
Date game_date; ///< Current date
uint16 map_width; ///< Map width
uint16 map_height; ///< Map height
uint32 map_width; ///< Map width
uint32 map_height; ///< Map height
char server_name[NETWORK_NAME_LENGTH]; ///< Server name
char hostname[NETWORK_HOSTNAME_LENGTH]; ///< Hostname of the server (if any)
char server_revision[NETWORK_REVISION_LENGTH]; ///< The version number the server is using (e.g.: 'r304' or 0.5.0)
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

@ -42,14 +42,8 @@ Packet::Packet(NetworkSocketHandler *cs)
*/
Packet::Packet(PacketType type)
{
this->cs = NULL;
this->next = NULL;
/* Skip the size so we can write that in before sending the packet */
this->pos = 0;
this->size = sizeof(PacketSize);
this->buffer = MallocT<byte>(SHRT_MAX);
this->buffer[this->size++] = type;
this->buffer = MallocT<byte>(SHRT_MAX);
this->ResetState(type);
}
/**
@ -60,6 +54,17 @@ Packet::~Packet()
free(this->buffer);
}
void Packet::ResetState(PacketType type)
{
this->cs = NULL;
this->next = NULL;
/* Skip the size so we can write that in before sending the packet */
this->pos = 0;
this->size = sizeof(PacketSize);
this->buffer[this->size++] = type;
}
/**
* Writes the packet size from the raw packet from packet->size
*/
@ -181,16 +186,17 @@ void Packet::Send_binary(const char *data, const size_t size)
/**
* Is it safe to read from the packet, i.e. didn't we run over the buffer ?
* @param bytes_to_read The amount of bytes we want to try to read.
* @param non_fatal True if a false return value is considered non-fatal, and will not raise an error.
* @return True if that is safe, otherwise false.
*/
bool Packet::CanReadFromPacket(uint bytes_to_read)
bool Packet::CanReadFromPacket(uint bytes_to_read, bool non_fatal)
{
/* Don't allow reading from a quit client/client who send bad data */
if (this->cs->HasClientQuit()) return false;
/* Check if variable is within packet-size */
if (this->pos + bytes_to_read > this->size) {
this->cs->NetworkSocketHandler::CloseConnection();
if (!non_fatal) this->cs->NetworkSocketHandler::CloseConnection();
return false;
}

@ -64,6 +64,8 @@ public:
Packet(PacketType type);
~Packet();
void ResetState(PacketType type);
/* Sending/writing of packets */
void PrepareToSend();
@ -79,7 +81,7 @@ public:
void ReadRawPacketSize();
void PrepareToRead();
bool CanReadFromPacket (uint bytes_to_read);
bool CanReadFromPacket (uint bytes_to_read, bool non_fatal = false);
bool Recv_bool ();
uint8 Recv_uint8 ();
uint16 Recv_uint16();

@ -184,11 +184,6 @@ Packet *NetworkTCPSocketHandler::ReceivePacket()
/* Read the packet size from the received packet */
p->ReadRawPacketSize();
if (p->size > SEND_MTU) {
this->CloseConnection();
return NULL;
}
}
/* Read rest of packet */

@ -16,10 +16,14 @@
#include "../../stdafx.h"
#include "../../date_func.h"
#include "../../debug.h"
#include "../../core/random_func.hpp"
#include "../../fios.h"
#include "udp.h"
#include "../../safeguards.h"
extern const uint8 _out_of_band_grf_md5[16];
/**
* Create an UDP socket but don't listen yet.
* @param bind the addresses to bind to.
@ -37,6 +41,8 @@ NetworkUDPSocketHandler::NetworkUDPSocketHandler(NetworkAddressList *bind)
*this->bind.Append() = NetworkAddress(NULL, 0, AF_INET);
*this->bind.Append() = NetworkAddress(NULL, 0, AF_INET6);
}
this->fragment_token = ((uint64) InteractiveRandom()) | (((uint64) InteractiveRandom()) << 32);
}
@ -84,6 +90,33 @@ void NetworkUDPSocketHandler::SendPacket(Packet *p, NetworkAddress *recv, bool a
{
if (this->sockets.Length() == 0) this->Listen();
if (p->size > SEND_MTU) {
p->PrepareToSend();
uint64 token = this->fragment_token++;
const uint PAYLOAD_MTU = SEND_MTU - (1 + 2 + 8 + 1 + 1 + 2);
const uint8 frag_count = (p->size + PAYLOAD_MTU - 1) / PAYLOAD_MTU;
Packet frag(PACKET_UDP_EX_MULTI);
uint8 current_frag = 0;
uint16 offset = 0;
while (offset < p->size) {
uint16 payload_size = min<uint>(PAYLOAD_MTU, p->size - offset);
frag.Send_uint64(token);
frag.Send_uint8 (current_frag);
frag.Send_uint8 (frag_count);
frag.Send_uint16 (payload_size);
frag.Send_binary((const char *) p->buffer + offset, payload_size);
current_frag++;
offset += payload_size;
this->SendPacket(&frag, recv, all, broadcast);
frag.ResetState(PACKET_UDP_EX_MULTI);
}
assert_msg(current_frag == frag_count, "%u, %u", current_frag, frag_count);
return;
}
for (SocketList::iterator s = this->sockets.Begin(); s != this->sockets.End(); s++) {
/* Make a local copy because if we resolve it we cannot
* easily unresolve it so we can resolve it later again. */
@ -142,7 +175,7 @@ void NetworkUDPSocketHandler::ReceivePackets()
/* If the size does not match the packet must be corrupted.
* Otherwise it will be marked as corrupted later on. */
if (nbytes != p.size) {
DEBUG(net, 1, "received a packet with mismatching size from %s", address.GetAddressAsString());
DEBUG(net, 1, "received a packet with mismatching size from %s, (%u, %u)", address.GetAddressAsString(), nbytes, p.size);
continue;
}
@ -183,11 +216,24 @@ void NetworkUDPSocketHandler::SendNetworkGameInfo(Packet *p, const NetworkGameIn
for (c = info->grfconfig; c != NULL; c = c->next) {
if (!HasBit(c->flags, GCF_STATIC)) count++;
}
p->Send_uint8 (count); // Send number of GRFs
p->Send_uint8(min<uint>(count, NETWORK_MAX_GRF_COUNT)); // Send number of GRFs
/* Send actual GRF Identifications */
uint index = 0;
for (c = info->grfconfig; c != NULL; c = c->next) {
if (!HasBit(c->flags, GCF_STATIC)) this->SendGRFIdentifier(p, &c->ident);
if (!HasBit(c->flags, GCF_STATIC)) {
if (index == NETWORK_MAX_GRF_COUNT - 1 && count > NETWORK_MAX_GRF_COUNT) {
/* Send fake GRF ID */
p->Send_uint32(0x56D2B000);
p->Send_binary((const char*) _out_of_band_grf_md5, 16);
} else if (index >= NETWORK_MAX_GRF_COUNT) {
break;
} else {
this->SendGRFIdentifier(p, &c->ident);
}
index++;
}
}
}
@ -202,7 +248,7 @@ void NetworkUDPSocketHandler::SendNetworkGameInfo(Packet *p, const NetworkGameIn
/* NETWORK_GAME_INFO_VERSION = 1 */
p->Send_string(info->server_name);
p->Send_string(info->server_revision);
p->Send_string(info->short_server_revision);
p->Send_uint8 (info->server_lang);
p->Send_bool (info->use_password);
p->Send_uint8 (info->clients_max);
@ -215,6 +261,56 @@ void NetworkUDPSocketHandler::SendNetworkGameInfo(Packet *p, const NetworkGameIn
p->Send_bool (info->dedicated);
}
/**
* Serializes the NetworkGameInfo struct to the packet
* @param p the packet to write the data to
* @param info the NetworkGameInfo struct to serialize
*/
void NetworkUDPSocketHandler::SendNetworkGameInfoExtended(Packet *p, const NetworkGameInfo *info, uint16 version)
{
p->Send_uint8(0); // 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_string(info->server_name);
p->Send_string(info->server_revision);
p->Send_uint8 (info->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(info->map_name);
p->Send_uint32(info->map_width);
p->Send_uint32(info->map_height);
p->Send_uint8 (info->map_set);
p->Send_bool (info->dedicated);
{
/* 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 != NULL; c = c->next) {
if (!HasBit(c->flags, GCF_STATIC)) count++;
}
p->Send_uint32(count); // Send number of GRFs
/* Send actual GRF Identifications */
for (c = info->grfconfig; c != NULL; c = c->next) {
if (!HasBit(c->flags, GCF_STATIC)) {
this->SendGRFIdentifier(p, &c->ident);
}
}
}
}
/**
* Deserializes the NetworkGameInfo struct from the packet
* @param p the packet to read the data from
@ -288,6 +384,60 @@ void NetworkUDPSocketHandler::ReceiveNetworkGameInfo(Packet *p, NetworkGameInfo
if (info->map_set >= NETWORK_NUM_LANDSCAPES) info->map_set = 0;
}
}
/**
* Deserializes the NetworkGameInfo struct from the packet
* @param p the packet to read the data from
* @param info the NetworkGameInfo to deserialize into
*/
void NetworkUDPSocketHandler::ReceiveNetworkGameInfoExtended(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
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_lang = p->Recv_uint8 ();
info->use_password = p->Recv_bool ();
info->clients_max = p->Recv_uint8 ();
info->clients_on = p->Recv_uint8 ();
info->spectators_on = p->Recv_uint8 ();
p->Recv_string(info->map_name, sizeof(info->map_name));
info->map_width = p->Recv_uint32();
info->map_height = p->Recv_uint32();
info->map_set = p->Recv_uint8 ();
info->dedicated = p->Recv_bool ();
{
GRFConfig **dst = &info->grfconfig;
uint i;
uint num_grfs = p->Recv_uint32();
/* Broken/bad data. It cannot have that many NewGRFs. */
if (num_grfs > MAX_NEWGRFS) return;
for (i = 0; i < num_grfs; i++) {
GRFConfig *c = new GRFConfig();
this->ReceiveGRFIdentifier(p, &c->ident);
this->HandleIncomingNetworkGameInfoGRFConfig(c);
/* Append GRFConfig to the list */
*dst = c;
dst = &c->next;
}
}
if (info->server_lang >= NETWORK_NUM_LANGUAGES) info->server_lang = 0;
if (info->map_set >= NETWORK_NUM_LANDSCAPES) info->map_set = 0;
}
/**
* Handle an incoming packets by sending it to the correct function.
@ -317,6 +467,9 @@ void NetworkUDPSocketHandler::HandleUDPPacket(Packet *p, NetworkAddress *client_
case PACKET_UDP_SERVER_NEWGRFS: this->Receive_SERVER_NEWGRFS(p, client_addr); break;
case PACKET_UDP_MASTER_SESSION_KEY: this->Receive_MASTER_SESSION_KEY(p, client_addr); break;
case PACKET_UDP_EX_MULTI: this->Receive_EX_MULTI(p, client_addr); break;
case PACKET_UDP_EX_SERVER_RESPONSE: this->Receive_EX_SERVER_RESPONSE(p, client_addr); break;
default:
if (this->HasClientQuit()) {
DEBUG(net, 0, "[udp] received invalid packet type %d from %s", type, client_addr->GetAddressAsString());
@ -327,6 +480,74 @@ void NetworkUDPSocketHandler::HandleUDPPacket(Packet *p, NetworkAddress *client_
}
}
void NetworkUDPSocketHandler::Receive_EX_MULTI(Packet *p, NetworkAddress *client_addr)
{
uint64 token = p->Recv_uint64();
uint8 index = p->Recv_uint8 ();
uint8 total = p->Recv_uint8 ();
uint16 payload_size = p->Recv_uint16();
DEBUG(net, 6, "[udp] received multi-part packet from %s: " OTTD_PRINTFHEX64 ", %u/%u, %u bytes",
client_addr->GetAddressAsString(), token, index, total, payload_size);
if (total == 0 || index >= total) return;
if (!p->CanReadFromPacket(payload_size)) return;
time_t cur_time = time(NULL);
auto add_to_fragment = [&](FragmentSet &fs) {
fs.fragments[index].assign((const char *) p->buffer + p->pos, payload_size);
uint total_payload = 0;
for (auto &frag : fs.fragments) {
if (!frag.size()) return;
total_payload += frag.size();
}
DEBUG(net, 6, "[udp] merged multi-part packet from %s: " OTTD_PRINTFHEX64 ", %u bytes",
client_addr->GetAddressAsString(), token, total_payload);
Packet merged(this);
for (auto &frag : fs.fragments) {
merged.Send_binary(frag.data(), frag.size());
}
merged.PrepareToRead();
/* If the size does not match the packet must be corrupted.
* Otherwise it will be marked as corrupted later on. */
if (total_payload != merged.size) {
DEBUG(net, 1, "received an extended packet with mismatching size from %s, (%u, %u)",
client_addr->GetAddressAsString(), total_payload, merged.size);
} else {
this->HandleUDPPacket(&merged, client_addr);
}
fs = this->fragments.back();
this->fragments.pop_back();
};
uint i = 0;
while (i < this->fragments.size()) {
FragmentSet &fs = this->fragments[i];
if (fs.create_time < cur_time - 10) {
fs = this->fragments.back();
this->fragments.pop_back();
continue;
}
if (fs.token == token && fs.address == *client_addr && fs.fragments.size() == total) {
add_to_fragment(fs);
return;
}
i++;
}
this->fragments.push_back({ token, *client_addr, cur_time, {} });
this->fragments.back().fragments.resize(total);
add_to_fragment(this->fragments.back());
}
/**
* Helper for logging receiving invalid packets.
* @param type The received packet type.
@ -339,6 +560,7 @@ void NetworkUDPSocketHandler::ReceiveInvalidPacket(PacketUDPType type, NetworkAd
void NetworkUDPSocketHandler::Receive_CLIENT_FIND_SERVER(Packet *p, NetworkAddress *client_addr) { this->ReceiveInvalidPacket(PACKET_UDP_CLIENT_FIND_SERVER, client_addr); }
void NetworkUDPSocketHandler::Receive_SERVER_RESPONSE(Packet *p, NetworkAddress *client_addr) { this->ReceiveInvalidPacket(PACKET_UDP_SERVER_RESPONSE, client_addr); }
void NetworkUDPSocketHandler::Receive_EX_SERVER_RESPONSE(Packet *p, NetworkAddress *client_addr) { this->ReceiveInvalidPacket(PACKET_UDP_EX_SERVER_RESPONSE, client_addr); }
void NetworkUDPSocketHandler::Receive_CLIENT_DETAIL_INFO(Packet *p, NetworkAddress *client_addr) { this->ReceiveInvalidPacket(PACKET_UDP_CLIENT_DETAIL_INFO, client_addr); }
void NetworkUDPSocketHandler::Receive_SERVER_DETAIL_INFO(Packet *p, NetworkAddress *client_addr) { this->ReceiveInvalidPacket(PACKET_UDP_SERVER_DETAIL_INFO, client_addr); }
void NetworkUDPSocketHandler::Receive_SERVER_REGISTER(Packet *p, NetworkAddress *client_addr) { this->ReceiveInvalidPacket(PACKET_UDP_SERVER_REGISTER, client_addr); }

@ -18,6 +18,10 @@
#include "game.h"
#include "packet.h"
#include <vector>
#include <string>
#include <time.h>
#ifdef ENABLE_NETWORK
/** Enum with all types of UDP packets. The order MUST not be changed **/
@ -34,7 +38,10 @@ enum PacketUDPType {
PACKET_UDP_CLIENT_GET_NEWGRFS, ///< Requests the name for a list of GRFs (GRF_ID and MD5)
PACKET_UDP_SERVER_NEWGRFS, ///< Sends the list of NewGRF's requested.
PACKET_UDP_MASTER_SESSION_KEY, ///< Sends a fresh session key to the client
PACKET_UDP_END, ///< Must ALWAYS be on the end of this list!! (period)
PACKET_UDP_END, ///< Must ALWAYS be the last non-extended item in the list!! (period)
PACKET_UDP_EX_MULTI = 128, ///< Extended/multi packet type
PACKET_UDP_EX_SERVER_RESPONSE, ///< Reply of the game server with extended game information
};
/** The types of server lists we can get */
@ -54,6 +61,16 @@ protected:
/** The opened sockets. */
SocketList sockets;
uint64 fragment_token;
struct FragmentSet {
uint64 token;
NetworkAddress address;
time_t create_time;
std::vector<std::string> fragments;
};
std::vector<FragmentSet> fragments;
NetworkRecvStatus CloseConnection(bool error = true);
void ReceiveInvalidPacket(PacketUDPType, NetworkAddress *client_addr);
@ -106,6 +123,8 @@ protected:
*/
virtual void Receive_SERVER_RESPONSE(Packet *p, NetworkAddress *client_addr);
virtual void Receive_EX_SERVER_RESPONSE(Packet *p, NetworkAddress *client_addr);
/**
* Query for detailed information about companies.
* @param p The received packet.
@ -230,6 +249,8 @@ protected:
* @param config the GRF to handle
*/
virtual void HandleIncomingNetworkGameInfoGRFConfig(GRFConfig *config) { NOT_REACHED(); }
virtual void Receive_EX_MULTI(Packet *p, NetworkAddress *client_addr);
public:
NetworkUDPSocketHandler(NetworkAddressList *bind = NULL);
@ -243,7 +264,9 @@ public:
void ReceivePackets();
void SendNetworkGameInfo(Packet *p, const NetworkGameInfo *info);
void SendNetworkGameInfoExtended(Packet *p, const NetworkGameInfo *info, uint16 version);
void ReceiveNetworkGameInfo(Packet *p, NetworkGameInfo *info);
void ReceiveNetworkGameInfoExtended(Packet *p, NetworkGameInfo *info);
};
#endif /* ENABLE_NETWORK */

@ -1124,9 +1124,9 @@ void NetworkShutDown()
* Checks whether the given version string is compatible with our version.
* @param other the version string to compare to
*/
bool IsNetworkCompatibleVersion(const char *other)
bool IsNetworkCompatibleVersion(const char *other, bool extended)
{
return strncmp(_openttd_revision, other, NETWORK_REVISION_LENGTH - 1) == 0;
return strncmp(_openttd_revision, other, (extended ? NETWORK_LONG_REVISION_LENGTH : NETWORK_REVISION_LENGTH) - 1) == 0;
}
#endif /* ENABLE_NETWORK */

@ -27,6 +27,7 @@
#include "../gfx_func.h"
#include "../error.h"
#include "../rev.h"
#include "../fios.h"
#include "network.h"
#include "network_base.h"
#include "network_client.h"
@ -684,7 +685,8 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_CHECK_NEWGRFS(P
{
if (this->status != STATUS_JOIN) return NETWORK_RECV_STATUS_MALFORMED_PACKET;
uint grf_count = p->Recv_uint8();
uint grf_count = p->Recv_uint32();
if (grf_count > MAX_NEWGRFS) return NETWORK_RECV_STATUS_MALFORMED_PACKET;
NetworkRecvStatus ret = NETWORK_RECV_STATUS_OKAY;
/* Check all GRFs */

@ -1179,10 +1179,6 @@ struct NetworkStartServerWindow : public Window {
}
case WID_NSS_GENERATE_GAME: // Start game
if (CountSelectedGRFs(_grfconfig_newgame) > NETWORK_MAX_GRF_COUNT) {
ShowErrorMessage(STR_NEWGRF_ERROR_TOO_MANY_NEWGRFS_LOADED, INVALID_STRING_ID, WL_ERROR);
break;
}
_is_network_server = true;
if (_ctrl_pressed) {
StartNewGameWithoutGUI(GENERATE_NEW_SEED);

@ -144,7 +144,7 @@ void NetworkAddServer(const char *b);
void NetworkRebuildHostList();
void UpdateNetworkGameWindow();
bool IsNetworkCompatibleVersion(const char *version);
bool IsNetworkCompatibleVersion(const char *version, bool extended = false);
/* From network_command.cpp */
/**

@ -481,7 +481,7 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::SendNewGRFCheck()
if (!HasBit(c->flags, GCF_STATIC)) grf_count++;
}
p->Send_uint8 (grf_count);
p->Send_uint32 (grf_count);
for (c = _grfconfig; c != NULL; c = c->next) {
if (!HasBit(c->flags, GCF_STATIC)) this->SendGRFIdentifier(p, &c->ident);
}

@ -34,8 +34,13 @@
#include "core/udp.h"
#include <vector>
#include "../safeguards.h"
extern const uint8 _out_of_band_grf_md5[16] { 0x00, 0xB0, 0xC0, 0xDE, 0x00, 0x00, 0x00, 0x00, 0x00, 0xB0, 0xC0, 0xDE, 0x00, 0x00, 0x00, 0x00 };
static const uint32 FIND_SERVER_EXTENDED_TOKEN = 0x2A49582A;
/** Mutex for all out threaded udp resolution and such. */
static ThreadMutex *_network_udp_mutex = ThreadMutex::New();
@ -66,6 +71,15 @@ struct NetworkUDPQueryServerInfo : NetworkAddress {
}
};
static Packet PrepareUdpClientFindServerPacket()
{
Packet p(PACKET_UDP_CLIENT_FIND_SERVER);
p.Send_uint32(FIND_SERVER_EXTENDED_TOKEN);
p.Send_uint16(0); // flags
p.Send_uint16(0); // version
return p;
}
/**
* Helper function doing the actual work for querying the server.
* @param address The address of the server.
@ -84,7 +98,7 @@ static void NetworkUDPQueryServer(NetworkAddress *address, bool needs_mutex, boo
if (needs_mutex) _network_udp_mutex->BeginCritical();
/* Init the packet */
Packet p(PACKET_UDP_CLIENT_FIND_SERVER);
Packet p = PrepareUdpClientFindServerPacket();
if (_udp_client_socket != NULL) _udp_client_socket->SendPacket(&p, address);
if (needs_mutex) _network_udp_mutex->EndCritical();
}
@ -153,6 +167,7 @@ protected:
virtual void Receive_CLIENT_FIND_SERVER(Packet *p, NetworkAddress *client_addr);
virtual void Receive_CLIENT_DETAIL_INFO(Packet *p, NetworkAddress *client_addr);
virtual void Receive_CLIENT_GET_NEWGRFS(Packet *p, NetworkAddress *client_addr);
void Reply_CLIENT_FIND_SERVER_extended(Packet *p, NetworkAddress *client_addr, NetworkGameInfo *ngi);
public:
/**
* Create the socket.
@ -192,6 +207,12 @@ void ServerNetworkUDPSocketHandler::Receive_CLIENT_FIND_SERVER(Packet *p, Networ
strecpy(ngi.map_name, _network_game_info.map_name, lastof(ngi.map_name));
strecpy(ngi.server_name, _settings_client.network.server_name, lastof(ngi.server_name));
strecpy(ngi.server_revision, _openttd_revision, lastof(ngi.server_revision));
strecpy(ngi.short_server_revision, _openttd_revision, lastof(ngi.short_server_revision));
if (p->CanReadFromPacket(8, true) && p->Recv_uint32() == FIND_SERVER_EXTENDED_TOKEN) {
this->Reply_CLIENT_FIND_SERVER_extended(p, client_addr, &ngi);
return;
}
Packet packet(PACKET_UDP_SERVER_RESPONSE);
this->SendNetworkGameInfo(&packet, &ngi);
@ -202,6 +223,19 @@ 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)
{
uint16 version = p->Recv_uint16();
Packet packet(PACKET_UDP_EX_SERVER_RESPONSE);
this->SendNetworkGameInfoExtended(&packet, ngi, version);
/* Let the client know that we are here */
this->SendPacket(&packet, client_addr);
DEBUG(net, 2, "[udp] queried (extended: %u) from %s", version, client_addr->GetHostname());
}
void ServerNetworkUDPSocketHandler::Receive_CLIENT_DETAIL_INFO(Packet *p, NetworkAddress *client_addr)
{
/* Just a fail-safe.. should never happen */
@ -274,8 +308,12 @@ void ServerNetworkUDPSocketHandler::Receive_CLIENT_GET_NEWGRFS(Packet *p, Networ
uint8 num_grfs;
uint i;
const GRFConfig *in_reply[NETWORK_MAX_GRF_COUNT];
uint8 in_reply_count = 0;
struct GRFInfo {
GRFIdentifier ident;
const char *name = nullptr;
};
std::vector<GRFInfo> in_reply;
size_t packet_len = 0;
DEBUG(net, 6, "[udp] newgrf data request from %s", client_addr->GetAddressAsString());
@ -284,37 +322,47 @@ void ServerNetworkUDPSocketHandler::Receive_CLIENT_GET_NEWGRFS(Packet *p, Networ
if (num_grfs > NETWORK_MAX_GRF_COUNT) return;
for (i = 0; i < num_grfs; i++) {
GRFIdentifier c;
const GRFConfig *f;
GRFInfo info;
this->ReceiveGRFIdentifier(p, &info.ident);
this->ReceiveGRFIdentifier(p, &c);
if (memcmp(info.ident.md5sum, _out_of_band_grf_md5, 16) == 0) {
if (info.ident.grfid == 0x56D2B000) {
info.name = "=*=*= More than 62 GRFs in total =*=*=";
} else {
continue;
}
} else {
const GRFConfig *f;
/* Find the matching GRF file */
f = FindGRFConfig(info.ident.grfid, FGCM_EXACT, info.ident.md5sum);
if (f == NULL) continue; // The GRF is unknown to this server
/* Find the matching GRF file */
f = FindGRFConfig(c.grfid, FGCM_EXACT, c.md5sum);
if (f == NULL) continue; // The GRF is unknown to this server
info.ident = f->ident;
info.name = f->GetName();
}
/* If the reply might exceed the size of the packet, only reply
* the current list and do not send the other data.
* The name could be an empty string, if so take the filename. */
packet_len += sizeof(c.grfid) + sizeof(c.md5sum) +
min(strlen(f->GetName()) + 1, (size_t)NETWORK_GRF_NAME_LENGTH);
packet_len += sizeof(info.ident.grfid) + sizeof(info.ident.md5sum) +
min(strlen(info.name) + 1, (size_t)NETWORK_GRF_NAME_LENGTH);
if (packet_len > SEND_MTU - 4) { // 4 is 3 byte header + grf count in reply
break;
}
in_reply[in_reply_count] = f;
in_reply_count++;
in_reply.push_back(info);
}
if (in_reply_count == 0) return;
if (in_reply.empty()) return;
Packet packet(PACKET_UDP_SERVER_NEWGRFS);
packet.Send_uint8(in_reply_count);
for (i = 0; i < in_reply_count; i++) {
packet.Send_uint8(in_reply.size());
for (const GRFInfo &info : in_reply) {
char name[NETWORK_GRF_NAME_LENGTH];
/* The name could be an empty string, if so take the filename */
strecpy(name, in_reply[i]->GetName(), lastof(name));
this->SendGRFIdentifier(&packet, &in_reply[i]->ident);
strecpy(name, info.name, lastof(name));
this->SendGRFIdentifier(&packet, &info.ident);
packet.Send_string(name);
}
@ -325,8 +373,11 @@ void ServerNetworkUDPSocketHandler::Receive_CLIENT_GET_NEWGRFS(Packet *p, Networ
/** Helper class for handling all client side communication. */
class ClientNetworkUDPSocketHandler : public NetworkUDPSocketHandler {
private:
void Receive_SERVER_RESPONSE_Common(Packet *p, NetworkAddress *client_addr, bool extended);
protected:
virtual void Receive_SERVER_RESPONSE(Packet *p, NetworkAddress *client_addr);
virtual void Receive_EX_SERVER_RESPONSE(Packet *p, NetworkAddress *client_addr);
virtual void Receive_MASTER_RESPONSE_LIST(Packet *p, NetworkAddress *client_addr);
virtual void Receive_SERVER_NEWGRFS(Packet *p, NetworkAddress *client_addr);
virtual void HandleIncomingNetworkGameInfoGRFConfig(GRFConfig *config);
@ -335,19 +386,33 @@ public:
};
void ClientNetworkUDPSocketHandler::Receive_SERVER_RESPONSE(Packet *p, NetworkAddress *client_addr)
{
this->Receive_SERVER_RESPONSE_Common(p, client_addr, false);
}
void ClientNetworkUDPSocketHandler::Receive_EX_SERVER_RESPONSE(Packet *p, NetworkAddress *client_addr)
{
this->Receive_SERVER_RESPONSE_Common(p, client_addr, true);
}
void ClientNetworkUDPSocketHandler::Receive_SERVER_RESPONSE_Common(Packet *p, NetworkAddress *client_addr, bool extended)
{
NetworkGameList *item;
/* Just a fail-safe.. should never happen */
if (_network_udp_server) return;
DEBUG(net, 4, "[udp] server response from %s", client_addr->GetAddressAsString());
DEBUG(net, 4, "[udp]%s server response from %s", extended ? " extended" : "", client_addr->GetAddressAsString());
/* Find next item */
item = NetworkGameListAddItem(*client_addr);
ClearGRFConfigList(&item->info.grfconfig);
this->ReceiveNetworkGameInfo(p, &item->info);
if (extended) {
this->ReceiveNetworkGameInfoExtended(p, &item->info);
} else {
this->ReceiveNetworkGameInfo(p, &item->info);
}
item->info.compatible = true;
{
@ -362,14 +427,7 @@ void ClientNetworkUDPSocketHandler::Receive_SERVER_RESPONSE(Packet *p, NetworkAd
const GRFConfig *c;
uint in_request_count = 0;
for (c = item->info.grfconfig; c != NULL; c = c->next) {
if (c->status == GCS_NOT_FOUND) item->info.compatible = false;
if (c->status != GCS_NOT_FOUND || strcmp(c->GetName(), UNKNOWN_GRF_NAME_PLACEHOLDER) != 0) continue;
in_request[in_request_count] = c;
in_request_count++;
}
if (in_request_count > 0) {
auto flush_request = [&]() {
/* There are 'unknown' GRFs, now send a request for them */
uint i;
Packet packet(PACKET_UDP_CLIENT_GET_NEWGRFS);
@ -380,7 +438,19 @@ void ClientNetworkUDPSocketHandler::Receive_SERVER_RESPONSE(Packet *p, NetworkAd
}
this->SendPacket(&packet, &item->address);
in_request_count = 0;
};
for (c = item->info.grfconfig; c != NULL; c = c->next) {
if (c->status == GCS_NOT_FOUND) item->info.compatible = false;
if (c->status != GCS_NOT_FOUND || strcmp(c->GetName(), UNKNOWN_GRF_NAME_PLACEHOLDER) != 0) continue;
in_request[in_request_count] = c;
in_request_count++;
if (in_request_count == NETWORK_MAX_GRF_COUNT) flush_request();
}
if (in_request_count > 0) flush_request();
}
if (item->info.hostname[0] == '\0') {
@ -392,7 +462,7 @@ void ClientNetworkUDPSocketHandler::Receive_SERVER_RESPONSE(Packet *p, NetworkAd
}
/* Check if we are allowed on this server based on the revision-match */
item->info.version_compatible = IsNetworkCompatibleVersion(item->info.server_revision);
item->info.version_compatible = IsNetworkCompatibleVersion(item->info.server_revision, extended);
item->info.compatible &= item->info.version_compatible; // Already contains match for GRFs
item->online = true;
@ -498,7 +568,7 @@ void ClientNetworkUDPSocketHandler::HandleIncomingNetworkGameInfoGRFConfig(GRFCo
static void NetworkUDPBroadCast(NetworkUDPSocketHandler *socket)
{
for (NetworkAddress *addr = _broadcast_list.Begin(); addr != _broadcast_list.End(); addr++) {
Packet p(PACKET_UDP_CLIENT_FIND_SERVER);
Packet p = PrepareUdpClientFindServerPacket();
DEBUG(net, 4, "[udp] broadcasting to %s", addr->GetHostname());

@ -9318,7 +9318,7 @@ void LoadNewGRF(uint load_index, uint file_index, uint num_baseset)
if (stage == GLS_LABELSCAN) InitNewGRFFile(c);
if (!HasBit(c->flags, GCF_STATIC) && !HasBit(c->flags, GCF_SYSTEM)) {
if ((_networking && num_non_static == NETWORK_MAX_GRF_COUNT) || slot == MAX_FILE_SLOTS) {
if (slot == MAX_FILE_SLOTS) {
DEBUG(grf, 0, "'%s' is not loaded as the maximum number of non-static GRFs has been reached", c->filename);
c->status = GCS_DISABLED;
c->error = new GRFError(STR_NEWGRF_ERROR_MSG_FATAL, STR_NEWGRF_ERROR_TOO_MANY_NEWGRFS_LOADED);

@ -895,13 +895,6 @@ int openttd_main(int argc, char *argv[])
DriverFactoryBase::SelectDriver(musicdriver, Driver::DT_MUSIC);
free(musicdriver);
// Check if not too much GRFs are loaded for network game
if (dedicated && CountSelectedGRFs(_grfconfig) > NETWORK_MAX_GRF_COUNT) {
DEBUG(net, 0, "Too many GRF loaded. Max %d are allowed.\nExiting ...", NETWORK_MAX_GRF_COUNT);
ShutdownGame();
goto exit_normal;
}
/* Take our initial lock on whatever we might want to do! */
_modal_progress_paint_mutex->BeginCritical();
_modal_progress_work_mutex->BeginCritical();

@ -763,13 +763,6 @@ bool AfterLoadGame()
return false;
}
if (_networking && CountSelectedGRFs(_grfconfig) > NETWORK_MAX_GRF_COUNT) {
SetSaveLoadError(STR_NEWGRF_ERROR_TOO_MANY_NEWGRFS_LOADED);
/* Restore the signals */
ResetSignalHandlers();
return false;
}
/* The value of _date_fract got divided, so make sure that old games are converted correctly. */
if (IsSavegameVersionBefore(11, 1) || (IsSavegameVersionBefore(147) && _date_fract > DAY_TICKS)) _date_fract /= 885;

Loading…
Cancel
Save