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:
@@ -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); }
|
||||
|
Reference in New Issue
Block a user