diff --git a/src/network/core/config.h b/src/network/core/config.h index f5e73dd12d..b6f63f8dca 100644 --- a/src/network/core/config.h +++ b/src/network/core/config.h @@ -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' diff --git a/src/network/core/game.h b/src/network/core/game.h index c82c5aefed..846551678e 100644 --- a/src/network/core/game.h +++ b/src/network/core/game.h @@ -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 diff --git a/src/network/core/packet.cpp b/src/network/core/packet.cpp index b3ebd422e2..a8a82823e5 100644 --- a/src/network/core/packet.cpp +++ b/src/network/core/packet.cpp @@ -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(SHRT_MAX); - this->buffer[this->size++] = type; + this->buffer = MallocT(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; } diff --git a/src/network/core/packet.h b/src/network/core/packet.h index b27e590d55..ec24ea06bc 100644 --- a/src/network/core/packet.h +++ b/src/network/core/packet.h @@ -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(); diff --git a/src/network/core/tcp.cpp b/src/network/core/tcp.cpp index 790941fc01..c98d51ff54 100644 --- a/src/network/core/tcp.cpp +++ b/src/network/core/tcp.cpp @@ -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 */ diff --git a/src/network/core/udp.cpp b/src/network/core/udp.cpp index d2dc15d427..7511a5513f 100644 --- a/src/network/core/udp.cpp +++ b/src/network/core/udp.cpp @@ -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(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(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); } diff --git a/src/network/core/udp.h b/src/network/core/udp.h index 9aa0c9dc49..3026818234 100644 --- a/src/network/core/udp.h +++ b/src/network/core/udp.h @@ -18,6 +18,10 @@ #include "game.h" #include "packet.h" +#include +#include +#include + #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 fragments; + }; + std::vector 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 */ diff --git a/src/network/network.cpp b/src/network/network.cpp index bf9ee3d27b..e935ef4b5e 100644 --- a/src/network/network.cpp +++ b/src/network/network.cpp @@ -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 */ diff --git a/src/network/network_client.cpp b/src/network/network_client.cpp index 9dac1cab19..50ee775b95 100644 --- a/src/network/network_client.cpp +++ b/src/network/network_client.cpp @@ -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 */ diff --git a/src/network/network_gui.cpp b/src/network/network_gui.cpp index 854ab922eb..2be8b13edc 100644 --- a/src/network/network_gui.cpp +++ b/src/network/network_gui.cpp @@ -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); diff --git a/src/network/network_internal.h b/src/network/network_internal.h index 528c197914..112d834ea8 100644 --- a/src/network/network_internal.h +++ b/src/network/network_internal.h @@ -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 */ /** diff --git a/src/network/network_server.cpp b/src/network/network_server.cpp index b9b77fc1d8..a5d0afdf76 100644 --- a/src/network/network_server.cpp +++ b/src/network/network_server.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); } diff --git a/src/network/network_udp.cpp b/src/network/network_udp.cpp index 1dc6960659..01d7b12cfb 100644 --- a/src/network/network_udp.cpp +++ b/src/network/network_udp.cpp @@ -34,8 +34,13 @@ #include "core/udp.h" +#include + #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 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()); diff --git a/src/newgrf.cpp b/src/newgrf.cpp index 878fa734e4..605e24f7e1 100644 --- a/src/newgrf.cpp +++ b/src/newgrf.cpp @@ -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); diff --git a/src/openttd.cpp b/src/openttd.cpp index 145966faff..3204e48105 100644 --- a/src/openttd.cpp +++ b/src/openttd.cpp @@ -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(); diff --git a/src/saveload/afterload.cpp b/src/saveload/afterload.cpp index 5d4fb77a47..0ab9a5bfac 100644 --- a/src/saveload/afterload.cpp +++ b/src/saveload/afterload.cpp @@ -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;