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:
@@ -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->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;
|
||||
GRFInfo info;
|
||||
this->ReceiveGRFIdentifier(p, &info.ident);
|
||||
|
||||
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;
|
||||
|
||||
this->ReceiveGRFIdentifier(p, &c);
|
||||
|
||||
/* Find the matching GRF file */
|
||||
f = FindGRFConfig(c.grfid, FGCM_EXACT, c.md5sum);
|
||||
f = FindGRFConfig(info.ident.grfid, FGCM_EXACT, info.ident.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);
|
||||
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;
|
||||
|
||||
|
Reference in New Issue
Block a user