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:
Jonathan G Rennison
2018-05-12 09:11:41 +01:00
parent 9b42ae954c
commit 6342099c4d
16 changed files with 382 additions and 78 deletions

View File

@@ -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); }