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
This commit is contained in:
@@ -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());
|
||||
|
||||
|
Reference in New Issue
Block a user