From f7e390bdc084a20462fe833373087fd9106c5972 Mon Sep 17 00:00:00 2001 From: Patric Stout Date: Thu, 6 May 2021 23:13:35 +0200 Subject: [PATCH 01/61] Feature: use Happy Eyeballs to make network connections (TCP-only) (#9199) Hostnames like "content.openttd.org" resolve into multiple IPv4 and IPv6. It is possible that either of the IPs is not working, either due to a poorly configured OS (having IPv6 but no valid route), broken network paths, or a service that is temporary unavailable. Instead of trying the IPs one by one, waiting for a 3s timeout between each, be a bit more like browsers, and stack attempts on top of each other with slight delays. This is called Happy Eyebells. Initially, try the first IPv6 address. If within 250ms there is no connection yet, try the first IPv4 address. 250ms later, try the second IPv6 address, etc, till all addresses are tried. If any connection is created, abort all the other (pending) connections and use the one that is created. If all fail 3s after the last connect(), trigger a timeout for all. --- src/network/core/address.cpp | 91 ++-------- src/network/core/address.h | 2 +- src/network/core/tcp.h | 28 +-- src/network/core/tcp_connect.cpp | 301 +++++++++++++++++++++++++++---- src/network/core/tcp_http.cpp | 4 +- src/network/core/tcp_http.h | 10 +- src/network/network.cpp | 41 +++-- src/network/network_internal.h | 1 + 8 files changed, 333 insertions(+), 145 deletions(-) diff --git a/src/network/core/address.cpp b/src/network/core/address.cpp index d40a72420b..205b5df224 100644 --- a/src/network/core/address.cpp +++ b/src/network/core/address.cpp @@ -14,8 +14,6 @@ #include "../../safeguards.h" -static const int DEFAULT_CONNECT_TIMEOUT_SECONDS = 3; ///< Allow connect() three seconds to connect. - /** * Get the hostname; in case it wasn't given the * IPv4 dotted representation is given. @@ -306,82 +304,6 @@ SOCKET NetworkAddress::Resolve(int family, int socktype, int flags, SocketList * return sock; } -/** - * Helper function to resolve a connected socket. - * @param runp information about the socket to try not - * @return the opened socket or INVALID_SOCKET - */ -static SOCKET ConnectLoopProc(addrinfo *runp) -{ - const char *type = NetworkAddress::SocketTypeAsString(runp->ai_socktype); - const char *family = NetworkAddress::AddressFamilyAsString(runp->ai_family); - std::string address = NetworkAddress(runp->ai_addr, (int)runp->ai_addrlen).GetAddressAsString(); - - SOCKET sock = socket(runp->ai_family, runp->ai_socktype, runp->ai_protocol); - if (sock == INVALID_SOCKET) { - DEBUG(net, 1, "[%s] could not create %s socket: %s", type, family, NetworkError::GetLast().AsString()); - return INVALID_SOCKET; - } - - if (!SetNoDelay(sock)) DEBUG(net, 1, "[%s] setting TCP_NODELAY failed", type); - - if (!SetNonBlocking(sock)) DEBUG(net, 0, "[%s] setting non-blocking mode failed", type); - - int err = connect(sock, runp->ai_addr, (int)runp->ai_addrlen); - if (err != 0 && !NetworkError::GetLast().IsConnectInProgress()) { - DEBUG(net, 1, "[%s] could not connect to %s over %s: %s", type, address.c_str(), family, NetworkError::GetLast().AsString()); - closesocket(sock); - return INVALID_SOCKET; - } - - fd_set write_fd; - struct timeval tv; - - FD_ZERO(&write_fd); - FD_SET(sock, &write_fd); - - /* Wait for connect() to either connect, timeout or fail. */ - tv.tv_usec = 0; - tv.tv_sec = DEFAULT_CONNECT_TIMEOUT_SECONDS; - int n = select(FD_SETSIZE, NULL, &write_fd, NULL, &tv); - if (n < 0) { - DEBUG(net, 1, "[%s] could not connect to %s: %s", type, address.c_str(), NetworkError::GetLast().AsString()); - closesocket(sock); - return INVALID_SOCKET; - } - - /* If no fd is selected, the timeout has been reached. */ - if (n == 0) { - DEBUG(net, 1, "[%s] timed out while connecting to %s", type, address.c_str()); - closesocket(sock); - return INVALID_SOCKET; - } - - /* Retrieve last error, if any, on the socket. */ - NetworkError socket_error = GetSocketError(sock); - if (socket_error.HasError()) { - DEBUG(net, 1, "[%s] could not connect to %s: %s", type, address.c_str(), socket_error.AsString()); - closesocket(sock); - return INVALID_SOCKET; - } - - /* Connection succeeded. */ - DEBUG(net, 1, "[%s] connected to %s", type, address.c_str()); - - return sock; -} - -/** - * Connect to the given address. - * @return the connected socket or INVALID_SOCKET. - */ -SOCKET NetworkAddress::Connect() -{ - DEBUG(net, 1, "Connecting to %s", this->GetAddressAsString().c_str()); - - return this->Resolve(AF_UNSPEC, SOCK_STREAM, AI_ADDRCONFIG, nullptr, ConnectLoopProc); -} - /** * Helper function to resolve a listening. * @param runp information about the socket to try not @@ -486,3 +408,16 @@ void NetworkAddress::Listen(int socktype, SocketList *sockets) default: return "unsupported"; } } + +/** + * Get the peer name of a socket in string format. + * @param sock The socket to get the peer name of. + * @return The string representation of the peer name. + */ +/* static */ const std::string NetworkAddress::GetPeerName(SOCKET sock) +{ + sockaddr_storage addr; + socklen_t addr_len = sizeof(addr); + getpeername(sock, (sockaddr *)&addr, &addr_len); + return NetworkAddress(addr, addr_len).GetAddressAsString(); +} diff --git a/src/network/core/address.h b/src/network/core/address.h index b4364c95ab..c3f95b4925 100644 --- a/src/network/core/address.h +++ b/src/network/core/address.h @@ -171,11 +171,11 @@ public: return this->CompareTo(address) < 0; } - SOCKET Connect(); void Listen(int socktype, SocketList *sockets); static const char *SocketTypeAsString(int socktype); static const char *AddressFamilyAsString(int family); + static const std::string GetPeerName(SOCKET sock); }; #endif /* NETWORK_CORE_ADDRESS_H */ diff --git a/src/network/core/tcp.h b/src/network/core/tcp.h index 5acf9d12e6..b90ce0232c 100644 --- a/src/network/core/tcp.h +++ b/src/network/core/tcp.h @@ -15,6 +15,7 @@ #include "address.h" #include "packet.h" +#include #include /** The states of sending the packets. */ @@ -63,23 +64,28 @@ public: */ class TCPConnecter { private: - std::atomic connected;///< Whether we succeeded in making the connection - std::atomic aborted; ///< Whether we bailed out (i.e. connection making failed) - bool killed; ///< Whether we got killed - SOCKET sock; ///< The socket we're connecting with + addrinfo *ai = nullptr; ///< getaddrinfo() allocated linked-list of resolved addresses. + std::vector addresses; ///< Addresses we can connect to. + size_t current_address = 0; ///< Current index in addresses we are trying. - void Connect(); + std::vector sockets; ///< Pending connect() attempts. + std::chrono::steady_clock::time_point last_attempt; ///< Time we last tried to connect. - static void ThreadEntry(TCPConnecter *param); + std::atomic is_resolved = false; ///< Whether resolving is done. -protected: - /** Address we're connecting to */ - NetworkAddress address; + void Resolve(); + void OnResolved(addrinfo *ai); + bool TryNextAddress(); + void Connect(addrinfo *address); + bool CheckActivity(); + + static void ResolveThunk(TCPConnecter *connecter); public: + std::string connection_string; ///< Current address we are connecting to (before resolving). + TCPConnecter(const std::string &connection_string, uint16 default_port); - /** Silence the warnings */ - virtual ~TCPConnecter() {} + virtual ~TCPConnecter(); /** * Callback when the connection succeeded. diff --git a/src/network/core/tcp_connect.cpp b/src/network/core/tcp_connect.cpp index 81c4d8c264..cca9f09b71 100644 --- a/src/network/core/tcp_connect.cpp +++ b/src/network/core/tcp_connect.cpp @@ -15,6 +15,8 @@ #include "tcp.h" #include "../network_internal.h" +#include + #include "../../safeguards.h" /** List of connections that are currently being created */ @@ -24,38 +26,271 @@ static std::vector _tcp_connecters; * Create a new connecter for the given address * @param connection_string the address to connect to */ -TCPConnecter::TCPConnecter(const std::string &connection_string, uint16 default_port) : - connected(false), - aborted(false), - killed(false), - sock(INVALID_SOCKET) +TCPConnecter::TCPConnecter(const std::string &connection_string, uint16 default_port) { - this->address = ParseConnectionString(connection_string, default_port); + this->connection_string = NormalizeConnectionString(connection_string, default_port); _tcp_connecters.push_back(this); - if (!StartNewThread(nullptr, "ottd:tcp", &TCPConnecter::ThreadEntry, this)) { - this->Connect(); + + if (!StartNewThread(nullptr, "ottd:resolve", &TCPConnecter::ResolveThunk, this)) { + this->Resolve(); + } +} + +TCPConnecter::~TCPConnecter() +{ + for (const auto &socket : this->sockets) { + close(socket); } + + freeaddrinfo(this->ai); } -/** The actual connection function */ -void TCPConnecter::Connect() +/** + * Start a connection to the indicated address. + * @param address The address to connection to. + */ +void TCPConnecter::Connect(addrinfo *address) { - this->sock = this->address.Connect(); - if (this->sock == INVALID_SOCKET) { - this->aborted = true; - } else { - this->connected = true; + SOCKET sock = socket(address->ai_family, address->ai_socktype, address->ai_protocol); + if (sock == INVALID_SOCKET) { + DEBUG(net, 0, "Could not create %s %s socket: %s", NetworkAddress::SocketTypeAsString(address->ai_socktype), NetworkAddress::AddressFamilyAsString(address->ai_family), NetworkError::GetLast().AsString()); + return; + } + + if (!SetNoDelay(sock)) DEBUG(net, 1, "Setting TCP_NODELAY failed"); + if (!SetNonBlocking(sock)) DEBUG(net, 0, "Setting non-blocking mode failed"); + + NetworkAddress network_address = NetworkAddress(address->ai_addr, (int)address->ai_addrlen); + DEBUG(net, 4, "Attempting to connect to %s", network_address.GetAddressAsString().c_str()); + + int err = connect(sock, address->ai_addr, (int)address->ai_addrlen); + if (err != 0 && !NetworkError::GetLast().IsConnectInProgress()) { + closesocket(sock); + + DEBUG(net, 1, "Could not connect to %s: %s", network_address.GetAddressAsString().c_str(), NetworkError::GetLast().AsString()); + return; + } + + this->sockets.push_back(sock); +} + +/** + * Start the connect() for the next address in the list. + * @return True iff a new connect() is attempted. + */ +bool TCPConnecter::TryNextAddress() +{ + if (this->current_address >= this->addresses.size()) return false; + + this->last_attempt = std::chrono::steady_clock::now(); + this->Connect(this->addresses[this->current_address++]); + + return true; +} + +void TCPConnecter::OnResolved(addrinfo *ai) +{ + std::deque addresses_ipv4, addresses_ipv6; + + /* Apply "Happy Eyeballs" if it is likely IPv6 is functional. */ + + /* Detect if IPv6 is likely to succeed or not. */ + bool seen_ipv6 = false; + bool resort = true; + for (addrinfo *runp = ai; runp != nullptr; runp = runp->ai_next) { + if (runp->ai_family == AF_INET6) { + seen_ipv6 = true; + } else if (!seen_ipv6) { + /* We see an IPv4 before an IPv6; this most likely means there is + * no IPv6 available on the system, so keep the order of this + * list. */ + resort = false; + break; + } + } + + /* Convert the addrinfo into NetworkAddresses. */ + for (addrinfo *runp = ai; runp != nullptr; runp = runp->ai_next) { + if (resort) { + if (runp->ai_family == AF_INET6) { + addresses_ipv6.emplace_back(runp); + } else { + addresses_ipv4.emplace_back(runp); + } + } else { + this->addresses.emplace_back(runp); + } + } + + /* If we want to resort, make the list like IPv6 / IPv4 / IPv6 / IPv4 / .. + * for how ever many (round-robin) DNS entries we have. */ + if (resort) { + while (!addresses_ipv4.empty() || !addresses_ipv6.empty()) { + if (!addresses_ipv6.empty()) { + this->addresses.push_back(addresses_ipv6.front()); + addresses_ipv6.pop_front(); + } + if (!addresses_ipv4.empty()) { + this->addresses.push_back(addresses_ipv4.front()); + addresses_ipv4.pop_front(); + } + } + } + + if (_debug_net_level >= 5) { + DEBUG(net, 5, "%s resolved in:", this->connection_string.c_str()); + for (const auto &address : this->addresses) { + DEBUG(net, 5, "- %s", NetworkAddress(address->ai_addr, (int)address->ai_addrlen).GetAddressAsString().c_str()); + } + } + + this->current_address = 0; +} + +void TCPConnecter::Resolve() +{ + /* Port is already guaranteed part of the connection_string. */ + NetworkAddress address = ParseConnectionString(this->connection_string, 0); + + addrinfo hints; + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; + hints.ai_flags = AI_ADDRCONFIG; + hints.ai_socktype = SOCK_STREAM; + + char port_name[6]; + seprintf(port_name, lastof(port_name), "%u", address.GetPort()); + + static bool getaddrinfo_timeout_error_shown = false; + auto start = std::chrono::steady_clock::now(); + + addrinfo *ai; + int e = getaddrinfo(address.GetHostname(), port_name, &hints, &ai); + + auto end = std::chrono::steady_clock::now(); + auto duration = std::chrono::duration_cast(end - start); + if (!getaddrinfo_timeout_error_shown && duration >= std::chrono::seconds(5)) { + DEBUG(net, 0, "getaddrinfo() for address \"%s\" took %i seconds", this->connection_string.c_str(), (int)duration.count()); + DEBUG(net, 0, " This is likely an issue in the DNS name resolver's configuration causing it to time out"); + getaddrinfo_timeout_error_shown = true; } + + if (e != 0) { + DEBUG(misc, 0, "Failed to resolve DNS for %s", this->connection_string.c_str()); + this->OnFailure(); + return; + } + + this->ai = ai; + this->OnResolved(ai); + this->is_resolved = true; +} + +/* static */ void TCPConnecter::ResolveThunk(TCPConnecter *connecter) +{ + connecter->Resolve(); } /** - * Entry point for the new threads. - * @param param the TCPConnecter instance to call Connect on. + * Check if there was activity for this connecter. + * @return True iff the TCPConnecter is done and can be cleaned up. */ -/* static */ void TCPConnecter::ThreadEntry(TCPConnecter *param) +bool TCPConnecter::CheckActivity() { - param->Connect(); + if (!this->is_resolved.load()) return false; + + /* If there are no attempts pending, connect to the next. */ + if (this->sockets.empty()) { + if (!this->TryNextAddress()) { + /* There were no more addresses to try, so we failed. */ + this->OnFailure(); + return true; + } + return false; + } + + fd_set write_fd; + FD_ZERO(&write_fd); + for (const auto &socket : this->sockets) { + FD_SET(socket, &write_fd); + } + + timeval tv; + tv.tv_usec = 0; + tv.tv_sec = 0; + int n = select(FD_SETSIZE, NULL, &write_fd, NULL, &tv); + /* select() failed; hopefully next try it doesn't. */ + if (n < 0) { + /* select() normally never fails; so hopefully it works next try! */ + DEBUG(net, 1, "select() failed with %s", NetworkError::GetLast().AsString()); + return false; + } + + /* No socket updates. */ + if (n == 0) { + /* Wait 250ms between attempting another address. */ + if (std::chrono::steady_clock::now() < this->last_attempt + std::chrono::milliseconds(250)) return false; + + /* Try the next address in the list. */ + if (this->TryNextAddress()) return false; + + /* Wait up to 3 seconds since the last connection we started. */ + if (std::chrono::steady_clock::now() < this->last_attempt + std::chrono::milliseconds(3000)) return false; + + /* More than 3 seconds no socket reported activity, and there are no + * more address to try. Timeout the attempt. */ + DEBUG(net, 0, "Timeout while connecting to %s", this->connection_string.c_str()); + + for (const auto &socket : this->sockets) { + closesocket(socket); + } + this->OnFailure(); + return true; + } + + /* Check for errors on any of the sockets. */ + for (auto it = this->sockets.begin(); it != this->sockets.end(); /* nothing */) { + NetworkError socket_error = GetSocketError(*it); + if (socket_error.HasError()) { + DEBUG(net, 1, "Could not connect to %s: %s", NetworkAddress::GetPeerName(*it).c_str(), socket_error.AsString()); + closesocket(*it); + it = this->sockets.erase(it); + } else { + it++; + } + } + + /* In case all sockets had an error, queue a new one. */ + if (this->sockets.empty()) { + if (!this->TryNextAddress()) { + /* There were no more addresses to try, so we failed. */ + this->OnFailure(); + return true; + } + return false; + } + + /* At least one socket is connected. The first one that does is the one + * we will be using, and we close all other sockets. */ + SOCKET connected_socket = INVALID_SOCKET; + for (auto it = this->sockets.begin(); it != this->sockets.end(); /* nothing */) { + if (connected_socket == INVALID_SOCKET && FD_ISSET(*it, &write_fd)) { + connected_socket = *it; + } else { + closesocket(*it); + } + it = this->sockets.erase(it); + } + assert(connected_socket != INVALID_SOCKET); + + DEBUG(net, 1, "Connected to %s", this->connection_string.c_str()); + if (_debug_net_level >= 5) { + DEBUG(net, 5, "- using %s", NetworkAddress::GetPeerName(connected_socket).c_str()); + } + + this->OnConnect(connected_socket); + return true; } /** @@ -68,32 +303,22 @@ void TCPConnecter::Connect() { for (auto iter = _tcp_connecters.begin(); iter < _tcp_connecters.end(); /* nothing */) { TCPConnecter *cur = *iter; - const bool connected = cur->connected.load(); - const bool aborted = cur->aborted.load(); - if ((connected || aborted) && cur->killed) { - iter = _tcp_connecters.erase(iter); - if (cur->sock != INVALID_SOCKET) closesocket(cur->sock); - delete cur; - continue; - } - if (connected) { - iter = _tcp_connecters.erase(iter); - cur->OnConnect(cur->sock); - delete cur; - continue; - } - if (aborted) { + + if (cur->CheckActivity()) { iter = _tcp_connecters.erase(iter); - cur->OnFailure(); delete cur; - continue; + } else { + iter++; } - iter++; } } /** Kill all connection attempts. */ /* static */ void TCPConnecter::KillAll() { - for (TCPConnecter *conn : _tcp_connecters) conn->killed = true; + for (auto iter = _tcp_connecters.begin(); iter < _tcp_connecters.end(); /* nothing */) { + TCPConnecter *cur = *iter; + iter = _tcp_connecters.erase(iter); + delete cur; + } } diff --git a/src/network/core/tcp_http.cpp b/src/network/core/tcp_http.cpp index 3b7579fcea..ccad120aec 100644 --- a/src/network/core/tcp_http.cpp +++ b/src/network/core/tcp_http.cpp @@ -203,9 +203,11 @@ int NetworkHTTPSocketHandler::HandleHeader() *url = '\0'; + std::string hostname = std::string(hname); + /* Restore the URL. */ *url = '/'; - new NetworkHTTPContentConnecter(hname, callback, url, data, depth); + new NetworkHTTPContentConnecter(hostname, callback, url, data, depth); return 0; } diff --git a/src/network/core/tcp_http.h b/src/network/core/tcp_http.h index cc9a3adac2..d7be0c327b 100644 --- a/src/network/core/tcp_http.h +++ b/src/network/core/tcp_http.h @@ -73,6 +73,7 @@ public: /** Connect with a HTTP server and do ONE query. */ class NetworkHTTPContentConnecter : TCPConnecter { + std::string hostname; ///< Hostname we are connecting to. HTTPCallback *callback; ///< Callback to tell that we received some data (or won't). const char *url; ///< The URL we want to get at the server. const char *data; ///< The data to send @@ -81,14 +82,15 @@ class NetworkHTTPContentConnecter : TCPConnecter { public: /** * Start the connecting. - * @param connection_string The address to connect to. + * @param hostname The hostname to connect to. * @param callback The callback for HTTP retrieval. * @param url The url at the server. * @param data The data to send. * @param depth The depth (redirect recursion) of the queries. */ - NetworkHTTPContentConnecter(const std::string &connection_string, HTTPCallback *callback, const char *url, const char *data = nullptr, int depth = 0) : - TCPConnecter(connection_string, 80), + NetworkHTTPContentConnecter(const std::string &hostname, HTTPCallback *callback, const char *url, const char *data = nullptr, int depth = 0) : + TCPConnecter(hostname, 80), + hostname(hostname), callback(callback), url(stredup(url)), data(data), @@ -110,7 +112,7 @@ public: void OnConnect(SOCKET s) override { - new NetworkHTTPSocketHandler(s, this->callback, this->address.GetHostname(), this->url, this->data, this->depth); + new NetworkHTTPSocketHandler(s, this->callback, this->hostname.c_str(), this->url, this->data, this->depth); /* We've relinquished control of data now. */ this->data = nullptr; } diff --git a/src/network/network.cpp b/src/network/network.cpp index 83a93a22ea..bbf6bb5deb 100644 --- a/src/network/network.cpp +++ b/src/network/network.cpp @@ -501,6 +501,19 @@ std::string_view ParseFullConnectionString(const std::string &connection_string, return ip; } +/** + * Normalize a connection string. That is, ensure there is a port in the string. + * @param connection_string The connection string to normalize. + * @param default_port The port to use if none is given. + * @return The normalized connection string. + */ +std::string NormalizeConnectionString(const std::string &connection_string, uint16 default_port) +{ + uint16 port = default_port; + std::string_view ip = ParseFullConnectionString(connection_string, port); + return std::string(ip) + ":" + std::to_string(port); +} + /** * Convert a string containing either "hostname" or "hostname:ip" to a * NetworkAddress. @@ -1131,23 +1144,27 @@ static void NetworkGenerateServerId() seprintf(_settings_client.network.network_id, lastof(_settings_client.network.network_id), "%s", hex_output); } -void NetworkStartDebugLog(const std::string &connection_string) -{ - extern SOCKET _debug_socket; // Comes from debug.c +class TCPNetworkDebugConnecter : TCPConnecter { +public: + TCPNetworkDebugConnecter(const std::string &connection_string) : TCPConnecter(connection_string, NETWORK_DEFAULT_DEBUGLOG_PORT) {} - NetworkAddress address = ParseConnectionString(connection_string, NETWORK_DEFAULT_DEBUGLOG_PORT); + void OnFailure() override + { + DEBUG(net, 0, "Failed to open connection to %s for redirecting DEBUG()", this->connection_string.c_str()); + } - DEBUG(net, 0, "Redirecting DEBUG() to %s", address.GetAddressAsString().c_str()); + void OnConnect(SOCKET s) override + { + DEBUG(net, 0, "Redirecting DEBUG() to %s", this->connection_string.c_str()); - SOCKET s = address.Connect(); - if (s == INVALID_SOCKET) { - DEBUG(net, 0, "Failed to open socket for redirection DEBUG()"); - return; + extern SOCKET _debug_socket; + _debug_socket = s; } +}; - _debug_socket = s; - - DEBUG(net, 0, "DEBUG() is now redirected"); +void NetworkStartDebugLog(const std::string &connection_string) +{ + new TCPNetworkDebugConnecter(connection_string); } /** This tries to launch the network for a given OS */ diff --git a/src/network/network_internal.h b/src/network/network_internal.h index 2a2024bac5..88af965cfc 100644 --- a/src/network/network_internal.h +++ b/src/network/network_internal.h @@ -120,5 +120,6 @@ bool NetworkFindName(char *new_name, const char *last); const char *GenerateCompanyPasswordHash(const char *password, const char *password_server_id, uint32 password_game_seed); NetworkAddress ParseConnectionString(const std::string &connection_string, uint16 default_port); +std::string NormalizeConnectionString(const std::string &connection_string, uint16 default_port); #endif /* NETWORK_INTERNAL_H */ From b56c03b12009e2356af0ff01c028132e36c97b5d Mon Sep 17 00:00:00 2001 From: translators Date: Fri, 7 May 2021 18:59:46 +0000 Subject: [PATCH 02/61] Update: Translations from eints dutch: 2 changes by Afoklala --- src/lang/dutch.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/lang/dutch.txt b/src/lang/dutch.txt index 9e54ba3fdb..c1f03eab95 100644 --- a/src/lang/dutch.txt +++ b/src/lang/dutch.txt @@ -2217,6 +2217,7 @@ STR_NETWORK_ERROR_TIMEOUT_COMPUTER :{WHITE}Uw compu STR_NETWORK_ERROR_TIMEOUT_MAP :{WHITE}Uw computer deed er te lang over om de kaart te downloaden STR_NETWORK_ERROR_TIMEOUT_JOIN :{WHITE}Uw computer deed er te lang over om met de server te verbinden STR_NETWORK_ERROR_INVALID_CLIENT_NAME :{WHITE}Je spelernaam is niet geldig +STR_NETWORK_ERROR_SERVER_TOO_OLD :{WHITE}De opgevraagde server is te oud voor deze client ############ Leave those lines in this order!! STR_NETWORK_ERROR_CLIENT_GENERAL :algemene fout @@ -2268,7 +2269,7 @@ STR_NETWORK_MESSAGE_CLIENT_COMPANY_JOIN :*** {STRING} he STR_NETWORK_MESSAGE_CLIENT_COMPANY_SPECTATE :*** {STRING} kijkt nu toe STR_NETWORK_MESSAGE_CLIENT_COMPANY_NEW :*** {STRING} heeft een nieuw bedrijf opgericht (nr. {2:NUM}) STR_NETWORK_MESSAGE_CLIENT_LEFT :*** {STRING} heeft het spel verlaten ({2:STRING}) -STR_NETWORK_MESSAGE_NAME_CHANGE :*** {STRING} heeft zijn/haar naam gewijzigd naar {STRING} +STR_NETWORK_MESSAGE_NAME_CHANGE :*** {STRING} heeft diens naam gewijzigd in {STRING} STR_NETWORK_MESSAGE_GIVE_MONEY :*** {STRING} gaf {2:CURRENCY_LONG} aan {1:STRING} STR_NETWORK_MESSAGE_SERVER_SHUTDOWN :{WHITE}De server heeft de sessie gesloten STR_NETWORK_MESSAGE_SERVER_REBOOT :{WHITE}De server wordt opnieuw gestart...{}Wacht alstublieft... From a87999601294af97d8d914db232f24525d9248ce Mon Sep 17 00:00:00 2001 From: embeddedt <42941056+embeddedt@users.noreply.github.com> Date: Sat, 8 May 2021 03:43:17 -0400 Subject: [PATCH 03/61] Fix: [Emscripten] Use non-XDG directories to simplify lolac storage (#9207) --- src/stdafx.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/stdafx.h b/src/stdafx.h index 819eb0e614..937c053f2f 100644 --- a/src/stdafx.h +++ b/src/stdafx.h @@ -347,7 +347,7 @@ typedef unsigned char byte; #endif /* Define the the platforms that use XDG */ -#if defined(WITH_PERSONAL_DIR) && defined(UNIX) && !defined(__APPLE__) +#if defined(WITH_PERSONAL_DIR) && defined(UNIX) && !defined(__APPLE__) && !defined(__EMSCRIPTEN__) # define USE_XDG #endif From e2774354b4f64a46992a74b399fb882aa075cfac Mon Sep 17 00:00:00 2001 From: rubidium42 Date: Sat, 8 May 2021 10:19:42 +0200 Subject: [PATCH 04/61] Codechange: [Network] Change ChatMessage's message to std::string and simplify some code --- src/network/network.cpp | 2 +- src/network/network_chat_gui.cpp | 15 +++------------ src/network/network_func.h | 2 +- 3 files changed, 5 insertions(+), 14 deletions(-) diff --git a/src/network/network.cpp b/src/network/network.cpp index bbf6bb5deb..3965c200fd 100644 --- a/src/network/network.cpp +++ b/src/network/network.cpp @@ -262,7 +262,7 @@ void NetworkTextMessage(NetworkAction action, TextColour colour, bool self_send, DEBUG(desync, 1, "msg: %08x; %02x; %s", _date, _date_fract, message); IConsolePrintF(colour, "%s", message); - NetworkAddChatMessage((TextColour)colour, _settings_client.gui.network_chat_timeout, "%s", message); + NetworkAddChatMessage((TextColour)colour, _settings_client.gui.network_chat_timeout, message); } /* Calculate the frame-lag of a client */ diff --git a/src/network/network_chat_gui.cpp b/src/network/network_chat_gui.cpp index 14edc15032..e8fbf0f124 100644 --- a/src/network/network_chat_gui.cpp +++ b/src/network/network_chat_gui.cpp @@ -39,7 +39,7 @@ static const uint NETWORK_CHAT_LINE_SPACING = 3; /** Container for a message. */ struct ChatMessage { - char message[DRAW_STRING_BUFFER]; ///< The action message. + std::string message; ///< The action message. TextColour colour; ///< The colour of the message. std::chrono::steady_clock::time_point remove_time; ///< The time to remove the message. }; @@ -87,23 +87,14 @@ static inline bool HaveChatMessages(bool show_all) * @param duration The duration of the chat message in seconds * @param message message itself in printf() style */ -void CDECL NetworkAddChatMessage(TextColour colour, uint duration, const char *message, ...) +void CDECL NetworkAddChatMessage(TextColour colour, uint duration, const std::string &message) { - char buf[DRAW_STRING_BUFFER]; - va_list va; - - va_start(va, message); - vseprintf(buf, lastof(buf), message, va); - va_end(va); - - Utf8TrimString(buf, DRAW_STRING_BUFFER); - if (_chatmsg_list.size() == MAX_CHAT_MESSAGES) { _chatmsg_list.pop_back(); } ChatMessage *cmsg = &_chatmsg_list.emplace_front(); - strecpy(cmsg->message, buf, lastof(cmsg->message)); + cmsg->message = message; cmsg->colour = (colour & TC_IS_PALETTE_COLOUR) ? colour : TC_WHITE; cmsg->remove_time = std::chrono::steady_clock::now() + std::chrono::seconds(duration); diff --git a/src/network/network_func.h b/src/network/network_func.h index ef81b0fb16..e7709d3075 100644 --- a/src/network/network_func.h +++ b/src/network/network_func.h @@ -84,7 +84,7 @@ uint NetworkServerKickOrBanIP(ClientID client_id, bool ban, const char *reason); uint NetworkServerKickOrBanIP(const char *ip, bool ban, const char *reason); void NetworkInitChatMessage(); -void CDECL NetworkAddChatMessage(TextColour colour, uint duration, const char *message, ...) WARN_FORMAT(3, 4); +void CDECL NetworkAddChatMessage(TextColour colour, uint duration, const std::string &message); void NetworkUndrawChatMessage(); void NetworkChatMessageLoop(); From 8321ef0061fa898cfb215e57c67aebd7411475e1 Mon Sep 17 00:00:00 2001 From: Peter Nelson Date: Thu, 6 May 2021 01:36:56 +0100 Subject: [PATCH 05/61] Codechange: Set specific widgets dirty instead of window. --- src/misc_gui.cpp | 2 +- src/music_gui.cpp | 2 +- src/settings_gui.cpp | 9 +++++---- src/window.cpp | 2 +- 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/misc_gui.cpp b/src/misc_gui.cpp index e9ee7b193b..a40bf50139 100644 --- a/src/misc_gui.cpp +++ b/src/misc_gui.cpp @@ -557,7 +557,7 @@ struct AboutWindow : public Window { if (this->text_position < (int)(this->GetWidget(WID_A_SCROLLING_TEXT)->pos_y - lengthof(_credits) * this->line_height)) { this->text_position = this->GetWidget(WID_A_SCROLLING_TEXT)->pos_y + this->GetWidget(WID_A_SCROLLING_TEXT)->current_y; } - this->SetDirty(); + this->SetWidgetDirty(WID_A_SCROLLING_TEXT); } } }; diff --git a/src/music_gui.cpp b/src/music_gui.cpp index 16245a0b24..80d55eb4ba 100644 --- a/src/music_gui.cpp +++ b/src/music_gui.cpp @@ -789,7 +789,7 @@ struct MusicWindow : public Window { byte &vol = (widget == WID_M_MUSIC_VOL) ? _settings_client.music.music_vol : _settings_client.music.effect_vol; if (ClickVolumeSliderWidget(this->GetWidget(widget)->GetCurrentRect(), pt, vol)) { if (widget == WID_M_MUSIC_VOL) MusicDriver::GetInstance()->SetVolume(vol); - this->SetDirty(); + this->SetWidgetDirty(widget); SetWindowClassesDirty(WC_GAME_OPTIONS); } diff --git a/src/settings_gui.cpp b/src/settings_gui.cpp index 9935d596c0..15c6a887e9 100644 --- a/src/settings_gui.cpp +++ b/src/settings_gui.cpp @@ -446,17 +446,18 @@ struct GameOptionsWindow : Window { ShowErrorMessage(STR_ERROR_FULLSCREEN_FAILED, INVALID_STRING_ID, WL_ERROR); } this->SetWidgetLoweredState(WID_GO_FULLSCREEN_BUTTON, _fullscreen); - this->SetDirty(); + this->SetWidgetDirty(WID_GO_FULLSCREEN_BUTTON); break; case WID_GO_VIDEO_ACCEL_BUTTON: _video_hw_accel = !_video_hw_accel; ShowErrorMessage(STR_GAME_OPTIONS_VIDEO_ACCELERATION_RESTART, INVALID_STRING_ID, WL_INFO); this->SetWidgetLoweredState(WID_GO_VIDEO_ACCEL_BUTTON, _video_hw_accel); + this->SetWidgetDirty(WID_GO_VIDEO_ACCEL_BUTTON); #ifndef __APPLE__ this->SetWidgetDisabledState(WID_GO_VIDEO_VSYNC_BUTTON, !_video_hw_accel); + this->SetWidgetDirty(WID_GO_VIDEO_VSYNC_BUTTON); #endif - this->SetDirty(); break; case WID_GO_VIDEO_VSYNC_BUTTON: @@ -466,7 +467,7 @@ struct GameOptionsWindow : Window { VideoDriver::GetInstance()->ToggleVsync(_video_vsync); this->SetWidgetLoweredState(WID_GO_VIDEO_VSYNC_BUTTON, _video_vsync); - this->SetDirty(); + this->SetWidgetDirty(WID_GO_VIDEO_VSYNC_BUTTON); break; case WID_GO_BASE_SFX_VOLUME: @@ -474,7 +475,7 @@ struct GameOptionsWindow : Window { byte &vol = (widget == WID_GO_BASE_MUSIC_VOLUME) ? _settings_client.music.music_vol : _settings_client.music.effect_vol; if (ClickVolumeSliderWidget(this->GetWidget(widget)->GetCurrentRect(), pt, vol)) { if (widget == WID_GO_BASE_MUSIC_VOLUME) MusicDriver::GetInstance()->SetVolume(vol); - this->SetDirty(); + this->SetWidgetDirty(widget); SetWindowClassesDirty(WC_MUSIC_WINDOW); } diff --git a/src/window.cpp b/src/window.cpp index a409726002..20c7ffbddf 100644 --- a/src/window.cpp +++ b/src/window.cpp @@ -2438,8 +2438,8 @@ static EventState HandleActiveWidget() if (w->mouse_capture_widget >= 0) { /* Abort if no button is clicked any more. */ if (!_left_button_down) { + w->SetWidgetDirty(w->mouse_capture_widget); w->mouse_capture_widget = -1; - w->SetDirty(); return ES_HANDLED; } From 52b16237ad863ce30746ac7efc46a9ece7e3bdd2 Mon Sep 17 00:00:00 2001 From: Peter Nelson Date: Fri, 7 May 2021 12:36:08 +0100 Subject: [PATCH 06/61] Codechange: Don't update window contents if scrollbar position has not moved. --- src/widget.cpp | 17 ++++++++++++----- src/widget_type.h | 12 ++++++++---- src/window.cpp | 9 +++------ 3 files changed, 23 insertions(+), 15 deletions(-) diff --git a/src/widget.cpp b/src/widget.cpp index 5c1c0ae8fc..93a5afe953 100644 --- a/src/widget.cpp +++ b/src/widget.cpp @@ -108,6 +108,7 @@ static void ScrollbarClickPositioning(Window *w, NWidgetScrollbar *sb, int x, in int pos; int button_size; bool rtl = false; + bool changed = false; if (sb->type == NWID_HSCROLLBAR) { pos = x; @@ -122,7 +123,7 @@ static void ScrollbarClickPositioning(Window *w, NWidgetScrollbar *sb, int x, in SetBit(sb->disp_flags, NDB_SCROLLBAR_UP); if (_scroller_click_timeout <= 1) { _scroller_click_timeout = 3; - sb->UpdatePosition(rtl ? 1 : -1); + changed = sb->UpdatePosition(rtl ? 1 : -1); } w->mouse_capture_widget = sb->index; } else if (pos >= ma - button_size) { @@ -131,16 +132,16 @@ static void ScrollbarClickPositioning(Window *w, NWidgetScrollbar *sb, int x, in if (_scroller_click_timeout <= 1) { _scroller_click_timeout = 3; - sb->UpdatePosition(rtl ? -1 : 1); + changed = sb->UpdatePosition(rtl ? -1 : 1); } w->mouse_capture_widget = sb->index; } else { Point pt = HandleScrollbarHittest(sb, mi, ma, sb->type == NWID_HSCROLLBAR); if (pos < pt.x) { - sb->UpdatePosition(rtl ? 1 : -1, Scrollbar::SS_BIG); + changed = sb->UpdatePosition(rtl ? 1 : -1, Scrollbar::SS_BIG); } else if (pos > pt.y) { - sb->UpdatePosition(rtl ? -1 : 1, Scrollbar::SS_BIG); + changed = sb->UpdatePosition(rtl ? -1 : 1, Scrollbar::SS_BIG); } else { _scrollbar_start_pos = pt.x - mi - button_size; _scrollbar_size = ma - mi - button_size * 2; @@ -149,7 +150,13 @@ static void ScrollbarClickPositioning(Window *w, NWidgetScrollbar *sb, int x, in } } - w->SetDirty(); + if (changed) { + /* Position changed so refresh the window */ + w->SetDirty(); + } else { + /* No change so only refresh this scrollbar */ + sb->SetDirty(w); + } } /** diff --git a/src/widget_type.h b/src/widget_type.h index 2007113565..c50a263d68 100644 --- a/src/widget_type.h +++ b/src/widget_type.h @@ -737,12 +737,15 @@ public: /** * Sets the position of the first visible element * @param position the position of the element + * @return true iff the position has changed */ - void SetPosition(int position) + bool SetPosition(int position) { assert(position >= 0); assert(this->count <= this->cap ? (position == 0) : (position + this->cap <= this->count)); + uint16 old_pos = this->pos; this->pos = position; + return this->pos != old_pos; } /** @@ -750,16 +753,17 @@ public: * If the position would be too low or high it will be clamped appropriately * @param difference the amount of change requested * @param unit The stepping unit of \a difference + * @return true iff the position has changed */ - void UpdatePosition(int difference, ScrollbarStepping unit = SS_SMALL) + bool UpdatePosition(int difference, ScrollbarStepping unit = SS_SMALL) { - if (difference == 0) return; + if (difference == 0) return false; switch (unit) { case SS_SMALL: difference *= this->stepsize; break; case SS_BIG: difference *= this->cap; break; default: break; } - this->SetPosition(Clamp(this->pos + difference, 0, std::max(this->count - this->cap, 0))); + return this->SetPosition(Clamp(this->pos + difference, 0, std::max(this->count - this->cap, 0))); } /** diff --git a/src/window.cpp b/src/window.cpp index 20c7ffbddf..d69a34144e 100644 --- a/src/window.cpp +++ b/src/window.cpp @@ -849,8 +849,7 @@ static void DispatchMouseWheelEvent(Window *w, NWidgetCore *nwid, int wheel) if (nwid->type == NWID_VSCROLLBAR) { NWidgetScrollbar *sb = static_cast(nwid); if (sb->GetCount() > sb->GetCapacity()) { - sb->UpdatePosition(wheel); - w->SetDirty(); + if (sb->UpdatePosition(wheel)) w->SetDirty(); } return; } @@ -858,8 +857,7 @@ static void DispatchMouseWheelEvent(Window *w, NWidgetCore *nwid, int wheel) /* Scroll the widget attached to the scrollbar. */ Scrollbar *sb = (nwid->scrollbar_index >= 0 ? w->GetScrollbar(nwid->scrollbar_index) : nullptr); if (sb != nullptr && sb->GetCount() > sb->GetCapacity()) { - sb->UpdatePosition(wheel); - w->SetDirty(); + if (sb->UpdatePosition(wheel)) w->SetDirty(); } } @@ -2413,8 +2411,7 @@ static void HandleScrollbarScrolling(Window *w) if (sb->disp_flags & ND_SCROLLBAR_BTN) { if (_scroller_click_timeout == 1) { _scroller_click_timeout = 3; - sb->UpdatePosition(rtl == HasBit(sb->disp_flags, NDB_SCROLLBAR_UP) ? 1 : -1); - w->SetDirty(); + if (sb->UpdatePosition(rtl == HasBit(sb->disp_flags, NDB_SCROLLBAR_UP) ? 1 : -1)) w->SetDirty(); } return; } From d8e06e590aff56c837b29ddbba3764292ea08d85 Mon Sep 17 00:00:00 2001 From: Peter Nelson Date: Tue, 4 May 2021 00:24:14 +0100 Subject: [PATCH 07/61] Codechange: Make GetCurrentRect() conform to usual Rect bounds, and reuse it. Similar code is already repeated in other locations. --- src/station_gui.cpp | 4 ++-- src/widget.cpp | 18 +++--------------- src/widget_type.h | 4 ++-- src/widgets/dropdown.cpp | 6 +----- src/window.cpp | 5 +---- 5 files changed, 9 insertions(+), 28 deletions(-) diff --git a/src/station_gui.cpp b/src/station_gui.cpp index 98b44c6f98..cf3f455bc9 100644 --- a/src/station_gui.cpp +++ b/src/station_gui.cpp @@ -1440,7 +1440,7 @@ struct StationViewWindow : public Window { if (!this->IsShaded()) { /* Draw 'accepted cargo' or 'cargo ratings'. */ const NWidgetBase *wid = this->GetWidget(WID_SV_ACCEPT_RATING_LIST); - const Rect r = {(int)wid->pos_x, (int)wid->pos_y, (int)(wid->pos_x + wid->current_x - 1), (int)(wid->pos_y + wid->current_y - 1)}; + const Rect r = wid->GetCurrentRect(); if (this->GetWidget(WID_SV_ACCEPTS_RATINGS)->widget_data == STR_STATION_VIEW_RATINGS_BUTTON) { int lines = this->DrawAcceptedCargo(r); if (lines > this->accepts_lines) { // Resize the widget, and perform re-initialization of the window. @@ -1468,7 +1468,7 @@ struct StationViewWindow : public Window { /* Draw waiting cargo. */ NWidgetBase *nwi = this->GetWidget(WID_SV_WAITING); - Rect waiting_rect = { (int)nwi->pos_x, (int)nwi->pos_y, (int)(nwi->pos_x + nwi->current_x - 1), (int)(nwi->pos_y + nwi->current_y - 1)}; + Rect waiting_rect = nwi->GetCurrentRect(); this->DrawEntries(&cargo, waiting_rect, pos, maxrows, 0); scroll_to_row = INT_MAX; } diff --git a/src/widget.cpp b/src/widget.cpp index 93a5afe953..aaa00d4d79 100644 --- a/src/widget.cpp +++ b/src/widget.cpp @@ -1964,11 +1964,7 @@ void NWidgetBackground::Draw(const Window *w) { if (this->current_x == 0 || this->current_y == 0) return; - Rect r; - r.left = this->pos_x; - r.right = this->pos_x + this->current_x - 1; - r.top = this->pos_y; - r.bottom = this->pos_y + this->current_y - 1; + Rect r = this->GetCurrentRect(); const DrawPixelInfo *dpi = _cur_dpi; if (dpi->left > r.right || dpi->left + dpi->width <= r.left || dpi->top > r.bottom || dpi->top + dpi->height <= r.top) return; @@ -2232,11 +2228,7 @@ void NWidgetScrollbar::Draw(const Window *w) { if (this->current_x == 0 || this->current_y == 0) return; - Rect r; - r.left = this->pos_x; - r.right = this->pos_x + this->current_x - 1; - r.top = this->pos_y; - r.bottom = this->pos_y + this->current_y - 1; + Rect r = this->GetCurrentRect(); const DrawPixelInfo *dpi = _cur_dpi; if (dpi->left > r.right || dpi->left + dpi->width <= r.left || dpi->top > r.bottom || dpi->top + dpi->height <= r.top) return; @@ -2615,11 +2607,7 @@ void NWidgetLeaf::Draw(const Window *w) DrawPixelInfo *old_dpi = _cur_dpi; _cur_dpi = &new_dpi; - Rect r; - r.left = this->pos_x; - r.right = this->pos_x + this->current_x - 1; - r.top = this->pos_y; - r.bottom = this->pos_y + this->current_y - 1; + Rect r = this->GetCurrentRect(); bool clicked = this->IsLowered(); switch (this->type) { diff --git a/src/widget_type.h b/src/widget_type.h index c50a263d68..a5bd988492 100644 --- a/src/widget_type.h +++ b/src/widget_type.h @@ -167,8 +167,8 @@ public: Rect r; r.left = this->pos_x; r.top = this->pos_y; - r.right = this->pos_x + this->current_x; - r.bottom = this->pos_y + this->current_y; + r.right = this->pos_x + this->current_x - 1; + r.bottom = this->pos_y + this->current_y - 1; return r; } diff --git a/src/widgets/dropdown.cpp b/src/widgets/dropdown.cpp index 4b89b48092..b6f7de3c18 100644 --- a/src/widgets/dropdown.cpp +++ b/src/widgets/dropdown.cpp @@ -448,12 +448,8 @@ void ShowDropDownList(Window *w, DropDownList &&list, int selected, int button, { /* Our parent's button widget is used to determine where to place the drop * down list window. */ - Rect wi_rect; NWidgetCore *nwi = w->GetWidget(button); - wi_rect.left = nwi->pos_x; - wi_rect.right = nwi->pos_x + nwi->current_x - 1; - wi_rect.top = nwi->pos_y; - wi_rect.bottom = nwi->pos_y + nwi->current_y - 1; + Rect wi_rect = nwi->GetCurrentRect(); Colours wi_colour = nwi->colour; if ((nwi->type & WWT_MASK) == NWID_BUTTON_DROPDOWN) { diff --git a/src/window.cpp b/src/window.cpp index d69a34144e..3fcda7d42d 100644 --- a/src/window.cpp +++ b/src/window.cpp @@ -2097,10 +2097,7 @@ static void EnsureVisibleCaption(Window *w, int nx, int ny) Rect caption_rect; const NWidgetBase *caption = w->nested_root->GetWidgetOfType(WWT_CAPTION); if (caption != nullptr) { - caption_rect.left = caption->pos_x; - caption_rect.right = caption->pos_x + caption->current_x; - caption_rect.top = caption->pos_y; - caption_rect.bottom = caption->pos_y + caption->current_y; + caption_rect = caption->GetCurrentRect(); /* Make sure the window doesn't leave the screen */ nx = Clamp(nx, MIN_VISIBLE_TITLE_BAR - caption_rect.right, _screen.width - MIN_VISIBLE_TITLE_BAR - caption_rect.left); From dd41de89315156cf7c5e43872de0863d4c539a95 Mon Sep 17 00:00:00 2001 From: Peter Nelson Date: Fri, 7 May 2021 22:33:48 +0100 Subject: [PATCH 08/61] Codechange: Slider widget used different range for drawing vs setting. Using the same range for setting means that no workaround for setting the extremes is necessary. --- src/widgets/slider.cpp | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/widgets/slider.cpp b/src/widgets/slider.cpp index 6d6d732884..67c4c372d2 100644 --- a/src/widgets/slider.cpp +++ b/src/widgets/slider.cpp @@ -16,6 +16,7 @@ #include "../safeguards.h" +static const int SLIDER_WIDTH = 3; /** * Draw a volume slider widget with know at given value @@ -24,8 +25,6 @@ */ void DrawVolumeSliderWidget(Rect r, byte value) { - static const int slider_width = 3; - /* Draw a wedge indicating low to high volume level. */ const int ha = (r.bottom - r.top) / 5; int wx1 = r.left, wx2 = r.right; @@ -40,7 +39,7 @@ void DrawVolumeSliderWidget(Rect r, byte value) GfxDrawLine(wedge[0].x, wedge[0].y, wedge[1].x, wedge[1].y, shadow); /* Draw a slider handle indicating current volume level. */ - const int sw = ScaleGUITrad(slider_width); + const int sw = ScaleGUITrad(SLIDER_WIDTH); if (_current_text_dir == TD_RTL) value = 127 - value; const int x = r.left + (value * (r.right - r.left - sw) / 127); DrawFrameRect(x, r.top, x + sw, r.bottom, COLOUR_GREY, FR_NONE); @@ -55,12 +54,10 @@ void DrawVolumeSliderWidget(Rect r, byte value) */ bool ClickVolumeSliderWidget(Rect r, Point pt, byte &value) { - byte new_vol = Clamp((pt.x - r.left) * 127 / (r.right - r.left), 0, 127); + const int sw = ScaleGUITrad(SLIDER_WIDTH); + byte new_vol = Clamp((pt.x - r.left - sw / 2) * 127 / (r.right - r.left - sw), 0, 127); if (_current_text_dir == TD_RTL) new_vol = 127 - new_vol; - /* Clamp to make sure min and max are properly settable */ - if (new_vol > 124) new_vol = 127; - if (new_vol < 3) new_vol = 0; if (new_vol != value) { value = new_vol; return true; From 8c3fa2a3bf079424529a49b58f0466e4285d5874 Mon Sep 17 00:00:00 2001 From: PeterN Date: Sat, 8 May 2021 09:54:32 +0100 Subject: [PATCH 09/61] Fix #9209: Excessive time resizing highscore/news window when screen is too small. (#9210) If the highscore/news window panel size, which is now scaled by GUI zoom, is larger than the screen size, a loop will be entered where the window is repeatedly resized. This is resolved by removing the minimal size from the panel, as the window is always resized to cover the screen anyway. This means the screen size can never be too small. --- src/highscore_gui.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/highscore_gui.cpp b/src/highscore_gui.cpp index b1c428a768..356235fa03 100644 --- a/src/highscore_gui.cpp +++ b/src/highscore_gui.cpp @@ -205,7 +205,7 @@ struct HighScoreWindow : EndGameHighScoreBaseWindow { }; static const NWidgetPart _nested_highscore_widgets[] = { - NWidget(WWT_PANEL, COLOUR_BROWN, WID_H_BACKGROUND), SetMinimalSize(641, 481), SetResize(1, 1), EndContainer(), + NWidget(WWT_PANEL, COLOUR_BROWN, WID_H_BACKGROUND), SetResize(1, 1), EndContainer(), }; static WindowDesc _highscore_desc( From f187708b3b6bcb95a0d92908f8a90c1431ca2578 Mon Sep 17 00:00:00 2001 From: Patric Stout Date: Sat, 8 May 2021 11:57:41 +0200 Subject: [PATCH 10/61] Fix f7e390bd: getpeername() doesn't work on closed sockets (#9213) --- src/network/core/tcp.h | 4 +++- src/network/core/tcp_connect.cpp | 12 ++++++++++-- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/network/core/tcp.h b/src/network/core/tcp.h index b90ce0232c..624555649d 100644 --- a/src/network/core/tcp.h +++ b/src/network/core/tcp.h @@ -15,8 +15,9 @@ #include "address.h" #include "packet.h" -#include #include +#include +#include /** The states of sending the packets. */ enum SendPacketsState { @@ -66,6 +67,7 @@ class TCPConnecter { private: addrinfo *ai = nullptr; ///< getaddrinfo() allocated linked-list of resolved addresses. std::vector addresses; ///< Addresses we can connect to. + std::map sock_to_address; ///< Mapping of a socket to the real address it is connecting to. USed for DEBUG statements. size_t current_address = 0; ///< Current index in addresses we are trying. std::vector sockets; ///< Pending connect() attempts. diff --git a/src/network/core/tcp_connect.cpp b/src/network/core/tcp_connect.cpp index cca9f09b71..a1e369295a 100644 --- a/src/network/core/tcp_connect.cpp +++ b/src/network/core/tcp_connect.cpp @@ -40,8 +40,10 @@ TCPConnecter::TCPConnecter(const std::string &connection_string, uint16 default_ TCPConnecter::~TCPConnecter() { for (const auto &socket : this->sockets) { - close(socket); + closesocket(socket); } + this->sockets.clear(); + this->sock_to_address.clear(); freeaddrinfo(this->ai); } @@ -72,6 +74,7 @@ void TCPConnecter::Connect(addrinfo *address) return; } + this->sock_to_address[sock] = network_address; this->sockets.push_back(sock); } @@ -245,6 +248,9 @@ bool TCPConnecter::CheckActivity() for (const auto &socket : this->sockets) { closesocket(socket); } + this->sockets.clear(); + this->sock_to_address.clear(); + this->OnFailure(); return true; } @@ -253,8 +259,9 @@ bool TCPConnecter::CheckActivity() for (auto it = this->sockets.begin(); it != this->sockets.end(); /* nothing */) { NetworkError socket_error = GetSocketError(*it); if (socket_error.HasError()) { - DEBUG(net, 1, "Could not connect to %s: %s", NetworkAddress::GetPeerName(*it).c_str(), socket_error.AsString()); + DEBUG(net, 1, "Could not connect to %s: %s", this->sock_to_address[*it].GetAddressAsString().c_str(), socket_error.AsString()); closesocket(*it); + this->sock_to_address.erase(*it); it = this->sockets.erase(it); } else { it++; @@ -280,6 +287,7 @@ bool TCPConnecter::CheckActivity() } else { closesocket(*it); } + this->sock_to_address.erase(*it); it = this->sockets.erase(it); } assert(connected_socket != INVALID_SOCKET); From 881e1da51d47bde0b6ddf86be1da55aceced7839 Mon Sep 17 00:00:00 2001 From: William Davis Date: Sat, 8 May 2021 06:02:30 -0400 Subject: [PATCH 11/61] Change: Use gender-neutral pronouns in console command messages (and comments) (#9203) --- src/console_cmds.cpp | 8 ++++---- src/economy.cpp | 2 +- src/industry_cmd.cpp | 2 +- src/network/network_client.cpp | 8 ++++---- src/network/network_server.cpp | 8 ++++---- src/network/network_server.h | 2 +- src/openttd.cpp | 2 +- src/order_cmd.cpp | 2 +- src/roadveh_cmd.cpp | 2 +- src/saveload/ai_sl.cpp | 2 +- src/saveload/game_sl.cpp | 2 +- src/saveload/saveload.h | 2 +- src/script/api/script_companymode.hpp | 2 +- src/script/api/script_event_types.hpp | 2 +- src/script/api/script_execmode.hpp | 2 +- src/script/api/script_stationlist.hpp | 2 +- src/script/api/script_testmode.hpp | 2 +- src/script/squirrel_class.hpp | 4 ++-- src/town_cmd.cpp | 2 +- 19 files changed, 29 insertions(+), 29 deletions(-) diff --git a/src/console_cmds.cpp b/src/console_cmds.cpp index 99b3b54582..01c35ecf88 100644 --- a/src/console_cmds.cpp +++ b/src/console_cmds.cpp @@ -485,7 +485,7 @@ static bool ConKickOrBan(const char *argv, bool ban, const char *reason) * would be reading from and writing to after returning. So we would read or write data * from freed memory up till the segfault triggers. */ if (client_id == CLIENT_ID_SERVER || client_id == _redirect_console_to_client) { - IConsolePrintF(CC_ERROR, "ERROR: Silly boy, you can not %s yourself!", ban ? "ban" : "kick"); + IConsolePrintF(CC_ERROR, "ERROR: You can not %s yourself!", ban ? "ban" : "kick"); return true; } @@ -544,7 +544,7 @@ DEF_CONSOLE_CMD(ConBan) if (argc == 0) { IConsoleHelp("Ban a client from a network game. Usage: 'ban []'"); IConsoleHelp("For client-id's, see the command 'clients'"); - IConsoleHelp("If the client is no longer online, you can still ban his/her IP"); + IConsoleHelp("If the client is no longer online, you can still ban their IP"); return true; } @@ -806,12 +806,12 @@ DEF_CONSOLE_CMD(ConMoveClient) } if (ci->client_id == CLIENT_ID_SERVER && _network_dedicated) { - IConsoleError("Silly boy, you cannot move the server!"); + IConsoleError("You cannot move the server!"); return true; } if (ci->client_playas == company_id) { - IConsoleError("You cannot move someone to where he/she already is!"); + IConsoleError("You cannot move someone to where they already are!"); return true; } diff --git a/src/economy.cpp b/src/economy.cpp index 9982e79e02..ae289a51da 100644 --- a/src/economy.cpp +++ b/src/economy.cpp @@ -607,7 +607,7 @@ static void CompanyCheckBankrupt(Company *c) if (!_networking && _local_company == c->index) { /* If we are in singleplayer mode, leave the company playing. Eg. there * is no THE-END, otherwise mark the client as spectator to make sure - * he/she is no long in control of this company. However... when you + * they are no longer in control of this company. However... when you * join another company (cheat) the "unowned" company can bankrupt. */ c->bankrupt_asked = MAX_UVALUE(CompanyMask); break; diff --git a/src/industry_cmd.cpp b/src/industry_cmd.cpp index 37bbd915c3..f9e75ed880 100644 --- a/src/industry_cmd.cpp +++ b/src/industry_cmd.cpp @@ -2579,7 +2579,7 @@ static void CanCargoServiceIndustry(CargoID cargo, Industry *ind, bool *c_accept /** * Compute who can service the industry. * - * Here, 'can service' means that he/she has trains and stations close enough + * Here, 'can service' means that they have trains and stations close enough * to the industry with the right cargo type and the right orders (ie has the * technical means). * diff --git a/src/network/network_client.cpp b/src/network/network_client.cpp index 190bb29450..f827c57351 100644 --- a/src/network/network_client.cpp +++ b/src/network/network_client.cpp @@ -194,8 +194,8 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::CloseConnection(NetworkRecvSta */ void ClientNetworkGameSocketHandler::ClientError(NetworkRecvStatus res) { - /* First, send a CLIENT_ERROR to the server, so he knows we are - * disconnection (and why!) */ + /* First, send a CLIENT_ERROR to the server, so it knows we are + * disconnected (and why!) */ NetworkErrorCode errorno; /* We just want to close the connection.. */ @@ -293,7 +293,7 @@ void ClientNetworkGameSocketHandler::ClientError(NetworkRecvStatus res) /* If this is the first time we have a sync-frame, we * need to let the server know that we are ready and at the same - * frame as he is.. so we can start playing! */ + * frame as it is.. so we can start playing! */ if (_network_first_time) { _network_first_time = false; SendAck(); @@ -1399,7 +1399,7 @@ void NetworkClientSetCompanyPassword(const char *password) } /** - * Tell whether the client has team members where he/she can chat to. + * Tell whether the client has team members who they can chat to. * @param cio client to check members of. * @return true if there is at least one team member. */ diff --git a/src/network/network_server.cpp b/src/network/network_server.cpp index 1a9ddecfbc..e8266a84fc 100644 --- a/src/network/network_server.cpp +++ b/src/network/network_server.cpp @@ -1027,7 +1027,7 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::Receive_CLIENT_MAP_OK(Packet * InvalidateWindowData(WC_CLIENT_LIST, 0); /* Mark the client as pre-active, and wait for an ACK - * so we know he is done loading and in sync with us */ + * so we know it is done loading and in sync with us */ this->status = STATUS_PRE_ACTIVE; NetworkHandleCommandQueue(this); this->SendFrame(); @@ -1199,7 +1199,7 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::Receive_CLIENT_ACK(Packet *p) /* The client is not yet caught up? */ if (frame + DAY_TICKS < _frame_counter) return NETWORK_RECV_STATUS_OKAY; - /* Now he is! Unpause the game */ + /* Now it is! Unpause the game */ this->status = STATUS_ACTIVE; this->last_token_frame = _frame_counter; @@ -1855,14 +1855,14 @@ void NetworkServer_Tick(bool send_frame) case NetworkClientSocket::STATUS_MAP_WAIT: /* Send every two seconds a packet to the client, to make sure - * he knows the server is still there; just someone else is + * it knows the server is still there; just someone else is * still receiving the map. */ if (std::chrono::steady_clock::now() > cs->last_packet + std::chrono::seconds(2)) { cs->SendWait(); /* We need to reset the timer, as otherwise we will be * spamming the client. Strictly speaking this variable * tracks when we last received a packet from the client, - * but as he is waiting, he will not send us any till we + * but as it is waiting, it will not send us any till we * start sending him data. */ cs->last_packet = std::chrono::steady_clock::now(); } diff --git a/src/network/network_server.h b/src/network/network_server.h index e72377e824..fb4c4f63eb 100644 --- a/src/network/network_server.h +++ b/src/network/network_server.h @@ -72,7 +72,7 @@ public: size_t receive_limit; ///< Amount of bytes that we can receive at this moment struct PacketWriter *savegame; ///< Writer used to write the savegame. - NetworkAddress client_address; ///< IP-address of the client (so he can be banned) + NetworkAddress client_address; ///< IP-address of the client (so they can be banned) ServerNetworkGameSocketHandler(SOCKET s); ~ServerNetworkGameSocketHandler(); diff --git a/src/openttd.cpp b/src/openttd.cpp index d9e0b835e0..d8b197466a 100644 --- a/src/openttd.cpp +++ b/src/openttd.cpp @@ -652,7 +652,7 @@ int openttd_main(int argc, char *argv[]) } if (i == -2 || mgo.numleft > 0) { - /* Either the user typed '-h', he made an error, or he added unrecognized command line arguments. + /* Either the user typed '-h', they made an error, or they added unrecognized command line arguments. * In all cases, print the help, and exit. * * The next two functions are needed to list the graphics sets. We can't do them earlier diff --git a/src/order_cmd.cpp b/src/order_cmd.cpp index 082ec1ae33..c4793fa33b 100644 --- a/src/order_cmd.cpp +++ b/src/order_cmd.cpp @@ -660,7 +660,7 @@ static inline bool OrderGoesToStation(const Vehicle *v, const Order *o) * Delete all news items regarding defective orders about a vehicle * This could kill still valid warnings (for example about void order when just * another order gets added), but assume the company will notice the problems, - * when (s)he's changing the orders. + * when they're changing the orders. */ static void DeleteOrderWarnings(const Vehicle *v) { diff --git a/src/roadveh_cmd.cpp b/src/roadveh_cmd.cpp index 1a4e4e0852..d44a37c872 100644 --- a/src/roadveh_cmd.cpp +++ b/src/roadveh_cmd.cpp @@ -1257,7 +1257,7 @@ again: tile = v->tile; start_frame = RVC_TURN_AROUND_START_FRAME_SHORT_TRAM; } else { - /* The company can build on the next tile, so wait till (s)he does. */ + /* The company can build on the next tile, so wait till they do. */ v->cur_speed = 0; return false; } diff --git a/src/saveload/ai_sl.cpp b/src/saveload/ai_sl.cpp index 9b58aea11c..0ece819813 100644 --- a/src/saveload/ai_sl.cpp +++ b/src/saveload/ai_sl.cpp @@ -98,7 +98,7 @@ static void Load_AIPL() DEBUG(script, 0, "The savegame has an AI by the name '%s', version %d which is no longer available.", _ai_saveload_name, _ai_saveload_version); DEBUG(script, 0, "The latest version of that AI has been loaded instead, but it'll not get the savegame data as it's incompatible."); } - /* Make sure the AI doesn't get the saveload data, as he was not the + /* Make sure the AI doesn't get the saveload data, as it was not the * writer of the saveload data in the first place */ _ai_saveload_version = -1; } diff --git a/src/saveload/game_sl.cpp b/src/saveload/game_sl.cpp index e134843850..b570d1054d 100644 --- a/src/saveload/game_sl.cpp +++ b/src/saveload/game_sl.cpp @@ -90,7 +90,7 @@ static void Load_GSDT() DEBUG(script, 0, "The savegame has an GameScript by the name '%s', version %d which is no longer available.", _game_saveload_name, _game_saveload_version); DEBUG(script, 0, "The latest version of that GameScript has been loaded instead, but it'll not get the savegame data as it's incompatible."); } - /* Make sure the GameScript doesn't get the saveload data, as he was not the + /* Make sure the GameScript doesn't get the saveload data, as it was not the * writer of the saveload data in the first place */ _game_saveload_version = -1; } diff --git a/src/saveload/saveload.h b/src/saveload/saveload.h index cda6af82b6..28513c70c1 100644 --- a/src/saveload/saveload.h +++ b/src/saveload/saveload.h @@ -313,7 +313,7 @@ enum SaveLoadVersion : uint16 { * cannot digest. But, this gives for ugly errors. As we have plenty of * versions anyway, we simply skip the versions we know belong to * patchpacks. This way we can present the user with a clean error - * indicate he is loading a savegame from a patchpack. + * indicate they are loading a savegame from a patchpack. * For future patchpack creators: please follow a system like JGRPP, where * the version is masked with 0x8000, and the true version is stored in * its own chunk with feature toggles. diff --git a/src/script/api/script_companymode.hpp b/src/script/api/script_companymode.hpp index 4eba2dd9f7..e00cc7bfb0 100644 --- a/src/script/api/script_companymode.hpp +++ b/src/script/api/script_companymode.hpp @@ -37,7 +37,7 @@ public: * Creating instance of this class switches the company used for queries * and commands. * @param company The new company to switch to. - * @note When the instance is destroyed, he restores the company that was + * @note When the instance is destroyed, it restores the company that was * current when the instance was created! */ ScriptCompanyMode(int company); diff --git a/src/script/api/script_event_types.hpp b/src/script/api/script_event_types.hpp index f0487f48a0..1d514389b5 100644 --- a/src/script/api/script_event_types.hpp +++ b/src/script/api/script_event_types.hpp @@ -968,7 +968,7 @@ public: private: uint16 uniqueid; ///< The uniqueid of the question. ScriptCompany::CompanyID company; ///< The company given the answer. - ScriptGoal::QuestionButton button; ///< The button he pressed. + ScriptGoal::QuestionButton button; ///< The button that was pressed. }; /** diff --git a/src/script/api/script_execmode.hpp b/src/script/api/script_execmode.hpp index 8e4fd71578..f7ea55a1f6 100644 --- a/src/script/api/script_execmode.hpp +++ b/src/script/api/script_execmode.hpp @@ -34,7 +34,7 @@ protected: public: /** * Creating instance of this class switches the build mode to Execute. - * @note When the instance is destroyed, he restores the mode that was + * @note When the instance is destroyed, it restores the mode that was * current when the instance was created! */ ScriptExecMode(); diff --git a/src/script/api/script_stationlist.hpp b/src/script/api/script_stationlist.hpp index ee862cb935..8628dbb4a3 100644 --- a/src/script/api/script_stationlist.hpp +++ b/src/script/api/script_stationlist.hpp @@ -279,7 +279,7 @@ public: class ScriptStationList_Vehicle : public ScriptList { public: /** - * @param vehicle_id The vehicle to get the list of stations he has in its orders from. + * @param vehicle_id The vehicle to get the list of stations it has in its orders from. */ ScriptStationList_Vehicle(VehicleID vehicle_id); }; diff --git a/src/script/api/script_testmode.hpp b/src/script/api/script_testmode.hpp index eddd9340a7..956a25e2d6 100644 --- a/src/script/api/script_testmode.hpp +++ b/src/script/api/script_testmode.hpp @@ -36,7 +36,7 @@ protected: public: /** * Creating instance of this class switches the build mode to Testing. - * @note When the instance is destroyed, he restores the mode that was + * @note When the instance is destroyed, it restores the mode that was * current when the instance was created! */ ScriptTestMode(); diff --git a/src/script/squirrel_class.hpp b/src/script/squirrel_class.hpp index b600839d4d..8794e2441a 100644 --- a/src/script/squirrel_class.hpp +++ b/src/script/squirrel_class.hpp @@ -48,7 +48,7 @@ public: /** * This defines a method inside a class for Squirrel with defined params. - * @note If you define nparam, make sure that he first param is always 'x', + * @note If you define nparam, make sure that the first param is always 'x', * which is the 'this' inside the function. This is hidden from the rest * of the code, but without it calling your function will fail! */ @@ -81,7 +81,7 @@ public: /** * This defines a static method inside a class for Squirrel with defined params. - * @note If you define nparam, make sure that he first param is always 'x', + * @note If you define nparam, make sure that the first param is always 'x', * which is the 'this' inside the function. This is hidden from the rest * of the code, but without it calling your function will fail! */ diff --git a/src/town_cmd.cpp b/src/town_cmd.cpp index a0f5df23ed..01331a269e 100644 --- a/src/town_cmd.cpp +++ b/src/town_cmd.cpp @@ -3209,7 +3209,7 @@ static CommandCost TownActionFundBuildings(Town *t, DoCommandFlag flags) * than 1 house per 2 * TOWN_GROWTH_TICKS ticks. * Also emulate original behaviour when town was only growing in * TOWN_GROWTH_TICKS intervals, to make sure that it's not too - * tick-perfect and gives player some time window where he can + * tick-perfect and gives player some time window where they can * spam funding with the exact same efficiency. */ t->grow_counter = std::min(t->grow_counter, 2 * TOWN_GROWTH_TICKS - (t->growth_rate - t->grow_counter) % TOWN_GROWTH_TICKS); From 69e5da0b544e83ed445cf500938c24602230fa2c Mon Sep 17 00:00:00 2001 From: PeterN Date: Sat, 8 May 2021 11:36:39 +0100 Subject: [PATCH 12/61] Codechange: Don't save unused NewGRF override mappings. (#9202) --- src/newgrf_commons.h | 1 + src/saveload/newgrf_sl.cpp | 1 + 2 files changed, 2 insertions(+) diff --git a/src/newgrf_commons.h b/src/newgrf_commons.h index ed18aaa9e2..b86e3d10af 100644 --- a/src/newgrf_commons.h +++ b/src/newgrf_commons.h @@ -218,6 +218,7 @@ public: inline uint16 GetMaxMapping() const { return max_new_entities; } inline uint16 GetMaxOffset() const { return max_offset; } + inline bool IsValidID(uint16 entity_id) const { return entity_overrides[entity_id] != invalid_ID; } }; diff --git a/src/saveload/newgrf_sl.cpp b/src/saveload/newgrf_sl.cpp index b2d2b0df99..f7f6ab72e0 100644 --- a/src/saveload/newgrf_sl.cpp +++ b/src/saveload/newgrf_sl.cpp @@ -30,6 +30,7 @@ static const SaveLoad _newgrf_mapping_desc[] = { void Save_NewGRFMapping(const OverrideManagerBase &mapping) { for (uint i = 0; i < mapping.GetMaxMapping(); i++) { + if (!mapping.IsValidID(i)) continue; SlSetArrayIndex(i); SlObject(&mapping.mapping_ID[i], _newgrf_mapping_desc); } From 8e0b1b5d1a4cc8c72e662416a9de58066e9ceb37 Mon Sep 17 00:00:00 2001 From: Rubidium Date: Tue, 13 Apr 2021 21:36:01 +0200 Subject: [PATCH 13/61] Add: concept of a RandomAccessFile to replace the FIO slot functions --- src/CMakeLists.txt | 2 + src/random_access_file.cpp | 156 ++++++++++++++++++++++++++++++++++ src/random_access_file_type.h | 58 +++++++++++++ 3 files changed, 216 insertions(+) create mode 100644 src/random_access_file.cpp create mode 100644 src/random_access_file_type.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index e0af799994..2d4fffc8c7 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -309,6 +309,8 @@ add_files( rail_gui.h rail_map.h rail_type.h + random_access_file.cpp + random_access_file_type.h rev.h road.cpp road.h diff --git a/src/random_access_file.cpp b/src/random_access_file.cpp new file mode 100644 index 0000000000..2fd6a64b64 --- /dev/null +++ b/src/random_access_file.cpp @@ -0,0 +1,156 @@ +/* + * This file is part of OpenTTD. + * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. + * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see . + */ + + /** @file random_access_file.cpp Actual implementation of the RandomAccessFile class. */ + +#include "stdafx.h" +#include "random_access_file_type.h" + +#include "debug.h" +#include "fileio_func.h" +#include "string_func.h" + +#include "safeguards.h" + +/** + * Create the RandomAccesFile. + * @param filename Name of the file at the disk. + * @param subdir The sub directory to search this file in. + */ +RandomAccessFile::RandomAccessFile(const std::string &filename, Subdirectory subdir) : filename(filename) +{ + this->file_handle = FioFOpenFile(filename, "rb", subdir); + if (this->file_handle == nullptr) usererror("Cannot open file '%s'", filename.c_str()); + + /* When files are in a tar-file, the begin of the file might not be at 0. */ + long pos = ftell(this->file_handle); + if (pos < 0) usererror("Cannot read file '%s'", filename.c_str()); + + /* Store the filename without path and extension */ + auto t = filename.rfind(PATHSEPCHAR); + std::string name_without_path = filename.substr(t != std::string::npos ? t + 1 : 0); + this->simplified_filename = name_without_path.substr(0, name_without_path.rfind('.')); + strtolower(this->simplified_filename); + + this->SeekTo((size_t)pos, SEEK_SET); +} + +/** + * Close the file's file handle. + */ +RandomAccessFile::~RandomAccessFile() +{ + fclose(this->file_handle); +} + +/** + * Get the filename of the opened file with the path from the SubDirectory and the extension. + * @return Name of the file. + */ +const std::string& RandomAccessFile::GetFilename() const +{ + return this->filename; +} + +/** + * Get the simplified filename of the opened file. The simplified filename is the name of the + * file without the SubDirectory or extension in lower case. + * @return Name of the file. + */ +const std::string& RandomAccessFile::GetSimplifiedFilename() const +{ + return this->simplified_filename; +} + +/** + * Get position in the file. + * @return Position in the file. + */ +size_t RandomAccessFile::GetPos() const +{ + return this->pos + (this->buffer - this->buffer_end); +} + +/** + * Seek in the current file. + * @param pos New position. + * @param mode Type of seek (\c SEEK_CUR means \a pos is relative to current position, \c SEEK_SET means \a pos is absolute). + */ +void RandomAccessFile::SeekTo(size_t pos, int mode) +{ + if (mode == SEEK_CUR) pos += this->GetPos(); + + this->pos = pos; + if (fseek(this->file_handle, this->pos, SEEK_SET) < 0) { + DEBUG(misc, 0, "Seeking in %s failed", this->filename.c_str()); + } + + /* Reset the buffer, so the next ReadByte will read bytes from the file. */ + this->buffer = this->buffer_end = this->buffer_start; +} + +/** + * Read a byte from the file. + * @return Read byte. + */ +byte RandomAccessFile::ReadByte() +{ + if (this->buffer == this->buffer_end) { + this->buffer = this->buffer_start; + size_t size = fread(this->buffer, 1, RandomAccessFile::BUFFER_SIZE, this->file_handle); + this->pos += size; + this->buffer_end = this->buffer_start + size; + + if (size == 0) return 0; + } + return *this->buffer++; +} + +/** + * Read a word (16 bits) from the file (in low endian format). + * @return Read word. + */ +uint16 RandomAccessFile::ReadWord() +{ + byte b = this->ReadByte(); + return (this->ReadByte() << 8) | b; +} + +/** + * Read a double word (32 bits) from the file (in low endian format). + * @return Read word. + */ +uint32 RandomAccessFile::ReadDword() +{ + uint b = this->ReadWord(); + return (this->ReadWord() << 16) | b; +} + +/** + * Read a block. + * @param ptr Destination buffer. + * @param size Number of bytes to read. + */ +void RandomAccessFile::ReadBlock(void *ptr, size_t size) +{ + this->SeekTo(this->GetPos(), SEEK_SET); + this->pos += fread(ptr, 1, size, this->file_handle); +} + +/** + * Skip \a n bytes ahead in the file. + * @param n Number of bytes to skip reading. + */ +void RandomAccessFile::SkipBytes(int n) +{ + int remaining = this->buffer_end - this->buffer; + if (n <= remaining) { + this->buffer += n; + } else { + this->SeekTo(n, SEEK_CUR); + } +} diff --git a/src/random_access_file_type.h b/src/random_access_file_type.h new file mode 100644 index 0000000000..ce0e9e3584 --- /dev/null +++ b/src/random_access_file_type.h @@ -0,0 +1,58 @@ +/* + * This file is part of OpenTTD. + * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. + * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see . + */ + + /** @file random_access_file_type.h Class related to random access to files. */ + +#ifndef RANDOM_ACCESS_FILE_TYPE_H +#define RANDOM_ACCESS_FILE_TYPE_H + +#include "fileio_type.h" +#include + +/** + * A file from which bytes, words and double words are read in (potentially) a random order. + * + * This is mostly intended to be used for things that can be read from GRFs when needed, so + * the graphics but also the sounds. This also ties into the spritecache as it uses these + * files to load the sprites from when needed. + */ +class RandomAccessFile { + /** The number of bytes to allocate for the buffer. */ + static constexpr int BUFFER_SIZE = 512; + + std::string filename; ///< Full name of the file; relative path to subdir plus the extension of the file. + std::string simplified_filename; ///< Simplified lowecase name of the file; only the name, no path or extension. + + FILE *file_handle; ///< File handle of the open file. + size_t pos; ///< Position in the file of the end of the read buffer. + + byte *buffer; ///< Current position within the local buffer. + byte *buffer_end; ///< Last valid byte of buffer. + byte buffer_start[BUFFER_SIZE]; ///< Local buffer when read from file. + +public: + RandomAccessFile(const std::string &filename, Subdirectory subdir); + RandomAccessFile(const RandomAccessFile&) = delete; + void operator=(const RandomAccessFile&) = delete; + + virtual ~RandomAccessFile(); + + const std::string &GetFilename() const; + const std::string &GetSimplifiedFilename() const; + + size_t GetPos() const; + void SeekTo(size_t pos, int mode); + + byte ReadByte(); + uint16 ReadWord(); + uint32 ReadDword(); + + void ReadBlock(void *ptr, size_t size); + void SkipBytes(int n); +}; + +#endif /* RANDOM_ACCESS_FILE_TYPE_H */ From b144e56b2c4d92baa868435818a99faa8ed06439 Mon Sep 17 00:00:00 2001 From: Rubidium Date: Tue, 13 Apr 2021 21:36:24 +0200 Subject: [PATCH 14/61] Codechange: use the new RandomAccessFile as backend for the FIO slot functions --- src/fileio.cpp | 109 ++++++++----------------------------- src/random_access_file.cpp | 4 +- 2 files changed, 24 insertions(+), 89 deletions(-) diff --git a/src/fileio.cpp b/src/fileio.cpp index 810f3f34ef..465e713048 100644 --- a/src/fileio.cpp +++ b/src/fileio.cpp @@ -9,6 +9,7 @@ #include "stdafx.h" #include "fileio_func.h" +#include "random_access_file_type.h" #include "debug.h" #include "fios.h" #include "string_func.h" @@ -29,22 +30,8 @@ #include "safeguards.h" -/** Size of the #Fio data buffer. */ -#define FIO_BUFFER_SIZE 512 - -/** Structure for keeping several open files with just one data buffer. */ -struct Fio { - byte *buffer, *buffer_end; ///< position pointer in local buffer and last valid byte of buffer - byte buffer_start[FIO_BUFFER_SIZE]; ///< local buffer when read from file - size_t pos; ///< current (system) position in file - FILE *cur_fh; ///< current file handle - std::string filename; ///< current filename - std::array handles; ///< array of file handles we can have open - std::array filenames; ///< array of filenames we (should) have open - std::array shortnames;///< array of short names for spriteloader's use -}; - -static Fio _fio; ///< #Fio instance. +static RandomAccessFile *_fio_current_file; ///< current file handle for the Fio functions +static std::array _fio_files; ///< array of random access files we can have open /** Whether the working directory should be scanned. */ static bool _do_scan_working_directory = true; @@ -58,7 +45,7 @@ extern std::string _highscore_file; */ size_t FioGetPos() { - return _fio.pos + (_fio.buffer - _fio.buffer_end); + return _fio_current_file->GetPos(); } /** @@ -68,7 +55,7 @@ size_t FioGetPos() */ const char *FioGetFilename(uint8 slot) { - return _fio.shortnames[slot].c_str(); + return _fio_current_file->GetSimplifiedFilename().c_str(); } /** @@ -78,12 +65,7 @@ const char *FioGetFilename(uint8 slot) */ void FioSeekTo(size_t pos, int mode) { - if (mode == SEEK_CUR) pos += FioGetPos(); - _fio.buffer = _fio.buffer_end = _fio.buffer_start + FIO_BUFFER_SIZE; - _fio.pos = pos; - if (fseek(_fio.cur_fh, _fio.pos, SEEK_SET) < 0) { - DEBUG(misc, 0, "Seeking in %s failed", _fio.filename.c_str()); - } + _fio_current_file->SeekTo(pos, mode); } /** @@ -93,11 +75,10 @@ void FioSeekTo(size_t pos, int mode) */ void FioSeekToFile(uint8 slot, size_t pos) { - FILE *f = _fio.handles[slot]; - assert(f != nullptr); - _fio.cur_fh = f; - _fio.filename = _fio.filenames[slot]; - FioSeekTo(pos, SEEK_SET); + RandomAccessFile *raf = _fio_files[slot]; + assert(raf != nullptr); + _fio_current_file = raf; + _fio_current_file->SeekTo(pos, SEEK_SET); } /** @@ -106,15 +87,7 @@ void FioSeekToFile(uint8 slot, size_t pos) */ byte FioReadByte() { - if (_fio.buffer == _fio.buffer_end) { - _fio.buffer = _fio.buffer_start; - size_t size = fread(_fio.buffer, 1, FIO_BUFFER_SIZE, _fio.cur_fh); - _fio.pos += size; - _fio.buffer_end = _fio.buffer_start + size; - - if (size == 0) return 0; - } - return *_fio.buffer++; + return _fio_current_file->ReadByte(); } /** @@ -123,14 +96,7 @@ byte FioReadByte() */ void FioSkipBytes(int n) { - for (;;) { - int m = std::min(_fio.buffer_end - _fio.buffer, n); - _fio.buffer += m; - n -= m; - if (n == 0) break; - FioReadByte(); - n--; - } + return _fio_current_file->SkipBytes(n); } /** @@ -139,8 +105,7 @@ void FioSkipBytes(int n) */ uint16 FioReadWord() { - byte b = FioReadByte(); - return (FioReadByte() << 8) | b; + return _fio_current_file->ReadWord(); } /** @@ -149,8 +114,7 @@ uint16 FioReadWord() */ uint32 FioReadDword() { - uint b = FioReadWord(); - return (FioReadWord() << 16) | b; + return _fio_current_file->ReadDword(); } /** @@ -160,30 +124,15 @@ uint32 FioReadDword() */ void FioReadBlock(void *ptr, size_t size) { - FioSeekTo(FioGetPos(), SEEK_SET); - _fio.pos += fread(ptr, 1, size, _fio.cur_fh); -} - -/** - * Close the file at the given slot number. - * @param slot File index to close. - */ -static inline void FioCloseFile(int slot) -{ - if (_fio.handles[slot] != nullptr) { - fclose(_fio.handles[slot]); - - _fio.shortnames[slot].clear(); - - _fio.handles[slot] = nullptr; - } + _fio_current_file->ReadBlock(ptr, size); } /** Close all slotted open files. */ void FioCloseAll() { - for (int i = 0; i != lengthof(_fio.handles); i++) { - FioCloseFile(i); + for (int i = 0; i != lengthof(_fio_files); i++) { + delete _fio_files[i]; + _fio_files[i] = nullptr; } } @@ -195,24 +144,10 @@ void FioCloseAll() */ void FioOpenFile(int slot, const std::string &filename, Subdirectory subdir) { - FILE *f; - - f = FioFOpenFile(filename, "rb", subdir); - if (f == nullptr) usererror("Cannot open file '%s'", filename.c_str()); - long pos = ftell(f); - if (pos < 0) usererror("Cannot read file '%s'", filename.c_str()); - - FioCloseFile(slot); // if file was opened before, close it - _fio.handles[slot] = f; - _fio.filenames[slot] = filename; - - /* Store the filename without path and extension */ - auto t = filename.rfind(PATHSEPCHAR); - std::string sn = filename.substr(t != std::string::npos ? t + 1 : 0); - _fio.shortnames[slot] = sn.substr(0, sn.rfind('.')); - strtolower(_fio.shortnames[slot]); - - FioSeekToFile(slot, (size_t)pos); + RandomAccessFile *raf = new RandomAccessFile(filename, subdir); + delete _fio_files[slot]; + _fio_files[slot] = raf; + _fio_current_file = raf; } static const char * const _subdirs[] = { diff --git a/src/random_access_file.cpp b/src/random_access_file.cpp index 2fd6a64b64..72f2078377 100644 --- a/src/random_access_file.cpp +++ b/src/random_access_file.cpp @@ -51,7 +51,7 @@ RandomAccessFile::~RandomAccessFile() * Get the filename of the opened file with the path from the SubDirectory and the extension. * @return Name of the file. */ -const std::string& RandomAccessFile::GetFilename() const +const std::string &RandomAccessFile::GetFilename() const { return this->filename; } @@ -61,7 +61,7 @@ const std::string& RandomAccessFile::GetFilename() const * file without the SubDirectory or extension in lower case. * @return Name of the file. */ -const std::string& RandomAccessFile::GetSimplifiedFilename() const +const std::string &RandomAccessFile::GetSimplifiedFilename() const { return this->simplified_filename; } From c097bc9d7d610b613f76152b3cdcb9be2af7a1c7 Mon Sep 17 00:00:00 2001 From: Rubidium Date: Tue, 13 Apr 2021 21:58:56 +0200 Subject: [PATCH 15/61] Codechange: let NewGRF sounds make use of RandomAccessFile instead of the FIO slot functions --- src/fileio.cpp | 8 ++++++ src/fileio_func.h | 1 + src/fios.h | 2 -- src/newgrf.cpp | 2 +- src/newgrf_sound.cpp | 63 +++++++++++++++++++++--------------------- src/sound.cpp | 65 ++++++++++++++++++++++++-------------------- src/sound_type.h | 2 +- 7 files changed, 79 insertions(+), 64 deletions(-) diff --git a/src/fileio.cpp b/src/fileio.cpp index 465e713048..6b13e30890 100644 --- a/src/fileio.cpp +++ b/src/fileio.cpp @@ -39,6 +39,14 @@ static bool _do_scan_working_directory = true; extern std::string _config_file; extern std::string _highscore_file; +/** + * Transition helper to get the RandomAccessFile associated with a given slot. + */ +RandomAccessFile *FioGetRandomAccessFile(int slot) +{ + return _fio_files[slot]; +} + /** * Get position in the current file. * @return Position in the file. diff --git a/src/fileio_func.h b/src/fileio_func.h index 4b9b2dd9ee..55fac39efd 100644 --- a/src/fileio_func.h +++ b/src/fileio_func.h @@ -26,6 +26,7 @@ void FioCloseAll(); void FioOpenFile(int slot, const std::string &filename, Subdirectory subdir); void FioReadBlock(void *ptr, size_t size); void FioSkipBytes(int n); +class RandomAccessFile *FioGetRandomAccessFile(int slot); void FioFCloseFile(FILE *f); FILE *FioFOpenFile(const std::string &filename, const char *mode, Subdirectory subdir, size_t *filesize = nullptr); diff --git a/src/fios.h b/src/fios.h index 28a1cc6aa5..5029d4862f 100644 --- a/src/fios.h +++ b/src/fios.h @@ -91,8 +91,6 @@ enum FileSlots { * and thus cannot be used for files, which are continuously accessed during a game. */ CONFIG_SLOT = 0, - /** Slot for the sound. */ - SOUND_SLOT = 1, /** First slot usable for (New)GRFs used during the game. */ FIRST_GRF_SLOT = 2, /** Maximum number of slots. */ diff --git a/src/newgrf.cpp b/src/newgrf.cpp index 3a4fe12619..b51a18b437 100644 --- a/src/newgrf.cpp +++ b/src/newgrf.cpp @@ -7553,7 +7553,7 @@ static void LoadGRFSound(size_t offs, SoundEntry *sound) if (offs != SIZE_MAX) { /* Sound is present in the NewGRF. */ - sound->file_slot = _cur.file_index; + sound->file = FioGetRandomAccessFile(_cur.file_index); sound->file_offset = offs; sound->grf_container_ver = _cur.grf_container_ver; } diff --git a/src/newgrf_sound.cpp b/src/newgrf_sound.cpp index aead090016..523b210d1c 100644 --- a/src/newgrf_sound.cpp +++ b/src/newgrf_sound.cpp @@ -14,7 +14,7 @@ #include "newgrf_sound.h" #include "vehicle_base.h" #include "sound_func.h" -#include "fileio_func.h" +#include "random_access_file_type.h" #include "debug.h" #include "settings_type.h" @@ -65,56 +65,57 @@ uint GetNumSounds() */ bool LoadNewGRFSound(SoundEntry *sound) { - if (sound->file_offset == SIZE_MAX || sound->file_slot == 0) return false; + if (sound->file_offset == SIZE_MAX || sound->file == nullptr) return false; - FioSeekToFile(sound->file_slot, sound->file_offset); + RandomAccessFile &file = *sound->file; + file.SeekTo(sound->file_offset, SEEK_SET); /* Skip ID for container version >= 2 as we only look at the first * entry and ignore any further entries with the same ID. */ - if (sound->grf_container_ver >= 2) FioReadDword(); + if (sound->grf_container_ver >= 2) file.ReadDword(); /* Format: '\0' */ - uint32 num = sound->grf_container_ver >= 2 ? FioReadDword() : FioReadWord(); - if (FioReadByte() != 0xFF) return false; - if (FioReadByte() != 0xFF) return false; + uint32 num = sound->grf_container_ver >= 2 ? file.ReadDword() : file.ReadWord(); + if (file.ReadByte() != 0xFF) return false; + if (file.ReadByte() != 0xFF) return false; - uint8 name_len = FioReadByte(); + uint8 name_len = file.ReadByte(); char *name = AllocaM(char, name_len + 1); - FioReadBlock(name, name_len + 1); + file.ReadBlock(name, name_len + 1); /* Test string termination */ if (name[name_len] != 0) { - DEBUG(grf, 2, "LoadNewGRFSound [%s]: Name not properly terminated", FioGetFilename(sound->file_slot)); + DEBUG(grf, 2, "LoadNewGRFSound [%s]: Name not properly terminated", file.GetSimplifiedFilename().c_str()); return false; } - DEBUG(grf, 2, "LoadNewGRFSound [%s]: Sound name '%s'...", FioGetFilename(sound->file_slot), name); + DEBUG(grf, 2, "LoadNewGRFSound [%s]: Sound name '%s'...", file.GetSimplifiedFilename().c_str(), name); - if (FioReadDword() != BSWAP32('RIFF')) { - DEBUG(grf, 1, "LoadNewGRFSound [%s]: Missing RIFF header", FioGetFilename(sound->file_slot)); + if (file.ReadDword() != BSWAP32('RIFF')) { + DEBUG(grf, 1, "LoadNewGRFSound [%s]: Missing RIFF header", file.GetSimplifiedFilename().c_str()); return false; } - uint32 total_size = FioReadDword(); + uint32 total_size = file.ReadDword(); uint header_size = 11; if (sound->grf_container_ver >= 2) header_size++; // The first FF in the sprite is only counted for container version >= 2. if (total_size + name_len + header_size > num) { - DEBUG(grf, 1, "LoadNewGRFSound [%s]: RIFF was truncated", FioGetFilename(sound->file_slot)); + DEBUG(grf, 1, "LoadNewGRFSound [%s]: RIFF was truncated", file.GetSimplifiedFilename().c_str()); return false; } - if (FioReadDword() != BSWAP32('WAVE')) { - DEBUG(grf, 1, "LoadNewGRFSound [%s]: Invalid RIFF type", FioGetFilename(sound->file_slot)); + if (file.ReadDword() != BSWAP32('WAVE')) { + DEBUG(grf, 1, "LoadNewGRFSound [%s]: Invalid RIFF type", file.GetSimplifiedFilename().c_str()); return false; } while (total_size >= 8) { - uint32 tag = FioReadDword(); - uint32 size = FioReadDword(); + uint32 tag = file.ReadDword(); + uint32 size = file.ReadDword(); total_size -= 8; if (total_size < size) { - DEBUG(grf, 1, "LoadNewGRFSound [%s]: Invalid RIFF", FioGetFilename(sound->file_slot)); + DEBUG(grf, 1, "LoadNewGRFSound [%s]: Invalid RIFF", file.GetSimplifiedFilename().c_str()); return false; } total_size -= size; @@ -122,15 +123,15 @@ bool LoadNewGRFSound(SoundEntry *sound) switch (tag) { case ' tmf': // 'fmt ' /* Audio format, must be 1 (PCM) */ - if (size < 16 || FioReadWord() != 1) { - DEBUG(grf, 1, "LoadGRFSound [%s]: Invalid audio format", FioGetFilename(sound->file_slot)); + if (size < 16 || file.ReadWord() != 1) { + DEBUG(grf, 1, "LoadGRFSound [%s]: Invalid audio format", file.GetSimplifiedFilename().c_str()); return false; } - sound->channels = FioReadWord(); - sound->rate = FioReadDword(); - FioReadDword(); - FioReadWord(); - sound->bits_per_sample = FioReadWord(); + sound->channels = file.ReadWord(); + sound->rate = file.ReadDword(); + file.ReadDword(); + file.ReadWord(); + sound->bits_per_sample = file.ReadWord(); /* The rest will be skipped */ size -= 16; @@ -138,9 +139,9 @@ bool LoadNewGRFSound(SoundEntry *sound) case 'atad': // 'data' sound->file_size = size; - sound->file_offset = FioGetPos(); + sound->file_offset = file.GetPos(); - DEBUG(grf, 2, "LoadNewGRFSound [%s]: channels %u, sample rate %u, bits per sample %u, length %u", FioGetFilename(sound->file_slot), sound->channels, sound->rate, sound->bits_per_sample, size); + DEBUG(grf, 2, "LoadNewGRFSound [%s]: channels %u, sample rate %u, bits per sample %u, length %u", file.GetSimplifiedFilename().c_str(), sound->channels, sound->rate, sound->bits_per_sample, size); return true; // the fmt chunk has to appear before data, so we are finished default: @@ -149,10 +150,10 @@ bool LoadNewGRFSound(SoundEntry *sound) } /* Skip rest of chunk */ - if (size > 0) FioSkipBytes(size); + if (size > 0) file.SkipBytes(size); } - DEBUG(grf, 1, "LoadNewGRFSound [%s]: RIFF does not contain any sound data", FioGetFilename(sound->file_slot)); + DEBUG(grf, 1, "LoadNewGRFSound [%s]: RIFF does not contain any sound data", file.GetSimplifiedFilename().c_str()); /* Clear everything that was read */ MemSetT(sound, 0); diff --git a/src/sound.cpp b/src/sound.cpp index d91476729b..413c70af2a 100644 --- a/src/sound.cpp +++ b/src/sound.cpp @@ -11,7 +11,7 @@ #include "landscape.h" #include "mixer.h" #include "newgrf_sound.h" -#include "fios.h" +#include "random_access_file_type.h" #include "window_gui.h" #include "vehicle_base.h" @@ -25,14 +25,20 @@ static SoundEntry _original_sounds[ORIGINAL_SAMPLE_COUNT]; static void OpenBankFile(const char *filename) { + /** + * The sound file for the original sounds, i.e. those not defined/overridden by a NewGRF. + * Needs to be kept alive during the game as _original_sounds[n].file refers to this. + */ + static std::unique_ptr original_sound_file; + memset(_original_sounds, 0, sizeof(_original_sounds)); /* If there is no sound file (nosound set), don't load anything */ if (filename == nullptr) return; - FioOpenFile(SOUND_SLOT, filename, BASESET_DIR); - size_t pos = FioGetPos(); - uint count = FioReadDword(); + original_sound_file.reset(new RandomAccessFile(filename, BASESET_DIR)); + size_t pos = original_sound_file->GetPos(); + uint count = original_sound_file->ReadDword(); /* The new format has the highest bit always set */ bool new_format = HasBit(count, 31); @@ -48,43 +54,43 @@ static void OpenBankFile(const char *filename) return; } - FioSeekTo(pos, SEEK_SET); + original_sound_file->SeekTo(pos, SEEK_SET); for (uint i = 0; i != ORIGINAL_SAMPLE_COUNT; i++) { - _original_sounds[i].file_slot = SOUND_SLOT; - _original_sounds[i].file_offset = GB(FioReadDword(), 0, 31) + pos; - _original_sounds[i].file_size = FioReadDword(); + _original_sounds[i].file = original_sound_file.get(); + _original_sounds[i].file_offset = GB(original_sound_file->ReadDword(), 0, 31) + pos; + _original_sounds[i].file_size = original_sound_file->ReadDword(); } for (uint i = 0; i != ORIGINAL_SAMPLE_COUNT; i++) { SoundEntry *sound = &_original_sounds[i]; char name[255]; - FioSeekTo(sound->file_offset, SEEK_SET); + original_sound_file->SeekTo(sound->file_offset, SEEK_SET); /* Check for special case, see else case */ - FioReadBlock(name, FioReadByte()); // Read the name of the sound + original_sound_file->ReadBlock(name, original_sound_file->ReadByte()); // Read the name of the sound if (new_format || strcmp(name, "Corrupt sound") != 0) { - FioSeekTo(12, SEEK_CUR); // Skip past RIFF header + original_sound_file->SeekTo(12, SEEK_CUR); // Skip past RIFF header /* Read riff tags */ for (;;) { - uint32 tag = FioReadDword(); - uint32 size = FioReadDword(); + uint32 tag = original_sound_file->ReadDword(); + uint32 size = original_sound_file->ReadDword(); if (tag == ' tmf') { - FioReadWord(); // wFormatTag - sound->channels = FioReadWord(); // wChannels - sound->rate = FioReadDword(); // samples per second - if (!new_format) sound->rate = 11025; // seems like all old samples should be played at this rate. - FioReadDword(); // avg bytes per second - FioReadWord(); // alignment - sound->bits_per_sample = FioReadByte(); // bits per sample - FioSeekTo(size - (2 + 2 + 4 + 4 + 2 + 1), SEEK_CUR); + original_sound_file->ReadWord(); // wFormatTag + sound->channels = original_sound_file->ReadWord(); // wChannels + sound->rate = original_sound_file->ReadDword(); // samples per second + if (!new_format) sound->rate = 11025; // seems like all old samples should be played at this rate. + original_sound_file->ReadDword(); // avg bytes per second + original_sound_file->ReadWord(); // alignment + sound->bits_per_sample = original_sound_file->ReadByte(); // bits per sample + original_sound_file->SeekTo(size - (2 + 2 + 4 + 4 + 2 + 1), SEEK_CUR); } else if (tag == 'atad') { sound->file_size = size; - sound->file_slot = SOUND_SLOT; - sound->file_offset = FioGetPos(); + sound->file = original_sound_file.get(); + sound->file_offset = original_sound_file->GetPos(); break; } else { sound->file_size = 0; @@ -100,8 +106,8 @@ static void OpenBankFile(const char *filename) sound->channels = 1; sound->rate = 11025; sound->bits_per_sample = 8; - sound->file_slot = SOUND_SLOT; - sound->file_offset = FioGetPos(); + sound->file = original_sound_file.get(); + sound->file_offset = original_sound_file->GetPos(); } } } @@ -119,8 +125,9 @@ static bool SetBankSource(MixerChannel *mc, const SoundEntry *sound) mem[sound->file_size ] = 0; mem[sound->file_size + 1] = 0; - FioSeekToFile(sound->file_slot, sound->file_offset); - FioReadBlock(mem, sound->file_size); + RandomAccessFile *file = sound->file; + file->SeekTo(sound->file_offset, SEEK_SET); + file->ReadBlock(mem, sound->file_size); /* 16-bit PCM WAV files should be signed by default */ if (sound->bits_per_sample == 8) { @@ -163,10 +170,10 @@ static void StartSound(SoundID sound_id, float pan, uint volume) if (sound == nullptr) return; /* NewGRF sound that wasn't loaded yet? */ - if (sound->rate == 0 && sound->file_slot != 0) { + if (sound->rate == 0 && sound->file != nullptr) { if (!LoadNewGRFSound(sound)) { /* Mark as invalid. */ - sound->file_slot = 0; + sound->file = nullptr; return; } } diff --git a/src/sound_type.h b/src/sound_type.h index e8d4b7becb..02bfc4f835 100644 --- a/src/sound_type.h +++ b/src/sound_type.h @@ -11,7 +11,7 @@ #define SOUND_TYPE_H struct SoundEntry { - uint8 file_slot; + class RandomAccessFile *file; size_t file_offset; size_t file_size; uint16 rate; From 0dd339ecd8fab0ae3a4901e0ad185798e04cccf3 Mon Sep 17 00:00:00 2001 From: Rubidium Date: Wed, 14 Apr 2021 06:49:46 +0200 Subject: [PATCH 16/61] Codechange: lets music make use of RandomAccessFile instead of the FIO slot functions --- src/music.cpp | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/music.cpp b/src/music.cpp index 66ac2b511b..62a9482337 100644 --- a/src/music.cpp +++ b/src/music.cpp @@ -15,7 +15,7 @@ #include "base_media_func.h" #include "safeguards.h" -#include "fios.h" +#include "random_access_file_type.h" /** @@ -29,15 +29,15 @@ char *GetMusicCatEntryName(const char *filename, size_t entrynum) { if (!FioCheckFileExists(filename, BASESET_DIR)) return nullptr; - FioOpenFile(CONFIG_SLOT, filename, BASESET_DIR); - uint32 ofs = FioReadDword(); + RandomAccessFile file(filename, BASESET_DIR); + uint32 ofs = file.ReadDword(); size_t entry_count = ofs / 8; if (entrynum < entry_count) { - FioSeekTo(entrynum * 8, SEEK_SET); - FioSeekTo(FioReadDword(), SEEK_SET); - byte namelen = FioReadByte(); + file.SeekTo(entrynum * 8, SEEK_SET); + file.SeekTo(file.ReadDword(), SEEK_SET); + byte namelen = file.ReadByte(); char *name = MallocT(namelen + 1); - FioReadBlock(name, namelen); + file.ReadBlock(name, namelen); name[namelen] = '\0'; return name; } @@ -57,17 +57,17 @@ byte *GetMusicCatEntryData(const char *filename, size_t entrynum, size_t &entryl entrylen = 0; if (!FioCheckFileExists(filename, BASESET_DIR)) return nullptr; - FioOpenFile(CONFIG_SLOT, filename, BASESET_DIR); - uint32 ofs = FioReadDword(); + RandomAccessFile file(filename, BASESET_DIR); + uint32 ofs = file.ReadDword(); size_t entry_count = ofs / 8; if (entrynum < entry_count) { - FioSeekTo(entrynum * 8, SEEK_SET); - size_t entrypos = FioReadDword(); - entrylen = FioReadDword(); - FioSeekTo(entrypos, SEEK_SET); - FioSkipBytes(FioReadByte()); + file.SeekTo(entrynum * 8, SEEK_SET); + size_t entrypos = file.ReadDword(); + entrylen = file.ReadDword(); + file.SeekTo(entrypos, SEEK_SET); + file.SkipBytes(file.ReadByte()); byte *data = MallocT(entrylen); - FioReadBlock(data, entrylen); + file.ReadBlock(data, entrylen); return data; } return nullptr; From fdc11a9f943bcbd674b8b69a2bb0895414928028 Mon Sep 17 00:00:00 2001 From: Rubidium Date: Wed, 14 Apr 2021 17:20:39 +0200 Subject: [PATCH 17/61] Codechange: introduce SpriteFile to be used by the sprite loader instead of the global FIO slot functionality --- src/fileio.cpp | 26 ++--- src/fileio_func.h | 4 +- src/gfx_func.h | 2 - src/gfxinit.cpp | 42 ++++---- src/newgrf.cpp | 95 ++++++----------- src/newgrf.h | 2 - src/newgrf_debug_gui.cpp | 4 +- src/spritecache.cpp | 144 ++++++++++++++++---------- src/spritecache.h | 12 ++- src/spriteloader/CMakeLists.txt | 2 + src/spriteloader/grf.cpp | 103 +++++++++--------- src/spriteloader/grf.hpp | 2 +- src/spriteloader/sprite_file.cpp | 50 +++++++++ src/spriteloader/sprite_file_type.hpp | 46 ++++++++ src/spriteloader/spriteloader.hpp | 3 +- 15 files changed, 317 insertions(+), 220 deletions(-) create mode 100644 src/spriteloader/sprite_file.cpp create mode 100644 src/spriteloader/sprite_file_type.hpp diff --git a/src/fileio.cpp b/src/fileio.cpp index 6b13e30890..6d5e2da287 100644 --- a/src/fileio.cpp +++ b/src/fileio.cpp @@ -9,7 +9,7 @@ #include "stdafx.h" #include "fileio_func.h" -#include "random_access_file_type.h" +#include "spriteloader/spriteloader.hpp" #include "debug.h" #include "fios.h" #include "string_func.h" @@ -30,8 +30,8 @@ #include "safeguards.h" -static RandomAccessFile *_fio_current_file; ///< current file handle for the Fio functions -static std::array _fio_files; ///< array of random access files we can have open +static SpriteFile *_fio_current_file; ///< current file handle for the Fio functions +static std::array _fio_files; ///< array of random access files we can have open /** Whether the working directory should be scanned. */ static bool _do_scan_working_directory = true; @@ -40,9 +40,9 @@ extern std::string _config_file; extern std::string _highscore_file; /** - * Transition helper to get the RandomAccessFile associated with a given slot. + * Transition helper to get the SpriteFile associated with a given slot. */ -RandomAccessFile *FioGetRandomAccessFile(int slot) +SpriteFile *FioGetSpriteFile(int slot) { return _fio_files[slot]; } @@ -83,9 +83,9 @@ void FioSeekTo(size_t pos, int mode) */ void FioSeekToFile(uint8 slot, size_t pos) { - RandomAccessFile *raf = _fio_files[slot]; - assert(raf != nullptr); - _fio_current_file = raf; + SpriteFile *file = _fio_files[slot]; + assert(file != nullptr); + _fio_current_file = file; _fio_current_file->SeekTo(pos, SEEK_SET); } @@ -149,13 +149,15 @@ void FioCloseAll() * @param slot Index to assign. * @param filename Name of the file at the disk. * @param subdir The sub directory to search this file in. + * @param palette_remap Whether palette remapping needs to take place. */ -void FioOpenFile(int slot, const std::string &filename, Subdirectory subdir) +SpriteFile &FioOpenFile(int slot, const std::string &filename, Subdirectory subdir, bool palette_remap) { - RandomAccessFile *raf = new RandomAccessFile(filename, subdir); + SpriteFile *file = new SpriteFile(filename, subdir, palette_remap); delete _fio_files[slot]; - _fio_files[slot] = raf; - _fio_current_file = raf; + _fio_files[slot] = file; + _fio_current_file = file; + return *file; } static const char * const _subdirs[] = { diff --git a/src/fileio_func.h b/src/fileio_func.h index 55fac39efd..b3ed9e138d 100644 --- a/src/fileio_func.h +++ b/src/fileio_func.h @@ -23,10 +23,10 @@ byte FioReadByte(); uint16 FioReadWord(); uint32 FioReadDword(); void FioCloseAll(); -void FioOpenFile(int slot, const std::string &filename, Subdirectory subdir); +class SpriteFile &FioOpenFile(int slot, const std::string &filename, Subdirectory subdir, bool palette_remap = false); void FioReadBlock(void *ptr, size_t size); void FioSkipBytes(int n); -class RandomAccessFile *FioGetRandomAccessFile(int slot); +class SpriteFile *FioGetSpriteFile(int slot); void FioFCloseFile(FILE *f); FILE *FioFOpenFile(const std::string &filename, const char *mode, Subdirectory subdir, size_t *filesize = nullptr); diff --git a/src/gfx_func.h b/src/gfx_func.h index 3deb4f3eb6..6363a699d9 100644 --- a/src/gfx_func.h +++ b/src/gfx_func.h @@ -178,8 +178,6 @@ TextColour GetContrastColour(uint8 background, uint8 threshold = 128); */ extern byte _colour_gradient[COLOUR_END][8]; -extern bool _palette_remap_grf[]; - /** * Return the colour for a particular greyscale level. * @param level Intensity, 0 = black, 15 = white diff --git a/src/gfxinit.cpp b/src/gfxinit.cpp index c0ba4c4d4d..3bacc5af65 100644 --- a/src/gfxinit.cpp +++ b/src/gfxinit.cpp @@ -26,9 +26,6 @@ #include "safeguards.h" -/** Whether the given NewGRFs must get a palette remap from windows to DOS or not. */ -bool _palette_remap_grf[MAX_FILE_SLOTS]; - #include "table/landscape_sprite.h" /** Offsets for loading the different "replacement" sprites in the files. */ @@ -43,27 +40,28 @@ static const SpriteID * const _landscape_spriteindexes[] = { * @param filename The name of the file to open. * @param load_index The offset of the first sprite. * @param file_index The Fio offset to load the file in. + * @param needs_palette_remap Whether the colours in the GRF file need a palette remap. * @return The number of loaded sprites. */ -static uint LoadGrfFile(const char *filename, uint load_index, int file_index) +static uint LoadGrfFile(const char *filename, uint load_index, int file_index, bool needs_palette_remap) { uint load_index_org = load_index; uint sprite_id = 0; - FioOpenFile(file_index, filename, BASESET_DIR); + SpriteFile &file = FioOpenFile(file_index, filename, BASESET_DIR, needs_palette_remap); DEBUG(sprite, 2, "Reading grf-file '%s'", filename); - byte container_ver = GetGRFContainerVersion(); + byte container_ver = file.GetContainerVersion(); if (container_ver == 0) usererror("Base grf '%s' is corrupt", filename); - ReadGRFSpriteOffsets(container_ver); + ReadGRFSpriteOffsets(file); if (container_ver >= 2) { /* Read compression. */ - byte compression = FioReadByte(); + byte compression = file.ReadByte(); if (compression != 0) usererror("Unsupported compression format"); } - while (LoadNextSprite(load_index, file_index, sprite_id, container_ver)) { + while (LoadNextSprite(load_index, file, sprite_id)) { load_index++; sprite_id++; if (load_index >= MAX_SPRITES) { @@ -80,23 +78,24 @@ static uint LoadGrfFile(const char *filename, uint load_index, int file_index) * @param filename The name of the file to open. * @param index_tbl The offsets of each of the sprites. * @param file_index The Fio offset to load the file in. + * @param needs_palette_remap Whether the colours in the GRF file need a palette remap. * @return The number of loaded sprites. */ -static void LoadGrfFileIndexed(const char *filename, const SpriteID *index_tbl, int file_index) +static void LoadGrfFileIndexed(const char *filename, const SpriteID *index_tbl, int file_index, bool needs_palette_remap) { uint start; uint sprite_id = 0; - FioOpenFile(file_index, filename, BASESET_DIR); + SpriteFile &file = FioOpenFile(file_index, filename, BASESET_DIR, needs_palette_remap); DEBUG(sprite, 2, "Reading indexed grf-file '%s'", filename); - byte container_ver = GetGRFContainerVersion(); + byte container_ver = file.GetContainerVersion(); if (container_ver == 0) usererror("Base grf '%s' is corrupt", filename); - ReadGRFSpriteOffsets(container_ver); + ReadGRFSpriteOffsets(file); if (container_ver >= 2) { /* Read compression. */ - byte compression = FioReadByte(); + byte compression = file.ReadByte(); if (compression != 0) usererror("Unsupported compression format"); } @@ -104,7 +103,7 @@ static void LoadGrfFileIndexed(const char *filename, const SpriteID *index_tbl, uint end = *index_tbl++; do { - bool b = LoadNextSprite(start, file_index, sprite_id, container_ver); + bool b = LoadNextSprite(start, file, sprite_id); (void)b; // Unused without asserts assert(b); sprite_id++; @@ -162,12 +161,10 @@ void CheckExternalFiles() /** Actually load the sprite tables. */ static void LoadSpriteTables() { - memset(_palette_remap_grf, 0, sizeof(_palette_remap_grf)); uint i = FIRST_GRF_SLOT; const GraphicsSet *used_set = BaseGraphics::GetUsedSet(); - _palette_remap_grf[i] = (PAL_DOS != used_set->palette); - LoadGrfFile(used_set->files[GFT_BASE].filename, 0, i++); + LoadGrfFile(used_set->files[GFT_BASE].filename, 0, i++, (PAL_DOS != used_set->palette)); /* * The second basic file always starts at the given location and does @@ -175,8 +172,7 @@ static void LoadSpriteTables() * has a few sprites less. However, we do not care about those missing * sprites as they are not shown anyway (logos in intro game). */ - _palette_remap_grf[i] = (PAL_DOS != used_set->palette); - LoadGrfFile(used_set->files[GFT_LOGOS].filename, 4793, i++); + LoadGrfFile(used_set->files[GFT_LOGOS].filename, 4793, i++, (PAL_DOS != used_set->palette)); /* * Load additional sprites for climates other than temperate. @@ -184,11 +180,11 @@ static void LoadSpriteTables() * and the ground sprites. */ if (_settings_game.game_creation.landscape != LT_TEMPERATE) { - _palette_remap_grf[i] = (PAL_DOS != used_set->palette); LoadGrfFileIndexed( used_set->files[GFT_ARCTIC + _settings_game.game_creation.landscape - 1].filename, _landscape_spriteindexes[_settings_game.game_creation.landscape - 1], - i++ + i++, + (PAL_DOS != used_set->palette) ); } @@ -230,7 +226,7 @@ static void LoadSpriteTables() LoadNewGRF(SPR_NEWGRFS_BASE, i, 2); uint total_extra_graphics = SPR_NEWGRFS_BASE - SPR_OPENTTD_BASE; - _missing_extra_graphics = GetSpriteCountForSlot(i, SPR_OPENTTD_BASE, SPR_NEWGRFS_BASE); + _missing_extra_graphics = GetSpriteCountForFile(used_set->files[GFT_EXTRA].filename, SPR_OPENTTD_BASE, SPR_NEWGRFS_BASE); DEBUG(sprite, 1, "%u extra sprites, %u from baseset, %u from fallback", total_extra_graphics, total_extra_graphics - _missing_extra_graphics, _missing_extra_graphics); /* The original baseset extra graphics intentionally make use of the fallback graphics. diff --git a/src/newgrf.cpp b/src/newgrf.cpp index b51a18b437..aadd1a3a92 100644 --- a/src/newgrf.cpp +++ b/src/newgrf.cpp @@ -99,11 +99,10 @@ public: SpriteID spriteid; ///< First available SpriteID for loading realsprites. /* Local state in the file */ - uint file_index; ///< File index of currently processed GRF file. + SpriteFile *file; ///< File of currently processed GRF file. GRFFile *grffile; ///< Currently processed GRF file. GRFConfig *grfconfig; ///< Config of the currently processed GRF file. uint32 nfo_line; ///< Currently processed pseudo sprite number in the GRF. - byte grf_container_ver; ///< Container format of the current GRF file. /* Kind of return values when processing certain actions */ int skip_sprites; ///< Number of pseudo sprites to skip before processing the next one. (-1 to skip to end of file) @@ -4884,7 +4883,7 @@ static void NewSpriteSet(ByteReader *buf) for (int i = 0; i < num_sets * num_ents; i++) { _cur.nfo_line++; - LoadNextSprite(_cur.spriteid++, _cur.file_index, _cur.nfo_line, _cur.grf_container_ver); + LoadNextSprite(_cur.spriteid++, *_cur.file, _cur.nfo_line); } } @@ -6120,16 +6119,16 @@ static void GraphicsNew(ByteReader *buf) /* Special not-TTDP-compatible case used in openttd.grf * Missing shore sprites and initialisation of SPR_SHORE_BASE */ grfmsg(2, "GraphicsNew: Loading 10 missing shore sprites from extra grf."); - LoadNextSprite(SPR_SHORE_BASE + 0, _cur.file_index, _cur.nfo_line++, _cur.grf_container_ver); // SLOPE_STEEP_S - LoadNextSprite(SPR_SHORE_BASE + 5, _cur.file_index, _cur.nfo_line++, _cur.grf_container_ver); // SLOPE_STEEP_W - LoadNextSprite(SPR_SHORE_BASE + 7, _cur.file_index, _cur.nfo_line++, _cur.grf_container_ver); // SLOPE_WSE - LoadNextSprite(SPR_SHORE_BASE + 10, _cur.file_index, _cur.nfo_line++, _cur.grf_container_ver); // SLOPE_STEEP_N - LoadNextSprite(SPR_SHORE_BASE + 11, _cur.file_index, _cur.nfo_line++, _cur.grf_container_ver); // SLOPE_NWS - LoadNextSprite(SPR_SHORE_BASE + 13, _cur.file_index, _cur.nfo_line++, _cur.grf_container_ver); // SLOPE_ENW - LoadNextSprite(SPR_SHORE_BASE + 14, _cur.file_index, _cur.nfo_line++, _cur.grf_container_ver); // SLOPE_SEN - LoadNextSprite(SPR_SHORE_BASE + 15, _cur.file_index, _cur.nfo_line++, _cur.grf_container_ver); // SLOPE_STEEP_E - LoadNextSprite(SPR_SHORE_BASE + 16, _cur.file_index, _cur.nfo_line++, _cur.grf_container_ver); // SLOPE_EW - LoadNextSprite(SPR_SHORE_BASE + 17, _cur.file_index, _cur.nfo_line++, _cur.grf_container_ver); // SLOPE_NS + LoadNextSprite(SPR_SHORE_BASE + 0, *_cur.file, _cur.nfo_line++); // SLOPE_STEEP_S + LoadNextSprite(SPR_SHORE_BASE + 5, *_cur.file, _cur.nfo_line++); // SLOPE_STEEP_W + LoadNextSprite(SPR_SHORE_BASE + 7, *_cur.file, _cur.nfo_line++); // SLOPE_WSE + LoadNextSprite(SPR_SHORE_BASE + 10, *_cur.file, _cur.nfo_line++); // SLOPE_STEEP_N + LoadNextSprite(SPR_SHORE_BASE + 11, *_cur.file, _cur.nfo_line++); // SLOPE_NWS + LoadNextSprite(SPR_SHORE_BASE + 13, *_cur.file, _cur.nfo_line++); // SLOPE_ENW + LoadNextSprite(SPR_SHORE_BASE + 14, *_cur.file, _cur.nfo_line++); // SLOPE_SEN + LoadNextSprite(SPR_SHORE_BASE + 15, *_cur.file, _cur.nfo_line++); // SLOPE_STEEP_E + LoadNextSprite(SPR_SHORE_BASE + 16, *_cur.file, _cur.nfo_line++); // SLOPE_EW + LoadNextSprite(SPR_SHORE_BASE + 17, *_cur.file, _cur.nfo_line++); // SLOPE_NS if (_loaded_newgrf_features.shore == SHORE_REPLACE_NONE) _loaded_newgrf_features.shore = SHORE_REPLACE_ONLY_NEW; return; } @@ -6177,7 +6176,7 @@ static void GraphicsNew(ByteReader *buf) for (; num > 0; num--) { _cur.nfo_line++; - LoadNextSprite(replace == 0 ? _cur.spriteid++ : replace++, _cur.file_index, _cur.nfo_line, _cur.grf_container_ver); + LoadNextSprite(replace == 0 ? _cur.spriteid++ : replace++, *_cur.file, _cur.nfo_line); } _cur.skip_sprites = skip_num; @@ -6397,7 +6396,7 @@ static void CfgApply(ByteReader *buf) /* Preload the next sprite */ size_t pos = FioGetPos(); - uint32 num = _cur.grf_container_ver >= 2 ? FioReadDword() : FioReadWord(); + uint32 num = _cur.file->GetContainerVersion() >= 2 ? FioReadDword() : FioReadWord(); uint8 type = FioReadByte(); byte *preload_sprite = nullptr; @@ -6758,7 +6757,7 @@ static void SpriteReplace(ByteReader *buf) for (uint j = 0; j < num_sprites; j++) { int load_index = first_sprite + j; _cur.nfo_line++; - LoadNextSprite(load_index, _cur.file_index, _cur.nfo_line, _cur.grf_container_ver); // XXX + LoadNextSprite(load_index, *_cur.file, _cur.nfo_line); // XXX /* Shore sprites now located at different addresses. * So detect when the old ones get replaced. */ @@ -7553,9 +7552,9 @@ static void LoadGRFSound(size_t offs, SoundEntry *sound) if (offs != SIZE_MAX) { /* Sound is present in the NewGRF. */ - sound->file = FioGetRandomAccessFile(_cur.file_index); + sound->file = _cur.file; sound->file_offset = offs; - sound->grf_container_ver = _cur.grf_container_ver; + sound->grf_container_ver = _cur.file->GetContainerVersion(); } } @@ -7587,10 +7586,11 @@ static void GRFSound(ByteReader *buf) size_t offs = FioGetPos(); - uint32 len = _cur.grf_container_ver >= 2 ? FioReadDword() : FioReadWord(); + byte grf_container_version = _cur.file->GetContainerVersion(); + uint32 len = grf_container_version >= 2 ? FioReadDword() : FioReadWord(); byte type = FioReadByte(); - if (_cur.grf_container_ver >= 2 && type == 0xFD) { + if (grf_container_version >= 2 && type == 0xFD) { /* Reference to sprite section. */ if (invalid) { grfmsg(1, "GRFSound: Sound index out of range (multiple Action 11?)"); @@ -7608,7 +7608,7 @@ static void GRFSound(ByteReader *buf) if (type != 0xFF) { grfmsg(1, "GRFSound: Unexpected RealSprite found, skipping"); FioSkipBytes(7); - SkipSpriteData(type, len - 8); + SkipSpriteData(*_cur.file, type, len - 8); continue; } @@ -7622,7 +7622,7 @@ static void GRFSound(ByteReader *buf) case 0xFF: /* Allocate sound only in init stage. */ if (_cur.stage == GLS_INIT) { - if (_cur.grf_container_ver >= 2) { + if (grf_container_version >= 2) { grfmsg(1, "GRFSound: Inline sounds are not supported for container version >= 2"); } else { LoadGRFSound(offs, sound + i); @@ -7688,7 +7688,7 @@ static void LoadFontGlyph(ByteReader *buf) for (uint c = 0; c < num_char; c++) { if (size < FS_END) SetUnicodeGlyph(size, base_char + c, _cur.spriteid); _cur.nfo_line++; - LoadNextSprite(_cur.spriteid++, _cur.file_index, _cur.nfo_line, _cur.grf_container_ver); + LoadNextSprite(_cur.spriteid++, *_cur.file, _cur.nfo_line); } } } @@ -9269,32 +9269,6 @@ static void DecodeSpecialSprite(byte *buf, uint num, GrfLoadingStage stage) } -/** Signature of a container version 2 GRF. */ -extern const byte _grf_cont_v2_sig[8] = {'G', 'R', 'F', 0x82, 0x0D, 0x0A, 0x1A, 0x0A}; - -/** - * Get the container version of the currently opened GRF file. - * @return Container version of the GRF file or 0 if the file is corrupt/no GRF file. - */ -byte GetGRFContainerVersion() -{ - size_t pos = FioGetPos(); - - if (FioReadWord() == 0) { - /* Check for GRF container version 2, which is identified by the bytes - * '47 52 46 82 0D 0A 1A 0A' at the start of the file. */ - for (uint i = 0; i < lengthof(_grf_cont_v2_sig); i++) { - if (FioReadByte() != _grf_cont_v2_sig[i]) return 0; // Invalid format - } - - return 2; - } - - /* Container version 1 has no header, rewind to start. */ - FioSeekTo(pos, SEEK_SET); - return 1; -} - /** * Load a particular NewGRF. * @param config The configuration of the to be loaded NewGRF. @@ -9329,16 +9303,13 @@ void LoadNewGRFFile(GRFConfig *config, uint file_index, GrfLoadingStage stage, S return; } - FioOpenFile(file_index, filename, subdir); - _cur.file_index = file_index; // XXX - _palette_remap_grf[_cur.file_index] = (config->palette & GRFP_USE_MASK); - + _cur.file = &FioOpenFile(file_index, filename, subdir, config->palette & GRFP_USE_MASK); _cur.grfconfig = config; DEBUG(grf, 2, "LoadNewGRFFile: Reading NewGRF-file '%s'", filename); - _cur.grf_container_ver = GetGRFContainerVersion(); - if (_cur.grf_container_ver == 0) { + byte grf_container_version = _cur.file->GetContainerVersion(); + if (grf_container_version == 0) { DEBUG(grf, 7, "LoadNewGRFFile: Custom .grf has invalid format"); return; } @@ -9346,13 +9317,13 @@ void LoadNewGRFFile(GRFConfig *config, uint file_index, GrfLoadingStage stage, S if (stage == GLS_INIT || stage == GLS_ACTIVATION) { /* We need the sprite offsets in the init stage for NewGRF sounds * and in the activation stage for real sprites. */ - ReadGRFSpriteOffsets(_cur.grf_container_ver); + ReadGRFSpriteOffsets(*_cur.file); } else { /* Skip sprite section offset if present. */ - if (_cur.grf_container_ver >= 2) FioReadDword(); + if (grf_container_version >= 2) FioReadDword(); } - if (_cur.grf_container_ver >= 2) { + if (grf_container_version >= 2) { /* Read compression value. */ byte compression = FioReadByte(); if (compression != 0) { @@ -9364,7 +9335,7 @@ void LoadNewGRFFile(GRFConfig *config, uint file_index, GrfLoadingStage stage, S /* Skip the first sprite; we don't care about how many sprites this * does contain; newest TTDPatches and George's longvehicles don't * neither, apparently. */ - uint32 num = _cur.grf_container_ver >= 2 ? FioReadDword() : FioReadWord(); + uint32 num = grf_container_version >= 2 ? FioReadDword() : FioReadWord(); if (num == 4 && FioReadByte() == 0xFF) { FioReadDword(); } else { @@ -9376,7 +9347,7 @@ void LoadNewGRFFile(GRFConfig *config, uint file_index, GrfLoadingStage stage, S ReusableBuffer buf; - while ((num = (_cur.grf_container_ver >= 2 ? FioReadDword() : FioReadWord())) != 0) { + while ((num = (grf_container_version >= 2 ? FioReadDword() : FioReadWord())) != 0) { byte type = FioReadByte(); _cur.nfo_line++; @@ -9398,12 +9369,12 @@ void LoadNewGRFFile(GRFConfig *config, uint file_index, GrfLoadingStage stage, S break; } - if (_cur.grf_container_ver >= 2 && type == 0xFD) { + if (grf_container_version >= 2 && type == 0xFD) { /* Reference to data section. Container version >= 2 only. */ FioSkipBytes(num); } else { FioSkipBytes(7); - SkipSpriteData(type, num - 8); + SkipSpriteData(*_cur.file, type, num - 8); } } diff --git a/src/newgrf.h b/src/newgrf.h index d9c8a4da58..477bfa892c 100644 --- a/src/newgrf.h +++ b/src/newgrf.h @@ -192,8 +192,6 @@ static inline bool HasGrfMiscBit(GrfMiscBit bit) /* Indicates which are the newgrf features currently loaded ingame */ extern GRFLoadedFeatures _loaded_newgrf_features; -byte GetGRFContainerVersion(); - void LoadNewGRFFile(struct GRFConfig *config, uint file_index, GrfLoadingStage stage, Subdirectory subdir); void LoadNewGRF(uint load_index, uint file_index, uint num_baseset); void ReloadNewGRFData(); // in saveload/afterload.cpp diff --git a/src/newgrf_debug_gui.cpp b/src/newgrf_debug_gui.cpp index 3a5e05a77b..0d9fae8a48 100644 --- a/src/newgrf_debug_gui.cpp +++ b/src/newgrf_debug_gui.cpp @@ -11,7 +11,7 @@ #include #include "window_gui.h" #include "window_func.h" -#include "fileio_func.h" +#include "random_access_file_type.h" #include "spritecache.h" #include "string_func.h" #include "strings_func.h" @@ -828,7 +828,7 @@ struct SpriteAlignerWindow : Window { switch (widget) { case WID_SA_CAPTION: SetDParam(0, this->current_sprite); - SetDParamStr(1, FioGetFilename(GetOriginFileSlot(this->current_sprite))); + SetDParamStr(1, GetOriginFile(this->current_sprite)->GetSimplifiedFilename().c_str()); break; case WID_SA_OFFSETS_ABS: diff --git a/src/spritecache.cpp b/src/spritecache.cpp index 288f74af43..f2840c1b02 100644 --- a/src/spritecache.cpp +++ b/src/spritecache.cpp @@ -8,7 +8,7 @@ /** @file spritecache.cpp Caching of sprites. */ #include "stdafx.h" -#include "fileio_func.h" +#include "random_access_file_type.h" #include "spriteloader/grf.hpp" #include "gfx_func.h" #include "error.h" @@ -31,18 +31,17 @@ uint _sprite_cache_size = 4; struct SpriteCache { void *ptr; size_t file_pos; + SpriteFile *file; ///< The file the sprite in this entry can be found in. uint32 id; - uint16 file_slot; int16 lru; SpriteType type; ///< In some cases a single sprite is misused by two NewGRFs. Once as real sprite and once as recolour sprite. If the recolour sprite gets into the cache it might be drawn as real sprite which causes enormous trouble. bool warned; ///< True iff the user has been warned about incorrect use of this sprite - byte container_ver; ///< Container version of the GRF the sprite is from. }; static uint _spritecache_items = 0; static SpriteCache *_spritecache = nullptr; - +static std::vector> _sprite_files; static inline SpriteCache *GetSpriteCache(uint index) { @@ -72,6 +71,37 @@ static SpriteCache *AllocateSpriteCache(uint index) return GetSpriteCache(index); } +/** + * Get the cached SpriteFile given the name of the file. + * @param filename The name of the file at the disk. + * @return The SpriteFile or \c null. + */ +static SpriteFile *GetCachedSpriteFileByName(const std::string &filename) { + for (auto &f : _sprite_files) { + if (f->GetFilename() == filename) { + return f.get(); + } + } + return nullptr; +} + +/** + * Open/get the SpriteFile that is cached for use in the sprite cache. + * @param filename Name of the file at the disk. + * @param subdir The sub directory to search this file in. + * @param palette_remap Whether a palette remap needs to be performed for this file. + * @return The reference to the SpriteCache. + */ +SpriteFile &OpenCachedSpriteFile(const std::string &filename, Subdirectory subdir, bool palette_remap) +{ + SpriteFile *file = GetCachedSpriteFileByName(filename); + if (file == nullptr) { + file = _sprite_files.emplace_back(new SpriteFile(filename, subdir, palette_remap)).get(); + } else { + file->SeekToBegin(); + } + return *file; +} struct MemBlock { size_t size; @@ -92,22 +122,22 @@ static void *AllocSprite(size_t mem_req); * @param num the amount of sprites to skip * @return true if the data could be correctly skipped. */ -bool SkipSpriteData(byte type, uint16 num) +bool SkipSpriteData(SpriteFile &file, byte type, uint16 num) { if (type & 2) { - FioSkipBytes(num); + file.SkipBytes(num); } else { while (num > 0) { - int8 i = FioReadByte(); + int8 i = file.ReadByte(); if (i >= 0) { int size = (i == 0) ? 0x80 : i; if (size > num) return false; num -= size; - FioSkipBytes(size); + file.SkipBytes(size); } else { i = -(i >> 3); num -= i; - FioReadByte(); + file.ReadByte(); } } } @@ -121,7 +151,7 @@ bool SpriteExists(SpriteID id) /* Special case for Sprite ID zero -- its position is also 0... */ if (id == 0) return true; - return !(GetSpriteCache(id)->file_pos == 0 && GetSpriteCache(id)->file_slot == 0); + return !(GetSpriteCache(id)->file_pos == 0 && GetSpriteCache(id)->file == nullptr); } /** @@ -136,14 +166,14 @@ SpriteType GetSpriteType(SpriteID sprite) } /** - * Get the (FIOS) file slot of a given sprite. + * Get the SpriteFile of a given sprite. * @param sprite The sprite to look at. - * @return the FIOS file slot + * @return The SpriteFile. */ -uint GetOriginFileSlot(SpriteID sprite) +SpriteFile *GetOriginFile(SpriteID sprite) { - if (!SpriteExists(sprite)) return 0; - return GetSpriteCache(sprite)->file_slot; + if (!SpriteExists(sprite)) return nullptr; + return GetSpriteCache(sprite)->file; } /** @@ -158,19 +188,22 @@ uint32 GetSpriteLocalID(SpriteID sprite) } /** - * Count the sprites which originate from a specific file slot in a range of SpriteIDs. - * @param file_slot FIOS file slot. + * Count the sprites which originate from a specific file in a range of SpriteIDs. + * @param file The loaded SpriteFile. * @param begin First sprite in range. * @param end First sprite not in range. * @return Number of sprites. */ -uint GetSpriteCountForSlot(uint file_slot, SpriteID begin, SpriteID end) +uint GetSpriteCountForFile(const std::string &filename, SpriteID begin, SpriteID end) { + SpriteFile *file = GetCachedSpriteFileByName(filename); + if (file == nullptr) return 0; + uint count = 0; for (SpriteID i = begin; i != end; i++) { if (SpriteExists(i)) { SpriteCache *sc = GetSpriteCache(i); - if (sc->file_slot == file_slot) count++; + if (sc->file == file) count++; } } return count; @@ -348,7 +381,7 @@ static bool PadSprites(SpriteLoader::Sprite *sprite, uint8 sprite_avail, SpriteE return true; } -static bool ResizeSprites(SpriteLoader::Sprite *sprite, uint8 sprite_avail, uint32 file_slot, uint32 file_pos, SpriteEncoder *encoder) +static bool ResizeSprites(SpriteLoader::Sprite *sprite, uint8 sprite_avail, SpriteEncoder *encoder) { /* Create a fully zoomed image if it does not exist */ ZoomLevel first_avail = static_cast(FIND_FIRST_BIT(sprite_avail)); @@ -379,11 +412,11 @@ static bool ResizeSprites(SpriteLoader::Sprite *sprite, uint8 sprite_avail, uint /** * Load a recolour sprite into memory. - * @param file_slot GRF we're reading from. + * @param file GRF we're reading from. * @param num Size of the sprite in the GRF. * @return Sprite data. */ -static void *ReadRecolourSprite(uint16 file_slot, uint num) +static void *ReadRecolourSprite(SpriteFile &file, uint num) { /* "Normal" recolour sprites are ALWAYS 257 bytes. Then there is a small * number of recolour sprites that are 17 bytes that only exist in DOS @@ -392,19 +425,19 @@ static void *ReadRecolourSprite(uint16 file_slot, uint num) static const uint RECOLOUR_SPRITE_SIZE = 257; byte *dest = (byte *)AllocSprite(std::max(RECOLOUR_SPRITE_SIZE, num)); - if (_palette_remap_grf[file_slot]) { + if (file.NeedsPaletteRemap()) { byte *dest_tmp = AllocaM(byte, std::max(RECOLOUR_SPRITE_SIZE, num)); /* Only a few recolour sprites are less than 257 bytes */ if (num < RECOLOUR_SPRITE_SIZE) memset(dest_tmp, 0, RECOLOUR_SPRITE_SIZE); - FioReadBlock(dest_tmp, num); + file.ReadBlock(dest_tmp, num); /* The data of index 0 is never used; "literal 00" according to the (New)GRF specs. */ for (uint i = 1; i < RECOLOUR_SPRITE_SIZE; i++) { dest[i] = _palmap_w2d[dest_tmp[_palmap_d2w[i - 1] + 1]]; } } else { - FioReadBlock(dest, num); + file.ReadBlock(dest, num); } return dest; @@ -424,7 +457,7 @@ static void *ReadSprite(const SpriteCache *sc, SpriteID id, SpriteType sprite_ty /* Use current blitter if no other sprite encoder is given. */ if (encoder == nullptr) encoder = BlitterFactory::GetCurrentBlitter(); - uint8 file_slot = sc->file_slot; + SpriteFile &file = *sc->file; size_t file_pos = sc->file_pos; assert(sprite_type != ST_RECOLOUR); @@ -437,13 +470,13 @@ static void *ReadSprite(const SpriteCache *sc, SpriteID id, SpriteType sprite_ty uint8 sprite_avail = 0; sprite[ZOOM_LVL_NORMAL].type = sprite_type; - SpriteLoaderGrf sprite_loader(sc->container_ver); + SpriteLoaderGrf sprite_loader(file.GetContainerVersion()); if (sprite_type != ST_MAPGEN && encoder->Is32BppSupported()) { /* Try for 32bpp sprites first. */ - sprite_avail = sprite_loader.LoadSprite(sprite, file_slot, file_pos, sprite_type, true); + sprite_avail = sprite_loader.LoadSprite(sprite, file, file_pos, sprite_type, true); } if (sprite_avail == 0) { - sprite_avail = sprite_loader.LoadSprite(sprite, file_slot, file_pos, sprite_type, false); + sprite_avail = sprite_loader.LoadSprite(sprite, file, file_pos, sprite_type, false); } if (sprite_avail == 0) { @@ -480,7 +513,7 @@ static void *ReadSprite(const SpriteCache *sc, SpriteID id, SpriteType sprite_ty return s; } - if (!ResizeSprites(sprite, sprite_avail, file_slot, sc->id, encoder)) { + if (!ResizeSprites(sprite, sprite_avail, encoder)) { if (id == SPR_IMG_QUERY) usererror("Okay... something went horribly wrong. I couldn't resize the fallback sprite. What should I do?"); return (void*)GetRawSprite(SPR_IMG_QUERY, ST_NORMAL, allocator, encoder); } @@ -516,27 +549,27 @@ size_t GetGRFSpriteOffset(uint32 id) * Parse the sprite section of GRFs. * @param container_version Container version of the GRF we're currently processing. */ -void ReadGRFSpriteOffsets(byte container_version) +void ReadGRFSpriteOffsets(SpriteFile &file) { _grf_sprite_offsets.clear(); - if (container_version >= 2) { + if (file.GetContainerVersion() >= 2) { /* Seek to sprite section of the GRF. */ - size_t data_offset = FioReadDword(); - size_t old_pos = FioGetPos(); - FioSeekTo(data_offset, SEEK_CUR); + size_t data_offset = file.ReadDword(); + size_t old_pos = file.GetPos(); + file.SeekTo(data_offset, SEEK_CUR); /* Loop over all sprite section entries and store the file * offset for each newly encountered ID. */ uint32 id, prev_id = 0; - while ((id = FioReadDword()) != 0) { - if (id != prev_id) _grf_sprite_offsets[id] = FioGetPos() - 4; + while ((id = file.ReadDword()) != 0) { + if (id != prev_id) _grf_sprite_offsets[id] = file.GetPos() - 4; prev_id = id; - FioSkipBytes(FioReadDword()); + file.SkipBytes(file.ReadDword()); } /* Continue processing the data section. */ - FioSeekTo(old_pos, SEEK_SET); + file.SeekTo(old_pos, SEEK_SET); } } @@ -544,19 +577,19 @@ void ReadGRFSpriteOffsets(byte container_version) /** * Load a real or recolour sprite. * @param load_index Global sprite index. - * @param file_slot GRF to load from. + * @param file GRF to load from. * @param file_sprite_id Sprite number in the GRF. * @param container_version Container version of the GRF. * @return True if a valid sprite was loaded, false on any error. */ -bool LoadNextSprite(int load_index, byte file_slot, uint file_sprite_id, byte container_version) +bool LoadNextSprite(int load_index, SpriteFile &file, uint file_sprite_id) { - size_t file_pos = FioGetPos(); + size_t file_pos = file.GetPos(); /* Read sprite header. */ - uint32 num = container_version >= 2 ? FioReadDword() : FioReadWord(); + uint32 num = file.GetContainerVersion() >= 2 ? file.ReadDword() : file.ReadWord(); if (num == 0) return false; - byte grf_type = FioReadByte(); + byte grf_type = file.ReadByte(); SpriteType type; void *data = nullptr; @@ -564,25 +597,25 @@ bool LoadNextSprite(int load_index, byte file_slot, uint file_sprite_id, byte co /* Some NewGRF files have "empty" pseudo-sprites which are 1 * byte long. Catch these so the sprites won't be displayed. */ if (num == 1) { - FioReadByte(); + file.ReadByte(); return false; } type = ST_RECOLOUR; - data = ReadRecolourSprite(file_slot, num); - } else if (container_version >= 2 && grf_type == 0xFD) { + data = ReadRecolourSprite(file, num); + } else if (file.GetContainerVersion() >= 2 && grf_type == 0xFD) { if (num != 4) { /* Invalid sprite section include, ignore. */ - FioSkipBytes(num); + file.SkipBytes(num); return false; } /* It is not an error if no sprite with the provided ID is found in the sprite section. */ - file_pos = GetGRFSpriteOffset(FioReadDword()); + file_pos = GetGRFSpriteOffset(file.ReadDword()); type = ST_NORMAL; } else { - FioSkipBytes(7); - type = SkipSpriteData(grf_type, num - 8) ? ST_NORMAL : ST_INVALID; + file.SkipBytes(7); + type = SkipSpriteData(file, grf_type, num - 8) ? ST_NORMAL : ST_INVALID; /* Inline sprites are not supported for container version >= 2. */ - if (container_version >= 2) return false; + if (file.GetContainerVersion() >= 2) return false; } if (type == ST_INVALID) return false; @@ -599,14 +632,13 @@ bool LoadNextSprite(int load_index, byte file_slot, uint file_sprite_id, byte co } SpriteCache *sc = AllocateSpriteCache(load_index); - sc->file_slot = file_slot; + sc->file = &file; sc->file_pos = file_pos; sc->ptr = data; sc->lru = 0; sc->id = file_sprite_id; sc->type = type; sc->warned = false; - sc->container_ver = container_version; return true; } @@ -617,13 +649,12 @@ void DupSprite(SpriteID old_spr, SpriteID new_spr) SpriteCache *scnew = AllocateSpriteCache(new_spr); // may reallocate: so put it first SpriteCache *scold = GetSpriteCache(old_spr); - scnew->file_slot = scold->file_slot; + scnew->file = scold->file; scnew->file_pos = scold->file_pos; scnew->ptr = nullptr; scnew->id = scold->id; scnew->type = scold->type; scnew->warned = false; - scnew->container_ver = scold->container_ver; } /** @@ -967,6 +998,7 @@ void GfxInitSpriteMem() _spritecache = nullptr; _compact_cache_counter = 0; + _sprite_files.clear(); } /** diff --git a/src/spritecache.h b/src/spritecache.h index 00503c4535..367d690951 100644 --- a/src/spritecache.h +++ b/src/spritecache.h @@ -31,9 +31,9 @@ void *GetRawSprite(SpriteID sprite, SpriteType type, AllocatorProc *allocator = bool SpriteExists(SpriteID sprite); SpriteType GetSpriteType(SpriteID sprite); -uint GetOriginFileSlot(SpriteID sprite); +SpriteFile *GetOriginFile(SpriteID sprite); uint32 GetSpriteLocalID(SpriteID sprite); -uint GetSpriteCountForSlot(uint file_slot, SpriteID begin, SpriteID end); +uint GetSpriteCountForFile(const std::string &filename, SpriteID begin, SpriteID end); uint GetMaxSpriteID(); @@ -53,10 +53,12 @@ void GfxInitSpriteMem(); void GfxClearSpriteCache(); void IncreaseSpriteLRU(); -void ReadGRFSpriteOffsets(byte container_version); +SpriteFile &OpenCachedSpriteFile(const std::string &filename, Subdirectory subdir, bool palette_remap); + +void ReadGRFSpriteOffsets(SpriteFile &file); size_t GetGRFSpriteOffset(uint32 id); -bool LoadNextSprite(int load_index, byte file_index, uint file_sprite_id, byte container_version); -bool SkipSpriteData(byte type, uint16 num); +bool LoadNextSprite(int load_index, SpriteFile &file, uint file_sprite_id); +bool SkipSpriteData(SpriteFile &file, byte type, uint16 num); void DupSprite(SpriteID old_spr, SpriteID new_spr); #endif /* SPRITECACHE_H */ diff --git a/src/spriteloader/CMakeLists.txt b/src/spriteloader/CMakeLists.txt index 5d6a2f865e..804bb1a2ee 100644 --- a/src/spriteloader/CMakeLists.txt +++ b/src/spriteloader/CMakeLists.txt @@ -1,5 +1,7 @@ add_files( grf.cpp grf.hpp + sprite_file.cpp + sprite_file_type.hpp spriteloader.hpp ) diff --git a/src/spriteloader/grf.cpp b/src/spriteloader/grf.cpp index 30ea094443..fae87b87fc 100644 --- a/src/spriteloader/grf.cpp +++ b/src/spriteloader/grf.cpp @@ -9,7 +9,6 @@ #include "../stdafx.h" #include "../gfx_func.h" -#include "../fileio_func.h" #include "../debug.h" #include "../settings_type.h" #include "../strings_func.h" @@ -32,14 +31,14 @@ extern const byte _palmap_w2d[]; * @param line the line where the error occurs. * @return always false (to tell loading the sprite failed) */ -static bool WarnCorruptSprite(uint8 file_slot, size_t file_pos, int line) +static bool WarnCorruptSprite(const SpriteFile &file, size_t file_pos, int line) { static byte warning_level = 0; if (warning_level == 0) { - SetDParamStr(0, FioGetFilename(file_slot)); + SetDParamStr(0, file.GetSimplifiedFilename().c_str()); ShowErrorMessage(STR_NEWGRF_ERROR_CORRUPT_SPRITE, INVALID_STRING_ID, WL_ERROR); } - DEBUG(sprite, warning_level, "[%i] Loading corrupted sprite from %s at position %i", line, FioGetFilename(file_slot), (int)file_pos); + DEBUG(sprite, warning_level, "[%i] Loading corrupted sprite from %s at position %i", line, file.GetSimplifiedFilename().c_str(), (int)file_pos); warning_level = 6; return false; } @@ -47,7 +46,7 @@ static bool WarnCorruptSprite(uint8 file_slot, size_t file_pos, int line) /** * Decode the image data of a single sprite. * @param[in,out] sprite Filled with the sprite image data. - * @param file_slot File slot. + * @param file The file with the sprite data. * @param file_pos File position. * @param sprite_type Type of the sprite we're decoding. * @param num Size of the decompressed sprite. @@ -57,7 +56,7 @@ static bool WarnCorruptSprite(uint8 file_slot, size_t file_pos, int line) * @param container_format Container format of the GRF this sprite is in. * @return True if the sprite was successfully loaded. */ -bool DecodeSingleSprite(SpriteLoader::Sprite *sprite, uint8 file_slot, size_t file_pos, SpriteType sprite_type, int64 num, byte type, ZoomLevel zoom_lvl, byte colour_fmt, byte container_format) +bool DecodeSingleSprite(SpriteLoader::Sprite *sprite, SpriteFile &file, size_t file_pos, SpriteType sprite_type, int64 num, byte type, ZoomLevel zoom_lvl, byte colour_fmt, byte container_format) { std::unique_ptr dest_orig(new byte[num]); byte *dest = dest_orig.get(); @@ -65,24 +64,24 @@ bool DecodeSingleSprite(SpriteLoader::Sprite *sprite, uint8 file_slot, size_t fi /* Read the file, which has some kind of compression */ while (num > 0) { - int8 code = FioReadByte(); + int8 code = file.ReadByte(); if (code >= 0) { /* Plain bytes to read */ int size = (code == 0) ? 0x80 : code; num -= size; - if (num < 0) return WarnCorruptSprite(file_slot, file_pos, __LINE__); + if (num < 0) return WarnCorruptSprite(file, file_pos, __LINE__); for (; size > 0; size--) { - *dest = FioReadByte(); + *dest = file.ReadByte(); dest++; } } else { /* Copy bytes from earlier in the sprite */ - const uint data_offset = ((code & 7) << 8) | FioReadByte(); - if (dest - data_offset < dest_orig.get()) return WarnCorruptSprite(file_slot, file_pos, __LINE__); + const uint data_offset = ((code & 7) << 8) | file.ReadByte(); + if (dest - data_offset < dest_orig.get()) return WarnCorruptSprite(file, file_pos, __LINE__); int size = -(code >> 3); num -= size; - if (num < 0) return WarnCorruptSprite(file_slot, file_pos, __LINE__); + if (num < 0) return WarnCorruptSprite(file, file_pos, __LINE__); for (; size > 0; size--) { *dest = *(dest - data_offset); dest++; @@ -90,7 +89,7 @@ bool DecodeSingleSprite(SpriteLoader::Sprite *sprite, uint8 file_slot, size_t fi } } - if (num != 0) return WarnCorruptSprite(file_slot, file_pos, __LINE__); + if (num != 0) return WarnCorruptSprite(file, file_pos, __LINE__); sprite->AllocateData(zoom_lvl, sprite->width * sprite->height); @@ -117,7 +116,7 @@ bool DecodeSingleSprite(SpriteLoader::Sprite *sprite, uint8 file_slot, size_t fi do { if (dest + (container_format >= 2 && sprite->width > 256 ? 4 : 2) > dest_orig.get() + dest_size) { - return WarnCorruptSprite(file_slot, file_pos, __LINE__); + return WarnCorruptSprite(file, file_pos, __LINE__); } SpriteLoader::CommonPixel *data; @@ -143,7 +142,7 @@ bool DecodeSingleSprite(SpriteLoader::Sprite *sprite, uint8 file_slot, size_t fi data = &sprite->data[y * sprite->width + skip]; if (skip + length > sprite->width || dest + length * bpp > dest_orig.get() + dest_size) { - return WarnCorruptSprite(file_slot, file_pos, __LINE__); + return WarnCorruptSprite(file, file_pos, __LINE__); } for (int x = 0; x < length; x++) { @@ -155,7 +154,7 @@ bool DecodeSingleSprite(SpriteLoader::Sprite *sprite, uint8 file_slot, size_t fi data->a = (colour_fmt & SCC_ALPHA) ? *dest++ : 0xFF; if (colour_fmt & SCC_PAL) { switch (sprite_type) { - case ST_NORMAL: data->m = _palette_remap_grf[file_slot] ? _palmap_w2d[*dest] : *dest; break; + case ST_NORMAL: data->m = file.NeedsPaletteRemap() ? _palmap_w2d[*dest] : *dest; break; case ST_FONT: data->m = std::min(*dest, 2u); break; default: data->m = *dest; break; } @@ -169,12 +168,12 @@ bool DecodeSingleSprite(SpriteLoader::Sprite *sprite, uint8 file_slot, size_t fi } } else { if (dest_size < sprite->width * sprite->height * bpp) { - return WarnCorruptSprite(file_slot, file_pos, __LINE__); + return WarnCorruptSprite(file, file_pos, __LINE__); } if (dest_size > sprite->width * sprite->height * bpp) { static byte warning_level = 0; - DEBUG(sprite, warning_level, "Ignoring " OTTD_PRINTF64 " unused extra bytes from the sprite from %s at position %i", dest_size - sprite->width * sprite->height * bpp, FioGetFilename(file_slot), (int)file_pos); + DEBUG(sprite, warning_level, "Ignoring " OTTD_PRINTF64 " unused extra bytes from the sprite from %s at position %i", dest_size - sprite->width * sprite->height * bpp, file.GetSimplifiedFilename().c_str(), (int)file_pos); warning_level = 6; } @@ -191,7 +190,7 @@ bool DecodeSingleSprite(SpriteLoader::Sprite *sprite, uint8 file_slot, size_t fi sprite->data[i].a = (colour_fmt & SCC_ALPHA) ? *pixel++ : 0xFF; if (colour_fmt & SCC_PAL) { switch (sprite_type) { - case ST_NORMAL: sprite->data[i].m = _palette_remap_grf[file_slot] ? _palmap_w2d[*pixel] : *pixel; break; + case ST_NORMAL: sprite->data[i].m = file.NeedsPaletteRemap() ? _palmap_w2d[*pixel] : *pixel; break; case ST_FONT: sprite->data[i].m = std::min(*pixel, 2u); break; default: sprite->data[i].m = *pixel; break; } @@ -205,31 +204,31 @@ bool DecodeSingleSprite(SpriteLoader::Sprite *sprite, uint8 file_slot, size_t fi return true; } -uint8 LoadSpriteV1(SpriteLoader::Sprite *sprite, uint8 file_slot, size_t file_pos, SpriteType sprite_type, bool load_32bpp) +uint8 LoadSpriteV1(SpriteLoader::Sprite *sprite, SpriteFile &file, size_t file_pos, SpriteType sprite_type, bool load_32bpp) { /* Check the requested colour depth. */ if (load_32bpp) return 0; /* Open the right file and go to the correct position */ - FioSeekToFile(file_slot, file_pos); + file.SeekTo(file_pos, SEEK_SET); /* Read the size and type */ - int num = FioReadWord(); - byte type = FioReadByte(); + int num = file.ReadWord(); + byte type = file.ReadByte(); /* Type 0xFF indicates either a colourmap or some other non-sprite info; we do not handle them here */ if (type == 0xFF) return 0; ZoomLevel zoom_lvl = (sprite_type != ST_MAPGEN) ? ZOOM_LVL_OUT_4X : ZOOM_LVL_NORMAL; - sprite[zoom_lvl].height = FioReadByte(); - sprite[zoom_lvl].width = FioReadWord(); - sprite[zoom_lvl].x_offs = FioReadWord(); - sprite[zoom_lvl].y_offs = FioReadWord(); + sprite[zoom_lvl].height = file.ReadByte(); + sprite[zoom_lvl].width = file.ReadWord(); + sprite[zoom_lvl].x_offs = file.ReadWord(); + sprite[zoom_lvl].y_offs = file.ReadWord(); sprite[zoom_lvl].colours = SCC_PAL; if (sprite[zoom_lvl].width > INT16_MAX) { - WarnCorruptSprite(file_slot, file_pos, __LINE__); + WarnCorruptSprite(file, file_pos, __LINE__); return 0; } @@ -237,12 +236,12 @@ uint8 LoadSpriteV1(SpriteLoader::Sprite *sprite, uint8 file_slot, size_t file_po * In case it is uncompressed, the size is 'num' - 8 (header-size). */ num = (type & 0x02) ? sprite[zoom_lvl].width * sprite[zoom_lvl].height : num - 8; - if (DecodeSingleSprite(&sprite[zoom_lvl], file_slot, file_pos, sprite_type, num, type, zoom_lvl, SCC_PAL, 1)) return 1 << zoom_lvl; + if (DecodeSingleSprite(&sprite[zoom_lvl], file, file_pos, sprite_type, num, type, zoom_lvl, SCC_PAL, 1)) return 1 << zoom_lvl; return 0; } -uint8 LoadSpriteV2(SpriteLoader::Sprite *sprite, uint8 file_slot, size_t file_pos, SpriteType sprite_type, bool load_32bpp) +uint8 LoadSpriteV2(SpriteLoader::Sprite *sprite, SpriteFile &file, size_t file_pos, SpriteType sprite_type, bool load_32bpp) { static const ZoomLevel zoom_lvl_map[6] = {ZOOM_LVL_OUT_4X, ZOOM_LVL_NORMAL, ZOOM_LVL_OUT_2X, ZOOM_LVL_OUT_8X, ZOOM_LVL_OUT_16X, ZOOM_LVL_OUT_32X}; @@ -250,21 +249,21 @@ uint8 LoadSpriteV2(SpriteLoader::Sprite *sprite, uint8 file_slot, size_t file_po if (file_pos == SIZE_MAX) return 0; /* Open the right file and go to the correct position */ - FioSeekToFile(file_slot, file_pos); + file.SeekTo(file_pos, SEEK_SET); - uint32 id = FioReadDword(); + uint32 id = file.ReadDword(); uint8 loaded_sprites = 0; do { - int64 num = FioReadDword(); - size_t start_pos = FioGetPos(); - byte type = FioReadByte(); + int64 num = file.ReadDword(); + size_t start_pos = file.GetPos(); + byte type = file.ReadByte(); /* Type 0xFF indicates either a colourmap or some other non-sprite info; we do not handle them here. */ if (type == 0xFF) return 0; byte colour = type & SCC_MASK; - byte zoom = FioReadByte(); + byte zoom = file.ReadByte(); bool is_wanted_colour_depth = (colour != 0 && (load_32bpp ? colour != SCC_PAL : colour == SCC_PAL)); bool is_wanted_zoom_lvl; @@ -280,18 +279,18 @@ uint8 LoadSpriteV2(SpriteLoader::Sprite *sprite, uint8 file_slot, size_t file_po if (HasBit(loaded_sprites, zoom_lvl)) { /* We already have this zoom level, skip sprite. */ - DEBUG(sprite, 1, "Ignoring duplicate zoom level sprite %u from %s", id, FioGetFilename(file_slot)); - FioSkipBytes(num - 2); + DEBUG(sprite, 1, "Ignoring duplicate zoom level sprite %u from %s", id, file.GetSimplifiedFilename().c_str()); + file.SkipBytes(num - 2); continue; } - sprite[zoom_lvl].height = FioReadWord(); - sprite[zoom_lvl].width = FioReadWord(); - sprite[zoom_lvl].x_offs = FioReadWord(); - sprite[zoom_lvl].y_offs = FioReadWord(); + sprite[zoom_lvl].height = file.ReadWord(); + sprite[zoom_lvl].width = file.ReadWord(); + sprite[zoom_lvl].x_offs = file.ReadWord(); + sprite[zoom_lvl].y_offs = file.ReadWord(); if (sprite[zoom_lvl].width > INT16_MAX || sprite[zoom_lvl].height > INT16_MAX) { - WarnCorruptSprite(file_slot, file_pos, __LINE__); + WarnCorruptSprite(file, file_pos, __LINE__); return 0; } @@ -308,30 +307,30 @@ uint8 LoadSpriteV2(SpriteLoader::Sprite *sprite, uint8 file_slot, size_t file_po /* For chunked encoding we store the decompressed size in the file, * otherwise we can calculate it from the image dimensions. */ - uint decomp_size = (type & 0x08) ? FioReadDword() : sprite[zoom_lvl].width * sprite[zoom_lvl].height * bpp; + uint decomp_size = (type & 0x08) ? file.ReadDword() : sprite[zoom_lvl].width * sprite[zoom_lvl].height * bpp; - bool valid = DecodeSingleSprite(&sprite[zoom_lvl], file_slot, file_pos, sprite_type, decomp_size, type, zoom_lvl, colour, 2); - if (FioGetPos() != start_pos + num) { - WarnCorruptSprite(file_slot, file_pos, __LINE__); + bool valid = DecodeSingleSprite(&sprite[zoom_lvl], file, file_pos, sprite_type, decomp_size, type, zoom_lvl, colour, 2); + if (file.GetPos() != start_pos + num) { + WarnCorruptSprite(file, file_pos, __LINE__); return 0; } if (valid) SetBit(loaded_sprites, zoom_lvl); } else { /* Not the wanted zoom level or colour depth, continue searching. */ - FioSkipBytes(num - 2); + file.SkipBytes(num - 2); } - } while (FioReadDword() == id); + } while (file.ReadDword() == id); return loaded_sprites; } -uint8 SpriteLoaderGrf::LoadSprite(SpriteLoader::Sprite *sprite, uint8 file_slot, size_t file_pos, SpriteType sprite_type, bool load_32bpp) +uint8 SpriteLoaderGrf::LoadSprite(SpriteLoader::Sprite *sprite, SpriteFile &file, size_t file_pos, SpriteType sprite_type, bool load_32bpp) { if (this->container_ver >= 2) { - return LoadSpriteV2(sprite, file_slot, file_pos, sprite_type, load_32bpp); + return LoadSpriteV2(sprite, file, file_pos, sprite_type, load_32bpp); } else { - return LoadSpriteV1(sprite, file_slot, file_pos, sprite_type, load_32bpp); + return LoadSpriteV1(sprite, file, file_pos, sprite_type, load_32bpp); } } diff --git a/src/spriteloader/grf.hpp b/src/spriteloader/grf.hpp index 20d60edf51..c100590afb 100644 --- a/src/spriteloader/grf.hpp +++ b/src/spriteloader/grf.hpp @@ -17,7 +17,7 @@ class SpriteLoaderGrf : public SpriteLoader { byte container_ver; public: SpriteLoaderGrf(byte container_ver) : container_ver(container_ver) {} - uint8 LoadSprite(SpriteLoader::Sprite *sprite, uint8 file_slot, size_t file_pos, SpriteType sprite_type, bool load_32bpp); + uint8 LoadSprite(SpriteLoader::Sprite *sprite, SpriteFile &file, size_t file_pos, SpriteType sprite_type, bool load_32bpp); }; #endif /* SPRITELOADER_GRF_HPP */ diff --git a/src/spriteloader/sprite_file.cpp b/src/spriteloader/sprite_file.cpp new file mode 100644 index 0000000000..be7160628b --- /dev/null +++ b/src/spriteloader/sprite_file.cpp @@ -0,0 +1,50 @@ +/* + * This file is part of OpenTTD. + * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. + * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see . + */ + +/** @file sprite_file.cpp Implementation of logic specific to the SpriteFile class. */ + +#include "../stdafx.h" +#include "sprite_file_type.hpp" + +/** Signature of a container version 2 GRF. */ +extern const byte _grf_cont_v2_sig[8] = {'G', 'R', 'F', 0x82, 0x0D, 0x0A, 0x1A, 0x0A}; + +/** + * Get the container version of the currently opened GRF file. + * @return Container version of the GRF file or 0 if the file is corrupt/no GRF file. + */ +static byte GetGRFContainerVersion(SpriteFile &file) +{ + size_t pos = file.GetPos(); + + if (file.ReadWord() == 0) { + /* Check for GRF container version 2, which is identified by the bytes + * '47 52 46 82 0D 0A 1A 0A' at the start of the file. */ + for (uint i = 0; i < lengthof(_grf_cont_v2_sig); i++) { + if (file.ReadByte() != _grf_cont_v2_sig[i]) return 0; // Invalid format + } + + return 2; + } + + /* Container version 1 has no header, rewind to start. */ + file.SeekTo(pos, SEEK_SET); + return 1; +} + +/** + * Create the SpriteFile. + * @param filename Name of the file at the disk. + * @param subdir The sub directory to search this file in. + * @param palette_remap Whether a palette remap needs to be performed for this file. + */ +SpriteFile::SpriteFile(const std::string &filename, Subdirectory subdir, bool palette_remap) + : RandomAccessFile(filename, subdir), palette_remap(palette_remap) +{ + this->container_version = GetGRFContainerVersion(*this); + this->content_begin = this->GetPos(); +} diff --git a/src/spriteloader/sprite_file_type.hpp b/src/spriteloader/sprite_file_type.hpp new file mode 100644 index 0000000000..b7492afade --- /dev/null +++ b/src/spriteloader/sprite_file_type.hpp @@ -0,0 +1,46 @@ +/* + * This file is part of OpenTTD. + * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. + * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see . + */ + +/** @file sprite_file_type.hpp Random Access File specialised for accessing sprites. */ + +#ifndef SPRITE_FILE_TYPE_HPP +#define SPRITE_FILE_TYPE_HPP + +#include "../random_access_file_type.h" + +/** + * RandomAccessFile with some extra information specific for sprite files. + * It automatically detects and stores the container version upload opening the file. + */ +class SpriteFile : public RandomAccessFile { + bool palette_remap; ///< Whether or not a remap of the palette is required for this file. + byte container_version; ///< Container format of the sprite file. + size_t content_begin; ///< The begin of the content of the sprite file, i.e. after the container metadata. +public: + SpriteFile(const std::string &filename, Subdirectory subdir, bool palette_remap); + SpriteFile(const SpriteFile&) = delete; + void operator=(const SpriteFile&) = delete; + + /** + * Whether a palette remap is needed when loading sprites from this file. + * @return True when needed, otherwise false. + */ + bool NeedsPaletteRemap() const { return this->palette_remap; } + + /** + * Get the version number of container type used by the file. + * @return The version. + */ + byte GetContainerVersion() const { return this->container_version; } + + /** + * Seek to the begin of the content, i.e. the position just after the container version has been determined. + */ + void SeekToBegin() { this->SeekTo(this->content_begin, SEEK_SET); } +}; + +#endif /* SPRITE_FILE_TYPE_HPP */ diff --git a/src/spriteloader/spriteloader.hpp b/src/spriteloader/spriteloader.hpp index 7b24746332..beadbb6591 100644 --- a/src/spriteloader/spriteloader.hpp +++ b/src/spriteloader/spriteloader.hpp @@ -13,6 +13,7 @@ #include "../core/alloc_type.hpp" #include "../core/enum_type.hpp" #include "../gfx_type.h" +#include "sprite_file_type.hpp" struct Sprite; typedef void *AllocatorProc(size_t size); @@ -73,7 +74,7 @@ public: * @param load_32bpp True if 32bpp sprites should be loaded, false for a 8bpp sprite. * @return Bit mask of the zoom levels successfully loaded or 0 if no sprite could be loaded. */ - virtual uint8 LoadSprite(SpriteLoader::Sprite *sprite, uint8 file_slot, size_t file_pos, SpriteType sprite_type, bool load_32bpp) = 0; + virtual uint8 LoadSprite(SpriteLoader::Sprite *sprite, SpriteFile &file, size_t file_pos, SpriteType sprite_type, bool load_32bpp) = 0; virtual ~SpriteLoader() { } }; From 10e35ca8e4249a7f443b5c89dd9a59df83052d0d Mon Sep 17 00:00:00 2001 From: Rubidium Date: Wed, 14 Apr 2021 17:41:45 +0200 Subject: [PATCH 18/61] Codechange: let NewGRF make use of SpriteFile instead of most of the FIO slot functions --- src/gfxinit.cpp | 20 ++--- src/newgrf.cpp | 175 +++++++++++++++++++++++------------------- src/newgrf.h | 4 +- src/newgrf_config.cpp | 4 +- 4 files changed, 110 insertions(+), 93 deletions(-) diff --git a/src/gfxinit.cpp b/src/gfxinit.cpp index 3bacc5af65..063d777edf 100644 --- a/src/gfxinit.cpp +++ b/src/gfxinit.cpp @@ -39,16 +39,15 @@ static const SpriteID * const _landscape_spriteindexes[] = { * Load an old fashioned GRF file. * @param filename The name of the file to open. * @param load_index The offset of the first sprite. - * @param file_index The Fio offset to load the file in. * @param needs_palette_remap Whether the colours in the GRF file need a palette remap. * @return The number of loaded sprites. */ -static uint LoadGrfFile(const char *filename, uint load_index, int file_index, bool needs_palette_remap) +static uint LoadGrfFile(const char *filename, uint load_index, bool needs_palette_remap) { uint load_index_org = load_index; uint sprite_id = 0; - SpriteFile &file = FioOpenFile(file_index, filename, BASESET_DIR, needs_palette_remap); + SpriteFile &file = OpenCachedSpriteFile(filename, BASESET_DIR, needs_palette_remap); DEBUG(sprite, 2, "Reading grf-file '%s'", filename); @@ -77,16 +76,15 @@ static uint LoadGrfFile(const char *filename, uint load_index, int file_index, b * Load an old fashioned GRF file to replace already loaded sprites. * @param filename The name of the file to open. * @param index_tbl The offsets of each of the sprites. - * @param file_index The Fio offset to load the file in. * @param needs_palette_remap Whether the colours in the GRF file need a palette remap. * @return The number of loaded sprites. */ -static void LoadGrfFileIndexed(const char *filename, const SpriteID *index_tbl, int file_index, bool needs_palette_remap) +static void LoadGrfFileIndexed(const char *filename, const SpriteID *index_tbl, bool needs_palette_remap) { uint start; uint sprite_id = 0; - SpriteFile &file = FioOpenFile(file_index, filename, BASESET_DIR, needs_palette_remap); + SpriteFile &file = OpenCachedSpriteFile(filename, BASESET_DIR, needs_palette_remap); DEBUG(sprite, 2, "Reading indexed grf-file '%s'", filename); @@ -161,10 +159,9 @@ void CheckExternalFiles() /** Actually load the sprite tables. */ static void LoadSpriteTables() { - uint i = FIRST_GRF_SLOT; const GraphicsSet *used_set = BaseGraphics::GetUsedSet(); - LoadGrfFile(used_set->files[GFT_BASE].filename, 0, i++, (PAL_DOS != used_set->palette)); + LoadGrfFile(used_set->files[GFT_BASE].filename, 0, PAL_DOS != used_set->palette); /* * The second basic file always starts at the given location and does @@ -172,7 +169,7 @@ static void LoadSpriteTables() * has a few sprites less. However, we do not care about those missing * sprites as they are not shown anyway (logos in intro game). */ - LoadGrfFile(used_set->files[GFT_LOGOS].filename, 4793, i++, (PAL_DOS != used_set->palette)); + LoadGrfFile(used_set->files[GFT_LOGOS].filename, 4793, PAL_DOS != used_set->palette); /* * Load additional sprites for climates other than temperate. @@ -183,8 +180,7 @@ static void LoadSpriteTables() LoadGrfFileIndexed( used_set->files[GFT_ARCTIC + _settings_game.game_creation.landscape - 1].filename, _landscape_spriteindexes[_settings_game.game_creation.landscape - 1], - i++, - (PAL_DOS != used_set->palette) + PAL_DOS != used_set->palette ); } @@ -223,7 +219,7 @@ static void LoadSpriteTables() master->next = extra; _grfconfig = master; - LoadNewGRF(SPR_NEWGRFS_BASE, i, 2); + LoadNewGRF(SPR_NEWGRFS_BASE, 2); uint total_extra_graphics = SPR_NEWGRFS_BASE - SPR_OPENTTD_BASE; _missing_extra_graphics = GetSpriteCountForFile(used_set->files[GFT_EXTRA].filename, SPR_OPENTTD_BASE, SPR_NEWGRFS_BASE); diff --git a/src/newgrf.cpp b/src/newgrf.cpp index aadd1a3a92..7b8ab9de1d 100644 --- a/src/newgrf.cpp +++ b/src/newgrf.cpp @@ -80,6 +80,7 @@ static uint32 _ttdpatch_flags[8]; GRFLoadedFeatures _loaded_newgrf_features; static const uint MAX_SPRITEGROUP = UINT8_MAX; ///< Maximum GRF-local ID for a spritegroup. +static const uint MAX_GRF_COUNT = 128; ///< Maximum number of NewGRF files that could be loaded. /** Temporary data during loading of GRFs */ struct GrfProcessingState { @@ -6395,19 +6396,20 @@ static void CfgApply(ByteReader *buf) * to place where parameter is to be stored. */ /* Preload the next sprite */ - size_t pos = FioGetPos(); - uint32 num = _cur.file->GetContainerVersion() >= 2 ? FioReadDword() : FioReadWord(); - uint8 type = FioReadByte(); + SpriteFile &file = *_cur.file; + size_t pos = file.GetPos(); + uint32 num = file.GetContainerVersion() >= 2 ? file.ReadDword() : file.ReadWord(); + uint8 type = file.ReadByte(); byte *preload_sprite = nullptr; /* Check if the sprite is a pseudo sprite. We can't operate on real sprites. */ if (type == 0xFF) { preload_sprite = MallocT(num); - FioReadBlock(preload_sprite, num); + file.ReadBlock(preload_sprite, num); } /* Reset the file position to the start of the next sprite */ - FioSeekTo(pos, SEEK_SET); + file.SeekTo(pos, SEEK_SET); if (type != 0xFF) { grfmsg(2, "CfgApply: Ignoring (next sprite is real, unsupported)"); @@ -6653,7 +6655,7 @@ static void SkipIf(ByteReader *buf) if (choice != nullptr) { grfmsg(2, "SkipIf: Jumping to label 0x%0X at line %d, test was true", choice->label, choice->nfo_line); - FioSeekTo(choice->pos, SEEK_SET); + _cur.file->SeekTo(choice->pos, SEEK_SET); _cur.nfo_line = choice->nfo_line; return; } @@ -7493,7 +7495,7 @@ static void DefineGotoLabel(ByteReader *buf) GRFLabel *label = MallocT(1); label->label = nfo_label; label->nfo_line = _cur.nfo_line; - label->pos = FioGetPos(); + label->pos = _cur.file->GetPos(); label->next = nullptr; /* Set up a linked list of goto targets which we will search in an Action 0x7/0x9 */ @@ -7516,8 +7518,8 @@ static void DefineGotoLabel(ByteReader *buf) static void ImportGRFSound(SoundEntry *sound) { const GRFFile *file; - uint32 grfid = FioReadDword(); - SoundID sound_id = FioReadWord(); + uint32 grfid = _cur.file->ReadDword(); + SoundID sound_id = _cur.file->ReadWord(); file = GetFileByGRFID(grfid); if (file == nullptr || file->sound_offset == 0) { @@ -7577,6 +7579,8 @@ static void GRFSound(ByteReader *buf) sound = GetSound(_cur.grffile->sound_offset); } + SpriteFile &file = *_cur.file; + byte grf_container_version = file.GetContainerVersion(); for (int i = 0; i < num; i++) { _cur.nfo_line++; @@ -7584,22 +7588,21 @@ static void GRFSound(ByteReader *buf) * While this is invalid, we do not check for this. But we should prevent it from causing bigger trouble */ bool invalid = i >= _cur.grffile->num_sounds; - size_t offs = FioGetPos(); + size_t offs = file.GetPos(); - byte grf_container_version = _cur.file->GetContainerVersion(); - uint32 len = grf_container_version >= 2 ? FioReadDword() : FioReadWord(); - byte type = FioReadByte(); + uint32 len = grf_container_version >= 2 ? file.ReadDword() : file.ReadWord(); + byte type = file.ReadByte(); if (grf_container_version >= 2 && type == 0xFD) { /* Reference to sprite section. */ if (invalid) { grfmsg(1, "GRFSound: Sound index out of range (multiple Action 11?)"); - FioSkipBytes(len); + file.SkipBytes(len); } else if (len != 4) { grfmsg(1, "GRFSound: Invalid sprite section import"); - FioSkipBytes(len); + file.SkipBytes(len); } else { - uint32 id = FioReadDword(); + uint32 id = file.ReadDword(); if (_cur.stage == GLS_INIT) LoadGRFSound(GetGRFSpriteOffset(id), sound + i); } continue; @@ -7607,17 +7610,17 @@ static void GRFSound(ByteReader *buf) if (type != 0xFF) { grfmsg(1, "GRFSound: Unexpected RealSprite found, skipping"); - FioSkipBytes(7); + file.SkipBytes(7); SkipSpriteData(*_cur.file, type, len - 8); continue; } if (invalid) { grfmsg(1, "GRFSound: Sound index out of range (multiple Action 11?)"); - FioSkipBytes(len); + file.SkipBytes(len); } - byte action = FioReadByte(); + byte action = file.ReadByte(); switch (action) { case 0xFF: /* Allocate sound only in init stage. */ @@ -7628,23 +7631,23 @@ static void GRFSound(ByteReader *buf) LoadGRFSound(offs, sound + i); } } - FioSkipBytes(len - 1); // already read + file.SkipBytes(len - 1); // already read break; case 0xFE: if (_cur.stage == GLS_ACTIVATION) { /* XXX 'Action 0xFE' isn't really specified. It is only mentioned for * importing sounds, so this is probably all wrong... */ - if (FioReadByte() != 0) grfmsg(1, "GRFSound: Import type mismatch"); + if (file.ReadByte() != 0) grfmsg(1, "GRFSound: Import type mismatch"); ImportGRFSound(sound + i); } else { - FioSkipBytes(len - 1); // already read + file.SkipBytes(len - 1); // already read } break; default: grfmsg(1, "GRFSound: Unexpected Action %x found, skipping", action); - FioSkipBytes(len - 1); // already read + file.SkipBytes(len - 1); // already read break; } } @@ -9234,14 +9237,14 @@ static void DecodeSpecialSprite(byte *buf, uint num, GrfLoadingStage stage) if (it == _grf_line_to_action6_sprite_override.end()) { /* No preloaded sprite to work with; read the * pseudo sprite content. */ - FioReadBlock(buf, num); + _cur.file->ReadBlock(buf, num); } else { /* Use the preloaded sprite data. */ buf = _grf_line_to_action6_sprite_override[location]; grfmsg(7, "DecodeSpecialSprite: Using preloaded pseudo sprite data"); /* Skip the real (original) content of this action. */ - FioSeekTo(num, SEEK_CUR); + _cur.file->SeekTo(num, SEEK_CUR); } ByteReader br(buf, buf + num); @@ -9268,47 +9271,20 @@ static void DecodeSpecialSprite(byte *buf, uint num, GrfLoadingStage stage) } } - /** - * Load a particular NewGRF. - * @param config The configuration of the to be loaded NewGRF. - * @param file_index The Fio index of the first NewGRF to load. - * @param stage The loading stage of the NewGRF. - * @param subdir The sub directory to find the NewGRF in. + * Load a particular NewGRF from a SpriteFile. + * @param config The configuration of the to be loaded NewGRF. + * @param stage The loading stage of the NewGRF. + * @param file The file to load the GRF data from. */ -void LoadNewGRFFile(GRFConfig *config, uint file_index, GrfLoadingStage stage, Subdirectory subdir) +static void LoadNewGRFFileFromFile(GRFConfig *config, GrfLoadingStage stage, SpriteFile &file) { - const char *filename = config->filename; - - /* A .grf file is activated only if it was active when the game was - * started. If a game is loaded, only its active .grfs will be - * reactivated, unless "loadallgraphics on" is used. A .grf file is - * considered active if its action 8 has been processed, i.e. its - * action 8 hasn't been skipped using an action 7. - * - * During activation, only actions 0, 1, 2, 3, 4, 5, 7, 8, 9, 0A and 0B are - * carried out. All others are ignored, because they only need to be - * processed once at initialization. */ - if (stage != GLS_FILESCAN && stage != GLS_SAFETYSCAN && stage != GLS_LABELSCAN) { - _cur.grffile = GetFileByFilename(filename); - if (_cur.grffile == nullptr) usererror("File '%s' lost in cache.\n", filename); - if (stage == GLS_RESERVE && config->status != GCS_INITIALISED) return; - if (stage == GLS_ACTIVATION && !HasBit(config->flags, GCF_RESERVED)) return; - } - - if (file_index >= MAX_FILE_SLOTS) { - DEBUG(grf, 0, "'%s' is not loaded as the maximum number of file slots has been reached", filename); - config->status = GCS_DISABLED; - config->error = new GRFError(STR_NEWGRF_ERROR_MSG_FATAL, STR_NEWGRF_ERROR_TOO_MANY_NEWGRFS_LOADED); - return; - } - - _cur.file = &FioOpenFile(file_index, filename, subdir, config->palette & GRFP_USE_MASK); + _cur.file = &file; _cur.grfconfig = config; - DEBUG(grf, 2, "LoadNewGRFFile: Reading NewGRF-file '%s'", filename); + DEBUG(grf, 2, "LoadNewGRFFile: Reading NewGRF-file '%s'", config->filename); - byte grf_container_version = _cur.file->GetContainerVersion(); + byte grf_container_version = file.GetContainerVersion(); if (grf_container_version == 0) { DEBUG(grf, 7, "LoadNewGRFFile: Custom .grf has invalid format"); return; @@ -9317,15 +9293,15 @@ void LoadNewGRFFile(GRFConfig *config, uint file_index, GrfLoadingStage stage, S if (stage == GLS_INIT || stage == GLS_ACTIVATION) { /* We need the sprite offsets in the init stage for NewGRF sounds * and in the activation stage for real sprites. */ - ReadGRFSpriteOffsets(*_cur.file); + ReadGRFSpriteOffsets(file); } else { /* Skip sprite section offset if present. */ - if (grf_container_version >= 2) FioReadDword(); + if (grf_container_version >= 2) file.ReadDword(); } if (grf_container_version >= 2) { /* Read compression value. */ - byte compression = FioReadByte(); + byte compression = file.ReadByte(); if (compression != 0) { DEBUG(grf, 7, "LoadNewGRFFile: Unsupported compression format"); return; @@ -9335,9 +9311,9 @@ void LoadNewGRFFile(GRFConfig *config, uint file_index, GrfLoadingStage stage, S /* Skip the first sprite; we don't care about how many sprites this * does contain; newest TTDPatches and George's longvehicles don't * neither, apparently. */ - uint32 num = grf_container_version >= 2 ? FioReadDword() : FioReadWord(); - if (num == 4 && FioReadByte() == 0xFF) { - FioReadDword(); + uint32 num = grf_container_version >= 2 ? file.ReadDword() : file.ReadWord(); + if (num == 4 && file.ReadByte() == 0xFF) { + file.ReadDword(); } else { DEBUG(grf, 7, "LoadNewGRFFile: Custom .grf has invalid format"); return; @@ -9347,8 +9323,8 @@ void LoadNewGRFFile(GRFConfig *config, uint file_index, GrfLoadingStage stage, S ReusableBuffer buf; - while ((num = (grf_container_version >= 2 ? FioReadDword() : FioReadWord())) != 0) { - byte type = FioReadByte(); + while ((num = (grf_container_version >= 2 ? file.ReadDword() : file.ReadWord())) != 0) { + byte type = file.ReadByte(); _cur.nfo_line++; if (type == 0xFF) { @@ -9360,7 +9336,7 @@ void LoadNewGRFFile(GRFConfig *config, uint file_index, GrfLoadingStage stage, S continue; } else { - FioSkipBytes(num); + file.SkipBytes(num); } } else { if (_cur.skip_sprites == 0) { @@ -9371,10 +9347,10 @@ void LoadNewGRFFile(GRFConfig *config, uint file_index, GrfLoadingStage stage, S if (grf_container_version >= 2 && type == 0xFD) { /* Reference to data section. Container version >= 2 only. */ - FioSkipBytes(num); + file.SkipBytes(num); } else { - FioSkipBytes(7); - SkipSpriteData(*_cur.file, type, num - 8); + file.SkipBytes(7); + SkipSpriteData(file, type, num - 8); } } @@ -9382,6 +9358,43 @@ void LoadNewGRFFile(GRFConfig *config, uint file_index, GrfLoadingStage stage, S } } +/** + * Load a particular NewGRF. + * @param config The configuration of the to be loaded NewGRF. + * @param stage The loading stage of the NewGRF. + * @param subdir The sub directory to find the NewGRF in. + * @param temporary The NewGRF/sprite file is to be loaded temporarily and should be closed immediately, + * contrary to loading the SpriteFile and having it cached by the SpriteCache. + */ +void LoadNewGRFFile(GRFConfig *config, GrfLoadingStage stage, Subdirectory subdir, bool temporary) +{ + const char *filename = config->filename; + + /* A .grf file is activated only if it was active when the game was + * started. If a game is loaded, only its active .grfs will be + * reactivated, unless "loadallgraphics on" is used. A .grf file is + * considered active if its action 8 has been processed, i.e. its + * action 8 hasn't been skipped using an action 7. + * + * During activation, only actions 0, 1, 2, 3, 4, 5, 7, 8, 9, 0A and 0B are + * carried out. All others are ignored, because they only need to be + * processed once at initialization. */ + if (stage != GLS_FILESCAN && stage != GLS_SAFETYSCAN && stage != GLS_LABELSCAN) { + _cur.grffile = GetFileByFilename(filename); + if (_cur.grffile == nullptr) usererror("File '%s' lost in cache.\n", filename); + if (stage == GLS_RESERVE && config->status != GCS_INITIALISED) return; + if (stage == GLS_ACTIVATION && !HasBit(config->flags, GCF_RESERVED)) return; + } + + bool needs_palette_remap = config->palette & GRFP_USE_MASK; + if (temporary) { + SpriteFile temporarySpriteFile(filename, subdir, needs_palette_remap); + LoadNewGRFFileFromFile(config, stage, temporarySpriteFile); + } else { + LoadNewGRFFileFromFile(config, stage, OpenCachedSpriteFile(filename, subdir, needs_palette_remap)); + } +} + /** * Relocates the old shore sprites at new positions. * @@ -9677,10 +9690,9 @@ static void AfterLoadGRFs() /** * Load all the NewGRFs. * @param load_index The offset for the first sprite to add. - * @param file_index The Fio index of the first NewGRF to load. * @param num_baseset Number of NewGRFs at the front of the list to look up in the baseset dir instead of the newgrf dir. */ -void LoadNewGRF(uint load_index, uint file_index, uint num_baseset) +void LoadNewGRF(uint load_index, uint num_baseset) { /* In case of networking we need to "sync" the start values * so all NewGRFs are loaded equally. For this we use the @@ -9738,7 +9750,7 @@ void LoadNewGRF(uint load_index, uint file_index, uint num_baseset) } } - uint slot = file_index; + uint num_grfs = 0; uint num_non_static = 0; _cur.stage = stage; @@ -9746,7 +9758,7 @@ void LoadNewGRF(uint load_index, uint file_index, uint num_baseset) if (c->status == GCS_DISABLED || c->status == GCS_NOT_FOUND) continue; if (stage > GLS_INIT && HasBit(c->flags, GCF_INIT_ONLY)) continue; - Subdirectory subdir = slot < file_index + num_baseset ? BASESET_DIR : NEWGRF_DIR; + Subdirectory subdir = num_grfs < num_baseset ? BASESET_DIR : NEWGRF_DIR; if (!FioCheckFileExists(c->filename, subdir)) { DEBUG(grf, 0, "NewGRF file is missing '%s'; disabling", c->filename); c->status = GCS_NOT_FOUND; @@ -9764,7 +9776,16 @@ void LoadNewGRF(uint load_index, uint file_index, uint num_baseset) } num_non_static++; } - LoadNewGRFFile(c, slot++, stage, subdir); + + if (num_grfs >= MAX_GRF_COUNT) { + DEBUG(grf, 0, "'%s' is not loaded as the maximum number of file slots 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); + continue; + } + num_grfs++; + + LoadNewGRFFile(c, stage, subdir, false); if (stage == GLS_RESERVE) { SetBit(c->flags, GCF_RESERVED); } else if (stage == GLS_ACTIVATION) { diff --git a/src/newgrf.h b/src/newgrf.h index 477bfa892c..eb6c22d477 100644 --- a/src/newgrf.h +++ b/src/newgrf.h @@ -192,8 +192,8 @@ static inline bool HasGrfMiscBit(GrfMiscBit bit) /* Indicates which are the newgrf features currently loaded ingame */ extern GRFLoadedFeatures _loaded_newgrf_features; -void LoadNewGRFFile(struct GRFConfig *config, uint file_index, GrfLoadingStage stage, Subdirectory subdir); -void LoadNewGRF(uint load_index, uint file_index, uint num_baseset); +void LoadNewGRFFile(struct GRFConfig *config, GrfLoadingStage stage, Subdirectory subdir, bool temporary); +void LoadNewGRF(uint load_index, uint num_baseset); void ReloadNewGRFData(); // in saveload/afterload.cpp void ResetNewGRFData(); void ResetPersistentNewGRFData(); diff --git a/src/newgrf_config.cpp b/src/newgrf_config.cpp index a4ec52f0f8..b92b297da0 100644 --- a/src/newgrf_config.cpp +++ b/src/newgrf_config.cpp @@ -375,7 +375,7 @@ bool FillGRFDetails(GRFConfig *config, bool is_static, Subdirectory subdir) } /* Find and load the Action 8 information */ - LoadNewGRFFile(config, CONFIG_SLOT, GLS_FILESCAN, subdir); + LoadNewGRFFile(config, GLS_FILESCAN, subdir, true); config->SetSuitablePalette(); config->FinalizeParameterInfo(); @@ -384,7 +384,7 @@ bool FillGRFDetails(GRFConfig *config, bool is_static, Subdirectory subdir) if (is_static) { /* Perform a 'safety scan' for static GRFs */ - LoadNewGRFFile(config, CONFIG_SLOT, GLS_SAFETYSCAN, subdir); + LoadNewGRFFile(config, GLS_SAFETYSCAN, subdir, true); /* GCF_UNSAFE is set if GLS_SAFETYSCAN finds unsafe actions */ if (HasBit(config->flags, GCF_UNSAFE)) return false; From fa6abe16463de8f252bac2322c9f84b6d02c9abc Mon Sep 17 00:00:00 2001 From: Rubidium Date: Wed, 21 Apr 2021 18:40:37 +0200 Subject: [PATCH 19/61] Cleanup: remove the old FIO slot functions --- src/fileio.cpp | 124 ---------------------------------------------- src/fileio_func.h | 13 ----- src/fios.h | 14 ------ src/openttd.cpp | 3 -- 4 files changed, 154 deletions(-) diff --git a/src/fileio.cpp b/src/fileio.cpp index 6d5e2da287..bd05a4e7d5 100644 --- a/src/fileio.cpp +++ b/src/fileio.cpp @@ -30,136 +30,12 @@ #include "safeguards.h" -static SpriteFile *_fio_current_file; ///< current file handle for the Fio functions -static std::array _fio_files; ///< array of random access files we can have open - /** Whether the working directory should be scanned. */ static bool _do_scan_working_directory = true; extern std::string _config_file; extern std::string _highscore_file; -/** - * Transition helper to get the SpriteFile associated with a given slot. - */ -SpriteFile *FioGetSpriteFile(int slot) -{ - return _fio_files[slot]; -} - -/** - * Get position in the current file. - * @return Position in the file. - */ -size_t FioGetPos() -{ - return _fio_current_file->GetPos(); -} - -/** - * Get the filename associated with a slot. - * @param slot Index of queried file. - * @return Name of the file. - */ -const char *FioGetFilename(uint8 slot) -{ - return _fio_current_file->GetSimplifiedFilename().c_str(); -} - -/** - * Seek in the current file. - * @param pos New position. - * @param mode Type of seek (\c SEEK_CUR means \a pos is relative to current position, \c SEEK_SET means \a pos is absolute). - */ -void FioSeekTo(size_t pos, int mode) -{ - _fio_current_file->SeekTo(pos, mode); -} - -/** - * Switch to a different file and seek to a position. - * @param slot Slot number of the new file. - * @param pos New absolute position in the new file. - */ -void FioSeekToFile(uint8 slot, size_t pos) -{ - SpriteFile *file = _fio_files[slot]; - assert(file != nullptr); - _fio_current_file = file; - _fio_current_file->SeekTo(pos, SEEK_SET); -} - -/** - * Read a byte from the file. - * @return Read byte. - */ -byte FioReadByte() -{ - return _fio_current_file->ReadByte(); -} - -/** - * Skip \a n bytes ahead in the file. - * @param n Number of bytes to skip reading. - */ -void FioSkipBytes(int n) -{ - return _fio_current_file->SkipBytes(n); -} - -/** - * Read a word (16 bits) from the file (in low endian format). - * @return Read word. - */ -uint16 FioReadWord() -{ - return _fio_current_file->ReadWord(); -} - -/** - * Read a double word (32 bits) from the file (in low endian format). - * @return Read word. - */ -uint32 FioReadDword() -{ - return _fio_current_file->ReadDword(); -} - -/** - * Read a block. - * @param ptr Destination buffer. - * @param size Number of bytes to read. - */ -void FioReadBlock(void *ptr, size_t size) -{ - _fio_current_file->ReadBlock(ptr, size); -} - -/** Close all slotted open files. */ -void FioCloseAll() -{ - for (int i = 0; i != lengthof(_fio_files); i++) { - delete _fio_files[i]; - _fio_files[i] = nullptr; - } -} - -/** - * Open a slotted file. - * @param slot Index to assign. - * @param filename Name of the file at the disk. - * @param subdir The sub directory to search this file in. - * @param palette_remap Whether palette remapping needs to take place. - */ -SpriteFile &FioOpenFile(int slot, const std::string &filename, Subdirectory subdir, bool palette_remap) -{ - SpriteFile *file = new SpriteFile(filename, subdir, palette_remap); - delete _fio_files[slot]; - _fio_files[slot] = file; - _fio_current_file = file; - return *file; -} - static const char * const _subdirs[] = { "", "save" PATHSEP, diff --git a/src/fileio_func.h b/src/fileio_func.h index b3ed9e138d..baa8cb1bb1 100644 --- a/src/fileio_func.h +++ b/src/fileio_func.h @@ -15,19 +15,6 @@ #include #include -void FioSeekTo(size_t pos, int mode); -void FioSeekToFile(uint8 slot, size_t pos); -size_t FioGetPos(); -const char *FioGetFilename(uint8 slot); -byte FioReadByte(); -uint16 FioReadWord(); -uint32 FioReadDword(); -void FioCloseAll(); -class SpriteFile &FioOpenFile(int slot, const std::string &filename, Subdirectory subdir, bool palette_remap = false); -void FioReadBlock(void *ptr, size_t size); -void FioSkipBytes(int n); -class SpriteFile *FioGetSpriteFile(int slot); - void FioFCloseFile(FILE *f); FILE *FioFOpenFile(const std::string &filename, const char *mode, Subdirectory subdir, size_t *filesize = nullptr); bool FioCheckFileExists(const std::string &filename, Subdirectory subdir); diff --git a/src/fios.h b/src/fios.h index 5029d4862f..34504d5e00 100644 --- a/src/fios.h +++ b/src/fios.h @@ -83,20 +83,6 @@ struct LoadCheckData { extern LoadCheckData _load_check_data; - -enum FileSlots { - /** - * Slot used for the GRF scanning and such. - * This slot is used for all temporary accesses to files when scanning/testing files, - * and thus cannot be used for files, which are continuously accessed during a game. - */ - CONFIG_SLOT = 0, - /** First slot usable for (New)GRFs used during the game. */ - FIRST_GRF_SLOT = 2, - /** Maximum number of slots. */ - MAX_FILE_SLOTS = 128, -}; - /** Deals with finding savegames */ struct FiosItem { FiosType type; diff --git a/src/openttd.cpp b/src/openttd.cpp index d8b197466a..ea802b3db9 100644 --- a/src/openttd.cpp +++ b/src/openttd.cpp @@ -324,9 +324,6 @@ static void ShutdownGame() /* No NewGRFs were loaded when it was still bootstrapping. */ if (_game_mode != GM_BOOTSTRAP) ResetNewGRFData(); - /* Close all and any open filehandles */ - FioCloseAll(); - UninitFreeType(); } From de940b1dbc7a1d6f245f3965bc43e8885b690441 Mon Sep 17 00:00:00 2001 From: rubidium42 Date: Sat, 8 May 2021 14:02:42 +0200 Subject: [PATCH 20/61] Fix fdc11a9: Missing sprite count determined on the wrong file --- src/gfxinit.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/gfxinit.cpp b/src/gfxinit.cpp index 063d777edf..713f6e5439 100644 --- a/src/gfxinit.cpp +++ b/src/gfxinit.cpp @@ -195,7 +195,8 @@ static void LoadSpriteTables() GRFConfig *top = _grfconfig; /* Default extra graphics */ - GRFConfig *master = new GRFConfig("OPENTTD.GRF"); + static const char *master_filename = "OPENTTD.GRF"; + GRFConfig *master = new GRFConfig(master_filename); master->palette |= GRFP_GRF_DOS; FillGRFDetails(master, false, BASESET_DIR); ClrBit(master->flags, GCF_INIT_ONLY); @@ -222,7 +223,7 @@ static void LoadSpriteTables() LoadNewGRF(SPR_NEWGRFS_BASE, 2); uint total_extra_graphics = SPR_NEWGRFS_BASE - SPR_OPENTTD_BASE; - _missing_extra_graphics = GetSpriteCountForFile(used_set->files[GFT_EXTRA].filename, SPR_OPENTTD_BASE, SPR_NEWGRFS_BASE); + _missing_extra_graphics = GetSpriteCountForFile(master_filename, SPR_OPENTTD_BASE, SPR_NEWGRFS_BASE); DEBUG(sprite, 1, "%u extra sprites, %u from baseset, %u from fallback", total_extra_graphics, total_extra_graphics - _missing_extra_graphics, _missing_extra_graphics); /* The original baseset extra graphics intentionally make use of the fallback graphics. From 664a8c3e85cef684610e0fa65fb2ea587e46232f Mon Sep 17 00:00:00 2001 From: Patric Stout Date: Sat, 8 May 2021 10:53:21 +0200 Subject: [PATCH 21/61] Codechange: move connection_string to private for TCPConnecter The most common case never needs access to it anymore. Make the one exception to this explicit. This means the fact that we store it is now an implementation detail. --- src/network/core/tcp.h | 3 +-- src/network/network.cpp | 5 ++++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/network/core/tcp.h b/src/network/core/tcp.h index 624555649d..1eddec6a44 100644 --- a/src/network/core/tcp.h +++ b/src/network/core/tcp.h @@ -74,6 +74,7 @@ private: std::chrono::steady_clock::time_point last_attempt; ///< Time we last tried to connect. std::atomic is_resolved = false; ///< Whether resolving is done. + std::string connection_string; ///< Current address we are connecting to (before resolving). void Resolve(); void OnResolved(addrinfo *ai); @@ -84,8 +85,6 @@ private: static void ResolveThunk(TCPConnecter *connecter); public: - std::string connection_string; ///< Current address we are connecting to (before resolving). - TCPConnecter(const std::string &connection_string, uint16 default_port); virtual ~TCPConnecter(); diff --git a/src/network/network.cpp b/src/network/network.cpp index 3965c200fd..5d12aa732b 100644 --- a/src/network/network.cpp +++ b/src/network/network.cpp @@ -1145,8 +1145,11 @@ static void NetworkGenerateServerId() } class TCPNetworkDebugConnecter : TCPConnecter { +private: + std::string connection_string; + public: - TCPNetworkDebugConnecter(const std::string &connection_string) : TCPConnecter(connection_string, NETWORK_DEFAULT_DEBUGLOG_PORT) {} + TCPNetworkDebugConnecter(const std::string &connection_string) : TCPConnecter(connection_string, NETWORK_DEFAULT_DEBUGLOG_PORT), connection_string(connection_string) {} void OnFailure() override { From 1b75a29d123db7aace2e8c2bf9c976e9ff8e89e3 Mon Sep 17 00:00:00 2001 From: Patric Stout Date: Sat, 8 May 2021 14:45:23 +0200 Subject: [PATCH 22/61] Fix: destroying a TCPConnecter that was still resolving made illegal writes Basically, we should join the resolve thread before we destruct the object. --- src/network/core/tcp.h | 5 ++++- src/network/core/tcp_connect.cpp | 7 ++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/network/core/tcp.h b/src/network/core/tcp.h index 1eddec6a44..46e3af8c66 100644 --- a/src/network/core/tcp.h +++ b/src/network/core/tcp.h @@ -18,6 +18,7 @@ #include #include #include +#include /** The states of sending the packets. */ enum SendPacketsState { @@ -65,6 +66,9 @@ public: */ class TCPConnecter { private: + std::thread resolve_thread; ///< Thread used during resolving. + std::atomic is_resolved = false; ///< Whether resolving is done. + addrinfo *ai = nullptr; ///< getaddrinfo() allocated linked-list of resolved addresses. std::vector addresses; ///< Addresses we can connect to. std::map sock_to_address; ///< Mapping of a socket to the real address it is connecting to. USed for DEBUG statements. @@ -73,7 +77,6 @@ private: std::vector sockets; ///< Pending connect() attempts. std::chrono::steady_clock::time_point last_attempt; ///< Time we last tried to connect. - std::atomic is_resolved = false; ///< Whether resolving is done. std::string connection_string; ///< Current address we are connecting to (before resolving). void Resolve(); diff --git a/src/network/core/tcp_connect.cpp b/src/network/core/tcp_connect.cpp index a1e369295a..96c82b5af8 100644 --- a/src/network/core/tcp_connect.cpp +++ b/src/network/core/tcp_connect.cpp @@ -32,13 +32,17 @@ TCPConnecter::TCPConnecter(const std::string &connection_string, uint16 default_ _tcp_connecters.push_back(this); - if (!StartNewThread(nullptr, "ottd:resolve", &TCPConnecter::ResolveThunk, this)) { + if (!StartNewThread(&this->resolve_thread, "ottd:resolve", &TCPConnecter::ResolveThunk, this)) { this->Resolve(); } } TCPConnecter::~TCPConnecter() { + if (this->resolve_thread.joinable()) { + this->resolve_thread.join(); + } + for (const auto &socket : this->sockets) { closesocket(socket); } @@ -187,6 +191,7 @@ void TCPConnecter::Resolve() this->ai = ai; this->OnResolved(ai); + this->is_resolved = true; } From fc91f1d1b221435dee870d84690e834e753e39da Mon Sep 17 00:00:00 2001 From: Patric Stout Date: Sat, 8 May 2021 14:50:56 +0200 Subject: [PATCH 23/61] Fix: don't do a network disconnect between two queries This meant that on opening the Multiplayer window, if you had more than one server configured, it would one by one cancel all pending queries and send a new. Result: only the last server was updated. --- src/network/network.cpp | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/network/network.cpp b/src/network/network.cpp index 5d12aa732b..ba20b0895d 100644 --- a/src/network/network.cpp +++ b/src/network/network.cpp @@ -628,11 +628,6 @@ private: public: TCPQueryConnecter(const std::string &connection_string, bool request_company_info) : TCPConnecter(connection_string, NETWORK_DEFAULT_PORT), request_company_info(request_company_info), connection_string(connection_string) {} - void OnFailure() override - { - NetworkDisconnect(); - } - void OnConnect(SOCKET s) override { _networking = true; @@ -650,7 +645,6 @@ void NetworkTCPQueryServer(const std::string &connection_string, bool request_co { if (!_network_available) return; - NetworkDisconnect(); NetworkInitialize(); new TCPQueryConnecter(connection_string, request_company_info); From b9ab3bd6b3f28b682bf5e155bf35d7cbb8b8224a Mon Sep 17 00:00:00 2001 From: Patric Stout Date: Tue, 4 May 2021 20:43:35 +0200 Subject: [PATCH 24/61] Fix: only query a manually added server if it isn't there yet But always mark it as manually, no matter if it was there or not. --- src/network/network.cpp | 9 +++++---- src/network/network_internal.h | 2 +- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/network/network.cpp b/src/network/network.cpp index ba20b0895d..daf2e1d3fd 100644 --- a/src/network/network.cpp +++ b/src/network/network.cpp @@ -657,7 +657,7 @@ void NetworkTCPQueryServer(const std::string &connection_string, bool request_co * @param connection_string The IP:port of the server to add. * @return The entry on the game list. */ -NetworkGameList *NetworkAddServer(const std::string &connection_string) +NetworkGameList *NetworkAddServer(const std::string &connection_string, bool manually) { if (connection_string.empty()) return nullptr; @@ -666,13 +666,14 @@ NetworkGameList *NetworkAddServer(const std::string &connection_string) if (item->info.server_name.empty()) { ClearGRFConfigList(&item->info.grfconfig); item->info.server_name = connection_string; - item->manually = true; NetworkRebuildHostList(); UpdateNetworkGameWindow(); + + NetworkTCPQueryServer(connection_string); } - NetworkTCPQueryServer(connection_string); + if (manually) item->manually = true; return item; } @@ -1202,7 +1203,7 @@ extern "C" { void CDECL em_openttd_add_server(const char *connection_string) { - NetworkAddServer(connection_string); + NetworkAddServer(connection_string, false); } } diff --git a/src/network/network_internal.h b/src/network/network_internal.h index 88af965cfc..ac5472fe18 100644 --- a/src/network/network_internal.h +++ b/src/network/network_internal.h @@ -90,7 +90,7 @@ extern CompanyMask _network_company_passworded; void NetworkTCPQueryServer(const std::string &connection_string, bool request_company_info = false); void GetBindAddresses(NetworkAddressList *addresses, uint16 port); -struct NetworkGameList *NetworkAddServer(const std::string &connection_string); +struct NetworkGameList *NetworkAddServer(const std::string &connection_string, bool manually = true); void NetworkRebuildHostList(); void UpdateNetworkGameWindow(); From 330a305c99766b2e62541a8956a00b2cbe7bbbe1 Mon Sep 17 00:00:00 2001 From: PeterN Date: Sat, 8 May 2021 21:01:16 +0100 Subject: [PATCH 25/61] Fix: Apply unscaled padding to Viewport inside WWT_INSET. (#9219) Since pixel dimensions in SetPadding() are scaled by GUI size, padding for inset viewports was excessive. Instead, automatically apply padding for WWT_INSET at widget level. This applies to all widgets inside a WWT_INSET, which in all instances is a NWID_VIEWPORT. --- src/industry_gui.cpp | 2 +- src/news_gui.cpp | 2 +- src/town_gui.cpp | 4 ++-- src/vehicle_gui.cpp | 2 +- src/waypoint_gui.cpp | 2 +- src/widget.cpp | 16 ++++++++++++++-- 6 files changed, 20 insertions(+), 8 deletions(-) diff --git a/src/industry_gui.cpp b/src/industry_gui.cpp index 147d4dc777..4e6c61ee21 100644 --- a/src/industry_gui.cpp +++ b/src/industry_gui.cpp @@ -1186,7 +1186,7 @@ static const NWidgetPart _nested_industry_view_widgets[] = { EndContainer(), NWidget(WWT_PANEL, COLOUR_CREAM), NWidget(WWT_INSET, COLOUR_CREAM), SetPadding(2, 2, 2, 2), - NWidget(NWID_VIEWPORT, INVALID_COLOUR, WID_IV_VIEWPORT), SetMinimalSize(254, 86), SetFill(1, 0), SetPadding(1, 1, 1, 1), SetResize(1, 1), + NWidget(NWID_VIEWPORT, INVALID_COLOUR, WID_IV_VIEWPORT), SetMinimalSize(254, 86), SetFill(1, 0), SetResize(1, 1), EndContainer(), EndContainer(), NWidget(WWT_PANEL, COLOUR_CREAM, WID_IV_INFO), SetMinimalSize(260, 2), SetResize(1, 0), diff --git a/src/news_gui.cpp b/src/news_gui.cpp index 8d0531682d..d3ddd463fa 100644 --- a/src/news_gui.cpp +++ b/src/news_gui.cpp @@ -192,7 +192,7 @@ static const NWidgetPart _nested_small_news_widgets[] = { /* Main part */ NWidget(WWT_PANEL, COLOUR_LIGHT_BLUE, WID_N_HEADLINE), NWidget(WWT_INSET, COLOUR_LIGHT_BLUE, WID_N_INSET), SetPadding(2, 2, 2, 2), - NWidget(NWID_VIEWPORT, INVALID_COLOUR, WID_N_VIEWPORT), SetPadding(1, 1, 1, 1), SetMinimalSize(274, 47), SetFill(1, 0), + NWidget(NWID_VIEWPORT, INVALID_COLOUR, WID_N_VIEWPORT), SetMinimalSize(274, 47), SetFill(1, 0), EndContainer(), NWidget(WWT_EMPTY, COLOUR_WHITE, WID_N_MESSAGE), SetMinimalSize(275, 20), SetFill(1, 0), SetPadding(0, 5, 0, 5), EndContainer(), diff --git a/src/town_gui.cpp b/src/town_gui.cpp index 22f8b757f8..bd5893561b 100644 --- a/src/town_gui.cpp +++ b/src/town_gui.cpp @@ -576,7 +576,7 @@ static const NWidgetPart _nested_town_game_view_widgets[] = { EndContainer(), NWidget(WWT_PANEL, COLOUR_BROWN), NWidget(WWT_INSET, COLOUR_BROWN), SetPadding(2, 2, 2, 2), - NWidget(NWID_VIEWPORT, INVALID_COLOUR, WID_TV_VIEWPORT), SetMinimalSize(254, 86), SetFill(1, 0), SetResize(1, 1), SetPadding(1, 1, 1, 1), + NWidget(NWID_VIEWPORT, INVALID_COLOUR, WID_TV_VIEWPORT), SetMinimalSize(254, 86), SetFill(1, 0), SetResize(1, 1), EndContainer(), EndContainer(), NWidget(WWT_PANEL, COLOUR_BROWN, WID_TV_INFO), SetMinimalSize(260, 32), SetResize(1, 0), SetFill(1, 0), EndContainer(), @@ -606,7 +606,7 @@ static const NWidgetPart _nested_town_editor_view_widgets[] = { EndContainer(), NWidget(WWT_PANEL, COLOUR_BROWN), NWidget(WWT_INSET, COLOUR_BROWN), SetPadding(2, 2, 2, 2), - NWidget(NWID_VIEWPORT, INVALID_COLOUR, WID_TV_VIEWPORT), SetMinimalSize(254, 86), SetFill(1, 1), SetResize(1, 1), SetPadding(1, 1, 1, 1), + NWidget(NWID_VIEWPORT, INVALID_COLOUR, WID_TV_VIEWPORT), SetMinimalSize(254, 86), SetFill(1, 1), SetResize(1, 1), EndContainer(), EndContainer(), NWidget(WWT_PANEL, COLOUR_BROWN, WID_TV_INFO), SetMinimalSize(260, 32), SetResize(1, 0), SetFill(1, 0), EndContainer(), diff --git a/src/vehicle_gui.cpp b/src/vehicle_gui.cpp index c6f2ee52a7..0066644919 100644 --- a/src/vehicle_gui.cpp +++ b/src/vehicle_gui.cpp @@ -2485,7 +2485,7 @@ static const NWidgetPart _nested_vehicle_view_widgets[] = { NWidget(NWID_HORIZONTAL), NWidget(WWT_PANEL, COLOUR_GREY), NWidget(WWT_INSET, COLOUR_GREY), SetPadding(2, 2, 2, 2), - NWidget(NWID_VIEWPORT, INVALID_COLOUR, WID_VV_VIEWPORT), SetMinimalSize(226, 84), SetResize(1, 1), SetPadding(1, 1, 1, 1), + NWidget(NWID_VIEWPORT, INVALID_COLOUR, WID_VV_VIEWPORT), SetMinimalSize(226, 84), SetResize(1, 1), EndContainer(), EndContainer(), NWidget(NWID_VERTICAL), diff --git a/src/waypoint_gui.cpp b/src/waypoint_gui.cpp index 701fb736ae..40c3401b0c 100644 --- a/src/waypoint_gui.cpp +++ b/src/waypoint_gui.cpp @@ -155,7 +155,7 @@ static const NWidgetPart _nested_waypoint_view_widgets[] = { EndContainer(), NWidget(WWT_PANEL, COLOUR_GREY), NWidget(WWT_INSET, COLOUR_GREY), SetPadding(2, 2, 2, 2), - NWidget(NWID_VIEWPORT, COLOUR_GREY, WID_W_VIEWPORT), SetMinimalSize(256, 88), SetPadding(1, 1, 1, 1), SetResize(1, 1), + NWidget(NWID_VIEWPORT, COLOUR_GREY, WID_W_VIEWPORT), SetMinimalSize(256, 88), SetResize(1, 1), EndContainer(), EndContainer(), NWidget(NWID_HORIZONTAL), diff --git a/src/widget.cpp b/src/widget.cpp index aaa00d4d79..638aa05fe7 100644 --- a/src/widget.cpp +++ b/src/widget.cpp @@ -1904,8 +1904,11 @@ void NWidgetBackground::SetupSmallestSize(Window *w, bool init_array) this->resize_x = this->child->resize_x; this->resize_y = this->child->resize_y; - /* Account for the size of the frame's text if that exists */ - if (w != nullptr && this->type == WWT_FRAME) { + /* Don't apply automatic padding if there is no child widget. */ + if (w == nullptr) return; + + if (this->type == WWT_FRAME) { + /* Account for the size of the frame's text if that exists */ this->child->padding_left = WD_FRAMETEXT_LEFT; this->child->padding_right = WD_FRAMETEXT_RIGHT; this->child->padding_top = std::max((int)WD_FRAMETEXT_TOP, this->widget_data != STR_NULL ? FONT_HEIGHT_NORMAL + WD_FRAMETEXT_TOP / 2 : 0); @@ -1916,6 +1919,15 @@ void NWidgetBackground::SetupSmallestSize(Window *w, bool init_array) if (this->index >= 0) w->SetStringParameters(this->index); this->smallest_x = std::max(this->smallest_x, GetStringBoundingBox(this->widget_data).width + WD_FRAMETEXT_LEFT + WD_FRAMETEXT_RIGHT); + } else if (this->type == WWT_INSET) { + /* Apply automatic padding for bevel thickness. */ + this->child->padding_left = WD_BEVEL_LEFT; + this->child->padding_right = WD_BEVEL_RIGHT; + this->child->padding_top = WD_BEVEL_TOP; + this->child->padding_bottom = WD_BEVEL_BOTTOM; + + this->smallest_x += this->child->padding_left + this->child->padding_right; + this->smallest_y += this->child->padding_top + this->child->padding_bottom; } } else { Dimension d = {this->min_x, this->min_y}; From e162d0a55ccc4cf38fd09ce0fa349e4887a55448 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Guilloux?= Date: Sun, 9 May 2021 18:28:47 +0200 Subject: [PATCH 26/61] Fix: [MinGW] Reapply 48fd7b27 to fix launch on Windows 7 x64 (#9225) --- CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index ecfbe408c6..6957c85c28 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -361,6 +361,7 @@ if(WIN32) ws2_32 winmm imm32 + usp10 ) endif() From 583011bca0a1aa5b508ccdebe591df4eae01efb5 Mon Sep 17 00:00:00 2001 From: Patric Stout Date: Sun, 9 May 2021 18:48:21 +0200 Subject: [PATCH 27/61] Fix: lobby window doesn't close if no connection could be established (#9223) --- src/network/network.cpp | 52 ++++++++++++++++++++++++++++------ src/network/network_gui.cpp | 6 ++-- src/network/network_internal.h | 3 +- 3 files changed, 48 insertions(+), 13 deletions(-) diff --git a/src/network/network.cpp b/src/network/network.cpp index daf2e1d3fd..4fdbdab73c 100644 --- a/src/network/network.cpp +++ b/src/network/network.cpp @@ -619,35 +619,69 @@ static void NetworkInitialize(bool close_admins = true) _network_reconnect = 0; } -/** Non blocking connection create to query servers */ +/** Non blocking connection to query servers for their game info. */ class TCPQueryConnecter : TCPConnecter { private: - bool request_company_info; std::string connection_string; public: - TCPQueryConnecter(const std::string &connection_string, bool request_company_info) : TCPConnecter(connection_string, NETWORK_DEFAULT_PORT), request_company_info(request_company_info), connection_string(connection_string) {} + TCPQueryConnecter(const std::string &connection_string) : TCPConnecter(connection_string, NETWORK_DEFAULT_PORT), connection_string(connection_string) {} void OnConnect(SOCKET s) override { _networking = true; new ClientNetworkGameSocketHandler(s, this->connection_string); - MyClient::SendInformationQuery(request_company_info); + MyClient::SendInformationQuery(false); } }; /** - * Query a server to fetch his game-info. + * Query a server to fetch the game-info. * @param connection_string the address to query. - * @param request_company_info Whether to request company info too. */ -void NetworkTCPQueryServer(const std::string &connection_string, bool request_company_info) +void NetworkQueryServer(const std::string &connection_string) { if (!_network_available) return; NetworkInitialize(); - new TCPQueryConnecter(connection_string, request_company_info); + new TCPQueryConnecter(connection_string); +} + +/** Non blocking connection to query servers for their game and company info. */ +class TCPLobbyQueryConnecter : TCPConnecter { +private: + std::string connection_string; + +public: + TCPLobbyQueryConnecter(const std::string &connection_string) : TCPConnecter(connection_string, NETWORK_DEFAULT_PORT), connection_string(connection_string) {} + + void OnFailure() override + { + DeleteWindowById(WC_NETWORK_WINDOW, WN_NETWORK_WINDOW_LOBBY); + + ShowErrorMessage(STR_NETWORK_ERROR_NOCONNECTION, INVALID_STRING_ID, WL_ERROR); + } + + void OnConnect(SOCKET s) override + { + _networking = true; + new ClientNetworkGameSocketHandler(s, this->connection_string); + MyClient::SendInformationQuery(true); + } +}; + +/** + * Query a server to fetch his game-info for the lobby. + * @param connection_string the address to query. + */ +void NetworkQueryLobbyServer(const std::string &connection_string) +{ + if (!_network_available) return; + + NetworkInitialize(); + + new TCPLobbyQueryConnecter(connection_string); } /** @@ -670,7 +704,7 @@ NetworkGameList *NetworkAddServer(const std::string &connection_string, bool man NetworkRebuildHostList(); UpdateNetworkGameWindow(); - NetworkTCPQueryServer(connection_string); + NetworkQueryServer(connection_string); } if (manually) item->manually = true; diff --git a/src/network/network_gui.cpp b/src/network/network_gui.cpp index cf16d06b13..613b562fb3 100644 --- a/src/network/network_gui.cpp +++ b/src/network/network_gui.cpp @@ -752,7 +752,7 @@ public: break; case WID_NG_REFRESH: // Refresh - if (this->server != nullptr) NetworkTCPQueryServer(this->server->connection_string); + if (this->server != nullptr) NetworkQueryServer(this->server->connection_string); break; case WID_NG_NEWGRF: // NewGRF Settings @@ -1487,7 +1487,7 @@ struct NetworkLobbyWindow : public Window { /* Clear the information so removed companies don't remain */ for (auto &company : this->company_info) company = {}; - NetworkTCPQueryServer(this->server->connection_string, true); + NetworkQueryLobbyServer(this->server->connection_string); break; } } @@ -1557,7 +1557,7 @@ static void ShowNetworkLobbyWindow(NetworkGameList *ngl) strecpy(_settings_client.network.last_joined, ngl->connection_string.c_str(), lastof(_settings_client.network.last_joined)); - NetworkTCPQueryServer(ngl->connection_string, true); + NetworkQueryLobbyServer(ngl->connection_string); new NetworkLobbyWindow(&_network_lobby_window_desc, ngl); } diff --git a/src/network/network_internal.h b/src/network/network_internal.h index ac5472fe18..3456ab251a 100644 --- a/src/network/network_internal.h +++ b/src/network/network_internal.h @@ -87,7 +87,8 @@ extern uint8 _network_reconnect; extern CompanyMask _network_company_passworded; -void NetworkTCPQueryServer(const std::string &connection_string, bool request_company_info = false); +void NetworkQueryServer(const std::string &connection_string); +void NetworkQueryLobbyServer(const std::string &connection_string); void GetBindAddresses(NetworkAddressList *addresses, uint16 port); struct NetworkGameList *NetworkAddServer(const std::string &connection_string, bool manually = true); From b7dd602dcd30bba54218e9c382d003f623110bb2 Mon Sep 17 00:00:00 2001 From: translators Date: Sun, 9 May 2021 18:57:07 +0000 Subject: [PATCH 28/61] Update: Translations from eints finnish: 1 change by hpiirai --- src/lang/finnish.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lang/finnish.txt b/src/lang/finnish.txt index b8a219fb5e..99c5da4087 100644 --- a/src/lang/finnish.txt +++ b/src/lang/finnish.txt @@ -2292,7 +2292,7 @@ STR_CONTENT_SEARCH_EXTERNAL :{BLACK}Etsi ulk STR_CONTENT_SEARCH_EXTERNAL_TOOLTIP :{BLACK}Etsi OpenTTD:n ulkopuolisilta verkkosivuilta sisältöä, jota ei ole saatavilla OpenTTD:n sisältöpalvelussa STR_CONTENT_SEARCH_EXTERNAL_DISCLAIMER_CAPTION :{WHITE}Olet poistumassa OpenTTD:stä! STR_CONTENT_SEARCH_EXTERNAL_DISCLAIMER :{WHITE}Ulkopuolisilta verkkosivuilta ladattaessa käyttöehdot voivat vaihdella.{}Sinun on noudatettava ulkopuolisen sivuston ohjeita sisällön asentamiseksi OpenTTD:hen.{}Haluatko jatkaa? -STR_CONTENT_FILTER_TITLE :{BLACK}Avainsana/nimi suodatus: +STR_CONTENT_FILTER_TITLE :{BLACK}Suodata avainsanalla tai nimellä: STR_CONTENT_OPEN_URL :{BLACK}Vieraile verkkosivulla STR_CONTENT_OPEN_URL_TOOLTIP :{BLACK}Vieraile sisällön verkkosivulla STR_CONTENT_DOWNLOAD_CAPTION :{BLACK}Lataa From 8f4a612a7c8ceb966d1cd538e9e53fd70336259d Mon Sep 17 00:00:00 2001 From: Patric Stout Date: Mon, 10 May 2021 13:40:28 +0200 Subject: [PATCH 29/61] Change: reworded many of the network errors during connect/listen (#9230) Also changed some levels to 0, as a failing listen() is something we should tell the user about. Hiding this information is a bit evil. --- src/network/core/address.cpp | 23 +++++++++++++---------- src/network/core/tcp_connect.cpp | 8 ++++++-- 2 files changed, 19 insertions(+), 12 deletions(-) diff --git a/src/network/core/address.cpp b/src/network/core/address.cpp index 205b5df224..aa27b41032 100644 --- a/src/network/core/address.cpp +++ b/src/network/core/address.cpp @@ -311,49 +311,52 @@ SOCKET NetworkAddress::Resolve(int family, int socktype, int flags, SocketList * */ static SOCKET ListenLoopProc(addrinfo *runp) { - const char *type = NetworkAddress::SocketTypeAsString(runp->ai_socktype); - const char *family = NetworkAddress::AddressFamilyAsString(runp->ai_family); std::string address = NetworkAddress(runp->ai_addr, (int)runp->ai_addrlen).GetAddressAsString(); SOCKET sock = socket(runp->ai_family, runp->ai_socktype, runp->ai_protocol); if (sock == INVALID_SOCKET) { - DEBUG(net, 0, "[%s] could not create %s socket on port %s: %s", type, family, address.c_str(), NetworkError::GetLast().AsString()); + const char *type = NetworkAddress::SocketTypeAsString(runp->ai_socktype); + const char *family = NetworkAddress::AddressFamilyAsString(runp->ai_family); + DEBUG(net, 0, "Could not create %s %s socket: %s", type, family, NetworkError::GetLast().AsString()); return INVALID_SOCKET; } if (runp->ai_socktype == SOCK_STREAM && !SetNoDelay(sock)) { - DEBUG(net, 3, "[%s] setting TCP_NODELAY failed for port %s", type, address.c_str()); + DEBUG(net, 1, "Setting no-delay mode failed: %s", NetworkError::GetLast().AsString()); } int on = 1; /* The (const char*) cast is needed for windows!! */ if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (const char*)&on, sizeof(on)) == -1) { - DEBUG(net, 3, "[%s] could not set reusable %s sockets for port %s: %s", type, family, address.c_str(), NetworkError::GetLast().AsString()); + DEBUG(net, 0, "Setting reuse-address mode failed: %s", NetworkError::GetLast().AsString()); } #ifndef __OS2__ if (runp->ai_family == AF_INET6 && setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, (const char*)&on, sizeof(on)) == -1) { - DEBUG(net, 3, "[%s] could not disable IPv4 over IPv6 on port %s: %s", type, address.c_str(), NetworkError::GetLast().AsString()); + DEBUG(net, 3, "Could not disable IPv4 over IPv6: %s", NetworkError::GetLast().AsString()); } #endif if (bind(sock, runp->ai_addr, (int)runp->ai_addrlen) != 0) { - DEBUG(net, 1, "[%s] could not bind on %s port %s: %s", type, family, address.c_str(), NetworkError::GetLast().AsString()); + DEBUG(net, 0, "Could not bind socket on %s: %s", address.c_str(), NetworkError::GetLast().AsString()); closesocket(sock); return INVALID_SOCKET; } if (runp->ai_socktype != SOCK_DGRAM && listen(sock, 1) != 0) { - DEBUG(net, 1, "[%s] could not listen at %s port %s: %s", type, family, address.c_str(), NetworkError::GetLast().AsString()); + DEBUG(net, 0, "Could not listen on socket: %s", NetworkError::GetLast().AsString()); closesocket(sock); return INVALID_SOCKET; } /* Connection succeeded */ - if (!SetNonBlocking(sock)) DEBUG(net, 0, "[%s] setting non-blocking mode failed for %s port %s", type, family, address.c_str()); - DEBUG(net, 1, "[%s] listening on %s port %s", type, family, address.c_str()); + if (!SetNonBlocking(sock)) { + DEBUG(net, 0, "Setting non-blocking mode failed: %s", NetworkError::GetLast().AsString()); + } + + DEBUG(net, 1, "Listening on %s", address.c_str()); return sock; } diff --git a/src/network/core/tcp_connect.cpp b/src/network/core/tcp_connect.cpp index 96c82b5af8..b343ae463a 100644 --- a/src/network/core/tcp_connect.cpp +++ b/src/network/core/tcp_connect.cpp @@ -64,8 +64,12 @@ void TCPConnecter::Connect(addrinfo *address) return; } - if (!SetNoDelay(sock)) DEBUG(net, 1, "Setting TCP_NODELAY failed"); - if (!SetNonBlocking(sock)) DEBUG(net, 0, "Setting non-blocking mode failed"); + if (!SetNoDelay(sock)) { + DEBUG(net, 1, "Setting TCP_NODELAY failed: %s", NetworkError::GetLast().AsString()); + } + if (!SetNonBlocking(sock)) { + DEBUG(net, 0, "Setting non-blocking mode failed: %s", NetworkError::GetLast().AsString()); + } NetworkAddress network_address = NetworkAddress(address->ai_addr, (int)address->ai_addrlen); DEBUG(net, 4, "Attempting to connect to %s", network_address.GetAddressAsString().c_str()); From c53d9991eed38177ad9b0e251b1e6f4cfb27191a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Guilloux?= Date: Mon, 10 May 2021 14:48:04 +0200 Subject: [PATCH 30/61] Add: [Actions] Check CI annotations to detect compile warnings (#9217) --- .github/workflows/ci-build.yml | 42 ++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/.github/workflows/ci-build.yml b/.github/workflows/ci-build.yml index 74677ee6d5..e0707291c3 100644 --- a/.github/workflows/ci-build.yml +++ b/.github/workflows/ci-build.yml @@ -315,3 +315,45 @@ jobs: run: | cd ${GITHUB_WORKSPACE}/build ctest --timeout 120 + + check_annotations: + name: Check Annotations + needs: + - emscripten + - linux + - macos + - windows + + if: always() && github.event_name == 'pull_request' + + runs-on: ubuntu-20.04 + + steps: + - name: Get check suite ID + id: check_suite_id + uses: octokit/request-action@v2.x + with: + route: GET /repos/{repository}/actions/runs/{run_id} + repository: ${{ github.repository }} + run_id: ${{ github.run_id }} + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Get check runs + id: check_runs + uses: octokit/request-action@v2.x + with: + route: GET /repos/{repository}/check-suites/{check_suite_id}/check-runs + repository: ${{ github.repository }} + check_suite_id: ${{ fromJson(steps.check_suite_id.outputs.data).check_suite_id }} + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Check annotations + shell: bash + run: | + echo '[ + ${{ toJson(fromJson(steps.check_runs.outputs.data).check_runs.*.output.title) }}, ${{ toJson(fromJson(steps.check_runs.outputs.data).check_runs.*.output.summary) }} + ]' | jq '.[0] as $t | .[1] as $s | reduce range(.[0] | length) as $i ([]; . + [if $t[$i] then $t[$i] + ": " + $s[$i] else empty end]) | .[]' + + exit $(echo '${{ toJson(fromJson(steps.check_runs.outputs.data).check_runs.*.output.annotations_count) }}' | jq 'add') From 79fc094c54801bba83dc8aca2028fd6baf35965f Mon Sep 17 00:00:00 2001 From: Rubidium Date: Sat, 8 May 2021 13:33:26 +0200 Subject: [PATCH 31/61] Cleanup: [Fluidsynth] Remove fluid_player_join The function fluid_player_join in the library is broken beyond compare for the usecases it was used for (see their #872). It does not wait until it is safe to delete the player, so it is up to the end user to ensure that. For OpenTTD we acquire a lock before fluid_synth_write_s16 and we acquire the same lock in the stop function. So, only one of the functions can be doing its thing, meaning we do not need to wait for the player to be stopped as it cannot be doing anything as we prevent that by the lock. --- src/music/fluidsynth.cpp | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/src/music/fluidsynth.cpp b/src/music/fluidsynth.cpp index 061ade3456..1c4b67e12b 100644 --- a/src/music/fluidsynth.cpp +++ b/src/music/fluidsynth.cpp @@ -163,21 +163,12 @@ void MusicDriver_FluidSynth::PlaySong(const MusicSongInfo &song) void MusicDriver_FluidSynth::StopSong() { - { - std::lock_guard lock{ _midi.synth_mutex }; - - if (_midi.player == nullptr) return; - - fluid_player_stop(_midi.player); - } + std::lock_guard lock{ _midi.synth_mutex }; - /* The join must be run without lock as the Music rendering needs to be - * running so FluidSynth's internals can actually stop the playing. */ - if (fluid_player_join(_midi.player) != FLUID_OK) { - DEBUG(driver, 0, "Could not join player"); - } + if (_midi.player == nullptr) return; - std::lock_guard lock{ _midi.synth_mutex }; + fluid_player_stop(_midi.player); + /* No fluid_player_join needed */ delete_fluid_player(_midi.player); fluid_synth_system_reset(_midi.synth); fluid_synth_all_sounds_off(_midi.synth, -1); From 296194ad36c601cf12d215a65d84222bbdf4de61 Mon Sep 17 00:00:00 2001 From: Rubidium Date: Sun, 9 May 2021 22:51:26 +0200 Subject: [PATCH 32/61] Fix: memory leak due to assigning result of strdup to a std::string --- src/newgrf.cpp | 2 +- src/signs_cmd.cpp | 2 +- src/town_cmd.cpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/newgrf.cpp b/src/newgrf.cpp index 7b8ab9de1d..c372f033a8 100644 --- a/src/newgrf.cpp +++ b/src/newgrf.cpp @@ -7394,7 +7394,7 @@ static void GRFInhibit(ByteReader *buf) if (file != nullptr && file != _cur.grfconfig) { grfmsg(2, "GRFInhibit: Deactivating file '%s'", file->filename); GRFError *error = DisableGrf(STR_NEWGRF_ERROR_FORCEFULLY_DISABLED, file); - error->data = stredup(_cur.grfconfig->GetName()); + error->data = _cur.grfconfig->GetName(); } } } diff --git a/src/signs_cmd.cpp b/src/signs_cmd.cpp index a0843917d8..6ffb6cda1f 100644 --- a/src/signs_cmd.cpp +++ b/src/signs_cmd.cpp @@ -54,7 +54,7 @@ CommandCost CmdPlaceSign(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 si->y = y; si->z = GetSlopePixelZ(x, y); if (!StrEmpty(text)) { - si->name = stredup(text); + si->name = text; } si->UpdateVirtCoord(); InvalidateWindowData(WC_SIGN_LIST, 0, 0); diff --git a/src/town_cmd.cpp b/src/town_cmd.cpp index 01331a269e..033d042486 100644 --- a/src/town_cmd.cpp +++ b/src/town_cmd.cpp @@ -2004,7 +2004,7 @@ CommandCost CmdFoundTown(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 old_generating_world.Restore(); if (t != nullptr && !StrEmpty(text)) { - t->name = stredup(text); + t->name = text; t->UpdateVirtCoord(); } From 495d73a67fe924eed5504003a3a5ba30758dcbc5 Mon Sep 17 00:00:00 2001 From: Rubidium Date: Sun, 9 May 2021 23:00:36 +0200 Subject: [PATCH 33/61] Fix: leaking file descriptors --- src/console_cmds.cpp | 1 + src/network/network_content.cpp | 10 ++++++++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/console_cmds.cpp b/src/console_cmds.cpp index 01c35ecf88..3929b44c11 100644 --- a/src/console_cmds.cpp +++ b/src/console_cmds.cpp @@ -939,6 +939,7 @@ DEF_CONSOLE_CMD(ConExec) } if (_script_current_depth == 11) { + FioFCloseFile(script_file); IConsoleError("Maximum 'exec' depth reached; script A is calling script B is calling script C ... more than 10 times."); return true; } diff --git a/src/network/network_content.cpp b/src/network/network_content.cpp index 528b7bd777..e4f368618b 100644 --- a/src/network/network_content.cpp +++ b/src/network/network_content.cpp @@ -410,7 +410,8 @@ static bool GunzipFile(const ContentInfo *ci) FILE *ftmp = fopen(GetFullFilename(ci, true).c_str(), "rb"); if (ftmp == nullptr) return false; /* Duplicate the handle, and close the FILE*, to avoid double-closing the handle later. */ - gzFile fin = gzdopen(dup(fileno(ftmp)), "rb"); + int fdup = dup(fileno(ftmp)); + gzFile fin = gzdopen(fdup, "rb"); fclose(ftmp); FILE *fout = fopen(GetFullFilename(ci, false).c_str(), "wb"); @@ -449,7 +450,12 @@ static bool GunzipFile(const ContentInfo *ci) } } - if (fin != nullptr) gzclose(fin); + if (fin != nullptr) { + gzclose(fin); + } else if (fdup != -1) { + /* Failing gzdopen does not close the passed file descriptor. */ + close(fdup); + } if (fout != nullptr) fclose(fout); return ret; From 2e429ee4853fb4baf78c864be6ba462bd5758096 Mon Sep 17 00:00:00 2001 From: translators Date: Mon, 10 May 2021 19:04:28 +0000 Subject: [PATCH 34/61] Update: Translations from eints german: 2 changes by Wuzzy2 --- src/lang/german.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/lang/german.txt b/src/lang/german.txt index 79df8af49c..ae1255d25c 100644 --- a/src/lang/german.txt +++ b/src/lang/german.txt @@ -2218,6 +2218,7 @@ STR_NETWORK_ERROR_TIMEOUT_COMPUTER :{WHITE}Dieser C STR_NETWORK_ERROR_TIMEOUT_MAP :{WHITE}Das Herunterladen der Karte dauerte zu lange STR_NETWORK_ERROR_TIMEOUT_JOIN :{WHITE}Der Beitritt zum Server dauerte zu lange STR_NETWORK_ERROR_INVALID_CLIENT_NAME :{WHITE}Ihr Spielername ist ungültig +STR_NETWORK_ERROR_SERVER_TOO_OLD :{WHITE}Der angefragte Server ist für diesen Client zu alt ############ Leave those lines in this order!! STR_NETWORK_ERROR_CLIENT_GENERAL :Allgemeiner Fehler @@ -2269,7 +2270,7 @@ STR_NETWORK_MESSAGE_CLIENT_COMPANY_JOIN :*** {STRING} is STR_NETWORK_MESSAGE_CLIENT_COMPANY_SPECTATE :*** {STRING} ist den Zuschauern beigetreten STR_NETWORK_MESSAGE_CLIENT_COMPANY_NEW :*** {STRING} hat eine neue Firma gegründet (#{2:NUM}) STR_NETWORK_MESSAGE_CLIENT_LEFT :*** {STRING} hat das Spiel verlassen ({2:STRING}) -STR_NETWORK_MESSAGE_NAME_CHANGE :*** {STRING} hat seinen/ihren Namen in {STRING} geändert +STR_NETWORK_MESSAGE_NAME_CHANGE :*** {STRING} hat den eigenen Namen zu {STRING} geändert STR_NETWORK_MESSAGE_GIVE_MONEY :*** {STRING} gab {2:CURRENCY_LONG} an {1:STRING} STR_NETWORK_MESSAGE_SERVER_SHUTDOWN :{WHITE}Der Server hat das Spiel beendet STR_NETWORK_MESSAGE_SERVER_REBOOT :{WHITE}Der Server startet neu...{}Bitte warten... From 36e22f3a7bc0f86c650a293e2f624ac5ddfffa84 Mon Sep 17 00:00:00 2001 From: Patric Stout Date: Tue, 11 May 2021 12:26:16 +0200 Subject: [PATCH 35/61] Fix: [Network] clients leaving because of broken connections was not broadcasted (#9238) The code mixed up "client has quit but we already told everyone" with "client lost connection, handle this". Split up those two signals: - CLIENT_QUIT means we told everyone and the connection is now dead - CONNECTION_LIST means we should tell everyone we lost a client --- src/network/core/core.h | 21 +++++++++++---------- src/network/core/tcp_admin.cpp | 2 +- src/network/core/tcp_game.cpp | 4 ++-- src/network/network.cpp | 4 ++-- src/network/network_client.cpp | 2 +- src/network/network_server.cpp | 16 ++++++++-------- 6 files changed, 25 insertions(+), 24 deletions(-) diff --git a/src/network/core/core.h b/src/network/core/core.h index aac36080ff..37948ad527 100644 --- a/src/network/core/core.h +++ b/src/network/core/core.h @@ -20,16 +20,17 @@ void NetworkCoreShutdown(); /** Status of a network client; reasons why a client has quit */ enum NetworkRecvStatus { - NETWORK_RECV_STATUS_OKAY, ///< Everything is okay - NETWORK_RECV_STATUS_DESYNC, ///< A desync did occur - NETWORK_RECV_STATUS_NEWGRF_MISMATCH, ///< We did not have the required NewGRFs - NETWORK_RECV_STATUS_SAVEGAME, ///< Something went wrong (down)loading the savegame - NETWORK_RECV_STATUS_CONN_LOST, ///< The connection is 'just' lost - NETWORK_RECV_STATUS_MALFORMED_PACKET, ///< We apparently send a malformed packet - NETWORK_RECV_STATUS_SERVER_ERROR, ///< The server told us we made an error - NETWORK_RECV_STATUS_SERVER_FULL, ///< The server is full - NETWORK_RECV_STATUS_SERVER_BANNED, ///< The server has banned us - NETWORK_RECV_STATUS_CLOSE_QUERY, ///< Done querying the server + NETWORK_RECV_STATUS_OKAY, ///< Everything is okay. + NETWORK_RECV_STATUS_DESYNC, ///< A desync did occur. + NETWORK_RECV_STATUS_NEWGRF_MISMATCH, ///< We did not have the required NewGRFs. + NETWORK_RECV_STATUS_SAVEGAME, ///< Something went wrong (down)loading the savegame. + NETWORK_RECV_STATUS_CLIENT_QUIT, ///< The connection is lost gracefully. Other clients are already informed of this leaving client. + NETWORK_RECV_STATUS_MALFORMED_PACKET, ///< We apparently send a malformed packet. + NETWORK_RECV_STATUS_SERVER_ERROR, ///< The server told us we made an error. + NETWORK_RECV_STATUS_SERVER_FULL, ///< The server is full. + NETWORK_RECV_STATUS_SERVER_BANNED, ///< The server has banned us. + NETWORK_RECV_STATUS_CLOSE_QUERY, ///< Done querying the server. + NETWORK_RECV_STATUS_CONNECTION_LOST, ///< The connection is lost unexpectedly. }; /** Forward declaration due to circular dependencies */ diff --git a/src/network/core/tcp_admin.cpp b/src/network/core/tcp_admin.cpp index c72583f553..36daae4a14 100644 --- a/src/network/core/tcp_admin.cpp +++ b/src/network/core/tcp_admin.cpp @@ -41,7 +41,7 @@ NetworkAdminSocketHandler::~NetworkAdminSocketHandler() NetworkRecvStatus NetworkAdminSocketHandler::CloseConnection(bool error) { delete this; - return NETWORK_RECV_STATUS_CONN_LOST; + return NETWORK_RECV_STATUS_CLIENT_QUIT; } /** diff --git a/src/network/core/tcp_game.cpp b/src/network/core/tcp_game.cpp index eb53db5acd..771ea37b15 100644 --- a/src/network/core/tcp_game.cpp +++ b/src/network/core/tcp_game.cpp @@ -48,10 +48,10 @@ NetworkRecvStatus NetworkGameSocketHandler::CloseConnection(bool error) _networking = false; ShowErrorMessage(STR_NETWORK_ERROR_LOSTCONNECTION, INVALID_STRING_ID, WL_CRITICAL); - return NETWORK_RECV_STATUS_CONN_LOST; + return NETWORK_RECV_STATUS_CLIENT_QUIT; } - return this->CloseConnection(error ? NETWORK_RECV_STATUS_SERVER_ERROR : NETWORK_RECV_STATUS_CONN_LOST); + return this->CloseConnection(NETWORK_RECV_STATUS_CONNECTION_LOST); } diff --git a/src/network/network.cpp b/src/network/network.cpp index 4fdbdab73c..2186cb8a1d 100644 --- a/src/network/network.cpp +++ b/src/network/network.cpp @@ -585,13 +585,13 @@ void NetworkClose(bool close_admins) } for (NetworkClientSocket *cs : NetworkClientSocket::Iterate()) { - cs->CloseConnection(NETWORK_RECV_STATUS_CONN_LOST); + cs->CloseConnection(NETWORK_RECV_STATUS_CLIENT_QUIT); } ServerNetworkGameSocketHandler::CloseListeners(); ServerNetworkAdminSocketHandler::CloseListeners(); } else if (MyClient::my_client != nullptr) { MyClient::SendQuit(); - MyClient::my_client->CloseConnection(NETWORK_RECV_STATUS_CONN_LOST); + MyClient::my_client->CloseConnection(NETWORK_RECV_STATUS_CLIENT_QUIT); } TCPConnecter::KillAll(); diff --git a/src/network/network_client.cpp b/src/network/network_client.cpp index f827c57351..501449705e 100644 --- a/src/network/network_client.cpp +++ b/src/network/network_client.cpp @@ -656,7 +656,7 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_CLIENT_INFO(Pac p->Recv_string(name, sizeof(name)); if (this->status < STATUS_AUTHORIZED) return NETWORK_RECV_STATUS_MALFORMED_PACKET; - if (this->HasClientQuit()) return NETWORK_RECV_STATUS_CONN_LOST; + if (this->HasClientQuit()) return NETWORK_RECV_STATUS_CLIENT_QUIT; /* The server validates the name when receiving it from clients, so when it is wrong * here something went really wrong. In the best case the packet got malformed on its * way too us, in the worst case the server is broken or compromised. */ diff --git a/src/network/network_server.cpp b/src/network/network_server.cpp index e8266a84fc..eccf80f781 100644 --- a/src/network/network_server.cpp +++ b/src/network/network_server.cpp @@ -256,7 +256,7 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::CloseConnection(NetworkRecvSta */ if (this->sock == INVALID_SOCKET) return status; - if (status != NETWORK_RECV_STATUS_CONN_LOST && status != NETWORK_RECV_STATUS_SERVER_ERROR && !this->HasClientQuit() && this->status >= STATUS_AUTHORIZED) { + if (status != NETWORK_RECV_STATUS_CLIENT_QUIT && status != NETWORK_RECV_STATUS_SERVER_ERROR && !this->HasClientQuit() && this->status >= STATUS_AUTHORIZED) { /* We did not receive a leave message from this client... */ char client_name[NETWORK_CLIENT_NAME_LENGTH]; @@ -893,7 +893,7 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::Receive_CLIENT_JOIN(Packet *p) p->Recv_string(name, sizeof(name)); playas = (Owner)p->Recv_uint8(); - if (this->HasClientQuit()) return NETWORK_RECV_STATUS_CONN_LOST; + if (this->HasClientQuit()) return NETWORK_RECV_STATUS_CLIENT_QUIT; /* join another company does not affect these values */ switch (playas) { @@ -1077,7 +1077,7 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::Receive_CLIENT_COMMAND(Packet CommandPacket cp; const char *err = this->ReceiveCommand(p, &cp); - if (this->HasClientQuit()) return NETWORK_RECV_STATUS_CONN_LOST; + if (this->HasClientQuit()) return NETWORK_RECV_STATUS_CLIENT_QUIT; NetworkClientInfo *ci = this->GetInfo(); @@ -1136,7 +1136,7 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::Receive_CLIENT_ERROR(Packet *p /* The client was never joined.. thank the client for the packet, but ignore it */ if (this->status < STATUS_DONE_MAP || this->HasClientQuit()) { - return this->CloseConnection(NETWORK_RECV_STATUS_CONN_LOST); + return this->CloseConnection(NETWORK_RECV_STATUS_CLIENT_QUIT); } this->GetClientName(client_name, lastof(client_name)); @@ -1156,7 +1156,7 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::Receive_CLIENT_ERROR(Packet *p NetworkAdminClientError(this->client_id, errorno); - return this->CloseConnection(NETWORK_RECV_STATUS_CONN_LOST); + return this->CloseConnection(NETWORK_RECV_STATUS_CLIENT_QUIT); } NetworkRecvStatus ServerNetworkGameSocketHandler::Receive_CLIENT_QUIT(Packet *p) @@ -1167,7 +1167,7 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::Receive_CLIENT_QUIT(Packet *p) /* The client was never joined.. thank the client for the packet, but ignore it */ if (this->status < STATUS_DONE_MAP || this->HasClientQuit()) { - return this->CloseConnection(NETWORK_RECV_STATUS_CONN_LOST); + return this->CloseConnection(NETWORK_RECV_STATUS_CLIENT_QUIT); } this->GetClientName(client_name, lastof(client_name)); @@ -1182,7 +1182,7 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::Receive_CLIENT_QUIT(Packet *p) NetworkAdminClientQuit(this->client_id); - return this->CloseConnection(NETWORK_RECV_STATUS_CONN_LOST); + return this->CloseConnection(NETWORK_RECV_STATUS_CLIENT_QUIT); } NetworkRecvStatus ServerNetworkGameSocketHandler::Receive_CLIENT_ACK(Packet *p) @@ -1412,7 +1412,7 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::Receive_CLIENT_SET_NAME(Packet p->Recv_string(client_name, sizeof(client_name)); ci = this->GetInfo(); - if (this->HasClientQuit()) return NETWORK_RECV_STATUS_CONN_LOST; + if (this->HasClientQuit()) return NETWORK_RECV_STATUS_CLIENT_QUIT; if (ci != nullptr) { if (!NetworkIsValidClientName(client_name)) { From 9841ebb0bd6531b4ac1474c4c35103d12b8eeb43 Mon Sep 17 00:00:00 2001 From: Patric Stout Date: Tue, 11 May 2021 12:26:30 +0200 Subject: [PATCH 36/61] Fix: [Network] don't mark the last-joined server as manual (#9239) --- src/network/network_gui.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/network/network_gui.cpp b/src/network/network_gui.cpp index 613b562fb3..2df40eeec1 100644 --- a/src/network/network_gui.cpp +++ b/src/network/network_gui.cpp @@ -476,7 +476,7 @@ public: EM_ASM(if (window["openttd_server_list"]) openttd_server_list()); #endif - this->last_joined = NetworkAddServer(_settings_client.network.last_joined); + this->last_joined = NetworkAddServer(_settings_client.network.last_joined, false); this->server = this->last_joined; this->requery_timer.SetInterval(MILLISECONDS_PER_TICK); From 9e7e87ce3e7a7a4f5373883550c6f05a5d023ab6 Mon Sep 17 00:00:00 2001 From: Patric Stout Date: Tue, 11 May 2021 12:32:27 +0200 Subject: [PATCH 37/61] Fix: [Network] don't rebuild the host-list during iterating the list (#9240) Additionally, only rebuild it when we added a new manual server, as otherwise it is a noop anyway. --- src/network/network.cpp | 1 - src/network/network_gui.cpp | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/src/network/network.cpp b/src/network/network.cpp index 2186cb8a1d..29e66d932c 100644 --- a/src/network/network.cpp +++ b/src/network/network.cpp @@ -701,7 +701,6 @@ NetworkGameList *NetworkAddServer(const std::string &connection_string, bool man ClearGRFConfigList(&item->info.grfconfig); item->info.server_name = connection_string; - NetworkRebuildHostList(); UpdateNetworkGameWindow(); NetworkQueryServer(connection_string); diff --git a/src/network/network_gui.cpp b/src/network/network_gui.cpp index 2df40eeec1..35597fa047 100644 --- a/src/network/network_gui.cpp +++ b/src/network/network_gui.cpp @@ -830,6 +830,7 @@ public: if (!StrEmpty(str)) { strecpy(_settings_client.network.connect_to_ip, str, lastof(_settings_client.network.connect_to_ip)); NetworkAddServer(str); + NetworkRebuildHostList(); } } From 0968d009c8b4ec5b3f5d84000f2f9f5636f9198a Mon Sep 17 00:00:00 2001 From: rubidium42 Date: Tue, 11 May 2021 18:53:23 +0200 Subject: [PATCH 38/61] Fix #9243: [Network] For a dedicated server use a fallback client and server name Also warn when the client or server name has not been set and provide pointers on how to set them --- src/network/network.cpp | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/network/network.cpp b/src/network/network.cpp index 29e66d932c..d8f0025472 100644 --- a/src/network/network.cpp +++ b/src/network/network.cpp @@ -841,6 +841,27 @@ static void NetworkInitGameInfo() strecpy(ci->client_name, _settings_client.network.client_name, lastof(ci->client_name)); } +/** + * Check whether the client and server name are set, for a dedicated server and if not set them to some default + * value and tell the user to change this as soon as possible. + * If the saved name is the default value, then the user is told to override this value too. + * This is only meant dedicated servers, as for the other servers the GUI ensures a name has been entered. + */ +static void CheckClientAndServerName() +{ + static const char *fallback_client_name = "Unnamed Client"; + if (StrEmpty(_settings_client.network.client_name) || strcmp(_settings_client.network.client_name, fallback_client_name) == 0) { + DEBUG(net, 0, "No \"client_name\" has been set, using \"%s\" instead. Please set this now using the \"name \" command.", fallback_client_name); + strecpy(_settings_client.network.client_name, fallback_client_name, lastof(_settings_client.network.client_name)); + } + + static const char *fallback_server_name = "Unnamed Server"; + if (StrEmpty(_settings_client.network.server_name) || strcmp(_settings_client.network.server_name, fallback_server_name) == 0) { + DEBUG(net, 0, "No \"server_name\" has been set, using \"%s\" instead. Please set this now using the \"server_name \" command.", fallback_server_name); + strecpy(_settings_client.network.server_name, fallback_server_name, lastof(_settings_client.network.server_name)); + } +} + bool NetworkServerStart() { if (!_network_available) return false; @@ -849,6 +870,9 @@ bool NetworkServerStart() IConsoleCmdExec("exec scripts/pre_server.scr 0"); if (_network_dedicated) IConsoleCmdExec("exec scripts/pre_dedicated.scr 0"); + /* Check for the client and server names to be set, but only after the scripts had a chance to set them.*/ + if (_network_dedicated) CheckClientAndServerName(); + NetworkDisconnect(false, false); NetworkInitialize(false); DEBUG(net, 1, "starting listeners for clients"); From d0eb3e4bc4904c64667efd791b24846d9ac3c741 Mon Sep 17 00:00:00 2001 From: Patric Stout Date: Tue, 11 May 2021 19:19:37 +0200 Subject: [PATCH 39/61] Fix: [Network] mark server as offline when no longer reachable (#9244) --- src/network/network.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/network/network.cpp b/src/network/network.cpp index d8f0025472..3b43ddbcd8 100644 --- a/src/network/network.cpp +++ b/src/network/network.cpp @@ -627,6 +627,14 @@ private: public: TCPQueryConnecter(const std::string &connection_string) : TCPConnecter(connection_string, NETWORK_DEFAULT_PORT), connection_string(connection_string) {} + void OnFailure() override + { + NetworkGameList *item = NetworkGameListAddItem(connection_string); + item->online = false; + + UpdateNetworkGameWindow(); + } + void OnConnect(SOCKET s) override { _networking = true; From 1dfc148613a18b1b71bcc07857149d8941f1b707 Mon Sep 17 00:00:00 2001 From: translators Date: Tue, 11 May 2021 19:03:10 +0000 Subject: [PATCH 40/61] Update: Translations from eints korean: 1 change by telk5093 slovak: 149 changes by ApplePie420 --- src/lang/korean.txt | 2 +- src/lang/slovak.txt | 254 ++++++++++++++++++++++++++------------------ 2 files changed, 152 insertions(+), 104 deletions(-) diff --git a/src/lang/korean.txt b/src/lang/korean.txt index 6bf90700c5..a132a0da5c 100644 --- a/src/lang/korean.txt +++ b/src/lang/korean.txt @@ -403,7 +403,7 @@ STR_SETTINGS_MENU_TRANSPARENT_SIGNS :역명판 감 ############ range for file menu starts STR_FILE_MENU_SAVE_GAME :게임 저장하기 -STR_FILE_MENU_LOAD_GAME :불러오기 +STR_FILE_MENU_LOAD_GAME :게임 불러오기 STR_FILE_MENU_QUIT_GAME :게임 그만두기 STR_FILE_MENU_SEPARATOR : STR_FILE_MENU_EXIT :종료 diff --git a/src/lang/slovak.txt b/src/lang/slovak.txt index 563fcaa41d..2ed535c7a9 100644 --- a/src/lang/slovak.txt +++ b/src/lang/slovak.txt @@ -475,7 +475,7 @@ STR_FILE_MENU_EXIT :Ukončiť # map menu STR_MAP_MENU_MAP_OF_WORLD :Mapa sveta STR_MAP_MENU_EXTRA_VIEWPORT :Ďalší pohľad -STR_MAP_MENU_LINGRAPH_LEGEND :Legenka k smerovaniu nákladu +STR_MAP_MENU_LINGRAPH_LEGEND :Legenda k smerovaniu nákladu STR_MAP_MENU_SIGN_LIST :Zoznam popisov ############ range for town menu starts @@ -626,7 +626,7 @@ STR_MONTH_ABBREV_DEC :Dec STR_MONTH_JAN :Január STR_MONTH_FEB :Február STR_MONTH_MAR :Marec -STR_MONTH_APR :April +STR_MONTH_APR :Apríl STR_MONTH_MAY :Máj STR_MONTH_JUN :Jún STR_MONTH_JUL :Júl @@ -877,8 +877,8 @@ STR_NEWS_DISASTER_ZEPPELIN :{BIG_FONT}{BLAC STR_NEWS_DISASTER_SMALL_UFO :{BIG_FONT}{BLACK}Cestné vozidlo bolo zničené pri kolízii s 'UFO' STR_NEWS_DISASTER_AIRPLANE_OIL_REFINERY :{BIG_FONT}{BLACK}Ropná rafinéria explodovala neďaleko mesta {TOWN}! STR_NEWS_DISASTER_HELICOPTER_FACTORY :{BIG_FONT}{BLACK}Za nejasných okolností bola zničená továreň neďaleko mesta {TOWN}! -STR_NEWS_DISASTER_BIG_UFO :{BIG_FONT}{BLACK}'UFO' pristalo nedaleko mesta {TOWN}! -STR_NEWS_DISASTER_COAL_MINE_SUBSIDENCE :{BIG_FONT}{BLACK}Zaval v uholnej bani nedaleko mesta {TOWN}! +STR_NEWS_DISASTER_BIG_UFO :{BIG_FONT}{BLACK}'UFO' pristálo neďaleko mesta {TOWN}! +STR_NEWS_DISASTER_COAL_MINE_SUBSIDENCE :{BIG_FONT}{BLACK}Zával v uhoľnej bani neďaleko mesta {TOWN}! STR_NEWS_DISASTER_FLOOD_VEHICLE :{BIG_FONT}{BLACK}Povodeň!{}Najmenej {COMMA} ľudí je nezvestných alebo mŕtvych po obrovských záplavách! STR_NEWS_COMPANY_IN_TROUBLE_TITLE :{BIG_FONT}{BLACK}Dopravná spoločnosť má problémy! @@ -909,7 +909,7 @@ STR_NEWS_END_OF_RECESSION :{BIG_FONT}{BLAC STR_NEWS_INDUSTRY_PRODUCTION_INCREASE_GENERAL :{BIG_FONT}{BLACK}{INDUSTRY} zvyšuje produkciu! STR_NEWS_INDUSTRY_PRODUCTION_INCREASE_COAL :{BIG_FONT}{BLACK}Nové nálezisko uhlia sa objavilo v {INDUSTRY}!{}Očakáva sa zdvojnásobenie produkcie! STR_NEWS_INDUSTRY_PRODUCTION_INCREASE_OIL :{BIG_FONT}{BLACK}Nové nálezisko ropy sa objavilo na {INDUSTRY}!{}Očakáva sa zdvojnásobenie produkcie! -STR_NEWS_INDUSTRY_PRODUCTION_INCREASE_FARM :{BIG_FONT}{BLACK}Na {INDUSTRY} zlepsili pouzivane metody!Ocakava sa zdvojnasobenie produkcie! +STR_NEWS_INDUSTRY_PRODUCTION_INCREASE_FARM :{BIG_FONT}{BLACK}Na {INDUSTRY} zlepšili používané metódy! Očakáva sa zdvojnásobenie produkcie! STR_NEWS_INDUSTRY_PRODUCTION_INCREASE_SMOOTH :{BIG_FONT}{BLACK}{1:INDUSTRY} zvyšuje produkciu {0:STRING.g} o {2:COMMA}%! STR_NEWS_INDUSTRY_PRODUCTION_DECREASE_GENERAL :{BIG_FONT}{BLACK}{INDUSTRY} znižuje produkciu o 50% STR_NEWS_INDUSTRY_PRODUCTION_DECREASE_FARM :{BIG_FONT}{BLACK}Premnoženie škodcov spôsobilo zničenie úrody na {INDUSTRY}!{}Produkcia sa zíižila o 50% @@ -957,7 +957,7 @@ STR_NEWS_SERVICE_SUBSIDY_AWARDED_DOUBLE :{BIG_FONT}{BLAC STR_NEWS_SERVICE_SUBSIDY_AWARDED_TRIPLE :{BIG_FONT}{BLACK}Dotácia udelená spoločnosti {STRING}!{}{}Preprava {STRING.g} z {STRING} do {STRING} bude budúci rok 3x výnosnejšia! STR_NEWS_SERVICE_SUBSIDY_AWARDED_QUADRUPLE :{BIG_FONT}{BLACK}Dotácia udelená spoločnosti {STRING}!{}{}Preprava {STRING.g} z {STRING} do {STRING} bude budúci rok 4x výnosnejšia! -STR_NEWS_ROAD_REBUILDING :{BIG_FONT}{BLACK}V meste {TOWN} zavladol dopravny chaos!{}{}Rekonstrukcia ciest financovana {STRING} prinesie 6 mesiacov utrpenia pre motoristov! +STR_NEWS_ROAD_REBUILDING :{BIG_FONT}{BLACK}V meste {TOWN} zavládol dopravný chaos!{}{}Rekonštrukcia ciest financovaná {STRING} prinesie 6 mesiacov utrpenia pre motoristov! STR_NEWS_EXCLUSIVE_RIGHTS_TITLE :{BIG_FONT}{BLACK}Prepravné monopoly! STR_NEWS_EXCLUSIVE_RIGHTS_DESCRIPTION :{BIG_FONT}{BLACK}Miestna samospráva mesta {TOWN} podpísala exkluzívnu zmluvu na prepravu so spoločnosťou {STRING} na 1 rok !!! @@ -1021,7 +1021,7 @@ STR_GAME_OPTIONS_CURRENCY_MYR :Malajzijský ri STR_GAME_OPTIONS_ROAD_VEHICLES_DROPDOWN_LEFT :Jazdia naľavo STR_GAME_OPTIONS_ROAD_VEHICLES_DROPDOWN_RIGHT :Jazdia napravo -STR_GAME_OPTIONS_TOWN_NAMES_FRAME :{BLACK}Názvy miest +STR_GAME_OPTIONS_TOWN_NAMES_FRAME :{BLACK}Názvy miest: STR_GAME_OPTIONS_TOWN_NAMES_DROPDOWN_TOOLTIP :{BLACK}Výber štýlu názvov miest ############ start of townname region @@ -1061,6 +1061,7 @@ STR_GAME_OPTIONS_AUTOSAVE_DROPDOWN_EVERY_12_MONTHS :Každých 12 me STR_GAME_OPTIONS_LANGUAGE :{BLACK}Jazyk STR_GAME_OPTIONS_LANGUAGE_TOOLTIP :{BLACK}Výber jazyka rozhrania +STR_GAME_OPTIONS_LANGUAGE_PERCENTAGE :{STRING} ({NUM}% hotovo) STR_GAME_OPTIONS_FULLSCREEN :{BLACK}Celá obrazovka STR_GAME_OPTIONS_FULLSCREEN_TOOLTIP :{BLACK}Zaškrtnite, ak chcete hrať OpenTTD na celej obrazovke @@ -1074,6 +1075,8 @@ STR_GAME_OPTIONS_VIDEO_ACCELERATION :{BLACK}Hardvér STR_GAME_OPTIONS_VIDEO_ACCELERATION_TOOLTIP :{BLACK}Zaškrtnutím políčka dovolíte, aby sa OpenTTD pokúsilo použiť hardvérové zrýchlenie. Zmena nastavenia sa uplatní až po reštartovaní hry STR_GAME_OPTIONS_VIDEO_ACCELERATION_RESTART :{WHITE}Nastavenie sa uplatní až po reštartovaní hry +STR_GAME_OPTIONS_VIDEO_VSYNC :{BLACK}VSync +STR_GAME_OPTIONS_VIDEO_VSYNC_TOOLTIP :{BLACK}Zaškrtnite toto políčko, aby ste povolili vertikálnu synchronizáciu. Zmeny budú uplatnené až po reštarte hry. Funguje iba ak je zapnutá hardvérová akcelerácia. STR_GAME_OPTIONS_GUI_ZOOM_FRAME :{BLACK}Veľkosť rozhrania STR_GAME_OPTIONS_GUI_ZOOM_DROPDOWN_TOOLTIP :{BLACK}Výber veľkosti prvkov rozhrania @@ -1162,9 +1165,9 @@ STR_VARIETY_HIGH :Vysoká STR_VARIETY_VERY_HIGH :Veľmi vysoká STR_AI_SPEED_VERY_SLOW :Veľmi pomalé -STR_AI_SPEED_SLOW :Pomale +STR_AI_SPEED_SLOW :Pomalé STR_AI_SPEED_MEDIUM :Stredne -STR_AI_SPEED_FAST :Rychle +STR_AI_SPEED_FAST :Rýchle STR_AI_SPEED_VERY_FAST :Veľmi rýchle STR_SEA_LEVEL_VERY_LOW :Veľmi nízka @@ -1196,9 +1199,9 @@ STR_TERRAIN_TYPE_ALPINIST :Alpinista STR_TERRAIN_TYPE_CUSTOM :Vlastná výška STR_TERRAIN_TYPE_CUSTOM_VALUE :Vlastná výška ({NUM}) -STR_CITY_APPROVAL_PERMISSIVE :Pozitivny -STR_CITY_APPROVAL_TOLERANT :Tolerantny -STR_CITY_APPROVAL_HOSTILE :Odmietavy +STR_CITY_APPROVAL_PERMISSIVE :Pozitívny +STR_CITY_APPROVAL_TOLERANT :Tolerantný +STR_CITY_APPROVAL_HOSTILE :Odmietavý STR_WARNING_NO_SUITABLE_AI :{WHITE}Nieje dostupné žiadne použiteľné AI...{}Niekoľko AI je možné stiahnuť cez 'Online obsah' @@ -1207,6 +1210,7 @@ STR_CONFIG_SETTING_TREE_CAPTION :{WHITE}Nastaven STR_CONFIG_SETTING_FILTER_TITLE :{BLACK}Filtrovací reťazec: STR_CONFIG_SETTING_EXPAND_ALL :{BLACK}Rozbaliť všetko STR_CONFIG_SETTING_COLLAPSE_ALL :{BLACK}Zabaliť všetko +STR_CONFIG_SETTING_RESET_ALL :Resetovať všetky hodnoty STR_CONFIG_SETTING_NO_EXPLANATION_AVAILABLE_HELPTEXT :(nie je dostupné vysvetlenie) STR_CONFIG_SETTING_DEFAULT_VALUE :{LTBLUE}Predvolená hodnota: {ORANGE}{STRING} STR_CONFIG_SETTING_TYPE :{LTBLUE}Nastavenie typu: {ORANGE}{STRING} @@ -1215,6 +1219,8 @@ STR_CONFIG_SETTING_TYPE_GAME_MENU :Nastavenie hry STR_CONFIG_SETTING_TYPE_GAME_INGAME :Nastavenie hry (ukladané v uložených hrách; ovplyvní iba aktuálnu hru) STR_CONFIG_SETTING_TYPE_COMPANY_MENU :Nastavenie spoločnosti (ukladané v uložených hrách; ovplyvní iba nové hry) STR_CONFIG_SETTING_TYPE_COMPANY_INGAME :Nastavenie spoločnosti (ukladané v uložených hrách; ovplyvní iba aktuálnu spoločnosť) +STR_CONFIG_SETTING_RESET_ALL_CONFIRMATION_DIALOG_CAPTION :{WHITE}Pozor! +STR_CONFIG_SETTING_RESET_ALL_CONFIRMATION_DIALOG_TEXT :{WHITE}Táto akcia vráti všetky nastavenia hry do pôvodného stavu.{}Chcete naozaj pokračovať? STR_CONFIG_SETTING_RESTRICT_CATEGORY :{BLACK}Kategória: STR_CONFIG_SETTING_RESTRICT_TYPE :{BLACK}Typ: @@ -1249,7 +1255,7 @@ STR_CONFIG_SETTING_NONE :Žiadne STR_CONFIG_SETTING_ORIGINAL :Pôvodné STR_CONFIG_SETTING_REALISTIC :Realistické -STR_CONFIG_SETTING_HORIZONTAL_POS_LEFT :vlavo +STR_CONFIG_SETTING_HORIZONTAL_POS_LEFT :vľavo STR_CONFIG_SETTING_HORIZONTAL_POS_CENTER :v strede STR_CONFIG_SETTING_HORIZONTAL_POS_RIGHT :vpravo @@ -1349,7 +1355,7 @@ STR_CONFIG_SETTING_BRIBE_HELPTEXT :Povolí podplá STR_CONFIG_SETTING_ALLOW_EXCLUSIVE :Povoliť zakúpenie exkluzívnych dopravných práv: {STRING} STR_CONFIG_SETTING_ALLOW_EXCLUSIVE_HELPTEXT :Ak si spoločnosť zakúpi exkluzívne prepravné práva od mesta, stanice protihráčov (pasažieri a náklad) nebudú prijímať žiaden náklad po celý rok ! STR_CONFIG_SETTING_ALLOW_FUND_BUILDINGS :Povoliť financovanie stavieb: {STRING} -STR_CONFIG_SETTING_ALLOW_FUND_BUILDINGS_HELPTEXT :Povolí spoločnostiam finančne prispievať na rozvoj mesta a budovanie nových domov. +STR_CONFIG_SETTING_ALLOW_FUND_BUILDINGS_HELPTEXT :Povoliť spoločnostiam finančne prispievať na rozvoj mesta a budovanie nových domov. STR_CONFIG_SETTING_ALLOW_FUND_ROAD :Povoliť financovanie rekonštrukcie miestných ciest: {STRING} STR_CONFIG_SETTING_ALLOW_FUND_ROAD_HELPTEXT :Povolí spoločnostiam finančne prispievať mestu na rekonštrukciu ciest. To môže zapríčiniť sabotovanie prepráv založených na cestných komunikáciách STR_CONFIG_SETTING_ALLOW_GIVE_MONEY :Umožniť posielanie peňazí ostatným spoločnostiam: {STRING} @@ -1482,7 +1488,7 @@ STR_CONFIG_SETTING_LIVERIES_NONE :Žiadne STR_CONFIG_SETTING_LIVERIES_OWN :Vlastná spoločnosť STR_CONFIG_SETTING_LIVERIES_ALL :Všetky spoločnosti STR_CONFIG_SETTING_PREFER_TEAMCHAT :Preferovať tímový chat s klávesou : {STRING} -STR_CONFIG_SETTING_PREFER_TEAMCHAT_HELPTEXT :Prepni systém odosielania správ medzi interným a verejným chatom. respektíve +STR_CONFIG_SETTING_PREFER_TEAMCHAT_HELPTEXT :Prepnúť systém odosielania správ medzi interným a verejným chatom. respektíve STR_CONFIG_SETTING_SCROLLWHEEL_SCROLLING :Funkcia rolovacieho kolieska myši: {STRING} STR_CONFIG_SETTING_SCROLLWHEEL_SCROLLING_HELPTEXT :Povolí posúvanie pomocou dvoj-dimenzionálnych kolečiek myši STR_CONFIG_SETTING_SCROLLWHEEL_ZOOM :Priblížovať mapu @@ -1532,7 +1538,7 @@ STR_CONFIG_SETTING_TIMETABLE_IN_TICKS_HELPTEXT :Zobrazí časy STR_CONFIG_SETTING_TIMETABLE_SHOW_ARRIVAL_DEPARTURE :Zobraziť príchody a odchody v cestovných poriadkoch: {STRING} STR_CONFIG_SETTING_TIMETABLE_SHOW_ARRIVAL_DEPARTURE_HELPTEXT :Zobrazí predpokladané časy príchodov a odchodov v časových rozpisoch. STR_CONFIG_SETTING_QUICKGOTO :Rýchla tvorba cestovného poriadku vozidla: {STRING} -STR_CONFIG_SETTING_QUICKGOTO_HELPTEXT :Prednastav "Choď do" kurzor pri otvorení okna s príkazmi +STR_CONFIG_SETTING_QUICKGOTO_HELPTEXT :Prednastaviť "Choď do" kurzor pri otvorení okna s príkazmi STR_CONFIG_SETTING_DEFAULT_RAIL_TYPE :Predvolený typ koľají (v novej/nahranej hre): {STRING} STR_CONFIG_SETTING_DEFAULT_RAIL_TYPE_HELPTEXT :Typ železnice zvolený po štarte alebo nahraní hry. 'prvé dostupné' zvolí najstarší typ koľají, 'posledný dostupný' zvolí najnovší typ koľají, a 'najpoužívanejší' vyberie typ ktorý je v danej dobe najviac používaný. STR_CONFIG_SETTING_DEFAULT_RAIL_TYPE_FIRST :prvé dostupné @@ -1594,10 +1600,10 @@ STR_CONFIG_SETTING_AI_PROFILE_EASY :Ľahký STR_CONFIG_SETTING_AI_PROFILE_MEDIUM :Stredný STR_CONFIG_SETTING_AI_PROFILE_HARD :{G=m}Ťažký -STR_CONFIG_SETTING_AI_IN_MULTIPLAYER :Povoliť AI-ov v hre viacerých hráčov: {STRING} +STR_CONFIG_SETTING_AI_IN_MULTIPLAYER :Povoliť AI v hre viacerých hráčov: {STRING} STR_CONFIG_SETTING_AI_IN_MULTIPLAYER_HELPTEXT :Povoliť AI počítačovým hráčom hrať "Hru viacerých hráčov". STR_CONFIG_SETTING_SCRIPT_MAX_OPCODES :#op kódov pred uspaním skriptu: {STRING} -STR_CONFIG_SETTING_SCRIPT_MAX_OPCODES_HELPTEXT :Maximálne počet krokov skriptu počas 1 ťahu. +STR_CONFIG_SETTING_SCRIPT_MAX_OPCODES_HELPTEXT :Maximálny počet krokov skriptu počas 1 ťahu. STR_CONFIG_SETTING_SCRIPT_MAX_MEMORY :Maximálne využitie pamäte na skript: {STRING} STR_CONFIG_SETTING_SCRIPT_MAX_MEMORY_HELPTEXT :Koľko pamäte môže jeden skript spotrebovať pred násilným ukončením. Pri veľkých mapách bude možno potrebné túto hodnotu zvýšiť. STR_CONFIG_SETTING_SCRIPT_MAX_MEMORY_VALUE :{COMMA} MiB @@ -1871,11 +1877,11 @@ STR_CONFIG_ERROR_INVALID_VALUE :{WHITE}... zlá STR_CONFIG_ERROR_TRAILING_CHARACTERS :{WHITE}... staviam postavičky na koniec nastavení '{STRING}' STR_CONFIG_ERROR_DUPLICATE_GRFID :{WHITE}... ignorujem NewGRF '{STRING}': duplikuj GRF ID s hodnotou '{STRING}' STR_CONFIG_ERROR_INVALID_GRF :{WHITE}... ignorujem nevhodné NewGRF '{STRING}': {STRING} -STR_CONFIG_ERROR_INVALID_GRF_NOT_FOUND :nenašiel som +STR_CONFIG_ERROR_INVALID_GRF_NOT_FOUND :nenájdené STR_CONFIG_ERROR_INVALID_GRF_UNSAFE :nevhodné pre statické použitie STR_CONFIG_ERROR_INVALID_GRF_SYSTEM :systém NewGRF STR_CONFIG_ERROR_INVALID_GRF_INCOMPATIBLE :nekompatibilné s touto verziou OpenTTD -STR_CONFIG_ERROR_INVALID_GRF_UNKNOWN :také nepoznám +STR_CONFIG_ERROR_INVALID_GRF_UNKNOWN :neznáme STR_CONFIG_ERROR_INVALID_SAVEGAME_COMPRESSION_LEVEL :{WHITE}... hodnota kompresie '{STRING}' je zle zadaná STR_CONFIG_ERROR_INVALID_SAVEGAME_COMPRESSION_ALGORITHM :{WHITE}... formát uloženej hry '{STRING}' je nedostupný. Zmeniť späť na '{STRING}' STR_CONFIG_ERROR_INVALID_BASE_GRAPHICS_NOT_FOUND :{WHITE}... ignorujem základnú grafickú sadu '{STRING}': nenájdené @@ -1949,8 +1955,8 @@ STR_CHEAT_CHANGE_COMPANY :{LTBLUE}Hrať z STR_CHEAT_EXTRA_DYNAMITE :{LTBLUE}Magický buldozér (odstráni priemysel a nehnuteľnosti): {ORANGE}{STRING} STR_CHEAT_CROSSINGTUNNELS :{LTBLUE}Tunely sa môžu navzájom krížiť: {ORANGE}{STRING} STR_CHEAT_NO_JETCRASH :{LTBLUE}Prúdové lietadlá nehavarujú (tak často) na malých letiskách: {ORANGE} {STRING} -STR_CHEAT_EDIT_MAX_HL :{LTBLUE}Zmeň maximálnu výšku mapy: {ORANGE}{NUM} -STR_CHEAT_EDIT_MAX_HL_QUERY_CAPT :{WHITE}Zmeň maximálnu výšku hôr na mape +STR_CHEAT_EDIT_MAX_HL :{LTBLUE}Zmeniť maximálnu výšku mapy: {ORANGE}{NUM} +STR_CHEAT_EDIT_MAX_HL_QUERY_CAPT :{WHITE}Zmeniť maximálnu výšku hôr na mape STR_CHEAT_SWITCH_CLIMATE_TEMPERATE_LANDSCAPE :Krajina mierneho pásma STR_CHEAT_SWITCH_CLIMATE_SUB_ARCTIC_LANDSCAPE :Subpolárna krajina STR_CHEAT_SWITCH_CLIMATE_SUB_TROPICAL_LANDSCAPE :Subtropická krajina @@ -2053,11 +2059,13 @@ STR_FACE_TIE :Kravata: STR_FACE_EARRING :Náušnica: STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}Zmeniť kravatu alebo náušnicu +STR_NETWORK_SERVER_VISIBILITY_PRIVATE :Privátny +STR_NETWORK_SERVER_VISIBILITY_PUBLIC :Verejný # Network server list STR_NETWORK_SERVER_LIST_CAPTION :{WHITE}Hra pre viac hráčov STR_NETWORK_SERVER_LIST_PLAYER_NAME :{BLACK}Meno hráča: -STR_NETWORK_SERVER_LIST_ENTER_NAME_TOOLTIP :{BLACK}Toto je meno podla ktoreho vas ostatny identifikuju +STR_NETWORK_SERVER_LIST_ENTER_NAME_TOOLTIP :{BLACK}Toto je meno podľa ktorého vás ostatný identifikujú STR_NETWORK_SERVER_LIST_GAME_NAME :{BLACK}Názov STR_NETWORK_SERVER_LIST_GAME_NAME_TOOLTIP :{BLACK}Názov hry @@ -2089,12 +2097,12 @@ STR_NETWORK_SERVER_LIST_CURRENT_DATE :{SILVER}Aktuál STR_NETWORK_SERVER_LIST_PASSWORD :{SILVER}Heslo: STR_NETWORK_SERVER_LIST_SERVER_OFFLINE :{SILVER}SERVER JE OFFLINE STR_NETWORK_SERVER_LIST_SERVER_FULL :{SILVER}SERVER JE PLNÝ -STR_NETWORK_SERVER_LIST_VERSION_MISMATCH :{SILVER}ROZNE VERZIE +STR_NETWORK_SERVER_LIST_VERSION_MISMATCH :{SILVER}RÔZNE VERZIE STR_NETWORK_SERVER_LIST_GRF_MISMATCH :{SILVER}NEWGRF Chyba -STR_NETWORK_SERVER_LIST_JOIN_GAME :{BLACK}Pripojit sa +STR_NETWORK_SERVER_LIST_JOIN_GAME :{BLACK}Pripojiť sa STR_NETWORK_SERVER_LIST_REFRESH :{BLACK}Obnoviť server -STR_NETWORK_SERVER_LIST_REFRESH_TOOLTIP :{BLACK}Obnovit info o serveri +STR_NETWORK_SERVER_LIST_REFRESH_TOOLTIP :{BLACK}Obnoviť info o serveri STR_NETWORK_SERVER_LIST_SEARCH_SERVER_INTERNET :{BLACK}Prehľadať internet STR_NETWORK_SERVER_LIST_SEARCH_SERVER_INTERNET_TOOLTIP :{BLACK}Prehľadať verejné servery na internete @@ -2116,6 +2124,8 @@ STR_NETWORK_START_SERVER_NEW_GAME_NAME_TOOLTIP :{BLACK}Názov h STR_NETWORK_START_SERVER_SET_PASSWORD :{BLACK}Nastaviť heslo STR_NETWORK_START_SERVER_PASSWORD_TOOLTIP :{BLACK}Zabezpeč hru heslom, ak nechceš povoliť verejný prístup +STR_NETWORK_START_SERVER_VISIBILITY_LABEL :{BLACK}Viditeľnosť +STR_NETWORK_START_SERVER_VISIBILITY_TOOLTIP :{BLACK}Aby ostatní hráči videli tento server v zozname verejných servrov STR_NETWORK_START_SERVER_CLIENTS_SELECT :{BLACK}{NUM} klient{P "" i ov} STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS :{BLACK}Maximálny počet klientov: STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS_TOOLTIP :{BLACK}Zvoľ maximálny počet klientov. Môže sa ich pripojiť aj menej. @@ -2136,17 +2146,17 @@ STR_NETWORK_GAME_LOBBY_CAPTION :{WHITE}Sieťov STR_NETWORK_GAME_LOBBY_PREPARE_TO_JOIN :{BLACK}Pripravuje sa k vstupu: {ORANGE}{STRING} STR_NETWORK_GAME_LOBBY_COMPANY_LIST_TOOLTIP :{BLACK}Aktuálny zoznam spoločností v tejto hre. Buď môžeš do jednej vstúpiť, alebo založiť novú spoločnosť. -STR_NETWORK_GAME_LOBBY_COMPANY_INFO :{SILVER}INFO O SPOLOCNOSTI +STR_NETWORK_GAME_LOBBY_COMPANY_INFO :{SILVER}INFO O SPOLOČNOSTI STR_NETWORK_GAME_LOBBY_COMPANY_NAME :{SILVER}Názov spoločnosti: {WHITE}{STRING} -STR_NETWORK_GAME_LOBBY_INAUGURATION_YEAR :{SILVER}Zalozene: {WHITE}{NUM} +STR_NETWORK_GAME_LOBBY_INAUGURATION_YEAR :{SILVER}Založené: {WHITE}{NUM} STR_NETWORK_GAME_LOBBY_VALUE :{SILVER}Hodnota spoločnosti: {WHITE}{CURRENCY_LONG} -STR_NETWORK_GAME_LOBBY_CURRENT_BALANCE :{SILVER}Stav uctu: {WHITE}{CURRENCY_LONG} +STR_NETWORK_GAME_LOBBY_CURRENT_BALANCE :{SILVER}Stav účtu: {WHITE}{CURRENCY_LONG} STR_NETWORK_GAME_LOBBY_LAST_YEARS_INCOME :{SILVER}Zisk v minulom roku: {WHITE}{CURRENCY_LONG} -STR_NETWORK_GAME_LOBBY_PERFORMANCE :{SILVER}Vykon: {WHITE}{NUM} +STR_NETWORK_GAME_LOBBY_PERFORMANCE :{SILVER}Výkon: {WHITE}{NUM} -STR_NETWORK_GAME_LOBBY_VEHICLES :{SILVER}Vozidla: {WHITE}{NUM} {TRAIN}, {NUM} {LORRY}, {NUM} {BUS}, {NUM} {SHIP}, {NUM} {PLANE} +STR_NETWORK_GAME_LOBBY_VEHICLES :{SILVER}Vozidlá: {WHITE}{NUM} {TRAIN}, {NUM} {LORRY}, {NUM} {BUS}, {NUM} {SHIP}, {NUM} {PLANE} STR_NETWORK_GAME_LOBBY_STATIONS :{SILVER}Stanice: {WHITE}{NUM} {TRAIN}, {NUM} {LORRY}, {NUM} {BUS}, {NUM} {SHIP}, {NUM} {PLANE} -STR_NETWORK_GAME_LOBBY_PLAYERS :{SILVER}Hraci: {WHITE}{STRING} +STR_NETWORK_GAME_LOBBY_PLAYERS :{SILVER}Hráči: {WHITE}{STRING} STR_NETWORK_GAME_LOBBY_NEW_COMPANY :{BLACK}Nová spoločnosť STR_NETWORK_GAME_LOBBY_NEW_COMPANY_TOOLTIP :{BLACK}Založiť novú spoločnosť @@ -2179,11 +2189,44 @@ STR_NETWORK_NEED_GAME_PASSWORD_CAPTION :{WHITE}Server j STR_NETWORK_NEED_COMPANY_PASSWORD_CAPTION :{WHITE}Spoločnosť je chránená. Zadaj heslo # Network company list added strings -STR_NETWORK_COMPANY_LIST_CLIENT_LIST :Zoznam klientov +STR_NETWORK_COMPANY_LIST_CLIENT_LIST :Zoznam pripojených klientov # Network client list - - +STR_NETWORK_CLIENT_LIST_CAPTION :{WHITE}Hra pre viacerých hráčov +STR_NETWORK_CLIENT_LIST_SERVER :{BLACK}Server +STR_NETWORK_CLIENT_LIST_SERVER_NAME :{BLACK}Meno +STR_NETWORK_CLIENT_LIST_SERVER_NAME_TOOLTIP :{BLACK}Názov servra, na ktorom práve hráte +STR_NETWORK_CLIENT_LIST_SERVER_NAME_EDIT_TOOLTIP :{BLACK}Upraviť názov servra +STR_NETWORK_CLIENT_LIST_SERVER_NAME_QUERY_CAPTION :Názov servra +STR_NETWORK_CLIENT_LIST_SERVER_VISIBILITY :{BLACK}Viditelnosť +STR_NETWORK_CLIENT_LIST_SERVER_VISIBILITY_TOOLTIP :{BLACK}Aby ostatní hráči videli váš server v zozname verejných servrov +STR_NETWORK_CLIENT_LIST_PLAYER :{BLACK}Hráč +STR_NETWORK_CLIENT_LIST_PLAYER_NAME :{BLACK}Meno +STR_NETWORK_CLIENT_LIST_PLAYER_NAME_TOOLTIP :{BLACK}Meno vašeho hráča +STR_NETWORK_CLIENT_LIST_PLAYER_NAME_EDIT_TOOLTIP :{BLACK}Upraviť meno vašeho hráča +STR_NETWORK_CLIENT_LIST_PLAYER_NAME_QUERY_CAPTION :Meno vašeho hráča +STR_NETWORK_CLIENT_LIST_ADMIN_CLIENT_TOOLTIP :{BLACK}Administrátorské činnosti, ktoré môžete vykonačť pre tohoto klienta +STR_NETWORK_CLIENT_LIST_ADMIN_COMPANY_TOOLTIP :{BLACK}Administrátorské činnosti, ktoré pre túto spoločnosť môžete vykonať +STR_NETWORK_CLIENT_LIST_JOIN_TOOLTIP :{BLACK}Pripojiť sa k tejto spoločnosti +STR_NETWORK_CLIENT_LIST_CHAT_CLIENT_TOOLTIP :{BLACK}Poslať správu tomuto hráčovi +STR_NETWORK_CLIENT_LIST_CHAT_COMPANY_TOOLTIP :{BLACK}Poslať správu všetkým hráčom tejto spoločnosti +STR_NETWORK_CLIENT_LIST_CHAT_SPECTATOR_TOOLTIP :{BLACK}Poslať správu všetkým pozorovateľom +STR_NETWORK_CLIENT_LIST_SPECTATORS :Pozorovatelia +STR_NETWORK_CLIENT_LIST_NEW_COMPANY :(Nová spoločnosť) +STR_NETWORK_CLIENT_LIST_NEW_COMPANY_TOOLTIP :{BLACK}Založiť novú spoločnosť a pripojiť sa k nej +STR_NETWORK_CLIENT_LIST_PLAYER_ICON_SELF_TOOLTIP :{BLACK}Toto ste vy +STR_NETWORK_CLIENT_LIST_PLAYER_ICON_HOST_TOOLTIP :{BLACK}Toto je hosť hry + +STR_NETWORK_CLIENT_LIST_ADMIN_CLIENT_KICK :Vyhodiť +STR_NETWORK_CLIENT_LIST_ADMIN_CLIENT_BAN :Ban +STR_NETWORK_CLIENT_LIST_ADMIN_COMPANY_RESET :Vymazať +STR_NETWORK_CLIENT_LIST_ADMIN_COMPANY_UNLOCK :Odomknúť heslom + +STR_NETWORK_CLIENT_LIST_ASK_CAPTION :{WHITE}Akcia administrátora +STR_NETWORK_CLIENT_LIST_ASK_CLIENT_KICK :{YELLOW}Ste si istý, že chcete vyhodiť hráča '{STRING}'? +STR_NETWORK_CLIENT_LIST_ASK_CLIENT_BAN :{YELLOW}Ste si istý, že chcete zabanovť hráča '{STRING}'? +STR_NETWORK_CLIENT_LIST_ASK_COMPANY_RESET :{YELLOW}Ste si istý, že chcete vymazať spoločnosť '{COMPANY}'? +STR_NETWORK_CLIENT_LIST_ASK_COMPANY_UNLOCK :{YELLOW}Ste si istý, že chcete zresetovať heslo pre spoločnosť '{COMPANY}'? STR_NETWORK_SERVER :Server STR_NETWORK_CLIENT :Klient @@ -2218,50 +2261,54 @@ STR_NETWORK_CHAT_OSKTITLE :{BLACK}Zadajte # Network messages STR_NETWORK_ERROR_NOTAVAILABLE :{WHITE}Nebolo nájdené žiadne sieťové zariadenie, alebo je hra kompilovaná bez ENABLE_NETWORK -STR_NETWORK_ERROR_NOSERVER :{WHITE}Nebola najdena ziadna sietova hra -STR_NETWORK_ERROR_NOCONNECTION :{WHITE}Server neodpoveda na ziadost -STR_NETWORK_ERROR_NEWGRF_MISMATCH :{WHITE}Pripojenie zlyhalo kvoli nespravnemu NewGRF +STR_NETWORK_ERROR_NOSERVER :{WHITE}Nebola nájdená žiadna sieťová hra +STR_NETWORK_ERROR_NOCONNECTION :{WHITE}Server neodpovedá na žiadosť +STR_NETWORK_ERROR_NEWGRF_MISMATCH :{WHITE}Pripojenie zlyhalo kvôli nesprávnemu NewGRF STR_NETWORK_ERROR_DESYNC :{WHITE}Sieť - Chyba synchronizácie hry STR_NETWORK_ERROR_LOSTCONNECTION :{WHITE}Sieť - Stratené spojenie STR_NETWORK_ERROR_SAVEGAMEERROR :{WHITE}Nedokážem nahrať hru zo servera STR_NETWORK_ERROR_SERVER_START :{WHITE}Server nemôžem spustiť -STR_NETWORK_ERROR_CLIENT_START :{WHITE}Nemozem sa pripojit. +STR_NETWORK_ERROR_CLIENT_START :{WHITE}Nemožem sa pripojiť STR_NETWORK_ERROR_TIMEOUT :{WHITE}Vypršal časový limit pre spojenie č. {NUM} -STR_NETWORK_ERROR_SERVER_ERROR :{WHITE}Chyba vznikla v protokole a spojenie je zatvorene. +STR_NETWORK_ERROR_SERVER_ERROR :{WHITE}Chyba vznikla v protokole a spojenie je zatvorené +STR_NETWORK_ERROR_BAD_PLAYER_NAME :{WHITE}Meno vašeho hráča nebolo nastavené. Meno si môžete zmeniť v hlavičke okna Hry pre viacerých hráčov STR_NETWORK_ERROR_WRONG_REVISION :{WHITE}Revizia hry u tohto klienta nezodpoveda revizii hry na serveri. -STR_NETWORK_ERROR_WRONG_PASSWORD :{WHITE}Nespravne heslo. -STR_NETWORK_ERROR_SERVER_FULL :{WHITE}Server je plny -STR_NETWORK_ERROR_SERVER_BANNED :{WHITE}Si zablokovany na tomto serveri -STR_NETWORK_ERROR_KICKED :{WHITE}Bol si vyhodeny z hry +STR_NETWORK_ERROR_WRONG_PASSWORD :{WHITE}Nesprávne heslo +STR_NETWORK_ERROR_SERVER_FULL :{WHITE}Server je plný +STR_NETWORK_ERROR_SERVER_BANNED :{WHITE}Si zabanovaný na tomto serveri +STR_NETWORK_ERROR_KICKED :{WHITE}Bol si vyhodený z hry STR_NETWORK_ERROR_KICK_MESSAGE :{WHITE}Dôvod: {STRING} -STR_NETWORK_ERROR_CHEATER :{WHITE}Cheatovanie nie je povolene na tomto serveri +STR_NETWORK_ERROR_CHEATER :{WHITE}Cheatovanie nie je povolené na tomto serveri STR_NETWORK_ERROR_TOO_MANY_COMMANDS :{WHITE}Posielali ste priveľa príkazov serveru STR_NETWORK_ERROR_TIMEOUT_PASSWORD :{WHITE}Príliš dlho si zadával heslo STR_NETWORK_ERROR_TIMEOUT_COMPUTER :{WHITE}Tvoj počítač sa príliš pomaly pripájal na server STR_NETWORK_ERROR_TIMEOUT_MAP :{WHITE}Tvoj počítač sťahoval mapu príliš dlho STR_NETWORK_ERROR_TIMEOUT_JOIN :{WHITE}Tvoj počítač sa príliš dlho pripájal na server +STR_NETWORK_ERROR_INVALID_CLIENT_NAME :{WHITE}Meno vašeho hráča nieje platné +STR_NETWORK_ERROR_SERVER_TOO_OLD :{WHITE}Nájdený server je príliš starý pre vašeho klienta ############ Leave those lines in this order!! -STR_NETWORK_ERROR_CLIENT_GENERAL :generalna chyba -STR_NETWORK_ERROR_CLIENT_DESYNC :chyba synchronizacie +STR_NETWORK_ERROR_CLIENT_GENERAL :všeobecná chyba +STR_NETWORK_ERROR_CLIENT_DESYNC :chyba synchronizácie STR_NETWORK_ERROR_CLIENT_SAVEGAME :nemôžem načítať mapu STR_NETWORK_ERROR_CLIENT_CONNECTION_LOST :spojenie prerušené STR_NETWORK_ERROR_CLIENT_PROTOCOL_ERROR :chyba protokolu -STR_NETWORK_ERROR_CLIENT_NEWGRF_MISMATCH :Nespravne NewGRF -STR_NETWORK_ERROR_CLIENT_NOT_AUTHORIZED :overenie odmietnute +STR_NETWORK_ERROR_CLIENT_NEWGRF_MISMATCH :Nesprávne NewGRF +STR_NETWORK_ERROR_CLIENT_NOT_AUTHORIZED :overenie odmietnuté STR_NETWORK_ERROR_CLIENT_NOT_EXPECTED :prijatý neplatný, alebo neočakávaný paket -STR_NETWORK_ERROR_CLIENT_WRONG_REVISION :chybna revizia hry -STR_NETWORK_ERROR_CLIENT_NAME_IN_USE :meno sa uz pouziva -STR_NETWORK_ERROR_CLIENT_WRONG_PASSWORD :nespravne heslo +STR_NETWORK_ERROR_CLIENT_WRONG_REVISION :chybná verzia hry +STR_NETWORK_ERROR_CLIENT_NAME_IN_USE :meno sa už používa +STR_NETWORK_ERROR_CLIENT_WRONG_PASSWORD :nesprávne heslo STR_NETWORK_ERROR_CLIENT_COMPANY_MISMATCH :zlá spoločnosť-id in DoCommand STR_NETWORK_ERROR_CLIENT_KICKED :vyhodený zo servera -STR_NETWORK_ERROR_CLIENT_CHEATER :sa pokusal cheatovat -STR_NETWORK_ERROR_CLIENT_SERVER_FULL :server je plny +STR_NETWORK_ERROR_CLIENT_CHEATER :sa pokúšal cheatovať +STR_NETWORK_ERROR_CLIENT_SERVER_FULL :server je plný STR_NETWORK_ERROR_CLIENT_TOO_MANY_COMMANDS :posielal priveľa príkazov STR_NETWORK_ERROR_CLIENT_TIMEOUT_PASSWORD :nebolo včas obdržané heslo STR_NETWORK_ERROR_CLIENT_TIMEOUT_COMPUTER :všeobecný timeout STR_NETWORK_ERROR_CLIENT_TIMEOUT_MAP :sťahovanie mapy trvalo príliš dlho STR_NETWORK_ERROR_CLIENT_TIMEOUT_JOIN :spracovanie mapy trvalo príliš dlho +STR_NETWORK_ERROR_CLIENT_INVALID_CLIENT_NAME :Neplatný názov klienta ############ End of leave-in-this-order STR_NETWORK_ERROR_CLIENT_GUI_LOST_CONNECTION_CAPTION :{WHITE}Možná ztráta pripojenia @@ -2292,7 +2339,7 @@ STR_NETWORK_MESSAGE_CLIENT_COMPANY_NEW :*** {STRING} za STR_NETWORK_MESSAGE_CLIENT_LEFT :*** {STRING} opustil hru ({2:STRING}) STR_NETWORK_MESSAGE_NAME_CHANGE :*** {STRING} zmenil/-a svoje meno na {STRING} STR_NETWORK_MESSAGE_GIVE_MONEY :*** {STRING} dal {2:CURRENCY_LONG} spoločnosti {1:STRING} -STR_NETWORK_MESSAGE_SERVER_SHUTDOWN :{WHITE}Server ukoncil relaciu +STR_NETWORK_MESSAGE_SERVER_SHUTDOWN :{WHITE}Server ukončil reláciu STR_NETWORK_MESSAGE_SERVER_REBOOT :{WHITE}Server sa reštartuje...{}Čakajte prosím... STR_NETWORK_MESSAGE_KICKED :*** Hráč {STRING} bol vyhodený. Dôvod: ({STRING}) @@ -2462,7 +2509,7 @@ STR_STATION_BUILD_STATION_CLASS_TOOLTIP :{BLACK}Vyberte STR_STATION_BUILD_STATION_TYPE_TOOLTIP :{BLACK}Vyberte typ stanice pre vystavbu STR_STATION_CLASS_DFLT :Pôvodná stanica -STR_STATION_CLASS_WAYP :Smerove body +STR_STATION_CLASS_WAYP :Smerové body # Signal window STR_BUILD_SIGNAL_CAPTION :{WHITE}Výber návestidla @@ -2480,7 +2527,7 @@ STR_BUILD_SIGNAL_ELECTRIC_PBS_TOOLTIP :{BLACK}Trasové STR_BUILD_SIGNAL_ELECTRIC_PBS_OWAY_TOOLTIP :{BLACK}Jednosmerné trasové návestidlo (elektrické){}Trasové návestidlo povolí viac vlakov v jednom úseku súčasne, ak má vlak voľnú trasu, ktorú si rezervuje. Jednosmerné trasové návestidla môžu vlaky prechádzať iba z prednej strany STR_BUILD_SIGNAL_CONVERT_TOOLTIP :{BLACK}Zameniť návestidlo{}Po vybratí, kliknutím na existujúce návestidlo dochádza k zámene na vybraný typ a variantu návestidla. Ctrl+klik prepne existujúcu variantu. Shift+klik zobrazí odhadovanú cenu zámeny STR_BUILD_SIGNAL_DRAG_SIGNALS_DENSITY_TOOLTIP :{BLACK}Hustota návestidiel pri stavbe ťahaním -STR_BUILD_SIGNAL_DRAG_SIGNALS_DENSITY_DECREASE_TOOLTIP :{BLACK}Znižit hustotu návestidiel +STR_BUILD_SIGNAL_DRAG_SIGNALS_DENSITY_DECREASE_TOOLTIP :{BLACK}Znížiť hustotu návestidiel STR_BUILD_SIGNAL_DRAG_SIGNALS_DENSITY_INCREASE_TOOLTIP :{BLACK}Zvýšit hustotu návestidiel # Bridge selection window @@ -2551,7 +2598,7 @@ STR_WATERWAYS_TOOLBAR_BUILD_DOCK_TOOLTIP :{BLACK}Postavi STR_WATERWAYS_TOOLBAR_BUOY_TOOLTIP :{BLACK}Umiestniť bóju na vyznačenie trasy. Shift zobrazí odhadovanú cenu STR_WATERWAYS_TOOLBAR_BUILD_AQUEDUCT_TOOLTIP :{BLACK}Postaviť akvadukt. Shift zobrazí odhadovanú cenu STR_WATERWAYS_TOOLBAR_CREATE_LAKE_TOOLTIP :{BLACK}Definovať oblasť vody.{}Vytvorí vodný kanál, pri stlacení CTRL na úrovni mora zaplaví okolie -STR_WATERWAYS_TOOLBAR_CREATE_RIVER_TOOLTIP :{BLACK}Umiestniť rieku. +STR_WATERWAYS_TOOLBAR_CREATE_RIVER_TOOLTIP :{BLACK}Umiestniť rieku. Ctrl označí oblasť diagonálne # Ship depot construction window STR_DEPOT_BUILD_SHIP_CAPTION :{WHITE}Orientácia lodenice @@ -2620,16 +2667,16 @@ STR_TREES_MODE_FOREST_LG_TOOLTIP :{BLACK}Sadiť v # Land generation window (SE) STR_TERRAFORM_TOOLBAR_LAND_GENERATION_CAPTION :{WHITE}Generovanie územia -STR_TERRAFORM_TOOLTIP_PLACE_ROCKY_AREAS_ON_LANDSCAPE :{BLACK}Umiestnit nahodne skaly +STR_TERRAFORM_TOOLTIP_PLACE_ROCKY_AREAS_ON_LANDSCAPE :{BLACK}Umiestniť náhodne skaly STR_TERRAFORM_TOOLTIP_DEFINE_DESERT_AREA :{BLACK}Vytvoriť oblasť púšte.{}Držaním Ctrl ju odstránite STR_TERRAFORM_TOOLTIP_INCREASE_SIZE_OF_LAND_AREA :{BLACK}Pre zvýšenie/zníženie oblasti je potrebné zväčšiť územie STR_TERRAFORM_TOOLTIP_DECREASE_SIZE_OF_LAND_AREA :{BLACK}Pre zvýšenie/zníženie oblasti je potrebné zmenšiť územie STR_TERRAFORM_TOOLTIP_GENERATE_RANDOM_LAND :{BLACK}Vygenerovať náhodné územie STR_TERRAFORM_SE_NEW_WORLD :{BLACK}Vytvoriť nový scenár -STR_TERRAFORM_RESET_LANDSCAPE :{BLACK}Zresetovat územie +STR_TERRAFORM_RESET_LANDSCAPE :{BLACK}Zresetovať územie STR_TERRAFORM_RESET_LANDSCAPE_TOOLTIP :{BLACK}Odstrániť všetok majetok spoločnosti z mapy -STR_QUERY_RESET_LANDSCAPE_CAPTION :{WHITE}Zresetovat Územie +STR_QUERY_RESET_LANDSCAPE_CAPTION :{WHITE}Zresetovať Územie STR_RESET_LANDSCAPE_CONFIRMATION_TEXT :{WHITE}Ste si istý, že chcete odstrániť všetok majetok spoločnosti? # Town generation window (SE) @@ -2648,9 +2695,9 @@ STR_FOUND_TOWN_NAME_RANDOM_BUTTON :{BLACK}Náhodn STR_FOUND_TOWN_NAME_RANDOM_TOOLTIP :{BLACK}Vygenerovať nové náhodné meno STR_FOUND_TOWN_INITIAL_SIZE_TITLE :{YELLOW}Veľkosť mesta: -STR_FOUND_TOWN_INITIAL_SIZE_SMALL_BUTTON :{BLACK}Male +STR_FOUND_TOWN_INITIAL_SIZE_SMALL_BUTTON :{BLACK}Malé STR_FOUND_TOWN_INITIAL_SIZE_MEDIUM_BUTTON :{BLACK}Stredne -STR_FOUND_TOWN_INITIAL_SIZE_LARGE_BUTTON :{BLACK}Velke +STR_FOUND_TOWN_INITIAL_SIZE_LARGE_BUTTON :{BLACK}Veľké STR_FOUND_TOWN_SIZE_RANDOM :{BLACK}Náhodný STR_FOUND_TOWN_INITIAL_SIZE_TOOLTIP :{BLACK}Vyber veľkosť mesta STR_FOUND_TOWN_CITY :{BLACK}Veľkomesto @@ -2672,7 +2719,7 @@ STR_FUND_INDUSTRY_MANY_RANDOM_INDUSTRIES_TOOLTIP :{BLACK}Pokryť STR_FUND_INDUSTRY_MANY_RANDOM_INDUSTRIES_CAPTION :{WHITE}Vytvoriť náhodný priemysel STR_FUND_INDUSTRY_MANY_RANDOM_INDUSTRIES_QUERY :{YELLOW}Ste si istý, že chcete vytvoriť veľké množstvo náhodného priemyslu? STR_FUND_INDUSTRY_INDUSTRY_BUILD_COST :{BLACK}Cena: {YELLOW}{CURRENCY_LONG} -STR_FUND_INDUSTRY_PROSPECT_NEW_INDUSTRY :{BLACK}Vyhladat +STR_FUND_INDUSTRY_PROSPECT_NEW_INDUSTRY :{BLACK}Vyhľadať STR_FUND_INDUSTRY_BUILD_NEW_INDUSTRY :{BLACK}Postaviť STR_FUND_INDUSTRY_FUND_NEW_INDUSTRY :{BLACK}Založiť STR_FUND_INDUSTRY_REMOVE_ALL_INDUSTRIES :{BLACK}Odstrániť všetok priemysel @@ -2777,7 +2824,7 @@ STR_LAI_TREE_NAME_CACTUS_PLANTS :Kaktusy STR_LAI_STATION_DESCRIPTION_RAILROAD_STATION :Železničná stanica STR_LAI_STATION_DESCRIPTION_AIRCRAFT_HANGAR :Letecký hangár STR_LAI_STATION_DESCRIPTION_AIRPORT :Letisko -STR_LAI_STATION_DESCRIPTION_TRUCK_LOADING_AREA :Vykladka +STR_LAI_STATION_DESCRIPTION_TRUCK_LOADING_AREA :Vykládka STR_LAI_STATION_DESCRIPTION_BUS_STATION :Stanica STR_LAI_STATION_DESCRIPTION_SHIP_DOCK :Prístav STR_LAI_STATION_DESCRIPTION_BUOY :Bója @@ -2965,13 +3012,13 @@ STR_MAPGEN_DESERT_COVERAGE_QUERY_CAPT :{WHITE}Pokrytie STR_MAPGEN_START_DATE_QUERY_CAPT :{WHITE}Zmeniť rok začiatku hry # SE Map generation -STR_SE_MAPGEN_CAPTION :{WHITE}Typ scenara -STR_SE_MAPGEN_FLAT_WORLD :{WHITE}Rovna krajina +STR_SE_MAPGEN_CAPTION :{WHITE}Typ scenára +STR_SE_MAPGEN_FLAT_WORLD :{WHITE}Rovná krajina STR_SE_MAPGEN_FLAT_WORLD_TOOLTIP :{BLACK}Generovať rovnú krajinu -STR_SE_MAPGEN_RANDOM_LAND :{WHITE}Nahodna krajina +STR_SE_MAPGEN_RANDOM_LAND :{WHITE}Náhodná krajina STR_SE_MAPGEN_FLAT_WORLD_HEIGHT :{BLACK}Výška rovnej krajiny: STR_SE_MAPGEN_FLAT_WORLD_HEIGHT_DOWN :{BLACK}Znizit vysku rovnej krajiny o jedno -STR_SE_MAPGEN_FLAT_WORLD_HEIGHT_UP :{BLACK}Zvysit vysku rovnej krajiny o jedno +STR_SE_MAPGEN_FLAT_WORLD_HEIGHT_UP :{BLACK}Zvýšiť výšku rovnej krajiny o jedno STR_SE_MAPGEN_FLAT_WORLD_HEIGHT_QUERY_CAPT :{WHITE}Zmeniť výšku rovnej krajiny @@ -3001,16 +3048,16 @@ STR_NEWGRF_SETTINGS_SELECT_PRESET :{ORANGE}Vybrať STR_NEWGRF_FILTER_TITLE :{ORANGE}Filtrovací reťazec: STR_NEWGRF_SETTINGS_PRESET_LIST_TOOLTIP :{BLACK}Nahrať vybraný zoznam STR_NEWGRF_SETTINGS_PRESET_SAVE :{BLACK}Uložit zoznam -STR_NEWGRF_SETTINGS_PRESET_SAVE_TOOLTIP :{BLACK}Uložit aktuálne nastavenie ako zoznam +STR_NEWGRF_SETTINGS_PRESET_SAVE_TOOLTIP :{BLACK}Uložiť aktuálne nastavenie ako zoznam STR_NEWGRF_SETTINGS_PRESET_SAVE_QUERY :{BLACK}Zadajte názov zoznamu -STR_NEWGRF_SETTINGS_PRESET_DELETE :{BLACK}Vymazat zoznam -STR_NEWGRF_SETTINGS_PRESET_DELETE_TOOLTIP :{BLACK}Vymazat vybraný zoznam -STR_NEWGRF_SETTINGS_ADD :{BLACK}Pridat -STR_NEWGRF_SETTINGS_ADD_FILE_TOOLTIP :{BLACK}Pridat vybraný NewGRF súbor do konfigurácie -STR_NEWGRF_SETTINGS_RESCAN_FILES :{BLACK}Obnovit zoznam súborov -STR_NEWGRF_SETTINGS_RESCAN_FILES_TOOLTIP :{BLACK}Obnovit zoznam dostupných NewGRF súborov -STR_NEWGRF_SETTINGS_REMOVE :{BLACK}Odstránit -STR_NEWGRF_SETTINGS_REMOVE_TOOLTIP :{BLACK}Odstránit vybraný NewGRF súbor zo zoznamu +STR_NEWGRF_SETTINGS_PRESET_DELETE :{BLACK}Vymazať zoznam +STR_NEWGRF_SETTINGS_PRESET_DELETE_TOOLTIP :{BLACK}Vymazať vybraný zoznam +STR_NEWGRF_SETTINGS_ADD :{BLACK}Pridať +STR_NEWGRF_SETTINGS_ADD_FILE_TOOLTIP :{BLACK}Pridať vybraný NewGRF súbor do konfigurácie +STR_NEWGRF_SETTINGS_RESCAN_FILES :{BLACK}Obnoviť zoznam súborov +STR_NEWGRF_SETTINGS_RESCAN_FILES_TOOLTIP :{BLACK}Obnoviť zoznam dostupných NewGRF súborov +STR_NEWGRF_SETTINGS_REMOVE :{BLACK}Odstrániť +STR_NEWGRF_SETTINGS_REMOVE_TOOLTIP :{BLACK}Odstrániť vybraný NewGRF súbor zo zoznamu STR_NEWGRF_SETTINGS_MOVEUP :{BLACK}Vyššie STR_NEWGRF_SETTINGS_MOVEUP_TOOLTIP :{BLACK}Posunút vybraný NewGRF súbor v zozname vyššie STR_NEWGRF_SETTINGS_MOVEDOWN :{BLACK}Nižšie @@ -3019,11 +3066,11 @@ STR_NEWGRF_SETTINGS_UPGRADE :{BLACK}Aktualiz STR_NEWGRF_SETTINGS_UPGRADE_TOOLTIP :{BLACK}Aktualizujte NewGRF súbory ktoré máte inštalované, na novšie verzie STR_NEWGRF_SETTINGS_FILE_TOOLTIP :{BLACK}Zoznam inštalovaných NewGRF súborov. -STR_NEWGRF_SETTINGS_SET_PARAMETERS :{BLACK}Nastavit parametre +STR_NEWGRF_SETTINGS_SET_PARAMETERS :{BLACK}Nastaviť parametre STR_NEWGRF_SETTINGS_SHOW_PARAMETERS :{BLACK}Ukázať parametre STR_NEWGRF_SETTINGS_TOGGLE_PALETTE :{BLACK}Prepnúť paletu STR_NEWGRF_SETTINGS_TOGGLE_PALETTE_TOOLTIP :{BLACK}Prepnúť paletu na zvolené NewGRF.{}Vykonajte to ak grafika z tohoto NewGRF je v hre ružová -STR_NEWGRF_SETTINGS_APPLY_CHANGES :{BLACK}Potvrdit zmeny +STR_NEWGRF_SETTINGS_APPLY_CHANGES :{BLACK}Potvrdiť zmeny STR_NEWGRF_SETTINGS_FIND_MISSING_CONTENT_BUTTON :{BLACK}Nájsť chýbajúci obsah online STR_NEWGRF_SETTINGS_FIND_MISSING_CONTENT_TOOLTIP :{BLACK}Skontrolovať či je chýbajúci obsah dostupný online @@ -3101,6 +3148,7 @@ STR_NEWGRF_ERROR_MSG_WARNING :{RED}Upozorneni STR_NEWGRF_ERROR_MSG_ERROR :{RED}Chyba: {SILVER}{STRING} STR_NEWGRF_ERROR_MSG_FATAL :{RED}Kritická chyba: {SILVER}{STRING} STR_NEWGRF_ERROR_FATAL_POPUP :{WHITE}Nastala závažná chyba NewGRF:{}{STRING} +STR_NEWGRF_ERROR_POPUP :{WHITE}Vyskytla sa chyba NewGRF:{}{STRING} STR_NEWGRF_ERROR_VERSION_NUMBER :{1:STRING} nebude fungovať s TTDPatch verziou nahlásenou OpenTTD. STR_NEWGRF_ERROR_DOS_OR_WINDOWS :{1:STRING} je pre verziu {STRING} TTD. STR_NEWGRF_ERROR_UNSET_SWITCH :{1:STRING} je navrhnutý pre použitie s {STRING} @@ -3212,7 +3260,7 @@ STR_TOWN_VIEW_LOCAL_AUTHORITY_BUTTON :{BLACK}Miestna STR_TOWN_VIEW_LOCAL_AUTHORITY_TOOLTIP :{BLACK}Zobraziť informácie o miestnej správe STR_TOWN_VIEW_RENAME_TOOLTIP :{BLACK}Zmeniť názov mesta -STR_TOWN_VIEW_EXPAND_BUTTON :{BLACK}Rozsirit +STR_TOWN_VIEW_EXPAND_BUTTON :{BLACK}Rozšíriť STR_TOWN_VIEW_EXPAND_TOOLTIP :{BLACK}Zväčšiť veľkosť mesta STR_TOWN_VIEW_DELETE_BUTTON :{BLACK}Vymazať STR_TOWN_VIEW_DELETE_TOOLTIP :{BLACK}Kompletne vymazať toto mesto @@ -3318,7 +3366,7 @@ STR_STATION_LIST_CAPTION :{WHITE}{COMPANY STR_STATION_LIST_STATION :{YELLOW}{STATION} {STATION_FEATURES} STR_STATION_LIST_WAYPOINT :{YELLOW}{WAYPOINT} STR_STATION_LIST_NONE :{YELLOW}- Žiadne - -STR_STATION_LIST_SELECT_ALL_FACILITIES :{BLACK}Oznacit vsetky moznosti +STR_STATION_LIST_SELECT_ALL_FACILITIES :{BLACK}Označiť všetky možnosti STR_STATION_LIST_SELECT_ALL_TYPES :{BLACK}Označiť všetky druhy nákladu (vrátane nečakajúceho na prepravu) STR_STATION_LIST_NO_WAITING_CARGO :{BLACK}Nečaká žiadny typ nákladu @@ -3603,7 +3651,7 @@ STR_BUY_VEHICLE_ROAD_VEHICLE_CAPTION :Nové automobil STR_BUY_VEHICLE_TRAM_VEHICLE_CAPTION :Nové električky ############ range for vehicle availability starts -STR_BUY_VEHICLE_TRAIN_ALL_CAPTION :Železnicne vozidlá +STR_BUY_VEHICLE_TRAIN_ALL_CAPTION :Železničné vozidlá STR_BUY_VEHICLE_ROAD_VEHICLE_ALL_CAPTION :Nové cestné vozidlá STR_BUY_VEHICLE_SHIP_CAPTION :Nové lode STR_BUY_VEHICLE_AIRCRAFT_CAPTION :Nové lietadlá @@ -3719,10 +3767,10 @@ STR_DEPOT_SELL_ALL_BUTTON_ROAD_VEHICLE_TOOLTIP :{BLACK}Predať STR_DEPOT_SELL_ALL_BUTTON_SHIP_TOOLTIP :{BLACK}Predať všetky lode v lodenici STR_DEPOT_SELL_ALL_BUTTON_AIRCRAFT_TOOLTIP :{BLACK}Predať všetky lietadlá v hangári -STR_DEPOT_AUTOREPLACE_TRAIN_TOOLTIP :{BLACK}Automaticky nahradit všetky vlaky v depe -STR_DEPOT_AUTOREPLACE_ROAD_VEHICLE_TOOLTIP :{BLACK}Automaticky nahradit všetky vozidlá v garáži -STR_DEPOT_AUTOREPLACE_SHIP_TOOLTIP :{BLACK}Automaticky nahradit všetky lode v lodenici -STR_DEPOT_AUTOREPLACE_AIRCRAFT_TOOLTIP :{BLACK}Automaticky nahradit všetky lietadlá v hangári +STR_DEPOT_AUTOREPLACE_TRAIN_TOOLTIP :{BLACK}Automaticky nahradiť všetky vlaky v depe +STR_DEPOT_AUTOREPLACE_ROAD_VEHICLE_TOOLTIP :{BLACK}Automaticky nahradiť všetky vozidlá v garáži +STR_DEPOT_AUTOREPLACE_SHIP_TOOLTIP :{BLACK}Automaticky nahradiť všetky lode v lodenici +STR_DEPOT_AUTOREPLACE_AIRCRAFT_TOOLTIP :{BLACK}Automaticky nahradiť všetky lietadlá v hangári STR_DEPOT_TRAIN_NEW_VEHICLES_BUTTON :{BLACK}Nové vlaky STR_DEPOT_ROAD_VEHICLE_NEW_VEHICLES_BUTTON :{BLACK}Nové vozidlá @@ -3886,7 +3934,7 @@ STR_VEHICLE_STATUS_CRASHED :{RED}Zničené! STR_VEHICLE_STATUS_BROKEN_DOWN :{RED}Pokazený STR_VEHICLE_STATUS_STOPPED :{RED}Zastavené STR_VEHICLE_STATUS_TRAIN_STOPPING_VEL :{RED}Zastavuje, {VELOCITY} -STR_VEHICLE_STATUS_TRAIN_NO_POWER :{RED}Nie je prud +STR_VEHICLE_STATUS_TRAIN_NO_POWER :{RED}Nie je prúd STR_VEHICLE_STATUS_TRAIN_STUCK :{ORANGE}Čakám na uvoľnenie trate STR_VEHICLE_STATUS_AIRCRAFT_TOO_FAR :{ORANGE}K ďalšej destinácii je to príliš ďaleko @@ -3980,7 +4028,7 @@ STR_REFIT_NEW_CAPACITY_COST_OF_AIRCRAFT_REFIT :{BLACK}Nová ka STR_REFIT_NEW_CAPACITY_INCOME_FROM_AIRCRAFT_REFIT :{BLACK}Nová kapacita: {GOLD}{CARGO_LONG}, {GOLD}{CARGO_LONG}{}{BLACK}Príjem z prestavby: {GREEN}{CURRENCY_LONG} STR_REFIT_SELECT_VEHICLES_TOOLTIP :{BLACK}Vyber vozidlo k prestavbe. Ťah myšou umožní vybrať viacej vozidiel. Kliknutie do voľného mista vybere celé vozidlo. Ctrl+Klik vyberie vozidlo a následujúci reťaz -STR_REFIT_TRAIN_LIST_TOOLTIP :{BLACK}Vybrat typ prepravovaneho nakladu +STR_REFIT_TRAIN_LIST_TOOLTIP :{BLACK}Vybrať typ prepravovaného nákladu STR_REFIT_ROAD_VEHICLE_LIST_TOOLTIP :{BLACK}Vyberte požadovaný typ nákladu pre cestné vozidlo STR_REFIT_SHIP_LIST_TOOLTIP :{BLACK}Vybrať typ nákladu pre loď STR_REFIT_AIRCRAFT_LIST_TOOLTIP :{BLACK}Vybrať nový typ nákladu @@ -4273,7 +4321,7 @@ STR_AI_CONFIG_CONFIGURE_TOOLTIP :{BLACK}Nastavi STR_AI_LIST_CAPTION :{WHITE}Dostupné {STRING} STR_AI_LIST_CAPTION_AI :AI STR_AI_LIST_CAPTION_GAMESCRIPT :Herné skripty -STR_AI_LIST_TOOLTIP :{BLACK}Klikni pre výber skriptu +STR_AI_LIST_TOOLTIP :{BLACK}Kliknite pre výber skriptu STR_AI_LIST_AUTHOR :{LTBLUE}Autor: {ORANGE}{STRING} STR_AI_LIST_VERSION :{LTBLUE}Verzia: {ORANGE}{NUM} @@ -4294,7 +4342,7 @@ STR_SCREENSHOT_MINIMAP_SCREENSHOT :{BLACK}Snímka # AI Parameters STR_AI_SETTINGS_CAPTION :{WHITE}{STRING} Parametre -STR_AI_SETTINGS_CAPTION_AI :Imelá inteligencia +STR_AI_SETTINGS_CAPTION_AI :Umelá inteligencia STR_AI_SETTINGS_CAPTION_GAMESCRIPT :Skript STR_AI_SETTINGS_CLOSE :{BLACK}Zavrieť STR_AI_SETTINGS_RESET :{BLACK}Resetovať @@ -4339,7 +4387,7 @@ STR_MESSAGE_ESTIMATED_INCOME :{WHITE}Odhadova # Saveload messages STR_ERROR_SAVE_STILL_IN_PROGRESS :{WHITE}Ukladanie hry este bezi,{}pockajte prosim na dokoncenie! -STR_ERROR_AUTOSAVE_FAILED :{WHITE}Autoulozenie zlyhalo +STR_ERROR_AUTOSAVE_FAILED :{WHITE}Automatické ukladanie zlyhalo STR_ERROR_UNABLE_TO_READ_DRIVE :{BLACK}Zariadenie je nečitateľné STR_ERROR_GAME_SAVE_FAILED :{WHITE}Uloženie hry zlyhalo{}{STRING} STR_ERROR_UNABLE_TO_DELETE_FILE :{WHITE}Súbor sa nedá vymazať @@ -4429,7 +4477,7 @@ STR_ERROR_CAN_T_CHANGE_PRESIDENT :{WHITE}Meno pre STR_ERROR_MAXIMUM_PERMITTED_LOAN :{WHITE}... úverovy limit je {CURRENCY_LONG} STR_ERROR_CAN_T_BORROW_ANY_MORE_MONEY :{WHITE}Nemôžete si požičať viac peňazí... STR_ERROR_LOAN_ALREADY_REPAYED :{WHITE}... úver už bol splatený -STR_ERROR_CURRENCY_REQUIRED :{WHITE}... {CURRENCY_LONG} potrebuješ +STR_ERROR_CURRENCY_REQUIRED :{WHITE}... Potrebuješ {CURRENCY_LONG} STR_ERROR_CAN_T_REPAY_LOAN :{WHITE}Úver sa nedá splatiť... STR_ERROR_INSUFFICIENT_FUNDS :{WHITE}Nie je možné presunúť peniaze, ktoré sú požičané z banky... STR_ERROR_CAN_T_GIVE_MONEY :{WHITE}Nie je možné presunúť peniaze tejto spoločnosti... @@ -4447,7 +4495,7 @@ STR_ERROR_CAN_T_EXPAND_TOWN :{WHITE}Nemožno STR_ERROR_TOO_CLOSE_TO_EDGE_OF_MAP_SUB :{WHITE}... príliš blízko okraja mapy STR_ERROR_TOO_CLOSE_TO_ANOTHER_TOWN :{WHITE}... príliš blízko iného mesta STR_ERROR_TOO_MANY_TOWNS :{WHITE}... príliš veľa miest -STR_ERROR_NO_SPACE_FOR_TOWN :{WHITE}... nie je dalsie miesto na mape +STR_ERROR_NO_SPACE_FOR_TOWN :{WHITE}... nie je miesto na mape STR_ERROR_TOWN_EXPAND_WARN_NO_ROADS :{WHITE}Mesto nebude stavať cesty. Môžete povoliť budovanie ciest cez Pokročilé nasvavenia->Ekonomika->Mestá. STR_ERROR_ROAD_WORKS_IN_PROGRESS :{WHITE}Prebiehajú cestné práce STR_ERROR_TOWN_CAN_T_DELETE :{WHITE}Toto mesto nie je možné odstrániť...{}Stanica alebo depo sa odvoláva na mesto, alebo parcela vo vlastníctve mesta nemôže byť odstránená @@ -4717,7 +4765,7 @@ STR_ERROR_CAN_T_MAKE_ROAD_VEHICLE_TURN :{WHITE}Nemôže STR_ERROR_AIRCRAFT_IS_IN_FLIGHT :{WHITE}Lietadlo je vo vzduchu # Order related errors -STR_ERROR_NO_MORE_SPACE_FOR_ORDERS :{WHITE}Nemozno zadat dalsie prikazy +STR_ERROR_NO_MORE_SPACE_FOR_ORDERS :{WHITE}Nemožno zadať ďalšie príkazy STR_ERROR_TOO_MANY_ORDERS :{WHITE}Príliš veľa príkazov STR_ERROR_CAN_T_INSERT_NEW_ORDER :{WHITE}Nemožno vložiť nový príkaz... STR_ERROR_CAN_T_DELETE_THIS_ORDER :{WHITE}Nemožno vymazať tento príkaz... @@ -4736,7 +4784,7 @@ STR_ERROR_TOO_FAR_FROM_PREVIOUS_DESTINATION :{WHITE}... prí STR_ERROR_AIRCRAFT_NOT_ENOUGH_RANGE :{WHITE}... lietadlo nemá dostatočný dosah # Timetable related errors -STR_ERROR_CAN_T_TIMETABLE_VEHICLE :{WHITE}Vozidlu nie je možné zadat cestovný poriadok ... +STR_ERROR_CAN_T_TIMETABLE_VEHICLE :{WHITE}Vozidlu nie je možné zadať cestovný poriadok ... STR_ERROR_TIMETABLE_ONLY_WAIT_AT_STATIONS :{WHITE}Vozidlá možu cakat len v staniciach. STR_ERROR_TIMETABLE_NOT_STOPPING_HERE :{WHITE}Toto vozidlo nezastavuje v tejto stanici. @@ -4773,21 +4821,21 @@ STR_TOWN_BUILDING_NAME_HOTEL_1 :{G=m}Hotel STR_TOWN_BUILDING_NAME_STATUE_1 :{G=z}Socha STR_TOWN_BUILDING_NAME_FOUNTAIN_1 :{G=z}Fontána STR_TOWN_BUILDING_NAME_PARK_1 :{G=m}Park -STR_TOWN_BUILDING_NAME_OFFICE_BLOCK_2 :Kancelarie +STR_TOWN_BUILDING_NAME_OFFICE_BLOCK_2 :Kancelárie STR_TOWN_BUILDING_NAME_SHOPS_AND_OFFICES_1 :Obchody a kancelárie STR_TOWN_BUILDING_NAME_MODERN_OFFICE_BUILDING_1 :Moderná administratívna budova STR_TOWN_BUILDING_NAME_WAREHOUSE_1 :{G=m}Sklad STR_TOWN_BUILDING_NAME_OFFICE_BLOCK_3 :Kancelárie -STR_TOWN_BUILDING_NAME_STADIUM_1 :{G=m}Stadión -STR_TOWN_BUILDING_NAME_OLD_HOUSES_1 :Stare domy +STR_TOWN_BUILDING_NAME_STADIUM_1 :{G=m}Štadión +STR_TOWN_BUILDING_NAME_OLD_HOUSES_1 :Staré domy STR_TOWN_BUILDING_NAME_COTTAGES_1 :Chaty STR_TOWN_BUILDING_NAME_HOUSES_1 :Domy STR_TOWN_BUILDING_NAME_FLATS_1 :Byty -STR_TOWN_BUILDING_NAME_TALL_OFFICE_BLOCK_2 :{G=z}Administrativa -STR_TOWN_BUILDING_NAME_SHOPS_AND_OFFICES_2 :Obchody a kancelarie -STR_TOWN_BUILDING_NAME_SHOPS_AND_OFFICES_3 :Obchody a kancelarie +STR_TOWN_BUILDING_NAME_TALL_OFFICE_BLOCK_2 :{G=z}Administratíva +STR_TOWN_BUILDING_NAME_SHOPS_AND_OFFICES_2 :Obchody a kancelárie +STR_TOWN_BUILDING_NAME_SHOPS_AND_OFFICES_3 :Obchody a kancelárie STR_TOWN_BUILDING_NAME_THEATER_1 :{G=s}Divadlo -STR_TOWN_BUILDING_NAME_STADIUM_2 :{G=m}Stadión +STR_TOWN_BUILDING_NAME_STADIUM_2 :{G=m}Štadión STR_TOWN_BUILDING_NAME_OFFICES_1 :Kancelárie STR_TOWN_BUILDING_NAME_HOUSES_2 :Domy STR_TOWN_BUILDING_NAME_CINEMA_1 :{G=s}Kino From c0a0d85d202843ca68dbc3cd817158a6ccfb53d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Guilloux?= Date: Tue, 11 May 2021 21:43:43 +0200 Subject: [PATCH 41/61] Fix: [Actions] Annotations not shown for MSVC (#9247) --- cmake/CompileFlags.cmake | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/cmake/CompileFlags.cmake b/cmake/CompileFlags.cmake index 86d336b595..2f7528fe3c 100644 --- a/cmake/CompileFlags.cmake +++ b/cmake/CompileFlags.cmake @@ -26,8 +26,10 @@ macro(compile_flags) add_compile_options(/Zc:rvalueCast) if(NOT CMAKE_CXX_COMPILER_ID STREQUAL "Clang") - # Enable multi-threaded compilation. - add_compile_options(/MP) + add_compile_options( + /MP # Enable multi-threaded compilation. + /FC # Display the full path of source code files passed to the compiler in diagnostics. + ) endif() endif() From 3d9436bd75d2d6ff2024ac4ccacf8d6e1e730cc3 Mon Sep 17 00:00:00 2001 From: PeterN Date: Wed, 12 May 2021 08:11:14 +0100 Subject: [PATCH 42/61] Fix #9202: Invalid test for unset NewGRF override mapping. (#9226) --- src/newgrf_commons.h | 1 - src/saveload/newgrf_sl.cpp | 3 ++- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/newgrf_commons.h b/src/newgrf_commons.h index b86e3d10af..ed18aaa9e2 100644 --- a/src/newgrf_commons.h +++ b/src/newgrf_commons.h @@ -218,7 +218,6 @@ public: inline uint16 GetMaxMapping() const { return max_new_entities; } inline uint16 GetMaxOffset() const { return max_offset; } - inline bool IsValidID(uint16 entity_id) const { return entity_overrides[entity_id] != invalid_ID; } }; diff --git a/src/saveload/newgrf_sl.cpp b/src/saveload/newgrf_sl.cpp index f7f6ab72e0..2e9f1ccaf6 100644 --- a/src/saveload/newgrf_sl.cpp +++ b/src/saveload/newgrf_sl.cpp @@ -30,7 +30,8 @@ static const SaveLoad _newgrf_mapping_desc[] = { void Save_NewGRFMapping(const OverrideManagerBase &mapping) { for (uint i = 0; i < mapping.GetMaxMapping(); i++) { - if (!mapping.IsValidID(i)) continue; + if (mapping.mapping_ID[i].grfid == 0 && + mapping.mapping_ID[i].entity_id == 0) continue; SlSetArrayIndex(i); SlObject(&mapping.mapping_ID[i], _newgrf_mapping_desc); } From 56050fc96f59cef171d3b8a03ae3f3c9b83f9426 Mon Sep 17 00:00:00 2001 From: Patric Stout Date: Wed, 12 May 2021 16:31:31 +0200 Subject: [PATCH 43/61] Fix 91b8ce07: dedicated servers could no longer create screenshots (#9232) Although most commands are not useful on a dedicated server, screenshot commands should be dequeued. --- src/video/dedicated_v.cpp | 1 + src/video/video_driver.hpp | 34 +++++++++++++++++----------------- 2 files changed, 18 insertions(+), 17 deletions(-) diff --git a/src/video/dedicated_v.cpp b/src/video/dedicated_v.cpp index e905a9d2c9..a412782344 100644 --- a/src/video/dedicated_v.cpp +++ b/src/video/dedicated_v.cpp @@ -270,6 +270,7 @@ void VideoDriver_Dedicated::MainLoop() while (!_exit_game) { if (!_dedicated_forks) DedicatedHandleKeyInput(); + this->DrainCommandQueue(); ChangeGameSpeed(_ddc_fastforward); this->Tick(); diff --git a/src/video/video_driver.hpp b/src/video/video_driver.hpp index db522a761d..b87dc70529 100644 --- a/src/video/video_driver.hpp +++ b/src/video/video_driver.hpp @@ -319,23 +319,6 @@ protected: return std::chrono::microseconds(1000000 / _settings_client.gui.refresh_rate); } - std::chrono::steady_clock::time_point next_game_tick; - std::chrono::steady_clock::time_point next_draw_tick; - - bool fast_forward_key_pressed; ///< The fast-forward key is being pressed. - bool fast_forward_via_key; ///< The fast-forward was enabled by key press. - - bool is_game_threaded; - std::thread game_thread; - std::mutex game_state_mutex; - std::mutex game_thread_wait_mutex; - - static void GameThreadThunk(VideoDriver *drv); - -private: - std::mutex cmd_queue_mutex; - std::vector> cmd_queue; - /** Execute all queued commands. */ void DrainCommandQueue() { @@ -354,6 +337,23 @@ private: } } + std::chrono::steady_clock::time_point next_game_tick; + std::chrono::steady_clock::time_point next_draw_tick; + + bool fast_forward_key_pressed; ///< The fast-forward key is being pressed. + bool fast_forward_via_key; ///< The fast-forward was enabled by key press. + + bool is_game_threaded; + std::thread game_thread; + std::mutex game_state_mutex; + std::mutex game_thread_wait_mutex; + + static void GameThreadThunk(VideoDriver *drv); + +private: + std::mutex cmd_queue_mutex; + std::vector> cmd_queue; + void GameLoop(); void GameThread(); }; From b136e65cf9375700e77579b344f8dd86a5a61336 Mon Sep 17 00:00:00 2001 From: Patric Stout Date: Wed, 12 May 2021 16:34:02 +0200 Subject: [PATCH 44/61] Change: reworked the debug levels for network facility (#9251) It now follows very simple rules: 0 - Fatal, user should know about this 1 - Error, but we are recovering 2 - Warning, wrong but okay if you don't know 3 - Info, information you might care about 4 - 5 - Debug #1 - High level debug messages 6 - Debug #2 - Low level debug messages 7 - Trace information --- src/genworld.cpp | 6 ++--- src/genworld_gui.cpp | 2 +- src/network/core/address.cpp | 2 +- src/network/core/core.cpp | 4 +-- src/network/core/game_info.cpp | 4 +-- src/network/core/host.cpp | 6 ++--- src/network/core/tcp.cpp | 6 ++--- src/network/core/tcp_admin.cpp | 6 ++--- src/network/core/tcp_connect.cpp | 14 +++++----- src/network/core/tcp_content.cpp | 6 ++--- src/network/core/tcp_game.cpp | 6 ++--- src/network/core/tcp_http.cpp | 28 +++++++++---------- src/network/core/tcp_listen.h | 12 ++++----- src/network/core/udp.cpp | 14 +++++----- src/network/network.cpp | 34 +++++++++++------------ src/network/network_admin.cpp | 20 +++++++------- src/network/network_client.cpp | 16 +++++------ src/network/network_command.cpp | 2 +- src/network/network_gamelist.cpp | 1 - src/network/network_server.cpp | 18 ++++++------- src/network/network_udp.cpp | 46 ++++++++++++++++---------------- src/openttd.cpp | 6 ++--- src/saveload/afterload.cpp | 4 +-- src/video/dedicated_v.cpp | 2 +- 24 files changed, 132 insertions(+), 133 deletions(-) diff --git a/src/genworld.cpp b/src/genworld.cpp index 28b28ac056..dc722ac000 100644 --- a/src/genworld.cpp +++ b/src/genworld.cpp @@ -91,7 +91,7 @@ static void _GenerateWorld() try { _generating_world = true; - if (_network_dedicated) DEBUG(net, 1, "Generating map, please wait..."); + if (_network_dedicated) DEBUG(net, 3, "Generating map, please wait..."); /* Set the Random() seed to generation_seed so we produce the same map with the same seed */ if (_settings_game.game_creation.generation_seed == GENERATE_NEW_SEED) _settings_game.game_creation.generation_seed = _settings_newgame.game_creation.generation_seed = InteractiveRandom(); _random.SetSeed(_settings_game.game_creation.generation_seed); @@ -188,7 +188,7 @@ static void _GenerateWorld() ShowNewGRFError(); - if (_network_dedicated) DEBUG(net, 1, "Map generated, starting game"); + if (_network_dedicated) DEBUG(net, 3, "Map generated, starting game"); DEBUG(desync, 1, "new_map: %08x", _settings_game.game_creation.generation_seed); if (_debug_desync_level > 0) { @@ -204,7 +204,7 @@ static void _GenerateWorld() if (_network_dedicated) { /* Exit the game to prevent a return to main menu. */ - DEBUG(net, 0, "Generating map failed, aborting"); + DEBUG(net, 0, "Generating map failed; closing server"); _exit_game = true; } else { SwitchToMode(_switch_mode); diff --git a/src/genworld_gui.cpp b/src/genworld_gui.cpp index 46f2eac246..69ae9576ae 100644 --- a/src/genworld_gui.cpp +++ b/src/genworld_gui.cpp @@ -1458,7 +1458,7 @@ static void _SetGeneratingWorldProgress(GenWorldProgress cls, uint progress, uin /* Never show steps smaller than 2%, even if it is a mod 5% */ if (_gws.percent <= last_percent + 2) return; - DEBUG(net, 1, "Map generation percentage complete: %d", _gws.percent); + DEBUG(net, 3, "Map generation percentage complete: %d", _gws.percent); last_percent = _gws.percent; return; diff --git a/src/network/core/address.cpp b/src/network/core/address.cpp index aa27b41032..91a481795c 100644 --- a/src/network/core/address.cpp +++ b/src/network/core/address.cpp @@ -356,7 +356,7 @@ static SOCKET ListenLoopProc(addrinfo *runp) DEBUG(net, 0, "Setting non-blocking mode failed: %s", NetworkError::GetLast().AsString()); } - DEBUG(net, 1, "Listening on %s", address.c_str()); + DEBUG(net, 3, "Listening on %s", address.c_str()); return sock; } diff --git a/src/network/core/core.cpp b/src/network/core/core.cpp index 563deae963..af35720dbc 100644 --- a/src/network/core/core.cpp +++ b/src/network/core/core.cpp @@ -27,9 +27,9 @@ bool NetworkCoreInitialize() #ifdef _WIN32 { WSADATA wsa; - DEBUG(net, 3, "[core] loading windows socket library"); + DEBUG(net, 5, "Loading windows socket library"); if (WSAStartup(MAKEWORD(2, 0), &wsa) != 0) { - DEBUG(net, 0, "[core] WSAStartup failed, network unavailable"); + DEBUG(net, 0, "WSAStartup failed, network unavailable"); return false; } } diff --git a/src/network/core/game_info.cpp b/src/network/core/game_info.cpp index f7044a3fce..46bb42ec1b 100644 --- a/src/network/core/game_info.cpp +++ b/src/network/core/game_info.cpp @@ -51,7 +51,7 @@ const char *GetNetworkRevisionString() /* Tag names are not mangled further. */ if (_openttd_revision_tagged) { - DEBUG(net, 1, "Network revision name is '%s'", network_revision); + DEBUG(net, 3, "Network revision name: %s", network_revision); return network_revision; } @@ -71,7 +71,7 @@ const char *GetNetworkRevisionString() /* Replace the git hash in revision string. */ strecpy(network_revision + hashofs, githash_suffix, network_revision + NETWORK_REVISION_LENGTH); assert(strlen(network_revision) < NETWORK_REVISION_LENGTH); // strlen does not include terminator, constant does, hence strictly less than - DEBUG(net, 1, "Network revision name is '%s'", network_revision); + DEBUG(net, 3, "Network revision name: %s", network_revision); } return network_revision; diff --git a/src/network/core/host.cpp b/src/network/core/host.cpp index bcb048e474..54a97aff01 100644 --- a/src/network/core/host.cpp +++ b/src/network/core/host.cpp @@ -39,14 +39,14 @@ static void NetworkFindBroadcastIPsInternal(NetworkAddressList *broadcast) // BE int sock = socket(AF_INET, SOCK_DGRAM, 0); if (sock < 0) { - DEBUG(net, 0, "[core] error creating socket"); + DEBUG(net, 0, "Could not create socket: %s", NetworkError::GetLast().AsString()); return; } char *output_pointer = nullptr; int output_length = _netstat(sock, &output_pointer, 1); if (output_length < 0) { - DEBUG(net, 0, "[core] error running _netstat"); + DEBUG(net, 0, "Error running _netstat()"); return; } @@ -200,6 +200,6 @@ void NetworkFindBroadcastIPs(NetworkAddressList *broadcast) int i = 0; for (NetworkAddress &addr : *broadcast) { addr.SetPort(NETWORK_DEFAULT_PORT); - DEBUG(net, 3, "%d) %s", i++, addr.GetHostname()); + DEBUG(net, 3, " %d) %s", i++, addr.GetHostname()); } } diff --git a/src/network/core/tcp.cpp b/src/network/core/tcp.cpp index 842e1a89b9..3bba291c73 100644 --- a/src/network/core/tcp.cpp +++ b/src/network/core/tcp.cpp @@ -90,7 +90,7 @@ SendPacketsState NetworkTCPSocketHandler::SendPackets(bool closing_down) if (!err.WouldBlock()) { /* Something went wrong.. close client! */ if (!closing_down) { - DEBUG(net, 0, "send failed with error %s", err.AsString()); + DEBUG(net, 0, "Send failed: %s", err.AsString()); this->CloseConnection(); } return SPS_CLOSED; @@ -139,7 +139,7 @@ Packet *NetworkTCPSocketHandler::ReceivePacket() NetworkError err = NetworkError::GetLast(); if (!err.WouldBlock()) { /* Something went wrong... */ - if (!err.IsConnectionReset()) DEBUG(net, 0, "recv failed with error %s", err.AsString()); + if (!err.IsConnectionReset()) DEBUG(net, 0, "Recv failed: %s", err.AsString()); this->CloseConnection(); return nullptr; } @@ -167,7 +167,7 @@ Packet *NetworkTCPSocketHandler::ReceivePacket() NetworkError err = NetworkError::GetLast(); if (!err.WouldBlock()) { /* Something went wrong... */ - if (!err.IsConnectionReset()) DEBUG(net, 0, "recv failed with error %s", err.AsString()); + if (!err.IsConnectionReset()) DEBUG(net, 0, "Recv failed: %s", err.AsString()); this->CloseConnection(); return nullptr; } diff --git a/src/network/core/tcp_admin.cpp b/src/network/core/tcp_admin.cpp index 36daae4a14..8cc8b1efe6 100644 --- a/src/network/core/tcp_admin.cpp +++ b/src/network/core/tcp_admin.cpp @@ -93,9 +93,9 @@ NetworkRecvStatus NetworkAdminSocketHandler::HandlePacket(Packet *p) default: if (this->HasClientQuit()) { - DEBUG(net, 0, "[tcp/admin] received invalid packet type %d from '%s' (%s)", type, this->admin_name, this->admin_version); + DEBUG(net, 0, "[tcp/admin] Received invalid packet type %d from '%s' (%s)", type, this->admin_name, this->admin_version); } else { - DEBUG(net, 0, "[tcp/admin] received illegal packet from '%s' (%s)", this->admin_name, this->admin_version); + DEBUG(net, 0, "[tcp/admin] Received illegal packet from '%s' (%s)", this->admin_name, this->admin_version); } this->CloseConnection(); @@ -129,7 +129,7 @@ NetworkRecvStatus NetworkAdminSocketHandler::ReceivePackets() */ NetworkRecvStatus NetworkAdminSocketHandler::ReceiveInvalidPacket(PacketAdminType type) { - DEBUG(net, 0, "[tcp/admin] received illegal packet type %d from admin %s (%s)", type, this->admin_name, this->admin_version); + DEBUG(net, 0, "[tcp/admin] Received illegal packet type %d from admin %s (%s)", type, this->admin_name, this->admin_version); return NETWORK_RECV_STATUS_MALFORMED_PACKET; } diff --git a/src/network/core/tcp_connect.cpp b/src/network/core/tcp_connect.cpp index b343ae463a..381f7e5892 100644 --- a/src/network/core/tcp_connect.cpp +++ b/src/network/core/tcp_connect.cpp @@ -72,7 +72,7 @@ void TCPConnecter::Connect(addrinfo *address) } NetworkAddress network_address = NetworkAddress(address->ai_addr, (int)address->ai_addrlen); - DEBUG(net, 4, "Attempting to connect to %s", network_address.GetAddressAsString().c_str()); + DEBUG(net, 5, "Attempting to connect to %s", network_address.GetAddressAsString().c_str()); int err = connect(sock, address->ai_addr, (int)address->ai_addrlen); if (err != 0 && !NetworkError::GetLast().IsConnectInProgress()) { @@ -149,10 +149,10 @@ void TCPConnecter::OnResolved(addrinfo *ai) } } - if (_debug_net_level >= 5) { - DEBUG(net, 5, "%s resolved in:", this->connection_string.c_str()); + if (_debug_net_level >= 6) { + DEBUG(net, 6, "%s resolved in:", this->connection_string.c_str()); for (const auto &address : this->addresses) { - DEBUG(net, 5, "- %s", NetworkAddress(address->ai_addr, (int)address->ai_addrlen).GetAddressAsString().c_str()); + DEBUG(net, 6, "- %s", NetworkAddress(address->ai_addr, (int)address->ai_addrlen).GetAddressAsString().c_str()); } } @@ -188,7 +188,7 @@ void TCPConnecter::Resolve() } if (e != 0) { - DEBUG(misc, 0, "Failed to resolve DNS for %s", this->connection_string.c_str()); + DEBUG(net, 0, "Failed to resolve DNS for %s", this->connection_string.c_str()); this->OnFailure(); return; } @@ -235,7 +235,7 @@ bool TCPConnecter::CheckActivity() /* select() failed; hopefully next try it doesn't. */ if (n < 0) { /* select() normally never fails; so hopefully it works next try! */ - DEBUG(net, 1, "select() failed with %s", NetworkError::GetLast().AsString()); + DEBUG(net, 1, "select() failed: %s", NetworkError::GetLast().AsString()); return false; } @@ -301,7 +301,7 @@ bool TCPConnecter::CheckActivity() } assert(connected_socket != INVALID_SOCKET); - DEBUG(net, 1, "Connected to %s", this->connection_string.c_str()); + DEBUG(net, 3, "Connected to %s", this->connection_string.c_str()); if (_debug_net_level >= 5) { DEBUG(net, 5, "- using %s", NetworkAddress::GetPeerName(connected_socket).c_str()); } diff --git a/src/network/core/tcp_content.cpp b/src/network/core/tcp_content.cpp index 488be50003..0371b76215 100644 --- a/src/network/core/tcp_content.cpp +++ b/src/network/core/tcp_content.cpp @@ -167,9 +167,9 @@ bool NetworkContentSocketHandler::HandlePacket(Packet *p) default: if (this->HasClientQuit()) { - DEBUG(net, 0, "[tcp/content] received invalid packet type %d", type); + DEBUG(net, 0, "[tcp/content] Received invalid packet type %d", type); } else { - DEBUG(net, 0, "[tcp/content] received illegal packet"); + DEBUG(net, 0, "[tcp/content] Received illegal packet"); } return false; } @@ -220,7 +220,7 @@ bool NetworkContentSocketHandler::ReceivePackets() */ bool NetworkContentSocketHandler::ReceiveInvalidPacket(PacketContentType type) { - DEBUG(net, 0, "[tcp/content] received illegal packet type %d", type); + DEBUG(net, 0, "[tcp/content] Received illegal packet type %d", type); return false; } diff --git a/src/network/core/tcp_game.cpp b/src/network/core/tcp_game.cpp index 771ea37b15..0e38133fbf 100644 --- a/src/network/core/tcp_game.cpp +++ b/src/network/core/tcp_game.cpp @@ -117,9 +117,9 @@ NetworkRecvStatus NetworkGameSocketHandler::HandlePacket(Packet *p) this->CloseConnection(); if (this->HasClientQuit()) { - DEBUG(net, 0, "[tcp/game] received invalid packet type %d from client %d", type, this->client_id); + DEBUG(net, 0, "[tcp/game] Received invalid packet type %d from client %d", type, this->client_id); } else { - DEBUG(net, 0, "[tcp/game] received illegal packet from client %d", this->client_id); + DEBUG(net, 0, "[tcp/game] Received illegal packet from client %d", this->client_id); } return NETWORK_RECV_STATUS_MALFORMED_PACKET; } @@ -151,7 +151,7 @@ NetworkRecvStatus NetworkGameSocketHandler::ReceivePackets() */ NetworkRecvStatus NetworkGameSocketHandler::ReceiveInvalidPacket(PacketGameType type) { - DEBUG(net, 0, "[tcp/game] received illegal packet type %d from client %d", type, this->client_id); + DEBUG(net, 0, "[tcp/game] Received illegal packet type %d from client %d", type, this->client_id); return NETWORK_RECV_STATUS_MALFORMED_PACKET; } diff --git a/src/network/core/tcp_http.cpp b/src/network/core/tcp_http.cpp index ccad120aec..08961e4029 100644 --- a/src/network/core/tcp_http.cpp +++ b/src/network/core/tcp_http.cpp @@ -45,7 +45,7 @@ NetworkHTTPSocketHandler::NetworkHTTPSocketHandler(SOCKET s, size_t bufferSize = strlen(url) + strlen(host) + strlen(GetNetworkRevisionString()) + (data == nullptr ? 0 : strlen(data)) + 128; char *buffer = AllocaM(char, bufferSize); - DEBUG(net, 7, "[tcp/http] requesting %s%s", host, url); + DEBUG(net, 5, "[tcp/http] Requesting %s%s", host, url); if (data != nullptr) { seprintf(buffer, buffer + bufferSize - 1, "POST %s HTTP/1.0\r\nHost: %s\r\nUser-Agent: OpenTTD/%s\r\nContent-Type: text/plain\r\nContent-Length: %d\r\n\r\n%s\r\n", url, host, GetNetworkRevisionString(), (int)strlen(data), data); } else { @@ -85,7 +85,7 @@ NetworkRecvStatus NetworkHTTPSocketHandler::CloseConnection(bool error) * Helper to simplify the error handling. * @param msg the error message to show. */ -#define return_error(msg) { DEBUG(net, 0, msg); return -1; } +#define return_error(msg) { DEBUG(net, 1, msg); return -1; } static const char * const NEWLINE = "\r\n"; ///< End of line marker static const char * const END_OF_HEADER = "\r\n\r\n"; ///< End of header marker @@ -112,7 +112,7 @@ int NetworkHTTPSocketHandler::HandleHeader() /* We expect a HTTP/1.[01] reply */ if (strncmp(this->recv_buffer, HTTP_1_0, strlen(HTTP_1_0)) != 0 && strncmp(this->recv_buffer, HTTP_1_1, strlen(HTTP_1_1)) != 0) { - return_error("[tcp/http] received invalid HTTP reply"); + return_error("[tcp/http] Received invalid HTTP reply"); } char *status = this->recv_buffer + strlen(HTTP_1_0); @@ -121,7 +121,7 @@ int NetworkHTTPSocketHandler::HandleHeader() /* Get the length of the document to receive */ char *length = strcasestr(this->recv_buffer, CONTENT_LENGTH); - if (length == nullptr) return_error("[tcp/http] missing 'content-length' header"); + if (length == nullptr) return_error("[tcp/http] Missing 'content-length' header"); /* Skip the header */ length += strlen(CONTENT_LENGTH); @@ -139,9 +139,9 @@ int NetworkHTTPSocketHandler::HandleHeader() /* Make sure we're going to download at least something; * zero sized files are, for OpenTTD's purposes, always * wrong. You can't have gzips of 0 bytes! */ - if (len == 0) return_error("[tcp/http] refusing to download 0 bytes"); + if (len == 0) return_error("[tcp/http] Refusing to download 0 bytes"); - DEBUG(net, 7, "[tcp/http] downloading %i bytes", len); + DEBUG(net, 7, "[tcp/http] Downloading %i bytes", len); return len; } @@ -154,15 +154,15 @@ int NetworkHTTPSocketHandler::HandleHeader() /* Search the end of the line. This is safe because the header will * always end with two newlines. */ *strstr(status, NEWLINE) = '\0'; - DEBUG(net, 0, "[tcp/http] unhandled status reply %s", status); + DEBUG(net, 1, "[tcp/http] Unhandled status reply %s", status); return -1; } - if (this->redirect_depth == 5) return_error("[tcp/http] too many redirects, looping redirects?"); + if (this->redirect_depth == 5) return_error("[tcp/http] Too many redirects, looping redirects?"); /* Redirect to other URL */ char *uri = strcasestr(this->recv_buffer, LOCATION); - if (uri == nullptr) return_error("[tcp/http] missing 'location' header for redirect"); + if (uri == nullptr) return_error("[tcp/http] Missing 'location' header for redirect"); uri += strlen(LOCATION); @@ -171,7 +171,7 @@ int NetworkHTTPSocketHandler::HandleHeader() char *end_of_line = strstr(uri, NEWLINE); *end_of_line = '\0'; - DEBUG(net, 6, "[tcp/http] redirecting to %s", uri); + DEBUG(net, 7, "[tcp/http] Redirecting to %s", uri); int ret = NetworkHTTPSocketHandler::Connect(uri, this->callback, this->data, this->redirect_depth + 1); if (ret != 0) return ret; @@ -194,12 +194,12 @@ int NetworkHTTPSocketHandler::HandleHeader() /* static */ int NetworkHTTPSocketHandler::Connect(char *uri, HTTPCallback *callback, const char *data, int depth) { char *hname = strstr(uri, "://"); - if (hname == nullptr) return_error("[tcp/http] invalid location"); + if (hname == nullptr) return_error("[tcp/http] Invalid location"); hname += 3; char *url = strchr(hname, '/'); - if (url == nullptr) return_error("[tcp/http] invalid location"); + if (url == nullptr) return_error("[tcp/http] Invalid location"); *url = '\0'; @@ -228,7 +228,7 @@ int NetworkHTTPSocketHandler::Receive() NetworkError err = NetworkError::GetLast(); if (!err.WouldBlock()) { /* Something went wrong... */ - if (!err.IsConnectionReset()) DEBUG(net, 0, "recv failed with error %s", err.AsString()); + if (!err.IsConnectionReset()) DEBUG(net, 0, "Recv failed: %s", err.AsString()); return -1; } /* Connection would block, so stop for now */ @@ -256,7 +256,7 @@ int NetworkHTTPSocketHandler::Receive() if (end_of_header == nullptr) { if (read == lengthof(this->recv_buffer)) { - DEBUG(net, 0, "[tcp/http] header too big"); + DEBUG(net, 1, "[tcp/http] Header too big"); return -1; } this->recv_pos = read; diff --git a/src/network/core/tcp_listen.h b/src/network/core/tcp_listen.h index e23ecae707..40fc84efee 100644 --- a/src/network/core/tcp_listen.h +++ b/src/network/core/tcp_listen.h @@ -49,7 +49,7 @@ public: SetNonBlocking(s); // XXX error handling? NetworkAddress address(sin, sin_len); - DEBUG(net, 1, "[%s] Client connected from %s on frame %d", Tsocket::GetName(), address.GetHostname(), _frame_counter); + DEBUG(net, 3, "[%s] Client connected from %s on frame %d", Tsocket::GetName(), address.GetHostname(), _frame_counter); SetNoDelay(s); // XXX error handling? @@ -61,10 +61,10 @@ public: Packet p(Tban_packet); p.PrepareToSend(); - DEBUG(net, 1, "[%s] Banned ip tried to join (%s), refused", Tsocket::GetName(), entry.c_str()); + DEBUG(net, 2, "[%s] Banned ip tried to join (%s), refused", Tsocket::GetName(), entry.c_str()); if (p.TransferOut(send, s, 0) < 0) { - DEBUG(net, 0, "send failed with error %s", NetworkError::GetLast().AsString()); + DEBUG(net, 0, "[%s] send failed: %s", Tsocket::GetName(), NetworkError::GetLast().AsString()); } closesocket(s); break; @@ -81,7 +81,7 @@ public: p.PrepareToSend(); if (p.TransferOut(send, s, 0) < 0) { - DEBUG(net, 0, "send failed with error %s", NetworkError::GetLast().AsString()); + DEBUG(net, 0, "[%s] send failed: %s", Tsocket::GetName(), NetworkError::GetLast().AsString()); } closesocket(s); @@ -150,7 +150,7 @@ public: } if (sockets.size() == 0) { - DEBUG(net, 0, "[server] could not start network: could not create listening socket"); + DEBUG(net, 0, "Could not start network: could not create listening socket"); ShowNetworkError(STR_NETWORK_ERROR_SERVER_START); return false; } @@ -165,7 +165,7 @@ public: closesocket(s.second); } sockets.clear(); - DEBUG(net, 1, "[%s] closed listeners", Tsocket::GetName()); + DEBUG(net, 5, "[%s] Closed listeners", Tsocket::GetName()); } }; diff --git a/src/network/core/udp.cpp b/src/network/core/udp.cpp index c4d448f268..c8d7533640 100644 --- a/src/network/core/udp.cpp +++ b/src/network/core/udp.cpp @@ -95,16 +95,16 @@ void NetworkUDPSocketHandler::SendPacket(Packet *p, NetworkAddress *recv, bool a /* Enable broadcast */ unsigned long val = 1; if (setsockopt(s.second, SOL_SOCKET, SO_BROADCAST, (char *) &val, sizeof(val)) < 0) { - DEBUG(net, 1, "[udp] setting broadcast failed with: %s", NetworkError::GetLast().AsString()); + DEBUG(net, 1, "Setting broadcast mode failed: %s", NetworkError::GetLast().AsString()); } } /* Send the buffer */ ssize_t res = p->TransferOut(sendto, s.second, 0, (const struct sockaddr *)send.GetAddress(), send.GetAddressLength()); - DEBUG(net, 7, "[udp] sendto(%s)", send.GetAddressAsString().c_str()); + DEBUG(net, 7, "sendto(%s)", send.GetAddressAsString().c_str()); /* Check for any errors, but ignore it otherwise */ - if (res == -1) DEBUG(net, 1, "[udp] sendto(%s) failed with: %s", send.GetAddressAsString().c_str(), NetworkError::GetLast().AsString()); + if (res == -1) DEBUG(net, 1, "sendto(%s) failed: %s", send.GetAddressAsString().c_str(), NetworkError::GetLast().AsString()); if (!all) break; } @@ -140,7 +140,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 (!p.ParsePacketSize() || (size_t)nbytes != p.Size()) { - DEBUG(net, 1, "received a packet with mismatching size from %s", address.GetAddressAsString().c_str()); + DEBUG(net, 1, "Received a packet with mismatching size from %s", address.GetAddressAsString().c_str()); continue; } p.PrepareToRead(); @@ -181,9 +181,9 @@ void NetworkUDPSocketHandler::HandleUDPPacket(Packet *p, NetworkAddress *client_ default: if (this->HasClientQuit()) { - DEBUG(net, 0, "[udp] received invalid packet type %d from %s", type, client_addr->GetAddressAsString().c_str()); + DEBUG(net, 0, "[udp] Received invalid packet type %d from %s", type, client_addr->GetAddressAsString().c_str()); } else { - DEBUG(net, 0, "[udp] received illegal packet from %s", client_addr->GetAddressAsString().c_str()); + DEBUG(net, 0, "[udp] Received illegal packet from %s", client_addr->GetAddressAsString().c_str()); } break; } @@ -196,7 +196,7 @@ void NetworkUDPSocketHandler::HandleUDPPacket(Packet *p, NetworkAddress *client_ */ void NetworkUDPSocketHandler::ReceiveInvalidPacket(PacketUDPType type, NetworkAddress *client_addr) { - DEBUG(net, 0, "[udp] received packet type %d on wrong port from %s", type, client_addr->GetAddressAsString().c_str()); + DEBUG(net, 0, "[udp] Received packet type %d on wrong port from %s", type, client_addr->GetAddressAsString().c_str()); } void NetworkUDPSocketHandler::Receive_CLIENT_FIND_SERVER(Packet *p, NetworkAddress *client_addr) { this->ReceiveInvalidPacket(PACKET_UDP_CLIENT_FIND_SERVER, client_addr); } diff --git a/src/network/network.cpp b/src/network/network.cpp index 3b43ddbcd8..a87aab88c7 100644 --- a/src/network/network.cpp +++ b/src/network/network.cpp @@ -859,13 +859,13 @@ static void CheckClientAndServerName() { static const char *fallback_client_name = "Unnamed Client"; if (StrEmpty(_settings_client.network.client_name) || strcmp(_settings_client.network.client_name, fallback_client_name) == 0) { - DEBUG(net, 0, "No \"client_name\" has been set, using \"%s\" instead. Please set this now using the \"name \" command.", fallback_client_name); + DEBUG(net, 1, "No \"client_name\" has been set, using \"%s\" instead. Please set this now using the \"name \" command", fallback_client_name); strecpy(_settings_client.network.client_name, fallback_client_name, lastof(_settings_client.network.client_name)); } static const char *fallback_server_name = "Unnamed Server"; if (StrEmpty(_settings_client.network.server_name) || strcmp(_settings_client.network.server_name, fallback_server_name) == 0) { - DEBUG(net, 0, "No \"server_name\" has been set, using \"%s\" instead. Please set this now using the \"server_name \" command.", fallback_server_name); + DEBUG(net, 1, "No \"server_name\" has been set, using \"%s\" instead. Please set this now using the \"server_name \" command", fallback_server_name); strecpy(_settings_client.network.server_name, fallback_server_name, lastof(_settings_client.network.server_name)); } } @@ -883,17 +883,17 @@ bool NetworkServerStart() NetworkDisconnect(false, false); NetworkInitialize(false); - DEBUG(net, 1, "starting listeners for clients"); + DEBUG(net, 5, "Starting listeners for clients"); if (!ServerNetworkGameSocketHandler::Listen(_settings_client.network.server_port)) return false; /* Only listen for admins when the password isn't empty. */ if (!StrEmpty(_settings_client.network.admin_password)) { - DEBUG(net, 1, "starting listeners for admins"); + DEBUG(net, 5, "Starting listeners for admins"); if (!ServerNetworkAdminSocketHandler::Listen(_settings_client.network.server_admin_port)) return false; } /* Try to start UDP-server */ - DEBUG(net, 1, "starting listeners for incoming server queries"); + DEBUG(net, 5, "Starting listeners for incoming server queries"); NetworkUDPServerListen(); _network_company_states = CallocT(MAX_COMPANIES); @@ -1044,7 +1044,7 @@ void NetworkGameLoop() static bool check_sync_state = false; static uint32 sync_state[2]; if (f == nullptr && next_date == 0) { - DEBUG(net, 0, "Cannot open commands.log"); + DEBUG(desync, 0, "Cannot open commands.log"); next_date = 1; } @@ -1052,15 +1052,15 @@ void NetworkGameLoop() if (_date == next_date && _date_fract == next_date_fract) { if (cp != nullptr) { NetworkSendCommand(cp->tile, cp->p1, cp->p2, cp->cmd & ~CMD_FLAGS_MASK, nullptr, cp->text, cp->company); - DEBUG(net, 0, "injecting: %08x; %02x; %02x; %06x; %08x; %08x; %08x; \"%s\" (%s)", _date, _date_fract, (int)_current_company, cp->tile, cp->p1, cp->p2, cp->cmd, cp->text, GetCommandName(cp->cmd)); + DEBUG(desync, 0, "Injecting: %08x; %02x; %02x; %06x; %08x; %08x; %08x; \"%s\" (%s)", _date, _date_fract, (int)_current_company, cp->tile, cp->p1, cp->p2, cp->cmd, cp->text, GetCommandName(cp->cmd)); free(cp); cp = nullptr; } if (check_sync_state) { if (sync_state[0] == _random.state[0] && sync_state[1] == _random.state[1]) { - DEBUG(net, 0, "sync check: %08x; %02x; match", _date, _date_fract); + DEBUG(desync, 0, "Sync check: %08x; %02x; match", _date, _date_fract); } else { - DEBUG(net, 0, "sync check: %08x; %02x; mismatch expected {%08x, %08x}, got {%08x, %08x}", + DEBUG(desync, 0, "Sync check: %08x; %02x; mismatch expected {%08x, %08x}, got {%08x, %08x}", _date, _date_fract, sync_state[0], sync_state[1], _random.state[0], _random.state[1]); NOT_REACHED(); } @@ -1101,7 +1101,7 @@ void NetworkGameLoop() /* Manually insert a pause when joining; this way the client can join at the exact right time. */ int ret = sscanf(p + 6, "%x; %x", &next_date, &next_date_fract); assert(ret == 2); - DEBUG(net, 0, "injecting pause for join at %08x:%02x; please join when paused", next_date, next_date_fract); + DEBUG(desync, 0, "Injecting pause for join at %08x:%02x; please join when paused", next_date, next_date_fract); cp = CallocT(1); cp->company = COMPANY_SPECTATOR; cp->cmd = CMD_PAUSE; @@ -1117,16 +1117,16 @@ void NetworkGameLoop() /* A message that is not very important to the log playback, but part of the log. */ #ifndef DEBUG_FAILED_DUMP_COMMANDS } else if (strncmp(p, "cmdf: ", 6) == 0) { - DEBUG(net, 0, "Skipping replay of failed command: %s", p + 6); + DEBUG(desync, 0, "Skipping replay of failed command: %s", p + 6); #endif } else { /* Can't parse a line; what's wrong here? */ - DEBUG(net, 0, "trying to parse: %s", p); + DEBUG(desync, 0, "Trying to parse: %s", p); NOT_REACHED(); } } if (f != nullptr && feof(f)) { - DEBUG(net, 0, "End of commands.log"); + DEBUG(desync, 0, "End of commands.log"); fclose(f); f = nullptr; } @@ -1218,7 +1218,7 @@ public: void OnConnect(SOCKET s) override { - DEBUG(net, 0, "Redirecting DEBUG() to %s", this->connection_string.c_str()); + DEBUG(net, 3, "Redirecting DEBUG() to %s", this->connection_string.c_str()); extern SOCKET _debug_socket; _debug_socket = s; @@ -1233,7 +1233,7 @@ void NetworkStartDebugLog(const std::string &connection_string) /** This tries to launch the network for a given OS */ void NetworkStartUp() { - DEBUG(net, 3, "[core] starting network..."); + DEBUG(net, 3, "Starting network"); /* Network is available */ _network_available = NetworkCoreInitialize(); @@ -1246,7 +1246,7 @@ void NetworkStartUp() _network_game_info = {}; NetworkInitialize(); - DEBUG(net, 3, "[core] network online, multiplayer available"); + DEBUG(net, 3, "Network online, multiplayer available"); NetworkFindBroadcastIPs(&_broadcast_list); } @@ -1256,7 +1256,7 @@ void NetworkShutDown() NetworkDisconnect(true); NetworkUDPClose(); - DEBUG(net, 3, "[core] shutting down network"); + DEBUG(net, 3, "Shutting down network"); _network_available = false; diff --git a/src/network/network_admin.cpp b/src/network/network_admin.cpp index 7b49754515..4e73aed0f8 100644 --- a/src/network/network_admin.cpp +++ b/src/network/network_admin.cpp @@ -74,7 +74,7 @@ ServerNetworkAdminSocketHandler::ServerNetworkAdminSocketHandler(SOCKET s) : Net ServerNetworkAdminSocketHandler::~ServerNetworkAdminSocketHandler() { _network_admins_connected--; - DEBUG(net, 1, "[admin] '%s' (%s) has disconnected", this->admin_name, this->admin_version); + DEBUG(net, 3, "[admin] '%s' (%s) has disconnected", this->admin_name, this->admin_version); if (_redirect_console_to_admin == this->index) _redirect_console_to_admin = INVALID_ADMIN_ID; } @@ -97,7 +97,7 @@ ServerNetworkAdminSocketHandler::~ServerNetworkAdminSocketHandler() { for (ServerNetworkAdminSocketHandler *as : ServerNetworkAdminSocketHandler::Iterate()) { if (as->status == ADMIN_STATUS_INACTIVE && std::chrono::steady_clock::now() > as->connect_time + ADMIN_AUTHORISATION_TIMEOUT) { - DEBUG(net, 1, "[admin] Admin did not send its authorisation within %d seconds", (uint32)std::chrono::duration_cast(ADMIN_AUTHORISATION_TIMEOUT).count()); + DEBUG(net, 2, "[admin] Admin did not send its authorisation within %d seconds", (uint32)std::chrono::duration_cast(ADMIN_AUTHORISATION_TIMEOUT).count()); as->CloseConnection(true); continue; } @@ -137,7 +137,7 @@ NetworkRecvStatus ServerNetworkAdminSocketHandler::SendError(NetworkErrorCode er StringID strid = GetNetworkErrorMsg(error); GetString(str, strid, lastof(str)); - DEBUG(net, 1, "[admin] the admin '%s' (%s) made an error and has been disconnected. Reason: '%s'", this->admin_name, this->admin_version, str); + DEBUG(net, 1, "[admin] The admin '%s' (%s) made an error and has been disconnected: '%s'", this->admin_name, this->admin_version, str); return this->CloseConnection(true); } @@ -518,7 +518,7 @@ NetworkRecvStatus ServerNetworkAdminSocketHandler::Receive_ADMIN_RCON(Packet *p) p->Recv_string(command, sizeof(command)); - DEBUG(net, 2, "[admin] Rcon command from '%s' (%s): '%s'", this->admin_name, this->admin_version, command); + DEBUG(net, 3, "[admin] Rcon command from '%s' (%s): %s", this->admin_name, this->admin_version, command); _redirect_console_to_admin = this->index; IConsoleCmdExec(command); @@ -534,7 +534,7 @@ NetworkRecvStatus ServerNetworkAdminSocketHandler::Receive_ADMIN_GAMESCRIPT(Pack p->Recv_string(json, sizeof(json)); - DEBUG(net, 2, "[admin] GameScript JSON from '%s' (%s): '%s'", this->admin_name, this->admin_version, json); + DEBUG(net, 6, "[admin] GameScript JSON from '%s' (%s): %s", this->admin_name, this->admin_version, json); Game::NewEvent(new ScriptEventAdminPort(json)); return NETWORK_RECV_STATUS_OKAY; @@ -546,7 +546,7 @@ NetworkRecvStatus ServerNetworkAdminSocketHandler::Receive_ADMIN_PING(Packet *p) uint32 d1 = p->Recv_uint32(); - DEBUG(net, 2, "[admin] Ping from '%s' (%s): '%d'", this->admin_name, this->admin_version, d1); + DEBUG(net, 6, "[admin] Ping from '%s' (%s): %d", this->admin_name, this->admin_version, d1); return this->SendPong(d1); } @@ -683,7 +683,7 @@ NetworkRecvStatus ServerNetworkAdminSocketHandler::Receive_ADMIN_JOIN(Packet *p) this->status = ADMIN_STATUS_ACTIVE; - DEBUG(net, 1, "[admin] '%s' (%s) has connected", this->admin_name, this->admin_version); + DEBUG(net, 3, "[admin] '%s' (%s) has connected", this->admin_name, this->admin_version); return this->SendProtocol(); } @@ -703,7 +703,7 @@ NetworkRecvStatus ServerNetworkAdminSocketHandler::Receive_ADMIN_UPDATE_FREQUENC if (type >= ADMIN_UPDATE_END || (_admin_update_type_frequencies[type] & freq) != freq) { /* The server does not know of this UpdateType. */ - DEBUG(net, 3, "[admin] Not supported update frequency %d (%d) from '%s' (%s).", type, freq, this->admin_name, this->admin_version); + DEBUG(net, 1, "[admin] Not supported update frequency %d (%d) from '%s' (%s)", type, freq, this->admin_name, this->admin_version); return this->SendError(NETWORK_ERROR_ILLEGAL_PACKET); } @@ -771,7 +771,7 @@ NetworkRecvStatus ServerNetworkAdminSocketHandler::Receive_ADMIN_POLL(Packet *p) default: /* An unsupported "poll" update type. */ - DEBUG(net, 3, "[admin] Not supported poll %d (%d) from '%s' (%s).", type, d1, this->admin_name, this->admin_version); + DEBUG(net, 1, "[admin] Not supported poll %d (%d) from '%s' (%s).", type, d1, this->admin_name, this->admin_version); return this->SendError(NETWORK_ERROR_ILLEGAL_PACKET); } @@ -798,7 +798,7 @@ NetworkRecvStatus ServerNetworkAdminSocketHandler::Receive_ADMIN_CHAT(Packet *p) break; default: - DEBUG(net, 3, "[admin] Invalid chat action %d from admin '%s' (%s).", action, this->admin_name, this->admin_version); + DEBUG(net, 1, "[admin] Invalid chat action %d from admin '%s' (%s).", action, this->admin_name, this->admin_version); return this->SendError(NETWORK_ERROR_ILLEGAL_PACKET); } diff --git a/src/network/network_client.cpp b/src/network/network_client.cpp index 501449705e..34746e935c 100644 --- a/src/network/network_client.cpp +++ b/src/network/network_client.cpp @@ -136,7 +136,7 @@ void ClientNetworkEmergencySave() if (!_networking) return; const char *filename = "netsave.sav"; - DEBUG(net, 0, "Client: Performing emergency save (%s)", filename); + DEBUG(net, 3, "Performing emergency save: %s", filename); SaveOrLoad(filename, SLO_SAVE, DFT_GAME_FILE, AUTOSAVE_DIR, false); } @@ -172,7 +172,7 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::CloseConnection(NetworkRecvSta */ if (this->sock == INVALID_SOCKET) return status; - DEBUG(net, 1, "Closed client connection %d", this->client_id); + DEBUG(net, 3, "Closed client connection %d", this->client_id); this->SendPackets(true); @@ -286,7 +286,7 @@ void ClientNetworkGameSocketHandler::ClientError(NetworkRecvStatus res) #endif ShowNetworkError(STR_NETWORK_ERROR_DESYNC); DEBUG(desync, 1, "sync_err: %08x; %02x", _date, _date_fract); - DEBUG(net, 0, "Sync error detected!"); + DEBUG(net, 0, "Sync error detected"); my_client->ClientError(NETWORK_RECV_STATUS_DESYNC); return false; } @@ -301,7 +301,7 @@ void ClientNetworkGameSocketHandler::ClientError(NetworkRecvStatus res) _sync_frame = 0; } else if (_sync_frame < _frame_counter) { - DEBUG(net, 1, "Missed frame for sync-test (%d / %d)", _sync_frame, _frame_counter); + DEBUG(net, 1, "Missed frame for sync-test: %d / %d", _sync_frame, _frame_counter); _sync_frame = 0; } } @@ -978,13 +978,13 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_FRAME(Packet *p /* Receive the token. */ if (p->CanReadFromPacket(sizeof(uint8))) this->token = p->Recv_uint8(); - DEBUG(net, 5, "Received FRAME %d", _frame_counter_server); + DEBUG(net, 7, "Received FRAME %d", _frame_counter_server); /* Let the server know that we received this frame correctly * We do this only once per day, to save some bandwidth ;) */ if (!_network_first_time && last_ack_frame < _frame_counter) { last_ack_frame = _frame_counter + DAY_TICKS; - DEBUG(net, 4, "Sent ACK at %d", _frame_counter); + DEBUG(net, 7, "Sent ACK at %d", _frame_counter); SendAck(); } @@ -1100,7 +1100,7 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_QUIT(Packet *p) NetworkTextMessage(NETWORK_ACTION_LEAVE, CC_DEFAULT, false, ci->client_name, nullptr, STR_NETWORK_MESSAGE_CLIENT_LEAVING); delete ci; } else { - DEBUG(net, 0, "Unknown client (%d) is leaving the game", client_id); + DEBUG(net, 1, "Unknown client (%d) is leaving the game", client_id); } InvalidateWindowData(WC_CLIENT_LIST, 0); @@ -1180,7 +1180,7 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_MOVE(Packet *p) if (client_id == 0) { /* definitely an invalid client id, debug message and do nothing. */ - DEBUG(net, 0, "[move] received invalid client index = 0"); + DEBUG(net, 1, "Received invalid client index = 0"); return NETWORK_RECV_STATUS_MALFORMED_PACKET; } diff --git a/src/network/network_command.cpp b/src/network/network_command.cpp index e876670587..c9b21204a1 100644 --- a/src/network/network_command.cpp +++ b/src/network/network_command.cpp @@ -333,7 +333,7 @@ void NetworkGameSocketHandler::SendCommand(Packet *p, const CommandPacket *cp) } if (callback == lengthof(_callback_table)) { - DEBUG(net, 0, "Unknown callback. (Pointer: %p) No callback sent", cp->callback); + DEBUG(net, 0, "Unknown callback for command; no callback sent (command: %d)", cp->cmd); callback = 0; // _callback_table[0] == nullptr } p->Send_uint8 (callback); diff --git a/src/network/network_gamelist.cpp b/src/network/network_gamelist.cpp index 20fbb19a7c..ae716070c6 100644 --- a/src/network/network_gamelist.cpp +++ b/src/network/network_gamelist.cpp @@ -112,7 +112,6 @@ void NetworkGameListRemoveItem(NetworkGameList *remove) ClearGRFConfigList(&remove->info.grfconfig); delete remove; - DEBUG(net, 4, "[gamelist] removed server from list"); NetworkRebuildHostList(); UpdateNetworkGameWindow(); return; diff --git a/src/network/network_server.cpp b/src/network/network_server.cpp index eccf80f781..bbadbeba4d 100644 --- a/src/network/network_server.cpp +++ b/src/network/network_server.cpp @@ -283,7 +283,7 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::CloseConnection(NetworkRecvSta } NetworkAdminClientError(this->client_id, NETWORK_ERROR_CONNECTION_LOST); - DEBUG(net, 1, "Closed client connection %d", this->client_id); + DEBUG(net, 3, "Closed client connection %d", this->client_id); /* We just lost one client :( */ if (this->status >= STATUS_AUTHORIZED) _network_game_info.clients_on--; @@ -448,7 +448,7 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::SendError(NetworkErrorCode err this->GetClientName(client_name, lastof(client_name)); - DEBUG(net, 1, "'%s' made an error and has been disconnected. Reason: '%s'", client_name, str); + DEBUG(net, 1, "'%s' made an error and has been disconnected: %s", client_name, str); if (error == NETWORK_ERROR_KICKED && reason != nullptr) { NetworkTextMessage(NETWORK_ACTION_KICKED, CC_DEFAULT, false, client_name, reason, strid); @@ -469,7 +469,7 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::SendError(NetworkErrorCode err NetworkAdminClientError(this->client_id, error); } else { - DEBUG(net, 1, "Client %d made an error and has been disconnected. Reason: '%s'", this->client_id, str); + DEBUG(net, 1, "Client %d made an error and has been disconnected: %s", this->client_id, str); } /* The client made a mistake, so drop his connection now! */ @@ -1144,7 +1144,7 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::Receive_CLIENT_ERROR(Packet *p StringID strid = GetNetworkErrorMsg(errorno); GetString(str, strid, lastof(str)); - DEBUG(net, 2, "'%s' reported an error and is closing its connection (%s)", client_name, str); + DEBUG(net, 1, "'%s' reported an error and is closing its connection: %s", client_name, str); NetworkTextMessage(NETWORK_ACTION_LEAVE, CC_DEFAULT, false, client_name, nullptr, strid); @@ -1335,7 +1335,7 @@ void NetworkServerSendChat(NetworkAction action, DestType desttype, int dest, co break; } default: - DEBUG(net, 0, "[server] received unknown chat destination type %d. Doing broadcast instead", desttype); + DEBUG(net, 1, "Received unknown chat destination type %d; doing broadcast instead", desttype); FALLTHROUGH; case DESTTYPE_BROADCAST: @@ -1445,11 +1445,11 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::Receive_CLIENT_RCON(Packet *p) p->Recv_string(command, sizeof(command)); if (strcmp(pass, _settings_client.network.rcon_password) != 0) { - DEBUG(net, 0, "[rcon] wrong password from client-id %d", this->client_id); + DEBUG(net, 1, "[rcon] Wrong password from client-id %d", this->client_id); return NETWORK_RECV_STATUS_OKAY; } - DEBUG(net, 0, "[rcon] client-id %d executed: '%s'", this->client_id, command); + DEBUG(net, 3, "[rcon] Client-id %d executed: %s", this->client_id, command); _redirect_console_to_client = this->client_id; IConsoleCmdExec(command); @@ -1474,7 +1474,7 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::Receive_CLIENT_MOVE(Packet *p) /* Incorrect password sent, return! */ if (strcmp(password, _network_company_states[company_id].password) != 0) { - DEBUG(net, 2, "[move] wrong password from client-id #%d for company #%d", this->client_id, company_id + 1); + DEBUG(net, 2, "Wrong password from client-id #%d for company #%d", this->client_id, company_id + 1); return NETWORK_RECV_STATUS_OKAY; } } @@ -1597,7 +1597,7 @@ void NetworkUpdateClientInfo(ClientID client_id) static void NetworkCheckRestartMap() { if (_settings_client.network.restart_game_year != 0 && _cur_year >= _settings_client.network.restart_game_year) { - DEBUG(net, 0, "Auto-restarting map. Year %d reached", _cur_year); + DEBUG(net, 3, "Auto-restarting map: year %d reached", _cur_year); _settings_newgame.game_creation.generation_seed = GENERATE_NEW_SEED; switch(_file_to_saveload.abstract_ftype) { diff --git a/src/network/network_udp.cpp b/src/network/network_udp.cpp index 8a95596c17..75bf4563d3 100644 --- a/src/network/network_udp.cpp +++ b/src/network/network_udp.cpp @@ -67,7 +67,7 @@ struct UDPSocket { std::unique_lock lock(mutex, std::defer_lock); if (!lock.try_lock()) { if (++receive_iterations_locked % 32 == 0) { - DEBUG(net, 0, "[udp] %s background UDP loop processing appears to be blocked. Your OS may be low on UDP send buffers.", name.c_str()); + DEBUG(net, 0, "%s background UDP loop processing appears to be blocked. Your OS may be low on UDP send buffers.", name.c_str()); } return; } @@ -133,7 +133,7 @@ public: void MasterNetworkUDPSocketHandler::Receive_MASTER_ACK_REGISTER(Packet *p, NetworkAddress *client_addr) { _network_advertise_retries = 0; - DEBUG(net, 2, "[udp] advertising on master server successful (%s)", NetworkAddress::AddressFamilyAsString(client_addr->GetAddress()->ss_family)); + DEBUG(net, 3, "Advertising on master server successful (%s)", NetworkAddress::AddressFamilyAsString(client_addr->GetAddress()->ss_family)); /* We are advertised, but we don't want to! */ if (!_settings_client.network.server_advertise) NetworkUDPRemoveAdvertise(false); @@ -142,7 +142,7 @@ void MasterNetworkUDPSocketHandler::Receive_MASTER_ACK_REGISTER(Packet *p, Netwo void MasterNetworkUDPSocketHandler::Receive_MASTER_SESSION_KEY(Packet *p, NetworkAddress *client_addr) { _session_key = p->Recv_uint64(); - DEBUG(net, 2, "[udp] received new session key from master server (%s)", NetworkAddress::AddressFamilyAsString(client_addr->GetAddress()->ss_family)); + DEBUG(net, 6, "Received new session key from master server (%s)", NetworkAddress::AddressFamilyAsString(client_addr->GetAddress()->ss_family)); } ///*** Communication with clients (we are server) ***/ @@ -175,7 +175,7 @@ void ServerNetworkUDPSocketHandler::Receive_CLIENT_FIND_SERVER(Packet *p, Networ /* Let the client know that we are here */ this->SendPacket(&packet, client_addr); - DEBUG(net, 2, "[udp] queried from %s", client_addr->GetHostname()); + DEBUG(net, 7, "Queried from %s", client_addr->GetHostname()); } void ServerNetworkUDPSocketHandler::Receive_CLIENT_DETAIL_INFO(Packet *p, NetworkAddress *client_addr) @@ -252,7 +252,7 @@ void ServerNetworkUDPSocketHandler::Receive_CLIENT_GET_NEWGRFS(Packet *p, Networ uint8 in_reply_count = 0; size_t packet_len = 0; - DEBUG(net, 6, "[udp] newgrf data request from %s", client_addr->GetAddressAsString().c_str()); + DEBUG(net, 7, "NewGRF data request from %s", client_addr->GetAddressAsString().c_str()); num_grfs = p->Recv_uint8 (); if (num_grfs > NETWORK_MAX_GRF_COUNT) return; @@ -314,7 +314,7 @@ void ClientNetworkUDPSocketHandler::Receive_SERVER_RESPONSE(Packet *p, NetworkAd /* Just a fail-safe.. should never happen */ if (_network_udp_server) return; - DEBUG(net, 4, "[udp] server response from %s", client_addr->GetAddressAsString().c_str()); + DEBUG(net, 3, "Server response from %s", client_addr->GetAddressAsString().c_str()); /* Find next item */ item = NetworkGameListAddItem(client_addr->GetAddressAsString(false)); @@ -409,7 +409,7 @@ void ClientNetworkUDPSocketHandler::Receive_SERVER_NEWGRFS(Packet *p, NetworkAdd uint8 num_grfs; uint i; - DEBUG(net, 6, "[udp] newgrf data reply from %s", client_addr->GetAddressAsString().c_str()); + DEBUG(net, 7, "NewGRF data reply from %s", client_addr->GetAddressAsString().c_str()); num_grfs = p->Recv_uint8 (); if (num_grfs > NETWORK_MAX_GRF_COUNT) return; @@ -441,7 +441,7 @@ static void NetworkUDPBroadCast(NetworkUDPSocketHandler *socket) for (NetworkAddress &addr : _broadcast_list) { Packet p(PACKET_UDP_CLIENT_FIND_SERVER); - DEBUG(net, 4, "[udp] broadcasting to %s", addr.GetHostname()); + DEBUG(net, 5, "Broadcasting to %s", addr.GetHostname()); socket->SendPacket(&p, &addr, true, true); } @@ -461,7 +461,7 @@ void NetworkUDPQueryMasterServer() std::lock_guard lock(_udp_client.mutex); _udp_client.socket->SendPacket(&p, &out_addr, true); - DEBUG(net, 2, "[udp] master server queried at %s", out_addr.GetAddressAsString().c_str()); + DEBUG(net, 6, "Master server queried at %s", out_addr.GetAddressAsString().c_str()); } /** Find all servers */ @@ -470,7 +470,7 @@ void NetworkUDPSearchGame() /* We are still searching.. */ if (_network_udp_broadcast > 0) return; - DEBUG(net, 0, "[udp] searching server"); + DEBUG(net, 3, "Searching server"); NetworkUDPBroadCast(_udp_client.socket); _network_udp_broadcast = 300; // Stay searching for 300 ticks @@ -481,7 +481,7 @@ void NetworkUDPSearchGame() */ static void NetworkUDPRemoveAdvertiseThread() { - DEBUG(net, 1, "[udp] removing advertise from master server"); + DEBUG(net, 3, "Removing advertise from master server"); /* Find somewhere to send */ NetworkAddress out_addr(NETWORK_MASTER_SERVER_HOST, NETWORK_MASTER_SERVER_PORT); @@ -518,22 +518,22 @@ static void NetworkUDPAdvertiseThread() /* Find somewhere to send */ NetworkAddress out_addr(NETWORK_MASTER_SERVER_HOST, NETWORK_MASTER_SERVER_PORT); - DEBUG(net, 1, "[udp] advertising to master server"); + DEBUG(net, 3, "Advertising to master server"); /* Add a bit more messaging when we cannot get a session key */ static byte session_key_retries = 0; if (_session_key == 0 && session_key_retries++ == 2) { - DEBUG(net, 0, "[udp] advertising to the master server is failing"); - DEBUG(net, 0, "[udp] we are not receiving the session key from the server"); - DEBUG(net, 0, "[udp] please allow udp packets from %s to you to be delivered", out_addr.GetAddressAsString(false).c_str()); - DEBUG(net, 0, "[udp] please allow udp packets from you to %s to be delivered", out_addr.GetAddressAsString(false).c_str()); + DEBUG(net, 0, "Advertising to the master server is failing"); + DEBUG(net, 0, " we are not receiving the session key from the server"); + DEBUG(net, 0, " please allow udp packets from %s to you to be delivered", out_addr.GetAddressAsString(false).c_str()); + DEBUG(net, 0, " please allow udp packets from you to %s to be delivered", out_addr.GetAddressAsString(false).c_str()); } if (_session_key != 0 && _network_advertise_retries == 0) { - DEBUG(net, 0, "[udp] advertising to the master server is failing"); - DEBUG(net, 0, "[udp] we are not receiving the acknowledgement from the server"); - DEBUG(net, 0, "[udp] this usually means that the master server cannot reach us"); - DEBUG(net, 0, "[udp] please allow udp and tcp packets to port %u to be delivered", _settings_client.network.server_port); - DEBUG(net, 0, "[udp] please allow udp and tcp packets from port %u to be delivered", _settings_client.network.server_port); + DEBUG(net, 0, "Advertising to the master server is failing"); + DEBUG(net, 0, " we are not receiving the acknowledgement from the server"); + DEBUG(net, 0, " this usually means that the master server cannot reach us"); + DEBUG(net, 0, " please allow udp and tcp packets to port %u to be delivered", _settings_client.network.server_port); + DEBUG(net, 0, " please allow udp and tcp packets from port %u to be delivered", _settings_client.network.server_port); } /* Send the packet */ @@ -589,7 +589,7 @@ void NetworkUDPInitialize() /* If not closed, then do it. */ if (_udp_server.socket != nullptr) NetworkUDPClose(); - DEBUG(net, 1, "[udp] initializing listeners"); + DEBUG(net, 3, "Initializing UDP listeners"); assert(_udp_client.socket == nullptr && _udp_server.socket == nullptr && _udp_master.socket == nullptr); std::scoped_lock lock(_udp_client.mutex, _udp_server.mutex, _udp_master.mutex); @@ -625,7 +625,7 @@ void NetworkUDPClose() _network_udp_server = false; _network_udp_broadcast = 0; - DEBUG(net, 1, "[udp] closed listeners"); + DEBUG(net, 5, "Closed UDP listeners"); } /** Receive the UDP packets. */ diff --git a/src/openttd.cpp b/src/openttd.cpp index ea802b3db9..785ba80dda 100644 --- a/src/openttd.cpp +++ b/src/openttd.cpp @@ -560,7 +560,7 @@ int openttd_main(int argc, char *argv[]) videodriver = "dedicated"; blitter = "null"; dedicated = true; - SetDebugString("net=6"); + SetDebugString("net=4"); if (mgo.opt != nullptr) { scanner->dedicated_host = ParseFullConnectionString(mgo.opt, scanner->dedicated_port); } @@ -666,7 +666,7 @@ int openttd_main(int argc, char *argv[]) DeterminePaths(argv[0]); TarScanner::DoScan(TarScanner::BASESET); - if (dedicated) DEBUG(net, 0, "Starting dedicated version %s", _openttd_revision); + if (dedicated) DEBUG(net, 3, "Starting dedicated server, version %s", _openttd_revision); if (_dedicated_forks && !dedicated) _dedicated_forks = false; #if defined(UNIX) @@ -945,7 +945,7 @@ bool SafeLoad(const std::string &filename, SaveLoadOperation fop, DetailedFileTy * special cases which make clients desync immediately. So we fall * back to just generating a new game with the current settings. */ - DEBUG(net, 0, "Loading game failed, so a new (random) game will be started!"); + DEBUG(net, 0, "Loading game failed, so a new (random) game will be started"); MakeNewGame(false, true); return false; } diff --git a/src/saveload/afterload.cpp b/src/saveload/afterload.cpp index 7fd2204943..26c749bf73 100644 --- a/src/saveload/afterload.cpp +++ b/src/saveload/afterload.cpp @@ -566,8 +566,8 @@ bool AfterLoadGame() if (IsSavegameVersionBefore(SLV_119)) { _pause_mode = (_pause_mode == 2) ? PM_PAUSED_NORMAL : PM_UNPAUSED; } else if (_network_dedicated && (_pause_mode & PM_PAUSED_ERROR) != 0) { - DEBUG(net, 0, "The loading savegame was paused due to an error state."); - DEBUG(net, 0, " The savegame cannot be used for multiplayer!"); + DEBUG(net, 0, "The loading savegame was paused due to an error state"); + DEBUG(net, 0, " This savegame cannot be used for multiplayer"); /* Restore the signals */ ResetSignalHandlers(); return false; diff --git a/src/video/dedicated_v.cpp b/src/video/dedicated_v.cpp index a412782344..6f631e2278 100644 --- a/src/video/dedicated_v.cpp +++ b/src/video/dedicated_v.cpp @@ -256,7 +256,7 @@ void VideoDriver_Dedicated::MainLoop() * intro game... */ if (SaveOrLoad(_file_to_saveload.name, _file_to_saveload.file_op, _file_to_saveload.detail_ftype, BASE_DIR) == SL_ERROR) { /* Loading failed, pop out.. */ - DEBUG(net, 0, "Loading requested map failed, aborting"); + DEBUG(net, 0, "Loading requested map failed; closing server."); return; } else { /* We can load this game, so go ahead */ From 7b7dbbc935c32263be800c0e22831e52ace813e9 Mon Sep 17 00:00:00 2001 From: PeterN Date: Wed, 12 May 2021 17:43:35 +0100 Subject: [PATCH 45/61] Fix #9063: Caption of news window incorrectly aligned. (#9252) --- src/news_gui.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/news_gui.cpp b/src/news_gui.cpp index d3ddd463fa..e3fb600336 100644 --- a/src/news_gui.cpp +++ b/src/news_gui.cpp @@ -422,7 +422,7 @@ struct NewsWindow : Window { { switch (widget) { case WID_N_CAPTION: - DrawCaption(r, COLOUR_LIGHT_BLUE, this->owner, TC_FROMSTRING, STR_NEWS_MESSAGE_CAPTION, SA_HOR_CENTER); + DrawCaption(r, COLOUR_LIGHT_BLUE, this->owner, TC_FROMSTRING, STR_NEWS_MESSAGE_CAPTION, SA_CENTER); break; case WID_N_PANEL: From b972ed86045e1c287d03b3f7cda1ff21243d00fe Mon Sep 17 00:00:00 2001 From: PeterN Date: Wed, 12 May 2021 18:17:57 +0100 Subject: [PATCH 46/61] Fix #9242: Tree tick handler did not scale by map size. (#9246) This means that random tree generation density is higher on small maps and lower on large maps. This difference is enough to make the Lumber Mill impractical to use on large maps. This change skips ticks on maps smaller than 256x256 and increases iterations or shortens the interval on maps larger than 256x256. --- src/tree_cmd.cpp | 40 +++++++++++++++++++++++++++++++++------- 1 file changed, 33 insertions(+), 7 deletions(-) diff --git a/src/tree_cmd.cpp b/src/tree_cmd.cpp index c0865ffc68..f1a5be7f90 100644 --- a/src/tree_cmd.cpp +++ b/src/tree_cmd.cpp @@ -22,6 +22,7 @@ #include "company_base.h" #include "core/random_func.hpp" #include "newgrf_generic.h" +#include "date_func.h" #include "table/strings.h" #include "table/tree_land.h" @@ -803,6 +804,23 @@ static void TileLoop_Trees(TileIndex tile) MarkTileDirtyByTile(tile); } +/** + * Decrement the tree tick counter. + * The interval is scaled by map size to allow for the same density regardless of size. + * Adjustment for map sizes below the standard 256 * 256 are handled earlier. + * @return true if the counter was decremented past zero + */ +bool DecrementTreeCounter() +{ + /* Ensure _trees_tick_ctr can be decremented past zero only once for the largest map size. */ + static_assert(2 * (MAX_MAP_SIZE_BITS - MIN_MAP_SIZE_BITS) - 4 <= std::numeric_limits::digits); + + /* byte underflow */ + byte old_trees_tick_ctr = _trees_tick_ctr; + _trees_tick_ctr -= ScaleByMapSize(1); + return old_trees_tick_ctr <= _trees_tick_ctr; +} + void OnTick_Trees() { /* Don't spread trees if that's not allowed */ @@ -812,16 +830,24 @@ void OnTick_Trees() TileIndex tile; TreeType tree; + /* Skip some tree ticks for map sizes below 256 * 256. 64 * 64 is 16 times smaller, so + * this is the maximum number of ticks that are skipped. Number of ticks to skip is + * inversely proportional to map size, so that is handled to create a mask. */ + int skip = ScaleByMapSize(16); + if (skip < 16 && (_tick_counter & (16 / skip - 1)) != 0) return; + /* place a tree at a random rainforest spot */ - if (_settings_game.game_creation.landscape == LT_TROPIC && - (r = Random(), tile = RandomTileSeed(r), GetTropicZone(tile) == TROPICZONE_RAINFOREST) && - CanPlantTreesOnTile(tile, false) && - (tree = GetRandomTreeType(tile, GB(r, 24, 8))) != TREE_INVALID) { - PlantTreesOnTile(tile, tree, 0, 0); + if (_settings_game.game_creation.landscape == LT_TROPIC) { + for (uint c = ScaleByMapSize(1); c > 0; c--) { + if ((r = Random(), tile = RandomTileSeed(r), GetTropicZone(tile) == TROPICZONE_RAINFOREST) && + CanPlantTreesOnTile(tile, false) && + (tree = GetRandomTreeType(tile, GB(r, 24, 8))) != TREE_INVALID) { + PlantTreesOnTile(tile, tree, 0, 0); + } + } } - /* byte underflow */ - if (--_trees_tick_ctr != 0 || _settings_game.construction.extra_tree_placement == ETP_SPREAD_RAINFOREST) return; + if (!DecrementTreeCounter() || _settings_game.construction.extra_tree_placement == ETP_SPREAD_RAINFOREST) return; /* place a tree at a random spot */ r = Random(); From 3e7e2d9ca3f798bd1a5f72574510d7205e5f97b2 Mon Sep 17 00:00:00 2001 From: translators Date: Wed, 12 May 2021 19:08:38 +0000 Subject: [PATCH 47/61] Update: Translations from eints romanian: 38 changes by kneekoo slovak: 19 changes by FuryPapaya catalan: 1 change by J0anJosep turkish: 191 changes by Anceph --- src/lang/catalan.txt | 2 +- src/lang/romanian.txt | 46 +++++++-- src/lang/slovak.txt | 38 ++++---- src/lang/turkish.txt | 216 +++++++++++++++++++++++++++++++++++++----- 4 files changed, 251 insertions(+), 51 deletions(-) diff --git a/src/lang/catalan.txt b/src/lang/catalan.txt index bc89f55f40..9c666cd112 100644 --- a/src/lang/catalan.txt +++ b/src/lang/catalan.txt @@ -2270,7 +2270,7 @@ STR_NETWORK_MESSAGE_CLIENT_COMPANY_JOIN :*** {STRING} s' STR_NETWORK_MESSAGE_CLIENT_COMPANY_SPECTATE :*** {STRING} s'ha unit als espectadors STR_NETWORK_MESSAGE_CLIENT_COMPANY_NEW :*** {STRING} ha començat una nova companyia (#{2:NUM}) STR_NETWORK_MESSAGE_CLIENT_LEFT :*** {STRING} ha deixat la partida ({2:STRING}) -STR_NETWORK_MESSAGE_NAME_CHANGE :*** {STRING} ha canviat el seu nom a {STRING} +STR_NETWORK_MESSAGE_NAME_CHANGE :*** {STRING} ha canviat el seu nom a {STRING}. STR_NETWORK_MESSAGE_GIVE_MONEY :*** {STRING} ha donat {2:CURRENCY_LONG} a {1:STRING}. STR_NETWORK_MESSAGE_SERVER_SHUTDOWN :{WHITE}El servidor ha tancat la sessió STR_NETWORK_MESSAGE_SERVER_REBOOT :{WHITE}El servidor està reiniciant...{}Espera un moment... diff --git a/src/lang/romanian.txt b/src/lang/romanian.txt index 60cb7a616e..fe070471dc 100644 --- a/src/lang/romanian.txt +++ b/src/lang/romanian.txt @@ -928,7 +928,7 @@ STR_GAME_OPTIONS_CURRENCY_MYR :Ringgit Malaysi STR_GAME_OPTIONS_ROAD_VEHICLES_DROPDOWN_LEFT :Pe partea stângă STR_GAME_OPTIONS_ROAD_VEHICLES_DROPDOWN_RIGHT :Pe partea dreaptă -STR_GAME_OPTIONS_TOWN_NAMES_FRAME :{BLACK}Numele oraşelor +STR_GAME_OPTIONS_TOWN_NAMES_FRAME :{BLACK}Numele orașelor: STR_GAME_OPTIONS_TOWN_NAMES_DROPDOWN_TOOLTIP :{BLACK}Alege naţionalitatea numelor oraşelor ############ start of townname region @@ -990,6 +990,7 @@ STR_GAME_OPTIONS_GUI_ZOOM_DROPDOWN_NORMAL :Normală STR_GAME_OPTIONS_GUI_ZOOM_DROPDOWN_2X_ZOOM :Mărime dublă STR_GAME_OPTIONS_GUI_ZOOM_DROPDOWN_4X_ZOOM :Mărime împătrită +STR_GAME_OPTIONS_FONT_ZOOM :{BLACK}Dimensiune font STR_GAME_OPTIONS_FONT_ZOOM_DROPDOWN_TOOLTIP :{BLACK}Alege dimensiunea fontului pentru interfață STR_GAME_OPTIONS_FONT_ZOOM_DROPDOWN_AUTO :(auto-detecție) @@ -998,7 +999,9 @@ STR_GAME_OPTIONS_FONT_ZOOM_DROPDOWN_2X_ZOOM :Mărime dublă STR_GAME_OPTIONS_GRAPHICS :{BLACK}Grafică +STR_GAME_OPTIONS_REFRESH_RATE :{BLACK}Afișează rata de reîmprospătare STR_GAME_OPTIONS_REFRESH_RATE_TOOLTIP :{BLACK}Alegeți rata de reîmprospătare dorită +STR_GAME_OPTIONS_REFRESH_RATE_OTHER :alta STR_GAME_OPTIONS_REFRESH_RATE_ITEM :{NUM}Hz STR_GAME_OPTIONS_REFRESH_RATE_WARNING :{WHITE}Ratele de împrospătare de peste 60Hz ar putea afecta performanța. @@ -1177,6 +1180,7 @@ STR_CONFIG_SETTING_CITY_APPROVAL_HELPTEXT :Alege în ce m STR_CONFIG_SETTING_MAP_HEIGHT_LIMIT :Înălțimea limită a hărții: {STRING} STR_CONFIG_SETTING_MAP_HEIGHT_LIMIT_VALUE :{NUM} +STR_CONFIG_SETTING_MAP_HEIGHT_LIMIT_AUTO :(auto) STR_CONFIG_SETTING_TOO_HIGH_MOUNTAIN :{WHITE}Nu poți seta înălțimea maximă a hărții la această valoare. Cel puțin un munte de pe hartă este mai înalt de-atât. STR_CONFIG_SETTING_AUTOSLOPE :Permite terra-formarea sub clădiri, şine, etc. (auto-pante): {STRING} STR_CONFIG_SETTING_AUTOSLOPE_HELPTEXT :Permite terraformarea sub clădiri şi şine fără eliminarea acestora @@ -1430,6 +1434,7 @@ STR_CONFIG_SETTING_PERSISTENT_BUILDINGTOOLS :Pastrează acti STR_CONFIG_SETTING_PERSISTENT_BUILDINGTOOLS_HELPTEXT :Menține barele de construcție pentru tunele, poduri șamd deschise după folosire STR_CONFIG_SETTING_EXPENSES_LAYOUT :Grupează cheltuielile în raportul financiar al companiei: {STRING} STR_CONFIG_SETTING_EXPENSES_LAYOUT_HELPTEXT :Definește stilul ferestrei care afișează cheltuielile companiei +STR_CONFIG_SETTING_AUTO_REMOVE_SIGNALS :Elimină automat semnalele pe durata construcției șinelor: {STRING} STR_CONFIG_SETTING_AUTO_REMOVE_SIGNALS_HELPTEXT :Elimină automat semnalele când construiești căi ferate dacă ele îți vin în cale. Nu uita că asta ar putea conduce la accidente feroviare. STR_CONFIG_SETTING_FAST_FORWARD_SPEED_LIMIT :Limita de viteză pentru trecerea timpului: {STRING} STR_CONFIG_SETTING_FAST_FORWARD_SPEED_LIMIT_VAL :{NUM}% din viteza normală a jocului @@ -1550,6 +1555,8 @@ STR_CONFIG_SETTING_ECONOMY_TYPE_SMOOTH :Lin STR_CONFIG_SETTING_ECONOMY_TYPE_FROZEN :Înghețată STR_CONFIG_SETTING_ALLOW_SHARES :Permite cumpărarea de acţiuni de la alte companii: {STRING} STR_CONFIG_SETTING_ALLOW_SHARES_HELPTEXT :Dacă este activată, se permite cumpărarea și vânzarea de acțiuni ale companiilor. Acțiunile devin disponibile doar când compania depășește o anumită vârstă +STR_CONFIG_SETTING_MIN_YEARS_FOR_SHARES :Vârsta minimă a companiilor pentru tranzacțiile cu acțiuni: {STRING} +STR_CONFIG_SETTING_MIN_YEARS_FOR_SHARES_HELPTEXT :Stabilește vechimea minimă a unei companii, ca alții să-i poată tranzacționa acțiunile. STR_CONFIG_SETTING_FEEDER_PAYMENT_SHARE :Procentul din profitul pe secţiune care să fie plătit pentru alimentare: {STRING} STR_CONFIG_SETTING_FEEDER_PAYMENT_SHARE_HELPTEXT :Procentul din câştig care este oferit legăturilor intermediare pentru alimentare, oferind mai mult control asupra încasărilor STR_CONFIG_SETTING_DRAG_SIGNALS_DENSITY :Când se trage cu mouse-ul, plasează semnale la fiecare: {STRING} @@ -1801,6 +1808,7 @@ STR_ABANDON_SCENARIO_QUERY :{YELLOW}Sigur v # Cheat window STR_CHEATS :{WHITE}Cheat-uri STR_CHEATS_TOOLTIP :{BLACK}Bifa vă indică dacă aţi folosit anterior acest cheat +STR_CHEATS_NOTE :{BLACK}Notă: utilizarea acestor setări va fi memorată în salvarea jocului STR_CHEAT_MONEY :{LTBLUE}Măreşte fondurile cu {CURRENCY_LONG} STR_CHEAT_CHANGE_COMPANY :{LTBLUE}Joacă drept compania: {ORANGE}{COMMA} STR_CHEAT_EXTRA_DYNAMITE :{LTBLUE}Buldozer magic (demolează industrii şi lucruri amovibile): {ORANGE}{STRING} @@ -1910,6 +1918,7 @@ STR_FACE_TIE :Cravată: STR_FACE_EARRING :Cercei: STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}Schimbă cravata sau cerceii +STR_NETWORK_SERVER_VISIBILITY_PRIVATE :Privat # Network server list STR_NETWORK_SERVER_LIST_CAPTION :{WHITE}Multiplayer @@ -1973,6 +1982,8 @@ STR_NETWORK_START_SERVER_NEW_GAME_NAME_TOOLTIP :{BLACK}Numele j STR_NETWORK_START_SERVER_SET_PASSWORD :{BLACK}Pune parolă STR_NETWORK_START_SERVER_PASSWORD_TOOLTIP :{BLACK}Protejează-ţi jocul cu o parolă dacă nu vrei să intre jucători neautorizaţi +STR_NETWORK_START_SERVER_VISIBILITY_LABEL :{BLACK}Vizibilitate +STR_NETWORK_START_SERVER_VISIBILITY_TOOLTIP :{BLACK}Dacă alți oameni îți pot vedea serverul în lista publică STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS :{BLACK}Număr maxim de clienţi: STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS_TOOLTIP :{BLACK}Alege un număr maxim de clienţi. Nu trebuie ocupate toate locurile. STR_NETWORK_START_SERVER_NUMBER_OF_COMPANIES :{BLACK}Companii maxim: @@ -2032,11 +2043,21 @@ STR_NETWORK_NEED_GAME_PASSWORD_CAPTION :{WHITE}Server p STR_NETWORK_NEED_COMPANY_PASSWORD_CAPTION :{WHITE}Companie protejată. Introdu parola # Network company list added strings -STR_NETWORK_COMPANY_LIST_CLIENT_LIST :Lista de clienţi +STR_NETWORK_COMPANY_LIST_CLIENT_LIST :Jucători conectați # Network client list +STR_NETWORK_CLIENT_LIST_SERVER :{BLACK}Server +STR_NETWORK_CLIENT_LIST_SERVER_NAME_EDIT_TOOLTIP :{BLACK}Modifică numele serverului tău +STR_NETWORK_CLIENT_LIST_SERVER_VISIBILITY :{BLACK}Vizibilitate +STR_NETWORK_CLIENT_LIST_SERVER_VISIBILITY_TOOLTIP :{BLACK}Dacă alți oameni îți pot vedea serverul în lista publică +STR_NETWORK_CLIENT_LIST_PLAYER_NAME_EDIT_TOOLTIP :{BLACK}Modifică-ți numele +STR_NETWORK_CLIENT_LIST_CHAT_COMPANY_TOOLTIP :{BLACK}Trimite un mesaj tuturor jucătorilor acestei companii +STR_NETWORK_CLIENT_LIST_CHAT_SPECTATOR_TOOLTIP :{BLACK}Trimite un mesaj tuturor spectatorilor +STR_NETWORK_CLIENT_LIST_ADMIN_COMPANY_UNLOCK :Deblocare cu parolă +STR_NETWORK_CLIENT_LIST_ASK_CLIENT_KICK :{YELLOW}Sigur vrei să dai afară jucătorul '{STRING}'? +STR_NETWORK_CLIENT_LIST_ASK_CLIENT_BAN :{YELLOW}Sigur vrei să blochezi jucătorul '{STRING}'? STR_NETWORK_SERVER :Server STR_NETWORK_CLIENT :Client @@ -2092,6 +2113,7 @@ STR_NETWORK_ERROR_TIMEOUT_PASSWORD :{WHITE}A expira STR_NETWORK_ERROR_TIMEOUT_COMPUTER :{WHITE}Calculatorul dvs. este prea lent pentru a se sincroniza cu serverul STR_NETWORK_ERROR_TIMEOUT_MAP :{WHITE}A expirat timpul pentru descărcarea hărţii STR_NETWORK_ERROR_TIMEOUT_JOIN :{WHITE}A expirat timpul pentru conectarea la server +STR_NETWORK_ERROR_SERVER_TOO_OLD :{WHITE}Serverul solicitat este prea vechi pentru acest client ############ Leave those lines in this order!! STR_NETWORK_ERROR_CLIENT_GENERAL :eroare generală @@ -2141,7 +2163,7 @@ STR_NETWORK_MESSAGE_CLIENT_COMPANY_JOIN :*** {STRING} a STR_NETWORK_MESSAGE_CLIENT_COMPANY_SPECTATE :*** {STRING} a intrat ca spectator STR_NETWORK_MESSAGE_CLIENT_COMPANY_NEW :*** {STRING} a început o companie nouă (#{2:NUM}) STR_NETWORK_MESSAGE_CLIENT_LEFT :*** {STRING} a ieşit din joc ({2:STRING}) -STR_NETWORK_MESSAGE_NAME_CHANGE :*** {STRING} şi-a schimbat numele în {STRING} +STR_NETWORK_MESSAGE_NAME_CHANGE :*** {STRING} și-a schimbat numele în {STRING} STR_NETWORK_MESSAGE_GIVE_MONEY :*** {STRING} a dat {2:CURRENCY_LONG} către {1:STRING} STR_NETWORK_MESSAGE_SERVER_SHUTDOWN :{WHITE}Serverul a închis conexiunea STR_NETWORK_MESSAGE_SERVER_REBOOT :{WHITE}Serverul este repornit...{}Vă rugăm aşteptaţi... @@ -2695,9 +2717,11 @@ STR_FRAMERATE_AI :{BLACK} IA {N ############ Leave those lines in this order!! STR_FRAMETIME_CAPTION_GAMELOOP :Buclă de joc STR_FRAMETIME_CAPTION_GL_ECONOMY :Manipularea încărcăturilor +STR_FRAMETIME_CAPTION_GL_LINKGRAPH :Decalaj grafic de conexiuni STR_FRAMETIME_CAPTION_DRAWING :Randare grafică STR_FRAMETIME_CAPTION_DRAWING_VIEWPORTS :Randarea vizorului global STR_FRAMETIME_CAPTION_SOUND :Mixaj de sunet +STR_FRAMETIME_CAPTION_ALLSCRIPTS :Totalul de scripturi GS/AI STR_FRAMETIME_CAPTION_AI :IA {NUM} {STRING} ############ End of leave-in-this-order @@ -3309,6 +3333,7 @@ STR_INDUSTRY_VIEW_CARGO_LIST_EXTENSION :, {STRING}{STRI STR_INDUSTRY_VIEW_REQUIRES :{BLACK}Necesită: STR_INDUSTRY_VIEW_ACCEPT_CARGO :{YELLOW}{STRING}{BLACK}{3:STRING} +STR_INDUSTRY_VIEW_ACCEPT_CARGO_AMOUNT :{YELLOW}{STRING}{BLACK}: {CARGO_SHORT} așteaptă{STRING} STR_CONFIG_GAME_PRODUCTION :{WHITE}Schimba productia (multiplu de 8, până la 2040) STR_CONFIG_GAME_PRODUCTION_LEVEL :{WHITE}Modifică nivelul producţiei (procent, până la 800%) @@ -3428,11 +3453,12 @@ STR_BUY_VEHICLE_ROAD_VEHICLE_BUY_REFIT_VEHICLE_BUTTON :{BLACK}Cumpăr STR_BUY_VEHICLE_AIRCRAFT_BUY_REFIT_VEHICLE_BUTTON :{BLACK}Cumpără și schimbă marfa transportată de aeronavă STR_BUY_VEHICLE_TRAIN_BUY_VEHICLE_TOOLTIP :{BLACK}Cumpără vehiculul feroviar selectat. Shift+Click arată costul estimat fără să cumpere vehiculul -STR_BUY_VEHICLE_ROAD_VEHICLE_BUY_VEHICLE_TOOLTIP :{BLACK}Cumpără autovehiculul selectat. Shift+Click arată costul estimat fără să cumpere autovehiculul +STR_BUY_VEHICLE_ROAD_VEHICLE_BUY_VEHICLE_TOOLTIP :{BLACK}Cumpără autovehiculul selectat. Shift+clic arată costul estimat fără achiziția autovehiculului STR_BUY_VEHICLE_SHIP_BUY_VEHICLE_TOOLTIP :{BLACK}Cumpără nava selectată. Shift+Click arată costul estimativ fără a efectua achiziţia STR_BUY_VEHICLE_AIRCRAFT_BUY_VEHICLE_TOOLTIP :{BLACK}Cumpără aeronava selectată. Shift+Click arată costul estimativ fără a efectua achiziţia -STR_BUY_VEHICLE_TRAIN_BUY_REFIT_VEHICLE_TOOLTIP :{BLACK}Cumpără și repară trenul selectat. Shift+Click arată costul estimat fără cumpărare +STR_BUY_VEHICLE_TRAIN_BUY_REFIT_VEHICLE_TOOLTIP :{BLACK}Cumpără și repară trenul selectat. Shift+clic arată costul estimat fără achiziție +STR_BUY_VEHICLE_ROAD_VEHICLE_BUY_REFIT_VEHICLE_TOOLTIP :{BLACK}Cumpără și repară autovehiculul selectat. Shift+clic arată costul estimat fără achiziție STR_BUY_VEHICLE_AIRCRAFT_BUY_REFIT_VEHICLE_TOOLTIP :{BLACK}Cumpără și repară aeronava selectată. Shift+clic afișează costul estimat fără achiziție STR_BUY_VEHICLE_TRAIN_RENAME_BUTTON :{BLACK}Nume nou @@ -3544,6 +3570,7 @@ STR_ENGINE_PREVIEW_CAPTION :{WHITE}Mesaj de STR_ENGINE_PREVIEW_MESSAGE :{GOLD}Am creat un nou tip de {STRING}. Aţi fi interesaţi de folosirea exclusivă pentru un an a acestui vehicul, astfel ca noi să-i putem observa performanţele înaintea lansării oficiale? STR_ENGINE_PREVIEW_RAILROAD_LOCOMOTIVE :locomotivă +STR_ENGINE_PREVIEW_ELRAIL_LOCOMOTIVE :locomotivă electrificată STR_ENGINE_PREVIEW_MONORAIL_LOCOMOTIVE :locomotivă monoşină STR_ENGINE_PREVIEW_MAGLEV_LOCOMOTIVE :locomotivă pernă magnetică @@ -3555,8 +3582,9 @@ STR_ENGINE_PREVIEW_SHIP :navă STR_ENGINE_PREVIEW_COST_WEIGHT_SPEED_POWER :{BLACK}Cost: {CURRENCY_LONG} Greutate: {WEIGHT_SHORT}{}Vitezã: {VELOCITY} Putere: {POWER}{}Cost de rulare: {CURRENCY_LONG}/an{}Capacitate: {CARGO_LONG} STR_ENGINE_PREVIEW_COST_WEIGHT_SPEED_POWER_MAX_TE :{BLACK}Cost: {CURRENCY_LONG} Greutate: {WEIGHT_SHORT}{}Viteză: {VELOCITY} Putere: {POWER} Ef. T. Max.: {6:FORCE}{}Cost rulaj: {4:CURRENCY_LONG}/an{}Capacitate: {5:CARGO_LONG} -STR_ENGINE_PREVIEW_COST_MAX_SPEED_CAP_RUNCOST :{BLACK}Cost: {CURRENCY_LONG} Viteză max.: {VELOCITY}{}Capacitate: {CARGO_LONG}{}Mentenanţă: {CURRENCY_LONG}/an -STR_ENGINE_PREVIEW_COST_MAX_SPEED_TYPE_RANGE_CAP_CAP_RUNCOST :{BLACK}Cost: {CURRENCY_LONG} Viteza maximă: {VELOCITY}{}Tip avion: {STRING} Rază: {COMMA} pătrățele{}Capacitate: {CARGO_LONG}, {CARGO_LONG}{}Cost întreținere: {CURRENCY_LONG}/an +STR_ENGINE_PREVIEW_COST_MAX_SPEED_CAP_RUNCOST :{BLACK}Cost: {CURRENCY_LONG} Viteză max.: {VELOCITY}{}Capacitate: {CARGO_LONG}{}Mentenanță: {CURRENCY_LONG}/an +STR_ENGINE_PREVIEW_COST_MAX_SPEED_TYPE_CAP_CAP_RUNCOST :{BLACK}Cost: {CURRENCY_LONG} Viteză max.: {VELOCITY}{}Tip avion: {STRING}{}Capacitate: {CARGO_LONG}, {CARGO_LONG}{}Mentenanță: {CURRENCY_LONG}/an +STR_ENGINE_PREVIEW_COST_MAX_SPEED_TYPE_RANGE_CAP_CAP_RUNCOST :{BLACK}Cost: {CURRENCY_LONG} Viteza maximă: {VELOCITY}{}Tip avion: {STRING} Rază: {COMMA} pătrățele{}Capacitate: {CARGO_LONG}, {CARGO_LONG}{}Mentenanță: {CURRENCY_LONG}/an # Autoreplace window STR_REPLACE_VEHICLES_WHITE :{WHITE}Înlocuieşte {STRING} - {STRING} @@ -3606,6 +3634,7 @@ STR_REPLACE_REMOVE_WAGON_HELP :{BLACK}Fă opti # Vehicle view STR_VEHICLE_VIEW_CAPTION :{WHITE}{VEHICLE} +STR_VEHICLE_VIEW_ROAD_VEHICLE_CENTER_TOOLTIP :{BLACK}Centrează vizorul principal pe locația vehiculului. Dublu-clic va urmări vehiculul în vizorul principal. Ctrl+clic deschide un nou vizor cu locația vehiculului STR_VEHICLE_VIEW_SHIP_CENTER_TOOLTIP :{BLACK}Centrează imaginea pe locația navei. Dublu clic va urmări nava în vizorul principal. Ctrl+Clic deschide un nou vizor pe locația navei STR_VEHICLE_VIEW_TRAIN_SEND_TO_DEPOT_TOOLTIP :{BLACK}Trimite trenul într-un depou @@ -3639,6 +3668,7 @@ STR_VEHICLE_VIEW_SHIP_SHOW_DETAILS_TOOLTIP :{BLACK}Afişeaz STR_VEHICLE_VIEW_AIRCRAFT_SHOW_DETAILS_TOOLTIP :{BLACK}Afişează detaliile aeronavei STR_VEHICLE_VIEW_TRAIN_STATUS_START_STOP_TOOLTIP :{BLACK}Acțiunea trenului curent - clic pentru oprirea/pornirea trenului +STR_VEHICLE_VIEW_ROAD_VEHICLE_STATUS_START_STOP_TOOLTIP :{BLACK}Acțiunea vehiculului curent - clic pentru oprirea/pornirea vehiculului STR_VEHICLE_VIEW_AIRCRAFT_STATUS_START_STOP_TOOLTIP :{BLACK}Acțiunea avionului curent - clic pentru oprirea/pornirea avionului @@ -4193,7 +4223,7 @@ STR_ERROR_CAN_T_BUY_COMPANY :{WHITE}Nu se po STR_ERROR_CAN_T_BUILD_COMPANY_HEADQUARTERS :{WHITE}Nu se poate construi sediul companiei... STR_ERROR_CAN_T_BUY_25_SHARE_IN_THIS :{WHITE}Nu se pot cumpăra 25% din acţiunile acestei companii... STR_ERROR_CAN_T_SELL_25_SHARE_IN :{WHITE}Nu se pot vinde 25% din acţiunile acestei companii... -STR_ERROR_PROTECTED :{WHITE}Această companie încă nu vinde acţiuni... +STR_ERROR_PROTECTED :{WHITE}Compania nu are vechimea necesară pentru tranzacționarea de acțiuni... # Town related errors STR_ERROR_CAN_T_GENERATE_TOWN :{WHITE}Nu pot construi nici un oras diff --git a/src/lang/slovak.txt b/src/lang/slovak.txt index 2ed535c7a9..908f055ecf 100644 --- a/src/lang/slovak.txt +++ b/src/lang/slovak.txt @@ -4461,9 +4461,9 @@ STR_ERROR_LOCAL_AUTHORITY_REFUSES_NOISE :{WHITE}{TOWN} m STR_ERROR_BRIBE_FAILED :{WHITE}Tvoj pokus o podplácanie bol odhalený vyšetrovateľom # Levelling errors -STR_ERROR_CAN_T_RAISE_LAND_HERE :{WHITE}Nemôžeš tu zvýšiť terén... -STR_ERROR_CAN_T_LOWER_LAND_HERE :{WHITE}Nemôžeš tu znížiť terén... -STR_ERROR_CAN_T_LEVEL_LAND_HERE :{WHITE}Nemôžem tu upraviť terén... +STR_ERROR_CAN_T_RAISE_LAND_HERE :{WHITE}Nemôžete tu zvýšiť terén... +STR_ERROR_CAN_T_LOWER_LAND_HERE :{WHITE}Nemôžete tu znížiť terén... +STR_ERROR_CAN_T_LEVEL_LAND_HERE :{WHITE}Nemôžete tu upraviť terén... STR_ERROR_EXCAVATION_WOULD_DAMAGE :{WHITE}Vyhĺbenie by poškodilo tunel STR_ERROR_ALREADY_AT_SEA_LEVEL :{WHITE}... už na úrovni mora STR_ERROR_TOO_HIGH :{WHITE}... príliš vysoko @@ -4483,14 +4483,14 @@ STR_ERROR_INSUFFICIENT_FUNDS :{WHITE}Nie je m STR_ERROR_CAN_T_GIVE_MONEY :{WHITE}Nie je možné presunúť peniaze tejto spoločnosti... STR_ERROR_CAN_T_BUY_COMPANY :{WHITE}Spoločnosť nie je možné kúpiť ... STR_ERROR_CAN_T_BUILD_COMPANY_HEADQUARTERS :{WHITE}Nemôžete tu postaviť sídlo spoločnosti... -STR_ERROR_CAN_T_BUY_25_SHARE_IN_THIS :{WHITE}Nemôžeš kúpiť 25% podiel v tejto spoločnosti... +STR_ERROR_CAN_T_BUY_25_SHARE_IN_THIS :{WHITE}Nemôžete kúpiť 25% podiel v tejto spoločnosti... STR_ERROR_CAN_T_SELL_25_SHARE_IN :{WHITE}25% podiel v tejto spoločnosti sa nedá predať ... STR_ERROR_PROTECTED :{WHITE}S akciami tejto spoločnosti nie je zatiaľ možné obchodovať... # Town related errors STR_ERROR_CAN_T_GENERATE_TOWN :{WHITE}Nie je možné postaviť viac miest STR_ERROR_CAN_T_RENAME_TOWN :{WHITE}Mesto nemôže byť odstránené... -STR_ERROR_CAN_T_FOUND_TOWN_HERE :{WHITE}Nemôžeš tu postaviť mesto... +STR_ERROR_CAN_T_FOUND_TOWN_HERE :{WHITE}Nemôžete tu založiť mesto... STR_ERROR_CAN_T_EXPAND_TOWN :{WHITE}Nemožno rozšíriť mesto... STR_ERROR_TOO_CLOSE_TO_EDGE_OF_MAP_SUB :{WHITE}... príliš blízko okraja mapy STR_ERROR_TOO_CLOSE_TO_ANOTHER_TOWN :{WHITE}... príliš blízko iného mesta @@ -4526,7 +4526,7 @@ STR_ERROR_NO_SUITABLE_PLACES_FOR_INDUSTRIES_EXPLANATION :{WHITE}Pre zís # Station construction related errors STR_ERROR_CAN_T_BUILD_RAILROAD_STATION :{WHITE}Nemôžete tu postaviť železničnú stanicu... STR_ERROR_CAN_T_BUILD_BUS_STATION :{WHITE}Nemôžete tu postaviť autobusovú zastávku... -STR_ERROR_CAN_T_BUILD_TRUCK_STATION :{WHITE}Nemôžeš tu postaviť vykládku... +STR_ERROR_CAN_T_BUILD_TRUCK_STATION :{WHITE}Nemôžete tu postaviť vykládku... STR_ERROR_CAN_T_BUILD_PASSENGER_TRAM_STATION :{WHITE}Nemôžete tu postaviť električkovú zastávku... STR_ERROR_CAN_T_BUILD_CARGO_TRAM_STATION :{WHITE}Nemôžete tu postaviť električkovú vykládku... STR_ERROR_CAN_T_BUILD_DOCK_HERE :{WHITE}Nemôžete tu postaviť prístav... @@ -4569,7 +4569,7 @@ STR_ERROR_WAYPOINT_ADJOINS_MORE_THAN_ONE_EXISTING :{WHITE}Susedí STR_ERROR_TOO_CLOSE_TO_ANOTHER_WAYPOINT :{WHITE}Príliš blízko iného smerovému bodu STR_ERROR_CAN_T_BUILD_TRAIN_WAYPOINT :{WHITE}Tu nie je možné postaviť železničný smerový bod... -STR_ERROR_CAN_T_POSITION_BUOY_HERE :{WHITE}Nemôžeš tu postaviť bóju... +STR_ERROR_CAN_T_POSITION_BUOY_HERE :{WHITE}Nemôžete tu umiestniť bóju... STR_ERROR_CAN_T_CHANGE_WAYPOINT_NAME :{WHITE}Názov smerového bodu sa nedá zmeniť... STR_ERROR_CAN_T_REMOVE_TRAIN_WAYPOINT :{WHITE}Tu nie je možné odstrániť železničný smerový bod... @@ -4580,8 +4580,8 @@ STR_ERROR_BUOY_IS_IN_USE :{WHITE}... bój # Depot related errors STR_ERROR_CAN_T_BUILD_TRAIN_DEPOT :{WHITE}Nemôžete tu postaviť vlakové depo... STR_ERROR_CAN_T_BUILD_ROAD_DEPOT :{WHITE}Nemôžete tu postaviť garáž... -STR_ERROR_CAN_T_BUILD_TRAM_DEPOT :{WHITE}Nemôžeš tu postaviť električkové depo... -STR_ERROR_CAN_T_BUILD_SHIP_DEPOT :{WHITE}Nemôžeš tu postaviť lodenicu... +STR_ERROR_CAN_T_BUILD_TRAM_DEPOT :{WHITE}Nemôžete tu postaviť električkovú garáž... +STR_ERROR_CAN_T_BUILD_SHIP_DEPOT :{WHITE}Nemôžete tu postaviť lodenicu... STR_ERROR_CAN_T_RENAME_DEPOT :{WHITE}Depo sa nedá premenovať... @@ -4618,7 +4618,7 @@ STR_ERROR_MUST_REMOVE_RAILROAD_TRACK :{WHITE}Musíš STR_ERROR_CROSSING_ON_ONEWAY_ROAD :{WHITE}Cesta je jednosmerná alebo blokovaná. STR_ERROR_CROSSING_DISALLOWED_RAIL :{WHITE}Nie sú povolené priecestia pre tento typ železnice STR_ERROR_CROSSING_DISALLOWED_ROAD :{WHITE}Nie sú povolené priecestia pre tento typ cesty -STR_ERROR_CAN_T_BUILD_SIGNALS_HERE :{WHITE}Nemôžeš tu umiestniť návestidlá... +STR_ERROR_CAN_T_BUILD_SIGNALS_HERE :{WHITE}Nemôžete tu umiestniť návestidlá... STR_ERROR_CAN_T_BUILD_RAILROAD_TRACK :{WHITE}Nemôžete tu postaviť železničnú trať... STR_ERROR_CAN_T_REMOVE_RAILROAD_TRACK :{WHITE}Nemôžeš tu odstrániť železničné koľaje... STR_ERROR_CAN_T_REMOVE_SIGNALS_FROM :{WHITE}Nemôžeš tu odstrániť návestidlá... @@ -4626,19 +4626,19 @@ STR_ERROR_SIGNAL_CAN_T_CONVERT_SIGNALS_HERE :{WHITE}Tu nie j STR_ERROR_THERE_IS_NO_RAILROAD_TRACK :{WHITE}...nenašli sa železničné koľaje STR_ERROR_THERE_ARE_NO_SIGNALS :{WHITE}... nenašli sa návestidlá -STR_ERROR_CAN_T_CONVERT_RAIL :{WHITE}Nemôžeš tu konvertovať železnicu... +STR_ERROR_CAN_T_CONVERT_RAIL :{WHITE}Nemôžete tu konvertovať železnicu... # Road construction errors STR_ERROR_MUST_REMOVE_ROAD_FIRST :{WHITE}Najprv treba odstrániť cestu STR_ERROR_ONEWAY_ROADS_CAN_T_HAVE_JUNCTION :{WHITE}... na jednosmerných cestách nie sú dovolené križovatky STR_ERROR_CAN_T_BUILD_ROAD_HERE :{WHITE}Nemôžete tu postaviť cestu... -STR_ERROR_CAN_T_BUILD_TRAMWAY_HERE :{WHITE}Nemôžeš tu stavať električkovú trať... +STR_ERROR_CAN_T_BUILD_TRAMWAY_HERE :{WHITE}Nemôžete tu postaviť električkovú trať... STR_ERROR_CAN_T_REMOVE_ROAD_FROM :{WHITE}Nemôžeš tu odstrániť cestu... STR_ERROR_CAN_T_REMOVE_TRAMWAY_FROM :{WHITE}Nemôžeš tu odstrániť električkovú trať... STR_ERROR_THERE_IS_NO_ROAD :{WHITE}... nenašla sa cesta STR_ERROR_THERE_IS_NO_TRAMWAY :{WHITE}...nenašla sa električková trať -STR_ERROR_CAN_T_CONVERT_ROAD :{WHITE}Nemôžeš tu konvertovať cestu... -STR_ERROR_CAN_T_CONVERT_TRAMWAY :{WHITE}Nemôžeš tu konvertovať električkovú trať... +STR_ERROR_CAN_T_CONVERT_ROAD :{WHITE}Nemôžete tu konvertovať cestu... +STR_ERROR_CAN_T_CONVERT_TRAMWAY :{WHITE}Nemôžete tu konvertovať električkovú trať... STR_ERROR_NO_SUITABLE_ROAD :{WHITE}Žiadna použiteľná cesta STR_ERROR_NO_SUITABLE_TRAMWAY :{WHITE}Žiadna použiteľná električková trať STR_ERROR_INCOMPATIBLE_TRAMWAY :{WHITE}... nekompatibilné električkové trate @@ -4658,7 +4658,7 @@ STR_ERROR_CAN_T_BUILD_AQUEDUCT_HERE :{WHITE}Nemôže # Tree related errors STR_ERROR_TREE_ALREADY_HERE :{WHITE}... strom tu už je STR_ERROR_TREE_WRONG_TERRAIN_FOR_TREE_TYPE :{WHITE}... zlý terén pre tento typ stromov -STR_ERROR_CAN_T_PLANT_TREE_HERE :{WHITE}Nemôžeš tu zasadiť strom... +STR_ERROR_CAN_T_PLANT_TREE_HERE :{WHITE}Nemôžete tu zasadiť stromy... # Bridge related errors STR_ERROR_CAN_T_BUILD_BRIDGE_HERE :{WHITE}Nemôžete tu postaviť most... @@ -4673,7 +4673,7 @@ STR_ERROR_BRIDGE_TOO_LONG :{WHITE}... most STR_ERROR_BRIDGE_THROUGH_MAP_BORDER :{WHITE}Most skončí za okrajom mapy # Tunnel related errors -STR_ERROR_CAN_T_BUILD_TUNNEL_HERE :{WHITE}Nemôžeš tu postaviť tunel... +STR_ERROR_CAN_T_BUILD_TUNNEL_HERE :{WHITE}Nemôžete tu postaviť tunel... STR_ERROR_SITE_UNSUITABLE_FOR_TUNNEL :{WHITE}Miesto nevhodné pre vjazd do tunela STR_ERROR_MUST_DEMOLISH_TUNNEL_FIRST :{WHITE}Tunel musi byť najskôr zbúraný STR_ERROR_ANOTHER_TUNNEL_IN_THE_WAY :{WHITE}Iný tunel v ceste @@ -4746,7 +4746,7 @@ STR_ERROR_SHIP_NOT_AVAILABLE :{WHITE}Loď nie STR_ERROR_AIRCRAFT_NOT_AVAILABLE :{WHITE}Lietadlo nie je dostupné STR_ERROR_TOO_MANY_VEHICLES_IN_GAME :{WHITE}V hre je príliš veľa dopravných prostriedkov -STR_ERROR_CAN_T_CHANGE_SERVICING :{WHITE}Nie je možné zmenit servisný interval ... +STR_ERROR_CAN_T_CHANGE_SERVICING :{WHITE}Nemožno zmeniť servisný interval... STR_ERROR_VEHICLE_IS_DESTROYED :{WHITE}... vozidlo je zničené @@ -4760,7 +4760,7 @@ STR_ERROR_CAN_T_MAKE_TRAIN_PASS_SIGNAL :{WHITE}Nemôže STR_ERROR_CAN_T_REVERSE_DIRECTION_TRAIN :{WHITE}Nemožno otočiť vlak naopak... STR_ERROR_TRAIN_START_NO_POWER :Vlak nemá energiu -STR_ERROR_CAN_T_MAKE_ROAD_VEHICLE_TURN :{WHITE}Nemôžem otočiť cestné vozidlo... +STR_ERROR_CAN_T_MAKE_ROAD_VEHICLE_TURN :{WHITE}Nemožno otočiť cestné vozidlo... STR_ERROR_AIRCRAFT_IS_IN_FLIGHT :{WHITE}Lietadlo je vo vzduchu @@ -4792,7 +4792,7 @@ STR_ERROR_TIMETABLE_NOT_STOPPING_HERE :{WHITE}Toto voz STR_ERROR_TOO_MANY_SIGNS :{WHITE}... príliš veľa popisov STR_ERROR_CAN_T_PLACE_SIGN_HERE :{WHITE}Tu sa nedá umiestniť popis... STR_ERROR_CAN_T_CHANGE_SIGN_NAME :{WHITE}Nemožno zmeniť text popisu... -STR_ERROR_CAN_T_DELETE_SIGN :{WHITE}Nemôžem zmazať popis... +STR_ERROR_CAN_T_DELETE_SIGN :{WHITE}Nemožno vymazať popis... # Translatable comment for OpenTTD's desktop shortcut STR_DESKTOP_SHORTCUT_COMMENT :Simulátor založený na hre Transport Tycoon Deluxe diff --git a/src/lang/turkish.txt b/src/lang/turkish.txt index cbedd61fae..76d30a7a6c 100644 --- a/src/lang/turkish.txt +++ b/src/lang/turkish.txt @@ -195,6 +195,7 @@ STR_COLOUR_DEFAULT :Varsayılan STR_UNITS_VELOCITY_IMPERIAL :{COMMA}{NBSP}mil/s STR_UNITS_VELOCITY_METRIC :{COMMA}{NBSP}km/s STR_UNITS_VELOCITY_SI :{COMMA}{NBSP}m/s +STR_UNITS_VELOCITY_GAMEUNITS :{DECIMAL}{NBSP}karo/gün STR_UNITS_POWER_IMPERIAL :{COMMA}{NBSP}bg STR_UNITS_POWER_METRIC :{COMMA}{NBSP}bg @@ -313,8 +314,15 @@ STR_SORT_BY_CARGO_CAPACITY :Kargo kapasites STR_SORT_BY_RANGE :Menzil STR_SORT_BY_POPULATION :Nüfus STR_SORT_BY_RATING :Değerlendirme +STR_SORT_BY_NUM_VEHICLES :Araç sayısı +STR_SORT_BY_TOTAL_PROFIT_LAST_YEAR :Geçen yılki toplam kar +STR_SORT_BY_TOTAL_PROFIT_THIS_YEAR :Bu yılki toplam kar +STR_SORT_BY_AVERAGE_PROFIT_LAST_YEAR :Geçen yılki ortalama kar +STR_SORT_BY_AVERAGE_PROFIT_THIS_YEAR :Bu yılki ortalama kar # Group by options for vehicle list +STR_GROUP_BY_NONE :Hiçbiri +STR_GROUP_BY_SHARED_ORDERS :Paylaşılan talimatlar # Tooltips for the main toolbar STR_TOOLBAR_TOOLTIP_PAUSE_GAME :{BLACK}Oyunu durdur @@ -688,7 +696,7 @@ STR_PLAYLIST_TOOLTIP_CLICK_TO_REMOVE_TRACK :{BLACK}Kullanı STR_HIGHSCORE_TOP_COMPANIES_WHO_REACHED :{BIG_FONT}{BLACK} {NUM} STR_HIGHSCORE_TOP_COMPANIES_NETWORK_GAME :{BIG_FONT}{BLACK} {NUM} senesindeki şirket ligi STR_HIGHSCORE_POSITION :{BIG_FONT}{BLACK}{COMMA}. -STR_HIGHSCORE_PERFORMANCE_TITLE_BUSINESSMAN :İşadamı +STR_HIGHSCORE_PERFORMANCE_TITLE_BUSINESSMAN :İş insanı STR_HIGHSCORE_PERFORMANCE_TITLE_ENTREPRENEUR :Girişimci STR_HIGHSCORE_PERFORMANCE_TITLE_INDUSTRIALIST :Sanayici STR_HIGHSCORE_PERFORMANCE_TITLE_CAPITALIST :Sermayedar @@ -740,6 +748,7 @@ STR_SMALLMAP_LEGENDA_DOCK :{TINY_FONT}{BLA STR_SMALLMAP_LEGENDA_ROUGH_LAND :{TINY_FONT}{BLACK}Engebeli Alan STR_SMALLMAP_LEGENDA_GRASS_LAND :{TINY_FONT}{BLACK}Çim alan STR_SMALLMAP_LEGENDA_BARE_LAND :{TINY_FONT}{BLACK}Çıplak Arazi +STR_SMALLMAP_LEGENDA_RAINFOREST :{TINY_FONT}{BLACK}Yağmur ormanı STR_SMALLMAP_LEGENDA_FIELDS :{TINY_FONT}{BLACK}Meralar STR_SMALLMAP_LEGENDA_TREES :{TINY_FONT}{BLACK}Ağaçlar STR_SMALLMAP_LEGENDA_ROCKS :{TINY_FONT}{BLACK}Kayalar @@ -771,6 +780,7 @@ STR_SMALLMAP_TOOLTIP_ENABLE_ALL_CARGOS :{BLACK}Haritada STR_STATUSBAR_TOOLTIP_SHOW_LAST_NEWS :{BLACK}Son mesajı ya da haberi göster STR_STATUSBAR_COMPANY_NAME :{SILVER}- - {COMPANY} - - STR_STATUSBAR_PAUSED :{YELLOW}* * DURAKLATILDI * * +STR_STATUSBAR_PAUSED_LINK_GRAPH :{ORANGE}* * DURDU (bağlantı grafiğinin güncellenmesi bekleniyor) * * STR_STATUSBAR_AUTOSAVE :{RED}OTOMATİK KAYDET STR_STATUSBAR_SAVING_GAME :{RED}* * KAYDEDiYOR * * @@ -937,13 +947,14 @@ STR_GAME_OPTIONS_CURRENCY_NTD :Yeni Tayvan Dol STR_GAME_OPTIONS_CURRENCY_CNY :Çin Yuanı (CNY) STR_GAME_OPTIONS_CURRENCY_HKD :Hong Kong Doları (HKD) STR_GAME_OPTIONS_CURRENCY_INR :Hindistan Rupisi (INR) +STR_GAME_OPTIONS_CURRENCY_IDR :Endonezya Rupiahı (IDR) STR_GAME_OPTIONS_CURRENCY_MYR :Malezya Ringgiti (MYR) ############ end of currency region STR_GAME_OPTIONS_ROAD_VEHICLES_DROPDOWN_LEFT :Soldan trafik STR_GAME_OPTIONS_ROAD_VEHICLES_DROPDOWN_RIGHT :Sağdan trafik -STR_GAME_OPTIONS_TOWN_NAMES_FRAME :{BLACK}Şehir isimleri +STR_GAME_OPTIONS_TOWN_NAMES_FRAME :{BLACK}Şehir isimleri: STR_GAME_OPTIONS_TOWN_NAMES_DROPDOWN_TOOLTIP :{BLACK}Şehir isimleri için bir tür seçin ############ start of townname region @@ -983,6 +994,7 @@ STR_GAME_OPTIONS_AUTOSAVE_DROPDOWN_EVERY_12_MONTHS :Her oniki ayda STR_GAME_OPTIONS_LANGUAGE :{BLACK}Dil STR_GAME_OPTIONS_LANGUAGE_TOOLTIP :{BLACK}Görünen dili seçin +STR_GAME_OPTIONS_LANGUAGE_PERCENTAGE :{STRING} (%{NUM} tamamlandı) STR_GAME_OPTIONS_FULLSCREEN :{BLACK}Tam ekran STR_GAME_OPTIONS_FULLSCREEN_TOOLTIP :{BLACK}Tam ekran oynamak için bunu isaretleyin @@ -992,11 +1004,17 @@ STR_GAME_OPTIONS_RESOLUTION_TOOLTIP :{BLACK}Kullanı STR_GAME_OPTIONS_RESOLUTION_OTHER :diğer STR_GAME_OPTIONS_RESOLUTION_ITEM :{NUM}x{NUM} +STR_GAME_OPTIONS_VIDEO_ACCELERATION :{BLACK}Donanım hızlandırma +STR_GAME_OPTIONS_VIDEO_ACCELERATION_TOOLTIP :{BLACK}OpenTTD'nin donanım ivmesini kullanmayı denemesine izin vermek için bu kutuyu işaretleyin. Değiştirilmiş bir ayar sadece oyun yeniden başlatıldığında uygulanır +STR_GAME_OPTIONS_VIDEO_ACCELERATION_RESTART :{WHITE}Ayar sadece oyun yeniden başlatıldığında uygulanır +STR_GAME_OPTIONS_VIDEO_VSYNC :{BLACK}VSync +STR_GAME_OPTIONS_VIDEO_VSYNC_TOOLTIP :{BLACK}Dikey senkronizasyon'u aktif etmek için bu kutuyu işaretleyin. Değiştirilmiş bir ayar sadece oyun yeniden başlatıldığında uygulanır. Yalnızca donanım ivmesi aktifken çalışır STR_GAME_OPTIONS_GUI_ZOOM_FRAME :{BLACK}Arayüz boyutu STR_GAME_OPTIONS_GUI_ZOOM_DROPDOWN_TOOLTIP :{BLACK}Kullanmak üzere arayüz bileşen boyutunu seçin +STR_GAME_OPTIONS_GUI_ZOOM_DROPDOWN_AUTO :(otomatik-tespit) STR_GAME_OPTIONS_GUI_ZOOM_DROPDOWN_NORMAL :Normal STR_GAME_OPTIONS_GUI_ZOOM_DROPDOWN_2X_ZOOM :İki kat büyük STR_GAME_OPTIONS_GUI_ZOOM_DROPDOWN_4X_ZOOM :Dört kat büyük @@ -1004,11 +1022,18 @@ STR_GAME_OPTIONS_GUI_ZOOM_DROPDOWN_4X_ZOOM :Dört kat büy STR_GAME_OPTIONS_FONT_ZOOM :{BLACK}Yazı boyutu STR_GAME_OPTIONS_FONT_ZOOM_DROPDOWN_TOOLTIP :Arayüz boyutunu seç +STR_GAME_OPTIONS_FONT_ZOOM_DROPDOWN_AUTO :(otomatik-tespit) STR_GAME_OPTIONS_FONT_ZOOM_DROPDOWN_NORMAL :Normal STR_GAME_OPTIONS_FONT_ZOOM_DROPDOWN_2X_ZOOM :Yazı iki kat büyük STR_GAME_OPTIONS_FONT_ZOOM_DROPDOWN_4X_ZOOM :Dört kat büyük +STR_GAME_OPTIONS_GRAPHICS :{BLACK}Grafikler +STR_GAME_OPTIONS_REFRESH_RATE :{BLACK}Tazeleme oranını görüntüle +STR_GAME_OPTIONS_REFRESH_RATE_TOOLTIP :{BLACK}Kullanılacak tazeleme oranını seç +STR_GAME_OPTIONS_REFRESH_RATE_OTHER :diğer +STR_GAME_OPTIONS_REFRESH_RATE_ITEM :{NUM}Hz +STR_GAME_OPTIONS_REFRESH_RATE_WARNING :{WHITE}60Hz'den yüksek tazeleme oranları performansı etkileyebilir. STR_GAME_OPTIONS_BASE_GRF :{BLACK}Temel grafik kümesi STR_GAME_OPTIONS_BASE_GRF_TOOLTIP :{BLACK}Kullanılacak temel grafik kümesini seçin @@ -1104,6 +1129,8 @@ STR_TERRAIN_TYPE_FLAT :Düz STR_TERRAIN_TYPE_HILLY :Engebeli STR_TERRAIN_TYPE_MOUNTAINOUS :Dağlık STR_TERRAIN_TYPE_ALPINIST :Alp Meraklısı +STR_TERRAIN_TYPE_CUSTOM :Özel yükseklik +STR_TERRAIN_TYPE_CUSTOM_VALUE :Özel yükseklik ({NUM}) STR_CITY_APPROVAL_PERMISSIVE :İzne tabi STR_CITY_APPROVAL_TOLERANT :Töleranslı @@ -1116,6 +1143,7 @@ STR_CONFIG_SETTING_TREE_CAPTION :{WHITE}Ayarlar STR_CONFIG_SETTING_FILTER_TITLE :{BLACK}Süzgeç metni: STR_CONFIG_SETTING_EXPAND_ALL :{BLACK}Tümünü genişlet STR_CONFIG_SETTING_COLLAPSE_ALL :{BLACK}Tümünü kısalt +STR_CONFIG_SETTING_RESET_ALL :{BLACK}Tüm değerleri sıfırla STR_CONFIG_SETTING_NO_EXPLANATION_AVAILABLE_HELPTEXT :(açıklama bulunmamaktadır) STR_CONFIG_SETTING_DEFAULT_VALUE :{LTBLUE}Varsayılan değer: {ORANGE}{STRING} STR_CONFIG_SETTING_TYPE :{LTBLUE}Ayar türü: {ORANGE}{STRING} @@ -1124,6 +1152,8 @@ STR_CONFIG_SETTING_TYPE_GAME_MENU :Kullanıcı aya STR_CONFIG_SETTING_TYPE_GAME_INGAME :Oyun ayarları (kayıtlı dosyada saklanır; sadece mevcut oyunu etkilemektedir) STR_CONFIG_SETTING_TYPE_COMPANY_MENU :Şirket ayarları (kayıtlı dosyada saklanır; sadece yeni oyunu etkilemektedir) STR_CONFIG_SETTING_TYPE_COMPANY_INGAME :Şirket ayarları (kayıtlı dosyada saklanır; sadece mevcut şirketi etkilemektedir) +STR_CONFIG_SETTING_RESET_ALL_CONFIRMATION_DIALOG_CAPTION :{WHITE}Dikkat! +STR_CONFIG_SETTING_RESET_ALL_CONFIRMATION_DIALOG_TEXT :{WHITE}Bu eylem tüm oyun ayarlarını varsayılan değerlerine sıfırlayacaktır.{}Devam etmek istediğine emin misin? STR_CONFIG_SETTING_RESTRICT_CATEGORY :{BLACK}Kategori: STR_CONFIG_SETTING_RESTRICT_TYPE :{BLACK}Tür: @@ -1185,6 +1215,10 @@ STR_CONFIG_SETTING_DISASTERS_HELPTEXT :Araçları ya d STR_CONFIG_SETTING_CITY_APPROVAL :Arazi şekillendirmeye karşı belediye meclisinin tavrı: {STRING} STR_CONFIG_SETTING_CITY_APPROVAL_HELPTEXT :Şirketlerin sebep olduğu gürültü ve çevreye zararın kasaba beğenilerini ve ilerideki inşaatlarını nasıl etkileyeceğini seçin +STR_CONFIG_SETTING_MAP_HEIGHT_LIMIT :Harita azami yükseklik limiti: {STRING} +STR_CONFIG_SETTING_MAP_HEIGHT_LIMIT_HELPTEXT :Harita arazisinin azami yüksekliğini ayarlayın. "(otomatik)" ile arazi oluşturulduktan sonra güzel bir değer seçilecektir +STR_CONFIG_SETTING_MAP_HEIGHT_LIMIT_VALUE :{NUM} +STR_CONFIG_SETTING_MAP_HEIGHT_LIMIT_AUTO :(otomatik) STR_CONFIG_SETTING_TOO_HIGH_MOUNTAIN :{WHITE}Azami harita yüksekliğini bu değere ayarlayamazsınız. Haritadaki en az bir dağ bu değerden yüksek STR_CONFIG_SETTING_AUTOSLOPE :Binaların, yolların vb. altındaki araziyi değiştirmeye izin ver: {STRING} STR_CONFIG_SETTING_AUTOSLOPE_HELPTEXT :Binaları ve yolları kaldırmaksızın altlarında yeryüzü şekillendirmesi yapılmasına izin ver @@ -1329,7 +1363,13 @@ STR_CONFIG_SETTING_INDUSTRY_DENSITY_HELPTEXT :Oyun boyunca ka STR_CONFIG_SETTING_OIL_REF_EDGE_DISTANCE :Petrol rafinerilerinin kenarlardan azami uzaklığı: {STRING} STR_CONFIG_SETTING_OIL_REF_EDGE_DISTANCE_HELPTEXT :Petrol rafinerileri sadece haritanın sınırlarında inşa edilir; ada haritalarında sahillere kurulurlar. STR_CONFIG_SETTING_SNOWLINE_HEIGHT :Kar kalınlığı: {STRING} -STR_CONFIG_SETTING_SNOWLINE_HEIGHT_HELPTEXT :Kutupsal arazide, karın ne kadar yükseklikten başlayacağını denetleyin. Kar aynı zamanda fabrika oluşumunu ve şehir gelişme gereksinimini de etkiler +STR_CONFIG_SETTING_SNOWLINE_HEIGHT_HELPTEXT :Kutupsal arazide, karın ne kadar yükseklikten başlayacağını denetleyin. Kar aynı zamanda fabrika oluşumunu ve şehir gelişme gereksinimini de etkiler. Yalnızca Senerya Editörü ile değiştirilebilir veya "kar örtüsü" üzerinden hesaplanır +STR_CONFIG_SETTING_SNOW_COVERAGE :Kar örtüsü: {STRING} +STR_CONFIG_SETTING_SNOW_COVERAGE_HELPTEXT :Arktik altındaki yaklaşık kar miktarını kontrol eder. Kar, aynı zamanda endüstri üretimini ve kasaba büyüme gereksinimlerini de etkiler. Yalnızca harita oluşumu sırasında kullanılır. Deniz seviyesinin hemen üzerindeki arazi her zaman karsızdır +STR_CONFIG_SETTING_SNOW_COVERAGE_VALUE :%{NUM} +STR_CONFIG_SETTING_DESERT_COVERAGE :Çöl örtüsü: {STRING} +STR_CONFIG_SETTING_DESERT_COVERAGE_HELPTEXT :Tropikal arazideki yaklaşık çöl miktarını kontrol eder. Çöl ayrıca endüstri üretimini de etkiler. Yalnızca harita oluşturma sırasında kullanılır +STR_CONFIG_SETTING_DESERT_COVERAGE_VALUE :%{NUM} STR_CONFIG_SETTING_ROUGHNESS_OF_TERRAIN :Arazinin engebesi: {STRING} STR_CONFIG_SETTING_ROUGHNESS_OF_TERRAIN_HELPTEXT :(Sadece TerraGenesis) Tepelerin sıklığını seçin: Yumuşak yerleşimler daha az, geniş alana yayılmış tepelere sahiptir. Sıkı yerleşimler tekrarlanmış gibi görülen çok sayıda tepeye sahip olacaklar. STR_CONFIG_SETTING_ROUGHNESS_OF_TERRAIN_VERY_SMOOTH :Dümdüz @@ -1443,6 +1483,11 @@ STR_CONFIG_SETTING_PERSISTENT_BUILDINGTOOLS :Yapı araçlar STR_CONFIG_SETTING_PERSISTENT_BUILDINGTOOLS_HELPTEXT :Köprü, tünel vb. için kullanılan inşa araçlarını kullanımdan sonra da açık tut STR_CONFIG_SETTING_EXPENSES_LAYOUT :Şirket mali tablosunda grup harcamaları: {STRING} STR_CONFIG_SETTING_EXPENSES_LAYOUT_HELPTEXT :Şirket harcamaları penceresinin nasıl düzenleneceğini belirle +STR_CONFIG_SETTING_AUTO_REMOVE_SIGNALS :Ray yapımı sırasında sinyalleri otomatik olarak kaldır: {STRING} +STR_CONFIG_SETTING_AUTO_REMOVE_SIGNALS_HELPTEXT :Ray yapımı sırasında sinyaller yolun üzerinde ise otomatik kaldır. Bunun potansiyel olarak tren kazalarına yol açabileceğini unutmayın. +STR_CONFIG_SETTING_FAST_FORWARD_SPEED_LIMIT :İleri sarma hız limiti: {STRING} +STR_CONFIG_SETTING_FAST_FORWARD_SPEED_LIMIT_HELPTEXT :Hızlı ileri sarma etkinleştirildiğinde oyunun ne kadar hızlı sarıldığını sınırlayın. 0 = sınır yok (bilgisayarın el verdiği kadar). %100'ün altındaki değerler oyunu yavaşlatır. Üst sınır bilgisayarının özelliklerine ve oyuna göre değişkenlik gösterebilir. +STR_CONFIG_SETTING_FAST_FORWARD_SPEED_LIMIT_VAL :%{NUM} normal oyun hızı STR_CONFIG_SETTING_FAST_FORWARD_SPEED_LIMIT_ZERO :Limit yok (bilgisayarın izin verdiği maksimum hızda ) STR_CONFIG_SETTING_SOUND_TICKER :Kayan haber bandı: {STRING} @@ -1557,6 +1602,11 @@ STR_CONFIG_SETTING_ENDING_YEAR :Yıl sonu puan STR_CONFIG_SETTING_ENDING_YEAR_HELPTEXT :Oyunun puan sonucuna göre bittiği yıl. Bu yılın sonunda, şirketin puanı kaydedilir ve yüksek puan ekranında gösterilir ama oyuncular oynamaya devam edebilir.{}Eğer bu başlama yılından önce ise, yüksek puan ekranı gösterilmez. STR_CONFIG_SETTING_ENDING_YEAR_VALUE :{NUM} STR_CONFIG_SETTING_ENDING_YEAR_ZERO :Asla +STR_CONFIG_SETTING_ECONOMY_TYPE :Ekonomi türü: {STRING} +STR_CONFIG_SETTING_ECONOMY_TYPE_HELPTEXT :Sorunsuz ekonomi, üretim değişikliklerini daha sık ve daha küçük adımlarla yapar. Donuk ekonomi, üretim değişikliklerini ve endüstri kapanışlarını durdurur. Sektör türleri bir NewGRF tarafından sağlanırsa bu ayarın hiçbir etkisi olmayabilir. +STR_CONFIG_SETTING_ECONOMY_TYPE_ORIGINAL :Orijinal +STR_CONFIG_SETTING_ECONOMY_TYPE_SMOOTH :Pürüzsüz +STR_CONFIG_SETTING_ECONOMY_TYPE_FROZEN :Donuk STR_CONFIG_SETTING_ALLOW_SHARES :Diğer şirketlerin hisseleri alınabilsin: {STRING} STR_CONFIG_SETTING_ALLOW_SHARES_HELPTEXT :Etkinleştirildiğinde, şirketlerin hisse senetlerinin alınıp satılması mümkün olur. Hisse senetleri sadece belli bir yaşa ulaşan şirketler için geçerlidir STR_CONFIG_SETTING_MIN_YEARS_FOR_SHARES :Hisse satmak için gerekli minimum şirket yaşı: {STRING} @@ -1608,6 +1658,10 @@ STR_CONFIG_SETTING_TOWN_CARGOGENMODE_BITCOUNT :Doğrusal STR_CONFIG_SETTING_EXTRA_TREE_PLACEMENT :Oyunda ağaç dikme: {STRING} STR_CONFIG_SETTING_EXTRA_TREE_PLACEMENT_HELPTEXT :Oyundaki rastgele ağaçların görünümünü kontrol eder. Bu, ağaçların büyümesine bağımlı olan endüstrileri etkileyebilir, örneğin keresteciler gibi +STR_CONFIG_SETTING_EXTRA_TREE_PLACEMENT_NO_SPREAD :Büyü ama yayılma {RED}(kereste fabrikasını kırar) +STR_CONFIG_SETTING_EXTRA_TREE_PLACEMENT_SPREAD_RAINFOREST :Büyü ama sadece yağmur ormanlarında yayıl +STR_CONFIG_SETTING_EXTRA_TREE_PLACEMENT_SPREAD_ALL :Büyü ve her yere yayıl +STR_CONFIG_SETTING_EXTRA_TREE_PLACEMENT_NO_GROWTH_NO_SPREAD :Büyüme, yayılma {RED}(kereste fabrikasını kırar) STR_CONFIG_SETTING_TOOLBAR_POS :Ana araç çubuğu konumu: {STRING} STR_CONFIG_SETTING_TOOLBAR_POS_HELPTEXT :Ekranın üst kısmındaki ana araç çubuğunun yatay konumu @@ -1625,12 +1679,17 @@ STR_CONFIG_SETTING_ZOOM_MIN :Azami yaklaşma STR_CONFIG_SETTING_ZOOM_MIN_HELPTEXT :Görüş alanları için azami yakınlaşma seviyesi. Daha yüksek uzaklaştırma ayarları oyunda gecikmelere sebep olabilir STR_CONFIG_SETTING_ZOOM_MAX :Azami uzaklaşma seviyesi: {STRING} STR_CONFIG_SETTING_ZOOM_MAX_HELPTEXT :Görüş alanları için azami uzaklaşma seviyesi. Daha yüksek uzaklaştırma ayarları oyunda gecikmelere sebep olabilir +STR_CONFIG_SETTING_SPRITE_ZOOM_MIN :Kullanılacak en yüksek çözünürlüklü sprite'lar: {STRING} +STR_CONFIG_SETTING_SPRITE_ZOOM_MIN_HELPTEXT :Sprite'lar için kullanılacak maksimum çözünürlüğü sınırlar. Sprite çözünürlüğünü sınırlamak, mevcut olunca bile yüksek çözünürlüklü grafikleri kullanmaktan kaçınacaktır. Bu, yüksek çözünürlüklü grafikleri içeren ve içermeyen GRF dosyalarının bir karşımı kullanılırken oyun görünümünü bir arada tutmaya yardımcı olabilir. STR_CONFIG_SETTING_ZOOM_LVL_MIN :4x STR_CONFIG_SETTING_ZOOM_LVL_IN_2X :2x STR_CONFIG_SETTING_ZOOM_LVL_NORMAL :Normal STR_CONFIG_SETTING_ZOOM_LVL_OUT_2X :2x STR_CONFIG_SETTING_ZOOM_LVL_OUT_4X :4x STR_CONFIG_SETTING_ZOOM_LVL_OUT_8X :8x +STR_CONFIG_SETTING_SPRITE_ZOOM_LVL_MIN :4x +STR_CONFIG_SETTING_SPRITE_ZOOM_LVL_IN_2X :2x +STR_CONFIG_SETTING_SPRITE_ZOOM_LVL_NORMAL :1x STR_CONFIG_SETTING_TOWN_GROWTH :Şehirlerin genişleme hızı: {STRING} STR_CONFIG_SETTING_TOWN_GROWTH_HELPTEXT :Şehir büyüme hızı STR_CONFIG_SETTING_TOWN_GROWTH_NONE :Hiçbiri @@ -1674,6 +1733,7 @@ STR_CONFIG_SETTING_LOCALISATION_UNITS_VELOCITY_HELPTEXT :Kullanıcı ara STR_CONFIG_SETTING_LOCALISATION_UNITS_VELOCITY_IMPERIAL :Imperial (İngiliz ölçü birimleri) (mph) STR_CONFIG_SETTING_LOCALISATION_UNITS_VELOCITY_METRIC :Metrik (km/s) STR_CONFIG_SETTING_LOCALISATION_UNITS_VELOCITY_SI :SI (Uluslararası Ölçüm Sistemi) (m/s) +STR_CONFIG_SETTING_LOCALISATION_UNITS_VELOCITY_GAMEUNITS :Oyun birimleri (karo/gün) STR_CONFIG_SETTING_LOCALISATION_UNITS_POWER :Araç gücü ölçü birimi: {STRING} STR_CONFIG_SETTING_LOCALISATION_UNITS_POWER_HELPTEXT :Kullanıcı arayüzünde bir aracın gücü görüntülendiğinde, bunu seçili ölçü biriminde göster. @@ -1764,6 +1824,8 @@ STR_CONFIG_ERROR_OUT_OF_MEMORY :{WHITE}Bellek y STR_CONFIG_ERROR_SPRITECACHE_TOO_BIG :{WHITE}{BYTES} sprite-önbelleği ayırma işlemi başarısız. Sprite-önbelleği {BYTES}'a düşürüldü. Bu OpenTTD'nin performansını azaltacak. Bellek gereksinimini azaltmak için 32bpp grafikleri ve/veya yakınlaştırma seviyelerini kapatmayı deneyebilirsiniz # Video initalization errors +STR_VIDEO_DRIVER_ERROR :{WHITE}Video ayarlarında hata... +STR_VIDEO_DRIVER_ERROR_NO_HARDWARE_ACCELERATION :{WHITE}... uyumlu GPU bulunamadı. Donanım hızlandırma devre dışı bırakıldı # Intro window STR_INTRO_CAPTION :{WHITE}OpenTTD {REV} @@ -1808,6 +1870,7 @@ STR_INTRO_TRANSLATION :{BLACK}Bu çevi # Quit window STR_QUIT_CAPTION :{WHITE}Çıkış +STR_QUIT_ARE_YOU_SURE_YOU_WANT_TO_EXIT_OPENTTD :{YELLOW}Oyundan çıkmak istediğinize emin misiniz? STR_QUIT_YES :{BLACK}Evet STR_QUIT_NO :{BLACK}Hayır @@ -1819,6 +1882,7 @@ STR_ABANDON_SCENARIO_QUERY :{YELLOW}Bu sena # Cheat window STR_CHEATS :{WHITE}Hileler STR_CHEATS_TOOLTIP :{BLACK}Onay kutuları bu hilenin daha önce kullanılıp kullanmadığını bildirir. +STR_CHEATS_NOTE :{BLACK}Not: bu ayarların herhangi bir kullanımı kayıt oyunu tarafından kaydedilecektir STR_CHEAT_MONEY :{LTBLUE}Parayı {CURRENCY_LONG} kadar arttır STR_CHEAT_CHANGE_COMPANY :{LTBLUE}Oynanan şirket: {ORANGE}{COMMA} STR_CHEAT_EXTRA_DYNAMITE :{LTBLUE}Sihirli buldozer (fabrikaları, silinemeyen nesneleri siler): {ORANGE}{STRING} @@ -1928,6 +1992,8 @@ STR_FACE_TIE :Kravat: STR_FACE_EARRING :Küpe: STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}Kravatı veya küpeyi değiştir +STR_NETWORK_SERVER_VISIBILITY_PRIVATE :Özel +STR_NETWORK_SERVER_VISIBILITY_PUBLIC :Halka açık # Network server list STR_NETWORK_SERVER_LIST_CAPTION :{WHITE}Çok Oyunculu @@ -1971,6 +2037,10 @@ STR_NETWORK_SERVER_LIST_JOIN_GAME :{BLACK}Oyuna gi STR_NETWORK_SERVER_LIST_REFRESH :{BLACK}Sunucuyu tazele STR_NETWORK_SERVER_LIST_REFRESH_TOOLTIP :{BLACK}Sunucu bilgisini tazele +STR_NETWORK_SERVER_LIST_SEARCH_SERVER_INTERNET :{BLACK}İnterneti Ara +STR_NETWORK_SERVER_LIST_SEARCH_SERVER_INTERNET_TOOLTIP :{BLACK}İnternette halka açık sunucu ara +STR_NETWORK_SERVER_LIST_SEARCH_SERVER_LAN :{BLACK}LAN ara +STR_NETWORK_SERVER_LIST_SEARCH_SERVER_LAN_TOOLTIP :{BLACK}Yerel alan ağında sunucu ara STR_NETWORK_SERVER_LIST_ADD_SERVER :{BLACK}Sunucu ekle STR_NETWORK_SERVER_LIST_ADD_SERVER_TOOLTIP :{BLACK}Her zaman çalışan oyunlarına bakabilmek için bir sunucu ekle STR_NETWORK_SERVER_LIST_START_SERVER :{BLACK}Sunucu başlat @@ -1987,6 +2057,8 @@ STR_NETWORK_START_SERVER_NEW_GAME_NAME_TOOLTIP :{BLACK}Bu oyun STR_NETWORK_START_SERVER_SET_PASSWORD :{BLACK}Parola koy STR_NETWORK_START_SERVER_PASSWORD_TOOLTIP :{BLACK}Erişimi kısıtlamak için oyuna parola koy +STR_NETWORK_START_SERVER_VISIBILITY_LABEL :{BLACK}Görünürlük +STR_NETWORK_START_SERVER_VISIBILITY_TOOLTIP :{BLACK}Halka açık listelemede öbür oyuncuların sizin sunucunuzu görüp göremeyeceği STR_NETWORK_START_SERVER_CLIENTS_SELECT :{BLACK}{NUM} istemci STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS :{BLACK}Azami istemci sayısı: STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS_TOOLTIP :{BLACK}İzin verilen en fazla oyuncu sayısını seç. Her yerin dolması gerekmez @@ -2050,11 +2122,44 @@ STR_NETWORK_NEED_GAME_PASSWORD_CAPTION :{WHITE}Sunucu k STR_NETWORK_NEED_COMPANY_PASSWORD_CAPTION :{WHITE}Şirket korumalı. Parola girin # Network company list added strings -STR_NETWORK_COMPANY_LIST_CLIENT_LIST :Oyuncu listesi +STR_NETWORK_COMPANY_LIST_CLIENT_LIST :Aktif oyuncular # Network client list - - +STR_NETWORK_CLIENT_LIST_CAPTION :{WHITE}Çok Oyunculu +STR_NETWORK_CLIENT_LIST_SERVER :{BLACK}Sunucu +STR_NETWORK_CLIENT_LIST_SERVER_NAME :{BLACK}İsim +STR_NETWORK_CLIENT_LIST_SERVER_NAME_TOOLTIP :{BLACK}Oynadığın sunucunun adı +STR_NETWORK_CLIENT_LIST_SERVER_NAME_EDIT_TOOLTIP :{BLACK}Sunucunun adını değiştir +STR_NETWORK_CLIENT_LIST_SERVER_NAME_QUERY_CAPTION :Sunucunun adı +STR_NETWORK_CLIENT_LIST_SERVER_VISIBILITY :{BLACK}Görünürlük +STR_NETWORK_CLIENT_LIST_SERVER_VISIBILITY_TOOLTIP :{BLACK}Halka açık listelemede öbür oyuncuların sizin sunucunuzu görüp göremeyeceği +STR_NETWORK_CLIENT_LIST_PLAYER :{BLACK}Oyuncu +STR_NETWORK_CLIENT_LIST_PLAYER_NAME :{BLACK}İsim +STR_NETWORK_CLIENT_LIST_PLAYER_NAME_TOOLTIP :{BLACK}İsmin +STR_NETWORK_CLIENT_LIST_PLAYER_NAME_EDIT_TOOLTIP :{BLACK}İsmini değiştir +STR_NETWORK_CLIENT_LIST_PLAYER_NAME_QUERY_CAPTION :İsmin +STR_NETWORK_CLIENT_LIST_ADMIN_CLIENT_TOOLTIP :{BLACK}Client için gerçekleştirilecek yönetici eylemleri +STR_NETWORK_CLIENT_LIST_ADMIN_COMPANY_TOOLTIP :{BLACK}Şirket için gerçekleştirilecek yönetici eylemleri +STR_NETWORK_CLIENT_LIST_JOIN_TOOLTIP :{BLACK}Bu şirkete katıl +STR_NETWORK_CLIENT_LIST_CHAT_CLIENT_TOOLTIP :{BLACK}Bu oyuncuya bir mesaj gönder +STR_NETWORK_CLIENT_LIST_CHAT_COMPANY_TOOLTIP :{BLACK}Bu şirketteki tüm oyunculara bir mesaj gönder +STR_NETWORK_CLIENT_LIST_CHAT_SPECTATOR_TOOLTIP :{BLACK}Bütün izleyicilere bir mesaj yolla +STR_NETWORK_CLIENT_LIST_SPECTATORS :İzleyiciler +STR_NETWORK_CLIENT_LIST_NEW_COMPANY :(Yeni şirket) +STR_NETWORK_CLIENT_LIST_NEW_COMPANY_TOOLTIP :{BLACK}Yeni bir şirket oluştur ve ona katıl +STR_NETWORK_CLIENT_LIST_PLAYER_ICON_SELF_TOOLTIP :{BLACK}Bu sensin +STR_NETWORK_CLIENT_LIST_PLAYER_ICON_HOST_TOOLTIP :{BLACK}Bu, oyunun ev sahibi + +STR_NETWORK_CLIENT_LIST_ADMIN_CLIENT_KICK :At +STR_NETWORK_CLIENT_LIST_ADMIN_CLIENT_BAN :Yasakla +STR_NETWORK_CLIENT_LIST_ADMIN_COMPANY_RESET :Sil +STR_NETWORK_CLIENT_LIST_ADMIN_COMPANY_UNLOCK :Parola ile kilit açma + +STR_NETWORK_CLIENT_LIST_ASK_CAPTION :{WHITE}Yönetici eylemi +STR_NETWORK_CLIENT_LIST_ASK_CLIENT_KICK :{YELLOW}'{STRING}' adlı oyuncuyu atmak istediğine emin misin? +STR_NETWORK_CLIENT_LIST_ASK_CLIENT_BAN :{YELLOW}'{STRING}' adlı oyunucuyu yasaklamak istediğine emin misin? +STR_NETWORK_CLIENT_LIST_ASK_COMPANY_RESET :{YELLOW}'{COMPANY}' şirketini silmek istediğine emin misin? +STR_NETWORK_CLIENT_LIST_ASK_COMPANY_UNLOCK :{YELLOW}'{COMPANY}' adlı şirketin şifresini sıfırlamak istediğine emin misin? STR_NETWORK_SERVER :Sunucu STR_NETWORK_CLIENT :İstemci @@ -2099,6 +2204,7 @@ STR_NETWORK_ERROR_SERVER_START :{WHITE}Sunucu b STR_NETWORK_ERROR_CLIENT_START :{WHITE}Bağlanamadı STR_NETWORK_ERROR_TIMEOUT :{WHITE}Bağlantı #{NUM} zaman aşımına uğradı STR_NETWORK_ERROR_SERVER_ERROR :{WHITE}Protokol hatası yapıldı ve bağlantı koparıldı +STR_NETWORK_ERROR_BAD_PLAYER_NAME :{WHITE}İsmin ayarlanmamış. Çok Oyunculu penceresinin üstünden ayarlanabilir STR_NETWORK_ERROR_WRONG_REVISION :{WHITE}Bu istemcinin revizyonu sunucununki ile aynı değil STR_NETWORK_ERROR_WRONG_PASSWORD :{WHITE}Yanlış parola STR_NETWORK_ERROR_SERVER_FULL :{WHITE}Sunucu dolu @@ -2111,6 +2217,8 @@ STR_NETWORK_ERROR_TIMEOUT_PASSWORD :{WHITE}Şifre g STR_NETWORK_ERROR_TIMEOUT_COMPUTER :{WHITE}Bilgisayarınız sunucuyla haberleşmeyi sürdürmek için çok yavaş. STR_NETWORK_ERROR_TIMEOUT_MAP :{WHITE}Bilgisayarınızın haritayı indirmesi çok uzun sürdü STR_NETWORK_ERROR_TIMEOUT_JOIN :{WHITE}Bilgisayarınızın sunucuya katılması çok uzun sürdü +STR_NETWORK_ERROR_INVALID_CLIENT_NAME :{WHITE}İsminiz geçerli değil +STR_NETWORK_ERROR_SERVER_TOO_OLD :{WHITE}Sorgulanan sunucu bu istemci için çok eski ############ Leave those lines in this order!! STR_NETWORK_ERROR_CLIENT_GENERAL :genel hata @@ -2133,6 +2241,7 @@ STR_NETWORK_ERROR_CLIENT_TIMEOUT_PASSWORD :şifre zamanın STR_NETWORK_ERROR_CLIENT_TIMEOUT_COMPUTER :genel zamanaşımı STR_NETWORK_ERROR_CLIENT_TIMEOUT_MAP :haritayı indirmek çok uzun sürdü STR_NETWORK_ERROR_CLIENT_TIMEOUT_JOIN :haritayı işlemek çok uzun sürdü +STR_NETWORK_ERROR_CLIENT_INVALID_CLIENT_NAME :Geçersiz ev sahibi ismi ############ End of leave-in-this-order STR_NETWORK_ERROR_CLIENT_GUI_LOST_CONNECTION_CAPTION :{WHITE}Olası bağlantı kaybı @@ -2146,11 +2255,13 @@ STR_NETWORK_SERVER_MESSAGE_GAME_STILL_PAUSED_1 :Oyun hala durak STR_NETWORK_SERVER_MESSAGE_GAME_STILL_PAUSED_2 :Oyun hala duraklıyor ({STRING}, {STRING}) STR_NETWORK_SERVER_MESSAGE_GAME_STILL_PAUSED_3 :Oyun hala duraklıyor ({STRING}, {STRING}, {STRING}) STR_NETWORK_SERVER_MESSAGE_GAME_STILL_PAUSED_4 :Oyun hala duraklatılmış ({STRING}, {STRING}, {STRING}, {STRING}) +STR_NETWORK_SERVER_MESSAGE_GAME_STILL_PAUSED_5 :Oyun hala duraklatıldı ({STRING}, {STRING}, {STRING}, {STRING}, {STRING}) STR_NETWORK_SERVER_MESSAGE_GAME_UNPAUSED :Oyun devam ediyor ({STRING}) STR_NETWORK_SERVER_MESSAGE_GAME_REASON_NOT_ENOUGH_PLAYERS :oyuncu sayısı STR_NETWORK_SERVER_MESSAGE_GAME_REASON_CONNECTING_CLIENTS :istemcilere bağlanıyor STR_NETWORK_SERVER_MESSAGE_GAME_REASON_MANUAL :el ile STR_NETWORK_SERVER_MESSAGE_GAME_REASON_GAME_SCRIPT :oyun betiği +STR_NETWORK_SERVER_MESSAGE_GAME_REASON_LINK_GRAPH :bağlantı grafiğinin güncellenmesi bekleniyor ############ End of leave-in-this-order STR_NETWORK_MESSAGE_CLIENT_LEAVING :ayrılıyor STR_NETWORK_MESSAGE_CLIENT_JOINED :*** {STRING} oyuna katıldı @@ -2160,6 +2271,7 @@ STR_NETWORK_MESSAGE_CLIENT_COMPANY_SPECTATE :*** {STRING} g STR_NETWORK_MESSAGE_CLIENT_COMPANY_NEW :*** {STRING} yeni bir şirket kurdu (#{2:NUM}) STR_NETWORK_MESSAGE_CLIENT_LEFT :*** {STRING} oyunu terketti ({2:STRING}) STR_NETWORK_MESSAGE_NAME_CHANGE :*** {STRING} adını {STRING} olarak değiştirdi +STR_NETWORK_MESSAGE_GIVE_MONEY :*** {STRING} {1:STRING} adlı şirkete {2:CURRENCY_LONG} verdi STR_NETWORK_MESSAGE_SERVER_SHUTDOWN :{WHITE}Sunucu kapandı STR_NETWORK_MESSAGE_SERVER_REBOOT :{WHITE}Sunucu baştan başlatılıyor...{}Lütfen bekleyin... STR_NETWORK_MESSAGE_KICKED :*** {STRING} atıldı. Sebep: ({STRING}) @@ -2237,6 +2349,9 @@ STR_MISSING_GRAPHICS_SET_MESSAGE :{BLACK}OpenTTD STR_MISSING_GRAPHICS_YES_DOWNLOAD :{BLACK}Evet, grafikleri indir STR_MISSING_GRAPHICS_NO_QUIT :{BLACK}Hayır, OpenTTD'den çık +STR_MISSING_GRAPHICS_ERROR_TITLE :{WHITE}İndirme başarısız +STR_MISSING_GRAPHICS_ERROR :{BLACK}Grafikleri indirme başarısız.{}Lütfen grafikleri manuel olarak indirin. +STR_MISSING_GRAPHICS_ERROR_QUIT :{BLACK}OpenTTD'den Çık # Transparency settings window STR_TRANSPARENCY_CAPTION :{WHITE}Şeffaflık Seçenekleri @@ -2280,6 +2395,7 @@ STR_JOIN_WAYPOINT_CAPTION :{WHITE}Yerimini STR_JOIN_WAYPOINT_CREATE_SPLITTED_WAYPOINT :{YELLOW}Ayrı bir yerimi yap # Generic toolbar +STR_TOOLBAR_DISABLED_NO_VEHICLE_AVAILABLE :{BLACK}Şu anda bu altyapı için hiçbir araç bulunmadığından devre dışı bırakıldı # Rail construction toolbar STR_RAIL_TOOLBAR_RAILROAD_CONSTRUCTION_CAPTION :Demiryolu Yapımı @@ -2415,7 +2531,7 @@ STR_WATERWAYS_TOOLBAR_BUILD_DOCK_TOOLTIP :{BLACK}İskele STR_WATERWAYS_TOOLBAR_BUOY_TOOLTIP :{BLACK}Yerimi olarak kullanilabilecek bir şamandıra yerlestir. Shift ile tıklama maliyet tahminini gösterir STR_WATERWAYS_TOOLBAR_BUILD_AQUEDUCT_TOOLTIP :{BLACK}Su kemeri yap. Shift ile tıklama maliyet tahminini gösterir STR_WATERWAYS_TOOLBAR_CREATE_LAKE_TOOLTIP :{BLACK}Su alanını belirle.{}Deniz seviyesinde CTRL tuşu basılı olmadığı sürece kanal yapar, basılıysa etraftakileri su altında bırakır -STR_WATERWAYS_TOOLBAR_CREATE_RIVER_TOOLTIP :{BLACK}Nehir yerleştir +STR_WATERWAYS_TOOLBAR_CREATE_RIVER_TOOLTIP :{BLACK}Nehir yerleştir. Ctrl tuşu alanı çapraz olarak seçer # Ship depot construction window STR_DEPOT_BUILD_SHIP_CAPTION :{WHITE}Tersane Yönü @@ -2475,6 +2591,12 @@ STR_TREES_RANDOM_TYPE :{BLACK}Rastgele STR_TREES_RANDOM_TYPE_TOOLTIP :{BLACK}Rastgele türde ağaçlar koy. Shift ile ağaç koyma/maliyet gösterme tercihi yapılır STR_TREES_RANDOM_TREES_BUTTON :{BLACK}Rastgele Ağaç STR_TREES_RANDOM_TREES_TOOLTIP :{BLACK}Haritaya rastgele ağaç dik +STR_TREES_MODE_NORMAL_BUTTON :{BLACK}Normal +STR_TREES_MODE_NORMAL_TOOLTIP :{BLACK}Arazinin üzerine sürükleyerek tekli ağaçlar dikin. +STR_TREES_MODE_FOREST_SM_BUTTON :{BLACK}Koru +STR_TREES_MODE_FOREST_SM_TOOLTIP :{BLACK}Arazinin üzerine sürükleyerek küçük ormanlar dikin. +STR_TREES_MODE_FOREST_LG_BUTTON :{BLACK}Orman +STR_TREES_MODE_FOREST_LG_TOOLTIP :{BLACK}Arazinin üzerine sürükleyerek büyük ormanlar dikin. # Land generation window (SE) STR_TERRAFORM_TOOLBAR_LAND_GENERATION_CAPTION :{WHITE}Arazi Yapımı @@ -2525,12 +2647,18 @@ STR_FOUND_TOWN_SELECT_LAYOUT_RANDOM :{BLACK}Rastgele # Fund new industry window STR_FUND_INDUSTRY_CAPTION :{WHITE}Yeni fabrika aç STR_FUND_INDUSTRY_SELECTION_TOOLTIP :{BLACK}Bu listeden uygun fabrikayı seçin -STR_FUND_INDUSTRY_MANY_RANDOM_INDUSTRIES :Birçok rastgele fabrika +STR_FUND_INDUSTRY_MANY_RANDOM_INDUSTRIES :{BLACK}Rastgele endüstriler oluştur STR_FUND_INDUSTRY_MANY_RANDOM_INDUSTRIES_TOOLTIP :{BLACK}Haritayı rastgele fabrikalarla doldur +STR_FUND_INDUSTRY_MANY_RANDOM_INDUSTRIES_CAPTION :{WHITE}Rastgele endüstriler oluştur +STR_FUND_INDUSTRY_MANY_RANDOM_INDUSTRIES_QUERY :{YELLOW}Birçok sayıda rastgele endüstri oluşturmak istediğinize emin misiniz? STR_FUND_INDUSTRY_INDUSTRY_BUILD_COST :{BLACK}Fiyat: {YELLOW}{CURRENCY_LONG} STR_FUND_INDUSTRY_PROSPECT_NEW_INDUSTRY :{BLACK}Tetkik arama STR_FUND_INDUSTRY_BUILD_NEW_INDUSTRY :{BLACK}İnşa et STR_FUND_INDUSTRY_FUND_NEW_INDUSTRY :{BLACK}Parayla Yap +STR_FUND_INDUSTRY_REMOVE_ALL_INDUSTRIES :{BLACK}Bütün endüstrileri kaldır +STR_FUND_INDUSTRY_REMOVE_ALL_INDUSTRIES_TOOLTIP :{BLACK}Şu anda haritada bulunan tüm endüstrileri kaldır +STR_FUND_INDUSTRY_REMOVE_ALL_INDUSTRIES_CAPTION :{WHITE}Bütün endüstrileri kaldır +STR_FUND_INDUSTRY_REMOVE_ALL_INDUSTRIES_QUERY :{YELLOW}Bütün endüstrileri kaldırmak istediğinize emin misiniz? # Industry cargoes window STR_INDUSTRY_CARGOES_INDUSTRY_CAPTION :{WHITE}{STRING} fabrikası için sanayi zinciri @@ -2551,6 +2679,7 @@ STR_INDUSTRY_CARGOES_SELECT_INDUSTRY_TOOLTIP :{BLACK}Gösterm # Land area window STR_LAND_AREA_INFORMATION_CAPTION :{WHITE}Arazi Bilgisi +STR_LAND_AREA_INFORMATION_LOCATION_TOOLTIP :{BLACK}Ana görünümü karonun konumuna ortalar. Ctrl+Sol Tık karonun konumunu gösteren yeni bir pencere açar STR_LAND_AREA_INFORMATION_COST_TO_CLEAR_N_A :{BLACK}Temizleme fiyatı: {LTBLUE}Yok STR_LAND_AREA_INFORMATION_COST_TO_CLEAR :{BLACK}Temizleme fiyatı: {RED}{CURRENCY_LONG} STR_LAND_AREA_INFORMATION_REVENUE_WHEN_CLEARED :{BLACK}Kaldırıldığında kazanılacak: {LTBLUE}{CURRENCY_LONG} @@ -2760,6 +2889,8 @@ STR_SAVELOAD_DETAIL_GRFSTATUS :{SILVER}NewGRF: STR_SAVELOAD_FILTER_TITLE :{BLACK}Süzgeç dizgesi: STR_SAVELOAD_OVERWRITE_TITLE :{WHITE}Dosya Üzerine Yaz STR_SAVELOAD_OVERWRITE_WARNING :{YELLOW}Mevcut dosyanın üzerine yazmak istediğinizden emin misiniz? +STR_SAVELOAD_DIRECTORY :{STRING} (Dizin) +STR_SAVELOAD_PARENT_DIRECTORY :{STRING} (Ana dizin) STR_SAVELOAD_OSKTITLE :{BLACK}Kayıtlı oyun için bir isim girin @@ -2771,6 +2902,17 @@ STR_MAPGEN_BY :{BLACK}* STR_MAPGEN_NUMBER_OF_TOWNS :{BLACK}Şehir sayısı: STR_MAPGEN_DATE :{BLACK}Tarih: STR_MAPGEN_NUMBER_OF_INDUSTRIES :{BLACK}Fabrika sayısı: +STR_MAPGEN_HEIGHTMAP_HEIGHT :{BLACK}En yüksek tepe: +STR_MAPGEN_HEIGHTMAP_HEIGHT_UP :{BLACK}Haritadaki en yüksek tepenin azami yüksekliğini 1 arttırın +STR_MAPGEN_HEIGHTMAP_HEIGHT_DOWN :{BLACK}Haritadaki en yüksek tepenin azami yüksekliğini 1 düşürün +STR_MAPGEN_SNOW_COVERAGE :{BLACK}Kar örtüsü: +STR_MAPGEN_SNOW_COVERAGE_UP :{BLACK}Kar örtüsünü yüzde on arttır +STR_MAPGEN_SNOW_COVERAGE_DOWN :{BLACK}Kar örtüsünü yüzde on azalt +STR_MAPGEN_SNOW_COVERAGE_TEXT :{BLACK}%{NUM} +STR_MAPGEN_DESERT_COVERAGE :{BLACK}Çöl örtüsü: +STR_MAPGEN_DESERT_COVERAGE_UP :{BLACK}Çöl örtüsünü yüzde on arttır +STR_MAPGEN_DESERT_COVERAGE_DOWN :{BLACK}Çöl örtüsünü yüzde on azalt +STR_MAPGEN_DESERT_COVERAGE_TEXT :{BLACK}%{NUM} STR_MAPGEN_LAND_GENERATOR :{BLACK}Harita üretici: STR_MAPGEN_TERRAIN_TYPE :{BLACK}Arazi türü: STR_MAPGEN_QUANTITY_OF_SEA_LAKES :{BLACK}Deniz seviyesi: @@ -2796,6 +2938,10 @@ STR_MAPGEN_HEIGHTMAP_NAME :{BLACK}Yüksekl STR_MAPGEN_HEIGHTMAP_SIZE_LABEL :{BLACK}Boyut: STR_MAPGEN_HEIGHTMAP_SIZE :{ORANGE}{NUM} x {NUM} +STR_MAPGEN_TERRAIN_TYPE_QUERY_CAPT :{WHITE}Hedef zirve yüksekliği +STR_MAPGEN_HEIGHTMAP_HEIGHT_QUERY_CAPT :{WHITE}En yüksek tepe +STR_MAPGEN_SNOW_COVERAGE_QUERY_CAPT :{WHITE}Kar örtüsü (% olarak) +STR_MAPGEN_DESERT_COVERAGE_QUERY_CAPT :{WHITE}Çöl örtüsü (% olarak) STR_MAPGEN_START_DATE_QUERY_CAPT :{WHITE}Başlangıç yılını değiştir # SE Map generation @@ -2935,6 +3081,7 @@ STR_NEWGRF_ERROR_MSG_WARNING :{RED}Uyarı: {S STR_NEWGRF_ERROR_MSG_ERROR :{RED}Hata: {SILVER}{STRING} STR_NEWGRF_ERROR_MSG_FATAL :{RED}Ölümcül hata: {SILVER}{STRING} STR_NEWGRF_ERROR_FATAL_POPUP :{WHITE}Ölümcül bir NewGRF hatası oluştu:{}{STRING} +STR_NEWGRF_ERROR_POPUP :{WHITE}NewGRF hatası oluştu:{}{STRING} STR_NEWGRF_ERROR_VERSION_NUMBER :{1:STRING} OpenTTD tarafından belirtilen TTDPatch sürümüyle çalışmayacaktır STR_NEWGRF_ERROR_DOS_OR_WINDOWS :{1:STRING}, TTD'nin {STRING} sürümü içindir STR_NEWGRF_ERROR_UNSET_SWITCH :{1:STRING}, {STRING} ile kullanılmak için tasarlanmıştır @@ -3012,6 +3159,7 @@ STR_SIGN_LIST_MATCH_CASE_TOOLTIP :{BLACK}Tabela a # Sign window STR_EDIT_SIGN_CAPTION :{WHITE}Tabelayı değiştir +STR_EDIT_SIGN_LOCATION_TOOLTIP :{BLACK}Ana görünümü işaretin konumuna ortalar. Ctrl+Sol Tık ile işaretin konumunda yeni bir pencere açar STR_EDIT_SIGN_NEXT_SIGN_TOOLTIP :{BLACK}Sonraki tabelaya git STR_EDIT_SIGN_PREVIOUS_SIGN_TOOLTIP :{BLACK}Önceki tabelaya git @@ -3072,20 +3220,23 @@ STR_LOCAL_AUTHORITY_ACTION_NEW_BUILDINGS :Yeni binalar ya STR_LOCAL_AUTHORITY_ACTION_EXCLUSIVE_TRANSPORT :Ayrıcalıklı nakliyat haklarını satın al STR_LOCAL_AUTHORITY_ACTION_BRIBE :Belediyeye rüşvet ver -STR_LOCAL_AUTHORITY_ACTION_TOOLTIP_SMALL_ADVERTISING :{YELLOW} Müsteri çekmek için duvarlara poster yapıştır.{} Fiyat: {CURRENCY_LONG} -STR_LOCAL_AUTHORITY_ACTION_TOOLTIP_MEDIUM_ADVERTISING :{YELLOW} Müsteri çekmek için radyoya reklam ver.{} Fiyat: {CURRENCY_LONG} -STR_LOCAL_AUTHORITY_ACTION_TOOLTIP_LARGE_ADVERTISING :{YELLOW} Müsteri çekmek için televizyona reklam ver.{} Fiyat: {CURRENCY_LONG} -STR_LOCAL_AUTHORITY_ACTION_TOOLTIP_ROAD_RECONSTRUCTION :{YELLOW} Yol tamirleri için belediyeye bağış yap. 6 ay boyunca şehrin yolları kullanılamaz.{} Fiyat: {CURRENCY_LONG} -STR_LOCAL_AUTHORITY_ACTION_TOOLTIP_STATUE_OF_COMPANY :{YELLOW}Şirket sahibinin heykelini dik.{}Fiyatı: {CURRENCY_LONG} -STR_LOCAL_AUTHORITY_ACTION_TOOLTIP_NEW_BUILDINGS :{YELLOW}Şehirde ticari binaların yapımı için bağış yap.{}Fiyatı: {CURRENCY_LONG} -STR_LOCAL_AUTHORITY_ACTION_TOOLTIP_EXCLUSIVE_TRANSPORT :{YELLOW} Bir yıllık ayrıcalıklı nakliyat haklarını satın al. Belediye şehirde sadece senin şirketine yolcu ve kargo taşıma izni verir.{} Fiyat: {CURRENCY_LONG} +STR_LOCAL_AUTHORITY_ACTION_TOOLTIP_SMALL_ADVERTISING :{YELLOW} Müsteri çekmek için duvarlara poster yapıştır.{}Şehir merkezinin etrafında küçük bir çapta istasyon derecelendirmesinde geçici bir yükseltme sağlar.{}Fiyat: {CURRENCY_LONG} +STR_LOCAL_AUTHORITY_ACTION_TOOLTIP_MEDIUM_ADVERTISING :{YELLOW} Müsteri çekmek için radyoya reklam ver.{}Şehir merkezinin etrafında orta bir çapta istasyon derecelendirmesinde geçici bir yükseltme sağlar.{}Fiyat: {CURRENCY_LONG} +STR_LOCAL_AUTHORITY_ACTION_TOOLTIP_LARGE_ADVERTISING :{YELLOW} Müsteri çekmek için televizyona reklam ver.{}Şehir merkezinin etrafında büyük bir çapta istasyon derecelendirmesinde geçici bir yükseltme sağlar.{}Fiyat: {CURRENCY_LONG} +STR_LOCAL_AUTHORITY_ACTION_TOOLTIP_ROAD_RECONSTRUCTION :{YELLOW} Yol tamirleri için belediyeye bağış yap.{}6 ay boyunca şehrin yolları kullanılamaz.{}Fiyat: {CURRENCY_LONG} +STR_LOCAL_AUTHORITY_ACTION_TOOLTIP_STATUE_OF_COMPANY :{YELLOW}Şirket sahibinin heykelini dik.{}Şehirdeki istasyon derecelendirmesinde kalıcı bir yükseltme sağlar.{}Fiyatı: {CURRENCY_LONG} +STR_LOCAL_AUTHORITY_ACTION_TOOLTIP_NEW_BUILDINGS :{YELLOW}Şehirde ticari binaların yapımı için bağış yap.{}Bu kasabanın büyümesine geçici bir yükseltme sağlar.{}Fiyatı: {CURRENCY_LONG} +STR_LOCAL_AUTHORITY_ACTION_TOOLTIP_EXCLUSIVE_TRANSPORT :{YELLOW} Bir yıllık ayrıcalıklı nakliyat haklarını satın al.{} Belediye, rakip istasyonların yolcu ve kargo taşımasına izin vermez.{} Fiyat: {CURRENCY_LONG} STR_LOCAL_AUTHORITY_ACTION_TOOLTIP_BRIBE :{YELLOW}Yakalanma riskini göze alarak belediyeye rüşvet ver.{}Fiyat: {CURRENCY_LONG} # Goal window STR_GOALS_CAPTION :{WHITE}{COMPANY} Hedefler STR_GOALS_SPECTATOR_CAPTION :{WHITE}Evrensel Amaçlar STR_GOALS_SPECTATOR :Evrensel Amaçlar +STR_GOALS_GLOBAL_BUTTON :{BLACK}Küresel STR_GOALS_GLOBAL_BUTTON_HELPTEXT :{BLACK}Evrensel amaçları göster +STR_GOALS_COMPANY_BUTTON :{BLACK}Şirket +STR_GOALS_COMPANY_BUTTON_HELPTEXT :{BLACK}Şirket hedeflerini göster STR_GOALS_TEXT :{ORANGE}{STRING} STR_GOALS_NONE :{ORANGE}- Hiçbiri - STR_GOALS_PROGRESS :{ORANGE}{STRING} @@ -3093,10 +3244,10 @@ STR_GOALS_PROGRESS_COMPLETE :{GREEN}{STRING} STR_GOALS_TOOLTIP_CLICK_ON_SERVICE_TO_CENTER :{BLACK}Ana görünümü istenen fabrika/kasaba/kareye getirmek için hedefe tıklayın. Ctrl+Tıklama fabrika/kasaba/kare konumunda yeni bir pencerede görünüm açar # Goal question window -STR_GOAL_QUESTION_CAPTION_QUESTION :Soru -STR_GOAL_QUESTION_CAPTION_INFORMATION :Bilgi -STR_GOAL_QUESTION_CAPTION_WARNING :Uyarı -STR_GOAL_QUESTION_CAPTION_ERROR :Hata +STR_GOAL_QUESTION_CAPTION_QUESTION :{BLACK}Soru +STR_GOAL_QUESTION_CAPTION_INFORMATION :{BLACK}Bilgi +STR_GOAL_QUESTION_CAPTION_WARNING :{BLACK}Uyarı +STR_GOAL_QUESTION_CAPTION_ERROR :{YELLOW}Hata ############ Start of Goal Question button list STR_GOAL_QUESTION_BUTTON_CANCEL :İptal @@ -3286,6 +3437,8 @@ STR_COMPANY_VIEW_RELOCATE_HQ :{BLACK}Binayı STR_COMPANY_VIEW_RELOCATE_COMPANY_HEADQUARTERS :{BLACK}Şirket değerinin %1'i karşılığında binanın yerini değiştir. Shift+tıklama şirket binasının yerini değiştirmeden tahmini maliyeti gösterir STR_COMPANY_VIEW_INFRASTRUCTURE_BUTTON :{BLACK}Ayrıntılar STR_COMPANY_VIEW_INFRASTRUCTURE_TOOLTIP :{BLACK}Ayrıntılı altyapı sayılarını göster +STR_COMPANY_VIEW_GIVE_MONEY_BUTTON :{BLACK}Para ver +STR_COMPANY_VIEW_GIVE_MONEY_TOOLTIP :{BLACK}Bu şirkete para ver STR_COMPANY_VIEW_NEW_FACE_BUTTON :{BLACK}Yeni Surat STR_COMPANY_VIEW_NEW_FACE_TOOLTIP :{BLACK}Müdür için yeni surat seç @@ -3303,6 +3456,7 @@ STR_COMPANY_VIEW_SELL_SHARE_TOOLTIP :{BLACK}Şirketi STR_COMPANY_VIEW_COMPANY_NAME_QUERY_CAPTION :Şirketin ismi STR_COMPANY_VIEW_PRESIDENT_S_NAME_QUERY_CAPTION :Yöneticinin ismi +STR_COMPANY_VIEW_GIVE_MONEY_QUERY_CAPTION :Vermek istediğin para miktarını gir STR_BUY_COMPANY_MESSAGE :{WHITE}Şirketimizi satın alacak birilerini arıyoruz.{}{} {COMPANY} şirketini şu fiyata almak ister misiniz: {CURRENCY_LONG}? @@ -3457,6 +3611,7 @@ STR_PURCHASE_INFO_PWAGPOWER_PWAGWEIGHT :{BLACK}Güç Ve STR_PURCHASE_INFO_REFITTABLE_TO :{BLACK}Dönüştürülebildiği kargolar: {GOLD}{STRING} STR_PURCHASE_INFO_ALL_TYPES :Tüm kargo türleri STR_PURCHASE_INFO_NONE :Hiçbiri +STR_PURCHASE_INFO_ENGINES_ONLY :Yalnızca lokomotifler STR_PURCHASE_INFO_ALL_BUT :Şunlar hariç tümü: {CARGO_LIST} STR_PURCHASE_INFO_MAX_TE :{BLACK}Aza. Çekim Gücü: {GOLD}{FORCE} STR_PURCHASE_INFO_AIRCRAFT_RANGE :{BLACK}Menzil: {GOLD}{COMMA} kare @@ -3663,6 +3818,10 @@ STR_REPLACE_REMOVE_WAGON_HELP :{BLACK}Otomatik # Vehicle view STR_VEHICLE_VIEW_CAPTION :{WHITE}{VEHICLE} +STR_VEHICLE_VIEW_TRAIN_CENTER_TOOLTIP :{BLACK}Ana görünümü trenin konumuna ortalar. Çift tık ana görünümdeki treni takip eder. Ctrl+Sol Tık trenin konumunu gösteren yeni bir pencere açar +STR_VEHICLE_VIEW_ROAD_VEHICLE_CENTER_TOOLTIP :{BLACK}Ana görünümü aracın konumuna ortalayın. Çift tık ana görünümdeki aracı takip eder. Ctrl+Sol Tık uçağın konumunda yeni bir görünüm penceresi açar +STR_VEHICLE_VIEW_SHIP_CENTER_TOOLTIP :{BLACK}Ana görünümü geminin konumuna ortalar. Çift tık ana görünümdeki gemiyi takip eder. Ctrl+Sol Tık geminin konumunu gösteren yeni bir pencere açar +STR_VEHICLE_VIEW_AIRCRAFT_CENTER_TOOLTIP :{BLACK}Ana görünümü uçağın konumuna ortalayın. Çift tık ana görünümdeki uçağı takip eder. Ctrl+Sol Tık uçağın konumunda yeni bir görünüm penceresi açar STR_VEHICLE_VIEW_TRAIN_SEND_TO_DEPOT_TOOLTIP :{BLACK}Treni garaja gönder STR_VEHICLE_VIEW_ROAD_VEHICLE_SEND_TO_DEPOT_TOOLTIP :{BLACK}Aracı garaja gönder @@ -3694,7 +3853,12 @@ STR_VEHICLE_VIEW_ROAD_VEHICLE_SHOW_DETAILS_TOOLTIP :{BLACK}Karayolu STR_VEHICLE_VIEW_SHIP_SHOW_DETAILS_TOOLTIP :{BLACK}Gemi ayrıntılarını göster STR_VEHICLE_VIEW_AIRCRAFT_SHOW_DETAILS_TOOLTIP :{BLACK}Uçak ayrıntılarını göster +STR_VEHICLE_VIEW_TRAIN_STATUS_START_STOP_TOOLTIP :{BLACK}Şu anki tren eylemi - treni durdurup/başlatmak için tıkla +STR_VEHICLE_VIEW_ROAD_VEHICLE_STATUS_START_STOP_TOOLTIP :{BLACK}Şu anki araç eylemi - aracı durdurup/başlatmak için tıkla +STR_VEHICLE_VIEW_SHIP_STATE_STATUS_STOP_TOOLTIP :{BLACK}Şu anki gemi eylemi - gemiyi durdurup/başlatmak için tıkla +STR_VEHICLE_VIEW_AIRCRAFT_STATUS_START_STOP_TOOLTIP :{BLACK}Şu anki uçak eylemi - uçağı durdurup/başlatmak için tıkla +STR_VEHICLE_VIEW_ORDER_LOCATION_TOOLTIP :{BLACK}Ana görünümü talimatın konumuna ortalar. Ctrl+Sol Tık ile talimatın konumunda yeni bir pencere açar # Messages in the start stop button in the vehicle view STR_VEHICLE_STATUS_LOADING_UNLOADING :{LTBLUE}Yükleme / Boşaltma @@ -3922,6 +4086,7 @@ STR_ORDER_REFIT_STOP_ORDER :(Kargo türün STR_ORDER_STOP_ORDER :(Dur) STR_ORDER_GO_TO_STATION :{STRING} {STATION} {STRING} +STR_ORDER_GO_TO_STATION_CAN_T_USE_STATION :{PUSH_COLOUR}{RED}(İstasyon kullanılamıyor){POP_COLOUR} {STRING} {STATION} {STRING} STR_ORDER_IMPLICIT :(Otomatik) @@ -4194,6 +4359,7 @@ STR_WARNING_FALLBACK_SOUNDSET :{WHITE}Yalnız STR_WARNING_SCREENSHOT_SIZE_CAPTION :{WHITE}Dev ekran görüntüsü STR_WARNING_SCREENSHOT_SIZE_MESSAGE :{YELLOW}Ekran görüntüsünün çözünürlüğü {COMMA} x {COMMA} piksel. Görüntüyü kaydetmek biraz zaman alabilir. Devam etmek istiyor musunuz? +STR_MESSAGE_HEIGHTMAP_SUCCESSFULLY :{WHITE}Yükseklik haritası başarıyla '{STRING}' olarak kaydedildi. En yüksek tepe: {NUM} STR_MESSAGE_SCREENSHOT_SUCCESSFULLY :{WHITE}Ekran görüntüsü '{STRING}' olarak kaydedildi STR_ERROR_SCREENSHOT_FAILED :{WHITE}Ekran görüntüsü alınamadı! @@ -4247,6 +4413,7 @@ STR_ERROR_LOAN_ALREADY_REPAYED :{WHITE}... öde STR_ERROR_CURRENCY_REQUIRED :{WHITE}... {CURRENCY_LONG} gerekli STR_ERROR_CAN_T_REPAY_LOAN :{WHITE}Kredi ödenemiyor... STR_ERROR_INSUFFICIENT_FUNDS :{WHITE}Bankadan borç alınan para verilemez... +STR_ERROR_CAN_T_GIVE_MONEY :{WHITE}Bu şirkete para veremezsin... STR_ERROR_CAN_T_BUY_COMPANY :{WHITE}Şirket satın alınamaz... STR_ERROR_CAN_T_BUILD_COMPANY_HEADQUARTERS :{WHITE}Şirket binasi yapılamaz... STR_ERROR_CAN_T_BUY_25_SHARE_IN_THIS :{WHITE}Şirketin %25'i alınamıyor... @@ -4373,6 +4540,8 @@ STR_ERROR_DEPOT_WRONG_DEPOT_TYPE :Yanlış gar t STR_ERROR_TRAIN_TOO_LONG_AFTER_REPLACEMENT :{WHITE}{VEHICLE} değiştirmeden sonra çok uzun oldu STR_ERROR_AUTOREPLACE_NOTHING_TO_DO :{WHITE}Hiçbir otomatik değiştirme/yenileme kuralı uygulanmadı STR_ERROR_AUTOREPLACE_MONEY_LIMIT :(para yetmedi) +STR_ERROR_AUTOREPLACE_INCOMPATIBLE_CARGO :{WHITE}Yeni araçlar bunu taşıyamıyor {STRING} +STR_ERROR_AUTOREPLACE_INCOMPATIBLE_REFIT :{WHITE}{NUM} numaralı talimattaki yeni araç yenilenemez. # Rail construction errors STR_ERROR_IMPOSSIBLE_TRACK_COMBINATION :{WHITE}İmkansiz ray birleşimi @@ -4664,10 +4833,10 @@ STR_INDUSTRY_NAME_SUGAR_MINE :Şeker Madeni ##id 0x6000 STR_SV_EMPTY : STR_SV_UNNAMED :İsimsiz -STR_SV_TRAIN_NAME :Tren {COMMA} -STR_SV_ROAD_VEHICLE_NAME :Karayolu Aracı {COMMA} -STR_SV_SHIP_NAME :Gemi {COMMA} -STR_SV_AIRCRAFT_NAME :Uçak {COMMA} +STR_SV_TRAIN_NAME :Tren #{COMMA} +STR_SV_ROAD_VEHICLE_NAME :Karayolu Aracı #{COMMA} +STR_SV_SHIP_NAME :Gemi #{COMMA} +STR_SV_AIRCRAFT_NAME :Uçak #{COMMA} STR_SV_STNAME :{STRING} STR_SV_STNAME_NORTH :{STRING} Kuzey @@ -4969,6 +5138,7 @@ STR_FORMAT_BUOY_NAME :{TOWN} Şamand STR_FORMAT_BUOY_NAME_SERIAL :{TOWN} Şamandırası #{COMMA} STR_FORMAT_COMPANY_NUM :(Company {COMMA}) STR_FORMAT_GROUP_NAME :Grup {COMMA} +STR_FORMAT_GROUP_VEHICLE_NAME :{GROUP} #{COMMA} STR_FORMAT_INDUSTRY_NAME :{TOWN} {STRING.tamlanan} STR_FORMAT_WAYPOINT_NAME :Yerimi {TOWN} STR_FORMAT_WAYPOINT_NAME_SERIAL :Yerimi {TOWN} #{COMMA} From ba193f2e23ce82cdfca37771147848751c6993e1 Mon Sep 17 00:00:00 2001 From: PeterN Date: Wed, 12 May 2021 21:35:48 +0100 Subject: [PATCH 48/61] Fix #9186: Fix incorrect bounding box height causing station sprite glitch. (#9187) Increased height of small station building bounding box to cover the build rather than just the platform. --- src/table/station_land.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/table/station_land.h b/src/table/station_land.h index 8429914f32..53a69e1e21 100644 --- a/src/table/station_land.h +++ b/src/table/station_land.h @@ -71,13 +71,13 @@ static const DrawTileSeqStruct _station_display_datas_1[] = { }; static const DrawTileSeqStruct _station_display_datas_2[] = { - TILE_SEQ_LINE( 0, 0, 0, 16, 5, 2, SPR_RAIL_PLATFORM_BUILDING_X | (1U << PALETTE_MODIFIER_COLOUR)) + TILE_SEQ_LINE( 0, 0, 0, 16, 5, 15, SPR_RAIL_PLATFORM_BUILDING_X | (1U << PALETTE_MODIFIER_COLOUR)) TILE_SEQ_LINE( 0, 11, 0, 16, 5, 2, SPR_RAIL_PLATFORM_X_FRONT | (1U << PALETTE_MODIFIER_COLOUR)) TILE_SEQ_END() }; static const DrawTileSeqStruct _station_display_datas_3[] = { - TILE_SEQ_LINE( 0, 0, 0, 5, 16, 2, SPR_RAIL_PLATFORM_BUILDING_Y | (1U << PALETTE_MODIFIER_COLOUR)) + TILE_SEQ_LINE( 0, 0, 0, 5, 16, 15, SPR_RAIL_PLATFORM_BUILDING_Y | (1U << PALETTE_MODIFIER_COLOUR)) TILE_SEQ_LINE(11, 0, 0, 5, 16, 2, SPR_RAIL_PLATFORM_Y_FRONT | (1U << PALETTE_MODIFIER_COLOUR)) TILE_SEQ_END() }; From 95abdfdef9c9c79e79cb3f32bc54aec66e224d7e Mon Sep 17 00:00:00 2001 From: frosch Date: Sun, 9 May 2021 16:20:38 +0200 Subject: [PATCH 49/61] Cleanup: remove unneeded labels and gotos. The window list supports deletion of arbitrary windows, while iterating over it. --- src/window.cpp | 30 +++++------------------------- 1 file changed, 5 insertions(+), 25 deletions(-) diff --git a/src/window.cpp b/src/window.cpp index 3fcda7d42d..d6901bd97a 100644 --- a/src/window.cpp +++ b/src/window.cpp @@ -1168,14 +1168,10 @@ void DeleteWindowById(WindowClass cls, WindowNumber number, bool force) */ void DeleteWindowByClass(WindowClass cls) { -restart_search: - /* When we find the window to delete, we need to restart the search - * as deleting this window could cascade in deleting (many) others - * anywhere in the z-array */ + /* Note: the container remains stable, even when deleting windows. */ for (Window *w : Window::IterateFromBack()) { if (w->window_class == cls) { delete w; - goto restart_search; } } } @@ -1188,14 +1184,10 @@ restart_search: */ void DeleteCompanyWindows(CompanyID id) { -restart_search: - /* When we find the window to delete, we need to restart the search - * as deleting this window could cascade in deleting (many) others - * anywhere in the z-array */ + /* Note: the container remains stable, even when deleting windows. */ for (Window *w : Window::IterateFromBack()) { if (w->owner == id) { delete w; - goto restart_search; } } @@ -3325,10 +3317,7 @@ void CallWindowGameTickEvent() */ void DeleteNonVitalWindows() { -restart_search: - /* When we find the window to delete, we need to restart the search - * as deleting this window could cascade in deleting (many) others - * anywhere in the z-array */ + /* Note: the container remains stable, even when deleting windows. */ for (const Window *w : Window::IterateFromBack()) { if (w->window_class != WC_MAIN_WINDOW && w->window_class != WC_SELECT_GAME && @@ -3338,7 +3327,6 @@ restart_search: (w->flags & WF_STICKY) == 0) { // do not delete windows which are 'pinned' delete w; - goto restart_search; } } } @@ -3355,14 +3343,10 @@ void DeleteAllNonVitalWindows() /* Delete every window except for stickied ones, then sticky ones as well */ DeleteNonVitalWindows(); -restart_search: - /* When we find the window to delete, we need to restart the search - * as deleting this window could cascade in deleting (many) others - * anywhere in the z-array */ + /* Note: the container remains stable, even when deleting windows. */ for (const Window *w : Window::IterateFromBack()) { if (w->flags & WF_STICKY) { delete w; - goto restart_search; } } } @@ -3384,14 +3368,10 @@ void DeleteAllMessages() */ void DeleteConstructionWindows() { -restart_search: - /* When we find the window to delete, we need to restart the search - * as deleting this window could cascade in deleting (many) others - * anywhere in the z-array */ + /* Note: the container remains stable, even when deleting windows. */ for (const Window *w : Window::IterateFromBack()) { if (w->window_desc->flags & WDF_CONSTRUCTION) { delete w; - goto restart_search; } } From aba239479b6e75990cc914331bd2d735b8918628 Mon Sep 17 00:00:00 2001 From: frosch Date: Sun, 9 May 2021 18:35:49 +0200 Subject: [PATCH 50/61] Codechange: remove excessive templating in favour of a single const_cast. The const_cast will be removed again in a later commit. --- src/window_gui.h | 34 ++++++++++++++-------------------- 1 file changed, 14 insertions(+), 20 deletions(-) diff --git a/src/window_gui.h b/src/window_gui.h index 66c867a547..cf4ebcf73b 100644 --- a/src/window_gui.h +++ b/src/window_gui.h @@ -813,65 +813,59 @@ public: /** * Iterator to iterate all valid Windows - * @tparam T Type of the class/struct that is going to be iterated * @tparam Tfront Wether we iterate from front */ - template + template struct WindowIterator { - typedef T value_type; - typedef T *pointer; - typedef T &reference; + typedef Window *value_type; + typedef value_type *pointer; + typedef value_type &reference; typedef size_t difference_type; typedef std::forward_iterator_tag iterator_category; - explicit WindowIterator(T *start) : w(start) + explicit WindowIterator(const Window *start) : w(const_cast(start)) { this->Validate(); } bool operator==(const WindowIterator &other) const { return this->w == other.w; } bool operator!=(const WindowIterator &other) const { return !(*this == other); } - T * operator*() const { return this->w; } + Window * operator*() const { return this->w; } WindowIterator & operator++() { this->Next(); this->Validate(); return *this; } private: - T *w; + Window *w; void Validate() { while (this->w != nullptr && this->w->window_class == WC_INVALID) this->Next(); } void Next() { if (this->w != nullptr) this->w = Tfront ? this->w->z_back : this->w->z_front; } }; /** * Iterable ensemble of all valid Windows - * @tparam T Type of the class/struct that is going to be iterated * @tparam Tfront Wether we iterate from front */ - template + template struct Iterate { - Iterate(T *from) : from(from) {} - WindowIterator begin() { return WindowIterator(this->from); } - WindowIterator end() { return WindowIterator(nullptr); } + Iterate(const Window *from) : from(from) {} + WindowIterator begin() { return WindowIterator(this->from); } + WindowIterator end() { return WindowIterator(nullptr); } bool empty() { return this->begin() == this->end(); } private: - T *from; + const Window *from; }; /** * Returns an iterable ensemble of all valid Window from back to front - * @tparam T Type of the class/struct that is going to be iterated * @param from index of the first Window to consider * @return an iterable ensemble of all valid Window */ - template - static Iterate IterateFromBack(T *from = _z_back_window) { return Iterate(from); } + static Iterate IterateFromBack(const Window *from = _z_back_window) { return Iterate(from); } /** * Returns an iterable ensemble of all valid Window from front to back - * @tparam T Type of the class/struct that is going to be iterated * @param from index of the first Window to consider * @return an iterable ensemble of all valid Window */ - template - static Iterate IterateFromFront(T *from = _z_front_window) { return Iterate(from); } + static Iterate IterateFromFront(const Window *from = _z_front_window) { return Iterate(from); } }; /** From 22567a1f43d29a7596b9ef88441d5a65776fa2c8 Mon Sep 17 00:00:00 2001 From: frosch Date: Sun, 9 May 2021 16:42:36 +0200 Subject: [PATCH 51/61] Codechange: use iterators instead of 'subranges' when iterating from a specific window. Using iterators makes it easier to include or exclude the start window in the iteration. --- src/viewport.cpp | 27 ++++++++++++++++----------- src/window.cpp | 10 ++++++++-- src/window_gui.h | 29 +++++++++-------------------- 3 files changed, 33 insertions(+), 33 deletions(-) diff --git a/src/viewport.cpp b/src/viewport.cpp index 303e027600..3e4c2d273f 100644 --- a/src/viewport.cpp +++ b/src/viewport.cpp @@ -266,35 +266,36 @@ void InitializeWindowViewport(Window *w, int x, int y, static Point _vp_move_offs; -static void DoSetViewportPosition(const Window *w, int left, int top, int width, int height) +static void DoSetViewportPosition(Window::IteratorToFront it, int left, int top, int width, int height) { - for (const Window *w : Window::IterateFromBack(w)) { + for (; !it.IsEnd(); ++it) { + const Window *w = *it; if (left + width > w->left && w->left + w->width > left && top + height > w->top && w->top + w->height > top) { if (left < w->left) { - DoSetViewportPosition(w, left, top, w->left - left, height); - DoSetViewportPosition(w, left + (w->left - left), top, width - (w->left - left), height); + DoSetViewportPosition(it, left, top, w->left - left, height); + DoSetViewportPosition(it, left + (w->left - left), top, width - (w->left - left), height); return; } if (left + width > w->left + w->width) { - DoSetViewportPosition(w, left, top, (w->left + w->width - left), height); - DoSetViewportPosition(w, left + (w->left + w->width - left), top, width - (w->left + w->width - left), height); + DoSetViewportPosition(it, left, top, (w->left + w->width - left), height); + DoSetViewportPosition(it, left + (w->left + w->width - left), top, width - (w->left + w->width - left), height); return; } if (top < w->top) { - DoSetViewportPosition(w, left, top, width, (w->top - top)); - DoSetViewportPosition(w, left, top + (w->top - top), width, height - (w->top - top)); + DoSetViewportPosition(it, left, top, width, (w->top - top)); + DoSetViewportPosition(it, left, top + (w->top - top), width, height - (w->top - top)); return; } if (top + height > w->top + w->height) { - DoSetViewportPosition(w, left, top, width, (w->top + w->height - top)); - DoSetViewportPosition(w, left, top + (w->top + w->height - top), width, height - (w->top + w->height - top)); + DoSetViewportPosition(it, left, top, width, (w->top + w->height - top)); + DoSetViewportPosition(it, left, top + (w->top + w->height - top), width, height - (w->top + w->height - top)); return; } @@ -380,7 +381,11 @@ static void SetViewportPosition(Window *w, int x, int y) i = top + height - _screen.height; if (i >= 0) height -= i; - if (height > 0) DoSetViewportPosition(w->z_front, left, top, width, height); + if (height > 0) { + Window::IteratorToFront it(w); + ++it; + DoSetViewportPosition(it, left, top, width, height); + } } } diff --git a/src/window.cpp b/src/window.cpp index d6901bd97a..9b51eafd8a 100644 --- a/src/window.cpp +++ b/src/window.cpp @@ -896,7 +896,10 @@ static bool MayBeShown(const Window *w) */ static void DrawOverlappedWindow(Window *w, int left, int top, int right, int bottom) { - for (const Window *v : Window::IterateFromBack(w->z_front)) { + Window::IteratorToFront it(w); + ++it; + for (; !it.IsEnd(); ++it) { + const Window *v = *it; if (MayBeShown(v) && right > v->left && bottom > v->top && @@ -2530,7 +2533,10 @@ static bool MaybeBringWindowToFront(Window *w) w_height = w->unshaded_size.height; } - for (Window *u : Window::IterateFromBack(w->z_front)) { + Window::IteratorToFront it(w); + ++it; + for (; !it.IsEnd(); ++it) { + Window *u = *it; /* A modal child will prevent the activation of the parent window */ if (u->parent == w && (u->window_desc->flags & WDF_MODAL)) { u->SetWhiteBorder(); diff --git a/src/window_gui.h b/src/window_gui.h index cf4ebcf73b..7f7ab2667e 100644 --- a/src/window_gui.h +++ b/src/window_gui.h @@ -833,39 +833,28 @@ public: Window * operator*() const { return this->w; } WindowIterator & operator++() { this->Next(); this->Validate(); return *this; } + bool IsEnd() const { return this->w == nullptr; } + private: Window *w; void Validate() { while (this->w != nullptr && this->w->window_class == WC_INVALID) this->Next(); } void Next() { if (this->w != nullptr) this->w = Tfront ? this->w->z_back : this->w->z_front; } }; + using IteratorToFront = WindowIterator; //!< Iterate in Z order towards front. + using IteratorToBack = WindowIterator; //!< Iterate in Z order towards back. /** * Iterable ensemble of all valid Windows * @tparam Tfront Wether we iterate from front */ template - struct Iterate { - Iterate(const Window *from) : from(from) {} - WindowIterator begin() { return WindowIterator(this->from); } + struct AllWindows { + AllWindows() {} + WindowIterator begin() { return WindowIterator(Tfront ? _z_front_window : _z_back_window); } WindowIterator end() { return WindowIterator(nullptr); } - bool empty() { return this->begin() == this->end(); } - private: - const Window *from; }; - - /** - * Returns an iterable ensemble of all valid Window from back to front - * @param from index of the first Window to consider - * @return an iterable ensemble of all valid Window - */ - static Iterate IterateFromBack(const Window *from = _z_back_window) { return Iterate(from); } - - /** - * Returns an iterable ensemble of all valid Window from front to back - * @param from index of the first Window to consider - * @return an iterable ensemble of all valid Window - */ - static Iterate IterateFromFront(const Window *from = _z_front_window) { return Iterate(from); } + using IterateFromBack = AllWindows; //!< Iterate all windows in Z order from back to front. + using IterateFromFront = AllWindows; //!< Iterate all windows in Z order from front to back. }; /** From f96f11395162a8d57f5fdcef18c7a64f7acf76f8 Mon Sep 17 00:00:00 2001 From: frosch Date: Sun, 9 May 2021 17:10:07 +0200 Subject: [PATCH 52/61] Codechange: use IterateFromBack/Front only if the order is important. Use Iterate if the order does not matter. --- src/misc_gui.cpp | 2 +- src/sound.cpp | 1 + src/viewport.cpp | 6 ++-- src/widgets/dropdown.cpp | 2 +- src/window.cpp | 68 ++++++++++++++++++++-------------------- src/window_gui.h | 1 + 6 files changed, 41 insertions(+), 39 deletions(-) diff --git a/src/misc_gui.cpp b/src/misc_gui.cpp index a40bf50139..3094d0eade 100644 --- a/src/misc_gui.cpp +++ b/src/misc_gui.cpp @@ -1289,7 +1289,7 @@ void ShowQuery(StringID caption, StringID message, Window *parent, QueryCallback { if (parent == nullptr) parent = FindWindowById(WC_MAIN_WINDOW, 0); - for (const Window *w : Window::IterateFromBack()) { + for (const Window *w : Window::Iterate()) { if (w->window_class != WC_CONFIRM_POPUP_QUERY) continue; const QueryWindow *qw = (const QueryWindow *)w; diff --git a/src/sound.cpp b/src/sound.cpp index 413c70af2a..c22e044372 100644 --- a/src/sound.cpp +++ b/src/sound.cpp @@ -245,6 +245,7 @@ static void SndPlayScreenCoordFx(SoundID sound, int left, int right, int top, in { if (_settings_client.music.effect_vol == 0) return; + /* Iterate from back, so that main viewport is checked first */ for (const Window *w : Window::IterateFromBack()) { const Viewport *vp = w->viewport; diff --git a/src/viewport.cpp b/src/viewport.cpp index 3e4c2d273f..4344eac2a3 100644 --- a/src/viewport.cpp +++ b/src/viewport.cpp @@ -1480,7 +1480,7 @@ void ViewportSign::MarkDirty(ZoomLevel maxzoom) const zoomlevels[zoom].bottom = this->top + ScaleByZoom(VPSM_TOP + FONT_HEIGHT_NORMAL + VPSM_BOTTOM + 1, zoom); } - for (const Window *w : Window::IterateFromBack()) { + for (const Window *w : Window::Iterate()) { Viewport *vp = w->viewport; if (vp != nullptr && vp->zoom <= maxzoom) { assert(vp->width != 0); @@ -1953,7 +1953,7 @@ bool MarkAllViewportsDirty(int left, int top, int right, int bottom) { bool dirty = false; - for (const Window *w : Window::IterateFromBack()) { + for (const Window *w : Window::Iterate()) { Viewport *vp = w->viewport; if (vp != nullptr) { assert(vp->width != 0); @@ -1966,7 +1966,7 @@ bool MarkAllViewportsDirty(int left, int top, int right, int bottom) void ConstrainAllViewportsZoom() { - for (Window *w : Window::IterateFromFront()) { + for (Window *w : Window::Iterate()) { if (w->viewport == nullptr) continue; ZoomLevel zoom = static_cast(Clamp(w->viewport->zoom, _settings_client.gui.zoom_min, _settings_client.gui.zoom_max)); diff --git a/src/widgets/dropdown.cpp b/src/widgets/dropdown.cpp index b6f7de3c18..a4bd002593 100644 --- a/src/widgets/dropdown.cpp +++ b/src/widgets/dropdown.cpp @@ -501,7 +501,7 @@ void ShowDropDownMenu(Window *w, const StringID *strings, int selected, int butt */ int HideDropDownMenu(Window *pw) { - for (Window *w : Window::IterateFromBack()) { + for (Window *w : Window::Iterate()) { if (w->window_class != WC_DROPDOWN_MENU) continue; DropdownWindow *dw = dynamic_cast(w); diff --git a/src/window.cpp b/src/window.cpp index 9b51eafd8a..54671f90bc 100644 --- a/src/window.cpp +++ b/src/window.cpp @@ -1055,7 +1055,7 @@ void Window::SetShaded(bool make_shaded) */ static Window *FindChildWindow(const Window *w, WindowClass wc) { - for (Window *v : Window::IterateFromBack()) { + for (Window *v : Window::Iterate()) { if ((wc == WC_INVALID || wc == v->window_class) && v->parent == w) return v; } @@ -1129,7 +1129,7 @@ Window::~Window() */ Window *FindWindowById(WindowClass cls, WindowNumber number) { - for (Window *w : Window::IterateFromBack()) { + for (Window *w : Window::Iterate()) { if (w->window_class == cls && w->window_number == number) return w; } @@ -1144,7 +1144,7 @@ Window *FindWindowById(WindowClass cls, WindowNumber number) */ Window *FindWindowByClass(WindowClass cls) { - for (Window *w : Window::IterateFromBack()) { + for (Window *w : Window::Iterate()) { if (w->window_class == cls) return w; } @@ -1172,7 +1172,7 @@ void DeleteWindowById(WindowClass cls, WindowNumber number, bool force) void DeleteWindowByClass(WindowClass cls) { /* Note: the container remains stable, even when deleting windows. */ - for (Window *w : Window::IterateFromBack()) { + for (Window *w : Window::Iterate()) { if (w->window_class == cls) { delete w; } @@ -1188,7 +1188,7 @@ void DeleteWindowByClass(WindowClass cls) void DeleteCompanyWindows(CompanyID id) { /* Note: the container remains stable, even when deleting windows. */ - for (Window *w : Window::IterateFromBack()) { + for (Window *w : Window::Iterate()) { if (w->owner == id) { delete w; } @@ -1207,7 +1207,7 @@ void DeleteCompanyWindows(CompanyID id) */ void ChangeWindowOwner(Owner old_owner, Owner new_owner) { - for (Window *w : Window::IterateFromBack()) { + for (Window *w : Window::Iterate()) { if (w->owner != old_owner) continue; switch (w->window_class) { @@ -1576,7 +1576,7 @@ static bool IsGoodAutoPlace1(int left, int top, int width, int height, int toolb if (left < 0 || top < toolbar_y || right > _screen.width || bottom > _screen.height) return false; /* Make sure it is not obscured by any window. */ - for (const Window *w : Window::IterateFromBack()) { + for (const Window *w : Window::Iterate()) { if (w->window_class == WC_MAIN_WINDOW) continue; if (right > w->left && @@ -1621,7 +1621,7 @@ static bool IsGoodAutoPlace2(int left, int top, int width, int height, int toolb if (top < toolbar_y || top > _screen.height - (height >> 2)) return false; /* Make sure it is not obscured by any window. */ - for (const Window *w : Window::IterateFromBack()) { + for (const Window *w : Window::Iterate()) { if (w->window_class == WC_MAIN_WINDOW) continue; if (left + width > w->left && @@ -1658,7 +1658,7 @@ static Point GetAutoPlacePosition(int width, int height) * The new window must be entirely on-screen, and not overlap with an existing window. * Eight starting points are tried, two at each corner. */ - for (const Window *w : Window::IterateFromBack()) { + for (const Window *w : Window::Iterate()) { if (w->window_class == WC_MAIN_WINDOW) continue; if (IsGoodAutoPlace1(w->left + w->width, w->top, width, height, toolbar_y, pt)) return pt; @@ -1675,7 +1675,7 @@ static Point GetAutoPlacePosition(int width, int height) * The new window may be partly off-screen, and must not overlap with an existing window. * Only four starting points are tried. */ - for (const Window *w : Window::IterateFromBack()) { + for (const Window *w : Window::Iterate()) { if (w->window_class == WC_MAIN_WINDOW) continue; if (IsGoodAutoPlace2(w->left + w->width, w->top, width, height, toolbar_y, pt)) return pt; @@ -1692,7 +1692,7 @@ static Point GetAutoPlacePosition(int width, int height) int offset_y = std::max(NWidgetLeaf::closebox_dimension.height, FONT_HEIGHT_NORMAL + WD_CAPTIONTEXT_TOP + WD_CAPTIONTEXT_BOTTOM); restart: - for (const Window *w : Window::IterateFromBack()) { + for (const Window *w : Window::Iterate()) { if (w->left == left && w->top == top) { left += offset_x; top += offset_y; @@ -1896,7 +1896,7 @@ void UnInitWindowSystem() { UnshowCriticalError(); - for (Window *w : Window::IterateFromFront()) delete w; + for (Window *w : Window::Iterate()) delete w; for (Window *w = _z_front_window; w != nullptr; /* nothing */) { Window *to_del = w; @@ -1925,7 +1925,7 @@ static void DecreaseWindowCounters() if (_scroller_click_timeout != 0) _scroller_click_timeout--; if (hundredth_tick_timeout != 0) hundredth_tick_timeout--; - for (Window *w : Window::IterateFromFront()) { + for (Window *w : Window::Iterate()) { if (!_network_dedicated && hundredth_tick_timeout == 0) w->OnHundredthTick(); if (_scroller_click_timeout == 0) { @@ -1951,7 +1951,7 @@ static void DecreaseWindowCounters() w->OnMouseLoop(); } - for (Window *w : Window::IterateFromFront()) { + for (Window *w : Window::Iterate()) { if ((w->flags & WF_TIMEOUT) && --w->timeout_timer == 0) { CLRBITS(w->flags, WF_TIMEOUT); @@ -2190,7 +2190,7 @@ static EventState HandleWindowDragging() if (_left_button_down && _cursor.delta.x == 0 && _cursor.delta.y == 0) return ES_HANDLED; /* Otherwise find the window... */ - for (Window *w : Window::IterateFromBack()) { + for (Window *w : Window::Iterate()) { if (w->flags & WF_DRAGGING) { /* Stop the dragging if the left mouse button was released */ if (!_left_button_down) { @@ -2210,7 +2210,7 @@ static EventState HandleWindowDragging() int vsnap = _settings_client.gui.window_snap_radius; int delta; - for (const Window *v : Window::IterateFromBack()) { + for (const Window *v : Window::Iterate()) { if (v == w) continue; // Don't snap at yourself if (y + w->height > v->top && y < v->top + v->height) { @@ -2423,7 +2423,7 @@ static void HandleScrollbarScrolling(Window *w) */ static EventState HandleActiveWidget() { - for (Window *w : Window::IterateFromBack()) { + for (Window *w : Window::Iterate()) { if (w->mouse_capture_widget >= 0) { /* Abort if no button is clicked any more. */ if (!_left_button_down) { @@ -3095,7 +3095,7 @@ void InputLoop() */ void CallWindowRealtimeTickEvent(uint delta_ms) { - for (Window *w : Window::IterateFromFront()) { + for (Window *w : Window::Iterate()) { w->OnRealtimeTick(delta_ms); } } @@ -3124,7 +3124,7 @@ void UpdateWindows() } /* Process invalidations before anything else. */ - for (Window *w : Window::IterateFromFront()) { + for (Window *w : Window::Iterate()) { w->ProcessScheduledInvalidations(); w->ProcessHighlightedInvalidations(); } @@ -3157,7 +3157,7 @@ void UpdateWindows() if (window_timer.HasElapsed()) { window_timer.SetInterval(MILLISECONDS_PER_TICK); - for (Window *w : Window::IterateFromFront()) { + for (Window *w : Window::Iterate()) { if ((w->flags & WF_WHITE_BORDER) && --w->white_border_timer == 0) { CLRBITS(w->flags, WF_WHITE_BORDER); w->SetDirty(); @@ -3167,7 +3167,7 @@ void UpdateWindows() DrawDirtyBlocks(); - for (Window *w : Window::IterateFromBack()) { + for (Window *w : Window::Iterate()) { /* Update viewport only if window is not shaded. */ if (w->viewport != nullptr && !w->IsShaded()) UpdateViewportPosition(w); } @@ -3183,7 +3183,7 @@ void UpdateWindows() */ void SetWindowDirty(WindowClass cls, WindowNumber number) { - for (const Window *w : Window::IterateFromBack()) { + for (const Window *w : Window::Iterate()) { if (w->window_class == cls && w->window_number == number) w->SetDirty(); } } @@ -3196,7 +3196,7 @@ void SetWindowDirty(WindowClass cls, WindowNumber number) */ void SetWindowWidgetDirty(WindowClass cls, WindowNumber number, byte widget_index) { - for (const Window *w : Window::IterateFromBack()) { + for (const Window *w : Window::Iterate()) { if (w->window_class == cls && w->window_number == number) { w->SetWidgetDirty(widget_index); } @@ -3209,7 +3209,7 @@ void SetWindowWidgetDirty(WindowClass cls, WindowNumber number, byte widget_inde */ void SetWindowClassesDirty(WindowClass cls) { - for (const Window *w : Window::IterateFromBack()) { + for (const Window *w : Window::Iterate()) { if (w->window_class == cls) w->SetDirty(); } } @@ -3281,7 +3281,7 @@ void Window::ProcessHighlightedInvalidations() */ void InvalidateWindowData(WindowClass cls, WindowNumber number, int data, bool gui_scope) { - for (Window *w : Window::IterateFromBack()) { + for (Window *w : Window::Iterate()) { if (w->window_class == cls && w->window_number == number) { w->InvalidateData(data, gui_scope); } @@ -3298,7 +3298,7 @@ void InvalidateWindowData(WindowClass cls, WindowNumber number, int data, bool g */ void InvalidateWindowClassesData(WindowClass cls, int data, bool gui_scope) { - for (Window *w : Window::IterateFromBack()) { + for (Window *w : Window::Iterate()) { if (w->window_class == cls) { w->InvalidateData(data, gui_scope); } @@ -3310,7 +3310,7 @@ void InvalidateWindowClassesData(WindowClass cls, int data, bool gui_scope) */ void CallWindowGameTickEvent() { - for (Window *w : Window::IterateFromFront()) { + for (Window *w : Window::Iterate()) { w->OnGameTick(); } } @@ -3324,7 +3324,7 @@ void CallWindowGameTickEvent() void DeleteNonVitalWindows() { /* Note: the container remains stable, even when deleting windows. */ - for (const Window *w : Window::IterateFromBack()) { + for (const Window *w : Window::Iterate()) { if (w->window_class != WC_MAIN_WINDOW && w->window_class != WC_SELECT_GAME && w->window_class != WC_MAIN_TOOLBAR && @@ -3350,7 +3350,7 @@ void DeleteAllNonVitalWindows() DeleteNonVitalWindows(); /* Note: the container remains stable, even when deleting windows. */ - for (const Window *w : Window::IterateFromBack()) { + for (const Window *w : Window::Iterate()) { if (w->flags & WF_STICKY) { delete w; } @@ -3375,13 +3375,13 @@ void DeleteAllMessages() void DeleteConstructionWindows() { /* Note: the container remains stable, even when deleting windows. */ - for (const Window *w : Window::IterateFromBack()) { + for (const Window *w : Window::Iterate()) { if (w->window_desc->flags & WDF_CONSTRUCTION) { delete w; } } - for (const Window *w : Window::IterateFromBack()) w->SetDirty(); + for (const Window *w : Window::Iterate()) w->SetDirty(); } /** Delete all always on-top windows to get an empty screen */ @@ -3400,7 +3400,7 @@ void ReInitAllWindows(bool zoom_changed) extern void InitDepotWindowBlockSizes(); InitDepotWindowBlockSizes(); - for (Window *w : Window::IterateFromBack()) { + for (Window *w : Window::Iterate()) { if (zoom_changed) w->nested_root->AdjustPaddingForZoom(); w->ReInit(); } @@ -3490,7 +3490,7 @@ int PositionNetworkChatWindow(Window *w) */ void ChangeVehicleViewports(VehicleID from_index, VehicleID to_index) { - for (const Window *w : Window::IterateFromBack()) { + for (const Window *w : Window::Iterate()) { if (w->viewport != nullptr && w->viewport->follow_vehicle == from_index) { w->viewport->follow_vehicle = to_index; w->SetDirty(); @@ -3508,7 +3508,7 @@ void RelocateAllWindows(int neww, int newh) { DeleteWindowById(WC_DROPDOWN_MENU, 0); - for (Window *w : Window::IterateFromBack()) { + for (Window *w : Window::Iterate()) { int left, top; /* XXX - this probably needs something more sane. For example specifying * in a 'backup'-desc that the window should always be centered. */ diff --git a/src/window_gui.h b/src/window_gui.h index 7f7ab2667e..2b7c693674 100644 --- a/src/window_gui.h +++ b/src/window_gui.h @@ -853,6 +853,7 @@ public: WindowIterator begin() { return WindowIterator(Tfront ? _z_front_window : _z_back_window); } WindowIterator end() { return WindowIterator(nullptr); } }; + using Iterate = AllWindows; //!< Iterate all windows in whatever order is easiest. using IterateFromBack = AllWindows; //!< Iterate all windows in Z order from back to front. using IterateFromFront = AllWindows; //!< Iterate all windows in Z order from front to back. }; From f6d5c0136e5ac130fd887daf1b07c13a160c7fc3 Mon Sep 17 00:00:00 2001 From: frosch Date: Sun, 9 May 2021 17:12:34 +0200 Subject: [PATCH 53/61] Codechange: make Window destruction not rely on undefined behavior. --- src/window.cpp | 136 +++++++++-------------------------------------- src/window_gui.h | 67 +++++++++++++---------- 2 files changed, 63 insertions(+), 140 deletions(-) diff --git a/src/window.cpp b/src/window.cpp index 54671f90bc..af47214454 100644 --- a/src/window.cpp +++ b/src/window.cpp @@ -53,10 +53,8 @@ static Point _drag_delta; ///< delta between mouse cursor and upper left corner static Window *_mouseover_last_w = nullptr; ///< Window of the last OnMouseOver event. static Window *_last_scroll_window = nullptr; ///< Window of the last scroll event. -/** List of windows opened at the screen sorted from the front. */ -Window *_z_front_window = nullptr; -/** List of windows opened at the screen sorted from the back. */ -Window *_z_back_window = nullptr; +/** List of windows opened at the screen sorted from the front to back. */ +WindowList _z_windows; /** If false, highlight is white, otherwise the by the widget defined colour. */ bool _window_highlight_colour = false; @@ -1109,16 +1107,7 @@ Window::~Window() free(this->nested_array); // Contents is released through deletion of #nested_root. delete this->nested_root; - /* - * Make fairly sure that this is written, and not "optimized" away. - * The delete operator is overwritten to not delete it; the deletion - * happens at a later moment in time after the window has been - * removed from the list of windows to prevent issues with items - * being removed during the iteration as not one but more windows - * may be removed by a single call to ~Window by means of the - * DeleteChildWindows function. - */ - const_cast(this->window_class) = WC_INVALID; + *this->z_position = nullptr; } /** @@ -1231,7 +1220,7 @@ void ChangeWindowOwner(Owner old_owner, Owner new_owner) } } -static void BringWindowToFront(Window *w); +static void BringWindowToFront(Window *w, bool dirty = true); /** * Find a window and make it the relative top-window on the screen. @@ -1351,90 +1340,23 @@ static uint GetWindowZPriority(WindowClass wc) } } -/** - * Adds a window to the z-ordering, according to its z-priority. - * @param w Window to add - */ -static void AddWindowToZOrdering(Window *w) -{ - assert(w->z_front == nullptr && w->z_back == nullptr); - - if (_z_front_window == nullptr) { - /* It's the only window. */ - _z_front_window = _z_back_window = w; - w->z_front = w->z_back = nullptr; - } else { - /* Search down the z-ordering for its location. */ - Window *v = _z_front_window; - uint last_z_priority = UINT_MAX; - (void)last_z_priority; // Unused without asserts - while (v != nullptr && (v->window_class == WC_INVALID || GetWindowZPriority(v->window_class) > GetWindowZPriority(w->window_class))) { - if (v->window_class != WC_INVALID) { - /* Sanity check z-ordering, while we're at it. */ - assert(last_z_priority >= GetWindowZPriority(v->window_class)); - last_z_priority = GetWindowZPriority(v->window_class); - } - - v = v->z_back; - } - - if (v == nullptr) { - /* It's the new back window. */ - w->z_front = _z_back_window; - w->z_back = nullptr; - _z_back_window->z_back = w; - _z_back_window = w; - } else if (v == _z_front_window) { - /* It's the new front window. */ - w->z_front = nullptr; - w->z_back = _z_front_window; - _z_front_window->z_front = w; - _z_front_window = w; - } else { - /* It's somewhere else in the z-ordering. */ - w->z_front = v->z_front; - w->z_back = v; - v->z_front->z_back = w; - v->z_front = w; - } - } -} - - -/** - * Removes a window from the z-ordering. - * @param w Window to remove - */ -static void RemoveWindowFromZOrdering(Window *w) -{ - if (w->z_front == nullptr) { - assert(_z_front_window == w); - _z_front_window = w->z_back; - } else { - w->z_front->z_back = w->z_back; - } - - if (w->z_back == nullptr) { - assert(_z_back_window == w); - _z_back_window = w->z_front; - } else { - w->z_back->z_front = w->z_front; - } - - w->z_front = w->z_back = nullptr; -} - /** * On clicking on a window, make it the frontmost window of all windows with an equal * or lower z-priority. The window is marked dirty for a repaint * @param w window that is put into the relative foreground + * @param dirty whether to mark the window dirty */ -static void BringWindowToFront(Window *w) +static void BringWindowToFront(Window *w, bool dirty) { - RemoveWindowFromZOrdering(w); - AddWindowToZOrdering(w); + auto priority = GetWindowZPriority(w->window_class); + WindowList::iterator dest = _z_windows.begin(); + while (dest != _z_windows.end() && (*dest == nullptr || GetWindowZPriority((*dest)->window_class) <= priority)) ++dest; - w->SetDirty(); + if (dest != w->z_position) { + _z_windows.splice(dest, _z_windows, w->z_position); + } + + if (dirty) w->SetDirty(); } /** @@ -1476,7 +1398,7 @@ void Window::InitializeData(WindowNumber window_number) if (!EditBoxInGlobalFocus() || this->nested_root->GetWidgetOfType(WWT_EDITBOX) != nullptr) SetFocusedWindow(this); /* Insert the window into the correct location in the z-ordering. */ - AddWindowToZOrdering(this); + BringWindowToFront(this, false); } /** @@ -1848,6 +1770,7 @@ void Window::InitNested(WindowNumber window_number) */ Window::Window(WindowDesc *desc) : window_desc(desc), mouse_capture_widget(-1) { + this->z_position = _z_windows.insert(_z_windows.end(), this); } /** @@ -1875,8 +1798,6 @@ void InitWindowSystem() { IConsoleClose(); - _z_back_window = nullptr; - _z_front_window = nullptr; _focused_window = nullptr; _mouseover_last_w = nullptr; _last_scroll_window = nullptr; @@ -1898,14 +1819,7 @@ void UnInitWindowSystem() for (Window *w : Window::Iterate()) delete w; - for (Window *w = _z_front_window; w != nullptr; /* nothing */) { - Window *to_del = w; - w = w->z_back; - free(to_del); - } - - _z_front_window = nullptr; - _z_back_window = nullptr; + _z_windows.clear(); } /** @@ -3068,15 +2982,13 @@ void InputLoop() CheckSoftLimit(); - /* Do the actual free of the deleted windows. */ - for (Window *v = _z_front_window; v != nullptr; /* nothing */) { - Window *w = v; - v = v->z_back; - - if (w->window_class != WC_INVALID) continue; - - RemoveWindowFromZOrdering(w); - free(w); + /* Remove dead entries from the window list */ + for (auto it = _z_windows.begin(); it != _z_windows.end(); ) { + if (*it == nullptr) { + it = _z_windows.erase(it); + } else { + ++it; + } } if (_input_events_this_tick != 0) { diff --git a/src/window_gui.h b/src/window_gui.h index 2b7c693674..1195c6c491 100644 --- a/src/window_gui.h +++ b/src/window_gui.h @@ -10,6 +10,8 @@ #ifndef WINDOW_GUI_H #define WINDOW_GUI_H +#include + #include "vehicle_type.h" #include "viewport_type.h" #include "company_type.h" @@ -143,8 +145,8 @@ void DrawFrameRect(int left, int top, int right, int bottom, Colours colour, Fra void DrawCaption(const Rect &r, Colours colour, Owner owner, TextColour text_colour, StringID str, StringAlignment align); /* window.cpp */ -extern Window *_z_front_window; -extern Window *_z_back_window; +using WindowList = std::list; +extern WindowList _z_windows; extern Window *_focused_window; @@ -293,19 +295,7 @@ public: * to destruct them all at the same time too, which is kinda hard. * @param size the amount of space not to allocate */ - inline void *operator new[](size_t size) - { - NOT_REACHED(); - } - - /** - * Helper allocation function to disallow something. - * Don't free the window directly; it corrupts the linked list when iterating - * @param ptr the pointer not to free - */ - inline void operator delete(void *ptr) - { - } + inline void *operator new[](size_t size) = delete; WindowDesc *window_desc; ///< Window description WindowFlags flags; ///< Window flags @@ -336,8 +326,7 @@ public: int mouse_capture_widget; ///< Widgetindex of current mouse capture widget (e.g. dragged scrollbar). -1 if no widget has mouse capture. Window *parent; ///< Parent window. - Window *z_front; ///< The window in front of us in z-order. - Window *z_back; ///< The window behind us in z-order. + WindowList::iterator z_position; template inline const NWID *GetWidget(uint widnum) const; @@ -813,9 +802,9 @@ public: /** * Iterator to iterate all valid Windows - * @tparam Tfront Wether we iterate from front + * @tparam TtoBack whether we iterate towards the back. */ - template + template struct WindowIterator { typedef Window *value_type; typedef value_type *pointer; @@ -823,22 +812,35 @@ public: typedef size_t difference_type; typedef std::forward_iterator_tag iterator_category; - explicit WindowIterator(const Window *start) : w(const_cast(start)) + explicit WindowIterator(WindowList::iterator start) : it(start) { this->Validate(); } + explicit WindowIterator(const Window *w) : it(w->z_position) {} - bool operator==(const WindowIterator &other) const { return this->w == other.w; } + bool operator==(const WindowIterator &other) const { return this->it == other.it; } bool operator!=(const WindowIterator &other) const { return !(*this == other); } - Window * operator*() const { return this->w; } + Window * operator*() const { return *this->it; } WindowIterator & operator++() { this->Next(); this->Validate(); return *this; } - bool IsEnd() const { return this->w == nullptr; } + bool IsEnd() const { return this->it == _z_windows.end(); } private: - Window *w; - void Validate() { while (this->w != nullptr && this->w->window_class == WC_INVALID) this->Next(); } - void Next() { if (this->w != nullptr) this->w = Tfront ? this->w->z_back : this->w->z_front; } + WindowList::iterator it; + void Validate() + { + while (!this->IsEnd() && *this->it == nullptr) this->Next(); + } + void Next() + { + if constexpr (!TtoBack) { + ++this->it; + } else if (this->it == _z_windows.begin()) { + this->it = _z_windows.end(); + } else { + --this->it; + } + } }; using IteratorToFront = WindowIterator; //!< Iterate in Z order towards front. using IteratorToBack = WindowIterator; //!< Iterate in Z order towards back. @@ -850,8 +852,17 @@ public: template struct AllWindows { AllWindows() {} - WindowIterator begin() { return WindowIterator(Tfront ? _z_front_window : _z_back_window); } - WindowIterator end() { return WindowIterator(nullptr); } + WindowIterator begin() + { + if constexpr (Tfront) { + auto back = _z_windows.end(); + if (back != _z_windows.begin()) --back; + return WindowIterator(back); + } else { + return WindowIterator(_z_windows.begin()); + } + } + WindowIterator end() { return WindowIterator(_z_windows.end()); } }; using Iterate = AllWindows; //!< Iterate all windows in whatever order is easiest. using IterateFromBack = AllWindows; //!< Iterate all windows in Z order from back to front. From 5bd81448539b63519d70ba85d4833e446f0597fe Mon Sep 17 00:00:00 2001 From: frosch Date: Wed, 12 May 2021 23:40:03 +0200 Subject: [PATCH 54/61] Fix #9256, 12e43c697d2: invalid read after free. (#9258) This also changes ScriptEventVehicleAutoReplaced when replacing wagons: The event is now only spawned, if the head engine changes, so only if the VehicleID of the consist changes. Previously replacing wagons spawned an event with OldVehicleID==NewVehicleID. --- src/autoreplace_cmd.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/autoreplace_cmd.cpp b/src/autoreplace_cmd.cpp index d9996bb117..d5f031aa65 100644 --- a/src/autoreplace_cmd.cpp +++ b/src/autoreplace_cmd.cpp @@ -614,6 +614,7 @@ static CommandCost ReplaceChain(Vehicle **chain, DoCommandFlag flags, bool wagon /* Success ! */ if ((flags & DC_EXEC) != 0 && new_head != old_head) { *chain = new_head; + AI::NewEvent(old_head->owner, new ScriptEventVehicleAutoReplaced(old_head->index, new_head->index)); } /* Transfer cargo of old vehicles and sell them */ @@ -631,10 +632,7 @@ static CommandCost ReplaceChain(Vehicle **chain, DoCommandFlag flags, bool wagon cost.AddCost(DoCommand(0, w->index, 0, flags | DC_AUTOREPLACE, GetCmdSellVeh(w))); if ((flags & DC_EXEC) != 0) { old_vehs[i] = nullptr; - if (i == 0) { - AI::NewEvent(old_head->owner, new ScriptEventVehicleAutoReplaced(old_head->index, new_head->index)); - old_head = nullptr; - } + if (i == 0) old_head = nullptr; } } From 38c97e14926f4bc538c20b24f8a3decdef1668f9 Mon Sep 17 00:00:00 2001 From: glx22 Date: Wed, 12 May 2021 16:45:28 +0200 Subject: [PATCH 55/61] Codechange: Replace TILE_AREA_LOOP with range-based for loops --- src/industry_cmd.cpp | 20 ++++++++++---------- src/newgrf_airporttiles.cpp | 2 +- src/newgrf_industrytiles.cpp | 4 ++-- src/newgrf_object.cpp | 2 +- src/newgrf_station.cpp | 6 +++--- src/object_cmd.cpp | 14 +++++++------- src/openttd.cpp | 4 ++-- src/road_gui.cpp | 2 +- src/saveload/station_sl.cpp | 2 +- src/script/api/script_industry.cpp | 4 ++-- src/script/api/script_order.cpp | 8 ++++---- src/script/api/script_tilelist.cpp | 8 ++++---- src/smallmap_gui.cpp | 2 +- src/station.cpp | 8 ++++---- src/station_base.h | 4 ++-- src/station_cmd.cpp | 24 ++++++++++++------------ src/station_gui.cpp | 4 ++-- src/subsidy.cpp | 4 ++-- src/terraform_gui.cpp | 10 +++++----- src/tilearea.cpp | 18 ++++++++++++++++++ src/tilearea_type.h | 23 +++++++++++++++-------- src/tree_cmd.cpp | 2 +- src/water_cmd.cpp | 2 +- 23 files changed, 101 insertions(+), 76 deletions(-) diff --git a/src/industry_cmd.cpp b/src/industry_cmd.cpp index f9e75ed880..5bd2a3dc27 100644 --- a/src/industry_cmd.cpp +++ b/src/industry_cmd.cpp @@ -149,7 +149,7 @@ Industry::~Industry() const bool has_neutral_station = this->neutral_station != nullptr; - TILE_AREA_LOOP(tile_cur, this->location) { + for (TileIndex tile_cur : this->location) { if (IsTileType(tile_cur, MP_INDUSTRY)) { if (GetIndustryIndex(tile_cur) == this->index) { DeleteNewGRFInspectWindow(GSF_INDUSTRYTILES, tile_cur); @@ -164,7 +164,7 @@ Industry::~Industry() if (has_neutral_station) { /* Remove possible docking tiles */ - TILE_AREA_LOOP(tile_cur, this->location) { + for (TileIndex tile_cur : this->location) { ClearDockingTilesCheckingNeighbours(tile_cur); } } @@ -173,7 +173,7 @@ Industry::~Industry() TileArea ta = TileArea(this->location.tile, 0, 0).Expand(21); /* Remove the farmland and convert it to regular tiles over time. */ - TILE_AREA_LOOP(tile_cur, ta) { + for (TileIndex tile_cur : ta) { if (IsTileType(tile_cur, MP_CLEAR) && IsClearGround(tile_cur, CLEAR_FIELDS) && GetIndustryIndexOfField(tile_cur) == this->index) { SetIndustryIndexOfField(tile_cur, INVALID_INDUSTRY); @@ -1041,7 +1041,7 @@ static void PlantFarmField(TileIndex tile, IndustryID industry) /* check the amount of bad tiles */ int count = 0; - TILE_AREA_LOOP(cur_tile, ta) { + for (TileIndex cur_tile : ta) { assert(cur_tile < MapSize()); count += IsSuitableForFarmField(cur_tile, false); } @@ -1053,7 +1053,7 @@ static void PlantFarmField(TileIndex tile, IndustryID industry) uint field_type = GB(r, 8, 8) * 9 >> 8; /* make field */ - TILE_AREA_LOOP(cur_tile, ta) { + for (TileIndex cur_tile : ta) { assert(cur_tile < MapSize()); if (IsSuitableForFarmField(cur_tile, true)) { MakeField(cur_tile, field_type, industry); @@ -1115,7 +1115,7 @@ static bool SearchLumberMillTrees(TileIndex tile, void *user_data) static void ChopLumberMillTrees(Industry *i) { /* We only want to cut trees if all tiles are completed. */ - TILE_AREA_LOOP(tile_cur, i->location) { + for (TileIndex tile_cur : i->location) { if (i->TileBelongsToIndustry(tile_cur)) { if (!IsIndustryCompleted(tile_cur)) return; } @@ -1532,7 +1532,7 @@ static bool CheckCanTerraformSurroundingTiles(TileIndex tile, uint height, int i if (TileX(tile) == 0 || TileY(tile) == 0 || GetTileType(tile) == MP_VOID) return false; TileArea ta(tile - TileDiffXY(1, 1), 2, 2); - TILE_AREA_LOOP(tile_walk, ta) { + for (TileIndex tile_walk : ta) { uint curh = TileHeight(tile_walk); /* Is the tile clear? */ if ((GetTileType(tile_walk) != MP_CLEAR) && (GetTileType(tile_walk) != MP_TREES)) return false; @@ -1587,7 +1587,7 @@ static bool CheckIfCanLevelIndustryPlatform(TileIndex tile, DoCommandFlag flags, * Perform terraforming as OWNER_TOWN to disable autoslope and town ratings. */ Backup cur_company(_current_company, OWNER_TOWN, FILE_LINE); - TILE_AREA_LOOP(tile_walk, ta) { + for (TileIndex tile_walk : ta) { uint curh = TileHeight(tile_walk); if (curh != h) { /* This tile needs terraforming. Check if we can do that without @@ -1607,7 +1607,7 @@ static bool CheckIfCanLevelIndustryPlatform(TileIndex tile, DoCommandFlag flags, if (flags & DC_EXEC) { /* Terraform the land under the industry */ - TILE_AREA_LOOP(tile_walk, ta) { + for (TileIndex tile_walk : ta) { uint curh = TileHeight(tile_walk); while (curh != h) { /* We give the terraforming for free here, because we can't calculate @@ -1639,7 +1639,7 @@ static CommandCost CheckIfFarEnoughFromConflictingIndustry(TileIndex tile, int t if (Industry::GetNumItems() > (size_t) (dmax * dmax * 2)) { const Industry* i = nullptr; TileArea tile_area = TileArea(tile, 1, 1).Expand(dmax); - TILE_AREA_LOOP(atile, tile_area) { + for (TileIndex atile : tile_area) { if (GetTileType(atile) == MP_INDUSTRY) { const Industry *i2 = Industry::GetByTile(atile); if (i == i2) continue; diff --git a/src/newgrf_airporttiles.cpp b/src/newgrf_airporttiles.cpp index a437fb596a..a049064515 100644 --- a/src/newgrf_airporttiles.cpp +++ b/src/newgrf_airporttiles.cpp @@ -307,7 +307,7 @@ void AirportAnimationTrigger(Station *st, AirpAnimationTrigger trigger, CargoID { if (st->airport.tile == INVALID_TILE) return; - TILE_AREA_LOOP(tile, st->airport) { + for (TileIndex tile : st->airport) { if (st->TileBelongsToAirport(tile)) AirportTileAnimationTrigger(st, tile, trigger, cargo_type); } } diff --git a/src/newgrf_industrytiles.cpp b/src/newgrf_industrytiles.cpp index e9c99f6cd5..0e48d0be97 100644 --- a/src/newgrf_industrytiles.cpp +++ b/src/newgrf_industrytiles.cpp @@ -287,7 +287,7 @@ bool StartStopIndustryTileAnimation(const Industry *ind, IndustryAnimationTrigge { bool ret = true; uint32 random = Random(); - TILE_AREA_LOOP(tile, ind->location) { + for (TileIndex tile : ind->location) { if (ind->TileBelongsToIndustry(tile)) { if (StartStopIndustryTileAnimation(tile, iat, random)) { SB(random, 0, 16, Random()); @@ -372,7 +372,7 @@ void TriggerIndustryTile(TileIndex tile, IndustryTileTrigger trigger) void TriggerIndustry(Industry *ind, IndustryTileTrigger trigger) { uint32 reseed_industry = 0; - TILE_AREA_LOOP(tile, ind->location) { + for (TileIndex tile : ind->location) { if (ind->TileBelongsToIndustry(tile)) { DoTriggerIndustryTile(tile, trigger, ind, reseed_industry); } diff --git a/src/newgrf_object.cpp b/src/newgrf_object.cpp index 3c069f4c95..640ac46705 100644 --- a/src/newgrf_object.cpp +++ b/src/newgrf_object.cpp @@ -554,7 +554,7 @@ void TriggerObjectAnimation(Object *o, ObjectAnimationTrigger trigger, const Obj { if (!HasBit(spec->animation.triggers, trigger)) return; - TILE_AREA_LOOP(tile, o->location) { + for (TileIndex tile : o->location) { TriggerObjectTileAnimation(o, tile, trigger, spec); } } diff --git a/src/newgrf_station.cpp b/src/newgrf_station.cpp index eff5ef2b2c..fbc6f5c600 100644 --- a/src/newgrf_station.cpp +++ b/src/newgrf_station.cpp @@ -737,7 +737,7 @@ void DeallocateSpecFromStation(BaseStation *st, byte specindex) ETileArea area = ETileArea(st, INVALID_TILE, TA_WHOLE); /* Check all tiles over the station to check if the specindex is still in use */ - TILE_AREA_LOOP(tile, area) { + for (TileIndex tile : area) { if (st->TileBelongsToRailStation(tile) && GetCustomStationSpecIndex(tile) == specindex) { return; } @@ -939,7 +939,7 @@ void TriggerStationAnimation(BaseStation *st, TileIndex tile, StationAnimationTr ETileArea area = ETileArea(st, tile, tas[trigger]); /* Check all tiles over the station to check if the specindex is still in use */ - TILE_AREA_LOOP(tile, area) { + for (TileIndex tile : area) { if (st->TileBelongsToRailStation(tile)) { const StationSpec *ss = GetStationSpec(tile); if (ss != nullptr && HasBit(ss->animation.triggers, trigger)) { @@ -995,7 +995,7 @@ void TriggerStationRandomisation(Station *st, TileIndex tile, StationRandomTrigg uint32 used_triggers = 0; /* Check all tiles over the station to check if the specindex is still in use */ - TILE_AREA_LOOP(tile, area) { + for (TileIndex tile : area) { if (st->TileBelongsToRailStation(tile)) { const StationSpec *ss = GetStationSpec(tile); if (ss == nullptr) continue; diff --git a/src/object_cmd.cpp b/src/object_cmd.cpp index 7a2ff26524..c11409d4ba 100644 --- a/src/object_cmd.cpp +++ b/src/object_cmd.cpp @@ -114,7 +114,7 @@ void BuildObject(ObjectType type, TileIndex tile, CompanyID owner, Town *town, u assert(o->town != nullptr); - TILE_AREA_LOOP(t, ta) { + for (TileIndex t : ta) { WaterClass wc = (IsWaterTile(t) ? GetWaterClass(t) : WATER_CLASS_INVALID); /* Update company infrastructure counts for objects build on canals owned by nobody. */ if (wc == WATER_CLASS_CANAL && owner != OWNER_NONE && (IsTileOwner(tile, OWNER_NONE) || IsTileOwner(tile, OWNER_WATER))) { @@ -136,7 +136,7 @@ void BuildObject(ObjectType type, TileIndex tile, CompanyID owner, Town *town, u static void IncreaseAnimationStage(TileIndex tile) { TileArea ta = Object::GetByTile(tile)->location; - TILE_AREA_LOOP(t, ta) { + for (TileIndex t : ta) { SetAnimationFrame(t, GetAnimationFrame(t) + 1); MarkTileDirtyByTile(t); } @@ -230,7 +230,7 @@ CommandCost CmdBuildObject(TileIndex tile, DoCommandFlag flags, uint32 p1, uint3 * some information about the tiles. */ bool allow_water = (spec->flags & (OBJECT_FLAG_BUILT_ON_WATER | OBJECT_FLAG_NOT_ON_LAND)) != 0; bool allow_ground = (spec->flags & OBJECT_FLAG_NOT_ON_LAND) == 0; - TILE_AREA_LOOP(t, ta) { + for (TileIndex t : ta) { if (HasTileWaterGround(t)) { if (!allow_water) return_cmd_error(STR_ERROR_CAN_T_BUILD_ON_WATER); if (!IsWaterTile(t)) { @@ -263,7 +263,7 @@ CommandCost CmdBuildObject(TileIndex tile, DoCommandFlag flags, uint32 p1, uint3 int allowed_z; if (GetTileSlope(tile, &allowed_z) != SLOPE_FLAT) allowed_z++; - TILE_AREA_LOOP(t, ta) { + for (TileIndex t : ta) { uint16 callback = CALLBACK_FAILED; if (HasBit(spec->callback_mask, CBM_OBJ_SLOPE_CHECK)) { TileIndex diff = t - tile; @@ -283,7 +283,7 @@ CommandCost CmdBuildObject(TileIndex tile, DoCommandFlag flags, uint32 p1, uint3 if (flags & DC_EXEC) { /* This is basically a copy of the loop above with the exception that we now * execute the commands and don't check for errors, since that's already done. */ - TILE_AREA_LOOP(t, ta) { + for (TileIndex t : ta) { if (HasTileWaterGround(t)) { if (!IsWaterTile(t)) { DoCommand(t, 0, 0, (flags & ~DC_NO_WATER) | DC_NO_MODIFY_TOWN_RATING, CMD_LANDSCAPE_CLEAR); @@ -297,7 +297,7 @@ CommandCost CmdBuildObject(TileIndex tile, DoCommandFlag flags, uint32 p1, uint3 if (cost.Failed()) return cost; /* Finally do a check for bridges. */ - TILE_AREA_LOOP(t, ta) { + for (TileIndex t : ta) { if (IsBridgeAbove(t) && ( !(spec->flags & OBJECT_FLAG_ALLOW_UNDER_BRIDGE) || (GetTileMaxZ(t) + spec->height >= GetBridgeHeight(GetSouthernBridgeEnd(t))))) { @@ -438,7 +438,7 @@ static Foundation GetFoundation_Object(TileIndex tile, Slope tileh) static void ReallyClearObjectTile(Object *o) { Object::DecTypeCount(o->type); - TILE_AREA_LOOP(tile_cur, o->location) { + for (TileIndex tile_cur : o->location) { DeleteNewGRFInspectWindow(GSF_OBJECTS, tile_cur); MakeWaterKeepingClass(tile_cur, GetTileOwner(tile_cur)); diff --git a/src/openttd.cpp b/src/openttd.cpp index 785ba80dda..a416f531a2 100644 --- a/src/openttd.cpp +++ b/src/openttd.cpp @@ -1265,7 +1265,7 @@ static void CheckCaches() /* Check docking tiles */ TileArea ta; std::map docking_tiles; - TILE_AREA_LOOP(tile, st->docking_station) { + for (TileIndex tile : st->docking_station) { ta.Add(tile); docking_tiles[tile] = IsDockingTile(tile); } @@ -1273,7 +1273,7 @@ static void CheckCaches() if (ta.tile != st->docking_station.tile || ta.w != st->docking_station.w || ta.h != st->docking_station.h) { DEBUG(desync, 2, "station docking mismatch: station %i, company %i", st->index, (int)st->owner); } - TILE_AREA_LOOP(tile, ta) { + for (TileIndex tile : ta) { if (docking_tiles[tile] != IsDockingTile(tile)) { DEBUG(desync, 2, "docking tile mismatch: tile %i", (int)tile); } diff --git a/src/road_gui.cpp b/src/road_gui.cpp index 9cc68028d5..ae84404a2a 100644 --- a/src/road_gui.cpp +++ b/src/road_gui.cpp @@ -163,7 +163,7 @@ void CcRoadStop(const CommandCost &result, TileIndex tile, uint32 p1, uint32 p2, if (_settings_client.sound.confirm) SndPlayTileFx(SND_1F_CONSTRUCTION_OTHER, tile); if (!_settings_client.gui.persistent_buildingtools) ResetObjectToPlace(); TileArea roadstop_area(tile, GB(p1, 0, 8), GB(p1, 8, 8)); - TILE_AREA_LOOP(cur_tile, roadstop_area) { + for (TileIndex cur_tile : roadstop_area) { ConnectRoadToStructure(cur_tile, dir); /* For a drive-through road stop build connecting road for other entrance. */ if (HasBit(p2, 1)) ConnectRoadToStructure(cur_tile, ReverseDiagDir(dir)); diff --git a/src/saveload/station_sl.cpp b/src/saveload/station_sl.cpp index c4d157c0f4..74f3a5fb9b 100644 --- a/src/saveload/station_sl.cpp +++ b/src/saveload/station_sl.cpp @@ -86,7 +86,7 @@ void MoveBuoysToWaypoints() if (train) { /* When we make a rail waypoint of the station, convert the map as well. */ - TILE_AREA_LOOP(t, train_st) { + for (TileIndex t : train_st) { if (!IsTileType(t, MP_STATION) || GetStationIndex(t) != index) continue; SB(_me[t].m6, 3, 3, STATION_WAYPOINT); diff --git a/src/script/api/script_industry.cpp b/src/script/api/script_industry.cpp index 0a5ca98d35..6f54fda14c 100644 --- a/src/script/api/script_industry.cpp +++ b/src/script/api/script_industry.cpp @@ -186,7 +186,7 @@ if (!HasHeliport(industry_id)) return INVALID_TILE; const Industry *ind = ::Industry::Get(industry_id); - TILE_AREA_LOOP(tile_cur, ind->location) { + for (TileIndex tile_cur : ind->location) { if (IsTileType(tile_cur, MP_STATION) && IsOilRig(tile_cur)) { return tile_cur; } @@ -208,7 +208,7 @@ if (!HasDock(industry_id)) return INVALID_TILE; const Industry *ind = ::Industry::Get(industry_id); - TILE_AREA_LOOP(tile_cur, ind->location) { + for (TileIndex tile_cur : ind->location) { if (IsTileType(tile_cur, MP_STATION) && IsOilRig(tile_cur)) { return tile_cur; } diff --git a/src/script/api/script_order.cpp b/src/script/api/script_order.cpp index 5166c06144..b7bcde7307 100644 --- a/src/script/api/script_order.cpp +++ b/src/script/api/script_order.cpp @@ -256,11 +256,11 @@ static int ScriptOrderPositionToRealOrderPosition(VehicleID vehicle_id, ScriptOr case OT_GOTO_STATION: { const Station *st = ::Station::Get(order->GetDestination()); if (st->train_station.tile != INVALID_TILE) { - TILE_AREA_LOOP(t, st->train_station) { + for (TileIndex t : st->train_station) { if (st->TileBelongsToRailStation(t)) return t; } } else if (st->ship_station.tile != INVALID_TILE) { - TILE_AREA_LOOP(t, st->ship_station) { + for (TileIndex t : st->ship_station) { if (IsTileType(t, MP_STATION) && (IsDock(t) || IsOilRig(t)) && GetStationIndex(t) == st->index) return t; } } else if (st->bus_stops != nullptr) { @@ -268,7 +268,7 @@ static int ScriptOrderPositionToRealOrderPosition(VehicleID vehicle_id, ScriptOr } else if (st->truck_stops != nullptr) { return st->truck_stops->xy; } else if (st->airport.tile != INVALID_TILE) { - TILE_AREA_LOOP(tile, st->airport) { + for (TileIndex tile : st->airport) { if (st->TileBelongsToAirport(tile) && !::IsHangar(tile)) return tile; } } @@ -278,7 +278,7 @@ static int ScriptOrderPositionToRealOrderPosition(VehicleID vehicle_id, ScriptOr case OT_GOTO_WAYPOINT: { const Waypoint *wp = ::Waypoint::Get(order->GetDestination()); if (wp->train_station.tile != INVALID_TILE) { - TILE_AREA_LOOP(t, wp->train_station) { + for (TileIndex t : wp->train_station) { if (wp->TileBelongsToRailStation(t)) return t; } } diff --git a/src/script/api/script_tilelist.cpp b/src/script/api/script_tilelist.cpp index 7f4e3ca5cf..8266f46ff9 100644 --- a/src/script/api/script_tilelist.cpp +++ b/src/script/api/script_tilelist.cpp @@ -21,7 +21,7 @@ void ScriptTileList::AddRectangle(TileIndex t1, TileIndex t2) if (!::IsValidTile(t2)) return; TileArea ta(t1, t2); - TILE_AREA_LOOP(t, ta) this->AddItem(t); + for (TileIndex t : ta) this->AddItem(t); } void ScriptTileList::AddTile(TileIndex tile) @@ -37,7 +37,7 @@ void ScriptTileList::RemoveRectangle(TileIndex t1, TileIndex t2) if (!::IsValidTile(t2)) return; TileArea ta(t1, t2); - TILE_AREA_LOOP(t, ta) this->RemoveItem(t); + for (TileIndex t : ta) this->RemoveItem(t); } void ScriptTileList::RemoveTile(TileIndex tile) @@ -55,7 +55,7 @@ void ScriptTileList::RemoveTile(TileIndex tile) */ static void FillIndustryCatchment(const Industry *i, int radius, BitmapTileArea &bta) { - TILE_AREA_LOOP(cur_tile, i->location) { + for (TileIndex cur_tile : i->location) { if (!::IsTileType(cur_tile, MP_INDUSTRY) || ::GetIndustryIndex(cur_tile) != i->index) continue; int tx = TileX(cur_tile); @@ -156,7 +156,7 @@ ScriptTileList_StationType::ScriptTileList_StationType(StationID station_id, Scr if ((station_type & ScriptStation::STATION_DOCK) != 0) station_type_value |= (1 << ::STATION_DOCK) | (1 << ::STATION_OILRIG); TileArea ta(::TileXY(rect->left, rect->top), rect->right - rect->left + 1, rect->bottom - rect->top + 1); - TILE_AREA_LOOP(cur_tile, ta) { + for (TileIndex cur_tile : ta) { if (!::IsTileType(cur_tile, MP_STATION)) continue; if (::GetStationIndex(cur_tile) != station_id) continue; if (!HasBit(station_type_value, ::GetStationType(cur_tile))) continue; diff --git a/src/smallmap_gui.cpp b/src/smallmap_gui.cpp index b00cf0e7a9..17d1d25cb0 100644 --- a/src/smallmap_gui.cpp +++ b/src/smallmap_gui.cpp @@ -750,7 +750,7 @@ inline uint32 SmallMapWindow::GetTileColours(const TileArea &ta) const TileIndex tile = INVALID_TILE; // Position of the most important tile. TileType et = MP_VOID; // Effective tile type at that position. - TILE_AREA_LOOP(ti, ta) { + for (TileIndex ti : ta) { TileType ttype = GetTileType(ti); switch (ttype) { diff --git a/src/station.cpp b/src/station.cpp index f859495d19..8926773122 100644 --- a/src/station.cpp +++ b/src/station.cpp @@ -416,7 +416,7 @@ void Station::RecomputeCatchment() if (!_settings_game.station.serve_neutral_industries && this->industry != nullptr) { /* Station is associated with an industry, so we only need to deliver to that industry. */ this->catchment_tiles.Initialize(this->industry->location); - TILE_AREA_LOOP(tile, this->industry->location) { + for (TileIndex tile : this->industry->location) { if (IsTileType(tile, MP_INDUSTRY) && GetIndustryIndex(tile) == this->industry->index) { this->catchment_tiles.SetTile(tile); } @@ -435,7 +435,7 @@ void Station::RecomputeCatchment() /* Loop finding all station tiles */ TileArea ta(TileXY(this->rect.left, this->rect.top), TileXY(this->rect.right, this->rect.bottom)); - TILE_AREA_LOOP(tile, ta) { + for (TileIndex tile : ta) { if (!IsTileType(tile, MP_STATION) || GetStationIndex(tile) != this->index) continue; uint r = GetTileCatchmentRadius(tile, this); @@ -443,7 +443,7 @@ void Station::RecomputeCatchment() /* This tile sub-loop doesn't need to test any tiles, they are simply added to the catchment set. */ TileArea ta2 = TileArea(tile, 1, 1).Expand(r); - TILE_AREA_LOOP(tile2, ta2) this->catchment_tiles.SetTile(tile2); + for (TileIndex tile2 : ta2) this->catchment_tiles.SetTile(tile2); } /* Search catchment tiles for towns and industries */ @@ -567,7 +567,7 @@ CommandCost StationRect::BeforeAddRect(TileIndex tile, int w, int h, StationRect /* static */ bool StationRect::ScanForStationTiles(StationID st_id, int left_a, int top_a, int right_a, int bottom_a) { TileArea ta(TileXY(left_a, top_a), TileXY(right_a, bottom_a)); - TILE_AREA_LOOP(tile, ta) { + for (TileIndex tile : ta) { if (IsTileType(tile, MP_STATION) && GetStationIndex(tile) == st_id) return true; } diff --git a/src/station_base.h b/src/station_base.h index 4f16469766..4f79dd98c6 100644 --- a/src/station_base.h +++ b/src/station_base.h @@ -575,7 +575,7 @@ void ForAllStationsAroundTiles(const TileArea &ta, Func func) * to find the possible nearby stations. */ uint max_c = _settings_game.station.modified_catchment ? MAX_CATCHMENT : CA_UNMODIFIED; TileArea ta_ext = TileArea(ta).Expand(max_c); - TILE_AREA_LOOP(tile, ta_ext) { + for (TileIndex tile : ta_ext) { if (IsTileType(tile, MP_STATION)) seen_stations.insert(GetStationIndex(tile)); } @@ -587,7 +587,7 @@ void ForAllStationsAroundTiles(const TileArea &ta, Func func) if (!_settings_game.station.serve_neutral_industries && st->industry != nullptr) continue; /* Test if the tile is within the station's catchment */ - TILE_AREA_LOOP(tile, ta) { + for (TileIndex tile : ta) { if (st->TileIsInCatchment(tile)) { if (func(st, tile)) break; } diff --git a/src/station_cmd.cpp b/src/station_cmd.cpp index 39efb710e6..eecb08d7cd 100644 --- a/src/station_cmd.cpp +++ b/src/station_cmd.cpp @@ -105,7 +105,7 @@ CommandCost GetStationAround(TileArea ta, StationID closest_station, CompanyID c ta.Expand(1); /* check around to see if there are any stations there owned by the company */ - TILE_AREA_LOOP(tile_cur, ta) { + for (TileIndex tile_cur : ta) { if (IsTileType(tile_cur, MP_STATION)) { StationID t = GetStationIndex(tile_cur); if (!T::IsValidID(t) || Station::Get(t)->owner != company) continue; @@ -511,7 +511,7 @@ CargoArray GetProductionAroundTiles(TileIndex tile, int w, int h, int rad) /* Loop over all tiles to get the produced cargo of * everything except industries */ - TILE_AREA_LOOP(tile, ta) { + for (TileIndex tile : ta) { if (IsTileType(tile, MP_INDUSTRY)) industries.insert(GetIndustryIndex(tile)); AddProducedCargo(tile, produced); } @@ -549,7 +549,7 @@ CargoArray GetAcceptanceAroundTiles(TileIndex tile, int w, int h, int rad, Cargo TileArea ta = TileArea(tile, w, h).Expand(rad); - TILE_AREA_LOOP(tile, ta) { + for (TileIndex tile : ta) { /* Ignore industry if it has a neutral station. */ if (!_settings_game.station.serve_neutral_industries && IsTileType(tile, MP_INDUSTRY) && Industry::GetByTile(tile)->neutral_station != nullptr) continue; @@ -872,7 +872,7 @@ static CommandCost CheckFlatLandRailStation(TileArea tile_area, DoCommandFlag fl const StationSpec *statspec = StationClass::Get(spec_class)->GetSpec(spec_index); bool slope_cb = statspec != nullptr && HasBit(statspec->callback_mask, CBM_STATION_SLOPE_CHECK); - TILE_AREA_LOOP(tile_cur, tile_area) { + for (TileIndex tile_cur : tile_area) { CommandCost ret = CheckBuildableTile(tile_cur, invalid_dirs, allowed_z, false); if (ret.Failed()) return ret; cost.AddCost(ret); @@ -954,7 +954,7 @@ static CommandCost CheckFlatLandRoadStop(TileArea tile_area, DoCommandFlag flags CommandCost cost(EXPENSES_CONSTRUCTION); int allowed_z = -1; - TILE_AREA_LOOP(cur_tile, tile_area) { + for (TileIndex cur_tile : tile_area) { CommandCost ret = CheckBuildableTile(cur_tile, invalid_dirs, allowed_z, !is_drive_through); if (ret.Failed()) return ret; cost.AddCost(ret); @@ -1435,7 +1435,7 @@ CommandCost CmdBuildRailStation(TileIndex tile_org, DoCommandFlag flags, uint32 update_reservation_area = TileArea(tile_org, numtracks_orig, 1); } - TILE_AREA_LOOP(tile, update_reservation_area) { + for (TileIndex tile : update_reservation_area) { /* Don't even try to make eye candy parts reserved. */ if (IsStationTileBlocked(tile)) continue; @@ -1563,7 +1563,7 @@ CommandCost RemoveFromRailBaseStation(TileArea ta, std::vector &affected_st CommandCost error; /* Do the action for every tile into the area */ - TILE_AREA_LOOP(tile, ta) { + for (TileIndex tile : ta) { /* Make sure the specified tile is a rail station */ if (!HasStationTileRail(tile)) continue; @@ -1729,7 +1729,7 @@ CommandCost RemoveRailStation(T *st, DoCommandFlag flags, Money removal_cost) CommandCost cost(EXPENSES_CONSTRUCTION); /* clear all areas of the station */ - TILE_AREA_LOOP(tile, ta) { + for (TileIndex tile : ta) { /* only remove tiles that are actually train station tiles */ if (st->TileBelongsToRailStation(tile)) { std::vector affected_stations; // dummy @@ -1894,7 +1894,7 @@ CommandCost CmdBuildRoadStop(TileIndex tile, DoCommandFlag flags, uint32 p1, uin if (flags & DC_EXEC) { /* Check every tile in the area. */ - TILE_AREA_LOOP(cur_tile, roadstop_area) { + for (TileIndex cur_tile : roadstop_area) { /* Get existing road types and owners before any tile clearing */ RoadType road_rt = MayHaveRoad(cur_tile) ? GetRoadType(cur_tile, RTT_ROAD) : INVALID_ROADTYPE; RoadType tram_rt = MayHaveRoad(cur_tile) ? GetRoadType(cur_tile, RTT_TRAM) : INVALID_ROADTYPE; @@ -2101,7 +2101,7 @@ CommandCost CmdRemoveRoadStop(TileIndex tile, DoCommandFlag flags, uint32 p1, ui CommandCost last_error(STR_ERROR_THERE_IS_NO_STATION); bool had_success = false; - TILE_AREA_LOOP(cur_tile, roadstop_area) { + for (TileIndex cur_tile : roadstop_area) { /* Make sure the specified tile is a road stop of the correct type */ if (!IsTileType(cur_tile, MP_STATION) || !IsRoadStop(cur_tile) || (uint32)GetRoadStopType(cur_tile) != GB(p2, 0, 1)) continue; @@ -2409,7 +2409,7 @@ static CommandCost RemoveAirport(TileIndex tile, DoCommandFlag flags) nearest->noise_reached -= GetAirportNoiseLevelForDistance(as, dist); } - TILE_AREA_LOOP(tile_cur, st->airport) { + for (TileIndex tile_cur : st->airport) { if (!st->TileBelongsToAirport(tile_cur)) continue; CommandCost ret = EnsureNoVehicleOnGround(tile_cur); @@ -4120,7 +4120,7 @@ void UpdateStationDockingTiles(Station *st) int y1 = std::max(y - 1, 0); TileArea ta(TileXY(x1, y1), TileXY(x2 - 1, y2 - 1)); - TILE_AREA_LOOP(tile, ta) { + for (TileIndex tile : ta) { if (IsValidTile(tile) && IsPossibleDockingTile(tile)) CheckForDockingTile(tile); } } diff --git a/src/station_gui.cpp b/src/station_gui.cpp index cf3f455bc9..ab0eb5d1a5 100644 --- a/src/station_gui.cpp +++ b/src/station_gui.cpp @@ -103,7 +103,7 @@ static void FindStationsAroundSelection() Station *adjacent = nullptr; /* Direct loop instead of ForAllStationsAroundTiles as we are not interested in catchment area */ - TILE_AREA_LOOP(tile, ta) { + for (TileIndex tile : ta) { if (IsTileType(tile, MP_STATION) && GetTileOwner(tile) == _local_company) { Station *st = Station::GetByTile(tile); if (st == nullptr) continue; @@ -2214,7 +2214,7 @@ static const T *FindStationsNearby(TileArea ta, bool distant_join) _deleted_stations_nearby.clear(); /* Check the inside, to return, if we sit on another station */ - TILE_AREA_LOOP(t, ta) { + for (TileIndex t : ta) { if (t < MapSize() && IsTileType(t, MP_STATION) && T::IsValidID(GetStationIndex(t))) return T::GetByTile(t); } diff --git a/src/subsidy.cpp b/src/subsidy.cpp index 2668a62bd7..4cf42eb5c9 100644 --- a/src/subsidy.cpp +++ b/src/subsidy.cpp @@ -329,7 +329,7 @@ bool FindSubsidyTownCargoRoute() /* Calculate the produced cargo of houses around town center. */ CargoArray town_cargo_produced; TileArea ta = TileArea(src_town->xy, 1, 1).Expand(SUBSIDY_TOWN_CARGO_RADIUS); - TILE_AREA_LOOP(tile, ta) { + for (TileIndex tile : ta) { if (IsTileType(tile, MP_HOUSE)) { AddProducedCargo(tile, town_cargo_produced); } @@ -440,7 +440,7 @@ bool FindSubsidyCargoDestination(CargoID cid, SourceType src_type, SourceID src) /* Calculate cargo acceptance of houses around town center. */ CargoArray town_cargo_accepted; TileArea ta = TileArea(dst_town->xy, 1, 1).Expand(SUBSIDY_TOWN_CARGO_RADIUS); - TILE_AREA_LOOP(tile, ta) { + for (TileIndex tile : ta) { if (IsTileType(tile, MP_HOUSE)) { AddAcceptedCargo(tile, town_cargo_accepted, nullptr); } diff --git a/src/terraform_gui.cpp b/src/terraform_gui.cpp index 84cd2eca8e..0e1896a440 100644 --- a/src/terraform_gui.cpp +++ b/src/terraform_gui.cpp @@ -57,7 +57,7 @@ static void GenerateDesertArea(TileIndex end, TileIndex start) _generating_world = true; TileArea ta(start, end); - TILE_AREA_LOOP(tile, ta) { + for (TileIndex tile : ta) { SetTropicZone(tile, (_ctrl_pressed) ? TROPICZONE_NORMAL : TROPICZONE_DESERT); DoCommandP(tile, 0, 0, CMD_LANDSCAPE_CLEAR); MarkTileDirtyByTile(tile); @@ -74,7 +74,7 @@ static void GenerateRockyArea(TileIndex end, TileIndex start) bool success = false; TileArea ta(start, end); - TILE_AREA_LOOP(tile, ta) { + for (TileIndex tile : ta) { switch (GetTileType(tile)) { case MP_TREES: if (GetTreeGround(tile) == TREE_GROUND_SHORE) continue; @@ -408,18 +408,18 @@ static void CommonRaiseLowerBigLand(TileIndex tile, int mode) if (mode != 0) { /* Raise land */ h = MAX_TILE_HEIGHT; - TILE_AREA_LOOP(tile2, ta) { + for (TileIndex tile2 : ta) { h = std::min(h, TileHeight(tile2)); } } else { /* Lower land */ h = 0; - TILE_AREA_LOOP(tile2, ta) { + for (TileIndex tile2 : ta) { h = std::max(h, TileHeight(tile2)); } } - TILE_AREA_LOOP(tile2, ta) { + for (TileIndex tile2 : ta) { if (TileHeight(tile2) == h) { DoCommandP(tile2, SLOPE_N, (uint32)mode, CMD_TERRAFORM_LAND); } diff --git a/src/tilearea.cpp b/src/tilearea.cpp index 84ac7a90ff..21271f94f1 100644 --- a/src/tilearea.cpp +++ b/src/tilearea.cpp @@ -146,6 +146,24 @@ void OrthogonalTileArea::ClampToMap() this->h = std::min(this->h, MapSizeY() - TileY(this->tile)); } +/** + * Returns an iterator to the beginning of the tile area. + * @return The OrthogonalTileIterator. + */ +OrthogonalTileIterator OrthogonalTileArea::begin() const +{ + return OrthogonalTileIterator(*this); +} + +/** + * Returns an iterator to the end of the tile area. + * @return The OrthogonalTileIterator. + */ +OrthogonalTileIterator OrthogonalTileArea::end() const +{ + return OrthogonalTileIterator(OrthogonalTileArea()); +} + /** * Create a diagonal tile area from two corners. * @param start First corner of the area. diff --git a/src/tilearea_type.h b/src/tilearea_type.h index 2648219853..e6d9bad602 100644 --- a/src/tilearea_type.h +++ b/src/tilearea_type.h @@ -12,6 +12,8 @@ #include "map_func.h" +class OrthogonalTileIterator; + /** Represents the covered area of e.g. a rail station */ struct OrthogonalTileArea { TileIndex tile; ///< The base tile of the area @@ -58,6 +60,10 @@ struct OrthogonalTileArea { { return TILE_ADDXY(this->tile, this->w / 2, this->h / 2); } + + OrthogonalTileIterator begin() const; + + OrthogonalTileIterator end() const; }; /** Represents a diagonal tile area. */ @@ -123,6 +129,15 @@ public: return this->tile; } + /** + * Get the tile we are currently at. + * @return The tile we are at, or INVALID_TILE when we're done. + */ + inline TileIndex operator *() const + { + return this->tile; + } + /** * Move ourselves to the next tile in the rectangle on the map. */ @@ -223,12 +238,4 @@ public: } }; -/** - * A loop which iterates over the tiles of a TileArea. - * @param var The name of the variable which contains the current tile. - * This variable will be allocated in this \c for of this loop. - * @param ta The tile area to search over. - */ -#define TILE_AREA_LOOP(var, ta) for (OrthogonalTileIterator var(ta); var != INVALID_TILE; ++var) - #endif /* TILEAREA_TYPE_H */ diff --git a/src/tree_cmd.cpp b/src/tree_cmd.cpp index f1a5be7f90..9798c2bb56 100644 --- a/src/tree_cmd.cpp +++ b/src/tree_cmd.cpp @@ -392,7 +392,7 @@ CommandCost CmdPlantTree(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 int limit = (c == nullptr ? INT32_MAX : GB(c->tree_limit, 16, 16)); TileArea ta(tile, p2); - TILE_AREA_LOOP(tile, ta) { + for (TileIndex tile : ta) { switch (GetTileType(tile)) { case MP_TREES: /* no more space for trees? */ diff --git a/src/water_cmd.cpp b/src/water_cmd.cpp index 731954c2eb..8c8a438a58 100644 --- a/src/water_cmd.cpp +++ b/src/water_cmd.cpp @@ -1044,7 +1044,7 @@ static void FloodVehicles(TileIndex tile) if (IsAirportTile(tile)) { const Station *st = Station::GetByTile(tile); - TILE_AREA_LOOP(tile, st->airport) { + for (TileIndex tile : st->airport) { if (st->TileBelongsToAirport(tile)) FindVehicleOnPos(tile, &z, &FloodVehicleProc); } From d7ce61f10674567c97a1edd78ea1baf4e08153f2 Mon Sep 17 00:00:00 2001 From: Patric Stout Date: Thu, 13 May 2021 08:13:48 +0200 Subject: [PATCH 56/61] Fix #9255: [Network] TCPConnecter crashes when hostname not found (#9259) --- src/network/core/tcp.h | 16 +++++++++- src/network/core/tcp_connect.cpp | 55 ++++++++++++++++++++++++++------ 2 files changed, 61 insertions(+), 10 deletions(-) diff --git a/src/network/core/tcp.h b/src/network/core/tcp.h index 46e3af8c66..44316bfca0 100644 --- a/src/network/core/tcp.h +++ b/src/network/core/tcp.h @@ -66,8 +66,22 @@ public: */ class TCPConnecter { private: + /** + * The current status of the connecter. + * + * We track the status like this to ensure everything is executed from the + * game-thread, and not at another random time where we might not have the + * lock on the game-state. + */ + enum class Status { + INIT, ///< TCPConnecter is created but resolving hasn't started. + RESOLVING, ///< The hostname is being resolved (threaded). + FAILURE, ///< Resolving failed. + CONNECTING, ///< We are currently connecting. + }; + std::thread resolve_thread; ///< Thread used during resolving. - std::atomic is_resolved = false; ///< Whether resolving is done. + std::atomic status = Status::INIT; ///< The current status of the connecter. addrinfo *ai = nullptr; ///< getaddrinfo() allocated linked-list of resolved addresses. std::vector addresses; ///< Addresses we can connect to. diff --git a/src/network/core/tcp_connect.cpp b/src/network/core/tcp_connect.cpp index 381f7e5892..921a1e6c12 100644 --- a/src/network/core/tcp_connect.cpp +++ b/src/network/core/tcp_connect.cpp @@ -31,10 +31,6 @@ TCPConnecter::TCPConnecter(const std::string &connection_string, uint16 default_ this->connection_string = NormalizeConnectionString(connection_string, default_port); _tcp_connecters.push_back(this); - - if (!StartNewThread(&this->resolve_thread, "ottd:resolve", &TCPConnecter::ResolveThunk, this)) { - this->Resolve(); - } } TCPConnecter::~TCPConnecter() @@ -100,6 +96,10 @@ bool TCPConnecter::TryNextAddress() return true; } +/** + * Callback when resolving is done. + * @param ai A linked-list of address information. + */ void TCPConnecter::OnResolved(addrinfo *ai) { std::deque addresses_ipv4, addresses_ipv6; @@ -159,6 +159,12 @@ void TCPConnecter::OnResolved(addrinfo *ai) this->current_address = 0; } +/** + * Start resolving the hostname. + * + * This function must change "status" to either Status::FAILURE + * or Status::CONNECTING before returning. + */ void TCPConnecter::Resolve() { /* Port is already guaranteed part of the connection_string. */ @@ -177,7 +183,7 @@ void TCPConnecter::Resolve() auto start = std::chrono::steady_clock::now(); addrinfo *ai; - int e = getaddrinfo(address.GetHostname(), port_name, &hints, &ai); + int error = getaddrinfo(address.GetHostname(), port_name, &hints, &ai); auto end = std::chrono::steady_clock::now(); auto duration = std::chrono::duration_cast(end - start); @@ -187,18 +193,21 @@ void TCPConnecter::Resolve() getaddrinfo_timeout_error_shown = true; } - if (e != 0) { + if (error != 0) { DEBUG(net, 0, "Failed to resolve DNS for %s", this->connection_string.c_str()); - this->OnFailure(); + this->status = Status::FAILURE; return; } this->ai = ai; this->OnResolved(ai); - this->is_resolved = true; + this->status = Status::CONNECTING; } +/** + * Thunk to start Resolve() on the right instance. + */ /* static */ void TCPConnecter::ResolveThunk(TCPConnecter *connecter) { connecter->Resolve(); @@ -210,7 +219,35 @@ void TCPConnecter::Resolve() */ bool TCPConnecter::CheckActivity() { - if (!this->is_resolved.load()) return false; + switch (this->status.load()) { + case Status::INIT: + /* Start the thread delayed, so the vtable is loaded. This allows classes + * to overload functions used by Resolve() (in case threading is disabled). */ + if (StartNewThread(&this->resolve_thread, "ottd:resolve", &TCPConnecter::ResolveThunk, this)) { + this->status = Status::RESOLVING; + return false; + } + + /* No threads, do a blocking resolve. */ + this->Resolve(); + + /* Continue as we are either failed or can start the first + * connection. The rest of this function handles exactly that. */ + break; + + case Status::RESOLVING: + /* Wait till Resolve() comes back with an answer (in case it runs threaded). */ + return false; + + case Status::FAILURE: + /* Ensure the OnFailure() is called from the game-thread instead of the + * resolve-thread, as otherwise we can get into some threading issues. */ + this->OnFailure(); + return true; + + case Status::CONNECTING: + break; + } /* If there are no attempts pending, connect to the next. */ if (this->sockets.empty()) { From 7755f81bb8ba1c6a47e3a528acbbc462e56adaff Mon Sep 17 00:00:00 2001 From: Rubidium Date: Wed, 12 May 2021 19:44:00 +0200 Subject: [PATCH 57/61] Codechange: make explicit that virtual functions in a con/destructor are resolved statically This as during construction the sub class has not been initialized yet, and during destruction the sub class has already been destroyed, so the overriding virtual function would be accessing uninitialized data. --- src/fontcache.cpp | 3 ++- src/script/script_config.cpp | 5 ++++- src/window.cpp | 3 ++- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/fontcache.cpp b/src/fontcache.cpp index ce15233b7e..530dc7cf80 100644 --- a/src/fontcache.cpp +++ b/src/fontcache.cpp @@ -216,7 +216,8 @@ TrueTypeFontCache::TrueTypeFontCache(FontSize fs, int pixels) : FontCache(fs), r */ TrueTypeFontCache::~TrueTypeFontCache() { - this->ClearFontCache(); + /* Virtual functions get called statically in destructors, so make it explicit to remove any confusion. */ + this->TrueTypeFontCache::ClearFontCache(); for (auto &iter : this->font_tables) { free(iter.second.second); diff --git a/src/script/script_config.cpp b/src/script/script_config.cpp index 9bb953c4c1..eeab51bede 100644 --- a/src/script/script_config.cpp +++ b/src/script/script_config.cpp @@ -37,6 +37,7 @@ void ScriptConfig::Change(const char *name, int version, bool force_exact_match, this->SetSetting(item.name, InteractiveRandomRange(item.max_value + 1 - item.min_value) + item.min_value); } } + this->AddRandomDeviation(); } } @@ -52,7 +53,9 @@ ScriptConfig::ScriptConfig(const ScriptConfig *config) for (const auto &item : config->settings) { this->settings[stredup(item.first)] = item.second; } - this->AddRandomDeviation(); + + /* Virtual functions get called statically in constructors, so make it explicit to remove any confusion. */ + this->ScriptConfig::AddRandomDeviation(); } ScriptConfig::~ScriptConfig() diff --git a/src/window.cpp b/src/window.cpp index af47214454..bb7787581a 100644 --- a/src/window.cpp +++ b/src/window.cpp @@ -1094,7 +1094,8 @@ Window::~Window() /* Make sure we don't try to access this window as the focused window when it doesn't exist anymore. */ if (_focused_window == this) { - this->OnFocusLost(); + /* Virtual functions get called statically in destructors, so make it explicit to remove any confusion. */ + this->Window::OnFocusLost(); _focused_window = nullptr; } From 187a3f20bfd7578362f666b869580bafe25ffd0b Mon Sep 17 00:00:00 2001 From: Rubidium Date: Wed, 12 May 2021 23:06:35 +0200 Subject: [PATCH 58/61] Codechange: remove pointless close call due to resolving virtual functions statically in destructors In the destructors of many of the network related classes Close() is called, just like the top class in that hierarchy. However, due to virtual functions getting resolved statically in the destructor it would always call the empty Close() of the top class. Document the other cases where a virtual call is resolved statically. --- src/network/core/core.h | 5 +---- src/network/core/tcp.cpp | 3 ++- src/network/core/tcp_content.cpp | 6 ++++-- src/network/core/tcp_content.h | 8 ++++++-- src/network/core/udp.h | 2 +- src/network/network_content.cpp | 4 +++- src/network/network_content.h | 2 +- 7 files changed, 18 insertions(+), 12 deletions(-) diff --git a/src/network/core/core.h b/src/network/core/core.h index 37948ad527..a16ed9f23b 100644 --- a/src/network/core/core.h +++ b/src/network/core/core.h @@ -46,10 +46,7 @@ public: NetworkSocketHandler() { this->has_quit = false; } /** Close the socket when destructing the socket handler */ - virtual ~NetworkSocketHandler() { this->Close(); } - - /** Really close the socket */ - virtual void Close() {} + virtual ~NetworkSocketHandler() {} /** * Close the current connection; for TCP this will be mostly equivalent diff --git a/src/network/core/tcp.cpp b/src/network/core/tcp.cpp index 3bba291c73..5c436edf05 100644 --- a/src/network/core/tcp.cpp +++ b/src/network/core/tcp.cpp @@ -29,7 +29,8 @@ NetworkTCPSocketHandler::NetworkTCPSocketHandler(SOCKET s) : NetworkTCPSocketHandler::~NetworkTCPSocketHandler() { - this->CloseConnection(); + /* Virtual functions get called statically in destructors, so make it explicit to remove any confusion. */ + this->NetworkTCPSocketHandler::CloseConnection(); if (this->sock != INVALID_SOCKET) closesocket(this->sock); this->sock = INVALID_SOCKET; diff --git a/src/network/core/tcp_content.cpp b/src/network/core/tcp_content.cpp index 0371b76215..3abf1c29c9 100644 --- a/src/network/core/tcp_content.cpp +++ b/src/network/core/tcp_content.cpp @@ -137,9 +137,11 @@ const char *ContentInfo::GetTextfile(TextfileType type) const return ::GetTextfile(type, GetContentInfoSubDir(this->type), tmp); } -void NetworkContentSocketHandler::Close() +/** + * Close the actual socket. + */ +void NetworkContentSocketHandler::CloseSocket() { - CloseConnection(); if (this->sock == INVALID_SOCKET) return; closesocket(this->sock); diff --git a/src/network/core/tcp_content.h b/src/network/core/tcp_content.h index 52cae1e0ed..b1bde48172 100644 --- a/src/network/core/tcp_content.h +++ b/src/network/core/tcp_content.h @@ -21,7 +21,7 @@ /** Base socket handler for all Content TCP sockets */ class NetworkContentSocketHandler : public NetworkTCPSocketHandler { protected: - void Close() override; + void CloseSocket(); bool ReceiveInvalidPacket(PacketContentType type); @@ -124,7 +124,11 @@ public: } /** On destructing of this class, the socket needs to be closed */ - virtual ~NetworkContentSocketHandler() { this->Close(); } + virtual ~NetworkContentSocketHandler() + { + /* Virtual functions get called statically in destructors, so make it explicit to remove any confusion. */ + this->CloseSocket(); + } bool ReceivePackets(); }; diff --git a/src/network/core/udp.h b/src/network/core/udp.h index 881fb0a612..ab898eeee3 100644 --- a/src/network/core/udp.h +++ b/src/network/core/udp.h @@ -190,7 +190,7 @@ public: virtual ~NetworkUDPSocketHandler() { this->Close(); } bool Listen(); - void Close() override; + void Close(); void SendPacket(Packet *p, NetworkAddress *recv, bool all = false, bool broadcast = false); void ReceivePackets(); diff --git a/src/network/network_content.cpp b/src/network/network_content.cpp index e4f368618b..26d220b6ae 100644 --- a/src/network/network_content.cpp +++ b/src/network/network_content.cpp @@ -784,7 +784,9 @@ void ClientNetworkContentSocketHandler::Connect() void ClientNetworkContentSocketHandler::Close() { if (this->sock == INVALID_SOCKET) return; - NetworkContentSocketHandler::Close(); + + this->CloseConnection(); + this->CloseSocket(); this->OnDisconnect(); } diff --git a/src/network/network_content.h b/src/network/network_content.h index f28821a0bf..13b93417c3 100644 --- a/src/network/network_content.h +++ b/src/network/network_content.h @@ -107,7 +107,7 @@ public: void Connect(); void SendReceive(); - void Close() override; + void Close(); void RequestContentList(ContentType type); void RequestContentList(uint count, const ContentID *content_ids); From 6ee8690b47beab9429c754dd438b7936165af4d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20=C5=9Awi=C4=85tkowski?= Date: Sun, 9 May 2021 01:12:33 +0200 Subject: [PATCH 59/61] Fix: Replace Susz with Leszno Susz is masculine, not neuter, so it should result in "Susz Mazowiecki", "Susz Morski", and not "Susz Mazowieckie" or "Susz Morskie". However, because order of the names whould not be changed, it was replaced with Leszno, which is neuter. --- src/table/townname.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/table/townname.h b/src/table/townname.h index 086fc4f116..8a6e9964ff 100644 --- a/src/table/townname.h +++ b/src/table/townname.h @@ -1480,7 +1480,7 @@ static const char * const _name_polish_2_n[] = { "Pilzno", "Przodkowo", "Strzelno", - "Susz", + "Leszno", "Jaworzno", "Choszczno", "Mogilno", From 56a46f5cae73e2362d6ab9b1741e51361d753b30 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20=C5=9Awi=C4=85tkowski?= Date: Sun, 9 May 2021 15:11:44 +0200 Subject: [PATCH 60/61] Fix: Correct name of Golub-Dobrzyn --- src/table/townname.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/table/townname.h b/src/table/townname.h index 8a6e9964ff..85ac330609 100644 --- a/src/table/townname.h +++ b/src/table/townname.h @@ -1386,7 +1386,7 @@ static const char * const _name_polish_2_o[] = { "Zakopane", u8"Szklarska Por\u0119ba", "Bochnia", - "Golub-Dobrzyn", + u8"Golub-Dobrzy\u0144", "Chojnice", "Ostrowiec", "Otwock", From 86741ad489c3ee2d519eeb071be846721b90412c Mon Sep 17 00:00:00 2001 From: embeddedt <42941056+embeddedt@users.noreply.github.com> Date: Thu, 13 May 2021 04:13:34 -0400 Subject: [PATCH 61/61] Fix: [Emscripten] Force secure WebSockets over HTTPS (#9248) --- os/emscripten/pre.js | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/os/emscripten/pre.js b/os/emscripten/pre.js index 82664004ea..2fb641017c 100644 --- a/os/emscripten/pre.js +++ b/os/emscripten/pre.js @@ -9,7 +9,15 @@ Module['websocket'] = { url: function(host, port, proto) { * If you run your own server you can setup your own WebSocket proxy in * front of it and let people connect to your server via the proxy. You * are best to add another "if" statement as above for this. */ - return null; + + if (location.protocol === 'https:') { + /* Insecure WebSockets do not work over HTTPS, so we force + * secure ones. */ + return 'wss://'; + } else { + /* Use the default provided by Emscripten. */ + return null; + } } }; Module.preRun.push(function() {