From a341852cd56d6a3f83dfdb293426b1c38786da0b Mon Sep 17 00:00:00 2001 From: Milek7 Date: Wed, 28 Apr 2021 00:22:03 +0200 Subject: [PATCH 01/81] Fix: missing include in network/core/packet.h (#9123) --- src/network/core/packet.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/network/core/packet.h b/src/network/core/packet.h index d55dad316a..bc7bab6c4f 100644 --- a/src/network/core/packet.h +++ b/src/network/core/packet.h @@ -17,6 +17,7 @@ #include "core.h" #include "../../string_type.h" #include +#include typedef uint16 PacketSize; ///< Size of the whole packet. typedef uint8 PacketType; ///< Identifier for the packet From 267703c14be579b168125e2b3fbab2e407638ced Mon Sep 17 00:00:00 2001 From: translators Date: Wed, 28 Apr 2021 17:53:26 +0000 Subject: [PATCH 02/81] Update: Translations from eints spanish (mexican): 40 changes by absay english (us): 1 change by 2TallTyler korean: 3 changes by telk5093 german: 1 change by danidoedel finnish: 1 change by hpiirai catalan: 1 change by J0anJosep portuguese: 45 changes by azulcosta portuguese (brazilian): 44 changes by Vimerum --- src/lang/brazilian_portuguese.txt | 50 ++++++++++++++++++++++++++--- src/lang/catalan.txt | 1 + src/lang/english_US.txt | 1 + src/lang/finnish.txt | 1 + src/lang/german.txt | 1 + src/lang/korean.txt | 5 +-- src/lang/portuguese.txt | 52 ++++++++++++++++++++++++++++--- src/lang/spanish_MX.txt | 46 ++++++++++++++++++++++++--- 8 files changed, 142 insertions(+), 15 deletions(-) diff --git a/src/lang/brazilian_portuguese.txt b/src/lang/brazilian_portuguese.txt index 92c6929b82..96e9fbbd1c 100644 --- a/src/lang/brazilian_portuguese.txt +++ b/src/lang/brazilian_portuguese.txt @@ -954,7 +954,7 @@ STR_GAME_OPTIONS_CURRENCY_MYR :Ringgit Malaio STR_GAME_OPTIONS_ROAD_VEHICLES_DROPDOWN_LEFT :Dirigem na esquerda STR_GAME_OPTIONS_ROAD_VEHICLES_DROPDOWN_RIGHT :Dirigem na direita -STR_GAME_OPTIONS_TOWN_NAMES_FRAME :{BLACK}Nomes das cidades +STR_GAME_OPTIONS_TOWN_NAMES_FRAME :{BLACK}Nome das cidades: STR_GAME_OPTIONS_TOWN_NAMES_DROPDOWN_TOOLTIP :{BLACK}Selecionar o estilo dos nomes das cidades ############ start of townname region @@ -994,6 +994,7 @@ STR_GAME_OPTIONS_AUTOSAVE_DROPDOWN_EVERY_12_MONTHS :A cada 12 meses STR_GAME_OPTIONS_LANGUAGE :{BLACK}Idioma STR_GAME_OPTIONS_LANGUAGE_TOOLTIP :{BLACK}Selecionar o idioma da interface do jogo +STR_GAME_OPTIONS_LANGUAGE_PERCENTAGE :{STRING} ({NUM}% concluído) STR_GAME_OPTIONS_FULLSCREEN :{BLACK}Tela cheia STR_GAME_OPTIONS_FULLSCREEN_TOOLTIP :{BLACK}Marcar esta caixa para jogar OpenTTD modo de tela cheia @@ -1991,6 +1992,8 @@ STR_FACE_TIE :Gravata: STR_FACE_EARRING :Brinco: STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}Alterar gravata ou brinco +STR_NETWORK_SERVER_VISIBILITY_PRIVATE :Privado +STR_NETWORK_SERVER_VISIBILITY_PUBLIC :Público # Network server list STR_NETWORK_SERVER_LIST_CAPTION :{WHITE}Multi-jogador @@ -2054,6 +2057,8 @@ STR_NETWORK_START_SERVER_NEW_GAME_NAME_TOOLTIP :{BLACK}O nome d STR_NETWORK_START_SERVER_SET_PASSWORD :{BLACK}Definir senha STR_NETWORK_START_SERVER_PASSWORD_TOOLTIP :{BLACK}Proteja o jogo com uma senha se não desejar que seja publicamente acessível +STR_NETWORK_START_SERVER_VISIBILITY_LABEL :{BLACK}Visibilidade +STR_NETWORK_START_SERVER_VISIBILITY_TOOLTIP :{BLACK}Se outras pessoas podem ver seu servidor na lista pública STR_NETWORK_START_SERVER_CLIENTS_SELECT :{BLACK}{NUM} cliente{P "" s} STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS :{BLACK}Num máx de clientes: STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS_TOOLTIP :{BLACK}Escolha o número máximo de clientes. Não é necessário estarem todos preenchidos @@ -2117,11 +2122,44 @@ STR_NETWORK_NEED_GAME_PASSWORD_CAPTION :{WHITE}Servidor STR_NETWORK_NEED_COMPANY_PASSWORD_CAPTION :{WHITE}Empresa está protegida. Digite a senha # Network company list added strings -STR_NETWORK_COMPANY_LIST_CLIENT_LIST :Lista de clientes +STR_NETWORK_COMPANY_LIST_CLIENT_LIST :Jogadores online # Network client list - - +STR_NETWORK_CLIENT_LIST_CAPTION :{WHITE}Multijogador +STR_NETWORK_CLIENT_LIST_SERVER :{BLACK}Servidor +STR_NETWORK_CLIENT_LIST_SERVER_NAME :{BLACK}Nome +STR_NETWORK_CLIENT_LIST_SERVER_NAME_TOOLTIP :{BLACK}Nome do servidor que você está jogando +STR_NETWORK_CLIENT_LIST_SERVER_NAME_EDIT_TOOLTIP :{BLACK}Edita o nome do seu servidor +STR_NETWORK_CLIENT_LIST_SERVER_NAME_QUERY_CAPTION :Nome do servidor +STR_NETWORK_CLIENT_LIST_SERVER_VISIBILITY :{BLACK}Visibilidade +STR_NETWORK_CLIENT_LIST_SERVER_VISIBILITY_TOOLTIP :{BLACK}Se outras pessoas podem ver seu servidor na lista pública +STR_NETWORK_CLIENT_LIST_PLAYER :{BLACK}Jogador +STR_NETWORK_CLIENT_LIST_PLAYER_NAME :{BLACK}Nome +STR_NETWORK_CLIENT_LIST_PLAYER_NAME_TOOLTIP :{BLACK}Seu nome de jogador +STR_NETWORK_CLIENT_LIST_PLAYER_NAME_EDIT_TOOLTIP :{BLACK}Edita seu nome de jogador +STR_NETWORK_CLIENT_LIST_PLAYER_NAME_QUERY_CAPTION :Seu nome de jogador +STR_NETWORK_CLIENT_LIST_ADMIN_CLIENT_TOOLTIP :{BLACK}Ações administrativas a serem executadas para esse cliente +STR_NETWORK_CLIENT_LIST_ADMIN_COMPANY_TOOLTIP :{BLACK}Ações administrativas a serem executadas para essa empresa +STR_NETWORK_CLIENT_LIST_JOIN_TOOLTIP :{BLACK}Junta-se a essa empresa +STR_NETWORK_CLIENT_LIST_CHAT_CLIENT_TOOLTIP :{BLACK}Envia uma mensagem a esse jogador +STR_NETWORK_CLIENT_LIST_CHAT_COMPANY_TOOLTIP :{BLACK}Envia uma mensagem a todos os jogadores dessa empresa +STR_NETWORK_CLIENT_LIST_CHAT_SPECTATOR_TOOLTIP :{BLACK}Envia uma mensagem a todos os espectadores +STR_NETWORK_CLIENT_LIST_SPECTATORS :Espectadores +STR_NETWORK_CLIENT_LIST_NEW_COMPANY :(Nova empresa) +STR_NETWORK_CLIENT_LIST_NEW_COMPANY_TOOLTIP :{BLACK}Cria uma nova empresa e se une a ela +STR_NETWORK_CLIENT_LIST_PLAYER_ICON_SELF_TOOLTIP :{BLACK}Esse é você +STR_NETWORK_CLIENT_LIST_PLAYER_ICON_HOST_TOOLTIP :{BLACK}Esse é o hospedeiro do jogo + +STR_NETWORK_CLIENT_LIST_ADMIN_CLIENT_KICK :Expulsar +STR_NETWORK_CLIENT_LIST_ADMIN_CLIENT_BAN :Banir +STR_NETWORK_CLIENT_LIST_ADMIN_COMPANY_RESET :Excluir +STR_NETWORK_CLIENT_LIST_ADMIN_COMPANY_UNLOCK :Desbloqueio com senha + +STR_NETWORK_CLIENT_LIST_ASK_CAPTION :{WHITE}Ação de administrador +STR_NETWORK_CLIENT_LIST_ASK_CLIENT_KICK :{YELLOW}Você tem certeza que quer expulsar o jogador '{STRING}'? +STR_NETWORK_CLIENT_LIST_ASK_CLIENT_BAN :{YELLOW}Você tem certeza que quer banir o jogador '{STRING}'? +STR_NETWORK_CLIENT_LIST_ASK_COMPANY_RESET :{YELLOW}Você tem certeza que quer excluir a empresa '{COMPANY}'? +STR_NETWORK_CLIENT_LIST_ASK_COMPANY_UNLOCK :{YELLOW}Você tem certeza que quer restaurar a senha da empresa '{COMPANY}'? STR_NETWORK_SERVER :Servidor STR_NETWORK_CLIENT :Cliente @@ -2166,6 +2204,7 @@ STR_NETWORK_ERROR_SERVER_START :{WHITE}Não foi STR_NETWORK_ERROR_CLIENT_START :{WHITE}Não foi possível estabelecer conexão STR_NETWORK_ERROR_TIMEOUT :{WHITE}Tempo de espera esgotado na conexão #{NUM} STR_NETWORK_ERROR_SERVER_ERROR :{WHITE}Ocorreu um erro de protocolo e a conexão foi encerrada +STR_NETWORK_ERROR_BAD_PLAYER_NAME :{WHITE}Seu nome de jogador não foi definido. O nome pode ser definido no topo da janela de Multijogador STR_NETWORK_ERROR_WRONG_REVISION :{WHITE}A versão deste cliente não condiz com a versão do servidor STR_NETWORK_ERROR_WRONG_PASSWORD :{WHITE}Senha incorreta STR_NETWORK_ERROR_SERVER_FULL :{WHITE}O servidor está cheio @@ -2178,6 +2217,7 @@ STR_NETWORK_ERROR_TIMEOUT_PASSWORD :{WHITE}Você de STR_NETWORK_ERROR_TIMEOUT_COMPUTER :{WHITE}Seu computador é lento demais para acompanhar o servidor STR_NETWORK_ERROR_TIMEOUT_MAP :{WHITE}Seu computador demorou demais para baixar o mapa STR_NETWORK_ERROR_TIMEOUT_JOIN :{WHITE}Seu computador demorou demais para entrar no servidor +STR_NETWORK_ERROR_INVALID_CLIENT_NAME :{WHITE}Seu nome de jogador não é válido ############ Leave those lines in this order!! STR_NETWORK_ERROR_CLIENT_GENERAL :erro geral @@ -2200,6 +2240,7 @@ STR_NETWORK_ERROR_CLIENT_TIMEOUT_PASSWORD :não recebeu se STR_NETWORK_ERROR_CLIENT_TIMEOUT_COMPUTER :tempo esgotado STR_NETWORK_ERROR_CLIENT_TIMEOUT_MAP :a baixa do mapa demorou demais STR_NETWORK_ERROR_CLIENT_TIMEOUT_JOIN :o processamento do mapa demorou demais +STR_NETWORK_ERROR_CLIENT_INVALID_CLIENT_NAME :nome de cliente inválido ############ End of leave-in-this-order STR_NETWORK_ERROR_CLIENT_GUI_LOST_CONNECTION_CAPTION :{WHITE}Possível perda de conexão @@ -3039,6 +3080,7 @@ STR_NEWGRF_ERROR_MSG_WARNING :{RED}Atenção: STR_NEWGRF_ERROR_MSG_ERROR :{RED}Erro: {SILVER}{STRING} STR_NEWGRF_ERROR_MSG_FATAL :{RED}Erro Fatal: {SILVER}{STRING} STR_NEWGRF_ERROR_FATAL_POPUP :{WHITE}Um erro de NewGRF fatal ocorreu:{}{STRING} +STR_NEWGRF_ERROR_POPUP :{WHITE}Um erro NewGRF ocorreu:{}{STRING} STR_NEWGRF_ERROR_VERSION_NUMBER :{1:STRING} não irá funcionar com a versão do TTDPatch encontrada pelo OpenTTD STR_NEWGRF_ERROR_DOS_OR_WINDOWS :{1:STRING} funciona na versão {STRING} de TTD STR_NEWGRF_ERROR_UNSET_SWITCH :{1:STRING} é projetado para ser usado com {STRING} diff --git a/src/lang/catalan.txt b/src/lang/catalan.txt index 6dd49f586b..34b6cfb0b4 100644 --- a/src/lang/catalan.txt +++ b/src/lang/catalan.txt @@ -3080,6 +3080,7 @@ STR_NEWGRF_ERROR_MSG_WARNING :{RED}Alerta: {S STR_NEWGRF_ERROR_MSG_ERROR :{RED}Error: {SILVER}{STRING} STR_NEWGRF_ERROR_MSG_FATAL :{RED}Fatal: {SILVER}{STRING} STR_NEWGRF_ERROR_FATAL_POPUP :{WHITE}S'ha produït un error fatal de NewGRF:{}{STRING} +STR_NEWGRF_ERROR_POPUP :{WHITE}S'ha produït un error relacionat amb els NewGRF:{}{STRING} STR_NEWGRF_ERROR_VERSION_NUMBER :{1:STRING} no funcionarà amb la versió TTDPatch informada per l'OpenTTD STR_NEWGRF_ERROR_DOS_OR_WINDOWS :{1:STRING} és per la versió {STRING} de TTD STR_NEWGRF_ERROR_UNSET_SWITCH :{1:STRING} està dissenyat per ser utilitzat amb {STRING} diff --git a/src/lang/english_US.txt b/src/lang/english_US.txt index 444e46462f..dd81df78e3 100644 --- a/src/lang/english_US.txt +++ b/src/lang/english_US.txt @@ -3079,6 +3079,7 @@ STR_NEWGRF_ERROR_MSG_WARNING :{RED}Warning: { STR_NEWGRF_ERROR_MSG_ERROR :{RED}Error: {SILVER}{STRING} STR_NEWGRF_ERROR_MSG_FATAL :{RED}Fatal: {SILVER}{STRING} STR_NEWGRF_ERROR_FATAL_POPUP :{WHITE}A fatal NewGRF error has occurred:{}{STRING} +STR_NEWGRF_ERROR_POPUP :{WHITE}A NewGRF error has occurred:{}{STRING} STR_NEWGRF_ERROR_VERSION_NUMBER :{1:STRING} will not work with the TTDPatch version reported by OpenTTD STR_NEWGRF_ERROR_DOS_OR_WINDOWS :{1:STRING} is for the {STRING} version of TTD STR_NEWGRF_ERROR_UNSET_SWITCH :{1:STRING} is designed to be used with {STRING} diff --git a/src/lang/finnish.txt b/src/lang/finnish.txt index 4e795f5949..4b5765492b 100644 --- a/src/lang/finnish.txt +++ b/src/lang/finnish.txt @@ -3079,6 +3079,7 @@ STR_NEWGRF_ERROR_MSG_WARNING :{RED}Varoitus: STR_NEWGRF_ERROR_MSG_ERROR :{RED}Virhe: {SILVER}{STRING} STR_NEWGRF_ERROR_MSG_FATAL :{RED}Virhe: {SILVER}{STRING} STR_NEWGRF_ERROR_FATAL_POPUP :{WHITE}Vakava NewGRF-virhe on tapahtunut:{}{STRING} +STR_NEWGRF_ERROR_POPUP :{WHITE}NewGRF-virhe on tapahtunut:{}{STRING} STR_NEWGRF_ERROR_VERSION_NUMBER :{1:STRING} ei toimi OpenTTD:n ilmoittaman TTDPatch-version kanssa STR_NEWGRF_ERROR_DOS_OR_WINDOWS :{1:STRING} on TTD:n {STRING}-versiota varten STR_NEWGRF_ERROR_UNSET_SWITCH :{1:STRING} ja {STRING} on suunniteltu toimimaan yhdessä diff --git a/src/lang/german.txt b/src/lang/german.txt index 2c30fea557..79df8af49c 100644 --- a/src/lang/german.txt +++ b/src/lang/german.txt @@ -3080,6 +3080,7 @@ STR_NEWGRF_ERROR_MSG_WARNING :{RED}Warnung: { STR_NEWGRF_ERROR_MSG_ERROR :{RED}Fehler: {SILVER}{STRING} STR_NEWGRF_ERROR_MSG_FATAL :{RED}Schwerer Fehler: {SILVER}{STRING} STR_NEWGRF_ERROR_FATAL_POPUP :{WHITE}Ein schwerer NewGRF-Fehler ist aufgetreten:{}{STRING} +STR_NEWGRF_ERROR_POPUP :{WHITE}Ein NewGRF-Fehler ist aufgetreten:{}{STRING} STR_NEWGRF_ERROR_VERSION_NUMBER :{1:STRING} funktioniert nicht im Zusammenhang mit der von OpenTTD ermittelten TTDPatch-Version STR_NEWGRF_ERROR_DOS_OR_WINDOWS :{1:STRING} ist für die {STRING}-Version von TTD STR_NEWGRF_ERROR_UNSET_SWITCH :{1:STRING} ist für die Nutzung mit {STRING} vorgesehen diff --git a/src/lang/korean.txt b/src/lang/korean.txt index 95efeeead7..2a51f3dfcc 100644 --- a/src/lang/korean.txt +++ b/src/lang/korean.txt @@ -1808,8 +1808,8 @@ STR_CONFIG_ERROR :{WHITE}설정 STR_CONFIG_ERROR_ARRAY :{WHITE}... 배열 '{STRING}'에서 오류 발생 STR_CONFIG_ERROR_INVALID_VALUE :{WHITE}... '{1:STRING}'에 잘못된 값('{0:STRING}')이 지정되었습니다. STR_CONFIG_ERROR_TRAILING_CHARACTERS :{WHITE}... '{STRING}' 설정의 끝에 후행 문자가 있습니다. -STR_CONFIG_ERROR_DUPLICATE_GRFID :{WHITE}... NewGRF '{STRING}' 무시중: '{STRING}'{G 1 "과" "와"} GRF ID가 겹침 -STR_CONFIG_ERROR_INVALID_GRF :{WHITE}... 유효하지 않은 NewGRF '{STRING}' 무시중: {STRING} +STR_CONFIG_ERROR_DUPLICATE_GRFID :{WHITE}... '{STRING}' NewGRF를 무시합니다: '{STRING}'{G 1 "과" "와"} GRF ID가 겹침 +STR_CONFIG_ERROR_INVALID_GRF :{WHITE}... 유효하지 않은 '{STRING}' NewGRF를 무시합니다: {STRING} STR_CONFIG_ERROR_INVALID_GRF_NOT_FOUND :찾을 수 없음 STR_CONFIG_ERROR_INVALID_GRF_UNSAFE :사용하기에 불안함 STR_CONFIG_ERROR_INVALID_GRF_SYSTEM :NewGRF 시스템 @@ -3080,6 +3080,7 @@ STR_NEWGRF_ERROR_MSG_WARNING :{RED}경고: {S STR_NEWGRF_ERROR_MSG_ERROR :{RED}오류: {SILVER}{STRING} STR_NEWGRF_ERROR_MSG_FATAL :{RED}치명적 오류: {SILVER}{STRING} STR_NEWGRF_ERROR_FATAL_POPUP :{WHITE}치명적인 NewGRF 오류가 발생했습니다:{}{STRING} +STR_NEWGRF_ERROR_POPUP :{WHITE}NewGRF 관련 오류가 발생했습니다:{}{STRING} STR_NEWGRF_ERROR_VERSION_NUMBER :{1:STRING}{G 1 "은" "는"} OpenTTD에서 보고된 TTDPatch 버전에서 작동하지 않을 것입니다 STR_NEWGRF_ERROR_DOS_OR_WINDOWS :{1:STRING}{G 1 "은" "는"} {STRING} 버전의 TTD를 위한 것입니다 STR_NEWGRF_ERROR_UNSET_SWITCH :{1:STRING}{G 1 "은" "는"} {STRING}{G 1 "와" "과"} 같이 사용해야 합니다 diff --git a/src/lang/portuguese.txt b/src/lang/portuguese.txt index fadf6b9f90..d97e52569a 100644 --- a/src/lang/portuguese.txt +++ b/src/lang/portuguese.txt @@ -954,7 +954,7 @@ STR_GAME_OPTIONS_CURRENCY_MYR :Ringgit da Mal STR_GAME_OPTIONS_ROAD_VEHICLES_DROPDOWN_LEFT :Circular pela esquerda STR_GAME_OPTIONS_ROAD_VEHICLES_DROPDOWN_RIGHT :Circular pela direita -STR_GAME_OPTIONS_TOWN_NAMES_FRAME :{BLACK}Nomes das localidades +STR_GAME_OPTIONS_TOWN_NAMES_FRAME :{BLACK}Nomes das localidades: STR_GAME_OPTIONS_TOWN_NAMES_DROPDOWN_TOOLTIP :{BLACK}Seleccionar o estilo dos nomes das localidades ############ start of townname region @@ -994,6 +994,7 @@ STR_GAME_OPTIONS_AUTOSAVE_DROPDOWN_EVERY_12_MONTHS :Cada 12 meses STR_GAME_OPTIONS_LANGUAGE :{BLACK}Idioma STR_GAME_OPTIONS_LANGUAGE_TOOLTIP :{BLACK}Seleccionar o idioma da interface do jogo +STR_GAME_OPTIONS_LANGUAGE_PERCENTAGE :{STRING} ({NUM}% completado) STR_GAME_OPTIONS_FULLSCREEN :{BLACK}Ecrã Inteiro STR_GAME_OPTIONS_FULLSCREEN_TOOLTIP :{BLACK}Seleccione esta opção para jogar o OpenTTD em modo de ecrã inteiro @@ -1991,6 +1992,8 @@ STR_FACE_TIE :Gravata: STR_FACE_EARRING :Brinco: STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}Mudar gravata ou brinco +STR_NETWORK_SERVER_VISIBILITY_PRIVATE :Privado +STR_NETWORK_SERVER_VISIBILITY_PUBLIC :Público # Network server list STR_NETWORK_SERVER_LIST_CAPTION :{WHITE}Multi-jogador @@ -2043,7 +2046,7 @@ STR_NETWORK_SERVER_LIST_ADD_SERVER_TOOLTIP :{BLACK}Adiciona STR_NETWORK_SERVER_LIST_START_SERVER :{BLACK}Iniciar servidor STR_NETWORK_SERVER_LIST_START_SERVER_TOOLTIP :{BLACK}Iniciar um servidor próprio -STR_NETWORK_SERVER_LIST_PLAYER_NAME_OSKTITLE :{BLACK}Digite teu nome +STR_NETWORK_SERVER_LIST_PLAYER_NAME_OSKTITLE :{BLACK}Introduza o seu nome STR_NETWORK_SERVER_LIST_ENTER_IP :{BLACK}Introduza o endereço IP do servidor # Start new multiplayer server @@ -2054,6 +2057,8 @@ STR_NETWORK_START_SERVER_NEW_GAME_NAME_TOOLTIP :{BLACK}O nome d STR_NETWORK_START_SERVER_SET_PASSWORD :{BLACK}Definir palavra-chave STR_NETWORK_START_SERVER_PASSWORD_TOOLTIP :{BLACK}Proteja o jogo com uma senha se não desejar que pessoas indesejadas se juntem +STR_NETWORK_START_SERVER_VISIBILITY_LABEL :{BLACK}Visibilidade +STR_NETWORK_START_SERVER_VISIBILITY_TOOLTIP :{BLACK}Se as outras pessoas podem ver o seu servidor na lista pública STR_NETWORK_START_SERVER_CLIENTS_SELECT :{BLACK}{NUM} cliente{P "" s} STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS :{BLACK}Máximo de clientes: STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS_TOOLTIP :{BLACK}Escolha o número máximo de clientes. Não necessitam estar todos presentes. @@ -2117,11 +2122,44 @@ STR_NETWORK_NEED_GAME_PASSWORD_CAPTION :{WHITE}Servidor STR_NETWORK_NEED_COMPANY_PASSWORD_CAPTION :{WHITE}Empresa protegida. Introduza palavra-chave # Network company list added strings -STR_NETWORK_COMPANY_LIST_CLIENT_LIST :Lista de clientes +STR_NETWORK_COMPANY_LIST_CLIENT_LIST :Jogadores "online" # Network client list - - +STR_NETWORK_CLIENT_LIST_CAPTION :{WHITE}Multi-jogador +STR_NETWORK_CLIENT_LIST_SERVER :{BLACK}Servidor +STR_NETWORK_CLIENT_LIST_SERVER_NAME :{BLACK}Nome +STR_NETWORK_CLIENT_LIST_SERVER_NAME_TOOLTIP :{BLACK}Nome do servidor onde está a jogar +STR_NETWORK_CLIENT_LIST_SERVER_NAME_EDIT_TOOLTIP :{BLACK}Editar o nome do seu servidor +STR_NETWORK_CLIENT_LIST_SERVER_NAME_QUERY_CAPTION :Nome do servidor +STR_NETWORK_CLIENT_LIST_SERVER_VISIBILITY :{BLACK}Visibilidade +STR_NETWORK_CLIENT_LIST_SERVER_VISIBILITY_TOOLTIP :{BLACK}Se as outras pessoas podem ver o seu servidor na lista pública +STR_NETWORK_CLIENT_LIST_PLAYER :{BLACK}Jogador +STR_NETWORK_CLIENT_LIST_PLAYER_NAME :{BLACK}Nome +STR_NETWORK_CLIENT_LIST_PLAYER_NAME_TOOLTIP :{BLACK}O seu nome de jogador +STR_NETWORK_CLIENT_LIST_PLAYER_NAME_EDIT_TOOLTIP :{BLACK}Editar o seu nome de jogador +STR_NETWORK_CLIENT_LIST_PLAYER_NAME_QUERY_CAPTION :O seu nome de jogador +STR_NETWORK_CLIENT_LIST_ADMIN_CLIENT_TOOLTIP :{BLACK}Ações administrativas para executar a este cliente +STR_NETWORK_CLIENT_LIST_ADMIN_COMPANY_TOOLTIP :{BLACK}Ações administrativas para executar a esta empresa +STR_NETWORK_CLIENT_LIST_JOIN_TOOLTIP :{BLACK}Juntar-se a esta empresa +STR_NETWORK_CLIENT_LIST_CHAT_CLIENT_TOOLTIP :{BLACK}Enviar uma mensagem a este jogador +STR_NETWORK_CLIENT_LIST_CHAT_COMPANY_TOOLTIP :{BLACK}Enviar uma mensagem a todos os jogadores desta empresa +STR_NETWORK_CLIENT_LIST_CHAT_SPECTATOR_TOOLTIP :{BLACK}Enviar uma mensagem a todos os espectadores +STR_NETWORK_CLIENT_LIST_SPECTATORS :Espectadores +STR_NETWORK_CLIENT_LIST_NEW_COMPANY :(Nova empresa) +STR_NETWORK_CLIENT_LIST_NEW_COMPANY_TOOLTIP :{BLACK}Criar uma nova empresa e juntar-se a ela +STR_NETWORK_CLIENT_LIST_PLAYER_ICON_SELF_TOOLTIP :{BLACK}Este é você +STR_NETWORK_CLIENT_LIST_PLAYER_ICON_HOST_TOOLTIP :{BLACK}Este é o anfitrião do jogo + +STR_NETWORK_CLIENT_LIST_ADMIN_CLIENT_KICK :Expulsar +STR_NETWORK_CLIENT_LIST_ADMIN_CLIENT_BAN :Banir +STR_NETWORK_CLIENT_LIST_ADMIN_COMPANY_RESET :Apagar +STR_NETWORK_CLIENT_LIST_ADMIN_COMPANY_UNLOCK :desbloquear com palavra-passe + +STR_NETWORK_CLIENT_LIST_ASK_CAPTION :{WHITE}Ação administrativa +STR_NETWORK_CLIENT_LIST_ASK_CLIENT_KICK :{YELLOW}Tem a certeza que quer expulsar o jogador '{STRING}'? +STR_NETWORK_CLIENT_LIST_ASK_CLIENT_BAN :{YELLOW}Tem a certeza que quer banir o jogador '{STRING}'? +STR_NETWORK_CLIENT_LIST_ASK_COMPANY_RESET :{YELLOW}Tem a certeza que quer apagar a empresa '{COMPANY}'? +STR_NETWORK_CLIENT_LIST_ASK_COMPANY_UNLOCK :{YELLOW}Tem a certeza que quer restabelecer a palavra-chave da empresa '{COMPANY}'? STR_NETWORK_SERVER :Servidor STR_NETWORK_CLIENT :Cliente @@ -2166,6 +2204,7 @@ STR_NETWORK_ERROR_SERVER_START :{WHITE}Não foi STR_NETWORK_ERROR_CLIENT_START :{WHITE}Não foi possível estabelecer ligação STR_NETWORK_ERROR_TIMEOUT :{WHITE}Tempo de espera esgotado na conexão #{NUM}. STR_NETWORK_ERROR_SERVER_ERROR :{WHITE}Ocorreu um erro de protocolo e a ligação foi encerrada +STR_NETWORK_ERROR_BAD_PLAYER_NAME :{WHITE}O seu nome de jogador não foi definido. O nome pode ser definido no topo da janela de Multi-jogador STR_NETWORK_ERROR_WRONG_REVISION :{WHITE}A revisão deste cliente não condiz com a revisão do servidor STR_NETWORK_ERROR_WRONG_PASSWORD :{WHITE}Palavra-chave incorrecta STR_NETWORK_ERROR_SERVER_FULL :{WHITE}Servidor cheio @@ -2178,6 +2217,7 @@ STR_NETWORK_ERROR_TIMEOUT_PASSWORD :{WHITE}Demorou STR_NETWORK_ERROR_TIMEOUT_COMPUTER :{WHITE}O seu computador é demasiado lento para acompanhar com o servidor STR_NETWORK_ERROR_TIMEOUT_MAP :{WHITE}O seu computador demorou demasiado a transferir o mapa STR_NETWORK_ERROR_TIMEOUT_JOIN :{WHITE}O seu computador demorou demasiado a ligar ao servidor +STR_NETWORK_ERROR_INVALID_CLIENT_NAME :{WHITE}O seu nome de jogador não é válido ############ Leave those lines in this order!! STR_NETWORK_ERROR_CLIENT_GENERAL :erro geral @@ -2200,6 +2240,7 @@ STR_NETWORK_ERROR_CLIENT_TIMEOUT_PASSWORD :não foi recebi STR_NETWORK_ERROR_CLIENT_TIMEOUT_COMPUTER :demasiado tempo (geral) STR_NETWORK_ERROR_CLIENT_TIMEOUT_MAP :demorou demasiado a transferir o mapa STR_NETWORK_ERROR_CLIENT_TIMEOUT_JOIN :demorou demasiado a processar o mapa +STR_NETWORK_ERROR_CLIENT_INVALID_CLIENT_NAME :nome de cliente inválido ############ End of leave-in-this-order STR_NETWORK_ERROR_CLIENT_GUI_LOST_CONNECTION_CAPTION :{WHITE}Possível perda de conexão @@ -3039,6 +3080,7 @@ STR_NEWGRF_ERROR_MSG_WARNING :{RED}Aviso: {SI STR_NEWGRF_ERROR_MSG_ERROR :{RED}Erro: {SILVER}{STRING} STR_NEWGRF_ERROR_MSG_FATAL :{RED}Fatal: {SILVER}{STRING} STR_NEWGRF_ERROR_FATAL_POPUP :{WHITE}Ocorreu um erro fatal num NewGRF:{}{STRING} +STR_NEWGRF_ERROR_POPUP :{WHITE}Ocorreu um erro de NewGRF:{}{STRING} STR_NEWGRF_ERROR_VERSION_NUMBER :{1:STRING} não funciona com a versão do TTDPatch reportada por OpenTTD. STR_NEWGRF_ERROR_DOS_OR_WINDOWS :{1:STRING} é para a versão {STRING} do TTD. STR_NEWGRF_ERROR_UNSET_SWITCH :{1:STRING} foi concebido para ser usado com {STRING} diff --git a/src/lang/spanish_MX.txt b/src/lang/spanish_MX.txt index 74b8b10cff..08d5bdea6b 100644 --- a/src/lang/spanish_MX.txt +++ b/src/lang/spanish_MX.txt @@ -954,7 +954,7 @@ STR_GAME_OPTIONS_CURRENCY_MYR :Ringgit malasio STR_GAME_OPTIONS_ROAD_VEHICLES_DROPDOWN_LEFT :Manejar por la izquierda STR_GAME_OPTIONS_ROAD_VEHICLES_DROPDOWN_RIGHT :Manejar por la derecha -STR_GAME_OPTIONS_TOWN_NAMES_FRAME :{BLACK}Nombres de pueblos +STR_GAME_OPTIONS_TOWN_NAMES_FRAME :{BLACK}Nombres de pueblos: STR_GAME_OPTIONS_TOWN_NAMES_DROPDOWN_TOOLTIP :{BLACK}Elegir el estilo de nombres para pueblos ############ start of townname region @@ -1992,6 +1992,8 @@ STR_FACE_TIE :Corbata: STR_FACE_EARRING :Aretes: STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}Cambiar corbata o aretes +STR_NETWORK_SERVER_VISIBILITY_PRIVATE :Privado +STR_NETWORK_SERVER_VISIBILITY_PUBLIC :Público # Network server list STR_NETWORK_SERVER_LIST_CAPTION :{WHITE}Multijugador @@ -2055,6 +2057,8 @@ STR_NETWORK_START_SERVER_NEW_GAME_NAME_TOOLTIP :{BLACK}La parti STR_NETWORK_START_SERVER_SET_PASSWORD :{BLACK}Establecer contraseña STR_NETWORK_START_SERVER_PASSWORD_TOOLTIP :{BLACK}Proteger la partida con una contraseña para prevenir el acceso a otras personas +STR_NETWORK_START_SERVER_VISIBILITY_LABEL :{BLACK}Visibilidad +STR_NETWORK_START_SERVER_VISIBILITY_TOOLTIP :{BLACK}Quién puede ver tu servidor en la lista pública STR_NETWORK_START_SERVER_CLIENTS_SELECT :{BLACK}{NUM} cliente{P "" s} STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS :{BLACK}Número máximo de clientes: STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS_TOOLTIP :{BLACK}Elegir el número máximo de clientes. No es necesario que se conecten todos @@ -2118,11 +2122,44 @@ STR_NETWORK_NEED_GAME_PASSWORD_CAPTION :{WHITE}Servidor STR_NETWORK_NEED_COMPANY_PASSWORD_CAPTION :{WHITE}Empresa protegida. Introducir contraseña # Network company list added strings -STR_NETWORK_COMPANY_LIST_CLIENT_LIST :Lista de clientes +STR_NETWORK_COMPANY_LIST_CLIENT_LIST :Jugadores conectados # Network client list - - +STR_NETWORK_CLIENT_LIST_CAPTION :{WHITE}Multijugador +STR_NETWORK_CLIENT_LIST_SERVER :{BLACK}Servidor +STR_NETWORK_CLIENT_LIST_SERVER_NAME :{BLACK}Nombre +STR_NETWORK_CLIENT_LIST_SERVER_NAME_TOOLTIP :{BLACK}Nombre del servidor en el que estás jugando +STR_NETWORK_CLIENT_LIST_SERVER_NAME_EDIT_TOOLTIP :{BLACK}Modificar nombre del servidor +STR_NETWORK_CLIENT_LIST_SERVER_NAME_QUERY_CAPTION :Nombre del servidor +STR_NETWORK_CLIENT_LIST_SERVER_VISIBILITY :{BLACK}Visibilidad +STR_NETWORK_CLIENT_LIST_SERVER_VISIBILITY_TOOLTIP :{BLACK}Quién puede ver tu servidor en la lista pública +STR_NETWORK_CLIENT_LIST_PLAYER :{BLACK}Jugador +STR_NETWORK_CLIENT_LIST_PLAYER_NAME :{BLACK}Nombre +STR_NETWORK_CLIENT_LIST_PLAYER_NAME_TOOLTIP :{BLACK}Tu nombre de jugador +STR_NETWORK_CLIENT_LIST_PLAYER_NAME_EDIT_TOOLTIP :{BLACK}Modificar nombre de jugador +STR_NETWORK_CLIENT_LIST_PLAYER_NAME_QUERY_CAPTION :Tu nombre de jugador +STR_NETWORK_CLIENT_LIST_ADMIN_CLIENT_TOOLTIP :{BLACK}Acciones administrativas para este cliente +STR_NETWORK_CLIENT_LIST_ADMIN_COMPANY_TOOLTIP :{BLACK}Acciones administrativas para esta empresa +STR_NETWORK_CLIENT_LIST_JOIN_TOOLTIP :{BLACK}Unirse a esta empresa +STR_NETWORK_CLIENT_LIST_CHAT_CLIENT_TOOLTIP :{BLACK}Enviar mensaje a este jugador +STR_NETWORK_CLIENT_LIST_CHAT_COMPANY_TOOLTIP :{BLACK}Enviar mensaje a todos los jugadores de la empresa +STR_NETWORK_CLIENT_LIST_CHAT_SPECTATOR_TOOLTIP :{BLACK}Enviar mensaje a los espectadores +STR_NETWORK_CLIENT_LIST_SPECTATORS :Espectadores +STR_NETWORK_CLIENT_LIST_NEW_COMPANY :(Nueva empresa) +STR_NETWORK_CLIENT_LIST_NEW_COMPANY_TOOLTIP :{BLACK}Crear nueva empresa y unirse a ella +STR_NETWORK_CLIENT_LIST_PLAYER_ICON_SELF_TOOLTIP :{BLACK}Este eres tú +STR_NETWORK_CLIENT_LIST_PLAYER_ICON_HOST_TOOLTIP :{BLACK}Este es el host del juego + +STR_NETWORK_CLIENT_LIST_ADMIN_CLIENT_KICK :Expulsar +STR_NETWORK_CLIENT_LIST_ADMIN_CLIENT_BAN :Bloquear acceso +STR_NETWORK_CLIENT_LIST_ADMIN_COMPANY_RESET :Eliminar +STR_NETWORK_CLIENT_LIST_ADMIN_COMPANY_UNLOCK :Restablecer contraseña + +STR_NETWORK_CLIENT_LIST_ASK_CAPTION :{WHITE}Acción de aministrador +STR_NETWORK_CLIENT_LIST_ASK_CLIENT_KICK :{YELLOW}¿Sacar al jugador "{STRING}"? +STR_NETWORK_CLIENT_LIST_ASK_CLIENT_BAN :{YELLOW}¿Bloquear acceso al jugador "{STRING}"? +STR_NETWORK_CLIENT_LIST_ASK_COMPANY_RESET :{YELLOW}¿Eliminar la empresa "{COMPANY}"? +STR_NETWORK_CLIENT_LIST_ASK_COMPANY_UNLOCK :{YELLOW}¿Restablecer contraseña de la empresa "{COMPANY}"? STR_NETWORK_SERVER :Servidor STR_NETWORK_CLIENT :Cliente @@ -3043,6 +3080,7 @@ STR_NEWGRF_ERROR_MSG_WARNING :{RED}Atención: STR_NEWGRF_ERROR_MSG_ERROR :{RED}Error: {SILVER}{STRING} STR_NEWGRF_ERROR_MSG_FATAL :{RED}Error fatal: {SILVER}{STRING} STR_NEWGRF_ERROR_FATAL_POPUP :{WHITE}Ocurrió un error fatal de NewGRF:{}{STRING} +STR_NEWGRF_ERROR_POPUP :{WHITE}Ocurrió un error de NewGRF:{}{STRING} STR_NEWGRF_ERROR_VERSION_NUMBER :{1:STRING} no funcionará con la con la versión de TTDPatch reportada por OpenTTD STR_NEWGRF_ERROR_DOS_OR_WINDOWS :{1:STRING} es la para la versión {STRING} de TTD STR_NEWGRF_ERROR_UNSET_SWITCH :{1:STRING} está diseñado para usarse con {STRING} From 8e9eca6dddf238c77f01d93afcefcca262447e98 Mon Sep 17 00:00:00 2001 From: Milek7 Date: Wed, 28 Apr 2021 23:06:47 +0200 Subject: [PATCH 03/81] Codechange: Use __attribute__ access none to silence GCC 11 -Wmaybe-uninitialized warnings (#9124) --- src/stdafx.h | 6 ++++++ src/string_func.h | 16 ++++++++-------- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/src/stdafx.h b/src/stdafx.h index 6a33223455..19009f5ff5 100644 --- a/src/stdafx.h +++ b/src/stdafx.h @@ -138,6 +138,12 @@ # endif #endif /* __GNUC__ || __clang__ */ +#if __GNUC__ > 11 || (__GNUC__ == 11 && __GNUC_MINOR__ >= 1) +# define NOACCESS(args) __attribute__ ((access (none, args))) +#else +# define NOACCESS(args) +#endif + #if defined(__WATCOMC__) # define NORETURN # define CDECL diff --git a/src/string_func.h b/src/string_func.h index 6c894b3cba..4fe9d7b825 100644 --- a/src/string_func.h +++ b/src/string_func.h @@ -30,25 +30,25 @@ #include "core/bitmath_func.hpp" #include "string_type.h" -char *strecat(char *dst, const char *src, const char *last); -char *strecpy(char *dst, const char *src, const char *last); -char *stredup(const char *src, const char *last = nullptr); +char *strecat(char *dst, const char *src, const char *last) NOACCESS(3); +char *strecpy(char *dst, const char *src, const char *last) NOACCESS(3); +char *stredup(const char *src, const char *last = nullptr) NOACCESS(2); -int CDECL seprintf(char *str, const char *last, const char *format, ...) WARN_FORMAT(3, 4); -int CDECL vseprintf(char *str, const char *last, const char *format, va_list ap) WARN_FORMAT(3, 0); +int CDECL seprintf(char *str, const char *last, const char *format, ...) WARN_FORMAT(3, 4) NOACCESS(2); +int CDECL vseprintf(char *str, const char *last, const char *format, va_list ap) WARN_FORMAT(3, 0) NOACCESS(2); char *CDECL str_fmt(const char *str, ...) WARN_FORMAT(1, 2); -void str_validate(char *str, const char *last, StringValidationSettings settings = SVS_REPLACE_WITH_QUESTION_MARK); +void str_validate(char *str, const char *last, StringValidationSettings settings = SVS_REPLACE_WITH_QUESTION_MARK) NOACCESS(2); std::string str_validate(const std::string &str, StringValidationSettings settings = SVS_REPLACE_WITH_QUESTION_MARK); void ValidateString(const char *str); -void str_fix_scc_encoded(char *str, const char *last); +void str_fix_scc_encoded(char *str, const char *last) NOACCESS(2); void str_strip_colours(char *str); bool strtolower(char *str); bool strtolower(std::string &str, std::string::size_type offs = 0); -bool StrValid(const char *str, const char *last); +bool StrValid(const char *str, const char *last) NOACCESS(2); void StrTrimInPlace(char *str); /** From 96dc0d04ecb12ee83909782b9a2c39f336de7d9a Mon Sep 17 00:00:00 2001 From: Patric Stout Date: Wed, 28 Apr 2021 23:09:03 +0200 Subject: [PATCH 04/81] Fix b3003dd1: swap SERVER_GAME_INFO with CLIENT_GAME_INFO (#9129) The idea is that if you query an older server that does not support this packet yet, the client receives an error. The assumption was that on every "illegal packet" the connection would be closed. This turns out to be false. Now CLIENT_GAME_INFO aligns with the old PACKET_CLIENT_NEWGRFS_CHECKED, which does a pre-check (which fails), and an error is sent back and the connection is closed. This is not a nice solution, but it is the best we got. --- src/network/core/tcp_game.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/network/core/tcp_game.h b/src/network/core/tcp_game.h index 6dcc0bd01f..5fe9cd465d 100644 --- a/src/network/core/tcp_game.h +++ b/src/network/core/tcp_game.h @@ -43,8 +43,8 @@ enum PacketGameType { PACKET_SERVER_COMPANY_INFO, ///< Information about a single company. /* Packets used to get the game info. */ - PACKET_CLIENT_GAME_INFO, ///< Request information about the server. PACKET_SERVER_GAME_INFO, ///< Information about the server. + PACKET_CLIENT_GAME_INFO, ///< Request information about the server. /* * Packets after here assume that the client From ae7f07de74a8d242dce8a67eaece26be9d06f064 Mon Sep 17 00:00:00 2001 From: PeterN Date: Wed, 28 Apr 2021 22:32:43 +0100 Subject: [PATCH 05/81] Fix: Incorrect vertical alignment of icon and text in DropDownListIconItem. (#9133) This happens if the bounding dimensions are changed so that each item is the same size, as happens on the railtype/roadtype dropdown lists, as the vertical offset was calculated before this dimension is changed. --- src/widgets/dropdown.cpp | 12 +++--------- src/widgets/dropdown_type.h | 1 - 2 files changed, 3 insertions(+), 10 deletions(-) diff --git a/src/widgets/dropdown.cpp b/src/widgets/dropdown.cpp index 97c755e4bc..6b1debb9db 100644 --- a/src/widgets/dropdown.cpp +++ b/src/widgets/dropdown.cpp @@ -72,13 +72,7 @@ StringID DropDownListCharStringItem::String() const DropDownListIconItem::DropDownListIconItem(SpriteID sprite, PaletteID pal, StringID string, int result, bool masked) : DropDownListParamStringItem(string, result, masked), sprite(sprite), pal(pal) { this->dim = GetSpriteSize(sprite); - if (this->dim.height < (uint)FONT_HEIGHT_NORMAL) { - this->sprite_y = (FONT_HEIGHT_NORMAL - dim.height) / 2; - this->text_y = 0; - } else { - this->sprite_y = 0; - this->text_y = (dim.height - FONT_HEIGHT_NORMAL) / 2; - } + this->sprite_y = dim.height; } uint DropDownListIconItem::Height(uint width) const @@ -94,8 +88,8 @@ uint DropDownListIconItem::Width() const void DropDownListIconItem::Draw(int left, int right, int top, int bottom, bool sel, Colours bg_colour) const { bool rtl = _current_text_dir == TD_RTL; - DrawSprite(this->sprite, this->pal, rtl ? right - this->dim.width - WD_FRAMERECT_RIGHT : left + WD_FRAMERECT_LEFT, top + this->sprite_y); - DrawString(left + WD_FRAMERECT_LEFT + (rtl ? 0 : (this->dim.width + WD_FRAMERECT_LEFT)), right - WD_FRAMERECT_RIGHT - (rtl ? (this->dim.width + WD_FRAMERECT_RIGHT) : 0), top + this->text_y, this->String(), sel ? TC_WHITE : TC_BLACK); + DrawSprite(this->sprite, this->pal, rtl ? right - this->dim.width - WD_FRAMERECT_RIGHT : left + WD_FRAMERECT_LEFT, CenterBounds(top, bottom, this->sprite_y)); + DrawString(left + WD_FRAMERECT_LEFT + (rtl ? 0 : (this->dim.width + WD_FRAMERECT_LEFT)), right - WD_FRAMERECT_RIGHT - (rtl ? (this->dim.width + WD_FRAMERECT_RIGHT) : 0), CenterBounds(top, bottom, FONT_HEIGHT_NORMAL), this->String(), sel ? TC_WHITE : TC_BLACK); } void DropDownListIconItem::SetDimension(Dimension d) diff --git a/src/widgets/dropdown_type.h b/src/widgets/dropdown_type.h index a1f240e96d..5dfa9ed58b 100644 --- a/src/widgets/dropdown_type.h +++ b/src/widgets/dropdown_type.h @@ -84,7 +84,6 @@ class DropDownListIconItem : public DropDownListParamStringItem { PaletteID pal; Dimension dim; uint sprite_y; - uint text_y; public: DropDownListIconItem(SpriteID sprite, PaletteID pal, StringID string, int result, bool masked); From 3b3d80c8efbf3310a80e0540b4436a7ded147378 Mon Sep 17 00:00:00 2001 From: Peter Nelson Date: Wed, 28 Apr 2021 21:49:58 +0100 Subject: [PATCH 06/81] Cleanup: Replace FOR_ALL_SORTED_RAILTYPES macro with range iterator. --- src/company_gui.cpp | 9 +++------ src/rail.h | 6 ------ src/rail_gui.cpp | 5 ++--- 3 files changed, 5 insertions(+), 15 deletions(-) diff --git a/src/company_gui.cpp b/src/company_gui.cpp index c9e3eaf861..3706db2f5e 100644 --- a/src/company_gui.cpp +++ b/src/company_gui.cpp @@ -1840,8 +1840,7 @@ struct CompanyInfrastructureWindow : Window size->width = std::max(size->width, GetStringBoundingBox(STR_COMPANY_INFRASTRUCTURE_VIEW_RAIL_SECT).width); - RailType rt; - FOR_ALL_SORTED_RAILTYPES(rt) { + for (const auto &rt : _sorted_railtypes) { if (HasBit(this->railtypes, rt)) { lines++; SetDParam(0, GetRailTypeInfo(rt)->strings.name); @@ -1973,8 +1972,7 @@ struct CompanyInfrastructureWindow : Window if (this->railtypes != RAILTYPES_NONE) { /* Draw name of each valid railtype. */ - RailType rt; - FOR_ALL_SORTED_RAILTYPES(rt) { + for (const auto &rt : _sorted_railtypes) { if (HasBit(this->railtypes, rt)) { SetDParam(0, GetRailTypeInfo(rt)->strings.name); DrawString(r.left + offs_left, r.right - offs_right, y += FONT_HEIGHT_NORMAL, STR_WHITE_STRING); @@ -1991,8 +1989,7 @@ struct CompanyInfrastructureWindow : Window case WID_CI_RAIL_COUNT: { /* Draw infrastructure count for each valid railtype. */ uint32 rail_total = c->infrastructure.GetRailTotal(); - RailType rt; - FOR_ALL_SORTED_RAILTYPES(rt) { + for (const auto &rt : _sorted_railtypes) { if (HasBit(this->railtypes, rt)) { this->DrawCountLine(r, y, c->infrastructure.rail[rt], RailMaintenanceCost(rt, c->infrastructure.rail[rt], rail_total)); } diff --git a/src/rail.h b/src/rail.h index c5775b9467..a0bbb0109b 100644 --- a/src/rail.h +++ b/src/rail.h @@ -464,10 +464,4 @@ RailType AllocateRailType(RailTypeLabel label); extern std::vector _sorted_railtypes; extern RailTypes _railtypes_hidden_mask; -/** - * Loop header for iterating over railtypes, sorted by sortorder. - * @param var Railtype. - */ -#define FOR_ALL_SORTED_RAILTYPES(var) for (uint8 index = 0; index < _sorted_railtypes.size() && (var = _sorted_railtypes[index], true) ; index++) - #endif /* RAIL_H */ diff --git a/src/rail_gui.cpp b/src/rail_gui.cpp index f0f5beccac..49c7d736af 100644 --- a/src/rail_gui.cpp +++ b/src/rail_gui.cpp @@ -2191,17 +2191,16 @@ DropDownList GetRailTypeDropDownList(bool for_replacement, bool all_option) } Dimension d = { 0, 0 }; - RailType rt; /* Get largest icon size, to ensure text is aligned on each menu item. */ if (!for_replacement) { - FOR_ALL_SORTED_RAILTYPES(rt) { + for (const auto &rt : _sorted_railtypes) { if (!HasBit(used_railtypes, rt)) continue; const RailtypeInfo *rti = GetRailTypeInfo(rt); d = maxdim(d, GetSpriteSize(rti->gui_sprites.build_x_rail)); } } - FOR_ALL_SORTED_RAILTYPES(rt) { + for (const auto &rt : _sorted_railtypes) { /* If it's not used ever, don't show it to the user. */ if (!HasBit(used_railtypes, rt)) continue; From de81afdf4bfb97a48533489101e3aa0e5eb2cb4f Mon Sep 17 00:00:00 2001 From: Peter Nelson Date: Wed, 28 Apr 2021 21:50:23 +0100 Subject: [PATCH 07/81] Cleanup: Replace FOR_ALL_SORTED_ROADTYPES macro with range iterator. --- src/company_gui.cpp | 9 +++------ src/road.h | 6 ------ src/road_gui.cpp | 10 ++++------ 3 files changed, 7 insertions(+), 18 deletions(-) diff --git a/src/company_gui.cpp b/src/company_gui.cpp index 3706db2f5e..f9eceba177 100644 --- a/src/company_gui.cpp +++ b/src/company_gui.cpp @@ -1862,8 +1862,7 @@ struct CompanyInfrastructureWindow : Window size->width = std::max(size->width, GetStringBoundingBox(widget == WID_CI_ROAD_DESC ? STR_COMPANY_INFRASTRUCTURE_VIEW_ROAD_SECT : STR_COMPANY_INFRASTRUCTURE_VIEW_TRAM_SECT).width); - RoadType rt; - FOR_ALL_SORTED_ROADTYPES(rt) { + for (const auto &rt : _sorted_roadtypes) { if (HasBit(this->roadtypes, rt) && RoadTypeIsRoad(rt) == (widget == WID_CI_ROAD_DESC)) { lines++; SetDParam(0, GetRoadTypeInfo(rt)->strings.name); @@ -2005,8 +2004,7 @@ struct CompanyInfrastructureWindow : Window DrawString(r.left, r.right, y, widget == WID_CI_ROAD_DESC ? STR_COMPANY_INFRASTRUCTURE_VIEW_ROAD_SECT : STR_COMPANY_INFRASTRUCTURE_VIEW_TRAM_SECT); /* Draw name of each valid roadtype. */ - RoadType rt; - FOR_ALL_SORTED_ROADTYPES(rt) { + for (const auto &rt : _sorted_roadtypes) { if (HasBit(this->roadtypes, rt) && RoadTypeIsRoad(rt) == (widget == WID_CI_ROAD_DESC)) { SetDParam(0, GetRoadTypeInfo(rt)->strings.name); DrawString(r.left + offs_left, r.right - offs_right, y += FONT_HEIGHT_NORMAL, STR_WHITE_STRING); @@ -2019,8 +2017,7 @@ struct CompanyInfrastructureWindow : Window case WID_CI_ROAD_COUNT: case WID_CI_TRAM_COUNT: { uint32 road_tram_total = widget == WID_CI_ROAD_COUNT ? c->infrastructure.GetRoadTotal() : c->infrastructure.GetTramTotal(); - RoadType rt; - FOR_ALL_SORTED_ROADTYPES(rt) { + for (const auto &rt : _sorted_roadtypes) { if (HasBit(this->roadtypes, rt) && RoadTypeIsRoad(rt) == (widget == WID_CI_ROAD_COUNT)) { this->DrawCountLine(r, y, c->infrastructure.road[rt], RoadMaintenanceCost(rt, c->infrastructure.road[rt], road_tram_total)); } diff --git a/src/road.h b/src/road.h index 1082ab2729..becbf40512 100644 --- a/src/road.h +++ b/src/road.h @@ -305,10 +305,4 @@ bool HasAnyRoadTypesAvail(CompanyID company, RoadTramType rtt); extern std::vector _sorted_roadtypes; extern RoadTypes _roadtypes_hidden_mask; -/** - * Loop header for iterating over roadtypes, sorted by sortorder. - * @param var Roadtype. - */ -#define FOR_ALL_SORTED_ROADTYPES(var) for (uint8 index = 0; index < _sorted_roadtypes.size() && (var = _sorted_roadtypes[index], true) ; index++) - #endif /* ROAD_H */ diff --git a/src/road_gui.cpp b/src/road_gui.cpp index 9aa1952477..9cc68028d5 100644 --- a/src/road_gui.cpp +++ b/src/road_gui.cpp @@ -1313,17 +1313,16 @@ DropDownList GetRoadTypeDropDownList(RoadTramTypes rtts, bool for_replacement, b } Dimension d = { 0, 0 }; - RoadType rt; /* Get largest icon size, to ensure text is aligned on each menu item. */ if (!for_replacement) { - FOR_ALL_SORTED_ROADTYPES(rt) { + for (const auto &rt : _sorted_roadtypes) { if (!HasBit(used_roadtypes, rt)) continue; const RoadTypeInfo *rti = GetRoadTypeInfo(rt); d = maxdim(d, GetSpriteSize(rti->gui_sprites.build_x_road)); } } - FOR_ALL_SORTED_ROADTYPES(rt) { + for (const auto &rt : _sorted_roadtypes) { /* If it's not used ever, don't show it to the user. */ if (!HasBit(used_roadtypes, rt)) continue; @@ -1365,13 +1364,12 @@ DropDownList GetScenRoadTypeDropDownList(RoadTramTypes rtts) /* If it's not used ever, don't show it to the user. */ Dimension d = { 0, 0 }; - RoadType rt; - FOR_ALL_SORTED_ROADTYPES(rt) { + for (const auto &rt : _sorted_roadtypes) { if (!HasBit(used_roadtypes, rt)) continue; const RoadTypeInfo *rti = GetRoadTypeInfo(rt); d = maxdim(d, GetSpriteSize(rti->gui_sprites.build_x_road)); } - FOR_ALL_SORTED_ROADTYPES(rt) { + for (const auto &rt : _sorted_roadtypes) { if (!HasBit(used_roadtypes, rt)) continue; const RoadTypeInfo *rti = GetRoadTypeInfo(rt); From 20ac0b41484ebcaef3f8bdc9d4410903fd499386 Mon Sep 17 00:00:00 2001 From: Peter Nelson Date: Wed, 28 Apr 2021 21:50:46 +0100 Subject: [PATCH 08/81] Cleanup: Replace FOR_ALL_SORTED_CARGOSPECS macro with range iterator. --- src/cargotype.cpp | 2 +- src/cargotype.h | 7 ------- src/strings.cpp | 3 +-- src/vehicle_gui.cpp | 3 +-- 4 files changed, 3 insertions(+), 12 deletions(-) diff --git a/src/cargotype.cpp b/src/cargotype.cpp index e30b9899f1..81818d5f9d 100644 --- a/src/cargotype.cpp +++ b/src/cargotype.cpp @@ -181,7 +181,7 @@ void InitializeSortedCargoSpecs() _standard_cargo_mask = 0; _sorted_standard_cargo_specs_size = 0; - FOR_ALL_SORTED_CARGOSPECS(cargo) { + for (const auto &cargo : _sorted_cargo_specs) { if (cargo->classes & CC_SPECIAL) break; _sorted_standard_cargo_specs_size++; SetBit(_standard_cargo_mask, cargo->Index()); diff --git a/src/cargotype.h b/src/cargotype.h index f6b3eaead5..afc501a2f2 100644 --- a/src/cargotype.h +++ b/src/cargotype.h @@ -156,13 +156,6 @@ static inline bool IsCargoInClass(CargoID c, CargoClass cc) #define FOR_EACH_SET_CARGO_ID(var, cargo_bits) FOR_EACH_SET_BIT_EX(CargoID, var, CargoTypes, cargo_bits) -/** - * Loop header for iterating over cargoes, sorted by name. This includes phony cargoes like regearing cargoes. - * @param var Reference getting the cargospec. - * @see CargoSpec - */ -#define FOR_ALL_SORTED_CARGOSPECS(var) for (uint8 index = 0; index < _sorted_cargo_specs.size() && (var = _sorted_cargo_specs[index], true) ; index++) - /** * Loop header for iterating over 'real' cargoes, sorted by name. Phony cargoes like regearing cargoes are skipped. * @param var Reference getting the cargospec. diff --git a/src/strings.cpp b/src/strings.cpp index d533db1fce..e340a2b1c4 100644 --- a/src/strings.cpp +++ b/src/strings.cpp @@ -1171,8 +1171,7 @@ static char *FormatString(char *buff, const char *str_arg, StringParameters *arg CargoTypes cmask = args->GetInt64(SCC_CARGO_LIST); bool first = true; - const CargoSpec *cs; - FOR_ALL_SORTED_CARGOSPECS(cs) { + for (const auto &cs : _sorted_cargo_specs) { if (!HasBit(cmask, cs->Index())) continue; if (buff >= last - 2) break; // ',' and ' ' diff --git a/src/vehicle_gui.cpp b/src/vehicle_gui.cpp index f4b5185865..c6f2ee52a7 100644 --- a/src/vehicle_gui.cpp +++ b/src/vehicle_gui.cpp @@ -513,8 +513,7 @@ struct RefitWindow : public Window { /* Loop through all cargoes in the refit mask */ int current_index = 0; - const CargoSpec *cs; - FOR_ALL_SORTED_CARGOSPECS(cs) { + for (const auto &cs : _sorted_cargo_specs) { CargoID cid = cs->Index(); /* Skip cargo type if it's not listed */ if (!HasBit(cmask, cid)) { From 356bbbb90aaa3ac981b69693ce40d3afdaa6ea33 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Guilloux?= Date: Thu, 29 Apr 2021 14:26:08 +0200 Subject: [PATCH 09/81] Fix: [MinGW] Set minimum OS version to Windows XP (#9135) --- src/network/network_chat_gui.cpp | 6 +++--- src/saveload/saveload.cpp | 2 +- src/script/api/script_date.cpp | 3 ++- src/script/squirrel.cpp | 5 +++-- src/stdafx.h | 15 +++++++++------ 5 files changed, 18 insertions(+), 13 deletions(-) diff --git a/src/network/network_chat_gui.cpp b/src/network/network_chat_gui.cpp index 862a0a4cbf..14edc15032 100644 --- a/src/network/network_chat_gui.cpp +++ b/src/network/network_chat_gui.cpp @@ -7,9 +7,6 @@ /** @file network_chat_gui.cpp GUI for handling chat messages. */ -#include /* va_list */ -#include - #include "../stdafx.h" #include "../strings_func.h" #include "../blitter/factory.hpp" @@ -28,6 +25,9 @@ #include "table/strings.h" +#include /* va_list */ +#include + #include "../safeguards.h" /** The draw buffer must be able to contain the chat message, client name and the "[All]" message, diff --git a/src/saveload/saveload.cpp b/src/saveload/saveload.cpp index dcfcc9f431..f5e823a0df 100644 --- a/src/saveload/saveload.cpp +++ b/src/saveload/saveload.cpp @@ -19,7 +19,6 @@ *
  • repeat this until everything is done, and flush any remaining output to file * */ -#include #include "../stdafx.h" #include "../debug.h" @@ -44,6 +43,7 @@ #include "../fios.h" #include "../error.h" #include +#include #include #ifdef __EMSCRIPTEN__ # include diff --git a/src/script/api/script_date.cpp b/src/script/api/script_date.cpp index 9946a26ec6..ae3dff78c6 100644 --- a/src/script/api/script_date.cpp +++ b/src/script/api/script_date.cpp @@ -7,11 +7,12 @@ /** @file script_date.cpp Implementation of ScriptDate. */ -#include #include "../../stdafx.h" #include "script_date.hpp" #include "../../date_func.h" +#include + #include "../../safeguards.h" /* static */ bool ScriptDate::IsValidDate(Date date) diff --git a/src/script/squirrel.cpp b/src/script/squirrel.cpp index b9d614e8d0..77f84a6416 100644 --- a/src/script/squirrel.cpp +++ b/src/script/squirrel.cpp @@ -7,8 +7,6 @@ /** @file squirrel.cpp the implementation of the Squirrel class. It handles all Squirrel-stuff and gives a nice API back to work with. */ -#include -#include #include "../stdafx.h" #include "../debug.h" #include "squirrel_std.hpp" @@ -21,6 +19,9 @@ #include <../squirrel/sqvm.h> #include "../core/alloc_func.hpp" +#include +#include + /** * In the memory allocator for Squirrel we want to directly use malloc/realloc, so when the OS * does not have enough memory the game does not go into unrecoverable error mode and kill the diff --git a/src/stdafx.h b/src/stdafx.h index 19009f5ff5..819eb0e614 100644 --- a/src/stdafx.h +++ b/src/stdafx.h @@ -10,6 +10,15 @@ #ifndef STDAFX_H #define STDAFX_H +#if defined(_WIN32) + /* MinGW defaults to Windows 7 if none of these are set, and they must be set before any MinGW header is included */ +# define NTDDI_VERSION NTDDI_WINXP // Windows XP +# define _WIN32_WINNT 0x501 // Windows XP +# define _WIN32_WINDOWS 0x501 // Windows XP +# define WINVER 0x0501 // Windows XP +# define _WIN32_IE_ 0x0600 // 6.0 (XP+) +#endif + #ifdef _MSC_VER /* Stop Microsoft (and clang-cl) compilers from complaining about potentially-unsafe/potentially-non-standard functions */ # define _CRT_SECURE_NO_DEPRECATE @@ -164,12 +173,6 @@ /* Stuff for MSVC */ #if defined(_MSC_VER) # pragma once -# define NTDDI_VERSION NTDDI_WINXP // Windows XP -# define _WIN32_WINNT 0x501 // Windows XP -# define _WIN32_WINDOWS 0x501 // Windows XP -# define WINVER 0x0501 // Windows XP -# define _WIN32_IE_ 0x0600 // 6.0 (XP+) - # define NOMINMAX // Disable min/max macros in windows.h. # pragma warning(disable: 4244) // 'conversion' conversion from 'type1' to 'type2', possible loss of data From 72a05921b06b02b04af6e86d981f55de4960ca61 Mon Sep 17 00:00:00 2001 From: translators Date: Thu, 29 Apr 2021 17:52:22 +0000 Subject: [PATCH 10/81] Update: Translations from eints norwegian (bokmal): 16 changes by Anolitt finnish: 2 changes by hpiirai --- src/lang/finnish.txt | 4 ++-- src/lang/norwegian_bokmal.txt | 20 ++++++++++++++++++-- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/src/lang/finnish.txt b/src/lang/finnish.txt index 4b5765492b..4435cca260 100644 --- a/src/lang/finnish.txt +++ b/src/lang/finnish.txt @@ -4463,7 +4463,7 @@ STR_ERROR_CAN_T_BUILD_CARGO_TRAM_STATION :{WHITE}Ei voi r STR_ERROR_CAN_T_BUILD_DOCK_HERE :{WHITE}Satamaa ei voi rakentaa tähän... STR_ERROR_CAN_T_BUILD_AIRPORT_HERE :{WHITE}Lentokenttää ei voi rakentaa... -STR_ERROR_ADJOINS_MORE_THAN_ONE_EXISTING :{WHITE}Liitä yhteen useampi asema/lastausalue. +STR_ERROR_ADJOINS_MORE_THAN_ONE_EXISTING :{WHITE}Vieressä on useampi kuin yksi olemassaoleva asema tai kuormausalue. STR_ERROR_STATION_TOO_SPREAD_OUT :{WHITE}... asema liian levittäytynyt STR_ERROR_TOO_MANY_STATIONS_LOADING :{WHITE}Liian monta asemaa ja lastausaluetta. STR_ERROR_TOO_MANY_STATION_SPECS :{WHITE}Rautatieasema on jakautunut liian moneen osaan @@ -4496,7 +4496,7 @@ STR_ERROR_MUST_DEMOLISH_DOCK_FIRST :{WHITE}Satama p STR_ERROR_MUST_DEMOLISH_AIRPORT_FIRST :{WHITE}Lentokenttä pitää tuhota ensin. # Waypoint related errors -STR_ERROR_WAYPOINT_ADJOINS_MORE_THAN_ONE_EXISTING :{WHITE}Liittää useamman kuin yhden reittipisteen +STR_ERROR_WAYPOINT_ADJOINS_MORE_THAN_ONE_EXISTING :{WHITE}Vieressä on useampi kuin yksi olemassaoleva reittipiste. STR_ERROR_TOO_CLOSE_TO_ANOTHER_WAYPOINT :{WHITE}Liian lähellä toista reittipistettä STR_ERROR_CAN_T_BUILD_TRAIN_WAYPOINT :{WHITE}Junien reittipistettä ei voi rakentaa tähän... diff --git a/src/lang/norwegian_bokmal.txt b/src/lang/norwegian_bokmal.txt index 623c346ac1..b79089f237 100644 --- a/src/lang/norwegian_bokmal.txt +++ b/src/lang/norwegian_bokmal.txt @@ -1994,6 +1994,7 @@ STR_FACE_TIE :Slips: STR_FACE_EARRING :Ørering: STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}Endre slips eller ørering +STR_NETWORK_SERVER_VISIBILITY_PUBLIC :Offentlig # Network server list STR_NETWORK_SERVER_LIST_CAPTION :{WHITE}Flerspiller @@ -2057,6 +2058,7 @@ STR_NETWORK_START_SERVER_NEW_GAME_NAME_TOOLTIP :{BLACK}Spillnav STR_NETWORK_START_SERVER_SET_PASSWORD :{BLACK}Sett passord STR_NETWORK_START_SERVER_PASSWORD_TOOLTIP :{BLACK}Beskytt ditt spill med et passord hvis du ikke vil at hvem som helst skal bli med på det +STR_NETWORK_START_SERVER_VISIBILITY_LABEL :{BLACK}Synlighet STR_NETWORK_START_SERVER_CLIENTS_SELECT :{BLACK}{NUM} klient{P "" er} STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS :{BLACK}Maks antall klienter: STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS.small :dra og slipp @@ -2124,8 +2126,21 @@ STR_NETWORK_NEED_COMPANY_PASSWORD_CAPTION :{WHITE}Firmaet STR_NETWORK_COMPANY_LIST_CLIENT_LIST :Liste over klienter # Network client list - - +STR_NETWORK_CLIENT_LIST_SERVER_NAME :{BLACK}Navn +STR_NETWORK_CLIENT_LIST_SERVER_VISIBILITY_TOOLTIP :{BLACK}Om andre mennesker kan se serveren din i den offentlige oppføringen +STR_NETWORK_CLIENT_LIST_PLAYER :{BLACK}Spiller +STR_NETWORK_CLIENT_LIST_PLAYER_NAME :{BLACK}Navn +STR_NETWORK_CLIENT_LIST_PLAYER_NAME_EDIT_TOOLTIP :{BLACK}Rediger ditt spillernavn +STR_NETWORK_CLIENT_LIST_PLAYER_NAME_QUERY_CAPTION :Ditt spillernavn +STR_NETWORK_CLIENT_LIST_ADMIN_CLIENT_TOOLTIP :{BLACK}Administrative handlinger å utføre for denne klienten +STR_NETWORK_CLIENT_LIST_JOIN_TOOLTIP :{BLACK}Bli med i dette firmaet +STR_NETWORK_CLIENT_LIST_CHAT_COMPANY_TOOLTIP :{BLACK}Send en melding til alle spillerne i dette firmaet +STR_NETWORK_CLIENT_LIST_CHAT_SPECTATOR_TOOLTIP :{BLACK}Send en melding til alle tilskuerne +STR_NETWORK_CLIENT_LIST_NEW_COMPANY_TOOLTIP :{BLACK}Opprett et nytt firma og bli med i det + +STR_NETWORK_CLIENT_LIST_ADMIN_CLIENT_KICK :Spark + +STR_NETWORK_CLIENT_LIST_ASK_CLIENT_BAN :{YELLOW}Er du sikker på at du vil utestenge spiller '{STRING}'? STR_NETWORK_SERVER :Tjener STR_NETWORK_CLIENT :Klient @@ -3046,6 +3061,7 @@ STR_NEWGRF_ERROR_MSG_WARNING :{RED}Advarsel: STR_NEWGRF_ERROR_MSG_ERROR :{RED}Feil: {SILVER}{STRING} STR_NEWGRF_ERROR_MSG_FATAL :{RED}Fatal: {SILVER}{STRING} STR_NEWGRF_ERROR_FATAL_POPUP :{WHITE}En fatal NewGRF-feil har oppstått:{}{STRING} +STR_NEWGRF_ERROR_POPUP :{WHITE}En NewGRF feil har oppstått:{}{STRING} STR_NEWGRF_ERROR_VERSION_NUMBER :{1:STRING} virker ikke med TTDPatch-versjonen som er rapportert av OpenTTD STR_NEWGRF_ERROR_DOS_OR_WINDOWS :{1:STRING} er for versjon {STRING} av TTD STR_NEWGRF_ERROR_UNSET_SWITCH :{1:STRING} er laget for bruk med {STRING} From 0b460bf4a17e12ba479e0822b8f9b7de29ac5816 Mon Sep 17 00:00:00 2001 From: PeterN Date: Thu, 29 Apr 2021 18:58:26 +0100 Subject: [PATCH 11/81] Fix: 'Cache' top and bottom lines of textfile viewer to avoid overdraw. (#9131) * Fix: 'Cache' top and bottom lines of textfile viewer to avoid overdraw. The text file viewer calculated the number of lines required to set the scrollbar, but did not retain this information, so this was recalculated on every draw operation. This includes overdrawing text outside the bounds of the current scroll position. With this change the top and bottom lines for each line of text are remembered, and reflowing is avoided where possible. Text outside the current scroll bounds is not drawn. Additionally the scroll interval is now based on text lines instead of pixel lines, which increases the text capacity depending on the font size. * Fix: Limit text viewer to showing 64k lines. Text files with more than 64k wrapped lines would exceed the scrollbar capacity and cause an assert. This is harder to reach now that the scrollbar counts lines instead of pixels. --- src/textfile_gui.cpp | 81 ++++++++++++++++++++++++++++++-------------- src/textfile_gui.h | 16 +++++++-- 2 files changed, 70 insertions(+), 27 deletions(-) diff --git a/src/textfile_gui.cpp b/src/textfile_gui.cpp index 075898fd71..ac8da5c2e9 100644 --- a/src/textfile_gui.cpp +++ b/src/textfile_gui.cpp @@ -67,7 +67,6 @@ TextfileWindow::TextfileWindow(TextfileType file_type) : Window(&_textfile_desc) this->GetWidget(WID_TF_CAPTION)->SetDataTip(STR_TEXTFILE_README_CAPTION + file_type, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS); this->hscroll->SetStepSize(10); // Speed up horizontal scrollbar - this->vscroll->SetStepSize(FONT_HEIGHT_MONO); } /* virtual */ TextfileWindow::~TextfileWindow() @@ -79,23 +78,38 @@ TextfileWindow::TextfileWindow(TextfileType file_type) : Window(&_textfile_desc) * Get the total height of the content displayed in this window, if wrapping is disabled. * @return the height in pixels */ -uint TextfileWindow::GetContentHeight() +uint TextfileWindow::ReflowContent() { - int max_width = this->GetWidget(WID_TF_BACKGROUND)->current_x - WD_FRAMETEXT_LEFT - WD_FRAMERECT_RIGHT; - uint height = 0; - for (uint i = 0; i < this->lines.size(); i++) { - height += GetStringHeight(this->lines[i], max_width, FS_MONO); + if (!IsWidgetLowered(WID_TF_WRAPTEXT)) { + for (auto &line : this->lines) { + line.top = height; + height++; + line.bottom = height; + } + } else { + int max_width = this->GetWidget(WID_TF_BACKGROUND)->current_x - WD_FRAMETEXT_LEFT - WD_FRAMERECT_RIGHT; + for (auto &line : this->lines) { + line.top = height; + height += GetStringHeight(line.text, max_width, FS_MONO) / FONT_HEIGHT_MONO; + line.bottom = height; + } } return height; } +uint TextfileWindow::GetContentHeight() +{ + if (this->lines.size() == 0) return 0; + return this->lines.back().bottom; +} + /* virtual */ void TextfileWindow::UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize) { switch (widget) { case WID_TF_BACKGROUND: - resize->height = 1; + resize->height = FONT_HEIGHT_MONO; size->height = 4 * resize->height + TOP_SPACING + BOTTOM_SPACING; // At least 4 lines are visible. size->width = std::max(200u, size->width); // At least 200 pixels wide. @@ -104,18 +118,17 @@ uint TextfileWindow::GetContentHeight() } /** Set scrollbars to the right lengths. */ -void TextfileWindow::SetupScrollbars() +void TextfileWindow::SetupScrollbars(bool force_reflow) { if (IsWidgetLowered(WID_TF_WRAPTEXT)) { - this->vscroll->SetCount(this->GetContentHeight()); + /* Reflow is mandatory if text wrapping is on */ + uint height = this->ReflowContent(); + this->vscroll->SetCount(std::min(UINT16_MAX, height)); this->hscroll->SetCount(0); } else { - uint max_length = 0; - for (uint i = 0; i < this->lines.size(); i++) { - max_length = std::max(max_length, GetStringBoundingBox(this->lines[i], FS_MONO).width); - } - this->vscroll->SetCount((uint)this->lines.size() * FONT_HEIGHT_MONO); - this->hscroll->SetCount(max_length + WD_FRAMETEXT_LEFT + WD_FRAMETEXT_RIGHT); + uint height = force_reflow ? this->ReflowContent() : this->GetContentHeight(); + this->vscroll->SetCount(std::min(UINT16_MAX, height)); + this->hscroll->SetCount(this->max_length + WD_FRAMETEXT_LEFT + WD_FRAMETEXT_RIGHT); } this->SetWidgetDisabledState(WID_TF_HSCROLLBAR, IsWidgetLowered(WID_TF_WRAPTEXT)); @@ -126,7 +139,6 @@ void TextfileWindow::SetupScrollbars() switch (widget) { case WID_TF_WRAPTEXT: this->ToggleWidgetLoweredState(WID_TF_WRAPTEXT); - this->SetupScrollbars(); this->InvalidateData(); break; } @@ -148,14 +160,18 @@ void TextfileWindow::SetupScrollbars() /* Draw content (now coordinates given to DrawString* are local to the new clipping region). */ int line_height = FONT_HEIGHT_MONO; - int y_offset = -this->vscroll->GetPosition(); + int pos = this->vscroll->GetPosition(); + int cap = this->vscroll->GetCapacity(); + + for (auto &line : this->lines) { + if (line.bottom < pos) continue; + if (line.top > pos + cap) break; - for (uint i = 0; i < this->lines.size(); i++) { + int y_offset = (line.top - pos) * line_height; if (IsWidgetLowered(WID_TF_WRAPTEXT)) { - y_offset = DrawStringMultiLine(0, right - x, y_offset, bottom - y, this->lines[i], TC_WHITE, SA_TOP | SA_LEFT, false, FS_MONO); + DrawStringMultiLine(0, right - x, y_offset, bottom - y, line.text, TC_WHITE, SA_TOP | SA_LEFT, false, FS_MONO); } else { - DrawString(-this->hscroll->GetPosition(), right - x, y_offset, this->lines[i], TC_WHITE, SA_TOP | SA_LEFT, false, FS_MONO); - y_offset += line_height; // margin to previous element + DrawString(-this->hscroll->GetPosition(), right - x, y_offset, line.text, TC_WHITE, SA_TOP | SA_LEFT, false, FS_MONO); } } @@ -167,7 +183,14 @@ void TextfileWindow::SetupScrollbars() this->vscroll->SetCapacityFromWidget(this, WID_TF_BACKGROUND, TOP_SPACING + BOTTOM_SPACING); this->hscroll->SetCapacityFromWidget(this, WID_TF_BACKGROUND); - this->SetupScrollbars(); + this->SetupScrollbars(false); +} + +/* virtual */ void TextfileWindow::OnInvalidateData(int data, bool gui_scope) +{ + if (!gui_scope) return; + + this->SetupScrollbars(true); } /* virtual */ void TextfileWindow::Reset() @@ -184,7 +207,7 @@ void TextfileWindow::SetupScrollbars() { if (this->search_iterator >= this->lines.size()) return nullptr; - return this->lines[this->search_iterator++]; + return this->lines[this->search_iterator++].text; } /* virtual */ bool TextfileWindow::Monospace() @@ -364,14 +387,22 @@ static void Xunzip(byte **bufp, size_t *sizep) str_validate(p, this->text + filesize, SVS_REPLACE_WITH_QUESTION_MARK | SVS_ALLOW_NEWLINE); /* Split the string on newlines. */ - this->lines.push_back(p); + int row = 0; + this->lines.emplace_back(row, p); for (; *p != '\0'; p++) { if (*p == '\n') { *p = '\0'; - this->lines.push_back(p + 1); + this->lines.emplace_back(++row, p + 1); } } + /* Calculate maximum text line length. */ + uint max_length = 0; + for (auto &line : this->lines) { + max_length = std::max(max_length, GetStringBoundingBox(line.text, FS_MONO).width); + } + this->max_length = max_length; + CheckForMissingGlyphs(true, this); } diff --git a/src/textfile_gui.h b/src/textfile_gui.h index d67435c015..5f1db14ca9 100644 --- a/src/textfile_gui.h +++ b/src/textfile_gui.h @@ -19,13 +19,23 @@ const char *GetTextfile(TextfileType type, Subdirectory dir, const char *filenam /** Window for displaying a textfile */ struct TextfileWindow : public Window, MissingGlyphSearcher { + struct Line { + int top; ///< Top scroll position. + int bottom; ///< Bottom scroll position. + const char *text; ///< Pointer to text buffer. + + Line(int top, const char *text) : top(top), bottom(top + 1), text(text) {} + }; + TextfileType file_type; ///< Type of textfile to view. Scrollbar *vscroll; ///< Vertical scrollbar. Scrollbar *hscroll; ///< Horizontal scrollbar. char *text; ///< Lines of text from the NewGRF's textfile. - std::vector lines; ///< #text, split into lines in a table with lines. + std::vector lines; ///< #text, split into lines in a table with lines. uint search_iterator; ///< Iterator for the font check search. + uint max_length; ///< Maximum length of unwrapped text line. + static const int TOP_SPACING = WD_FRAMETEXT_TOP; ///< Additional spacing at the top of the #WID_TF_BACKGROUND widget. static const int BOTTOM_SPACING = WD_FRAMETEXT_BOTTOM; ///< Additional spacing at the bottom of the #WID_TF_BACKGROUND widget. @@ -36,6 +46,7 @@ struct TextfileWindow : public Window, MissingGlyphSearcher { void OnClick(Point pt, int widget, int click_count) override; void DrawWidget(const Rect &r, int widget) const override; void OnResize() override; + void OnInvalidateData(int data = 0, bool gui_scope = true) override; void Reset() override; FontSize DefaultSize() override; @@ -46,8 +57,9 @@ struct TextfileWindow : public Window, MissingGlyphSearcher { virtual void LoadTextfile(const char *textfile, Subdirectory dir); private: + uint ReflowContent(); uint GetContentHeight(); - void SetupScrollbars(); + void SetupScrollbars(bool force_reflow); }; #endif /* TEXTFILE_GUI_H */ From a8afbe74bfdbe6aa9ceb88c8e70c63bfe37aeda0 Mon Sep 17 00:00:00 2001 From: Patric Stout Date: Thu, 29 Apr 2021 12:01:54 +0200 Subject: [PATCH 12/81] Cleanup: remove write-only variable "hostname" in NetworkGameList --- src/network/core/game_info.h | 1 - src/network/network_gamelist.cpp | 1 - src/network/network_udp.cpp | 5 ----- 3 files changed, 7 deletions(-) diff --git a/src/network/core/game_info.h b/src/network/core/game_info.h index 28aea75073..324c6ea4aa 100644 --- a/src/network/core/game_info.h +++ b/src/network/core/game_info.h @@ -74,7 +74,6 @@ struct NetworkGameInfo : NetworkServerGameInfo { uint16 map_width; ///< Map width uint16 map_height; ///< Map height char server_name[NETWORK_NAME_LENGTH]; ///< Server name - char hostname[NETWORK_HOSTNAME_LENGTH]; ///< Hostname of the server (if any) char server_revision[NETWORK_REVISION_LENGTH]; ///< The version number the server is using (e.g.: 'r304' or 0.5.0) bool dedicated; ///< Is this a dedicated server? bool version_compatible; ///< Can we connect to this server or not? (based on server_revision) diff --git a/src/network/network_gamelist.cpp b/src/network/network_gamelist.cpp index dfe07bdbbb..3ee5099b73 100644 --- a/src/network/network_gamelist.cpp +++ b/src/network/network_gamelist.cpp @@ -51,7 +51,6 @@ static void NetworkGameListHandleDelayedInsert() ClearGRFConfigList(&item->info.grfconfig); memset(&item->info, 0, sizeof(item->info)); strecpy(item->info.server_name, ins_item->info.server_name, lastof(item->info.server_name)); - strecpy(item->info.hostname, ins_item->info.hostname, lastof(item->info.hostname)); item->online = false; } item->manually |= ins_item->manually; diff --git a/src/network/network_udp.cpp b/src/network/network_udp.cpp index d62998794e..bb71e5ceaa 100644 --- a/src/network/network_udp.cpp +++ b/src/network/network_udp.cpp @@ -92,7 +92,6 @@ static void DoNetworkUDPQueryServer(NetworkAddress &address, bool needs_mutex, b /* Clear item in gamelist */ NetworkGameList *item = CallocT(1); address.GetAddressAsString(item->info.server_name, lastof(item->info.server_name)); - strecpy(item->info.hostname, address.GetHostname(), lastof(item->info.hostname)); item->address = address; item->manually = manually; NetworkGameListAddItemDelayed(item); @@ -365,10 +364,6 @@ void ClientNetworkUDPSocketHandler::Receive_SERVER_RESPONSE(Packet *p, NetworkAd } } - if (item->info.hostname[0] == '\0') { - seprintf(item->info.hostname, lastof(item->info.hostname), "%s", client_addr->GetHostname()); - } - if (client_addr->GetAddress()->ss_family == AF_INET6) { strecat(item->info.server_name, " (IPv6)", lastof(item->info.server_name)); } From 99f998805ba835f48a8061762fa19761760c7451 Mon Sep 17 00:00:00 2001 From: Patric Stout Date: Thu, 29 Apr 2021 12:05:43 +0200 Subject: [PATCH 13/81] Codechange: use std::string over stack-based strings if possible --- src/network/core/address.cpp | 32 +++++++++++++++----------------- src/network/network_gui.cpp | 5 ++--- 2 files changed, 17 insertions(+), 20 deletions(-) diff --git a/src/network/core/address.cpp b/src/network/core/address.cpp index e53566c0bb..8b51e2c082 100644 --- a/src/network/core/address.cpp +++ b/src/network/core/address.cpp @@ -313,8 +313,7 @@ static SOCKET ConnectLoopProc(addrinfo *runp) { const char *type = NetworkAddress::SocketTypeAsString(runp->ai_socktype); const char *family = NetworkAddress::AddressFamilyAsString(runp->ai_family); - char address[NETWORK_HOSTNAME_LENGTH + 6 + 7]; - NetworkAddress(runp->ai_addr, (int)runp->ai_addrlen).GetAddressAsString(address, lastof(address)); + 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) { @@ -328,7 +327,7 @@ static SOCKET ConnectLoopProc(addrinfo *runp) int err = connect(sock, runp->ai_addr, (int)runp->ai_addrlen); if (err != 0 && NetworkGetLastError() != EINPROGRESS) { - DEBUG(net, 1, "[%s] could not connect to %s over %s: %s", type, address, family, NetworkGetLastErrorString()); + DEBUG(net, 1, "[%s] could not connect to %s over %s: %s", type, address.c_str(), family, NetworkGetLastErrorString()); closesocket(sock); return INVALID_SOCKET; } @@ -344,14 +343,14 @@ static SOCKET ConnectLoopProc(addrinfo *runp) 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, NetworkGetLastErrorString()); + DEBUG(net, 1, "[%s] could not connect to %s: %s", type, address.c_str(), NetworkGetLastErrorString()); 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); + DEBUG(net, 1, "[%s] timed out while connecting to %s", type, address.c_str()); closesocket(sock); return INVALID_SOCKET; } @@ -359,13 +358,13 @@ static SOCKET ConnectLoopProc(addrinfo *runp) /* Retrieve last error, if any, on the socket. */ err = GetSocketError(sock); if (err != 0) { - DEBUG(net, 1, "[%s] could not connect to %s: %s", type, address, NetworkGetErrorString(err)); + DEBUG(net, 1, "[%s] could not connect to %s: %s", type, address.c_str(), NetworkGetErrorString(err)); closesocket(sock); return INVALID_SOCKET; } /* Connection succeeded. */ - DEBUG(net, 1, "[%s] connected to %s", type, address); + DEBUG(net, 1, "[%s] connected to %s", type, address.c_str()); return sock; } @@ -390,48 +389,47 @@ static SOCKET ListenLoopProc(addrinfo *runp) { const char *type = NetworkAddress::SocketTypeAsString(runp->ai_socktype); const char *family = NetworkAddress::AddressFamilyAsString(runp->ai_family); - char address[NETWORK_HOSTNAME_LENGTH + 6 + 7]; - NetworkAddress(runp->ai_addr, (int)runp->ai_addrlen).GetAddressAsString(address, lastof(address)); + 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, NetworkGetLastErrorString()); + DEBUG(net, 0, "[%s] could not create %s socket on port %s: %s", type, family, address.c_str(), NetworkGetLastErrorString()); return INVALID_SOCKET; } if (runp->ai_socktype == SOCK_STREAM && !SetNoDelay(sock)) { - DEBUG(net, 3, "[%s] setting TCP_NODELAY failed for port %s", type, address); + DEBUG(net, 3, "[%s] setting TCP_NODELAY failed for port %s", type, address.c_str()); } 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, NetworkGetLastErrorString()); + DEBUG(net, 3, "[%s] could not set reusable %s sockets for port %s: %s", type, family, address.c_str(), NetworkGetLastErrorString()); } #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, NetworkGetLastErrorString()); + DEBUG(net, 3, "[%s] could not disable IPv4 over IPv6 on port %s: %s", type, address.c_str(), NetworkGetLastErrorString()); } #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, NetworkGetLastErrorString()); + DEBUG(net, 1, "[%s] could not bind on %s port %s: %s", type, family, address.c_str(), NetworkGetLastErrorString()); 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, NetworkGetLastErrorString()); + DEBUG(net, 1, "[%s] could not listen at %s port %s: %s", type, family, address.c_str(), NetworkGetLastErrorString()); 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); + 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); + DEBUG(net, 1, "[%s] listening on %s port %s", type, family, address.c_str()); return sock; } diff --git a/src/network/network_gui.cpp b/src/network/network_gui.cpp index 57127aee81..e1f9a791ba 100644 --- a/src/network/network_gui.cpp +++ b/src/network/network_gui.cpp @@ -645,9 +645,8 @@ public: DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, STR_NETWORK_SERVER_LIST_SERVER_VERSION); // server version y += FONT_HEIGHT_NORMAL; - char network_addr_buffer[NETWORK_HOSTNAME_LENGTH + 6 + 7]; - sel->address.GetAddressAsString(network_addr_buffer, lastof(network_addr_buffer)); - SetDParamStr(0, network_addr_buffer); + std::string address = sel->address.GetAddressAsString(); + SetDParamStr(0, address.c_str()); DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, STR_NETWORK_SERVER_LIST_SERVER_ADDRESS); // server address y += FONT_HEIGHT_NORMAL; From be37a2cab831cb645ef0f51dbcc944bd750f6926 Mon Sep 17 00:00:00 2001 From: Patric Stout Date: Thu, 29 Apr 2021 12:09:03 +0200 Subject: [PATCH 14/81] Codechange: use NetworkAddress instead of two host/port variables where possible This also means we no longer need last_host/last_port, but can just use a single last_joined setting. --- src/console_cmds.cpp | 26 +++---- src/network/core/address.cpp | 4 +- src/network/core/config.h | 1 + src/network/core/tcp_http.cpp | 6 +- src/network/network.cpp | 117 ++++++++++++++++--------------- src/network/network_func.h | 10 +-- src/network/network_gamelist.cpp | 2 +- src/network/network_gui.cpp | 23 +++--- src/openttd.cpp | 40 ++++------- src/settings_type.h | 5 +- src/table/settings.ini | 11 +-- 11 files changed, 108 insertions(+), 137 deletions(-) diff --git a/src/console_cmds.cpp b/src/console_cmds.cpp index 8cdf9c664e..7ab6d3179e 100644 --- a/src/console_cmds.cpp +++ b/src/console_cmds.cpp @@ -893,15 +893,16 @@ DEF_CONSOLE_CMD(ConNetworkReconnect) break; } - if (StrEmpty(_settings_client.network.last_host)) { + if (StrEmpty(_settings_client.network.last_joined)) { IConsolePrint(CC_DEFAULT, "No server for reconnecting."); return true; } /* Don't resolve the address first, just print it directly as it comes from the config file. */ - IConsolePrintF(CC_DEFAULT, "Reconnecting to %s:%d...", _settings_client.network.last_host, _settings_client.network.last_port); + IConsolePrintF(CC_DEFAULT, "Reconnecting to %s ...", _settings_client.network.last_joined); - NetworkClientConnectGame(_settings_client.network.last_host, _settings_client.network.last_port, playas); + NetworkAddress address = ParseConnectionString(_settings_client.network.last_joined, NETWORK_DEFAULT_PORT); + NetworkClientConnectGame(address, playas); return true; } @@ -917,18 +918,11 @@ DEF_CONSOLE_CMD(ConNetworkConnect) if (argc < 2) return false; if (_networking) NetworkDisconnect(); // we are in network-mode, first close it! - const char *port = nullptr; - const char *company = nullptr; - char *ip = argv[1]; - /* Default settings: default port and new company */ - uint16 rport = NETWORK_DEFAULT_PORT; CompanyID join_as = COMPANY_NEW_COMPANY; + NetworkAddress address = ParseGameConnectionString(&join_as, argv[1], NETWORK_DEFAULT_PORT); - ParseGameConnectionString(&company, &port, ip); - - IConsolePrintF(CC_DEFAULT, "Connecting to %s...", ip); - if (company != nullptr) { - join_as = (CompanyID)atoi(company); + IConsolePrintF(CC_DEFAULT, "Connecting to %s...", address.GetAddressAsString().c_str()); + if (join_as != COMPANY_NEW_COMPANY) { IConsolePrintF(CC_DEFAULT, " company-no: %d", join_as); /* From a user pov 0 is a new company, internally it's different and all @@ -938,12 +932,8 @@ DEF_CONSOLE_CMD(ConNetworkConnect) join_as--; } } - if (port != nullptr) { - rport = atoi(port); - IConsolePrintF(CC_DEFAULT, " port: %s", port); - } - NetworkClientConnectGame(ip, rport, join_as); + NetworkClientConnectGame(address, join_as); return true; } diff --git a/src/network/core/address.cpp b/src/network/core/address.cpp index 8b51e2c082..e91751c33f 100644 --- a/src/network/core/address.cpp +++ b/src/network/core/address.cpp @@ -101,8 +101,8 @@ void NetworkAddress::GetAddressAsString(char *buffer, const char *last, bool wit */ std::string NetworkAddress::GetAddressAsString(bool with_family) { - /* 6 = for the : and 5 for the decimal port number */ - char buf[NETWORK_HOSTNAME_LENGTH + 6 + 7]; + /* 7 extra are for with_family, which adds " (IPvX)". */ + char buf[NETWORK_HOSTNAME_PORT_LENGTH + 7]; this->GetAddressAsString(buf, lastof(buf), with_family); return buf; } diff --git a/src/network/core/config.h b/src/network/core/config.h index 866d1791d4..cacc907faf 100644 --- a/src/network/core/config.h +++ b/src/network/core/config.h @@ -56,6 +56,7 @@ static const byte NETWORK_MASTER_SERVER_VERSION = 2; ///< What vers static const uint NETWORK_NAME_LENGTH = 80; ///< The maximum length of the server name and map name, in bytes including '\0' static const uint NETWORK_COMPANY_NAME_LENGTH = 128; ///< The maximum length of the company name, in bytes including '\0' static const uint NETWORK_HOSTNAME_LENGTH = 80; ///< The maximum length of the host name, in bytes including '\0' +static const uint NETWORK_HOSTNAME_PORT_LENGTH = 80 + 6; ///< The maximum length of the host name + port, in bytes including '\0'. The extra six is ":" + port number (with a max of 65536) static const uint NETWORK_SERVER_ID_LENGTH = 33; ///< The maximum length of the network id of the servers, in bytes including '\0' static const uint NETWORK_REVISION_LENGTH = 33; ///< The maximum length of the revision, in bytes including '\0' static const uint NETWORK_PASSWORD_LENGTH = 33; ///< The maximum length of the password, in bytes including '\0' (must be >= NETWORK_SERVER_ID_LENGTH) diff --git a/src/network/core/tcp_http.cpp b/src/network/core/tcp_http.cpp index ee74c45070..99eca1cb74 100644 --- a/src/network/core/tcp_http.cpp +++ b/src/network/core/tcp_http.cpp @@ -203,11 +203,7 @@ int NetworkHTTPSocketHandler::HandleHeader() *url = '\0'; - /* Fetch the hostname, and possible port number. */ - const char *port = nullptr; - ParseConnectionString(&port, hname); - - NetworkAddress address(hname, port == nullptr ? 80 : atoi(port)); + NetworkAddress address = ParseConnectionString(hname, 80); /* Restore the URL. */ *url = '/'; diff --git a/src/network/network.cpp b/src/network/network.cpp index c0b2e06fd4..0be46bbf35 100644 --- a/src/network/network.cpp +++ b/src/network/network.cpp @@ -447,14 +447,14 @@ static void CheckPauseOnJoin() } /** - * Converts a string to ip/port - * Format: IP:port + * Converts a string to ip/port/company + * Format: IP:port#company * * connection_string will be re-terminated to separate out the hostname, port will * be set to the port strings given by the user, inside the memory area originally - * occupied by connection_string. + * occupied by connection_string. Similar for company, if set. */ -void ParseConnectionString(const char **port, char *connection_string) +void ParseFullConnectionString(const char **company, const char **port, char *connection_string) { bool ipv6 = (strchr(connection_string, ':') != strrchr(connection_string, ':')); for (char *p = connection_string; *p != '\0'; p++) { @@ -467,6 +467,12 @@ void ParseConnectionString(const char **port, char *connection_string) ipv6 = false; break; + case '#': + if (company == nullptr) continue; + *company = p + 1; + *p = '\0'; + break; + case ':': if (ipv6) break; *port = p + 1; @@ -477,38 +483,48 @@ void ParseConnectionString(const char **port, char *connection_string) } /** - * Converts a string to ip/port/company - * Format: IP:port#company + * Convert a string containing either "hostname" or "hostname:ip" to a + * NetworkAddress. * - * connection_string will be re-terminated to separate out the hostname, and company and port will - * be set to the company and port strings given by the user, inside the memory area originally - * occupied by connection_string. + * @param connection_string The string to parse. + * @param default_port The default port to set port to if not in connection_string. + * @return A valid NetworkAddress of the parsed information. */ -void ParseGameConnectionString(const char **company, const char **port, char *connection_string) +NetworkAddress ParseConnectionString(const char *connection_string, int default_port) { - bool ipv6 = (strchr(connection_string, ':') != strrchr(connection_string, ':')); - for (char *p = connection_string; *p != '\0'; p++) { - switch (*p) { - case '[': - ipv6 = true; - break; + char internal_connection_string[NETWORK_HOSTNAME_PORT_LENGTH]; + strecpy(internal_connection_string, connection_string, lastof(internal_connection_string)); - case ']': - ipv6 = false; - break; + const char *port = nullptr; + ParseFullConnectionString(nullptr, &port, internal_connection_string); - case '#': - *company = p + 1; - *p = '\0'; - break; + int rport = port != nullptr ? atoi(port) : default_port; + return NetworkAddress(internal_connection_string, rport); +} - case ':': - if (ipv6) break; - *port = p + 1; - *p = '\0'; - break; - } - } +/** + * Convert a string containing either "hostname" or "hostname:ip" to a + * NetworkAddress, where the string can be postfixed with "#company" to + * indicate the requested company. + * + * @param company Pointer to the company variable to set iff indicted. + * @param connection_string The string to parse. + * @param default_port The default port to set port to if not in connection_string. + * @return A valid NetworkAddress of the parsed information. + */ +NetworkAddress ParseGameConnectionString(CompanyID *company, const char *connection_string, int default_port) +{ + char internal_connection_string[NETWORK_HOSTNAME_PORT_LENGTH + 4]; // 4 extra for the "#" and company + strecpy(internal_connection_string, connection_string, lastof(internal_connection_string)); + + const char *port_s = nullptr; + const char *company_s = nullptr; + ParseFullConnectionString(&company_s, &port_s, internal_connection_string); + + if (company_s != nullptr) *company = (CompanyID)atoi(company_s); + + int port = port_s != nullptr ? atoi(port_s) : default_port; + return NetworkAddress(internal_connection_string, port); } /** @@ -616,26 +632,17 @@ void NetworkTCPQueryServer(NetworkAddress address) new TCPQueryConnecter(address); } -/* Validates an address entered as a string and adds the server to +/** + * Validates an address entered as a string and adds the server to * the list. If you use this function, the games will be marked - * as manually added. */ -void NetworkAddServer(const char *b) + * as manually added. + * @param connection_string The IP:port to add to the list. + */ +void NetworkAddServer(const char *connection_string) { - if (*b != '\0') { - const char *port = nullptr; - char host[NETWORK_HOSTNAME_LENGTH]; - uint16 rport; - - strecpy(host, b, lastof(host)); - - strecpy(_settings_client.network.connect_to_ip, b, lastof(_settings_client.network.connect_to_ip)); - rport = NETWORK_DEFAULT_PORT; + if (StrEmpty(connection_string)) return; - ParseConnectionString(&port, host); - if (port != nullptr) rport = atoi(port); - - NetworkUDPQueryServer(NetworkAddress(host, rport), true); - } + NetworkUDPQueryServer(ParseConnectionString(connection_string, NETWORK_DEFAULT_PORT), true); } /** @@ -688,16 +695,13 @@ public: /* Used by clients, to connect to a server */ -void NetworkClientConnectGame(const char *hostname, uint16 port, CompanyID join_as, const char *join_server_password, const char *join_company_password) +void NetworkClientConnectGame(NetworkAddress &address, CompanyID join_as, const char *join_server_password, const char *join_company_password) { if (!_network_available) return; - - if (port == 0) return; - if (!NetworkValidateClientName()) return; - strecpy(_settings_client.network.last_host, hostname, lastof(_settings_client.network.last_host)); - _settings_client.network.last_port = port; + strecpy(_settings_client.network.last_joined, address.GetAddressAsString(false).c_str(), lastof(_settings_client.network.last_joined)); + _network_join_as = join_as; _network_join_server_password = join_server_password; _network_join_company_password = join_company_password; @@ -708,7 +712,7 @@ void NetworkClientConnectGame(const char *hostname, uint16 port, CompanyID join_ _network_join_status = NETWORK_JOIN_STATUS_CONNECTING; ShowJoinStatusWindow(); - new TCPClientConnecter(NetworkAddress(hostname, port)); + new TCPClientConnecter(address); } static void NetworkInitGameInfo() @@ -1059,13 +1063,12 @@ static void NetworkGenerateServerId() seprintf(_settings_client.network.network_id, lastof(_settings_client.network.network_id), "%s", hex_output); } -void NetworkStartDebugLog(const char *hostname, uint16 port) +void NetworkStartDebugLog(NetworkAddress &address) { extern SOCKET _debug_socket; // Comes from debug.c - DEBUG(net, 0, "Redirecting DEBUG() to %s:%d", hostname, port); + DEBUG(net, 0, "Redirecting DEBUG() to %s", address.GetAddressAsString().c_str()); - NetworkAddress address(hostname, port); SOCKET s = address.Connect(); if (s == INVALID_SOCKET) { DEBUG(net, 0, "Failed to open socket for redirection DEBUG()"); diff --git a/src/network/network_func.h b/src/network/network_func.h index a0f163345e..967bb62603 100644 --- a/src/network/network_func.h +++ b/src/network/network_func.h @@ -18,6 +18,7 @@ // #define DEBUG_FAILED_DUMP_COMMANDS #include "network_type.h" +#include "core/address.h" #include "../console_type.h" #include "../gfx_type.h" #include "../openttd.h" @@ -45,14 +46,15 @@ void NetworkReboot(); void NetworkDisconnect(bool blocking = false, bool close_admins = true); void NetworkGameLoop(); void NetworkBackgroundLoop(); -void ParseConnectionString(const char **port, char *connection_string); -void ParseGameConnectionString(const char **company, const char **port, char *connection_string); -void NetworkStartDebugLog(const char *hostname, uint16 port); +void ParseFullConnectionString(const char **company, const char **port, char *connection_string); +NetworkAddress ParseConnectionString(const char *connection_string, int default_port); +NetworkAddress ParseGameConnectionString(CompanyID *company, const char *connection_string, int default_port); +void NetworkStartDebugLog(NetworkAddress &address); void NetworkPopulateCompanyStats(NetworkCompanyStats *stats); void NetworkUpdateClientInfo(ClientID client_id); void NetworkClientsToSpectators(CompanyID cid); -void NetworkClientConnectGame(const char *hostname, uint16 port, CompanyID join_as, const char *join_server_password = nullptr, const char *join_company_password = nullptr); +void NetworkClientConnectGame(NetworkAddress &address, CompanyID join_as, const char *join_server_password = nullptr, const char *join_company_password = nullptr); void NetworkClientRequestMove(CompanyID company, const char *pass = ""); void NetworkClientSendRcon(const char *password, const char *command); void NetworkClientSendChat(NetworkAction action, DestType type, int dest, const char *msg, int64 data = 0); diff --git a/src/network/network_gamelist.cpp b/src/network/network_gamelist.cpp index 3ee5099b73..6d4285c854 100644 --- a/src/network/network_gamelist.cpp +++ b/src/network/network_gamelist.cpp @@ -151,7 +151,7 @@ void NetworkGameListRequery() /* item gets mostly zeroed by NetworkUDPQueryServer */ uint8 retries = item->retries; - NetworkUDPQueryServer(NetworkAddress(item->address)); + NetworkUDPQueryServer(item->address); item->retries = (retries >= REFRESH_GAMEINFO_X_REQUERIES) ? 0 : retries; } } diff --git a/src/network/network_gui.cpp b/src/network/network_gui.cpp index e1f9a791ba..3204b2b962 100644 --- a/src/network/network_gui.cpp +++ b/src/network/network_gui.cpp @@ -472,7 +472,7 @@ public: EM_ASM(if (window["openttd_server_list"]) openttd_server_list()); #endif - this->last_joined = NetworkGameListAddItem(NetworkAddress(_settings_client.network.last_host, _settings_client.network.last_port)); + this->last_joined = NetworkGameListAddItem(ParseConnectionString(_settings_client.network.last_joined, NETWORK_DEFAULT_PORT)); this->server = this->last_joined; if (this->last_joined != nullptr) NetworkUDPQueryServer(this->last_joined->address); @@ -735,7 +735,7 @@ public: ShowQueryString( STR_JUST_RAW_STRING, STR_NETWORK_SERVER_LIST_ENTER_IP, - NETWORK_HOSTNAME_LENGTH, // maximum number of characters including '\0' + NETWORK_HOSTNAME_PORT_LENGTH, // maximum number of characters including '\0' this, CS_ALPHANUMERAL, QSF_ACCEPT_UNCHANGED); break; @@ -745,8 +745,6 @@ public: case WID_NG_JOIN: // Join Game if (this->server != nullptr) { - seprintf(_settings_client.network.last_host, lastof(_settings_client.network.last_host), "%s", this->server->address.GetHostname()); - _settings_client.network.last_port = this->server->address.GetPort(); ShowNetworkLobbyWindow(this->server); } break; @@ -827,7 +825,10 @@ public: void OnQueryTextFinished(char *str) override { - if (!StrEmpty(str)) NetworkAddServer(str); + if (!StrEmpty(str)) { + strecpy(_settings_client.network.connect_to_ip, str, lastof(_settings_client.network.connect_to_ip)); + NetworkAddServer(str); + } } void OnResize() override @@ -1469,22 +1470,22 @@ struct NetworkLobbyWindow : public Window { case WID_NL_JOIN: // Join company /* Button can be clicked only when it is enabled. */ - NetworkClientConnectGame(_settings_client.network.last_host, _settings_client.network.last_port, this->company); + NetworkClientConnectGame(this->server->address, this->company); break; case WID_NL_NEW: // New company - NetworkClientConnectGame(_settings_client.network.last_host, _settings_client.network.last_port, COMPANY_NEW_COMPANY); + NetworkClientConnectGame(this->server->address, COMPANY_NEW_COMPANY); break; case WID_NL_SPECTATE: // Spectate game - NetworkClientConnectGame(_settings_client.network.last_host, _settings_client.network.last_port, COMPANY_SPECTATOR); + NetworkClientConnectGame(this->server->address, COMPANY_SPECTATOR); break; case WID_NL_REFRESH: // Refresh /* Clear the information so removed companies don't remain */ for (auto &company : this->company_info) company = {}; - NetworkTCPQueryServer(NetworkAddress(_settings_client.network.last_host, _settings_client.network.last_port)); + NetworkTCPQueryServer(this->server->address); break; } } @@ -1552,7 +1553,9 @@ static void ShowNetworkLobbyWindow(NetworkGameList *ngl) DeleteWindowById(WC_NETWORK_WINDOW, WN_NETWORK_WINDOW_START); DeleteWindowById(WC_NETWORK_WINDOW, WN_NETWORK_WINDOW_GAME); - NetworkTCPQueryServer(NetworkAddress(_settings_client.network.last_host, _settings_client.network.last_port)); + strecpy(_settings_client.network.last_joined, ngl->address.GetAddressAsString(false).c_str(), lastof(_settings_client.network.last_joined)); + + NetworkTCPQueryServer(ngl->address); new NetworkLobbyWindow(&_network_lobby_window_desc, ngl); } diff --git a/src/openttd.cpp b/src/openttd.cpp index 016d482873..b128243854 100644 --- a/src/openttd.cpp +++ b/src/openttd.cpp @@ -473,29 +473,21 @@ struct AfterNewGRFScan : NewGRFScanCallback { if (_switch_mode != SM_NONE) MakeNewgameSettingsLive(); if (_network_available && network_conn != nullptr) { - const char *port = nullptr; - const char *company = nullptr; - uint16 rport = NETWORK_DEFAULT_PORT; CompanyID join_as = COMPANY_NEW_COMPANY; + NetworkAddress address = ParseGameConnectionString(&join_as, network_conn, NETWORK_DEFAULT_PORT); - ParseGameConnectionString(&company, &port, network_conn); - - if (company != nullptr) { - join_as = (CompanyID)atoi(company); - - if (join_as != COMPANY_SPECTATOR) { - join_as--; - if (join_as >= MAX_COMPANIES) { - delete this; - return; - } + if (join_as != COMPANY_NEW_COMPANY && join_as != COMPANY_SPECTATOR) { + join_as--; + if (join_as >= MAX_COMPANIES) { + delete this; + return; } } - if (port != nullptr) rport = atoi(port); LoadIntroGame(); _switch_mode = SM_NONE; - NetworkClientConnectGame(network_conn, rport, join_as, join_server_password, join_company_password); + + NetworkClientConnectGame(address, join_as, join_server_password, join_company_password); } /* After the scan we're not used anymore. */ @@ -585,7 +577,7 @@ int openttd_main(int argc, char *argv[]) SetDebugString("net=6"); if (mgo.opt != nullptr) { const char *port = nullptr; - ParseConnectionString(&port, mgo.opt); + ParseFullConnectionString(nullptr, &port, mgo.opt); if (!StrEmpty(mgo.opt)) scanner->dedicated_host = mgo.opt; if (port != nullptr) scanner->dedicated_port = atoi(port); } @@ -771,15 +763,8 @@ int openttd_main(int argc, char *argv[]) NetworkStartUp(); // initialize network-core if (debuglog_conn != nullptr && _network_available) { - const char *port = nullptr; - uint16 rport; - - rport = NETWORK_DEFAULT_DEBUGLOG_PORT; - - ParseConnectionString(&port, debuglog_conn); - if (port != nullptr) rport = atoi(port); - - NetworkStartDebugLog(debuglog_conn, rport); + NetworkAddress address = ParseConnectionString(debuglog_conn, NETWORK_DEFAULT_DEBUGLOG_PORT); + NetworkStartDebugLog(address); } if (!HandleBootstrap()) { @@ -1491,7 +1476,8 @@ void GameLoop() if (_network_reconnect > 0 && --_network_reconnect == 0) { /* This means that we want to reconnect to the last host * We do this here, because it means that the network is really closed */ - NetworkClientConnectGame(_settings_client.network.last_host, _settings_client.network.last_port, COMPANY_SPECTATOR); + NetworkAddress address = ParseConnectionString(_settings_client.network.last_joined, NETWORK_DEFAULT_PORT); + NetworkClientConnectGame(address, COMPANY_SPECTATOR); } /* Singleplayer */ StateGameLoop(); diff --git a/src/settings_type.h b/src/settings_type.h index bb078205b8..5361fc3f1f 100644 --- a/src/settings_type.h +++ b/src/settings_type.h @@ -269,7 +269,7 @@ struct NetworkSettings { bool server_advertise; ///< advertise the server to the masterserver char client_name[NETWORK_CLIENT_NAME_LENGTH]; ///< name of the player (as client) char default_company_pass[NETWORK_PASSWORD_LENGTH]; ///< default password for new companies in encrypted form - char connect_to_ip[NETWORK_HOSTNAME_LENGTH]; ///< default for the "Add server" query + char connect_to_ip[NETWORK_HOSTNAME_PORT_LENGTH]; ///< default for the "Add server" query char network_id[NETWORK_SERVER_ID_LENGTH]; ///< network ID for servers bool autoclean_companies; ///< automatically remove companies that are not in use uint8 autoclean_unprotected; ///< remove passwordless companies after this many months @@ -281,8 +281,7 @@ struct NetworkSettings { Year restart_game_year; ///< year the server restarts uint8 min_active_clients; ///< minimum amount of active clients to unpause the game bool reload_cfg; ///< reload the config file before restarting - char last_host[NETWORK_HOSTNAME_LENGTH]; ///< IP address of the last joined server - uint16 last_port; ///< port of the last joined server + char last_joined[NETWORK_HOSTNAME_PORT_LENGTH]; ///< Last joined server bool no_http_content_downloads; ///< do not do content downloads over HTTP }; diff --git a/src/table/settings.ini b/src/table/settings.ini index 391a5b427f..4e64bd0326 100644 --- a/src/table/settings.ini +++ b/src/table/settings.ini @@ -4069,21 +4069,12 @@ def = false cat = SC_EXPERT [SDTC_STR] -var = network.last_host +var = network.last_joined type = SLE_STRB flags = SLF_NOT_IN_SAVE | SLF_NO_NETWORK_SYNC def = """" cat = SC_EXPERT -[SDTC_VAR] -var = network.last_port -type = SLE_UINT16 -flags = SLF_NOT_IN_SAVE | SLF_NO_NETWORK_SYNC -def = 0 -min = 0 -max = UINT16_MAX -cat = SC_EXPERT - [SDTC_BOOL] var = network.no_http_content_downloads flags = SLF_NOT_IN_SAVE | SLF_NO_NETWORK_SYNC From a61696d6c565ff92c6604b12eefe36198d094056 Mon Sep 17 00:00:00 2001 From: rubidium42 Date: Thu, 29 Apr 2021 16:43:13 +0200 Subject: [PATCH 15/81] Change: [Network] Encapsulate logic about the connection string to the network code (#23) --- src/console_cmds.cpp | 21 ++------------------- src/network/core/tcp_http.cpp | 2 +- src/network/network.cpp | 26 +++++++++++++++++++++----- src/network/network_func.h | 7 ++----- src/network/network_internal.h | 4 ++++ src/openttd.cpp | 19 +++---------------- 6 files changed, 33 insertions(+), 46 deletions(-) diff --git a/src/console_cmds.cpp b/src/console_cmds.cpp index 7ab6d3179e..9dce399096 100644 --- a/src/console_cmds.cpp +++ b/src/console_cmds.cpp @@ -901,8 +901,7 @@ DEF_CONSOLE_CMD(ConNetworkReconnect) /* Don't resolve the address first, just print it directly as it comes from the config file. */ IConsolePrintF(CC_DEFAULT, "Reconnecting to %s ...", _settings_client.network.last_joined); - NetworkAddress address = ParseConnectionString(_settings_client.network.last_joined, NETWORK_DEFAULT_PORT); - NetworkClientConnectGame(address, playas); + NetworkClientConnectGame(_settings_client.network.last_joined, playas); return true; } @@ -918,23 +917,7 @@ DEF_CONSOLE_CMD(ConNetworkConnect) if (argc < 2) return false; if (_networking) NetworkDisconnect(); // we are in network-mode, first close it! - CompanyID join_as = COMPANY_NEW_COMPANY; - NetworkAddress address = ParseGameConnectionString(&join_as, argv[1], NETWORK_DEFAULT_PORT); - - IConsolePrintF(CC_DEFAULT, "Connecting to %s...", address.GetAddressAsString().c_str()); - if (join_as != COMPANY_NEW_COMPANY) { - IConsolePrintF(CC_DEFAULT, " company-no: %d", join_as); - - /* From a user pov 0 is a new company, internally it's different and all - * companies are offset by one to ease up on users (eg companies 1-8 not 0-7) */ - if (join_as != COMPANY_SPECTATOR) { - if (join_as > MAX_COMPANIES) return false; - join_as--; - } - } - - NetworkClientConnectGame(address, join_as); - + NetworkClientConnectGame(argv[1], COMPANY_NEW_COMPANY); return true; } diff --git a/src/network/core/tcp_http.cpp b/src/network/core/tcp_http.cpp index 99eca1cb74..e0c269fafb 100644 --- a/src/network/core/tcp_http.cpp +++ b/src/network/core/tcp_http.cpp @@ -12,7 +12,7 @@ #include "../../stdafx.h" #include "../../debug.h" #include "../../rev.h" -#include "../network_func.h" +#include "../network_internal.h" #include "game_info.h" #include "tcp_http.h" diff --git a/src/network/network.cpp b/src/network/network.cpp index 0be46bbf35..d7cf9367a1 100644 --- a/src/network/network.cpp +++ b/src/network/network.cpp @@ -490,10 +490,10 @@ void ParseFullConnectionString(const char **company, const char **port, char *co * @param default_port The default port to set port to if not in connection_string. * @return A valid NetworkAddress of the parsed information. */ -NetworkAddress ParseConnectionString(const char *connection_string, int default_port) +NetworkAddress ParseConnectionString(const std::string &connection_string, int default_port) { char internal_connection_string[NETWORK_HOSTNAME_PORT_LENGTH]; - strecpy(internal_connection_string, connection_string, lastof(internal_connection_string)); + strecpy(internal_connection_string, connection_string.c_str(), lastof(internal_connection_string)); const char *port = nullptr; ParseFullConnectionString(nullptr, &port, internal_connection_string); @@ -512,10 +512,10 @@ NetworkAddress ParseConnectionString(const char *connection_string, int default_ * @param default_port The default port to set port to if not in connection_string. * @return A valid NetworkAddress of the parsed information. */ -NetworkAddress ParseGameConnectionString(CompanyID *company, const char *connection_string, int default_port) +NetworkAddress ParseGameConnectionString(CompanyID *company, const std::string &connection_string, int default_port) { char internal_connection_string[NETWORK_HOSTNAME_PORT_LENGTH + 4]; // 4 extra for the "#" and company - strecpy(internal_connection_string, connection_string, lastof(internal_connection_string)); + strecpy(internal_connection_string, connection_string.c_str(), lastof(internal_connection_string)); const char *port_s = nullptr; const char *company_s = nullptr; @@ -693,6 +693,20 @@ public: } }; +void NetworkClientConnectGame(const std::string &connection_string, CompanyID default_company, const char *join_server_password, const char *join_company_password) +{ + CompanyID join_as = default_company; + NetworkAddress address = ParseGameConnectionString(&join_as, connection_string, NETWORK_DEFAULT_PORT); + + if (join_as != COMPANY_NEW_COMPANY && join_as != COMPANY_SPECTATOR) { + join_as--; + if (join_as >= MAX_COMPANIES) { + return; + } + } + + NetworkClientConnectGame(address, join_as, join_server_password, join_company_password); +} /* Used by clients, to connect to a server */ void NetworkClientConnectGame(NetworkAddress &address, CompanyID join_as, const char *join_server_password, const char *join_company_password) @@ -1063,10 +1077,12 @@ static void NetworkGenerateServerId() seprintf(_settings_client.network.network_id, lastof(_settings_client.network.network_id), "%s", hex_output); } -void NetworkStartDebugLog(NetworkAddress &address) +void NetworkStartDebugLog(const std::string &connection_string) { extern SOCKET _debug_socket; // Comes from debug.c + NetworkAddress address = ParseConnectionString(connection_string, NETWORK_DEFAULT_DEBUGLOG_PORT); + DEBUG(net, 0, "Redirecting DEBUG() to %s", address.GetAddressAsString().c_str()); SOCKET s = address.Connect(); diff --git a/src/network/network_func.h b/src/network/network_func.h index 967bb62603..cd2b291bc7 100644 --- a/src/network/network_func.h +++ b/src/network/network_func.h @@ -18,7 +18,6 @@ // #define DEBUG_FAILED_DUMP_COMMANDS #include "network_type.h" -#include "core/address.h" #include "../console_type.h" #include "../gfx_type.h" #include "../openttd.h" @@ -47,14 +46,12 @@ void NetworkDisconnect(bool blocking = false, bool close_admins = true); void NetworkGameLoop(); void NetworkBackgroundLoop(); void ParseFullConnectionString(const char **company, const char **port, char *connection_string); -NetworkAddress ParseConnectionString(const char *connection_string, int default_port); -NetworkAddress ParseGameConnectionString(CompanyID *company, const char *connection_string, int default_port); -void NetworkStartDebugLog(NetworkAddress &address); +void NetworkStartDebugLog(const std::string &connection_string); void NetworkPopulateCompanyStats(NetworkCompanyStats *stats); void NetworkUpdateClientInfo(ClientID client_id); void NetworkClientsToSpectators(CompanyID cid); -void NetworkClientConnectGame(NetworkAddress &address, CompanyID join_as, const char *join_server_password = nullptr, const char *join_company_password = nullptr); +void NetworkClientConnectGame(const std::string &connection_string, CompanyID default_company, const char *join_server_password = nullptr, const char *join_company_password = nullptr); void NetworkClientRequestMove(CompanyID company, const char *pass = ""); void NetworkClientSendRcon(const char *password, const char *command); void NetworkClientSendChat(NetworkAction action, DestType type, int dest, const char *msg, int64 data = 0); diff --git a/src/network/network_internal.h b/src/network/network_internal.h index c64eac7954..ff3c8c22d2 100644 --- a/src/network/network_internal.h +++ b/src/network/network_internal.h @@ -119,4 +119,8 @@ StringID GetNetworkErrorMsg(NetworkErrorCode err); bool NetworkFindName(char *new_name, const char *last); const char *GenerateCompanyPasswordHash(const char *password, const char *password_server_id, uint32 password_game_seed); +void NetworkClientConnectGame(NetworkAddress &address, CompanyID join_as, const char *join_server_password = nullptr, const char *join_company_password = nullptr); +NetworkAddress ParseConnectionString(const std::string &connection_string, int default_port); +NetworkAddress ParseGameConnectionString(CompanyID *company, const std::string &connection_string, int default_port); + #endif /* NETWORK_INTERNAL_H */ diff --git a/src/openttd.cpp b/src/openttd.cpp index b128243854..fbeeba793e 100644 --- a/src/openttd.cpp +++ b/src/openttd.cpp @@ -473,21 +473,10 @@ struct AfterNewGRFScan : NewGRFScanCallback { if (_switch_mode != SM_NONE) MakeNewgameSettingsLive(); if (_network_available && network_conn != nullptr) { - CompanyID join_as = COMPANY_NEW_COMPANY; - NetworkAddress address = ParseGameConnectionString(&join_as, network_conn, NETWORK_DEFAULT_PORT); - - if (join_as != COMPANY_NEW_COMPANY && join_as != COMPANY_SPECTATOR) { - join_as--; - if (join_as >= MAX_COMPANIES) { - delete this; - return; - } - } - LoadIntroGame(); _switch_mode = SM_NONE; - NetworkClientConnectGame(address, join_as, join_server_password, join_company_password); + NetworkClientConnectGame(network_conn, COMPANY_NEW_COMPANY, join_server_password, join_company_password); } /* After the scan we're not used anymore. */ @@ -763,8 +752,7 @@ int openttd_main(int argc, char *argv[]) NetworkStartUp(); // initialize network-core if (debuglog_conn != nullptr && _network_available) { - NetworkAddress address = ParseConnectionString(debuglog_conn, NETWORK_DEFAULT_DEBUGLOG_PORT); - NetworkStartDebugLog(address); + NetworkStartDebugLog(debuglog_conn); } if (!HandleBootstrap()) { @@ -1476,8 +1464,7 @@ void GameLoop() if (_network_reconnect > 0 && --_network_reconnect == 0) { /* This means that we want to reconnect to the last host * We do this here, because it means that the network is really closed */ - NetworkAddress address = ParseConnectionString(_settings_client.network.last_joined, NETWORK_DEFAULT_PORT); - NetworkClientConnectGame(address, COMPANY_SPECTATOR); + NetworkClientConnectGame(_settings_client.network.last_joined, COMPANY_SPECTATOR); } /* Singleplayer */ StateGameLoop(); From 14e92bd8e241998ced263ee542965a71bbdd77a5 Mon Sep 17 00:00:00 2001 From: glx22 Date: Wed, 28 Apr 2021 21:24:24 +0200 Subject: [PATCH 16/81] Codechange: Replace window related FOR_ALL with range-based for loops --- src/misc_gui.cpp | 3 +- src/sound.cpp | 3 +- src/viewport.cpp | 11 ++-- src/widgets/dropdown.cpp | 3 +- src/window.cpp | 131 +++++++++++++-------------------------- src/window_gui.h | 68 ++++++++++++++++++-- 6 files changed, 113 insertions(+), 106 deletions(-) diff --git a/src/misc_gui.cpp b/src/misc_gui.cpp index 9be207ac60..b2de451404 100644 --- a/src/misc_gui.cpp +++ b/src/misc_gui.cpp @@ -1284,8 +1284,7 @@ void ShowQuery(StringID caption, StringID message, Window *parent, QueryCallback { if (parent == nullptr) parent = FindWindowById(WC_MAIN_WINDOW, 0); - const Window *w; - FOR_ALL_WINDOWS_FROM_BACK(w) { + for (const Window *w : Window::IterateFromBack()) { 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 7d70fa760d..d91476729b 100644 --- a/src/sound.cpp +++ b/src/sound.cpp @@ -238,8 +238,7 @@ static void SndPlayScreenCoordFx(SoundID sound, int left, int right, int top, in { if (_settings_client.music.effect_vol == 0) return; - const Window *w; - FOR_ALL_WINDOWS_FROM_BACK(w) { + for (const Window *w : Window::IterateFromBack()) { const Viewport *vp = w->viewport; if (vp != nullptr && diff --git a/src/viewport.cpp b/src/viewport.cpp index aae659d53d..303e027600 100644 --- a/src/viewport.cpp +++ b/src/viewport.cpp @@ -268,7 +268,7 @@ static Point _vp_move_offs; static void DoSetViewportPosition(const Window *w, int left, int top, int width, int height) { - FOR_ALL_WINDOWS_FROM_BACK_FROM(w, w) { + for (const Window *w : Window::IterateFromBack(w)) { if (left + width > w->left && w->left + w->width > left && top + height > w->top && @@ -1475,8 +1475,7 @@ void ViewportSign::MarkDirty(ZoomLevel maxzoom) const zoomlevels[zoom].bottom = this->top + ScaleByZoom(VPSM_TOP + FONT_HEIGHT_NORMAL + VPSM_BOTTOM + 1, zoom); } - Window *w; - FOR_ALL_WINDOWS_FROM_BACK(w) { + for (const Window *w : Window::IterateFromBack()) { Viewport *vp = w->viewport; if (vp != nullptr && vp->zoom <= maxzoom) { assert(vp->width != 0); @@ -1949,8 +1948,7 @@ bool MarkAllViewportsDirty(int left, int top, int right, int bottom) { bool dirty = false; - Window *w; - FOR_ALL_WINDOWS_FROM_BACK(w) { + for (const Window *w : Window::IterateFromBack()) { Viewport *vp = w->viewport; if (vp != nullptr) { assert(vp->width != 0); @@ -1963,8 +1961,7 @@ bool MarkAllViewportsDirty(int left, int top, int right, int bottom) void ConstrainAllViewportsZoom() { - Window *w; - FOR_ALL_WINDOWS_FROM_FRONT(w) { + for (Window *w : Window::IterateFromFront()) { 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 6b1debb9db..f5536f52f4 100644 --- a/src/widgets/dropdown.cpp +++ b/src/widgets/dropdown.cpp @@ -505,8 +505,7 @@ void ShowDropDownMenu(Window *w, const StringID *strings, int selected, int butt */ int HideDropDownMenu(Window *pw) { - Window *w; - FOR_ALL_WINDOWS_FROM_BACK(w) { + for (Window *w : Window::IterateFromBack()) { if (w->window_class != WC_DROPDOWN_MENU) continue; DropdownWindow *dw = dynamic_cast(w); diff --git a/src/window.cpp b/src/window.cpp index 2216fb2418..a82afc7cda 100644 --- a/src/window.cpp +++ b/src/window.cpp @@ -898,8 +898,7 @@ static bool MayBeShown(const Window *w) */ static void DrawOverlappedWindow(Window *w, int left, int top, int right, int bottom) { - const Window *v; - FOR_ALL_WINDOWS_FROM_BACK_FROM(v, w->z_front) { + for (const Window *v : Window::IterateFromBack(w->z_front)) { if (MayBeShown(v) && right > v->left && bottom > v->top && @@ -958,13 +957,11 @@ static void DrawOverlappedWindow(Window *w, int left, int top, int right, int bo */ void DrawOverlappedWindowForAll(int left, int top, int right, int bottom) { - Window *w; - DrawPixelInfo *old_dpi = _cur_dpi; DrawPixelInfo bk; _cur_dpi = &bk; - FOR_ALL_WINDOWS_FROM_BACK(w) { + for (Window *w : Window::IterateFromBack()) { if (MayBeShown(w) && right > w->left && bottom > w->top && @@ -1057,8 +1054,7 @@ void Window::SetShaded(bool make_shaded) */ static Window *FindChildWindow(const Window *w, WindowClass wc) { - Window *v; - FOR_ALL_WINDOWS_FROM_BACK(v) { + for (Window *v : Window::IterateFromBack()) { if ((wc == WC_INVALID || wc == v->window_class) && v->parent == w) return v; } @@ -1132,8 +1128,7 @@ Window::~Window() */ Window *FindWindowById(WindowClass cls, WindowNumber number) { - Window *w; - FOR_ALL_WINDOWS_FROM_BACK(w) { + for (Window *w : Window::IterateFromBack()) { if (w->window_class == cls && w->window_number == number) return w; } @@ -1148,8 +1143,7 @@ Window *FindWindowById(WindowClass cls, WindowNumber number) */ Window *FindWindowByClass(WindowClass cls) { - Window *w; - FOR_ALL_WINDOWS_FROM_BACK(w) { + for (Window *w : Window::IterateFromBack()) { if (w->window_class == cls) return w; } @@ -1176,13 +1170,11 @@ void DeleteWindowById(WindowClass cls, WindowNumber number, bool force) */ void DeleteWindowByClass(WindowClass cls) { - Window *w; - 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 */ - FOR_ALL_WINDOWS_FROM_BACK(w) { + for (Window *w : Window::IterateFromBack()) { if (w->window_class == cls) { delete w; goto restart_search; @@ -1198,13 +1190,11 @@ restart_search: */ void DeleteCompanyWindows(CompanyID id) { - Window *w; - 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 */ - FOR_ALL_WINDOWS_FROM_BACK(w) { + for (Window *w : Window::IterateFromBack()) { if (w->owner == id) { delete w; goto restart_search; @@ -1224,8 +1214,7 @@ restart_search: */ void ChangeWindowOwner(Owner old_owner, Owner new_owner) { - Window *w; - FOR_ALL_WINDOWS_FROM_BACK(w) { + for (Window *w : Window::IterateFromBack()) { if (w->owner != old_owner) continue; switch (w->window_class) { @@ -1594,8 +1583,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. */ - const Window *w; - FOR_ALL_WINDOWS_FROM_BACK(w) { + for (const Window *w : Window::IterateFromBack()) { if (w->window_class == WC_MAIN_WINDOW) continue; if (right > w->left && @@ -1640,8 +1628,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. */ - const Window *w; - FOR_ALL_WINDOWS_FROM_BACK(w) { + for (const Window *w : Window::IterateFromBack()) { if (w->window_class == WC_MAIN_WINDOW) continue; if (left + width > w->left && @@ -1678,8 +1665,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. */ - const Window *w; - FOR_ALL_WINDOWS_FROM_BACK(w) { + for (const Window *w : Window::IterateFromBack()) { if (w->window_class == WC_MAIN_WINDOW) continue; if (IsGoodAutoPlace1(w->left + w->width, w->top, width, height, toolbar_y, pt)) return pt; @@ -1696,7 +1682,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_ALL_WINDOWS_FROM_BACK(w) { + for (const Window *w : Window::IterateFromBack()) { if (w->window_class == WC_MAIN_WINDOW) continue; if (IsGoodAutoPlace2(w->left + w->width, w->top, width, height, toolbar_y, pt)) return pt; @@ -1713,7 +1699,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_ALL_WINDOWS_FROM_BACK(w) { + for (const Window *w : Window::IterateFromBack()) { if (w->left == left && w->top == top) { left += offset_x; top += offset_y; @@ -1880,8 +1866,7 @@ Window::Window(WindowDesc *desc) : window_desc(desc), mouse_capture_widget(-1) */ Window *FindWindowFromPt(int x, int y) { - Window *w; - FOR_ALL_WINDOWS_FROM_FRONT(w) { + for (Window *w : Window::IterateFromFront()) { if (MayBeShown(w) && IsInsideBS(x, w->left, w->width) && IsInsideBS(y, w->top, w->height)) { return w; } @@ -1918,10 +1903,9 @@ void UnInitWindowSystem() { UnshowCriticalError(); - Window *w; - FOR_ALL_WINDOWS_FROM_FRONT(w) delete w; + for (Window *w : Window::IterateFromFront()) delete w; - for (w = _z_front_window; w != nullptr; /* nothing */) { + for (Window *w = _z_front_window; w != nullptr; /* nothing */) { Window *to_del = w; w = w->z_back; free(to_del); @@ -1948,8 +1932,7 @@ static void DecreaseWindowCounters() if (_scroller_click_timeout != 0) _scroller_click_timeout--; if (hundredth_tick_timeout != 0) hundredth_tick_timeout--; - Window *w; - FOR_ALL_WINDOWS_FROM_FRONT(w) { + for (Window *w : Window::IterateFromFront()) { if (!_network_dedicated && hundredth_tick_timeout == 0) w->OnHundredthTick(); if (_scroller_click_timeout == 0) { @@ -1975,7 +1958,7 @@ static void DecreaseWindowCounters() w->OnMouseLoop(); } - FOR_ALL_WINDOWS_FROM_FRONT(w) { + for (Window *w : Window::IterateFromFront()) { if ((w->flags & WF_TIMEOUT) && --w->timeout_timer == 0) { CLRBITS(w->flags, WF_TIMEOUT); @@ -2217,8 +2200,7 @@ static EventState HandleWindowDragging() if (_left_button_down && _cursor.delta.x == 0 && _cursor.delta.y == 0) return ES_HANDLED; /* Otherwise find the window... */ - Window *w; - FOR_ALL_WINDOWS_FROM_BACK(w) { + for (Window *w : Window::IterateFromBack()) { if (w->flags & WF_DRAGGING) { /* Stop the dragging if the left mouse button was released */ if (!_left_button_down) { @@ -2234,13 +2216,11 @@ static EventState HandleWindowDragging() int ny = y; if (_settings_client.gui.window_snap_radius != 0) { - const Window *v; - int hsnap = _settings_client.gui.window_snap_radius; int vsnap = _settings_client.gui.window_snap_radius; int delta; - FOR_ALL_WINDOWS_FROM_BACK(v) { + for (const Window *v : Window::IterateFromBack()) { if (v == w) continue; // Don't snap at yourself if (y + w->height > v->top && y < v->top + v->height) { @@ -2454,8 +2434,7 @@ static void HandleScrollbarScrolling(Window *w) */ static EventState HandleActiveWidget() { - Window *w; - FOR_ALL_WINDOWS_FROM_BACK(w) { + for (Window *w : Window::IterateFromBack()) { if (w->mouse_capture_widget >= 0) { /* Abort if no button is clicked any more. */ if (!_left_button_down) { @@ -2565,8 +2544,7 @@ static bool MaybeBringWindowToFront(Window *w) w_height = w->unshaded_size.height; } - Window *u; - FOR_ALL_WINDOWS_FROM_BACK_FROM(u, w->z_front) { + for (Window *u : Window::IterateFromBack(w->z_front)) { /* A modal child will prevent the activation of the parent window */ if (u->parent == w && (u->window_desc->flags & WDF_MODAL)) { u->SetWhiteBorder(); @@ -2708,8 +2686,7 @@ void HandleKeypress(uint keycode, WChar key) } /* Call the event, start with the uppermost window, but ignore the toolbar. */ - Window *w; - FOR_ALL_WINDOWS_FROM_FRONT(w) { + for (Window *w : Window::IterateFromFront()) { if (w->window_class == WC_MAIN_TOOLBAR) continue; if (w->window_desc->hotkeys != nullptr) { int hotkey = w->window_desc->hotkeys->CheckMatch(keycode); @@ -2718,7 +2695,7 @@ void HandleKeypress(uint keycode, WChar key) if (w->OnKeyPress(key, keycode) == ES_HANDLED) return; } - w = FindWindowById(WC_MAIN_TOOLBAR, 0); + Window *w = FindWindowById(WC_MAIN_TOOLBAR, 0); /* When there is no toolbar w is null, check for that */ if (w != nullptr) { if (w->window_desc->hotkeys != nullptr) { @@ -2737,8 +2714,7 @@ void HandleKeypress(uint keycode, WChar key) void HandleCtrlChanged() { /* Call the event, start with the uppermost window. */ - Window *w; - FOR_ALL_WINDOWS_FROM_FRONT(w) { + for (Window *w : Window::IterateFromFront()) { if (w->OnCTRLStateChange() == ES_HANDLED) return; } } @@ -3073,8 +3049,8 @@ static void CheckSoftLimit() for (;;) { uint deletable_count = 0; - Window *w, *last_deletable = nullptr; - FOR_ALL_WINDOWS_FROM_FRONT(w) { + Window *last_deletable = nullptr; + for (Window *w : Window::IterateFromFront()) { if (w->window_class == WC_MAIN_WINDOW || IsVitalWindow(w) || (w->flags & WF_STICKY)) continue; last_deletable = w; @@ -3127,8 +3103,7 @@ void InputLoop() */ void CallWindowRealtimeTickEvent(uint delta_ms) { - Window *w; - FOR_ALL_WINDOWS_FROM_FRONT(w) { + for (Window *w : Window::IterateFromFront()) { w->OnRealtimeTick(delta_ms); } } @@ -3156,10 +3131,8 @@ void UpdateWindows() NetworkChatMessageLoop(); } - Window *w; - /* Process invalidations before anything else. */ - FOR_ALL_WINDOWS_FROM_FRONT(w) { + for (Window *w : Window::IterateFromFront()) { w->ProcessScheduledInvalidations(); w->ProcessHighlightedInvalidations(); } @@ -3192,7 +3165,7 @@ void UpdateWindows() if (window_timer.HasElapsed()) { window_timer.SetInterval(MILLISECONDS_PER_TICK); - FOR_ALL_WINDOWS_FROM_FRONT(w) { + for (Window *w : Window::IterateFromFront()) { if ((w->flags & WF_WHITE_BORDER) && --w->white_border_timer == 0) { CLRBITS(w->flags, WF_WHITE_BORDER); w->SetDirty(); @@ -3202,7 +3175,7 @@ void UpdateWindows() DrawDirtyBlocks(); - FOR_ALL_WINDOWS_FROM_BACK(w) { + for (Window *w : Window::IterateFromBack()) { /* Update viewport only if window is not shaded. */ if (w->viewport != nullptr && !w->IsShaded()) UpdateViewportPosition(w); } @@ -3218,8 +3191,7 @@ void UpdateWindows() */ void SetWindowDirty(WindowClass cls, WindowNumber number) { - const Window *w; - FOR_ALL_WINDOWS_FROM_BACK(w) { + for (const Window *w : Window::IterateFromBack()) { if (w->window_class == cls && w->window_number == number) w->SetDirty(); } } @@ -3232,8 +3204,7 @@ void SetWindowDirty(WindowClass cls, WindowNumber number) */ void SetWindowWidgetDirty(WindowClass cls, WindowNumber number, byte widget_index) { - const Window *w; - FOR_ALL_WINDOWS_FROM_BACK(w) { + for (const Window *w : Window::IterateFromBack()) { if (w->window_class == cls && w->window_number == number) { w->SetWidgetDirty(widget_index); } @@ -3246,8 +3217,7 @@ void SetWindowWidgetDirty(WindowClass cls, WindowNumber number, byte widget_inde */ void SetWindowClassesDirty(WindowClass cls) { - Window *w; - FOR_ALL_WINDOWS_FROM_BACK(w) { + for (const Window *w : Window::IterateFromBack()) { if (w->window_class == cls) w->SetDirty(); } } @@ -3319,8 +3289,7 @@ void Window::ProcessHighlightedInvalidations() */ void InvalidateWindowData(WindowClass cls, WindowNumber number, int data, bool gui_scope) { - Window *w; - FOR_ALL_WINDOWS_FROM_BACK(w) { + for (Window *w : Window::IterateFromBack()) { if (w->window_class == cls && w->window_number == number) { w->InvalidateData(data, gui_scope); } @@ -3337,9 +3306,7 @@ void InvalidateWindowData(WindowClass cls, WindowNumber number, int data, bool g */ void InvalidateWindowClassesData(WindowClass cls, int data, bool gui_scope) { - Window *w; - - FOR_ALL_WINDOWS_FROM_BACK(w) { + for (Window *w : Window::IterateFromBack()) { if (w->window_class == cls) { w->InvalidateData(data, gui_scope); } @@ -3351,8 +3318,7 @@ void InvalidateWindowClassesData(WindowClass cls, int data, bool gui_scope) */ void CallWindowGameTickEvent() { - Window *w; - FOR_ALL_WINDOWS_FROM_FRONT(w) { + for (Window *w : Window::IterateFromFront()) { w->OnGameTick(); } } @@ -3365,13 +3331,11 @@ void CallWindowGameTickEvent() */ void DeleteNonVitalWindows() { - Window *w; - 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 */ - FOR_ALL_WINDOWS_FROM_BACK(w) { + for (const Window *w : Window::IterateFromBack()) { if (w->window_class != WC_MAIN_WINDOW && w->window_class != WC_SELECT_GAME && w->window_class != WC_MAIN_TOOLBAR && @@ -3394,8 +3358,6 @@ restart_search: */ void DeleteAllNonVitalWindows() { - Window *w; - /* Delete every window except for stickied ones, then sticky ones as well */ DeleteNonVitalWindows(); @@ -3403,7 +3365,7 @@ 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 */ - FOR_ALL_WINDOWS_FROM_BACK(w) { + for (const Window *w : Window::IterateFromBack()) { if (w->flags & WF_STICKY) { delete w; goto restart_search; @@ -3428,20 +3390,18 @@ void DeleteAllMessages() */ void DeleteConstructionWindows() { - Window *w; - 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 */ - FOR_ALL_WINDOWS_FROM_BACK(w) { + for (const Window *w : Window::IterateFromBack()) { if (w->window_desc->flags & WDF_CONSTRUCTION) { delete w; goto restart_search; } } - FOR_ALL_WINDOWS_FROM_BACK(w) w->SetDirty(); + for (const Window *w : Window::IterateFromBack()) w->SetDirty(); } /** Delete all always on-top windows to get an empty screen */ @@ -3460,8 +3420,7 @@ void ReInitAllWindows() extern void InitDepotWindowBlockSizes(); InitDepotWindowBlockSizes(); - Window *w; - FOR_ALL_WINDOWS_FROM_BACK(w) { + for (Window *w : Window::IterateFromBack()) { w->ReInit(); } @@ -3550,8 +3509,7 @@ int PositionNetworkChatWindow(Window *w) */ void ChangeVehicleViewports(VehicleID from_index, VehicleID to_index) { - Window *w; - FOR_ALL_WINDOWS_FROM_BACK(w) { + for (const Window *w : Window::IterateFromBack()) { if (w->viewport != nullptr && w->viewport->follow_vehicle == from_index) { w->viewport->follow_vehicle = to_index; w->SetDirty(); @@ -3569,8 +3527,7 @@ void RelocateAllWindows(int neww, int newh) { DeleteWindowById(WC_DROPDOWN_MENU, 0); - Window *w; - FOR_ALL_WINDOWS_FROM_BACK(w) { + for (Window *w : Window::IterateFromBack()) { 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 67a799c3d3..66c867a547 100644 --- a/src/window_gui.h +++ b/src/window_gui.h @@ -810,6 +810,68 @@ public: * @pre this->IsNewGRFInspectable() */ virtual void ShowNewGRFInspectWindow() const { NOT_REACHED(); } + + /** + * 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 + struct WindowIterator { + typedef T value_type; + typedef T *pointer; + typedef T &reference; + typedef size_t difference_type; + typedef std::forward_iterator_tag iterator_category; + + explicit WindowIterator(T *start) : w(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; } + WindowIterator & operator++() { this->Next(); this->Validate(); return *this; } + + private: + T *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 + struct Iterate { + Iterate(T *from) : from(from) {} + WindowIterator begin() { return WindowIterator(this->from); } + WindowIterator end() { return WindowIterator(nullptr); } + bool empty() { return this->begin() == this->end(); } + private: + T *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); } + + /** + * 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); } }; /** @@ -888,12 +950,6 @@ void GuiShowTooltips(Window *parent, StringID str, uint paramcount = 0, const ui /* widget.cpp */ int GetWidgetFromPos(const Window *w, int x, int y); -/** Iterate over all windows */ -#define FOR_ALL_WINDOWS_FROM_BACK_FROM(w, start) for (w = start; w != nullptr; w = w->z_front) if (w->window_class != WC_INVALID) -#define FOR_ALL_WINDOWS_FROM_FRONT_FROM(w, start) for (w = start; w != nullptr; w = w->z_back) if (w->window_class != WC_INVALID) -#define FOR_ALL_WINDOWS_FROM_BACK(w) FOR_ALL_WINDOWS_FROM_BACK_FROM(w, _z_back_window) -#define FOR_ALL_WINDOWS_FROM_FRONT(w) FOR_ALL_WINDOWS_FROM_FRONT_FROM(w, _z_front_window) - extern Point _cursorpos_drag_start; extern int _scrollbar_start_pos; From 9a8756d7ed6fdde20bad9be8c8b8bc8fda0170f9 Mon Sep 17 00:00:00 2001 From: glx22 Date: Thu, 29 Apr 2021 17:51:05 +0200 Subject: [PATCH 17/81] Codechange: Replace FOR_ALL_CARGOSPECS with range-based for loops --- src/cargotype.cpp | 10 ++---- src/cargotype.h | 47 ++++++++++++++++++++++++++--- src/economy.cpp | 3 +- src/newgrf.cpp | 6 ++-- src/newgrf_station.cpp | 3 +- src/script/api/script_cargolist.cpp | 3 +- src/station_cmd.cpp | 3 +- src/town_cmd.cpp | 3 +- 8 files changed, 53 insertions(+), 25 deletions(-) diff --git a/src/cargotype.cpp b/src/cargotype.cpp index 81818d5f9d..bf9561dda8 100644 --- a/src/cargotype.cpp +++ b/src/cargotype.cpp @@ -84,8 +84,7 @@ void SetupCargoForClimate(LandscapeID l) */ CargoID GetCargoIDByLabel(CargoLabel cl) { - const CargoSpec *cs; - FOR_ALL_CARGOSPECS(cs) { + for (const CargoSpec *cs : CargoSpec::Iterate()) { if (cs->label == cl) return cs->Index(); } @@ -103,8 +102,7 @@ CargoID GetCargoIDByBitnum(uint8 bitnum) { if (bitnum == INVALID_CARGO) return CT_INVALID; - const CargoSpec *cs; - FOR_ALL_CARGOSPECS(cs) { + for (const CargoSpec *cs : CargoSpec::Iterate()) { if (cs->bitnum == bitnum) return cs->Index(); } @@ -132,7 +130,6 @@ SpriteID CargoSpec::GetCargoIcon() const std::vector _sorted_cargo_specs; ///< Cargo specifications sorted alphabetically by name. uint8 _sorted_standard_cargo_specs_size; ///< Number of standard cargo specifications stored in the _sorted_cargo_specs array. - /** Sort cargo specifications by their name. */ static bool CargoSpecNameSorter(const CargoSpec * const &a, const CargoSpec * const &b) { @@ -169,9 +166,8 @@ static bool CargoSpecClassSorter(const CargoSpec * const &a, const CargoSpec * c void InitializeSortedCargoSpecs() { _sorted_cargo_specs.clear(); - const CargoSpec *cargo; /* Add each cargo spec to the list. */ - FOR_ALL_CARGOSPECS(cargo) { + for (const CargoSpec *cargo : CargoSpec::Iterate()) { _sorted_cargo_specs.push_back(cargo); } diff --git a/src/cargotype.h b/src/cargotype.h index afc501a2f2..4a295f1ae5 100644 --- a/src/cargotype.h +++ b/src/cargotype.h @@ -122,6 +122,49 @@ struct CargoSpec { SpriteID GetCargoIcon() const; + /** + * Iterator to iterate all valid CargoSpec + */ + struct Iterator { + typedef CargoSpec value_type; + typedef CargoSpec *pointer; + typedef CargoSpec &reference; + typedef size_t difference_type; + typedef std::forward_iterator_tag iterator_category; + + explicit Iterator(size_t index) : index(index) + { + this->ValidateIndex(); + }; + + bool operator==(const Iterator &other) const { return this->index == other.index; } + bool operator!=(const Iterator &other) const { return !(*this == other); } + CargoSpec * operator*() const { return CargoSpec::Get(this->index); } + Iterator & operator++() { this->index++; this->ValidateIndex(); return *this; } + + private: + size_t index; + void ValidateIndex() { while (this->index < CargoSpec::GetArraySize() && !(CargoSpec::Get(this->index)->IsValid())) this->index++; } + }; + + /* + * Iterable ensemble of all valid CargoSpec + */ + struct IterateWrapper { + size_t from; + IterateWrapper(size_t from = 0) : from(from) {} + Iterator begin() { return Iterator(this->from); } + Iterator end() { return Iterator(CargoSpec::GetArraySize()); } + bool empty() { return this->begin() == this->end(); } + }; + + /** + * Returns an iterable ensemble of all valid CargoSpec + * @param from index of the first CargoSpec to consider + * @return an iterable ensemble of all valid CargoSpec + */ + static IterateWrapper Iterate(size_t from = 0) { return IterateWrapper(from); } + private: static CargoSpec array[NUM_CARGO]; ///< Array holding all CargoSpecs @@ -150,10 +193,6 @@ static inline bool IsCargoInClass(CargoID c, CargoClass cc) return (CargoSpec::Get(c)->classes & cc) != 0; } -#define FOR_ALL_CARGOSPECS_FROM(var, start) for (size_t cargospec_index = start; var = nullptr, cargospec_index < CargoSpec::GetArraySize(); cargospec_index++) \ - if ((var = CargoSpec::Get(cargospec_index))->IsValid()) -#define FOR_ALL_CARGOSPECS(var) FOR_ALL_CARGOSPECS_FROM(var, 0) - #define FOR_EACH_SET_CARGO_ID(var, cargo_bits) FOR_EACH_SET_BIT_EX(CargoID, var, CargoTypes, cargo_bits) /** diff --git a/src/economy.cpp b/src/economy.cpp index 716446ba9e..9982e79e02 100644 --- a/src/economy.cpp +++ b/src/economy.cpp @@ -798,8 +798,7 @@ void RecomputePrices() } /* Setup cargo payment */ - CargoSpec *cs; - FOR_ALL_CARGOSPECS(cs) { + for (CargoSpec *cs : CargoSpec::Iterate()) { cs->current_payment = ((int64)cs->initial_payment * _economy.inflation_payment) >> 16; } diff --git a/src/newgrf.cpp b/src/newgrf.cpp index 46bd5a6558..19cc436dd7 100644 --- a/src/newgrf.cpp +++ b/src/newgrf.cpp @@ -5332,8 +5332,7 @@ static CargoID TranslateCargo(uint8 feature, uint8 ctype) return CT_INVALID; } - const CargoSpec *cs; - FOR_ALL_CARGOSPECS(cs) { + for (const CargoSpec *cs : CargoSpec::Iterate()) { if (cs->bitnum == ctype) { grfmsg(6, "TranslateCargo: Cargo bitnum %d mapped to cargo type %d.", ctype, cs->Index()); return cs->Index(); @@ -8811,8 +8810,7 @@ static void CalculateRefitMasks() if (_gted[engine].cargo_allowed != 0) { /* Build up the list of cargo types from the set cargo classes. */ - const CargoSpec *cs; - FOR_ALL_CARGOSPECS(cs) { + for (const CargoSpec *cs : CargoSpec::Iterate()) { if (_gted[engine].cargo_allowed & cs->classes) SetBit(mask, cs->Index()); if (_gted[engine].cargo_disallowed & cs->classes) SetBit(not_mask, cs->Index()); } diff --git a/src/newgrf_station.cpp b/src/newgrf_station.cpp index a127d54c9b..fa3f831aa5 100644 --- a/src/newgrf_station.cpp +++ b/src/newgrf_station.cpp @@ -572,8 +572,7 @@ StationResolverObject::StationResolverObject(const StationSpec *statspec, BaseSt } else if (Station::IsExpected(this->station_scope.st)) { const Station *st = Station::From(this->station_scope.st); /* Pick the first cargo that we have waiting */ - const CargoSpec *cs; - FOR_ALL_CARGOSPECS(cs) { + for (const CargoSpec *cs : CargoSpec::Iterate()) { if (this->station_scope.statspec->grf_prop.spritegroup[cs->Index()] != nullptr && st->goods[cs->Index()].cargo.TotalCount() > 0) { ctype = cs->Index(); diff --git a/src/script/api/script_cargolist.cpp b/src/script/api/script_cargolist.cpp index fbd150c6cf..0019dc5e91 100644 --- a/src/script/api/script_cargolist.cpp +++ b/src/script/api/script_cargolist.cpp @@ -19,8 +19,7 @@ ScriptCargoList::ScriptCargoList() { - const CargoSpec *cs; - FOR_ALL_CARGOSPECS(cs) { + for (const CargoSpec *cs : CargoSpec::Iterate()) { this->AddItem(cs->Index()); } } diff --git a/src/station_cmd.cpp b/src/station_cmd.cpp index 7266ad6187..482b954625 100644 --- a/src/station_cmd.cpp +++ b/src/station_cmd.cpp @@ -3467,8 +3467,7 @@ static void UpdateStationRating(Station *st) byte_inc_sat(&st->time_since_load); byte_inc_sat(&st->time_since_unload); - const CargoSpec *cs; - FOR_ALL_CARGOSPECS(cs) { + for (const CargoSpec *cs : CargoSpec::Iterate()) { GoodsEntry *ge = &st->goods[cs->Index()]; /* Slowly increase the rating back to his original level in the case we * didn't deliver cargo yet to this station. This happens when a bribe diff --git a/src/town_cmd.cpp b/src/town_cmd.cpp index 43f3523f8e..a0f5df23ed 100644 --- a/src/town_cmd.cpp +++ b/src/town_cmd.cpp @@ -2779,8 +2779,7 @@ CommandCost CmdRenameTown(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 */ const CargoSpec *FindFirstCargoWithTownEffect(TownEffect effect) { - const CargoSpec *cs; - FOR_ALL_CARGOSPECS(cs) { + for (const CargoSpec *cs : CargoSpec::Iterate()) { if (cs->town_effect == effect) return cs; } return nullptr; From f018471b36fe1ffaedf98430b4156ad369e26c66 Mon Sep 17 00:00:00 2001 From: PeterN Date: Thu, 29 Apr 2021 22:46:42 +0100 Subject: [PATCH 18/81] Cleanup: Remove old FiosList helper methods. (#9139) --- src/console_cmds.cpp | 4 +- src/fios.cpp | 35 ++++++++-------- src/fios.h | 86 +--------------------------------------- src/fios_gui.cpp | 20 +++++----- src/os/windows/win32.cpp | 2 +- 5 files changed, 30 insertions(+), 117 deletions(-) diff --git a/src/console_cmds.cpp b/src/console_cmds.cpp index 9dce399096..401a52b1f9 100644 --- a/src/console_cmds.cpp +++ b/src/console_cmds.cpp @@ -59,7 +59,7 @@ public: /** Declare the file storage cache as being invalid, also clears all stored files. */ void InvalidateFileList() { - this->Clear(); + this->clear(); this->file_list_valid = false; } @@ -403,7 +403,7 @@ DEF_CONSOLE_CMD(ConListFiles) } _console_file_list.ValidateFileList(true); - for (uint i = 0; i < _console_file_list.Length(); i++) { + for (uint i = 0; i < _console_file_list.size(); i++) { IConsolePrintF(CC_DEFAULT, "%d) %s", i, _console_file_list[i].title); } diff --git a/src/fios.cpp b/src/fios.cpp index b68da08f80..8528e8c622 100644 --- a/src/fios.cpp +++ b/src/fios.cpp @@ -63,11 +63,6 @@ bool FiosItem::operator< (const FiosItem &other) const return (_savegame_sort_order & SORT_DESCENDING) ? r > 0 : r < 0; } -FileList::~FileList() -{ - this->Clear(); -} - /** * Construct a file list with the given kind of files, for the stated purpose. * @param abstract_filetype Kind of files to collect. @@ -75,7 +70,7 @@ FileList::~FileList() */ void FileList::BuildFileList(AbstractFileType abstract_filetype, SaveLoadOperation fop) { - this->Clear(); + this->clear(); assert(fop == SLO_LOAD || fop == SLO_SAVE); switch (abstract_filetype) { @@ -107,7 +102,8 @@ void FileList::BuildFileList(AbstractFileType abstract_filetype, SaveLoadOperati */ const FiosItem *FileList::FindItem(const char *file) { - for (const FiosItem *item = this->Begin(); item != this->End(); item++) { + for (const auto &it : *this) { + const FiosItem *item = ⁢ if (strcmp(file, item->name) == 0) return item; if (strcmp(file, item->title) == 0) return item; } @@ -117,13 +113,14 @@ const FiosItem *FileList::FindItem(const char *file) int i = strtol(file, &endptr, 10); if (file == endptr || *endptr != '\0') i = -1; - if (IsInsideMM(i, 0, this->Length())) return this->Get(i); + if (IsInsideMM(i, 0, this->size())) return &this->at(i); /* As a last effort assume it is an OpenTTD savegame and * that the ".sav" part was not given. */ char long_file[MAX_PATH]; seprintf(long_file, lastof(long_file), "%s.sav", file); - for (const FiosItem *item = this->Begin(); item != this->End(); item++) { + for (const auto &it : *this) { + const FiosItem *item = ⁢ if (strcmp(long_file, item->name) == 0) return item; if (strcmp(long_file, item->title) == 0) return item; } @@ -302,11 +299,11 @@ bool FiosFileScanner::AddFile(const std::string &filename, size_t basepath_lengt FiosType type = this->callback_proc(this->fop, filename, ext.c_str(), fios_title, lastof(fios_title)); if (type == FIOS_TYPE_INVALID) return false; - for (const FiosItem *fios = file_list.Begin(); fios != file_list.End(); fios++) { - if (filename == fios->name) return false; + for (const auto &fios : file_list) { + if (filename == fios.name) return false; } - FiosItem *fios = file_list.Append(); + FiosItem *fios = &file_list.emplace_back(); #ifdef _WIN32 // Retrieve the file modified date using GetFileTime rather than stat to work around an obscure MSVC bug that affects Windows XP HANDLE fh = CreateFile(OTTD2FS(filename).c_str(), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, OPEN_EXISTING, 0, nullptr); @@ -367,13 +364,13 @@ static void FiosGetFileList(SaveLoadOperation fop, fios_getlist_callback_proc *c size_t sort_start; char d_name[sizeof(fios->name)]; - file_list.Clear(); + file_list.clear(); assert(_fios_path != nullptr); /* A parent directory link exists if we are not in the root directory */ if (!FiosIsRoot(_fios_path->c_str())) { - fios = file_list.Append(); + fios = &file_list.emplace_back(); fios->type = FIOS_TYPE_PARENT; fios->mtime = 0; strecpy(fios->name, "..", lastof(fios->name)); @@ -390,7 +387,7 @@ static void FiosGetFileList(SaveLoadOperation fop, fios_getlist_callback_proc *c if (FiosIsValidFile(_fios_path->c_str(), dirent, &sb) && S_ISDIR(sb.st_mode) && (!FiosIsHiddenFile(dirent) || strncasecmp(d_name, PERSONAL_DIR, strlen(d_name)) == 0) && strcmp(d_name, ".") != 0 && strcmp(d_name, "..") != 0) { - fios = file_list.Append(); + fios = &file_list.emplace_back(); fios->type = FIOS_TYPE_DIR; fios->mtime = 0; strecpy(fios->name, d_name, lastof(fios->name)); @@ -407,12 +404,12 @@ static void FiosGetFileList(SaveLoadOperation fop, fios_getlist_callback_proc *c { SortingBits order = _savegame_sort_order; _savegame_sort_order = SORT_BY_NAME | SORT_ASCENDING; - std::sort(file_list.files.begin(), file_list.files.end()); + std::sort(file_list.begin(), file_list.end()); _savegame_sort_order = order; } /* This is where to start sorting for the filenames */ - sort_start = file_list.Length(); + sort_start = file_list.size(); /* Show files */ FiosFileScanner scanner(fop, callback_proc, file_list); @@ -422,12 +419,12 @@ static void FiosGetFileList(SaveLoadOperation fop, fios_getlist_callback_proc *c scanner.Scan(nullptr, subdir, true, true); } - std::sort(file_list.files.begin() + sort_start, file_list.files.end()); + std::sort(file_list.begin() + sort_start, file_list.end()); /* Show drives */ FiosGetDrives(file_list); - file_list.Compact(); + file_list.shrink_to_fit(); } /** diff --git a/src/fios.h b/src/fios.h index 3a16b6426a..28a1cc6aa5 100644 --- a/src/fios.h +++ b/src/fios.h @@ -109,94 +109,10 @@ struct FiosItem { }; /** List of file information. */ -class FileList { +class FileList : public std::vector { public: - ~FileList(); - - /** - * Construct a new entry in the file list. - * @return Pointer to the new items to be initialized. - */ - inline FiosItem *Append() - { - return &this->files.emplace_back(); - } - - /** - * Get the number of files in the list. - * @return The number of files stored in the list. - */ - inline size_t Length() const - { - return this->files.size(); - } - - /** - * Get a pointer to the first file information. - * @return Address of the first file information. - */ - inline const FiosItem *Begin() const - { - return this->files.data(); - } - - /** - * Get a pointer behind the last file information. - * @return Address behind the last file information. - */ - inline const FiosItem *End() const - { - return this->Begin() + this->Length(); - } - - /** - * Get a pointer to the indicated file information. File information must exist. - * @return Address of the indicated existing file information. - */ - inline const FiosItem *Get(size_t index) const - { - return this->files.data() + index; - } - - /** - * Get a pointer to the indicated file information. File information must exist. - * @return Address of the indicated existing file information. - */ - inline FiosItem *Get(size_t index) - { - return this->files.data() + index; - } - - inline const FiosItem &operator[](size_t index) const - { - return this->files[index]; - } - - /** - * Get a reference to the indicated file information. File information must exist. - * @return The requested file information. - */ - inline FiosItem &operator[](size_t index) - { - return this->files[index]; - } - - /** Remove all items from the list. */ - inline void Clear() - { - this->files.clear(); - } - - /** Compact the list down to the smallest block size boundary. */ - inline void Compact() - { - this->files.shrink_to_fit(); - } - void BuildFileList(AbstractFileType abstract_filetype, SaveLoadOperation fop); const FiosItem *FindItem(const char *file); - - std::vector files; ///< The list of files. }; enum SortingBits { diff --git a/src/fios_gui.cpp b/src/fios_gui.cpp index e1133dd3cc..8de6235d62 100644 --- a/src/fios_gui.cpp +++ b/src/fios_gui.cpp @@ -249,8 +249,8 @@ static void SortSaveGameList(FileList &file_list) * Drives (A:\ (windows only) are always under the files (FIOS_TYPE_DRIVE) * Only sort savegames/scenarios, not directories */ - for (const FiosItem *item = file_list.Begin(); item != file_list.End(); item++) { - switch (item->type) { + for (const auto &item : file_list) { + switch (item.type) { case FIOS_TYPE_DIR: sort_start++; break; case FIOS_TYPE_PARENT: sort_start++; break; case FIOS_TYPE_DRIVE: sort_end++; break; @@ -258,7 +258,7 @@ static void SortSaveGameList(FileList &file_list) } } - std::sort(file_list.files.begin() + sort_start, file_list.files.end() - sort_end); + std::sort(file_list.begin() + sort_start, file_list.end() - sort_end); } struct SaveLoadWindow : public Window { @@ -437,14 +437,14 @@ public: uint y = r.top + WD_FRAMERECT_TOP; uint scroll_pos = this->vscroll->GetPosition(); - for (uint row = 0; row < this->fios_items.Length(); row++) { + for (uint row = 0; row < this->fios_items.size(); row++) { if (!this->fios_items_shown[row]) { /* The current item is filtered out : we do not show it */ scroll_pos++; continue; } if (row < scroll_pos) continue; - const FiosItem *item = this->fios_items.Get(row); + const FiosItem *item = &this->fios_items[row]; if (item == this->selected) { GfxFillRect(r.left + 1, y, r.right, y + this->resize.step_height, PC_DARK_BLUE); @@ -651,7 +651,7 @@ public: if (!this->fios_items_shown[i]) y++; i++; } - const FiosItem *file = this->fios_items.Get(y); + const FiosItem *file = &this->fios_items[y]; const char *name = FiosBrowseTo(file); if (name == nullptr) { @@ -734,7 +734,7 @@ public: if (!this->fios_items_shown[i]) y++; i++; } - const FiosItem *file = this->fios_items.Get(y); + const FiosItem *file = &this->fios_items[y]; if (file != this->highlighted) { this->highlighted = file; @@ -812,7 +812,7 @@ public: _fios_path_changed = true; this->fios_items.BuildFileList(this->abstract_filetype, this->fop); - this->vscroll->SetCount((uint)this->fios_items.Length()); + this->vscroll->SetCount((uint)this->fios_items.size()); this->selected = nullptr; _load_check_data.Clear(); @@ -852,10 +852,10 @@ public: case SLIWD_FILTER_CHANGES: /* Filter changes */ - this->fios_items_shown.resize(this->fios_items.Length()); + this->fios_items_shown.resize(this->fios_items.size()); uint items_shown_count = 0; ///< The number of items shown in the list /* We pass through every fios item */ - for (uint i = 0; i < this->fios_items.Length(); i++) { + for (uint i = 0; i < this->fios_items.size(); i++) { if (this->string_filter.IsEmpty()) { /* We don't filter anything out if the filter editbox is empty */ this->fios_items_shown[i] = true; diff --git a/src/os/windows/win32.cpp b/src/os/windows/win32.cpp index ddcfd4866c..eee81be402 100644 --- a/src/os/windows/win32.cpp +++ b/src/os/windows/win32.cpp @@ -209,7 +209,7 @@ void FiosGetDrives(FileList &file_list) GetLogicalDriveStrings(lengthof(drives), drives); for (s = drives; *s != '\0';) { - FiosItem *fios = file_list.Append(); + FiosItem *fios = &file_list.emplace_back(); fios->type = FIOS_TYPE_DRIVE; fios->mtime = 0; seprintf(fios->name, lastof(fios->name), "%c:", s[0] & 0xFF); From f00564eeb20a09647f6f52b4ca5b39cb1e6a59aa Mon Sep 17 00:00:00 2001 From: rubidium42 Date: Fri, 30 Apr 2021 00:16:41 +0200 Subject: [PATCH 19/81] Fix: String validation could leave invalid Utf8 encoded strings (#9096) In case a character was encoded in multiple bytes, but required fewer bytes to be encoded, the first byte would be copied to the output leaving an invalid Utf8 encoded string. Later uses of the validated string would use the same decode logic, which would yield a question mark and just read a single byte, so nothing dangerous happened. Furthermore, because the next byte would not be a first byte of an encoded Utf8 character, the last few valid characters could be removed by the validation as well. --- src/string.cpp | 42 ++++++++++++++++++++++++++++++------------ 1 file changed, 30 insertions(+), 12 deletions(-) diff --git a/src/string.cpp b/src/string.cpp index dfd01450e0..38f7d1bd10 100644 --- a/src/string.cpp +++ b/src/string.cpp @@ -192,19 +192,35 @@ static void str_validate(T &dst, const char *str, const char *last, StringValida while (str <= last && *str != '\0') { size_t len = Utf8EncodedCharLen(*str); - /* If the character is unknown, i.e. encoded length is 0 - * we assume worst case for the length check. - * The length check is needed to prevent Utf8Decode to read - * over the terminating '\0' if that happens to be placed - * within the encoding of an UTF8 character. */ - if ((len == 0 && str + 4 > last) || str + len > last) break; - WChar c; - len = Utf8Decode(&c, str); - /* It's possible to encode the string termination character - * into a multiple bytes. This prevents those termination - * characters to be skipped */ - if (c == '\0') break; + /* If the first byte does not look like the first byte of an encoded + * character, i.e. encoded length is 0, then this byte is definitely bad + * and it should be skipped. + * When the first byte looks like the first byte of an encoded character, + * then the remaining bytes in the string are checked whether the whole + * encoded character can be there. If that is not the case, this byte is + * skipped. + * Finally we attempt to decode the encoded character, which does certain + * extra validations to see whether the correct number of bytes were used + * to encode the character. If that is not the case, the byte is probably + * invalid and it is skipped. We could emit a question mark, but then the + * logic below cannot just copy bytes, it would need to re-encode the + * decoded characters as the length in bytes may have changed. + * + * The goals here is to get as much valid Utf8 encoded characters from the + * source string to the destination string. + * + * Note: a multi-byte encoded termination ('\0') will trigger the encoded + * char length and the decoded length to differ, so it will be ignored as + * invalid character data. If it were to reach the termination, then we + * would also reach the "last" byte of the string and a normal '\0' + * termination will be placed after it. + */ + if (len == 0 || str + len > last || len != Utf8Decode(&c, str)) { + /* Maybe the next byte is still a valid character? */ + str++; + continue; + } if ((IsPrintable(c) && (c < SCC_SPRITE_START || c > SCC_SPRITE_END)) || ((settings & SVS_ALLOW_CONTROL_CODE) != 0 && c == SCC_ENCODED)) { /* Copy the character back. Even if dst is current the same as str @@ -225,6 +241,8 @@ static void str_validate(T &dst, const char *str, const char *last, StringValida if ((settings & SVS_REPLACE_WITH_QUESTION_MARK) != 0) *dst++ = '?'; } } + + /* String termination, if needed, is left to the caller of this function. */ } /** From 69118d063f9316e10128580f790175cd2779abdc Mon Sep 17 00:00:00 2001 From: Patric Stout Date: Fri, 30 Apr 2021 11:34:47 +0200 Subject: [PATCH 20/81] Change: use TCP for everything except for master-server and initial server scan (#9130) This means that pressing Refresh button and adding servers manually now uses TCP. The master-server and initial scan are still UDP as they will be replaced by Game Coordinator; no need to change this now. If we query a server that is too old, show a proper warning to the user informing him the server is too old. --- os/emscripten/pre.js | 10 +++++--- src/lang/english.txt | 1 + src/network/network.cpp | 44 +++++++++++++++++++++++--------- src/network/network_client.cpp | 35 +++++++++++++++++++------ src/network/network_client.h | 6 +++-- src/network/network_gamelist.cpp | 10 -------- src/network/network_gui.cpp | 11 ++++---- src/network/network_internal.h | 4 +-- 8 files changed, 78 insertions(+), 43 deletions(-) diff --git a/os/emscripten/pre.js b/os/emscripten/pre.js index 1563e4f95b..82664004ea 100644 --- a/os/emscripten/pre.js +++ b/os/emscripten/pre.js @@ -65,10 +65,14 @@ Module.preRun.push(function() { } window.openttd_server_list = function() { - add_server = Module.cwrap("em_openttd_add_server", null, ["string", "number"]); + add_server = Module.cwrap("em_openttd_add_server", null, ["string"]); - /* Add servers that support WebSocket here. Example: - * add_server("localhost", 3979); */ + /* Add servers that support WebSocket here. Examples: + * add_server("localhost"); + * add_server("localhost:3979"); + * add_server("127.0.0.1:3979"); + * add_server("[::1]:3979"); + */ } var leftButtonDown = false; diff --git a/src/lang/english.txt b/src/lang/english.txt index 3037bf90fe..f8e3618e51 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -2217,6 +2217,7 @@ STR_NETWORK_ERROR_TIMEOUT_COMPUTER :{WHITE}Your com STR_NETWORK_ERROR_TIMEOUT_MAP :{WHITE}Your computer took too long to download the map STR_NETWORK_ERROR_TIMEOUT_JOIN :{WHITE}Your computer took too long to join the server STR_NETWORK_ERROR_INVALID_CLIENT_NAME :{WHITE}Your player name is not valid +STR_NETWORK_ERROR_SERVER_TOO_OLD :{WHITE}The queried server is too old for this client ############ Leave those lines in this order!! STR_NETWORK_ERROR_CLIENT_GENERAL :general error diff --git a/src/network/network.cpp b/src/network/network.cpp index d7cf9367a1..c31a67487d 100644 --- a/src/network/network.cpp +++ b/src/network/network.cpp @@ -602,8 +602,11 @@ static void NetworkInitialize(bool close_admins = true) /** Non blocking connection create to query servers */ class TCPQueryConnecter : TCPConnecter { +private: + bool request_company_info; + public: - TCPQueryConnecter(const NetworkAddress &address) : TCPConnecter(address) {} + TCPQueryConnecter(const NetworkAddress &address, bool request_company_info) : TCPConnecter(address), request_company_info(request_company_info) {} void OnFailure() override { @@ -613,36 +616,53 @@ public: void OnConnect(SOCKET s) override { _networking = true; - new ClientNetworkGameSocketHandler(s); - MyClient::SendInformationQuery(); + new ClientNetworkGameSocketHandler(s, address); + MyClient::SendInformationQuery(request_company_info); } }; /** * Query a server to fetch his game-info. * @param address the address to query. + * @param request_company_info Whether to request company info too. */ -void NetworkTCPQueryServer(NetworkAddress address) +void NetworkTCPQueryServer(NetworkAddress address, bool request_company_info) { if (!_network_available) return; NetworkDisconnect(); NetworkInitialize(); - new TCPQueryConnecter(address); + new TCPQueryConnecter(address, request_company_info); } /** * Validates an address entered as a string and adds the server to * the list. If you use this function, the games will be marked * as manually added. - * @param connection_string The IP:port to add to the list. + * @param connection_string The IP:port of the server to add. + * @return The entry on the game list. */ -void NetworkAddServer(const char *connection_string) +NetworkGameList *NetworkAddServer(const std::string &connection_string) { - if (StrEmpty(connection_string)) return; + if (connection_string.empty()) return nullptr; + + NetworkAddress address = ParseConnectionString(connection_string, NETWORK_DEFAULT_PORT); + + /* Ensure the item already exists in the list */ + NetworkGameList *item = NetworkGameListAddItem(address); + if (StrEmpty(item->info.server_name)) { + ClearGRFConfigList(&item->info.grfconfig); + address.GetAddressAsString(item->info.server_name, lastof(item->info.server_name)); + item->manually = true; + + NetworkRebuildHostList(); + UpdateNetworkGameWindow(); + } + + NetworkTCPQueryServer(address); - NetworkUDPQueryServer(ParseConnectionString(connection_string, NETWORK_DEFAULT_PORT), true); + return item; } /** @@ -687,7 +707,7 @@ public: void OnConnect(SOCKET s) override { _networking = true; - new ClientNetworkGameSocketHandler(s); + new ClientNetworkGameSocketHandler(s, this->address); IConsoleCmdExec("exec scripts/on_client.scr 0"); NetworkClient_Connected(); } @@ -1132,9 +1152,9 @@ void NetworkShutDown() #ifdef __EMSCRIPTEN__ extern "C" { -void CDECL em_openttd_add_server(const char *host, int port) +void CDECL em_openttd_add_server(const char *connection_string) { - NetworkUDPQueryServer(NetworkAddress(host, port), true); + NetworkAddServer(connection_string); } } diff --git a/src/network/network_client.cpp b/src/network/network_client.cpp index af7073e558..f73c8be528 100644 --- a/src/network/network_client.cpp +++ b/src/network/network_client.cpp @@ -145,7 +145,7 @@ void ClientNetworkEmergencySave() * Create a new socket for the client side of the game connection. * @param s The socket to connect with. */ -ClientNetworkGameSocketHandler::ClientNetworkGameSocketHandler(SOCKET s) : NetworkGameSocketHandler(s), savegame(nullptr), status(STATUS_INACTIVE) +ClientNetworkGameSocketHandler::ClientNetworkGameSocketHandler(SOCKET s, NetworkAddress address) : NetworkGameSocketHandler(s), address(address), savegame(nullptr), status(STATUS_INACTIVE) { assert(ClientNetworkGameSocketHandler::my_client == nullptr); ClientNetworkGameSocketHandler::my_client = this; @@ -345,14 +345,18 @@ static_assert(NETWORK_SERVER_ID_LENGTH == 16 * 2 + 1); /** * Query the server for server information. */ -NetworkRecvStatus ClientNetworkGameSocketHandler::SendInformationQuery() +NetworkRecvStatus ClientNetworkGameSocketHandler::SendInformationQuery(bool request_company_info) { - my_client->status = STATUS_COMPANY_INFO; - _network_join_status = NETWORK_JOIN_STATUS_GETTING_COMPANY_INFO; - SetWindowDirty(WC_NETWORK_STATUS_WINDOW, WN_NETWORK_STATUS_WINDOW_JOIN); - + my_client->status = STATUS_GAME_INFO; my_client->SendPacket(new Packet(PACKET_CLIENT_GAME_INFO)); - my_client->SendPacket(new Packet(PACKET_CLIENT_COMPANY_INFO)); + + if (request_company_info) { + my_client->status = STATUS_COMPANY_INFO; + _network_join_status = NETWORK_JOIN_STATUS_GETTING_COMPANY_INFO; + SetWindowDirty(WC_NETWORK_STATUS_WINDOW, WN_NETWORK_STATUS_WINDOW_JOIN); + + my_client->SendPacket(new Packet(PACKET_CLIENT_COMPANY_INFO)); + } return NETWORK_RECV_STATUS_OKAY; } @@ -577,9 +581,13 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_BANNED(Packet * NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_GAME_INFO(Packet *p) { - if (this->status != STATUS_COMPANY_INFO && this->status != STATUS_INACTIVE) return NETWORK_RECV_STATUS_MALFORMED_PACKET; + if (this->status != STATUS_COMPANY_INFO && this->status != STATUS_GAME_INFO) return NETWORK_RECV_STATUS_MALFORMED_PACKET; NetworkGameList *item = GetLobbyGameInfo(); + if (item == nullptr) { + /* This is not the lobby, so add it to the game list. */ + item = NetworkGameListAddItem(this->address); + } /* Clear any existing GRFConfig chain. */ ClearGRFConfigList(&item->info.grfconfig); @@ -590,6 +598,8 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_GAME_INFO(Packe /* Ensure we consider the server online. */ item->online = true; + /* It could be either window, but only one is open, so redraw both. */ + SetWindowDirty(WC_NETWORK_WINDOW, WN_NETWORK_WINDOW_GAME); SetWindowDirty(WC_NETWORK_WINDOW, WN_NETWORK_WINDOW_LOBBY); /* We will receive company info next, so keep connection open. */ @@ -727,6 +737,15 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_ERROR(Packet *p NetworkErrorCode error = (NetworkErrorCode)p->Recv_uint8(); + /* If we query a server that is 1.11.1 or older, we get an + * NETWORK_ERROR_NOT_EXPECTED on requesting the game info. Show a special + * error popup in that case. + */ + if (error == NETWORK_ERROR_NOT_EXPECTED && (this->status == STATUS_GAME_INFO || this->status == STATUS_COMPANY_INFO)) { + ShowErrorMessage(STR_NETWORK_ERROR_SERVER_TOO_OLD, INVALID_STRING_ID, WL_CRITICAL); + return NETWORK_RECV_STATUS_CLOSE_QUERY; + } + StringID err = STR_NETWORK_ERROR_LOSTCONNECTION; if (error < (ptrdiff_t)lengthof(network_error_strings)) err = network_error_strings[error]; /* In case of kicking a client, we assume there is a kick message in the packet if we can read one byte */ diff --git a/src/network/network_client.h b/src/network/network_client.h index 66cdb8ba57..40b8eedf92 100644 --- a/src/network/network_client.h +++ b/src/network/network_client.h @@ -15,12 +15,14 @@ /** Class for handling the client side of the game connection. */ class ClientNetworkGameSocketHandler : public ZeroedMemoryAllocator, public NetworkGameSocketHandler { private: + NetworkAddress address; ///< Address we are connected to. struct PacketReader *savegame; ///< Packet reader for reading the savegame. byte token; ///< The token we need to send back to the server to prove we're the right client. /** Status of the connection with the server. */ enum ServerStatus { STATUS_INACTIVE, ///< The client is not connected nor active. + STATUS_GAME_INFO, ///< We are trying to get the game information. STATUS_COMPANY_INFO, ///< We are trying to get company information. STATUS_JOIN, ///< We are trying to join a server. STATUS_NEWGRFS_CHECK, ///< Last action was checking NewGRFs. @@ -74,13 +76,13 @@ protected: static NetworkRecvStatus SendMapOk(); void CheckConnection(); public: - ClientNetworkGameSocketHandler(SOCKET s); + ClientNetworkGameSocketHandler(SOCKET s, NetworkAddress address); ~ClientNetworkGameSocketHandler(); NetworkRecvStatus CloseConnection(NetworkRecvStatus status) override; void ClientError(NetworkRecvStatus res); - static NetworkRecvStatus SendInformationQuery(); + static NetworkRecvStatus SendInformationQuery(bool request_company_info); static NetworkRecvStatus SendJoin(); static NetworkRecvStatus SendCommand(const CommandPacket *cp); diff --git a/src/network/network_gamelist.cpp b/src/network/network_gamelist.cpp index 6d4285c854..12dbbce615 100644 --- a/src/network/network_gamelist.cpp +++ b/src/network/network_gamelist.cpp @@ -69,15 +69,6 @@ static void NetworkGameListHandleDelayedInsert() */ NetworkGameList *NetworkGameListAddItem(NetworkAddress address) { - const char *hostname = address.GetHostname(); - - /* Do not query the 'any' address. */ - if (StrEmpty(hostname) || - strcmp(hostname, "0.0.0.0") == 0 || - strcmp(hostname, "::") == 0) { - return nullptr; - } - NetworkGameList *item, *prev_item; prev_item = nullptr; @@ -95,7 +86,6 @@ NetworkGameList *NetworkGameListAddItem(NetworkAddress address) } else { prev_item->next = item; } - DEBUG(net, 4, "[gamelist] added server to list"); UpdateNetworkGameWindow(); diff --git a/src/network/network_gui.cpp b/src/network/network_gui.cpp index 3204b2b962..590f2f5e09 100644 --- a/src/network/network_gui.cpp +++ b/src/network/network_gui.cpp @@ -472,9 +472,8 @@ public: EM_ASM(if (window["openttd_server_list"]) openttd_server_list()); #endif - this->last_joined = NetworkGameListAddItem(ParseConnectionString(_settings_client.network.last_joined, NETWORK_DEFAULT_PORT)); + this->last_joined = NetworkAddServer(_settings_client.network.last_joined); this->server = this->last_joined; - if (this->last_joined != nullptr) NetworkUDPQueryServer(this->last_joined->address); this->requery_timer.SetInterval(MILLISECONDS_PER_TICK); @@ -750,7 +749,7 @@ public: break; case WID_NG_REFRESH: // Refresh - if (this->server != nullptr) NetworkUDPQueryServer(this->server->address); + if (this->server != nullptr) NetworkTCPQueryServer(this->server->address); break; case WID_NG_NEWGRF: // NewGRF Settings @@ -971,7 +970,7 @@ void ShowNetworkGameWindow() first = false; /* Add all servers from the config file to our list. */ for (const auto &iter : _network_host_list) { - NetworkAddServer(iter.c_str()); + NetworkAddServer(iter); } } @@ -1485,7 +1484,7 @@ struct NetworkLobbyWindow : public Window { /* Clear the information so removed companies don't remain */ for (auto &company : this->company_info) company = {}; - NetworkTCPQueryServer(this->server->address); + NetworkTCPQueryServer(this->server->address, true); break; } } @@ -1555,7 +1554,7 @@ static void ShowNetworkLobbyWindow(NetworkGameList *ngl) strecpy(_settings_client.network.last_joined, ngl->address.GetAddressAsString(false).c_str(), lastof(_settings_client.network.last_joined)); - NetworkTCPQueryServer(ngl->address); + NetworkTCPQueryServer(ngl->address, true); new NetworkLobbyWindow(&_network_lobby_window_desc, ngl); } diff --git a/src/network/network_internal.h b/src/network/network_internal.h index ff3c8c22d2..683c954e82 100644 --- a/src/network/network_internal.h +++ b/src/network/network_internal.h @@ -87,10 +87,10 @@ extern uint8 _network_reconnect; extern CompanyMask _network_company_passworded; -void NetworkTCPQueryServer(NetworkAddress address); +void NetworkTCPQueryServer(NetworkAddress address, bool request_company_info = false); void GetBindAddresses(NetworkAddressList *addresses, uint16 port); -void NetworkAddServer(const char *b); +struct NetworkGameList *NetworkAddServer(const std::string &connection_string); void NetworkRebuildHostList(); void UpdateNetworkGameWindow(); From e162aff7a3d1dfcde1a9d02780235c617b6d4983 Mon Sep 17 00:00:00 2001 From: Patric Stout Date: Fri, 30 Apr 2021 12:05:54 +0200 Subject: [PATCH 21/81] Cleanup: remove weird left-over comment in yapf.hpp --- src/pathfinder/yapf/yapf.hpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/pathfinder/yapf/yapf.hpp b/src/pathfinder/yapf/yapf.hpp index 134c0dcf98..097aaf0f3a 100644 --- a/src/pathfinder/yapf/yapf.hpp +++ b/src/pathfinder/yapf/yapf.hpp @@ -15,9 +15,6 @@ #include "../pf_performance_timer.hpp" #include "yapf.h" -//#undef FORCEINLINE -//#define inline inline - #include "../../misc/fixedsizearray.hpp" #include "../../misc/array.hpp" #include "../../misc/hashtable.hpp" From 665a3928e2f07e6ee1faed1c8b08fe3a68fbc37a Mon Sep 17 00:00:00 2001 From: Patric Stout Date: Fri, 30 Apr 2021 11:57:37 +0200 Subject: [PATCH 22/81] Remove: performance measurements in YAPF YAPF was constantly measuring its performance, but only at certain debug-levels this information was shown. Now after years, I sincerely wonder if anyone still knows about this feature and who still use it. Especially with the new framerate window, this detailed performance is not as meaningful anymore as it once was. --- src/pathfinder/CMakeLists.txt | 1 - src/pathfinder/follow_track.hpp | 18 +++--- src/pathfinder/npf/npf.cpp | 1 + src/pathfinder/pf_performance_timer.hpp | 80 ------------------------- src/pathfinder/yapf/yapf.hpp | 1 - src/pathfinder/yapf/yapf_base.hpp | 40 ++++--------- src/pathfinder/yapf/yapf_costcache.hpp | 8 --- src/pathfinder/yapf/yapf_costrail.hpp | 8 +-- src/pathfinder/yapf/yapf_rail.cpp | 2 - 9 files changed, 21 insertions(+), 138 deletions(-) delete mode 100644 src/pathfinder/pf_performance_timer.hpp diff --git a/src/pathfinder/CMakeLists.txt b/src/pathfinder/CMakeLists.txt index 2e275706f2..0616371622 100644 --- a/src/pathfinder/CMakeLists.txt +++ b/src/pathfinder/CMakeLists.txt @@ -5,5 +5,4 @@ add_files( follow_track.hpp pathfinder_func.h pathfinder_type.h - pf_performance_timer.hpp ) diff --git a/src/pathfinder/follow_track.hpp b/src/pathfinder/follow_track.hpp index 9b95578fd5..7e5e0e39b7 100644 --- a/src/pathfinder/follow_track.hpp +++ b/src/pathfinder/follow_track.hpp @@ -18,7 +18,6 @@ #include "../tunnelbridge_map.h" #include "../depot_map.h" #include "pathfinder_func.h" -#include "pf_performance_timer.hpp" /** * Track follower helper template class (can serve pathfinders and vehicle @@ -49,34 +48,32 @@ struct CFollowTrackT bool m_is_station; ///< last turn passed station int m_tiles_skipped; ///< number of skipped tunnel or station tiles ErrorCode m_err; - CPerformanceTimer *m_pPerf; RailTypes m_railtypes; - inline CFollowTrackT(const VehicleType *v = nullptr, RailTypes railtype_override = INVALID_RAILTYPES, CPerformanceTimer *pPerf = nullptr) + inline CFollowTrackT(const VehicleType *v = nullptr, RailTypes railtype_override = INVALID_RAILTYPES) { - Init(v, railtype_override, pPerf); + Init(v, railtype_override); } - inline CFollowTrackT(Owner o, RailTypes railtype_override = INVALID_RAILTYPES, CPerformanceTimer *pPerf = nullptr) + inline CFollowTrackT(Owner o, RailTypes railtype_override = INVALID_RAILTYPES) { assert(IsRailTT()); m_veh = nullptr; - Init(o, railtype_override, pPerf); + Init(o, railtype_override); } - inline void Init(const VehicleType *v, RailTypes railtype_override, CPerformanceTimer *pPerf) + inline void Init(const VehicleType *v, RailTypes railtype_override) { assert(!IsRailTT() || (v != nullptr && v->type == VEH_TRAIN)); m_veh = v; - Init(v != nullptr ? v->owner : INVALID_OWNER, IsRailTT() && railtype_override == INVALID_RAILTYPES ? Train::From(v)->compatible_railtypes : railtype_override, pPerf); + Init(v != nullptr ? v->owner : INVALID_OWNER, IsRailTT() && railtype_override == INVALID_RAILTYPES ? Train::From(v)->compatible_railtypes : railtype_override); } - inline void Init(Owner o, RailTypes railtype_override, CPerformanceTimer *pPerf) + inline void Init(Owner o, RailTypes railtype_override) { assert(!IsRoadTT() || m_veh != nullptr); assert(!IsRailTT() || railtype_override != INVALID_RAILTYPES); m_veh_owner = o; - m_pPerf = pPerf; /* don't worry, all is inlined so compiler should remove unnecessary initializations */ m_old_tile = INVALID_TILE; m_old_td = INVALID_TRACKDIR; @@ -237,7 +234,6 @@ protected: /** stores track status (available trackdirs) for the new tile into m_new_td_bits */ inline bool QueryNewTileTrackStatus() { - CPerfStart perf(*m_pPerf); if (IsRailTT() && IsPlainRailTile(m_new_tile)) { m_new_td_bits = (TrackdirBits)(GetTrackBits(m_new_tile) * 0x101); } else if (IsRoadTT()) { diff --git a/src/pathfinder/npf/npf.cpp b/src/pathfinder/npf/npf.cpp index 315b43b0e7..0094521615 100644 --- a/src/pathfinder/npf/npf.cpp +++ b/src/pathfinder/npf/npf.cpp @@ -8,6 +8,7 @@ /** @file npf.cpp Implementation of the NPF pathfinder. */ #include "../../stdafx.h" +#include "../../debug.h" #include "../../network/network.h" #include "../../viewport_func.h" #include "../../ship.h" diff --git a/src/pathfinder/pf_performance_timer.hpp b/src/pathfinder/pf_performance_timer.hpp deleted file mode 100644 index 66ec9695f8..0000000000 --- a/src/pathfinder/pf_performance_timer.hpp +++ /dev/null @@ -1,80 +0,0 @@ -/* - * 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 pf_performance_timer.hpp Performance timer for pathfinders. */ - -#ifndef PF_PERFORMANCE_TIMER_HPP -#define PF_PERFORMANCE_TIMER_HPP - -#include "../debug.h" - -struct CPerformanceTimer -{ - int64 m_start; - int64 m_acc; - - CPerformanceTimer() : m_start(0), m_acc(0) {} - - inline void Start() - { - m_start = QueryTime(); - } - - inline void Stop() - { - m_acc += QueryTime() - m_start; - } - - inline int Get(int64 coef) - { - return (int)(m_acc * coef / QueryFrequency()); - } - - inline int64 QueryTime() - { - return ottd_rdtsc(); - } - - inline int64 QueryFrequency() - { - return ((int64)2200 * 1000000); - } -}; - -struct CPerfStartReal -{ - CPerformanceTimer *m_pperf; - - inline CPerfStartReal(CPerformanceTimer& perf) : m_pperf(&perf) - { - if (m_pperf != nullptr) m_pperf->Start(); - } - - inline ~CPerfStartReal() - { - Stop(); - } - - inline void Stop() - { - if (m_pperf != nullptr) { - m_pperf->Stop(); - m_pperf = nullptr; - } - } -}; - -struct CPerfStartFake -{ - inline CPerfStartFake(CPerformanceTimer& perf) {} - inline ~CPerfStartFake() {} - inline void Stop() {} -}; - -typedef CPerfStartFake CPerfStart; - -#endif /* PF_PERFORMANCE_TIMER_HPP */ diff --git a/src/pathfinder/yapf/yapf.hpp b/src/pathfinder/yapf/yapf.hpp index 097aaf0f3a..36df4db951 100644 --- a/src/pathfinder/yapf/yapf.hpp +++ b/src/pathfinder/yapf/yapf.hpp @@ -12,7 +12,6 @@ #include "../../landscape.h" #include "../pathfinder_func.h" -#include "../pf_performance_timer.hpp" #include "yapf.h" #include "../../misc/fixedsizearray.hpp" diff --git a/src/pathfinder/yapf/yapf_base.hpp b/src/pathfinder/yapf/yapf_base.hpp index c04fa2af92..b34e40a673 100644 --- a/src/pathfinder/yapf/yapf_base.hpp +++ b/src/pathfinder/yapf/yapf_base.hpp @@ -13,8 +13,6 @@ #include "../../debug.h" #include "../../settings_type.h" -extern int _total_pf_time_us; - /** * CYapfBaseT - A-star type path finder base class. * Derive your own pathfinder from it. You must provide the following template argument: @@ -67,12 +65,6 @@ protected: int m_stats_cost_calcs; ///< stats - how many node's costs were calculated int m_stats_cache_hits; ///< stats - how many node's costs were reused from cache -public: - CPerformanceTimer m_perf_cost; ///< stats - total CPU time of this run - CPerformanceTimer m_perf_slope_cost; ///< stats - slope calculation CPU time - CPerformanceTimer m_perf_ts_cost; ///< stats - GetTrackStatus() CPU time - CPerformanceTimer m_perf_other_cost; ///< stats - other CPU time - public: int m_num_steps; ///< this is there for debugging purposes (hope it doesn't hurt) @@ -120,9 +112,6 @@ public: { m_veh = v; - CPerformanceTimer perf; - perf.Start(); - Yapf().PfSetStartupNodes(); bool bDestFound = true; @@ -150,25 +139,18 @@ public: bDestFound &= (m_pBestDestNode != nullptr); - perf.Stop(); - if (_debug_yapf_level >= 2) { - int t = perf.Get(1000000); - _total_pf_time_us += t; - - if (_debug_yapf_level >= 3) { - UnitID veh_idx = (m_veh != nullptr) ? m_veh->unitnumber : 0; - char ttc = Yapf().TransportTypeChar(); - float cache_hit_ratio = (m_stats_cache_hits == 0) ? 0.0f : ((float)m_stats_cache_hits / (float)(m_stats_cache_hits + m_stats_cost_calcs) * 100.0f); - int cost = bDestFound ? m_pBestDestNode->m_cost : -1; - int dist = bDestFound ? m_pBestDestNode->m_estimate - m_pBestDestNode->m_cost : -1; - - DEBUG(yapf, 3, "[YAPF%c]%c%4d- %d us - %d rounds - %d open - %d closed - CHR %4.1f%% - C %d D %d - c%d(sc%d, ts%d, o%d) -- ", - ttc, bDestFound ? '-' : '!', veh_idx, t, m_num_steps, m_nodes.OpenCount(), m_nodes.ClosedCount(), - cache_hit_ratio, cost, dist, m_perf_cost.Get(1000000), m_perf_slope_cost.Get(1000000), - m_perf_ts_cost.Get(1000000), m_perf_other_cost.Get(1000000) - ); - } + if (_debug_yapf_level >= 3) { + UnitID veh_idx = (m_veh != nullptr) ? m_veh->unitnumber : 0; + char ttc = Yapf().TransportTypeChar(); + float cache_hit_ratio = (m_stats_cache_hits == 0) ? 0.0f : ((float)m_stats_cache_hits / (float)(m_stats_cache_hits + m_stats_cost_calcs) * 100.0f); + int cost = bDestFound ? m_pBestDestNode->m_cost : -1; + int dist = bDestFound ? m_pBestDestNode->m_estimate - m_pBestDestNode->m_cost : -1; + + DEBUG(yapf, 3, "[YAPF%c]%c%4d- %d rounds - %d open - %d closed - CHR %4.1f%% - C %d D %d", + ttc, bDestFound ? '-' : '!', veh_idx, m_num_steps, m_nodes.OpenCount(), m_nodes.ClosedCount(), cache_hit_ratio, cost, dist + ); } + return bDestFound; } diff --git a/src/pathfinder/yapf/yapf_costcache.hpp b/src/pathfinder/yapf/yapf_costcache.hpp index c56c47b5f2..1b4d52cff2 100644 --- a/src/pathfinder/yapf/yapf_costcache.hpp +++ b/src/pathfinder/yapf/yapf_costcache.hpp @@ -182,16 +182,8 @@ protected: inline static Cache& stGetGlobalCache() { static int last_rail_change_counter = 0; - static Date last_date = 0; static Cache C; - /* some statistics */ - if (last_date != _date) { - last_date = _date; - DEBUG(yapf, 2, "Pf time today: %5d ms", _total_pf_time_us / 1000); - _total_pf_time_us = 0; - } - /* delete the cache sometimes... */ if (last_rail_change_counter != Cache::s_rail_change_counter) { last_rail_change_counter = Cache::s_rail_change_counter; diff --git a/src/pathfinder/yapf/yapf_costrail.hpp b/src/pathfinder/yapf/yapf_costrail.hpp index 6bed27cba6..b50300b618 100644 --- a/src/pathfinder/yapf/yapf_costrail.hpp +++ b/src/pathfinder/yapf/yapf_costrail.hpp @@ -86,7 +86,6 @@ protected: public: inline int SlopeCost(TileIndex tile, Trackdir td) { - CPerfStart perf_cost(Yapf().m_perf_slope_cost); if (!stSlopeCost(tile, td)) return 0; return Yapf().PfGetSettings().rail_slope_penalty; } @@ -172,7 +171,6 @@ public: { int cost = 0; /* if there is one-way signal in the opposite direction, then it is not our way */ - CPerfStart perf_cost(Yapf().m_perf_other_cost); if (IsTileType(tile, MP_RAILWAY)) { bool has_signal_against = HasSignalOnTrackdir(tile, ReverseTrackdir(trackdir)); bool has_signal_along = HasSignalOnTrackdir(tile, trackdir); @@ -275,8 +273,6 @@ public: assert(tf->m_new_tile == n.m_key.m_tile); assert((HasTrackdir(tf->m_new_td_bits, n.m_key.m_td))); - CPerfStart perf_cost(Yapf().m_perf_cost); - /* Does the node have some parent node? */ bool has_parent = (n.m_parent != nullptr); @@ -326,7 +322,7 @@ public: EndSegmentReasonBits end_segment_reason = ESRB_NONE; - TrackFollower tf_local(v, Yapf().GetCompatibleRailTypes(), &Yapf().m_perf_ts_cost); + TrackFollower tf_local(v, Yapf().GetCompatibleRailTypes()); if (!has_parent) { /* We will jump to the middle of the cost calculator assuming that segment cache is not used. */ @@ -484,7 +480,7 @@ no_entry_cost: // jump here at the beginning if the node has no parent (it is th /* Move to the next tile/trackdir. */ tf = &tf_local; - tf_local.Init(v, Yapf().GetCompatibleRailTypes(), &Yapf().m_perf_ts_cost); + tf_local.Init(v, Yapf().GetCompatibleRailTypes()); if (!tf_local.Follow(cur.tile, cur.td)) { assert(tf_local.m_err != TrackFollower::EC_NONE); diff --git a/src/pathfinder/yapf/yapf_rail.cpp b/src/pathfinder/yapf/yapf_rail.cpp index 3b6686f20d..41aa5fdbd8 100644 --- a/src/pathfinder/yapf/yapf_rail.cpp +++ b/src/pathfinder/yapf/yapf_rail.cpp @@ -34,8 +34,6 @@ template void DumpState(Tpf &pf1, Tpf &pf2) fclose(f2); } -int _total_pf_time_us = 0; - template class CYapfReserveTrack { From 3dbd6475fec5ebd63b18004bb94048460c4fb4a3 Mon Sep 17 00:00:00 2001 From: PeterN Date: Fri, 30 Apr 2021 12:03:07 +0100 Subject: [PATCH 23/81] Codechange: Use C++ features for train wagon overrides. (#9141) This removes the need for C-style array management and allows use of iterators to perform wagon override lookups. --- src/engine.cpp | 11 ----------- src/engine_base.h | 12 ++++++++---- src/newgrf_engine.cpp | 43 +++++-------------------------------------- src/newgrf_engine.h | 2 -- 4 files changed, 13 insertions(+), 55 deletions(-) diff --git a/src/engine.cpp b/src/engine.cpp index 60b0d42224..41cd9dba80 100644 --- a/src/engine.cpp +++ b/src/engine.cpp @@ -66,12 +66,6 @@ static_assert(lengthof(_orig_rail_vehicle_info) + lengthof(_orig_road_vehicle_in const uint EngineOverrideManager::NUM_DEFAULT_ENGINES = _engine_counts[VEH_TRAIN] + _engine_counts[VEH_ROAD] + _engine_counts[VEH_SHIP] + _engine_counts[VEH_AIRCRAFT]; -Engine::Engine() : - overrides_count(0), - overrides(nullptr) -{ -} - Engine::Engine(VehicleType type, EngineID base) { this->type = type; @@ -136,11 +130,6 @@ Engine::Engine(VehicleType type, EngineID base) } } -Engine::~Engine() -{ - UnloadWagonOverrides(this); -} - /** * Checks whether the engine is a valid (non-articulated part of an) engine. * @return true if enabled diff --git a/src/engine_base.h b/src/engine_base.h index 2e434b589e..7822111e77 100644 --- a/src/engine_base.h +++ b/src/engine_base.h @@ -15,6 +15,12 @@ #include "core/pool_type.hpp" #include "newgrf_commons.h" +struct WagonOverride { + std::vector engines; + CargoID cargo; + const SpriteGroup *group; +}; + typedef Pool EnginePool; extern EnginePool _engine_pool; @@ -56,13 +62,11 @@ struct Engine : EnginePool::PoolItem<&_engine_pool> { * evaluating callbacks. */ GRFFilePropsBase grf_prop; - uint16 overrides_count; - struct WagonOverride *overrides; + std::vector overrides; uint16 list_position; - Engine(); + Engine() {} Engine(VehicleType type, EngineID base); - ~Engine(); bool IsEnabled() const; /** diff --git a/src/newgrf_engine.cpp b/src/newgrf_engine.cpp index ee14098c28..cd12148558 100644 --- a/src/newgrf_engine.cpp +++ b/src/newgrf_engine.cpp @@ -26,62 +26,29 @@ #include "safeguards.h" -struct WagonOverride { - EngineID *train_id; - uint trains; - CargoID cargo; - const SpriteGroup *group; -}; - void SetWagonOverrideSprites(EngineID engine, CargoID cargo, const SpriteGroup *group, EngineID *train_id, uint trains) { Engine *e = Engine::Get(engine); - WagonOverride *wo; assert(cargo < NUM_CARGO + 2); // Include CT_DEFAULT and CT_PURCHASE pseudo cargoes. - e->overrides_count++; - e->overrides = ReallocT(e->overrides, e->overrides_count); - - wo = &e->overrides[e->overrides_count - 1]; + WagonOverride *wo = &e->overrides.emplace_back(); wo->group = group; wo->cargo = cargo; - wo->trains = trains; - wo->train_id = MallocT(trains); - memcpy(wo->train_id, train_id, trains * sizeof *train_id); + wo->engines.assign(train_id, train_id + trains); } const SpriteGroup *GetWagonOverrideSpriteSet(EngineID engine, CargoID cargo, EngineID overriding_engine) { const Engine *e = Engine::Get(engine); - for (uint i = 0; i < e->overrides_count; i++) { - const WagonOverride *wo = &e->overrides[i]; - - if (wo->cargo != cargo && wo->cargo != CT_DEFAULT) continue; - - for (uint j = 0; j < wo->trains; j++) { - if (wo->train_id[j] == overriding_engine) return wo->group; - } + for (const WagonOverride &wo : e->overrides) { + if (wo.cargo != cargo && wo.cargo != CT_DEFAULT) continue; + if (std::find(wo.engines.begin(), wo.engines.end(), overriding_engine) != wo.engines.end()) return wo.group; } return nullptr; } -/** - * Unload all wagon override sprite groups. - */ -void UnloadWagonOverrides(Engine *e) -{ - for (uint i = 0; i < e->overrides_count; i++) { - WagonOverride *wo = &e->overrides[i]; - free(wo->train_id); - } - free(e->overrides); - e->overrides_count = 0; - e->overrides = nullptr; -} - - void SetCustomEngineSprites(EngineID engine, byte cargo, const SpriteGroup *group) { Engine *e = Engine::Get(engine); diff --git a/src/newgrf_engine.h b/src/newgrf_engine.h index f830ff499d..90d755d3c4 100644 --- a/src/newgrf_engine.h +++ b/src/newgrf_engine.h @@ -116,8 +116,6 @@ enum VehicleTrigger { }; void TriggerVehicle(Vehicle *veh, VehicleTrigger trigger); -void UnloadWagonOverrides(Engine *e); - void AlterVehicleListOrder(EngineID engine, uint target); void CommitVehicleListOrderChanges(); From 5153e1b6e38a75ef6eeb71760941018139feedea Mon Sep 17 00:00:00 2001 From: Peter Nelson Date: Thu, 22 Apr 2021 01:18:22 +0100 Subject: [PATCH 24/81] Cleanup: Horizontal widget size is commonly width rather than length. --- src/widget.cpp | 6 +++--- src/widget_type.h | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/widget.cpp b/src/widget.cpp index 1e3afb1557..74a89eaeaf 100644 --- a/src/widget.cpp +++ b/src/widget.cpp @@ -1503,12 +1503,12 @@ void NWidgetVertical::AssignSizePosition(SizingType sizing, uint x, uint y, uint /** * Generic spacer widget. - * @param length Horizontal size of the spacer widget. + * @param width Horizontal size of the spacer widget. * @param height Vertical size of the spacer widget. */ -NWidgetSpacer::NWidgetSpacer(int length, int height) : NWidgetResizeBase(NWID_SPACER, 0, 0) +NWidgetSpacer::NWidgetSpacer(int width, int height) : NWidgetResizeBase(NWID_SPACER, 0, 0) { - this->SetMinimalSize(length, height); + this->SetMinimalSize(width, height); this->SetResize(0, 0); } diff --git a/src/widget_type.h b/src/widget_type.h index e1fd92f638..c4c46ce66c 100644 --- a/src/widget_type.h +++ b/src/widget_type.h @@ -543,7 +543,7 @@ private: */ class NWidgetSpacer : public NWidgetResizeBase { public: - NWidgetSpacer(int length, int height); + NWidgetSpacer(int width, int height); void SetupSmallestSize(Window *w, bool init_array) override; void FillNestedArray(NWidgetBase **array, uint length) override; From 4c6cca459a678a5f1970b3ee5f23141d7ed89528 Mon Sep 17 00:00:00 2001 From: Peter Nelson Date: Thu, 22 Apr 2021 01:35:51 +0100 Subject: [PATCH 25/81] Cleanup: Remove fairly redundant DrawDropdown() function. --- src/widget.cpp | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/src/widget.cpp b/src/widget.cpp index 74a89eaeaf..78da9457cc 100644 --- a/src/widget.cpp +++ b/src/widget.cpp @@ -623,19 +623,6 @@ static inline void DrawButtonDropdown(const Rect &r, Colours colour, bool clicke } } -/** - * Draw a dropdown #WWT_DROPDOWN widget. - * @param r Rectangle containing the widget. - * @param colour Background colour of the widget. - * @param clicked The widget is lowered. - * @param str Text of the button. - * @param align Alignment of the text. - */ -static inline void DrawDropdown(const Rect &r, Colours colour, bool clicked, StringID str, StringAlignment align) -{ - DrawButtonDropdown(r, colour, false, clicked, str, align); -} - /** * Paint all widgets of a window. */ @@ -2639,7 +2626,7 @@ void NWidgetLeaf::Draw(const Window *w) case WWT_DROPDOWN: if (this->index >= 0) w->SetStringParameters(this->index); - DrawDropdown(r, this->colour, clicked, this->widget_data, this->align); + DrawButtonDropdown(r, this->colour, false, clicked, this->widget_data, this->align); break; case NWID_BUTTON_DROPDOWN: From 4f93dd95e4251e3227a55ee125838cefce173ef3 Mon Sep 17 00:00:00 2001 From: Peter Nelson Date: Wed, 21 Apr 2021 23:55:16 +0100 Subject: [PATCH 26/81] Cleanup: Tidy up resize, fill and minimal size on widgets in town list window. --- src/town_gui.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/town_gui.cpp b/src/town_gui.cpp index 1a7085c964..0629490393 100644 --- a/src/town_gui.cpp +++ b/src/town_gui.cpp @@ -647,12 +647,12 @@ static const NWidgetPart _nested_town_directory_widgets[] = { NWidget(NWID_HORIZONTAL), NWidget(WWT_TEXTBTN, COLOUR_BROWN, WID_TD_SORT_ORDER), SetDataTip(STR_BUTTON_SORT_BY, STR_TOOLTIP_SORT_ORDER), NWidget(WWT_DROPDOWN, COLOUR_BROWN, WID_TD_SORT_CRITERIA), SetDataTip(STR_JUST_STRING, STR_TOOLTIP_SORT_CRITERIA), - NWidget(WWT_EDITBOX, COLOUR_BROWN, WID_TD_FILTER), SetFill(35, 12), SetDataTip(STR_LIST_FILTER_OSKTITLE, STR_LIST_FILTER_TOOLTIP), + NWidget(WWT_EDITBOX, COLOUR_BROWN, WID_TD_FILTER), SetFill(1, 0), SetDataTip(STR_LIST_FILTER_OSKTITLE, STR_LIST_FILTER_TOOLTIP), EndContainer(), NWidget(WWT_PANEL, COLOUR_BROWN, WID_TD_LIST), SetMinimalSize(196, 0), SetDataTip(0x0, STR_TOWN_DIRECTORY_LIST_TOOLTIP), - SetFill(1, 0), SetResize(0, 10), SetScrollbar(WID_TD_SCROLLBAR), EndContainer(), + SetFill(1, 0), SetResize(0, 1), SetScrollbar(WID_TD_SCROLLBAR), EndContainer(), NWidget(WWT_PANEL, COLOUR_BROWN), - NWidget(WWT_TEXT, COLOUR_BROWN, WID_TD_WORLD_POPULATION), SetPadding(2, 0, 0, 2), SetMinimalSize(196, 12), SetFill(1, 0), SetDataTip(STR_TOWN_POPULATION, STR_NULL), + NWidget(WWT_TEXT, COLOUR_BROWN, WID_TD_WORLD_POPULATION), SetPadding(2, 0, 2, 2), SetMinimalSize(196, 0), SetMinimalTextLines(1, 0), SetFill(1, 0), SetDataTip(STR_TOWN_POPULATION, STR_NULL), EndContainer(), EndContainer(), NWidget(NWID_VERTICAL), From 2efa390a7d2091b2c2cef4aa7f986fdcb24eac2a Mon Sep 17 00:00:00 2001 From: Peter Nelson Date: Thu, 22 Apr 2021 01:17:30 +0100 Subject: [PATCH 27/81] Codechange: Simplify calling of DrawCharCentered() --- src/gfx.cpp | 10 ++++++---- src/gfx_func.h | 2 +- src/osk_gui.cpp | 5 +---- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/src/gfx.cpp b/src/gfx.cpp index d540f4dfe0..d3c730be12 100644 --- a/src/gfx.cpp +++ b/src/gfx.cpp @@ -891,15 +891,17 @@ const char *GetCharAtPosition(const char *str, int x, FontSize start_fontsize) /** * Draw single character horizontally centered around (x,y) * @param c Character (glyph) to draw - * @param x X position to draw character - * @param y Y position to draw character + * @param r Rectangle to draw character within * @param colour Colour to use, for details see _string_colourmap in * table/palettes.h or docs/ottd-colourtext-palette.png or the enum TextColour in gfx_type.h */ -void DrawCharCentered(WChar c, int x, int y, TextColour colour) +void DrawCharCentered(WChar c, const Rect &r, TextColour colour) { SetColourRemap(colour); - GfxMainBlitter(GetGlyph(FS_NORMAL, c), x - GetCharacterWidth(FS_NORMAL, c) / 2, y, BM_COLOUR_REMAP); + GfxMainBlitter(GetGlyph(FS_NORMAL, c), + CenterBounds(r.left, r.right, GetCharacterWidth(FS_NORMAL, c)), + CenterBounds(r.top, r.bottom, FONT_HEIGHT_NORMAL), + BM_COLOUR_REMAP); } /** diff --git a/src/gfx_func.h b/src/gfx_func.h index a4db8a045a..18b1966f9b 100644 --- a/src/gfx_func.h +++ b/src/gfx_func.h @@ -96,7 +96,7 @@ int DrawString(int left, int right, int top, StringID str, TextColour colour = T int DrawStringMultiLine(int left, int right, int top, int bottom, const char *str, TextColour colour = TC_FROMSTRING, StringAlignment align = (SA_TOP | SA_LEFT), bool underline = false, FontSize fontsize = FS_NORMAL); int DrawStringMultiLine(int left, int right, int top, int bottom, StringID str, TextColour colour = TC_FROMSTRING, StringAlignment align = (SA_TOP | SA_LEFT), bool underline = false, FontSize fontsize = FS_NORMAL); -void DrawCharCentered(WChar c, int x, int y, TextColour colour); +void DrawCharCentered(WChar c, const Rect &r, TextColour colour); void GfxFillRect(int left, int top, int right, int bottom, int colour, FillRectMode mode = FILLRECT_OPAQUE); void GfxFillPolygon(const std::vector &shape, int colour, FillRectMode mode = FILLRECT_OPAQUE); diff --git a/src/osk_gui.cpp b/src/osk_gui.cpp index 60c9023b82..233bb1c7aa 100644 --- a/src/osk_gui.cpp +++ b/src/osk_gui.cpp @@ -103,10 +103,7 @@ struct OskWindow : public Window { if (widget < WID_OSK_LETTERS) return; widget -= WID_OSK_LETTERS; - DrawCharCentered(_keyboard[this->shift][widget], - r.left + (r.right - r.left) / 2, - r.top + (r.bottom - r.top - FONT_HEIGHT_NORMAL) / 2, - TC_BLACK); + DrawCharCentered(_keyboard[this->shift][widget], r, TC_BLACK); } void OnClick(Point pt, int widget, int click_count) override From f5569763c9f34a32f59257f61f9797d54ace7b21 Mon Sep 17 00:00:00 2001 From: Peter Nelson Date: Mon, 5 Apr 2021 20:28:00 +0100 Subject: [PATCH 28/81] Fix: Specify width when width is required instead of top. --- src/widget.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/widget.cpp b/src/widget.cpp index 78da9457cc..705b93a946 100644 --- a/src/widget.cpp +++ b/src/widget.cpp @@ -2280,19 +2280,19 @@ NWidgetLeaf::NWidgetLeaf(WidgetType tp, Colours colour, int index, uint32 data, case WWT_SHADEBOX: this->SetFill(0, 0); - this->SetMinimalSize(WD_SHADEBOX_TOP, WD_CAPTION_HEIGHT); + this->SetMinimalSize(WD_SHADEBOX_WIDTH, WD_CAPTION_HEIGHT); this->SetDataTip(STR_NULL, STR_TOOLTIP_SHADE); break; case WWT_DEBUGBOX: this->SetFill(0, 0); - this->SetMinimalSize(WD_DEBUGBOX_TOP, WD_CAPTION_HEIGHT); + this->SetMinimalSize(WD_DEBUGBOX_WIDTH, WD_CAPTION_HEIGHT); this->SetDataTip(STR_NULL, STR_TOOLTIP_DEBUG); break; case WWT_DEFSIZEBOX: this->SetFill(0, 0); - this->SetMinimalSize(WD_DEFSIZEBOX_TOP, WD_CAPTION_HEIGHT); + this->SetMinimalSize(WD_DEFSIZEBOX_WIDTH, WD_CAPTION_HEIGHT); this->SetDataTip(STR_NULL, STR_TOOLTIP_DEFSIZE); break; From 03a43b824711898fe2c9f401a48fe2420d0abc28 Mon Sep 17 00:00:00 2001 From: Peter Nelson Date: Thu, 8 Apr 2021 09:02:38 +0100 Subject: [PATCH 29/81] Cleanup: Call SetMinimalSize instead of setting min_y directly. --- src/widget.cpp | 5 +++-- src/widget_type.h | 12 ++++++------ 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/widget.cpp b/src/widget.cpp index 705b93a946..931b525d47 100644 --- a/src/widget.cpp +++ b/src/widget.cpp @@ -2268,7 +2268,8 @@ NWidgetLeaf::NWidgetLeaf(WidgetType tp, Colours colour, int index, uint32 data, case WWT_CAPTION: this->SetFill(1, 0); this->SetResize(1, 0); - this->min_y = WD_CAPTION_HEIGHT; + this->SetMinimalSize(0, WD_CAPTION_HEIGHT); + this->SetMinimalTextLines(1, WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM, FS_NORMAL); this->SetDataTip(data, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS); break; @@ -2310,7 +2311,7 @@ NWidgetLeaf::NWidgetLeaf(WidgetType tp, Colours colour, int index, uint32 data, case WWT_DROPDOWN: this->SetFill(0, 0); - this->min_y = WD_DROPDOWN_HEIGHT; + this->SetMinimalSize(0, WD_DROPDOWN_HEIGHT); this->SetAlignment(SA_TOP | SA_LEFT); break; diff --git a/src/widget_type.h b/src/widget_type.h index c4c46ce66c..077261ff26 100644 --- a/src/widget_type.h +++ b/src/widget_type.h @@ -251,7 +251,7 @@ public: void SetFill(uint fill_x, uint fill_y); void SetResize(uint resize_x, uint resize_y); - void AssignSizePosition(SizingType sizing, uint x, uint y, uint given_width, uint given_height, bool rtl); + void AssignSizePosition(SizingType sizing, uint x, uint y, uint given_width, uint given_height, bool rtl) override; uint min_x; ///< Minimal horizontal size of only this widget. uint min_y; ///< Minimal vertical size of only this widget. @@ -470,8 +470,8 @@ class NWidgetHorizontal : public NWidgetPIPContainer { public: NWidgetHorizontal(NWidContainerFlags flags = NC_NONE); - void SetupSmallestSize(Window *w, bool init_array); - void AssignSizePosition(SizingType sizing, uint x, uint y, uint given_width, uint given_height, bool rtl); + void SetupSmallestSize(Window *w, bool init_array) override; + void AssignSizePosition(SizingType sizing, uint x, uint y, uint given_width, uint given_height, bool rtl) override; }; /** @@ -482,7 +482,7 @@ class NWidgetHorizontalLTR : public NWidgetHorizontal { public: NWidgetHorizontalLTR(NWidContainerFlags flags = NC_NONE); - void AssignSizePosition(SizingType sizing, uint x, uint y, uint given_width, uint given_height, bool rtl); + void AssignSizePosition(SizingType sizing, uint x, uint y, uint given_width, uint given_height, bool rtl) override; }; /** @@ -493,8 +493,8 @@ class NWidgetVertical : public NWidgetPIPContainer { public: NWidgetVertical(NWidContainerFlags flags = NC_NONE); - void SetupSmallestSize(Window *w, bool init_array); - void AssignSizePosition(SizingType sizing, uint x, uint y, uint given_width, uint given_height, bool rtl); + void SetupSmallestSize(Window *w, bool init_array) override; + void AssignSizePosition(SizingType sizing, uint x, uint y, uint given_width, uint given_height, bool rtl) override; }; /** From 6fe5353da2a0d3d60f8375b28536213e67cd869c Mon Sep 17 00:00:00 2001 From: Peter Nelson Date: Thu, 22 Apr 2021 02:13:55 +0100 Subject: [PATCH 30/81] Cleanup: Set unchanging scrollbar properties in constructor. --- src/widget.cpp | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/src/widget.cpp b/src/widget.cpp index 931b525d47..aae1a51651 100644 --- a/src/widget.cpp +++ b/src/widget.cpp @@ -2108,6 +2108,22 @@ NWidgetScrollbar::NWidgetScrollbar(WidgetType tp, Colours colour, int index) : N { assert(tp == NWID_HSCROLLBAR || tp == NWID_VSCROLLBAR); this->SetIndex(index); + + switch (this->type) { + case NWID_HSCROLLBAR: + this->SetResize(1, 0); + this->SetFill(1, 0); + this->SetDataTip(0x0, STR_TOOLTIP_HSCROLL_BAR_SCROLLS_LIST); + break; + + case NWID_VSCROLLBAR: + this->SetResize(0, 1); + this->SetFill(0, 1); + this->SetDataTip(0x0, STR_TOOLTIP_VSCROLL_BAR_SCROLLS_LIST); + break; + + default: NOT_REACHED(); + } } void NWidgetScrollbar::SetupSmallestSize(Window *w, bool init_array) @@ -2122,16 +2138,10 @@ void NWidgetScrollbar::SetupSmallestSize(Window *w, bool init_array) switch (this->type) { case NWID_HSCROLLBAR: this->SetMinimalSize(NWidgetScrollbar::GetHorizontalDimension().width * 3, NWidgetScrollbar::GetHorizontalDimension().height); - this->SetResize(1, 0); - this->SetFill(1, 0); - this->SetDataTip(0x0, STR_TOOLTIP_HSCROLL_BAR_SCROLLS_LIST); break; case NWID_VSCROLLBAR: this->SetMinimalSize(NWidgetScrollbar::GetVerticalDimension().width, NWidgetScrollbar::GetVerticalDimension().height * 3); - this->SetResize(0, 1); - this->SetFill(0, 1); - this->SetDataTip(0x0, STR_TOOLTIP_VSCROLL_BAR_SCROLLS_LIST); break; default: NOT_REACHED(); From 4791ff28627eabe6322352428dc1b42344f545b9 Mon Sep 17 00:00:00 2001 From: Peter Nelson Date: Mon, 5 Apr 2021 18:43:12 +0100 Subject: [PATCH 31/81] Fix: Recalculate padding and minimum sizes when GUI or Font zoom is changed. --- src/gfx.cpp | 2 +- src/gfxinit.cpp | 2 +- src/network/network_gui.cpp | 4 +- src/newgrf_gui.cpp | 2 +- src/osk_gui.cpp | 2 +- src/settings.cpp | 2 +- src/settings_gui.cpp | 7 +-- src/video/video_driver.cpp | 2 +- src/widget.cpp | 90 ++++++++++++++++++++++++++++++++----- src/widget_type.h | 33 ++++++++++++-- src/widgets/dropdown.cpp | 2 +- src/window.cpp | 3 +- src/window_func.h | 6 ++- 13 files changed, 129 insertions(+), 28 deletions(-) diff --git a/src/gfx.cpp b/src/gfx.cpp index d3c730be12..1f4b40a54c 100644 --- a/src/gfx.cpp +++ b/src/gfx.cpp @@ -1285,7 +1285,7 @@ void LoadStringWidthTable(bool monospace) } } - ReInitAllWindows(); + ReInitAllWindows(false); } /** diff --git a/src/gfxinit.cpp b/src/gfxinit.cpp index 7e2c2cc625..b744eaa5ef 100644 --- a/src/gfxinit.cpp +++ b/src/gfxinit.cpp @@ -323,7 +323,7 @@ void CheckBlitter() ClearFontCache(); GfxClearSpriteCache(); - ReInitAllWindows(); + ReInitAllWindows(false); } /** Initialise and load all the sprites. */ diff --git a/src/network/network_gui.cpp b/src/network/network_gui.cpp index 590f2f5e09..b2feacc955 100644 --- a/src/network/network_gui.cpp +++ b/src/network/network_gui.cpp @@ -98,7 +98,9 @@ public: this->Add(new NWidgetLeaf(WWT_PUSHTXTBTN, COLOUR_WHITE, WID_NG_YEARS, STR_NETWORK_SERVER_LIST_YEARS_CAPTION, STR_NETWORK_SERVER_LIST_YEARS_CAPTION_TOOLTIP)); leaf = new NWidgetLeaf(WWT_PUSHTXTBTN, COLOUR_WHITE, WID_NG_INFO, STR_EMPTY, STR_NETWORK_SERVER_LIST_INFO_ICONS_TOOLTIP); - leaf->SetMinimalSize(14 + GetSpriteSize(SPR_LOCK).width + GetSpriteSize(SPR_BLOT).width + GetSpriteSize(SPR_FLAGS_BASE).width, 12); + leaf->SetMinimalSize(14 + GetSpriteSize(SPR_LOCK, nullptr, ZOOM_LVL_OUT_4X).width + + GetSpriteSize(SPR_BLOT, nullptr, ZOOM_LVL_OUT_4X).width + + GetSpriteSize(SPR_FLAGS_BASE, nullptr, ZOOM_LVL_OUT_4X).width, 12); leaf->SetFill(0, 1); this->Add(leaf); diff --git a/src/newgrf_gui.cpp b/src/newgrf_gui.cpp index 3465a94379..6316a7c4bf 100644 --- a/src/newgrf_gui.cpp +++ b/src/newgrf_gui.cpp @@ -1972,7 +1972,7 @@ static void NewGRFConfirmationCallback(Window *w, bool confirmed) w->InvalidateData(); - ReInitAllWindows(); + ReInitAllWindows(false); DeleteWindowByClass(WC_BUILD_OBJECT); } } diff --git a/src/osk_gui.cpp b/src/osk_gui.cpp index 233bb1c7aa..119f26eae9 100644 --- a/src/osk_gui.cpp +++ b/src/osk_gui.cpp @@ -237,7 +237,7 @@ static void AddKey(NWidgetHorizontal *hor, int height, int num_half, WidgetType hor->Add(spc); } NWidgetLeaf *leaf = new NWidgetLeaf(widtype, COLOUR_GREY, widnum, widdata, STR_NULL); - leaf->SetMinimalSize(ScaleGUITrad(key_width), height); + leaf->SetMinimalSizeAbsolute(ScaleGUITrad(key_width), height); hor->Add(leaf); } diff --git a/src/settings.cpp b/src/settings.cpp index 9b97107a34..c5819297fc 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -1172,7 +1172,7 @@ static bool InvalidateNewGRFChangeWindows(int32 p1) { InvalidateWindowClassesData(WC_SAVELOAD); DeleteWindowByClass(WC_GAME_OPTIONS); - ReInitAllWindows(); + ReInitAllWindows(_gui_zoom_cfg); return true; } diff --git a/src/settings_gui.cpp b/src/settings_gui.cpp index 52d900e6e8..47524bcfba 100644 --- a/src/settings_gui.cpp +++ b/src/settings_gui.cpp @@ -530,7 +530,7 @@ struct GameOptionsWindow : Window { case WID_GO_CURRENCY_DROPDOWN: // Currency if (index == CURRENCY_CUSTOM) ShowCustCurrency(); this->opt->locale.currency = index; - ReInitAllWindows(); + ReInitAllWindows(false); break; case WID_GO_AUTOSAVE_DROPDOWN: // Autosave options @@ -545,7 +545,7 @@ struct GameOptionsWindow : Window { ClearAllCachedNames(); UpdateAllVirtCoords(); CheckBlitter(); - ReInitAllWindows(); + ReInitAllWindows(false); break; case WID_GO_RESOLUTION_DROPDOWN: // Change resolution @@ -573,7 +573,7 @@ struct GameOptionsWindow : Window { UpdateCursorSize(); UpdateAllVirtCoords(); FixTitleGameZoom(); - ReInitAllWindows(); + ReInitAllWindows(true); } break; } @@ -587,6 +587,7 @@ struct GameOptionsWindow : Window { ClearFontCache(); LoadStringWidthTable(); UpdateAllVirtCoords(); + ReInitAllWindows(true); } break; } diff --git a/src/video/video_driver.cpp b/src/video/video_driver.cpp index eaff0b7414..d8fbe0400a 100644 --- a/src/video/video_driver.cpp +++ b/src/video/video_driver.cpp @@ -115,7 +115,7 @@ void VideoDriver::RealChangeBlitter(const char *repl_blitter) this->ClearSystemSprites(); ClearFontCache(); GfxClearSpriteCache(); - ReInitAllWindows(); + ReInitAllWindows(false); } void VideoDriver::Tick() diff --git a/src/widget.cpp b/src/widget.cpp index aae1a51651..68bc43d78f 100644 --- a/src/widget.cpp +++ b/src/widget.cpp @@ -822,6 +822,14 @@ NWidgetBase *NWidgetBase::GetWidgetOfType(WidgetType tp) return (this->type == tp) ? this : nullptr; } +void NWidgetBase::AdjustPaddingForZoom() +{ + this->padding_top = ScaleGUITrad(this->uz_padding_top); + this->padding_right = ScaleGUITrad(this->uz_padding_right); + this->padding_bottom = ScaleGUITrad(this->uz_padding_bottom); + this->padding_left = ScaleGUITrad(this->uz_padding_left); +} + /** * Constructor for resizable nested widgets. * @param tp Nested widget type. @@ -834,6 +842,15 @@ NWidgetResizeBase::NWidgetResizeBase(WidgetType tp, uint fill_x, uint fill_y) : this->fill_y = fill_y; } +void NWidgetResizeBase::AdjustPaddingForZoom() +{ + if (!this->absolute) { + this->min_x = ScaleGUITrad(this->uz_min_x); + this->min_y = std::max(ScaleGUITrad(this->uz_min_y), this->uz_text_lines * GetCharacterHeight(this->uz_text_size) + ScaleGUITrad(this->uz_text_spacing)); + } + NWidgetBase::AdjustPaddingForZoom(); +} + /** * Set minimal size of the widget. * @param min_x Horizontal minimal size of the widget. @@ -841,6 +858,20 @@ NWidgetResizeBase::NWidgetResizeBase(WidgetType tp, uint fill_x, uint fill_y) : */ void NWidgetResizeBase::SetMinimalSize(uint min_x, uint min_y) { + this->uz_min_x = std::max(this->uz_min_x, min_x); + this->uz_min_y = std::max(this->uz_min_y, min_y); + this->min_x = ScaleGUITrad(this->uz_min_x); + this->min_y = std::max(ScaleGUITrad(this->uz_min_y), this->uz_text_lines * GetCharacterHeight(this->uz_text_size) + ScaleGUITrad(this->uz_text_spacing)); +} + +/** + * Set absolute (post-scaling) minimal size of the widget. + * @param min_x Horizontal minimal size of the widget. + * @param min_y Vertical minimal size of the widget. + */ +void NWidgetResizeBase::SetMinimalSizeAbsolute(uint min_x, uint min_y) +{ + this->absolute = true; this->min_x = std::max(this->min_x, min_x); this->min_y = std::max(this->min_y, min_y); } @@ -853,7 +884,10 @@ void NWidgetResizeBase::SetMinimalSize(uint min_x, uint min_y) */ void NWidgetResizeBase::SetMinimalTextLines(uint8 min_lines, uint8 spacing, FontSize size) { - this->min_y = min_lines * GetCharacterHeight(size) + spacing; + this->uz_text_lines = min_lines; + this->uz_text_spacing = spacing; + this->uz_text_size = size; + this->min_y = std::max(ScaleGUITrad(this->uz_min_y), this->uz_text_lines * GetCharacterHeight(this->uz_text_size) + ScaleGUITrad(this->uz_text_spacing)); } /** @@ -991,6 +1025,14 @@ NWidgetBase *NWidgetContainer::GetWidgetOfType(WidgetType tp) return nullptr; } +void NWidgetContainer::AdjustPaddingForZoom() +{ + for (NWidgetBase *child_wid = this->head; child_wid != nullptr; child_wid = child_wid->next) { + child_wid->AdjustPaddingForZoom(); + } + NWidgetBase::AdjustPaddingForZoom(); +} + /** * Append widget \a wid to container. * @param wid Widget to append. @@ -1032,6 +1074,14 @@ void NWidgetStacked::SetIndex(int index) this->index = index; } +void NWidgetStacked::AdjustPaddingForZoom() +{ + for (NWidgetBase *child_wid = this->head; child_wid != nullptr; child_wid = child_wid->next) { + child_wid->AdjustPaddingForZoom(); + } + NWidgetContainer::AdjustPaddingForZoom(); +} + void NWidgetStacked::SetupSmallestSize(Window *w, bool init_array) { if (this->index >= 0 && init_array) { // Fill w->nested_array[] @@ -1145,6 +1195,14 @@ NWidgetPIPContainer::NWidgetPIPContainer(WidgetType tp, NWidContainerFlags flags this->flags = flags; } +void NWidgetPIPContainer::AdjustPaddingForZoom() +{ + this->pip_pre = ScaleGUITrad(this->uz_pip_pre); + this->pip_inter = ScaleGUITrad(this->uz_pip_inter); + this->pip_post = ScaleGUITrad(this->uz_pip_post); + NWidgetContainer::AdjustPaddingForZoom(); +} + /** * Set additional pre/inter/post space for the container. * @@ -1156,9 +1214,13 @@ NWidgetPIPContainer::NWidgetPIPContainer(WidgetType tp, NWidContainerFlags flags */ void NWidgetPIPContainer::SetPIP(uint8 pip_pre, uint8 pip_inter, uint8 pip_post) { - this->pip_pre = pip_pre; - this->pip_inter = pip_inter; - this->pip_post = pip_post; + this->uz_pip_pre = pip_pre; + this->uz_pip_inter = pip_inter; + this->uz_pip_post = pip_post; + + this->pip_pre = ScaleGUITrad(this->uz_pip_pre); + this->pip_inter = ScaleGUITrad(this->uz_pip_inter); + this->pip_post = ScaleGUITrad(this->uz_pip_post); } void NWidgetPIPContainer::Draw(const Window *w) @@ -1813,6 +1875,12 @@ void NWidgetBackground::SetPIP(uint8 pip_pre, uint8 pip_inter, uint8 pip_post) this->child->SetPIP(pip_pre, pip_inter, pip_post); } +void NWidgetBackground::AdjustPaddingForZoom() +{ + if (child != nullptr) child->AdjustPaddingForZoom(); + NWidgetCore::AdjustPaddingForZoom(); +} + void NWidgetBackground::SetupSmallestSize(Window *w, bool init_array) { if (init_array && this->index >= 0) { @@ -2137,11 +2205,11 @@ void NWidgetScrollbar::SetupSmallestSize(Window *w, bool init_array) switch (this->type) { case NWID_HSCROLLBAR: - this->SetMinimalSize(NWidgetScrollbar::GetHorizontalDimension().width * 3, NWidgetScrollbar::GetHorizontalDimension().height); + this->SetMinimalSizeAbsolute(NWidgetScrollbar::GetHorizontalDimension().width * 3, NWidgetScrollbar::GetHorizontalDimension().height); break; case NWID_VSCROLLBAR: - this->SetMinimalSize(NWidgetScrollbar::GetVerticalDimension().width, NWidgetScrollbar::GetVerticalDimension().height * 3); + this->SetMinimalSizeAbsolute(NWidgetScrollbar::GetVerticalDimension().width, NWidgetScrollbar::GetVerticalDimension().height * 3); break; default: NOT_REACHED(); @@ -2768,7 +2836,7 @@ static int MakeNWidget(const NWidgetPart *parts, int count, NWidgetBase **dest, NWidgetResizeBase *nwrb = dynamic_cast(*dest); if (nwrb != nullptr) { assert(parts->u.xy.x >= 0 && parts->u.xy.y >= 0); - nwrb->SetMinimalSize(ScaleGUITrad(parts->u.xy.x), ScaleGUITrad(parts->u.xy.y)); + nwrb->SetMinimalSize(parts->u.xy.x, parts->u.xy.y); } break; } @@ -2814,15 +2882,15 @@ static int MakeNWidget(const NWidgetPart *parts, int count, NWidgetBase **dest, } case WPT_PADDING: - if (*dest != nullptr) (*dest)->SetPadding(ScaleGUITrad(parts->u.padding.top), ScaleGUITrad(parts->u.padding.right), ScaleGUITrad(parts->u.padding.bottom), ScaleGUITrad(parts->u.padding.left)); + if (*dest != nullptr) (*dest)->SetPadding(parts->u.padding.top, parts->u.padding.right, parts->u.padding.bottom, parts->u.padding.left); break; case WPT_PIPSPACE: { NWidgetPIPContainer *nwc = dynamic_cast(*dest); - if (nwc != nullptr) nwc->SetPIP(ScaleGUITrad(parts->u.pip.pre), ScaleGUITrad(parts->u.pip.inter), ScaleGUITrad(parts->u.pip.post)); + if (nwc != nullptr) nwc->SetPIP(parts->u.pip.pre, parts->u.pip.inter, parts->u.pip.post); NWidgetBackground *nwb = dynamic_cast(*dest); - if (nwb != nullptr) nwb->SetPIP(ScaleGUITrad(parts->u.pip.pre), ScaleGUITrad(parts->u.pip.inter), ScaleGUITrad(parts->u.pip.post)); + if (nwb != nullptr) nwb->SetPIP(parts->u.pip.pre, parts->u.pip.inter, parts->u.pip.post); break; } @@ -3019,7 +3087,7 @@ NWidgetBase *MakeCompanyButtonRows(int *biggest_index, int widget_first, int wid NWidgetHorizontal *hor = nullptr; // Storage for buttons in one row. int hor_length = 0; - Dimension sprite_size = GetSpriteSize(SPR_COMPANY_ICON); + Dimension sprite_size = GetSpriteSize(SPR_COMPANY_ICON, nullptr, ZOOM_LVL_OUT_4X); sprite_size.width += WD_MATRIX_LEFT + WD_MATRIX_RIGHT; sprite_size.height += WD_MATRIX_TOP + WD_MATRIX_BOTTOM + 1; // 1 for the 'offset' of being pressed diff --git a/src/widget_type.h b/src/widget_type.h index 077261ff26..2007113565 100644 --- a/src/widget_type.h +++ b/src/widget_type.h @@ -127,6 +127,7 @@ class NWidgetBase : public ZeroedMemoryAllocator { public: NWidgetBase(WidgetType tp); + virtual void AdjustPaddingForZoom(); virtual void SetupSmallestSize(Window *w, bool init_array) = 0; virtual void AssignSizePosition(SizingType sizing, uint x, uint y, uint given_width, uint given_height, bool rtl) = 0; @@ -148,10 +149,11 @@ public: */ inline void SetPadding(uint8 top, uint8 right, uint8 bottom, uint8 left) { - this->padding_top = top; - this->padding_right = right; - this->padding_bottom = bottom; - this->padding_left = left; + this->uz_padding_top = top; + this->uz_padding_right = right; + this->uz_padding_bottom = bottom; + this->uz_padding_left = left; + this->AdjustPaddingForZoom(); } inline uint GetHorizontalStepSize(SizingType sizing) const; @@ -195,6 +197,11 @@ public: uint8 padding_bottom; ///< Paddings added to the bottom of the widget. Managed by parent container widget. uint8 padding_left; ///< Paddings added to the left of the widget. Managed by parent container widget. (parent container may swap this with padding_right for RTL) + uint8 uz_padding_top; ///< Unscaled top padding, for resize calculation. + uint8 uz_padding_right; ///< Unscaled right padding, for resize calculation. + uint8 uz_padding_bottom; ///< Unscaled bottom padding, for resize calculation. + uint8 uz_padding_left; ///< Unscaled left padding, for resize calculation. + protected: inline void StoreSizePosition(SizingType sizing, uint x, uint y, uint given_width, uint given_height); }; @@ -246,7 +253,9 @@ class NWidgetResizeBase : public NWidgetBase { public: NWidgetResizeBase(WidgetType tp, uint fill_x, uint fill_y); + void AdjustPaddingForZoom() override; void SetMinimalSize(uint min_x, uint min_y); + void SetMinimalSizeAbsolute(uint min_x, uint min_y); void SetMinimalTextLines(uint8 min_lines, uint8 spacing, FontSize size); void SetFill(uint fill_x, uint fill_y); void SetResize(uint resize_x, uint resize_y); @@ -255,6 +264,14 @@ public: uint min_x; ///< Minimal horizontal size of only this widget. uint min_y; ///< Minimal vertical size of only this widget. + + bool absolute; ///< Set if minimum size is fixed and should not be resized. + uint uz_min_x; ///< Unscaled Minimal horizontal size of only this widget. + uint uz_min_y; ///< Unscaled Minimal vertical size of only this widget. + + uint8 uz_text_lines; ///< 'Unscaled' text lines, stored for resize calculation. + uint8 uz_text_spacing; ///< 'Unscaled' text padding, stored for resize calculation. + FontSize uz_text_size; ///< 'Unscaled' font size, stored for resize calculation. }; /** Nested widget flags that affect display and interaction with 'real' widgets. */ @@ -385,6 +402,7 @@ public: NWidgetContainer(WidgetType tp); ~NWidgetContainer(); + void AdjustPaddingForZoom() override; void Add(NWidgetBase *wid); void FillNestedArray(NWidgetBase **array, uint length) override; @@ -423,6 +441,7 @@ public: void SetIndex(int index); + void AdjustPaddingForZoom() override; void SetupSmallestSize(Window *w, bool init_array) override; void AssignSizePosition(SizingType sizing, uint x, uint y, uint given_width, uint given_height, bool rtl) override; void FillNestedArray(NWidgetBase **array, uint length) override; @@ -450,6 +469,7 @@ class NWidgetPIPContainer : public NWidgetContainer { public: NWidgetPIPContainer(WidgetType tp, NWidContainerFlags flags = NC_NONE); + void AdjustPaddingForZoom() override; void SetPIP(uint8 pip_pre, uint8 pip_inter, uint8 pip_post); void Draw(const Window *w) override; @@ -460,6 +480,10 @@ protected: uint8 pip_pre; ///< Amount of space before first widget. uint8 pip_inter; ///< Amount of space between widgets. uint8 pip_post; ///< Amount of space after last widget. + + uint8 uz_pip_pre; ///< Unscaled space before first widget. + uint8 uz_pip_inter; ///< Unscaled space between widgets. + uint8 uz_pip_post; ///< Unscaled space after last widget. }; /** @@ -565,6 +589,7 @@ public: void Add(NWidgetBase *nwid); void SetPIP(uint8 pip_pre, uint8 pip_inter, uint8 pip_post); + void AdjustPaddingForZoom() override; void SetupSmallestSize(Window *w, bool init_array) override; void AssignSizePosition(SizingType sizing, uint x, uint y, uint given_width, uint given_height, bool rtl) override; diff --git a/src/widgets/dropdown.cpp b/src/widgets/dropdown.cpp index f5536f52f4..8f4a8634ee 100644 --- a/src/widgets/dropdown.cpp +++ b/src/widgets/dropdown.cpp @@ -153,7 +153,7 @@ struct DropdownWindow : Window { uint items_width = size.width - (scroll ? NWidgetScrollbar::GetVerticalDimension().width : 0); NWidgetCore *nwi = this->GetWidget(WID_DM_ITEMS); - nwi->SetMinimalSize(items_width, size.height + 4); + nwi->SetMinimalSizeAbsolute(items_width, size.height + 4); nwi->colour = wi_colour; nwi = this->GetWidget(WID_DM_SCROLL); diff --git a/src/window.cpp b/src/window.cpp index a82afc7cda..a409726002 100644 --- a/src/window.cpp +++ b/src/window.cpp @@ -3412,7 +3412,7 @@ void HideVitalWindows() } /** Re-initialize all windows. */ -void ReInitAllWindows() +void ReInitAllWindows(bool zoom_changed) { NWidgetLeaf::InvalidateDimensionCache(); // Reset cached sizes of several widgets. NWidgetScrollbar::InvalidateDimensionCache(); @@ -3421,6 +3421,7 @@ void ReInitAllWindows() InitDepotWindowBlockSizes(); for (Window *w : Window::IterateFromBack()) { + if (zoom_changed) w->nested_root->AdjustPaddingForZoom(); w->ReInit(); } diff --git a/src/window_func.h b/src/window_func.h index 20866b34d9..fba00944a5 100644 --- a/src/window_func.h +++ b/src/window_func.h @@ -43,7 +43,11 @@ void DeleteConstructionWindows(); void HideVitalWindows(); void ShowVitalWindows(); -void ReInitAllWindows(); +/** + * Re-initialize all windows. + * @param zoom_changed Set if windows are being re-initialized due to a zoom level changed. + */ +void ReInitAllWindows(bool zoom_changed); void SetWindowWidgetDirty(WindowClass cls, WindowNumber number, byte widget_index); void SetWindowDirty(WindowClass cls, WindowNumber number); From d32df00b5c1040f34a4ade8c2a6911853b3ea00a Mon Sep 17 00:00:00 2001 From: Peter Nelson Date: Sun, 18 Apr 2021 11:30:28 +0100 Subject: [PATCH 32/81] Codechange: No longer necessary to manually resize volume sliders. --- src/settings_gui.cpp | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/src/settings_gui.cpp b/src/settings_gui.cpp index 47524bcfba..34c073fe92 100644 --- a/src/settings_gui.cpp +++ b/src/settings_gui.cpp @@ -402,16 +402,6 @@ struct GameOptionsWindow : Window { } break; - case WID_GO_BASE_SFX_VOLUME: - case WID_GO_BASE_MUSIC_VOLUME: - size->width = ScaleGUITrad(67); - size->height = ScaleGUITrad(12); - resize->width = 0; - resize->height = 0; - fill->width = 0; - fill->height = 0; - break; - default: { int selected; DropDownList list = this->BuildDropDownList(widget, &selected); @@ -716,7 +706,7 @@ static const NWidgetPart _nested_game_options_widgets[] = { NWidget(NWID_HORIZONTAL), SetPIP(0, 30, 7), NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_GO_BASE_SFX_DROPDOWN), SetMinimalSize(150, 12), SetDataTip(STR_BLACK_RAW_STRING, STR_GAME_OPTIONS_BASE_SFX_TOOLTIP), NWidget(NWID_SPACER), SetMinimalSize(150, 12), SetFill(1, 0), - NWidget(WWT_EMPTY, COLOUR_GREY, WID_GO_BASE_SFX_VOLUME), SetMinimalSize(67, 12), SetMinimalTextLines(1, 0), SetDataTip(0x0, STR_MUSIC_TOOLTIP_DRAG_SLIDERS_TO_SET_MUSIC), + NWidget(WWT_EMPTY, COLOUR_GREY, WID_GO_BASE_SFX_VOLUME), SetMinimalSize(67, 12), SetFill(0, 0), SetDataTip(0x0, STR_MUSIC_TOOLTIP_DRAG_SLIDERS_TO_SET_MUSIC), EndContainer(), NWidget(WWT_TEXT, COLOUR_GREY, WID_GO_BASE_SFX_DESCRIPTION), SetMinimalSize(330, 0), SetDataTip(STR_EMPTY, STR_GAME_OPTIONS_BASE_SFX_DESCRIPTION_TOOLTIP), SetFill(1, 0), SetPadding(6, 0, 6, 0), NWidget(NWID_HORIZONTAL, NC_EQUALSIZE), SetPIP(7, 0, 7), @@ -730,7 +720,7 @@ static const NWidgetPart _nested_game_options_widgets[] = { NWidget(NWID_HORIZONTAL), SetPIP(0, 30, 7), NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_GO_BASE_MUSIC_DROPDOWN), SetMinimalSize(150, 12), SetDataTip(STR_BLACK_RAW_STRING, STR_GAME_OPTIONS_BASE_MUSIC_TOOLTIP), NWidget(WWT_TEXT, COLOUR_GREY, WID_GO_BASE_MUSIC_STATUS), SetMinimalSize(150, 12), SetDataTip(STR_EMPTY, STR_NULL), SetFill(1, 0), - NWidget(WWT_EMPTY, COLOUR_GREY, WID_GO_BASE_MUSIC_VOLUME), SetMinimalSize(67, 12), SetMinimalTextLines(1, 0), SetDataTip(0x0, STR_MUSIC_TOOLTIP_DRAG_SLIDERS_TO_SET_MUSIC), + NWidget(WWT_EMPTY, COLOUR_GREY, WID_GO_BASE_MUSIC_VOLUME), SetMinimalSize(67, 12), SetFill(0, 0), SetDataTip(0x0, STR_MUSIC_TOOLTIP_DRAG_SLIDERS_TO_SET_MUSIC), EndContainer(), NWidget(WWT_TEXT, COLOUR_GREY, WID_GO_BASE_MUSIC_DESCRIPTION), SetMinimalSize(330, 0), SetDataTip(STR_EMPTY, STR_GAME_OPTIONS_BASE_MUSIC_DESCRIPTION_TOOLTIP), SetFill(1, 0), SetPadding(6, 0, 6, 0), NWidget(NWID_HORIZONTAL, NC_EQUALSIZE), SetPIP(7, 0, 7), From 63cc340dc189d3613a8604c8287fc8e647d5cb01 Mon Sep 17 00:00:00 2001 From: Peter Nelson Date: Tue, 20 Apr 2021 11:49:34 +0100 Subject: [PATCH 33/81] Codechange: Apply minimum size to toolbar widgets --- src/toolbar_gui.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/toolbar_gui.cpp b/src/toolbar_gui.cpp index fc8b183470..4cbd13dbf7 100644 --- a/src/toolbar_gui.cpp +++ b/src/toolbar_gui.cpp @@ -2234,7 +2234,9 @@ static NWidgetBase *MakeMainToolbar(int *biggest_index) hor->Add(new NWidgetSpacer(0, 0)); break; } - hor->Add(new NWidgetLeaf(i == WID_TN_SAVE ? WWT_IMGBTN_2 : WWT_IMGBTN, COLOUR_GREY, i, toolbar_button_sprites[i], STR_TOOLBAR_TOOLTIP_PAUSE_GAME + i)); + NWidgetLeaf *leaf = new NWidgetLeaf(i == WID_TN_SAVE ? WWT_IMGBTN_2 : WWT_IMGBTN, COLOUR_GREY, i, toolbar_button_sprites[i], STR_TOOLBAR_TOOLTIP_PAUSE_GAME + i); + leaf->SetMinimalSize(20, 20); + hor->Add(leaf); } *biggest_index = std::max(*biggest_index, WID_TN_SWITCH_BAR); From 56a6f66903de5b999c686008b1d3243f0c61f5f4 Mon Sep 17 00:00:00 2001 From: Peter Nelson Date: Mon, 19 Apr 2021 09:29:49 +0100 Subject: [PATCH 34/81] Codechange: Use text lines instead of pixel height of font for link graph widgets. --- src/linkgraph/linkgraph_gui.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/linkgraph/linkgraph_gui.cpp b/src/linkgraph/linkgraph_gui.cpp index 2d715aaf58..e0986fdb59 100644 --- a/src/linkgraph/linkgraph_gui.cpp +++ b/src/linkgraph/linkgraph_gui.cpp @@ -383,7 +383,8 @@ NWidgetBase *MakeSaturationLegendLinkGraphGUI(int *biggest_index) NWidgetVertical *panel = new NWidgetVertical(NC_EQUALSIZE); for (uint i = 0; i < lengthof(LinkGraphOverlay::LINK_COLOURS); ++i) { NWidgetBackground * wid = new NWidgetBackground(WWT_PANEL, COLOUR_DARK_GREEN, i + WID_LGL_SATURATION_FIRST); - wid->SetMinimalSize(50, FONT_HEIGHT_SMALL); + wid->SetMinimalSize(50, 0); + wid->SetMinimalTextLines(1, 0, FS_SMALL); wid->SetFill(1, 1); wid->SetResize(0, 0); panel->Add(wid); @@ -403,14 +404,16 @@ NWidgetBase *MakeCargoesLegendLinkGraphGUI(int *biggest_index) row = new NWidgetHorizontal(NC_EQUALSIZE); } NWidgetBackground * wid = new NWidgetBackground(WWT_PANEL, COLOUR_GREY, i + WID_LGL_CARGO_FIRST); - wid->SetMinimalSize(25, FONT_HEIGHT_SMALL); + wid->SetMinimalSize(25, 0); + wid->SetMinimalTextLines(1, 0, FS_SMALL); wid->SetFill(1, 1); wid->SetResize(0, 0); row->Add(wid); } /* Fill up last row */ for (uint i = 0; i < 4 - (NUM_CARGO - 1) % 5; ++i) { - NWidgetSpacer *spc = new NWidgetSpacer(25, FONT_HEIGHT_SMALL); + NWidgetSpacer *spc = new NWidgetSpacer(25, 0); + spc->SetMinimalTextLines(1, 0, FS_SMALL); spc->SetFill(1, 1); spc->SetResize(0, 0); row->Add(spc); From 09206be05448a7900bfa1f83c0b87212110fd96e Mon Sep 17 00:00:00 2001 From: Peter Nelson Date: Thu, 22 Apr 2021 01:19:54 +0100 Subject: [PATCH 35/81] Fix: Use unscaled values for padding OSK --- src/osk_gui.cpp | 56 ++++++++++++++++++++++++------------------------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/src/osk_gui.cpp b/src/osk_gui.cpp index 119f26eae9..155343f041 100644 --- a/src/osk_gui.cpp +++ b/src/osk_gui.cpp @@ -212,10 +212,13 @@ struct OskWindow : public Window { static const int HALF_KEY_WIDTH = 7; // Width of 1/2 key in pixels. static const int INTER_KEY_SPACE = 2; // Number of pixels between two keys. +static const int TOP_KEY_PADDING = 2; // Vertical padding for the top row of keys. +static const int KEY_PADDING = 6; // Vertical padding for remaining key rows. + /** * Add a key widget to a row of the keyboard. * @param hor Row container to add key widget to. - * @param height Height of the key (all keys in a row should have equal height). + * @param pad_y Vertical padding of the key (all keys in a row should have equal padding). * @param num_half Number of 1/2 key widths that this key has. * @param widtype Widget type of the key. Must be either \c NWID_SPACER for an invisible key, or a \c WWT_* widget. * @param widnum Widget number of the key. @@ -223,21 +226,24 @@ static const int INTER_KEY_SPACE = 2; // Number of pixels between two keys. * @param biggest_index Collected biggest widget index so far. * @note Key width is measured in 1/2 keys to allow for 1/2 key shifting between rows. */ -static void AddKey(NWidgetHorizontal *hor, int height, int num_half, WidgetType widtype, int widnum, uint16 widdata, int *biggest_index) +static void AddKey(NWidgetHorizontal *hor, int pad_y, int num_half, WidgetType widtype, int widnum, uint16 widdata, int *biggest_index) { int key_width = HALF_KEY_WIDTH + (INTER_KEY_SPACE + HALF_KEY_WIDTH) * (num_half - 1); if (widtype == NWID_SPACER) { if (!hor->IsEmpty()) key_width += INTER_KEY_SPACE; - NWidgetSpacer *spc = new NWidgetSpacer(ScaleGUITrad(key_width), height); + NWidgetSpacer *spc = new NWidgetSpacer(key_width, 0); + spc->SetMinimalTextLines(1, pad_y, FS_NORMAL); hor->Add(spc); } else { if (!hor->IsEmpty()) { - NWidgetSpacer *spc = new NWidgetSpacer(ScaleGUITrad(INTER_KEY_SPACE), height); + NWidgetSpacer *spc = new NWidgetSpacer(INTER_KEY_SPACE, 0); + spc->SetMinimalTextLines(1, pad_y, FS_NORMAL); hor->Add(spc); } NWidgetLeaf *leaf = new NWidgetLeaf(widtype, COLOUR_GREY, widnum, widdata, STR_NULL); - leaf->SetMinimalSizeAbsolute(ScaleGUITrad(key_width), height); + leaf->SetMinimalSize(key_width, 0); + leaf->SetMinimalTextLines(1, pad_y, FS_NORMAL); hor->Add(leaf); } @@ -248,11 +254,10 @@ static void AddKey(NWidgetHorizontal *hor, int height, int num_half, WidgetType static NWidgetBase *MakeTopKeys(int *biggest_index) { NWidgetHorizontal *hor = new NWidgetHorizontal(); - int key_height = FONT_HEIGHT_NORMAL + 2; - AddKey(hor, key_height, 6 * 2, WWT_TEXTBTN, WID_OSK_CANCEL, STR_BUTTON_CANCEL, biggest_index); - AddKey(hor, key_height, 6 * 2, WWT_TEXTBTN, WID_OSK_OK, STR_BUTTON_OK, biggest_index); - AddKey(hor, key_height, 2 * 2, WWT_PUSHIMGBTN, WID_OSK_BACKSPACE, SPR_OSK_BACKSPACE, biggest_index); + AddKey(hor, TOP_KEY_PADDING, 6 * 2, WWT_TEXTBTN, WID_OSK_CANCEL, STR_BUTTON_CANCEL, biggest_index); + AddKey(hor, TOP_KEY_PADDING, 6 * 2, WWT_TEXTBTN, WID_OSK_OK, STR_BUTTON_OK, biggest_index); + AddKey(hor, TOP_KEY_PADDING, 2 * 2, WWT_PUSHIMGBTN, WID_OSK_BACKSPACE, SPR_OSK_BACKSPACE, biggest_index); return hor; } @@ -260,10 +265,9 @@ static NWidgetBase *MakeTopKeys(int *biggest_index) static NWidgetBase *MakeNumberKeys(int *biggest_index) { NWidgetHorizontal *hor = new NWidgetHorizontalLTR(); - int key_height = FONT_HEIGHT_NORMAL + 6; for (int widnum = WID_OSK_NUMBERS_FIRST; widnum <= WID_OSK_NUMBERS_LAST; widnum++) { - AddKey(hor, key_height, 2, WWT_PUSHBTN, widnum, 0x0, biggest_index); + AddKey(hor, KEY_PADDING, 2, WWT_PUSHBTN, widnum, 0x0, biggest_index); } return hor; } @@ -272,13 +276,12 @@ static NWidgetBase *MakeNumberKeys(int *biggest_index) static NWidgetBase *MakeQwertyKeys(int *biggest_index) { NWidgetHorizontal *hor = new NWidgetHorizontalLTR(); - int key_height = FONT_HEIGHT_NORMAL + 6; - AddKey(hor, key_height, 3, WWT_PUSHIMGBTN, WID_OSK_SPECIAL, SPR_OSK_SPECIAL, biggest_index); + AddKey(hor, KEY_PADDING, 3, WWT_PUSHIMGBTN, WID_OSK_SPECIAL, SPR_OSK_SPECIAL, biggest_index); for (int widnum = WID_OSK_QWERTY_FIRST; widnum <= WID_OSK_QWERTY_LAST; widnum++) { - AddKey(hor, key_height, 2, WWT_PUSHBTN, widnum, 0x0, biggest_index); + AddKey(hor, KEY_PADDING, 2, WWT_PUSHBTN, widnum, 0x0, biggest_index); } - AddKey(hor, key_height, 1, NWID_SPACER, 0, 0, biggest_index); + AddKey(hor, KEY_PADDING, 1, NWID_SPACER, 0, 0, biggest_index); return hor; } @@ -286,11 +289,10 @@ static NWidgetBase *MakeQwertyKeys(int *biggest_index) static NWidgetBase *MakeAsdfgKeys(int *biggest_index) { NWidgetHorizontal *hor = new NWidgetHorizontalLTR(); - int key_height = FONT_HEIGHT_NORMAL + 6; - AddKey(hor, key_height, 4, WWT_IMGBTN, WID_OSK_CAPS, SPR_OSK_CAPS, biggest_index); + AddKey(hor, KEY_PADDING, 4, WWT_IMGBTN, WID_OSK_CAPS, SPR_OSK_CAPS, biggest_index); for (int widnum = WID_OSK_ASDFG_FIRST; widnum <= WID_OSK_ASDFG_LAST; widnum++) { - AddKey(hor, key_height, 2, WWT_PUSHBTN, widnum, 0x0, biggest_index); + AddKey(hor, KEY_PADDING, 2, WWT_PUSHBTN, widnum, 0x0, biggest_index); } return hor; } @@ -299,13 +301,12 @@ static NWidgetBase *MakeAsdfgKeys(int *biggest_index) static NWidgetBase *MakeZxcvbKeys(int *biggest_index) { NWidgetHorizontal *hor = new NWidgetHorizontalLTR(); - int key_height = FONT_HEIGHT_NORMAL + 6; - AddKey(hor, key_height, 3, WWT_IMGBTN, WID_OSK_SHIFT, SPR_OSK_SHIFT, biggest_index); + AddKey(hor, KEY_PADDING, 3, WWT_IMGBTN, WID_OSK_SHIFT, SPR_OSK_SHIFT, biggest_index); for (int widnum = WID_OSK_ZXCVB_FIRST; widnum <= WID_OSK_ZXCVB_LAST; widnum++) { - AddKey(hor, key_height, 2, WWT_PUSHBTN, widnum, 0x0, biggest_index); + AddKey(hor, KEY_PADDING, 2, WWT_PUSHBTN, widnum, 0x0, biggest_index); } - AddKey(hor, key_height, 1, NWID_SPACER, 0, 0, biggest_index); + AddKey(hor, KEY_PADDING, 1, NWID_SPACER, 0, 0, biggest_index); return hor; } @@ -313,13 +314,12 @@ static NWidgetBase *MakeZxcvbKeys(int *biggest_index) static NWidgetBase *MakeSpacebarKeys(int *biggest_index) { NWidgetHorizontal *hor = new NWidgetHorizontal(); - int key_height = FONT_HEIGHT_NORMAL + 6; - AddKey(hor, key_height, 8, NWID_SPACER, 0, 0, biggest_index); - AddKey(hor, key_height, 13, WWT_PUSHTXTBTN, WID_OSK_SPACE, STR_EMPTY, biggest_index); - AddKey(hor, key_height, 3, NWID_SPACER, 0, 0, biggest_index); - AddKey(hor, key_height, 2, WWT_PUSHIMGBTN, WID_OSK_LEFT, SPR_OSK_LEFT, biggest_index); - AddKey(hor, key_height, 2, WWT_PUSHIMGBTN, WID_OSK_RIGHT, SPR_OSK_RIGHT, biggest_index); + AddKey(hor, KEY_PADDING, 8, NWID_SPACER, 0, 0, biggest_index); + AddKey(hor, KEY_PADDING, 13, WWT_PUSHTXTBTN, WID_OSK_SPACE, STR_EMPTY, biggest_index); + AddKey(hor, KEY_PADDING, 3, NWID_SPACER, 0, 0, biggest_index); + AddKey(hor, KEY_PADDING, 2, WWT_PUSHIMGBTN, WID_OSK_LEFT, SPR_OSK_LEFT, biggest_index); + AddKey(hor, KEY_PADDING, 2, WWT_PUSHIMGBTN, WID_OSK_RIGHT, SPR_OSK_RIGHT, biggest_index); return hor; } From 1df510c297636652d826c512b009015f3de3a3b8 Mon Sep 17 00:00:00 2001 From: Peter Nelson Date: Thu, 22 Apr 2021 18:21:14 +0100 Subject: [PATCH 36/81] Fix: Company Key window scaling. --- src/graph_gui.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/graph_gui.cpp b/src/graph_gui.cpp index 67399c327f..52b37d6779 100644 --- a/src/graph_gui.cpp +++ b/src/graph_gui.cpp @@ -21,6 +21,7 @@ #include "sortlist_type.h" #include "core/geometry_func.hpp" #include "currency.h" +#include "zoom_func.h" #include "widgets/graph_widget.h" @@ -65,11 +66,11 @@ struct GraphLegendWindow : Window { bool rtl = _current_text_dir == TD_RTL; Dimension d = GetSpriteSize(SPR_COMPANY_ICON); - DrawCompanyIcon(cid, rtl ? r.right - d.width - 2 : r.left + 2, r.top + (r.bottom - r.top - d.height) / 2); + DrawCompanyIcon(cid, rtl ? r.right - d.width - ScaleGUITrad(2) : r.left + ScaleGUITrad(2), CenterBounds(r.top, r.bottom, d.height)); SetDParam(0, cid); SetDParam(1, cid); - DrawString(r.left + (rtl ? (uint)WD_FRAMERECT_LEFT : (d.width + 4)), r.right - (rtl ? (d.width + 4) : (uint)WD_FRAMERECT_RIGHT), r.top + (r.bottom - r.top + 1 - FONT_HEIGHT_NORMAL) / 2, STR_COMPANY_NAME_COMPANY_NUM, HasBit(_legend_excluded_companies, cid) ? TC_BLACK : TC_WHITE); + DrawString(r.left + (rtl ? (uint)WD_FRAMERECT_LEFT : (d.width + ScaleGUITrad(4))), r.right - (rtl ? (d.width + ScaleGUITrad(4)) : (uint)WD_FRAMERECT_RIGHT), CenterBounds(r.top, r.bottom, FONT_HEIGHT_NORMAL), STR_COMPANY_NAME_COMPANY_NUM, HasBit(_legend_excluded_companies, cid) ? TC_BLACK : TC_WHITE); } void OnClick(Point pt, int widget, int click_count) override @@ -110,11 +111,12 @@ struct GraphLegendWindow : Window { static NWidgetBase *MakeNWidgetCompanyLines(int *biggest_index) { NWidgetVertical *vert = new NWidgetVertical(); - uint line_height = std::max(GetSpriteSize(SPR_COMPANY_ICON).height, FONT_HEIGHT_NORMAL) + WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM; + uint sprite_height = GetSpriteSize(SPR_COMPANY_ICON, nullptr, ZOOM_LVL_OUT_4X).height; for (int widnum = WID_GL_FIRST_COMPANY; widnum <= WID_GL_LAST_COMPANY; widnum++) { NWidgetBackground *panel = new NWidgetBackground(WWT_PANEL, COLOUR_BROWN, widnum); - panel->SetMinimalSize(246, line_height); + panel->SetMinimalSize(246, sprite_height); + panel->SetMinimalTextLines(1, WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM, FS_NORMAL); panel->SetFill(1, 0); panel->SetDataTip(0x0, STR_GRAPH_KEY_COMPANY_SELECTION_TOOLTIP); vert->Add(panel); @@ -137,6 +139,7 @@ static const NWidgetPart _nested_graph_legend_widgets[] = { NWidgetFunction(MakeNWidgetCompanyLines), NWidget(NWID_SPACER), SetMinimalSize(2, 0), EndContainer(), + NWidget(NWID_SPACER), SetMinimalSize(0, 2), EndContainer(), }; From af70195e448e1a934fe921efcb9450ead99cb49e Mon Sep 17 00:00:00 2001 From: Peter Nelson Date: Thu, 22 Apr 2021 19:07:20 +0100 Subject: [PATCH 37/81] Fix: Tidy up sizing of sprite aligner window sprite list. --- src/newgrf_debug_gui.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/newgrf_debug_gui.cpp b/src/newgrf_debug_gui.cpp index bd78deee97..3a5e05a77b 100644 --- a/src/newgrf_debug_gui.cpp +++ b/src/newgrf_debug_gui.cpp @@ -863,8 +863,9 @@ struct SpriteAlignerWindow : Window { size->height = ScaleGUITrad(200); break; case WID_SA_LIST: - resize->height = std::max(11, FONT_HEIGHT_NORMAL + 1); + resize->height = FONT_HEIGHT_NORMAL + WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM; resize->width = 1; + fill->height = resize->height; break; default: break; From 5434d63f914b38dbffa86fe0e5c651b79816f3f2 Mon Sep 17 00:00:00 2001 From: Peter Nelson Date: Thu, 22 Apr 2021 21:47:18 +0100 Subject: [PATCH 38/81] Fix: Scale smallmap legend 'blob' to fit text. --- src/smallmap_gui.cpp | 21 +++++++++++++-------- src/smallmap_gui.h | 2 +- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/src/smallmap_gui.cpp b/src/smallmap_gui.cpp index 0e7d313a67..b00cf0e7a9 100644 --- a/src/smallmap_gui.cpp +++ b/src/smallmap_gui.cpp @@ -22,6 +22,7 @@ #include "window_func.h" #include "company_base.h" #include "guitimer_func.h" +#include "zoom_func.h" #include "smallmap_gui.h" @@ -1177,8 +1178,11 @@ void SmallMapWindow::RebuildColourIndexIfNecessary() this->min_number_of_columns = std::max(this->min_number_of_columns, num_columns); } + /* Width of the legend blob. */ + this->legend_width = (FONT_HEIGHT_SMALL - ScaleFontTrad(1)) * 8 / 5; + /* The width of a column is the minimum width of all texts + the size of the blob + some spacing */ - this->column_width = min_width + LEGEND_BLOB_WIDTH + WD_FRAMERECT_LEFT + WD_FRAMERECT_RIGHT; + this->column_width = min_width + this->legend_width + WD_FRAMERECT_LEFT + WD_FRAMERECT_RIGHT; } /* virtual */ void SmallMapWindow::OnPaint() @@ -1216,11 +1220,12 @@ void SmallMapWindow::RebuildColourIndexIfNecessary() uint y = y_org; uint i = 0; // Row counter for industry legend. uint row_height = FONT_HEIGHT_SMALL; + int padding = ScaleFontTrad(1); - uint text_left = rtl ? 0 : LEGEND_BLOB_WIDTH + WD_FRAMERECT_LEFT; - uint text_right = this->column_width - 1 - (rtl ? LEGEND_BLOB_WIDTH + WD_FRAMERECT_RIGHT : 0); - uint blob_left = rtl ? this->column_width - 1 - LEGEND_BLOB_WIDTH : 0; - uint blob_right = rtl ? this->column_width - 1 : LEGEND_BLOB_WIDTH; + uint text_left = rtl ? 0 : this->legend_width + WD_FRAMERECT_LEFT; + uint text_right = this->column_width - padding - (rtl ? this->legend_width + WD_FRAMERECT_RIGHT : 0); + uint blob_left = rtl ? this->column_width - padding - this->legend_width : 0; + uint blob_right = rtl ? this->column_width - padding : this->legend_width; StringID string = STR_NULL; switch (this->map_type) { @@ -1272,7 +1277,7 @@ void SmallMapWindow::RebuildColourIndexIfNecessary() DrawString(x + text_left, x + text_right, y, string, TC_GREY); } else { DrawString(x + text_left, x + text_right, y, string, TC_BLACK); - GfxFillRect(x + blob_left, y + 1, x + blob_right, y + row_height - 1, PC_BLACK); // Outer border of the legend colour + GfxFillRect(x + blob_left, y + padding, x + blob_right, y + row_height - 1, PC_BLACK); // Outer border of the legend colour } break; } @@ -1281,11 +1286,11 @@ void SmallMapWindow::RebuildColourIndexIfNecessary() default: if (this->map_type == SMT_CONTOUR) SetDParam(0, tbl->height * TILE_HEIGHT_STEP); /* Anything that is not an industry or a company is using normal process */ - GfxFillRect(x + blob_left, y + 1, x + blob_right, y + row_height - 1, PC_BLACK); + GfxFillRect(x + blob_left, y + padding, x + blob_right, y + row_height - 1, PC_BLACK); DrawString(x + text_left, x + text_right, y, tbl->legend); break; } - GfxFillRect(x + blob_left + 1, y + 2, x + blob_right - 1, y + row_height - 2, legend_colour); // Legend colour + GfxFillRect(x + blob_left + 1, y + padding + 1, x + blob_right - 1, y + row_height - 2, legend_colour); // Legend colour y += row_height; } diff --git a/src/smallmap_gui.h b/src/smallmap_gui.h index f1b8ece731..e2fa9074d7 100644 --- a/src/smallmap_gui.h +++ b/src/smallmap_gui.h @@ -64,7 +64,6 @@ protected: static bool show_towns; ///< Display town names in the smallmap. static int map_height_limit; ///< Currently used/cached map height limit. - static const uint LEGEND_BLOB_WIDTH = 8; ///< Width of the coloured blob in front of a line text in the #WID_SM_LEGEND widget. static const uint INDUSTRY_MIN_NUMBER_OF_COLUMNS = 2; ///< Minimal number of columns in the #WID_SM_LEGEND widget for the #SMT_INDUSTRY legend. static const uint FORCE_REFRESH_PERIOD = 930; ///< map is redrawn after that many milliseconds. static const uint BLINK_PERIOD = 450; ///< highlight blinking interval in milliseconds. @@ -72,6 +71,7 @@ protected: uint min_number_of_columns; ///< Minimal number of columns in legends. uint min_number_of_fixed_rows; ///< Minimal number of rows in the legends for the fixed layouts only (all except #SMT_INDUSTRY). uint column_width; ///< Width of a column in the #WID_SM_LEGEND widget. + uint legend_width; ///< Width of legend 'blob'. int32 scroll_x; ///< Horizontal world coordinate of the base tile left of the top-left corner of the smallmap display. int32 scroll_y; ///< Vertical world coordinate of the base tile left of the top-left corner of the smallmap display. From 617e85cc65c8dae6dbdaad99856e36e094f16a94 Mon Sep 17 00:00:00 2001 From: Peter Nelson Date: Fri, 23 Apr 2021 00:49:11 +0100 Subject: [PATCH 39/81] Fix: Scale legend blobs in Fund new industry window. --- src/industry_gui.cpp | 37 ++++++++++++++++++++++++------------- 1 file changed, 24 insertions(+), 13 deletions(-) diff --git a/src/industry_gui.cpp b/src/industry_gui.cpp index cce96e6d63..85e90515aa 100644 --- a/src/industry_gui.cpp +++ b/src/industry_gui.cpp @@ -38,6 +38,7 @@ #include "widgets/dropdown_type.h" #include "widgets/industry_widget.h" #include "clear_map.h" +#include "zoom_func.h" #include "table/strings.h" @@ -283,9 +284,8 @@ class BuildIndustryWindow : public Window { IndustryType index[NUM_INDUSTRYTYPES + 1]; ///< Type of industry, in the order it was loaded bool enabled[NUM_INDUSTRYTYPES + 1]; ///< availability state, coming from CBID_INDUSTRY_PROBABILITY (if ever) Scrollbar *vscroll; + Dimension legend; ///< Dimension of the legend 'blob'. - /** The offset for the text in the matrix. */ - static const int MATRIX_TEXT_OFFSET = 17; /** The largest allowed minimum-width of the window, given in line heights */ static const int MAX_MINWIDTH_LINEHEIGHTS = 20; @@ -407,6 +407,10 @@ public: void OnInit() override { + /* Width of the legend blob -- slightly larger than the smallmap legend blob. */ + this->legend.height = FONT_HEIGHT_SMALL; + this->legend.width = this->legend.height * 8 / 5; + this->SetupArrays(); } @@ -419,8 +423,8 @@ public: if (this->index[i] == INVALID_INDUSTRYTYPE) continue; d = maxdim(d, GetStringBoundingBox(GetIndustrySpec(this->index[i])->name)); } - resize->height = FONT_HEIGHT_NORMAL + WD_MATRIX_TOP + WD_MATRIX_BOTTOM; - d.width += MATRIX_TEXT_OFFSET + padding.width; + resize->height = std::max(this->legend.height, FONT_HEIGHT_NORMAL) + WD_MATRIX_TOP + WD_MATRIX_BOTTOM; + d.width += this->legend.width + ScaleFontTrad(7) + padding.width; d.height = 5 * resize->height; *size = maxdim(*size, d); break; @@ -509,30 +513,37 @@ public: uint text_left, text_right, icon_left, icon_right; if (_current_text_dir == TD_RTL) { icon_right = r.right - WD_MATRIX_RIGHT; - icon_left = icon_right - 10; - text_right = icon_right - BuildIndustryWindow::MATRIX_TEXT_OFFSET; + icon_left = icon_right - this->legend.width; + text_right = icon_left - ScaleFontTrad(7); text_left = r.left + WD_MATRIX_LEFT; } else { icon_left = r.left + WD_MATRIX_LEFT; - icon_right = icon_left + 10; - text_left = icon_left + BuildIndustryWindow::MATRIX_TEXT_OFFSET; + icon_right = icon_left + this->legend.width; + text_left = icon_right + ScaleFontTrad(7); text_right = r.right - WD_MATRIX_RIGHT; } + /* Vertical offset for legend icon. */ + int icon_top = (this->resize.step_height - this->legend.height + 1) / 2; + int icon_bottom = icon_top + this->legend.height; + + int y = r.top; for (byte i = 0; i < this->vscroll->GetCapacity() && i + this->vscroll->GetPosition() < this->count; i++) { - int y = r.top + WD_MATRIX_TOP + i * this->resize.step_height; bool selected = this->selected_index == i + this->vscroll->GetPosition(); if (this->index[i + this->vscroll->GetPosition()] == INVALID_INDUSTRYTYPE) { - DrawString(text_left, text_right, y, STR_FUND_INDUSTRY_MANY_RANDOM_INDUSTRIES, selected ? TC_WHITE : TC_ORANGE); + DrawString(text_left, text_right, y + WD_MATRIX_TOP, STR_FUND_INDUSTRY_MANY_RANDOM_INDUSTRIES, selected ? TC_WHITE : TC_ORANGE); + y += this->resize.step_height; continue; } const IndustrySpec *indsp = GetIndustrySpec(this->index[i + this->vscroll->GetPosition()]); /* Draw the name of the industry in white is selected, otherwise, in orange */ - DrawString(text_left, text_right, y, indsp->name, selected ? TC_WHITE : TC_ORANGE); - GfxFillRect(icon_left, y + 1, icon_right, y + 7, selected ? PC_WHITE : PC_BLACK); - GfxFillRect(icon_left + 1, y + 2, icon_right - 1, y + 6, indsp->map_colour); + DrawString(text_left, text_right, y + WD_MATRIX_TOP, indsp->name, selected ? TC_WHITE : TC_ORANGE); + GfxFillRect(icon_left, y + icon_top, icon_right, y + icon_bottom, selected ? PC_WHITE : PC_BLACK); + GfxFillRect(icon_left + 1, y + icon_top + 1, icon_right - 1, y + icon_bottom - 1, indsp->map_colour); + + y += this->resize.step_height; } break; } From 957beaaefcaac0fa0b90a0a8354d418e65f53e19 Mon Sep 17 00:00:00 2001 From: Peter Nelson Date: Fri, 23 Apr 2021 10:18:44 +0100 Subject: [PATCH 40/81] Fix: Improved scaling and spacing of sign list window. Both company icon sprite and text now centred within each row, and extra padding added to avoid the sprites running into each other. --- src/signs_gui.cpp | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/signs_gui.cpp b/src/signs_gui.cpp index ea55488cc7..93c4dd4312 100644 --- a/src/signs_gui.cpp +++ b/src/signs_gui.cpp @@ -194,14 +194,16 @@ struct SignListWindow : Window, SignList { switch (widget) { case WID_SIL_LIST: { uint y = r.top + WD_FRAMERECT_TOP; // Offset from top of widget. + uint text_offset_y = (this->resize.step_height - FONT_HEIGHT_NORMAL + 1) / 2; /* No signs? */ if (this->vscroll->GetCount() == 0) { - DrawString(r.left + WD_FRAMETEXT_LEFT, r.right - WD_FRAMETEXT_RIGHT, y, STR_STATION_LIST_NONE); + DrawString(r.left + WD_FRAMETEXT_LEFT, r.right - WD_FRAMETEXT_RIGHT, y + text_offset_y, STR_STATION_LIST_NONE); return; } + Dimension d = GetSpriteSize(SPR_COMPANY_ICON); bool rtl = _current_text_dir == TD_RTL; - int sprite_offset_y = (FONT_HEIGHT_NORMAL - 10) / 2 + 1; + int sprite_offset_y = (this->resize.step_height - d.height + 1) / 2; uint icon_left = 4 + (rtl ? r.right - this->text_offset : r.left); uint text_left = r.left + (rtl ? WD_FRAMERECT_LEFT : this->text_offset); uint text_right = r.right - (rtl ? this->text_offset : WD_FRAMERECT_RIGHT); @@ -213,7 +215,7 @@ struct SignListWindow : Window, SignList { if (si->owner != OWNER_NONE) DrawCompanyIcon(si->owner, icon_left, y + sprite_offset_y); SetDParam(0, si->index); - DrawString(text_left, text_right, y, STR_SIGN_NAME, TC_YELLOW); + DrawString(text_left, text_right, y + text_offset_y, STR_SIGN_NAME, TC_YELLOW); y += this->resize.step_height; } break; @@ -264,7 +266,7 @@ struct SignListWindow : Window, SignList { case WID_SIL_LIST: { Dimension spr_dim = GetSpriteSize(SPR_COMPANY_ICON); this->text_offset = WD_FRAMETEXT_LEFT + spr_dim.width + 2; // 2 pixels space between icon and the sign text. - resize->height = std::max(FONT_HEIGHT_NORMAL, spr_dim.height); + resize->height = std::max(FONT_HEIGHT_NORMAL, spr_dim.height + 2); Dimension d = {(uint)(this->text_offset + WD_FRAMETEXT_RIGHT), WD_FRAMERECT_TOP + 5 * resize->height + WD_FRAMERECT_BOTTOM}; *size = maxdim(*size, d); break; @@ -365,8 +367,8 @@ static const NWidgetPart _nested_sign_list_widgets[] = { EndContainer(), NWidget(NWID_HORIZONTAL), NWidget(NWID_VERTICAL), - NWidget(WWT_PANEL, COLOUR_BROWN, WID_SIL_LIST), SetMinimalSize(WD_FRAMETEXT_LEFT + 16 + 255 + WD_FRAMETEXT_RIGHT, 50), - SetResize(1, 10), SetFill(1, 0), SetScrollbar(WID_SIL_SCROLLBAR), EndContainer(), + NWidget(WWT_PANEL, COLOUR_BROWN, WID_SIL_LIST), SetMinimalSize(WD_FRAMETEXT_LEFT + 16 + 255 + WD_FRAMETEXT_RIGHT, 0), + SetResize(1, 1), SetFill(1, 0), SetScrollbar(WID_SIL_SCROLLBAR), EndContainer(), NWidget(NWID_HORIZONTAL), NWidget(WWT_PANEL, COLOUR_BROWN), SetFill(1, 1), NWidget(WWT_EDITBOX, COLOUR_BROWN, WID_SIL_FILTER_TEXT), SetMinimalSize(80, 12), SetResize(1, 0), SetFill(1, 0), SetPadding(2, 2, 2, 2), From 254ffe9dccbba3ef3199439b6285b2cef6edc79c Mon Sep 17 00:00:00 2001 From: Peter Nelson Date: Fri, 23 Apr 2021 11:07:00 +0100 Subject: [PATCH 41/81] Fix: Scale industry chain legend blob by font size. --- src/industry_gui.cpp | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/src/industry_gui.cpp b/src/industry_gui.cpp index 85e90515aa..2188487453 100644 --- a/src/industry_gui.cpp +++ b/src/industry_gui.cpp @@ -1856,7 +1856,9 @@ struct CargoesField { static const int CARGO_STUB_WIDTH; static const int HOR_CARGO_WIDTH, HOR_CARGO_SPACE; static const int VERT_CARGO_SPACE, VERT_CARGO_EDGE; - static const int BLOB_DISTANCE, BLOB_WIDTH, BLOB_HEIGHT; + static const int BLOB_DISTANCE; + + static Dimension legend; static const int INDUSTRY_LINE_COLOUR; static const int CARGO_LINE_COLOUR; @@ -2060,13 +2062,13 @@ struct CargoesField { int blob_left, blob_right; if (_current_text_dir == TD_RTL) { blob_right = xpos2 - BLOB_DISTANCE; - blob_left = blob_right - BLOB_WIDTH; + blob_left = blob_right - CargoesField::legend.width; } else { blob_left = xpos + BLOB_DISTANCE; - blob_right = blob_left + BLOB_WIDTH; + blob_right = blob_left + CargoesField::legend.width; } - GfxFillRect(blob_left, ypos2 - BLOB_DISTANCE - BLOB_HEIGHT, blob_right, ypos2 - BLOB_DISTANCE, PC_BLACK); // Border - GfxFillRect(blob_left + 1, ypos2 - BLOB_DISTANCE - BLOB_HEIGHT + 1, blob_right - 1, ypos2 - BLOB_DISTANCE - 1, indsp->map_colour); + GfxFillRect(blob_left, ypos2 - BLOB_DISTANCE - CargoesField::legend.height, blob_right, ypos2 - BLOB_DISTANCE, PC_BLACK); // Border + GfxFillRect(blob_left + 1, ypos2 - BLOB_DISTANCE - CargoesField::legend.height + 1, blob_right - 1, ypos2 - BLOB_DISTANCE - 1, indsp->map_colour); } else { DrawString(xpos, xpos2, ypos, STR_INDUSTRY_CARGOES_HOUSES, TC_FROMSTRING, SA_HOR_CENTER); } @@ -2268,6 +2270,8 @@ private: static_assert(MAX_CARGOES >= cpp_lengthof(IndustrySpec, produced_cargo)); static_assert(MAX_CARGOES >= cpp_lengthof(IndustrySpec, accepts_cargo)); +Dimension CargoesField::legend; ///< Dimension of the legend blob. + int CargoesField::small_height; ///< Height of the header row. int CargoesField::normal_height; ///< Height of the non-header rows. int CargoesField::industry_width; ///< Width of an industry field. @@ -2283,8 +2287,6 @@ const int CargoesField::VERT_CARGO_EDGE = 4; ///< Amount of vertical spa const int CargoesField::VERT_CARGO_SPACE = 4; ///< Amount of vertical space between two connected cargoes at an industry. const int CargoesField::BLOB_DISTANCE = 5; ///< Distance of the industry legend colour from the edge of the industry box. -const int CargoesField::BLOB_WIDTH = 12; ///< Width of the industry legend colour, including border. -const int CargoesField::BLOB_HEIGHT = 9; ///< Height of the industry legend colour, including border const int CargoesField::INDUSTRY_LINE_COLOUR = PC_YELLOW; ///< Line colour of the industry type box. const int CargoesField::CARGO_LINE_COLOUR = PC_YELLOW; ///< Line colour around the cargo. @@ -2455,6 +2457,10 @@ struct IndustryCargoesWindow : public Window { d.height += WD_FRAMETEXT_TOP + WD_FRAMETEXT_BOTTOM; CargoesField::small_height = d.height; + /* Width of the legend blob -- slightly larger than the smallmap legend blob. */ + CargoesField::legend.height = FONT_HEIGHT_SMALL; + CargoesField::legend.width = CargoesField::legend.height * 8 / 5; + /* Decide about the size of the box holding the text of an industry type. */ this->ind_textsize.width = 0; this->ind_textsize.height = 0; From 055067c49cb916150912d59d827b7bd706bddcfa Mon Sep 17 00:00:00 2001 From: Peter Nelson Date: Fri, 23 Apr 2021 14:39:00 +0100 Subject: [PATCH 42/81] Fix: Scale cargo lines in industry chain window. Replaces constant pixel values with values scaled based on font size. This allows the industry chain to maintain a consistent look across different sizes. Previously all except cargo line height were fixed. --- src/industry_gui.cpp | 113 ++++++++++++++++++++++++------------------- 1 file changed, 63 insertions(+), 50 deletions(-) diff --git a/src/industry_gui.cpp b/src/industry_gui.cpp index 2188487453..aa4d86da6c 100644 --- a/src/industry_gui.cpp +++ b/src/industry_gui.cpp @@ -1852,13 +1852,13 @@ static const uint MAX_CARGOES = 16; ///< Maximum number of cargoes carried in a /** Data about a single field in the #IndustryCargoesWindow panel. */ struct CargoesField { static const int VERT_INTER_INDUSTRY_SPACE; - static const int HOR_CARGO_BORDER_SPACE; - static const int CARGO_STUB_WIDTH; - static const int HOR_CARGO_WIDTH, HOR_CARGO_SPACE; - static const int VERT_CARGO_SPACE, VERT_CARGO_EDGE; static const int BLOB_DISTANCE; static Dimension legend; + static Dimension cargo_border; + static Dimension cargo_line; + static Dimension cargo_space; + static Dimension cargo_stub; static const int INDUSTRY_LINE_COLOUR; static const int CARGO_LINE_COLOUR; @@ -2022,9 +2022,9 @@ struct CargoesField { int n = this->u.cargo.num_cargoes; if (n % 2 == 0) { - return xpos + cargo_field_width / 2 - (HOR_CARGO_WIDTH + HOR_CARGO_SPACE / 2) * (n / 2); + return xpos + cargo_field_width / 2 - (CargoesField::cargo_line.width + CargoesField::cargo_space.width / 2) * (n / 2); } else { - return xpos + cargo_field_width / 2 - HOR_CARGO_WIDTH / 2 - (HOR_CARGO_WIDTH + HOR_CARGO_SPACE) * (n / 2); + return xpos + cargo_field_width / 2 - CargoesField::cargo_line.width / 2 - (CargoesField::cargo_line.width + CargoesField::cargo_space.width) * (n / 2); } } @@ -2082,21 +2082,21 @@ struct CargoesField { other_right = this->u.industry.other_produced; other_left = this->u.industry.other_accepted; } - ypos1 += VERT_CARGO_EDGE; + ypos1 += CargoesField::cargo_border.height + (FONT_HEIGHT_NORMAL - CargoesField::cargo_line.height) / 2; for (uint i = 0; i < CargoesField::max_cargoes; i++) { if (other_right[i] != INVALID_CARGO) { const CargoSpec *csp = CargoSpec::Get(other_right[i]); - int xp = xpos + industry_width + CARGO_STUB_WIDTH; + int xp = xpos + industry_width + CargoesField::cargo_stub.width; DrawHorConnection(xpos + industry_width, xp - 1, ypos1, csp); - GfxDrawLine(xp, ypos1, xp, ypos1 + FONT_HEIGHT_NORMAL - 1, CARGO_LINE_COLOUR); + GfxDrawLine(xp, ypos1, xp, ypos1 + CargoesField::cargo_line.height - 1, CARGO_LINE_COLOUR); } if (other_left[i] != INVALID_CARGO) { const CargoSpec *csp = CargoSpec::Get(other_left[i]); - int xp = xpos - CARGO_STUB_WIDTH; + int xp = xpos - CargoesField::cargo_stub.width; DrawHorConnection(xp + 1, xpos - 1, ypos1, csp); - GfxDrawLine(xp, ypos1, xp, ypos1 + FONT_HEIGHT_NORMAL - 1, CARGO_LINE_COLOUR); + GfxDrawLine(xp, ypos1, xp, ypos1 + CargoesField::cargo_line.height - 1, CARGO_LINE_COLOUR); } - ypos1 += FONT_HEIGHT_NORMAL + VERT_CARGO_SPACE; + ypos1 += FONT_HEIGHT_NORMAL + CargoesField::cargo_space.height; } break; } @@ -2107,15 +2107,15 @@ struct CargoesField { int bot = ypos - (this->u.cargo.bottom_end ? VERT_INTER_INDUSTRY_SPACE / 2 + 1 : 0) + normal_height - 1; int colpos = cargo_base; for (int i = 0; i < this->u.cargo.num_cargoes; i++) { - if (this->u.cargo.top_end) GfxDrawLine(colpos, top - 1, colpos + HOR_CARGO_WIDTH - 1, top - 1, CARGO_LINE_COLOUR); - if (this->u.cargo.bottom_end) GfxDrawLine(colpos, bot + 1, colpos + HOR_CARGO_WIDTH - 1, bot + 1, CARGO_LINE_COLOUR); + if (this->u.cargo.top_end) GfxDrawLine(colpos, top - 1, colpos + CargoesField::cargo_line.width - 1, top - 1, CARGO_LINE_COLOUR); + if (this->u.cargo.bottom_end) GfxDrawLine(colpos, bot + 1, colpos + CargoesField::cargo_line.width - 1, bot + 1, CARGO_LINE_COLOUR); GfxDrawLine(colpos, top, colpos, bot, CARGO_LINE_COLOUR); colpos++; const CargoSpec *csp = CargoSpec::Get(this->u.cargo.vertical_cargoes[i]); - GfxFillRect(colpos, top, colpos + HOR_CARGO_WIDTH - 2, bot, csp->legend_colour, FILLRECT_OPAQUE); - colpos += HOR_CARGO_WIDTH - 2; + GfxFillRect(colpos, top, colpos + CargoesField::cargo_line.width - 2, bot, csp->legend_colour, FILLRECT_OPAQUE); + colpos += CargoesField::cargo_line.width - 2; GfxDrawLine(colpos, top, colpos, bot, CARGO_LINE_COLOUR); - colpos += 1 + HOR_CARGO_SPACE; + colpos += 1 + CargoesField::cargo_space.width; } const CargoID *hor_left, *hor_right; @@ -2126,15 +2126,15 @@ struct CargoesField { hor_left = this->u.cargo.supp_cargoes; hor_right = this->u.cargo.cust_cargoes; } - ypos += VERT_CARGO_EDGE + VERT_INTER_INDUSTRY_SPACE / 2; + ypos += CargoesField::cargo_border.height + VERT_INTER_INDUSTRY_SPACE / 2 + (FONT_HEIGHT_NORMAL - CargoesField::cargo_line.height) / 2; for (uint i = 0; i < MAX_CARGOES; i++) { if (hor_left[i] != INVALID_CARGO) { int col = hor_left[i]; int dx = 0; const CargoSpec *csp = CargoSpec::Get(this->u.cargo.vertical_cargoes[col]); for (; col > 0; col--) { - int lf = cargo_base + col * HOR_CARGO_WIDTH + (col - 1) * HOR_CARGO_SPACE; - DrawHorConnection(lf, lf + HOR_CARGO_SPACE - dx, ypos, csp); + int lf = cargo_base + col * CargoesField::cargo_line.width + (col - 1) * CargoesField::cargo_space.width; + DrawHorConnection(lf, lf + CargoesField::cargo_space.width - dx, ypos, csp); dx = 1; } DrawHorConnection(xpos, cargo_base - dx, ypos, csp); @@ -2144,26 +2144,26 @@ struct CargoesField { int dx = 0; const CargoSpec *csp = CargoSpec::Get(this->u.cargo.vertical_cargoes[col]); for (; col < this->u.cargo.num_cargoes - 1; col++) { - int lf = cargo_base + (col + 1) * HOR_CARGO_WIDTH + col * HOR_CARGO_SPACE; - DrawHorConnection(lf + dx - 1, lf + HOR_CARGO_SPACE - 1, ypos, csp); + int lf = cargo_base + (col + 1) * CargoesField::cargo_line.width + col * CargoesField::cargo_space.width; + DrawHorConnection(lf + dx - 1, lf + CargoesField::cargo_space.width - 1, ypos, csp); dx = 1; } - DrawHorConnection(cargo_base + col * HOR_CARGO_SPACE + (col + 1) * HOR_CARGO_WIDTH - 1 + dx, xpos + CargoesField::cargo_field_width - 1, ypos, csp); + DrawHorConnection(cargo_base + col * CargoesField::cargo_space.width + (col + 1) * CargoesField::cargo_line.width - 1 + dx, xpos + CargoesField::cargo_field_width - 1, ypos, csp); } - ypos += FONT_HEIGHT_NORMAL + VERT_CARGO_SPACE; + ypos += FONT_HEIGHT_NORMAL + CargoesField::cargo_space.height; } break; } case CFT_CARGO_LABEL: - ypos += VERT_CARGO_EDGE + VERT_INTER_INDUSTRY_SPACE / 2; + ypos += CargoesField::cargo_border.height + VERT_INTER_INDUSTRY_SPACE / 2; for (uint i = 0; i < MAX_CARGOES; i++) { if (this->u.cargo_label.cargoes[i] != INVALID_CARGO) { const CargoSpec *csp = CargoSpec::Get(this->u.cargo_label.cargoes[i]); DrawString(xpos + WD_FRAMERECT_LEFT, xpos + industry_width - 1 - WD_FRAMERECT_RIGHT, ypos, csp->name, TC_WHITE, (this->u.cargo_label.left_align) ? SA_LEFT : SA_RIGHT); } - ypos += FONT_HEIGHT_NORMAL + VERT_CARGO_SPACE; + ypos += FONT_HEIGHT_NORMAL + CargoesField::cargo_space.height; } break; @@ -2188,17 +2188,17 @@ struct CargoesField { uint col; for (col = 0; col < this->u.cargo.num_cargoes; col++) { if (pt.x < cpos) break; - if (pt.x < cpos + CargoesField::HOR_CARGO_WIDTH) return this->u.cargo.vertical_cargoes[col]; - cpos += CargoesField::HOR_CARGO_WIDTH + CargoesField::HOR_CARGO_SPACE; + if (pt.x < cpos + (int)CargoesField::cargo_line.width) return this->u.cargo.vertical_cargoes[col]; + cpos += CargoesField::cargo_line.width + CargoesField::cargo_space.width; } /* col = 0 -> left of first col, 1 -> left of 2nd col, ... this->u.cargo.num_cargoes right of last-col. */ - int vpos = VERT_INTER_INDUSTRY_SPACE / 2 + VERT_CARGO_EDGE; + int vpos = VERT_INTER_INDUSTRY_SPACE / 2 + CargoesField::cargo_border.width; uint row; for (row = 0; row < MAX_CARGOES; row++) { if (pt.y < vpos) return INVALID_CARGO; if (pt.y < vpos + FONT_HEIGHT_NORMAL) break; - vpos += FONT_HEIGHT_NORMAL + VERT_CARGO_SPACE; + vpos += FONT_HEIGHT_NORMAL + CargoesField::cargo_space.width; } if (row == MAX_CARGOES) return INVALID_CARGO; @@ -2240,12 +2240,12 @@ struct CargoesField { { assert(this->type == CFT_CARGO_LABEL); - int vpos = VERT_INTER_INDUSTRY_SPACE / 2 + VERT_CARGO_EDGE; + int vpos = VERT_INTER_INDUSTRY_SPACE / 2 + CargoesField::cargo_border.height; uint row; for (row = 0; row < MAX_CARGOES; row++) { if (pt.y < vpos) return INVALID_CARGO; if (pt.y < vpos + FONT_HEIGHT_NORMAL) break; - vpos += FONT_HEIGHT_NORMAL + VERT_CARGO_SPACE; + vpos += FONT_HEIGHT_NORMAL + CargoesField::cargo_space.height; } if (row == MAX_CARGOES) return INVALID_CARGO; return this->u.cargo_label.cargoes[row]; @@ -2262,15 +2262,19 @@ private: static void DrawHorConnection(int left, int right, int top, const CargoSpec *csp) { GfxDrawLine(left, top, right, top, CARGO_LINE_COLOUR); - GfxFillRect(left, top + 1, right, top + FONT_HEIGHT_NORMAL - 2, csp->legend_colour, FILLRECT_OPAQUE); - GfxDrawLine(left, top + FONT_HEIGHT_NORMAL - 1, right, top + FONT_HEIGHT_NORMAL - 1, CARGO_LINE_COLOUR); + GfxFillRect(left, top + 1, right, top + CargoesField::cargo_line.height - 2, csp->legend_colour, FILLRECT_OPAQUE); + GfxDrawLine(left, top + CargoesField::cargo_line.height - 1, right, top + CargoesField::cargo_line.height - 1, CARGO_LINE_COLOUR); } }; static_assert(MAX_CARGOES >= cpp_lengthof(IndustrySpec, produced_cargo)); static_assert(MAX_CARGOES >= cpp_lengthof(IndustrySpec, accepts_cargo)); -Dimension CargoesField::legend; ///< Dimension of the legend blob. +Dimension CargoesField::legend; ///< Dimension of the legend blob. +Dimension CargoesField::cargo_border; ///< Dimensions of border between cargo lines and industry boxes. +Dimension CargoesField::cargo_line; ///< Dimensions of cargo lines. +Dimension CargoesField::cargo_space; ///< Dimensions of space between cargo lines. +Dimension CargoesField::cargo_stub; ///< Dimensions of cargo stub (unconnected cargo line.) int CargoesField::small_height; ///< Height of the header row. int CargoesField::normal_height; ///< Height of the non-header rows. @@ -2279,13 +2283,6 @@ int CargoesField::cargo_field_width; ///< Width of a cargo field. uint CargoesField::max_cargoes; ///< Largest number of cargoes actually on any industry. const int CargoesField::VERT_INTER_INDUSTRY_SPACE = 6; ///< Amount of space between two industries in a column. -const int CargoesField::HOR_CARGO_BORDER_SPACE = 15; ///< Amount of space between the left/right edge of a #CFT_CARGO field, and the left/right most vertical cargo. -const int CargoesField::CARGO_STUB_WIDTH = 10; ///< Width of a cargo not carried in the column (should be less than #HOR_CARGO_BORDER_SPACE). -const int CargoesField::HOR_CARGO_WIDTH = 15; ///< Width of a vertical cargo column (inclusive the border line). -const int CargoesField::HOR_CARGO_SPACE = 5; ///< Amount of horizontal space between two vertical cargoes. -const int CargoesField::VERT_CARGO_EDGE = 4; ///< Amount of vertical space between top/bottom and the top/bottom connected cargo at an industry. -const int CargoesField::VERT_CARGO_SPACE = 4; ///< Amount of vertical space between two connected cargoes at an industry. - const int CargoesField::BLOB_DISTANCE = 5; ///< Distance of the industry legend colour from the edge of the industry box. const int CargoesField::INDUSTRY_LINE_COLOUR = PC_YELLOW; ///< Line colour of the industry type box. @@ -2457,10 +2454,26 @@ struct IndustryCargoesWindow : public Window { d.height += WD_FRAMETEXT_TOP + WD_FRAMETEXT_BOTTOM; CargoesField::small_height = d.height; - /* Width of the legend blob -- slightly larger than the smallmap legend blob. */ + /* Size of the legend blob -- slightly larger than the smallmap legend blob. */ CargoesField::legend.height = FONT_HEIGHT_SMALL; CargoesField::legend.width = CargoesField::legend.height * 8 / 5; + /* Size of cargo lines. */ + CargoesField::cargo_line.width = FONT_HEIGHT_NORMAL; + CargoesField::cargo_line.height = CargoesField::cargo_line.width; + + /* Size of border between cargo lines and industry boxes. */ + CargoesField::cargo_border.width = CargoesField::cargo_line.width * 3 / 2; + CargoesField::cargo_border.height = CargoesField::cargo_line.width / 2; + + /* Size of space between cargo lines. */ + CargoesField::cargo_space.width = CargoesField::cargo_line.width / 2; + CargoesField::cargo_space.height = CargoesField::cargo_line.height / 2; + + /* Size of cargo stub (unconnected cargo line.) */ + CargoesField::cargo_stub.width = CargoesField::cargo_line.width / 2; + CargoesField::cargo_stub.height = CargoesField::cargo_line.height; /* Unused */ + /* Decide about the size of the box holding the text of an industry type. */ this->ind_textsize.width = 0; this->ind_textsize.height = 0; @@ -2489,21 +2502,23 @@ struct IndustryCargoesWindow : public Window { d.width += 2 * HOR_TEXT_PADDING; /* Ensure the height is enough for the industry type text, for the horizontal connections, and for the cargo labels. */ - uint min_ind_height = CargoesField::VERT_CARGO_EDGE * 2 + CargoesField::max_cargoes * FONT_HEIGHT_NORMAL + (CargoesField::max_cargoes - 1) * CargoesField::VERT_CARGO_SPACE; + uint min_ind_height = CargoesField::cargo_border.height * 2 + CargoesField::max_cargoes * FONT_HEIGHT_NORMAL + (CargoesField::max_cargoes - 1) * CargoesField::cargo_space.height; d.height = std::max(d.height + 2 * VERT_TEXT_PADDING, min_ind_height); CargoesField::industry_width = d.width; CargoesField::normal_height = d.height + CargoesField::VERT_INTER_INDUSTRY_SPACE; /* Width of a #CFT_CARGO field. */ - CargoesField::cargo_field_width = CargoesField::HOR_CARGO_BORDER_SPACE * 2 + CargoesField::HOR_CARGO_WIDTH * CargoesField::max_cargoes + CargoesField::HOR_CARGO_SPACE * (CargoesField::max_cargoes - 1); + CargoesField::cargo_field_width = CargoesField::cargo_border.width * 2 + CargoesField::cargo_line.width * CargoesField::max_cargoes + CargoesField::cargo_space.width * (CargoesField::max_cargoes - 1); } void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize) override { switch (widget) { case WID_IC_PANEL: + resize->height = CargoesField::normal_height; size->width = WD_FRAMETEXT_LEFT + CargoesField::industry_width * 3 + CargoesField::cargo_field_width * 2 + WD_FRAMETEXT_RIGHT; + size->height = WD_FRAMETEXT_TOP + CargoesField::small_height + 2 * resize->height + WD_FRAMETEXT_BOTTOM; break; case WID_IC_IND_DROPDOWN: @@ -2756,8 +2771,7 @@ struct IndustryCargoesWindow : public Window { this->ShortenCargoColumn(1, 1, num_indrows); this->ShortenCargoColumn(3, 1, num_indrows); - const NWidgetBase *nwp = this->GetWidget(WID_IC_PANEL); - this->vscroll->SetCount(CeilDiv(WD_FRAMETEXT_TOP + WD_FRAMETEXT_BOTTOM + CargoesField::small_height + num_indrows * CargoesField::normal_height, nwp->resize_y)); + this->vscroll->SetCount(num_indrows); this->SetDirty(); this->NotifySmallmap(); } @@ -2824,8 +2838,7 @@ struct IndustryCargoesWindow : public Window { } this->ShortenCargoColumn(1, 1, num_indrows); - const NWidgetBase *nwp = this->GetWidget(WID_IC_PANEL); - this->vscroll->SetCount(CeilDiv(WD_FRAMETEXT_TOP + WD_FRAMETEXT_BOTTOM + CargoesField::small_height + num_indrows * CargoesField::normal_height, nwp->resize_y)); + this->vscroll->SetCount(num_indrows); this->SetDirty(); this->NotifySmallmap(); } @@ -3072,7 +3085,7 @@ struct IndustryCargoesWindow : public Window { void OnResize() override { - this->vscroll->SetCapacityFromWidget(this, WID_IC_PANEL); + this->vscroll->SetCapacityFromWidget(this, WID_IC_PANEL, WD_FRAMERECT_TOP + CargoesField::small_height); } }; From 49aa3924404615269986e7040455827fe7263001 Mon Sep 17 00:00:00 2001 From: Peter Nelson Date: Sun, 25 Apr 2021 13:48:00 +0100 Subject: [PATCH 43/81] Fix: Cargo legend blob in cargo payment rate window did not rescale. --- src/graph_gui.cpp | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/src/graph_gui.cpp b/src/graph_gui.cpp index 52b37d6779..c50027718a 100644 --- a/src/graph_gui.cpp +++ b/src/graph_gui.cpp @@ -879,6 +879,7 @@ void ShowCompanyValueGraph() struct PaymentRatesGraphWindow : BaseGraphWindow { uint line_height; ///< Pixel height of each cargo type row. Scrollbar *vscroll; ///< Cargo list scrollbar. + uint legend_width; ///< Width of legend 'blob'. PaymentRatesGraphWindow(WindowDesc *desc, WindowNumber window_number) : BaseGraphWindow(desc, WID_CPR_GRAPH, STR_JUST_CURRENCY_SHORT) @@ -899,6 +900,12 @@ struct PaymentRatesGraphWindow : BaseGraphWindow { this->FinishInitNested(window_number); } + void OnInit() override + { + /* Width of the legend blob. */ + this->legend_width = (FONT_HEIGHT_SMALL - ScaleFontTrad(1)) * 8 / 5; + } + void UpdateExcludedData() { this->excluded_data = 0; @@ -922,7 +929,7 @@ struct PaymentRatesGraphWindow : BaseGraphWindow { FOR_ALL_SORTED_STANDARD_CARGOSPECS(cs) { SetDParam(0, cs->name); Dimension d = GetStringBoundingBox(STR_GRAPH_CARGO_PAYMENT_CARGO); - d.width += 14; // colour field + d.width += this->legend_width + 4; // colour field d.width += WD_FRAMERECT_LEFT + WD_FRAMERECT_RIGHT; d.height += WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM; *size = maxdim(d, *size); @@ -945,6 +952,8 @@ struct PaymentRatesGraphWindow : BaseGraphWindow { int x = r.left + WD_FRAMERECT_LEFT; int y = r.top; + uint row_height = FONT_HEIGHT_SMALL; + int padding = ScaleFontTrad(1); int pos = this->vscroll->GetPosition(); int max = pos + this->vscroll->GetCapacity(); @@ -960,12 +969,12 @@ struct PaymentRatesGraphWindow : BaseGraphWindow { if (lowered) DrawFrameRect(r.left, y, r.right, y + this->line_height - 1, COLOUR_BROWN, lowered ? FR_LOWERED : FR_NONE); byte clk_dif = lowered ? 1 : 0; - int rect_x = clk_dif + (rtl ? r.right - 12 : r.left + WD_FRAMERECT_LEFT); + int rect_x = clk_dif + (rtl ? r.right - this->legend_width - WD_FRAMERECT_RIGHT : r.left + WD_FRAMERECT_LEFT); - GfxFillRect(rect_x, y + clk_dif, rect_x + 8, y + 5 + clk_dif, PC_BLACK); - GfxFillRect(rect_x + 1, y + 1 + clk_dif, rect_x + 7, y + 4 + clk_dif, cs->legend_colour); + GfxFillRect(rect_x, y + padding + clk_dif, rect_x + this->legend_width, y + row_height - 1 + clk_dif, PC_BLACK); + GfxFillRect(rect_x + 1, y + padding + 1 + clk_dif, rect_x + this->legend_width - 1, y + row_height - 2 + clk_dif, cs->legend_colour); SetDParam(0, cs->name); - DrawString(rtl ? r.left : x + 14 + clk_dif, (rtl ? r.right - 14 + clk_dif : r.right), y + clk_dif, STR_GRAPH_CARGO_PAYMENT_CARGO); + DrawString(rtl ? r.left : x + this->legend_width + 4 + clk_dif, (rtl ? r.right - this->legend_width - 4 + clk_dif : r.right), y + clk_dif, STR_GRAPH_CARGO_PAYMENT_CARGO); y += this->line_height; } From ce55cd0ce703acb3a731f7d2d783fb3462fab6c8 Mon Sep 17 00:00:00 2001 From: Peter Nelson Date: Sat, 1 May 2021 08:48:29 +0100 Subject: [PATCH 44/81] Cleanup: Use GetDefaultFontHeight() call instead of direct access. This makes this part of font size setup in FreeTypeFontCache consist with OSX and Windows variants. --- src/fontcache.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/fontcache.cpp b/src/fontcache.cpp index d450deaca6..1290777c0f 100644 --- a/src/fontcache.cpp +++ b/src/fontcache.cpp @@ -428,14 +428,14 @@ void FreeTypeFontCache::SetFontSize(FontSize fs, FT_Face face, int pixels) { if (pixels == 0) { /* Try to determine a good height based on the minimal height recommended by the font. */ - int scaled_height = ScaleFontTrad(_default_font_height[this->fs]); + int scaled_height = ScaleFontTrad(this->GetDefaultFontHeight(this->fs)); pixels = scaled_height; TT_Header *head = (TT_Header *)FT_Get_Sfnt_Table(this->face, ft_sfnt_head); if (head != nullptr) { /* Font height is minimum height plus the difference between the default * height for this font size and the small size. */ - int diff = scaled_height - ScaleFontTrad(_default_font_height[FS_SMALL]); + int diff = scaled_height - ScaleFontTrad(this->GetDefaultFontHeight(FS_SMALL)); pixels = Clamp(std::min(head->Lowest_Rec_PPEM, MAX_FONT_MIN_REC_SIZE) + diff, scaled_height, MAX_FONT_SIZE); } } else { From 9c6c0a0966f79996dfb7b5c7097c7d6c04a8d35c Mon Sep 17 00:00:00 2001 From: Peter Nelson Date: Sat, 1 May 2021 08:54:05 +0100 Subject: [PATCH 45/81] Codechange: Scale sprite font height once on init instead of every call to GetHeight(). Scaling is not expensive, but it does not change either, and this avoids the need for a virtual method call. This cascades back to all GetCharacterHeight(FS_xxx) and FONT_HEIGHT_xxx calls. --- src/fontcache.cpp | 8 ++------ src/fontcache.h | 2 +- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/src/fontcache.cpp b/src/fontcache.cpp index 1290777c0f..ce15233b7e 100644 --- a/src/fontcache.cpp +++ b/src/fontcache.cpp @@ -87,7 +87,6 @@ public: virtual void ClearFontCache(); virtual const Sprite *GetGlyph(GlyphID key); virtual uint GetGlyphWidth(GlyphID key); - virtual int GetHeight() const; virtual bool GetDrawGlyphShadow(); virtual GlyphID MapCharToGlyph(WChar key) { assert(IsPrintable(key)); return SPRITE_GLYPH | key; } virtual const void *GetFontTable(uint32 tag, size_t &length) { length = 0; return nullptr; } @@ -102,6 +101,7 @@ public: SpriteFontCache::SpriteFontCache(FontSize fs) : FontCache(fs), glyph_to_spriteid_map(nullptr) { this->InitializeUnicodeGlyphMap(); + this->height = ScaleFontTrad(this->GetDefaultFontHeight(this->fs)); } /** @@ -177,6 +177,7 @@ void SpriteFontCache::ClearGlyphToSpriteMap() void SpriteFontCache::ClearFontCache() { Layouter::ResetFontCache(this->fs); + this->height = ScaleFontTrad(this->GetDefaultFontHeight(this->fs)); } const Sprite *SpriteFontCache::GetGlyph(GlyphID key) @@ -193,11 +194,6 @@ uint SpriteFontCache::GetGlyphWidth(GlyphID key) return SpriteExists(sprite) ? GetSprite(sprite, ST_FONT)->width + ScaleFontTrad(this->fs != FS_NORMAL ? 1 : 0) : 0; } -int SpriteFontCache::GetHeight() const -{ - return ScaleFontTrad(this->height); -} - bool SpriteFontCache::GetDrawGlyphShadow() { return false; diff --git a/src/fontcache.h b/src/fontcache.h index 73675cf44e..c3b20cb4f7 100644 --- a/src/fontcache.h +++ b/src/fontcache.h @@ -45,7 +45,7 @@ public: * Get the height of the font. * @return The height of the font. */ - virtual int GetHeight() const { return this->height; } + inline int GetHeight() const { return this->height; } /** * Get the ascender value of the font. From 40528db993e6883f7856c8d860ec0385e42e3bad Mon Sep 17 00:00:00 2001 From: rubidium42 Date: Sat, 1 May 2021 10:46:47 +0200 Subject: [PATCH 46/81] Fix #9152, Fix #9153: screenshot command showed error messages when successful --- src/screenshot.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/screenshot.cpp b/src/screenshot.cpp index 06fae707a7..dc76996fbf 100644 --- a/src/screenshot.cpp +++ b/src/screenshot.cpp @@ -957,7 +957,7 @@ bool MakeScreenshot(ScreenshotType t, const char *name, uint32 width, uint32 hei if (t == SC_HEIGHTMAP) { SetDParamStr(0, _screenshot_name); SetDParam(1, _heightmap_highest_peak); - ShowErrorMessage(STR_MESSAGE_HEIGHTMAP_SUCCESSFULLY, INVALID_STRING_ID, WL_CRITICAL); + ShowErrorMessage(STR_MESSAGE_HEIGHTMAP_SUCCESSFULLY, INVALID_STRING_ID, WL_WARNING); } else { SetDParamStr(0, _screenshot_name); ShowErrorMessage(STR_MESSAGE_SCREENSHOT_SUCCESSFULLY, INVALID_STRING_ID, WL_WARNING); From 2985277becdfd390d6116afab1fb2ada0d7940b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Guilloux?= Date: Sat, 1 May 2021 12:11:07 +0200 Subject: [PATCH 47/81] Fix d4f0b6f4: [CMake] CMAKE_PROJECT_VERSION_XXX are not in CMake 3.9 (#9154) --- CMakeLists.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 87d055047f..ecfbe408c6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -74,9 +74,9 @@ add_custom_target(find_version ${CMAKE_COMMAND} -DFIND_VERSION_BINARY_DIR=${CMAKE_BINARY_DIR}/generated -DCPACK_BINARY_DIR=${CMAKE_BINARY_DIR} - -DREV_MAJOR=${CMAKE_PROJECT_VERSION_MAJOR} - -DREV_MINOR=${CMAKE_PROJECT_VERSION_MINOR} - -DREV_BUILD=${CMAKE_PROJECT_VERSION_PATCH} + -DREV_MAJOR=${PROJECT_VERSION_MAJOR} + -DREV_MINOR=${PROJECT_VERSION_MINOR} + -DREV_BUILD=${PROJECT_VERSION_PATCH} -P "${CMAKE_SOURCE_DIR}/cmake/scripts/FindVersion.cmake" WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} BYPRODUCTS ${GENERATED_SOURCE_FILES} From 0345f99180075e44d7c549e2207936a109a5451b Mon Sep 17 00:00:00 2001 From: rubidium42 Date: Sat, 1 May 2021 10:31:00 +0200 Subject: [PATCH 48/81] Feature: make the town directory horizontally resizable --- src/town_gui.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/town_gui.cpp b/src/town_gui.cpp index 0629490393..a189c7ccb3 100644 --- a/src/town_gui.cpp +++ b/src/town_gui.cpp @@ -647,12 +647,12 @@ static const NWidgetPart _nested_town_directory_widgets[] = { NWidget(NWID_HORIZONTAL), NWidget(WWT_TEXTBTN, COLOUR_BROWN, WID_TD_SORT_ORDER), SetDataTip(STR_BUTTON_SORT_BY, STR_TOOLTIP_SORT_ORDER), NWidget(WWT_DROPDOWN, COLOUR_BROWN, WID_TD_SORT_CRITERIA), SetDataTip(STR_JUST_STRING, STR_TOOLTIP_SORT_CRITERIA), - NWidget(WWT_EDITBOX, COLOUR_BROWN, WID_TD_FILTER), SetFill(1, 0), SetDataTip(STR_LIST_FILTER_OSKTITLE, STR_LIST_FILTER_TOOLTIP), + NWidget(WWT_EDITBOX, COLOUR_BROWN, WID_TD_FILTER), SetFill(1, 0), SetResize(1, 0), SetDataTip(STR_LIST_FILTER_OSKTITLE, STR_LIST_FILTER_TOOLTIP), EndContainer(), - NWidget(WWT_PANEL, COLOUR_BROWN, WID_TD_LIST), SetMinimalSize(196, 0), SetDataTip(0x0, STR_TOWN_DIRECTORY_LIST_TOOLTIP), - SetFill(1, 0), SetResize(0, 1), SetScrollbar(WID_TD_SCROLLBAR), EndContainer(), + NWidget(WWT_PANEL, COLOUR_BROWN, WID_TD_LIST), SetDataTip(0x0, STR_TOWN_DIRECTORY_LIST_TOOLTIP), + SetFill(1, 0), SetResize(1, 1), SetScrollbar(WID_TD_SCROLLBAR), EndContainer(), NWidget(WWT_PANEL, COLOUR_BROWN), - NWidget(WWT_TEXT, COLOUR_BROWN, WID_TD_WORLD_POPULATION), SetPadding(2, 0, 2, 2), SetMinimalSize(196, 0), SetMinimalTextLines(1, 0), SetFill(1, 0), SetDataTip(STR_TOWN_POPULATION, STR_NULL), + NWidget(WWT_TEXT, COLOUR_BROWN, WID_TD_WORLD_POPULATION), SetPadding(2, 0, 2, 2), SetMinimalTextLines(1, 0), SetFill(1, 0), SetResize(1, 0), SetDataTip(STR_TOWN_POPULATION, STR_NULL), EndContainer(), EndContainer(), NWidget(NWID_VERTICAL), From 282d5d302dfe81e8a312c96732bb17439a8f8264 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Guilloux?= Date: Sat, 1 May 2021 16:18:41 +0200 Subject: [PATCH 49/81] Change: [Actions] Add a 2 minutes timeout for regression test (#9166) --- .github/workflows/ci-build.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci-build.yml b/.github/workflows/ci-build.yml index 1aa7e17ccb..74677ee6d5 100644 --- a/.github/workflows/ci-build.yml +++ b/.github/workflows/ci-build.yml @@ -143,7 +143,7 @@ jobs: - name: Test run: | cd build - ctest -j $(nproc) + ctest -j $(nproc) --timeout 120 macos: name: Mac OS @@ -224,7 +224,7 @@ jobs: - name: Test run: | cd build - ctest -j $(sysctl -n hw.logicalcpu) + ctest -j $(sysctl -n hw.logicalcpu) --timeout 120 windows: name: Windows @@ -314,4 +314,4 @@ jobs: shell: bash run: | cd ${GITHUB_WORKSPACE}/build - ctest + ctest --timeout 120 From 376f2509ad5b35910ce2cb949f7ba84d86aed739 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Guilloux?= Date: Sat, 1 May 2021 16:19:14 +0200 Subject: [PATCH 50/81] Fix: Don't consider regression AIs when starting a random AI (#9164) --- regression/regression/info.nut | 1 + regression/stationlist/info.nut | 1 + 2 files changed, 2 insertions(+) diff --git a/regression/regression/info.nut b/regression/regression/info.nut index 340dcff5c3..50c9fa5146 100644 --- a/regression/regression/info.nut +++ b/regression/regression/info.nut @@ -7,6 +7,7 @@ class Regression extends AIInfo { function GetAPIVersion() { return "1.12"; } function GetDate() { return "2007-03-18"; } function CreateInstance() { return "Regression"; } + function UseAsRandomAI() { return false; } } RegisterAI(Regression()); diff --git a/regression/stationlist/info.nut b/regression/stationlist/info.nut index a58fd20ba8..0558ccc1fb 100644 --- a/regression/stationlist/info.nut +++ b/regression/stationlist/info.nut @@ -7,6 +7,7 @@ class StationList extends AIInfo { function GetAPIVersion() { return "1.12"; } function GetDate() { return "2007-03-18"; } function CreateInstance() { return "StationList"; } + function UseAsRandomAI() { return false; } } RegisterAI(StationList()); From 3bd416bfdba351d9d76a753a0c5b4abdd84fea39 Mon Sep 17 00:00:00 2001 From: rubidium42 Date: Sat, 1 May 2021 13:54:30 +0200 Subject: [PATCH 51/81] Change: [Console] Show help when passing invalid company number --- src/console_cmds.cpp | 6 ++--- src/network/network.cpp | 41 ++++++++++++++++++++++++++++------ src/network/network_func.h | 2 +- src/network/network_internal.h | 2 +- 4 files changed, 38 insertions(+), 13 deletions(-) diff --git a/src/console_cmds.cpp b/src/console_cmds.cpp index 401a52b1f9..71f2d4b7b0 100644 --- a/src/console_cmds.cpp +++ b/src/console_cmds.cpp @@ -901,8 +901,7 @@ DEF_CONSOLE_CMD(ConNetworkReconnect) /* Don't resolve the address first, just print it directly as it comes from the config file. */ IConsolePrintF(CC_DEFAULT, "Reconnecting to %s ...", _settings_client.network.last_joined); - NetworkClientConnectGame(_settings_client.network.last_joined, playas); - return true; + return NetworkClientConnectGame(_settings_client.network.last_joined, playas); } DEF_CONSOLE_CMD(ConNetworkConnect) @@ -917,8 +916,7 @@ DEF_CONSOLE_CMD(ConNetworkConnect) if (argc < 2) return false; if (_networking) NetworkDisconnect(); // we are in network-mode, first close it! - NetworkClientConnectGame(argv[1], COMPANY_NEW_COMPANY); - return true; + return NetworkClientConnectGame(argv[1], COMPANY_NEW_COMPANY); } /********************************* diff --git a/src/network/network.cpp b/src/network/network.cpp index c31a67487d..22ac5dc74c 100644 --- a/src/network/network.cpp +++ b/src/network/network.cpp @@ -713,7 +713,24 @@ public: } }; -void NetworkClientConnectGame(const std::string &connection_string, CompanyID default_company, const char *join_server_password, const char *join_company_password) +/** + * Join a client to the server at with the given connection string. + * The default for the passwords is \c nullptr. When the server or company needs a + * password and none is given, the user is asked to enter the password in the GUI. + * This function will return false whenever some information required to join is not + * correct such as the company number or the client's name, or when there is not + * networking avalabile at all. If the function returns false the connection with + * the existing server is not disconnected. + * It will return true when it starts the actual join process, i.e. when it + * actually shows the join status window. + * + * @param connection_string The IP address, port and company number to join as. + * @param default_company The company number to join as when none is given. + * @param join_server_password The password for the server. + * @param join_company_password The password for the company. + * @return Whether the join has started. + */ +bool NetworkClientConnectGame(const std::string &connection_string, CompanyID default_company, const char *join_server_password, const char *join_company_password) { CompanyID join_as = default_company; NetworkAddress address = ParseGameConnectionString(&join_as, connection_string, NETWORK_DEFAULT_PORT); @@ -721,18 +738,27 @@ void NetworkClientConnectGame(const std::string &connection_string, CompanyID de if (join_as != COMPANY_NEW_COMPANY && join_as != COMPANY_SPECTATOR) { join_as--; if (join_as >= MAX_COMPANIES) { - return; + return false; } } - NetworkClientConnectGame(address, join_as, join_server_password, join_company_password); + return NetworkClientConnectGame(address, join_as, join_server_password, join_company_password); } -/* Used by clients, to connect to a server */ -void NetworkClientConnectGame(NetworkAddress &address, CompanyID join_as, const char *join_server_password, const char *join_company_password) +/** + * Join a client to the server at the given address. + * See the overloaded NetworkClientConnectGame for more details. + * + * @param address The network address of the server to join to. + * @param join_as The company number to join as. + * @param join_server_password The password for the server. + * @param join_company_password The password for the company. + * @return Whether the join has started. + */ +bool NetworkClientConnectGame(NetworkAddress &address, CompanyID join_as, const char *join_server_password, const char *join_company_password) { - if (!_network_available) return; - if (!NetworkValidateClientName()) return; + if (!_network_available) return false; + if (!NetworkValidateClientName()) return false; strecpy(_settings_client.network.last_joined, address.GetAddressAsString(false).c_str(), lastof(_settings_client.network.last_joined)); @@ -747,6 +773,7 @@ void NetworkClientConnectGame(NetworkAddress &address, CompanyID join_as, const ShowJoinStatusWindow(); new TCPClientConnecter(address); + return true; } static void NetworkInitGameInfo() diff --git a/src/network/network_func.h b/src/network/network_func.h index cd2b291bc7..252d207db5 100644 --- a/src/network/network_func.h +++ b/src/network/network_func.h @@ -51,7 +51,7 @@ void NetworkPopulateCompanyStats(NetworkCompanyStats *stats); void NetworkUpdateClientInfo(ClientID client_id); void NetworkClientsToSpectators(CompanyID cid); -void NetworkClientConnectGame(const std::string &connection_string, CompanyID default_company, const char *join_server_password = nullptr, const char *join_company_password = nullptr); +bool NetworkClientConnectGame(const std::string &connection_string, CompanyID default_company, const char *join_server_password = nullptr, const char *join_company_password = nullptr); void NetworkClientRequestMove(CompanyID company, const char *pass = ""); void NetworkClientSendRcon(const char *password, const char *command); void NetworkClientSendChat(NetworkAction action, DestType type, int dest, const char *msg, int64 data = 0); diff --git a/src/network/network_internal.h b/src/network/network_internal.h index 683c954e82..be5b74a0f5 100644 --- a/src/network/network_internal.h +++ b/src/network/network_internal.h @@ -119,7 +119,7 @@ StringID GetNetworkErrorMsg(NetworkErrorCode err); bool NetworkFindName(char *new_name, const char *last); const char *GenerateCompanyPasswordHash(const char *password, const char *password_server_id, uint32 password_game_seed); -void NetworkClientConnectGame(NetworkAddress &address, CompanyID join_as, const char *join_server_password = nullptr, const char *join_company_password = nullptr); +bool NetworkClientConnectGame(NetworkAddress &address, CompanyID join_as, const char *join_server_password = nullptr, const char *join_company_password = nullptr); NetworkAddress ParseConnectionString(const std::string &connection_string, int default_port); NetworkAddress ParseGameConnectionString(CompanyID *company, const std::string &connection_string, int default_port); From 39c51c35f4566553ec3c6c273c2642d796070521 Mon Sep 17 00:00:00 2001 From: rubidium42 Date: Sat, 1 May 2021 13:55:39 +0200 Subject: [PATCH 52/81] Fix #6598: Do not disconnect before company number validation NetworkClientConnectGame already does a NetworkDisconnect, so no reason to do it here --- src/console_cmds.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/console_cmds.cpp b/src/console_cmds.cpp index 71f2d4b7b0..69a44e63de 100644 --- a/src/console_cmds.cpp +++ b/src/console_cmds.cpp @@ -914,7 +914,6 @@ DEF_CONSOLE_CMD(ConNetworkConnect) } if (argc < 2) return false; - if (_networking) NetworkDisconnect(); // we are in network-mode, first close it! return NetworkClientConnectGame(argv[1], COMPANY_NEW_COMPANY); } From 83985fe26f1ad87df709f122f74e5d50485095fa Mon Sep 17 00:00:00 2001 From: rubidium42 Date: Sat, 1 May 2021 14:21:33 +0200 Subject: [PATCH 53/81] Codechange: Move join information into a single structure --- src/network/network.cpp | 6 +++--- src/network/network_client.cpp | 21 ++++++++------------- src/network/network_client.h | 11 ++++++++--- 3 files changed, 19 insertions(+), 19 deletions(-) diff --git a/src/network/network.cpp b/src/network/network.cpp index 22ac5dc74c..32fc4dec95 100644 --- a/src/network/network.cpp +++ b/src/network/network.cpp @@ -762,9 +762,9 @@ bool NetworkClientConnectGame(NetworkAddress &address, CompanyID join_as, const strecpy(_settings_client.network.last_joined, address.GetAddressAsString(false).c_str(), lastof(_settings_client.network.last_joined)); - _network_join_as = join_as; - _network_join_server_password = join_server_password; - _network_join_company_password = join_company_password; + _network_join.company = join_as; + _network_join.server_password = join_server_password; + _network_join.company_password = join_company_password; NetworkDisconnect(); NetworkInitialize(); diff --git a/src/network/network_client.cpp b/src/network/network_client.cpp index f73c8be528..ceb1463336 100644 --- a/src/network/network_client.cpp +++ b/src/network/network_client.cpp @@ -326,13 +326,8 @@ static uint8 _network_server_max_companies; /** Maximum number of spectators of the currently joined server. */ static uint8 _network_server_max_spectators; -/** Who would we like to join as. */ -CompanyID _network_join_as; - -/** Login password from -p argument */ -const char *_network_join_server_password = nullptr; -/** Company password from -P argument */ -const char *_network_join_company_password = nullptr; +/** Information about the game to join to. */ +NetworkJoinInfo _network_join; /** Make sure the server ID length is the same as a md5 hash. */ static_assert(NETWORK_SERVER_ID_LENGTH == 16 * 2 + 1); @@ -372,7 +367,7 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::SendJoin() p->Send_string(GetNetworkRevisionString()); p->Send_uint32(_openttd_newgrf_version); p->Send_string(_settings_client.network.client_name); // Client name - p->Send_uint8 (_network_join_as); // PlayAs + p->Send_uint8 (_network_join.company); // PlayAs p->Send_uint8 (0); // Used to be language my_client->SendPacket(p); return NETWORK_RECV_STATUS_OKAY; @@ -804,7 +799,7 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_NEED_GAME_PASSW if (this->status < STATUS_JOIN || this->status >= STATUS_AUTH_GAME) return NETWORK_RECV_STATUS_MALFORMED_PACKET; this->status = STATUS_AUTH_GAME; - const char *password = _network_join_server_password; + const char *password = _network_join.server_password; if (!StrEmpty(password)) { return SendGamePassword(password); } @@ -823,7 +818,7 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_NEED_COMPANY_PA p->Recv_string(_password_server_id, sizeof(_password_server_id)); if (this->HasClientQuit()) return NETWORK_RECV_STATUS_MALFORMED_PACKET; - const char *password = _network_join_company_password; + const char *password = _network_join.company_password; if (!StrEmpty(password)) { return SendCompanyPassword(password); } @@ -945,10 +940,10 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_MAP_DONE(Packet /* New company/spectator (invalid company) or company we want to join is not active * Switch local company to spectator and await the server's judgement */ - if (_network_join_as == COMPANY_NEW_COMPANY || !Company::IsValidID(_network_join_as)) { + if (_network_join.company == COMPANY_NEW_COMPANY || !Company::IsValidID(_network_join.company)) { SetLocalCompany(COMPANY_SPECTATOR); - if (_network_join_as != COMPANY_SPECTATOR) { + if (_network_join.company != COMPANY_SPECTATOR) { /* We have arrived and ready to start playing; send a command to make a new company; * the server will give us a client-id and let us in */ _network_join_status = NETWORK_JOIN_STATUS_REGISTERING; @@ -957,7 +952,7 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_MAP_DONE(Packet } } else { /* take control over an existing company */ - SetLocalCompany(_network_join_as); + SetLocalCompany(_network_join.company); } return NETWORK_RECV_STATUS_OKAY; diff --git a/src/network/network_client.h b/src/network/network_client.h index 40b8eedf92..28d2d00214 100644 --- a/src/network/network_client.h +++ b/src/network/network_client.h @@ -112,9 +112,14 @@ typedef ClientNetworkGameSocketHandler MyClient; void NetworkClient_Connected(); void NetworkClientSetCompanyPassword(const char *password); -extern CompanyID _network_join_as; +/** Information required to join a server. */ +struct NetworkJoinInfo { + NetworkJoinInfo() : company(COMPANY_SPECTATOR), server_password(nullptr), company_password(nullptr) {} + CompanyID company; ///< The company to join. + const char *server_password; ///< The password of the server to join. + const char *company_password; ///< The password of the company to join. +}; -extern const char *_network_join_server_password; -extern const char *_network_join_company_password; +extern NetworkJoinInfo _network_join; #endif /* NETWORK_CLIENT_H */ From 05394d5216f89c9a7e14487b571515e510828657 Mon Sep 17 00:00:00 2001 From: rubidium42 Date: Sat, 1 May 2021 14:41:25 +0200 Subject: [PATCH 54/81] Fix #6598: Prevent invalid memory accesses when abandoning a join from within a network game One could join a network game from within an already running network game. This would call a NetworkDisconnect, but keeps the UI alive. If, during that process the join is aborted, e.g. by cancelling on a password dialog, you would still be in your network game but also get shown the server list. Solve all the underlying problems by falling back to the main UI when (re)connecting to a(nother) server. --- src/network/network.cpp | 28 ++++++++++++++++++++++++---- src/network/network_client.h | 1 + src/network/network_func.h | 1 + src/openttd.cpp | 5 +++++ src/openttd.h | 1 + 5 files changed, 32 insertions(+), 4 deletions(-) diff --git a/src/network/network.cpp b/src/network/network.cpp index 32fc4dec95..ce79f4c514 100644 --- a/src/network/network.cpp +++ b/src/network/network.cpp @@ -760,20 +760,40 @@ bool NetworkClientConnectGame(NetworkAddress &address, CompanyID join_as, const if (!_network_available) return false; if (!NetworkValidateClientName()) return false; - strecpy(_settings_client.network.last_joined, address.GetAddressAsString(false).c_str(), lastof(_settings_client.network.last_joined)); - + _network_join.address = address; _network_join.company = join_as; _network_join.server_password = join_server_password; _network_join.company_password = join_company_password; + if (_game_mode == GM_MENU) { + /* From the menu we can immediately continue with the actual join. */ + NetworkClientJoinGame(); + } else { + /* When already playing a game, first go back to the main menu. This + * disconnects the user from the current game, meaning we can safely + * load in the new. After all, there is little point in continueing to + * play on a server if we are connecting to another one. + */ + _switch_mode = SM_JOIN_GAME; + } + return true; +} + +/** + * Actually perform the joining to the server. Use #NetworkClientConnectGame + * when you want to connect to a specific server/company. This function + * assumes _network_join is already fully set up. + */ +void NetworkClientJoinGame() +{ NetworkDisconnect(); NetworkInitialize(); + strecpy(_settings_client.network.last_joined, _network_join.address.GetAddressAsString(false).c_str(), lastof(_settings_client.network.last_joined)); _network_join_status = NETWORK_JOIN_STATUS_CONNECTING; ShowJoinStatusWindow(); - new TCPClientConnecter(address); - return true; + new TCPClientConnecter(_network_join.address); } static void NetworkInitGameInfo() diff --git a/src/network/network_client.h b/src/network/network_client.h index 28d2d00214..81d5b720cd 100644 --- a/src/network/network_client.h +++ b/src/network/network_client.h @@ -115,6 +115,7 @@ void NetworkClientSetCompanyPassword(const char *password); /** Information required to join a server. */ struct NetworkJoinInfo { NetworkJoinInfo() : company(COMPANY_SPECTATOR), server_password(nullptr), company_password(nullptr) {} + NetworkAddress address; ///< The address of the server to join. CompanyID company; ///< The company to join. const char *server_password; ///< The password of the server to join. const char *company_password; ///< The password of the company to join. diff --git a/src/network/network_func.h b/src/network/network_func.h index 252d207db5..5f3e27c12f 100644 --- a/src/network/network_func.h +++ b/src/network/network_func.h @@ -52,6 +52,7 @@ void NetworkPopulateCompanyStats(NetworkCompanyStats *stats); void NetworkUpdateClientInfo(ClientID client_id); void NetworkClientsToSpectators(CompanyID cid); bool NetworkClientConnectGame(const std::string &connection_string, CompanyID default_company, const char *join_server_password = nullptr, const char *join_company_password = nullptr); +void NetworkClientJoinGame(); void NetworkClientRequestMove(CompanyID company, const char *pass = ""); void NetworkClientSendRcon(const char *password, const char *command); void NetworkClientSendChat(NetworkAction action, DestType type, int dest, const char *msg, int64 data = 0); diff --git a/src/openttd.cpp b/src/openttd.cpp index fbeeba793e..234e3a4197 100644 --- a/src/openttd.cpp +++ b/src/openttd.cpp @@ -1068,6 +1068,11 @@ void SwitchToMode(SwitchMode new_mode) break; } + case SM_JOIN_GAME: // Join a multiplayer game + LoadIntroGame(); + NetworkClientJoinGame(); + break; + case SM_MENU: // Switch to game intro menu LoadIntroGame(); if (BaseSounds::ini_set.empty() && BaseSounds::GetUsedSet()->fallback && SoundDriver::GetInstance()->HasOutput()) { diff --git a/src/openttd.h b/src/openttd.h index 77fafab1d1..2cd9cc1f09 100644 --- a/src/openttd.h +++ b/src/openttd.h @@ -36,6 +36,7 @@ enum SwitchMode { SM_START_HEIGHTMAP, ///< Load a heightmap and start a new game from it. SM_LOAD_HEIGHTMAP, ///< Load heightmap from scenario editor. SM_RESTART_HEIGHTMAP, ///< Load a heightmap and start a new game from it with current settings. + SM_JOIN_GAME, ///< Join a network game. }; /** Display Options */ From 520595ff87bc55042010a370ef1070f296ff07c0 Mon Sep 17 00:00:00 2001 From: Matt Kimber Date: Sat, 1 May 2021 18:14:50 +0100 Subject: [PATCH 55/81] Fix 3d7ab09: stopped trains not updating viewport hash when reversed for a second time (#9165) --- src/vehicle.cpp | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/vehicle.cpp b/src/vehicle.cpp index a9af24c55a..90accdd80e 100644 --- a/src/vehicle.cpp +++ b/src/vehicle.cpp @@ -1639,13 +1639,19 @@ void Vehicle::UpdateBoundingBoxCoordinates(bool update_cache) const */ void Vehicle::UpdateViewport(bool dirty) { - Rect old_coord = this->sprite_cache.old_coord; + /* If the existing cache is invalid we should ignore it, as it will be set to the current coords by UpdateBoundingBoxCoordinates */ + bool ignore_cached_coords = this->sprite_cache.old_coord.left == INVALID_COORD; this->UpdateBoundingBoxCoordinates(true); - UpdateVehicleViewportHash(this, this->coord.left, this->coord.top, old_coord.left, old_coord.top); + + if (ignore_cached_coords) { + UpdateVehicleViewportHash(this, this->coord.left, this->coord.top, INVALID_COORD, INVALID_COORD); + } else { + UpdateVehicleViewportHash(this, this->coord.left, this->coord.top, this->sprite_cache.old_coord.left, this->sprite_cache.old_coord.top); + } if (dirty) { - if (old_coord.left == INVALID_COORD) { + if (ignore_cached_coords) { this->sprite_cache.is_viewport_candidate = this->MarkAllViewportsDirty(); } else { this->sprite_cache.is_viewport_candidate = ::MarkAllViewportsDirty( From 0eb17a70af86d11e49d9560088900c9d65cb07c1 Mon Sep 17 00:00:00 2001 From: rubidium42 Date: Fri, 30 Apr 2021 15:39:46 +0200 Subject: [PATCH 57/81] Codechange: rename NetworkError to ShowNetworkError --- src/network/core/tcp_listen.h | 2 +- src/network/network.cpp | 4 ++-- src/network/network_client.cpp | 2 +- src/network/network_internal.h | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/network/core/tcp_listen.h b/src/network/core/tcp_listen.h index 168f49f947..2ceea20aa9 100644 --- a/src/network/core/tcp_listen.h +++ b/src/network/core/tcp_listen.h @@ -151,7 +151,7 @@ public: if (sockets.size() == 0) { DEBUG(net, 0, "[server] could not start network: could not create listening socket"); - NetworkError(STR_NETWORK_ERROR_SERVER_START); + ShowNetworkError(STR_NETWORK_ERROR_SERVER_START); return false; } diff --git a/src/network/network.cpp b/src/network/network.cpp index ce79f4c514..af732facdb 100644 --- a/src/network/network.cpp +++ b/src/network/network.cpp @@ -277,7 +277,7 @@ uint NetworkCalculateLag(const NetworkClientSocket *cs) /* There was a non-recoverable error, drop back to the main menu with a nice * error */ -void NetworkError(StringID error_string) +void ShowNetworkError(StringID error_string) { _switch_mode = SM_MENU; ShowErrorMessage(error_string, INVALID_STRING_ID, WL_CRITICAL); @@ -701,7 +701,7 @@ public: void OnFailure() override { - NetworkError(STR_NETWORK_ERROR_NOCONNECTION); + ShowNetworkError(STR_NETWORK_ERROR_NOCONNECTION); } void OnConnect(SOCKET s) override diff --git a/src/network/network_client.cpp b/src/network/network_client.cpp index ceb1463336..d288c71a6e 100644 --- a/src/network/network_client.cpp +++ b/src/network/network_client.cpp @@ -284,7 +284,7 @@ void ClientNetworkGameSocketHandler::ClientError(NetworkRecvStatus res) #else if (_sync_seed_1 != _random.state[0]) { #endif - NetworkError(STR_NETWORK_ERROR_DESYNC); + ShowNetworkError(STR_NETWORK_ERROR_DESYNC); DEBUG(desync, 1, "sync_err: %08x; %02x", _date, _date_fract); DEBUG(net, 0, "Sync error detected!"); my_client->ClientError(NETWORK_RECV_STATUS_DESYNC); diff --git a/src/network/network_internal.h b/src/network/network_internal.h index be5b74a0f5..1fba1228a5 100644 --- a/src/network/network_internal.h +++ b/src/network/network_internal.h @@ -112,7 +112,7 @@ void NetworkExecuteLocalCommandQueue(); void NetworkFreeLocalCommandQueue(); void NetworkSyncCommandQueue(NetworkClientSocket *cs); -void NetworkError(StringID error_string); +void ShowNetworkError(StringID error_string); void NetworkTextMessage(NetworkAction action, TextColour colour, bool self_send, const char *name, const char *str = "", int64 data = 0); uint NetworkCalculateLag(const NetworkClientSocket *cs); StringID GetNetworkErrorMsg(NetworkErrorCode err); From 22720332eb9922e20148c7aae1127f7304f6f7d3 Mon Sep 17 00:00:00 2001 From: rubidium42 Date: Fri, 30 Apr 2021 15:38:22 +0200 Subject: [PATCH 58/81] Codechange: encapsulate network error handling --- src/network/core/CMakeLists.txt | 1 + src/network/core/address.cpp | 24 +++--- src/network/core/core.cpp | 18 ---- src/network/core/os_abstraction.cpp | 125 ++++++++++++++++++++++++++++ src/network/core/os_abstraction.h | 52 +++++------- src/network/core/tcp.cpp | 22 ++--- src/network/core/tcp_http.cpp | 8 +- src/network/core/tcp_listen.h | 4 +- src/network/core/udp.cpp | 4 +- src/safeguards.h | 8 +- 10 files changed, 183 insertions(+), 83 deletions(-) create mode 100644 src/network/core/os_abstraction.cpp diff --git a/src/network/core/CMakeLists.txt b/src/network/core/CMakeLists.txt index 37cc3e1954..bf713be99c 100644 --- a/src/network/core/CMakeLists.txt +++ b/src/network/core/CMakeLists.txt @@ -8,6 +8,7 @@ add_files( game_info.h host.cpp host.h + os_abstraction.cpp os_abstraction.h packet.cpp packet.h diff --git a/src/network/core/address.cpp b/src/network/core/address.cpp index e91751c33f..d3e373ef76 100644 --- a/src/network/core/address.cpp +++ b/src/network/core/address.cpp @@ -317,7 +317,7 @@ static SOCKET ConnectLoopProc(addrinfo *runp) 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, NetworkGetLastErrorString()); + DEBUG(net, 1, "[%s] could not create %s socket: %s", type, family, NetworkError::GetLast().AsString()); return INVALID_SOCKET; } @@ -326,8 +326,8 @@ static SOCKET ConnectLoopProc(addrinfo *runp) 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 && NetworkGetLastError() != EINPROGRESS) { - DEBUG(net, 1, "[%s] could not connect to %s over %s: %s", type, address.c_str(), family, NetworkGetLastErrorString()); + 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; } @@ -343,7 +343,7 @@ static SOCKET ConnectLoopProc(addrinfo *runp) 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(), NetworkGetLastErrorString()); + DEBUG(net, 1, "[%s] could not connect to %s: %s", type, address.c_str(), NetworkError::GetLast().AsString()); closesocket(sock); return INVALID_SOCKET; } @@ -356,9 +356,9 @@ static SOCKET ConnectLoopProc(addrinfo *runp) } /* Retrieve last error, if any, on the socket. */ - err = GetSocketError(sock); - if (err != 0) { - DEBUG(net, 1, "[%s] could not connect to %s: %s", type, address.c_str(), NetworkGetErrorString(err)); + 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; } @@ -393,7 +393,7 @@ static SOCKET ListenLoopProc(addrinfo *runp) 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(), NetworkGetLastErrorString()); + DEBUG(net, 0, "[%s] could not create %s socket on port %s: %s", type, family, address.c_str(), NetworkError::GetLast().AsString()); return INVALID_SOCKET; } @@ -404,24 +404,24 @@ static SOCKET ListenLoopProc(addrinfo *runp) 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(), NetworkGetLastErrorString()); + DEBUG(net, 3, "[%s] could not set reusable %s sockets for port %s: %s", type, family, address.c_str(), 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(), NetworkGetLastErrorString()); + DEBUG(net, 3, "[%s] could not disable IPv4 over IPv6 on port %s: %s", type, address.c_str(), 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(), NetworkGetLastErrorString()); + DEBUG(net, 1, "[%s] could not bind on %s port %s: %s", type, family, 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(), NetworkGetLastErrorString()); + DEBUG(net, 1, "[%s] could not listen at %s port %s: %s", type, family, address.c_str(), NetworkError::GetLast().AsString()); closesocket(sock); return INVALID_SOCKET; } diff --git a/src/network/core/core.cpp b/src/network/core/core.cpp index 5c12cb2242..563deae963 100644 --- a/src/network/core/core.cpp +++ b/src/network/core/core.cpp @@ -13,7 +13,6 @@ #include "../../debug.h" #include "os_abstraction.h" #include "packet.h" -#include "../../string_func.h" #include "../../safeguards.h" @@ -48,20 +47,3 @@ void NetworkCoreShutdown() WSACleanup(); #endif } - -#if defined(_WIN32) -/** - * Return the string representation of the given error from the OS's network functions. - * @param error The error number (from \c NetworkGetLastError()). - * @return The error message, potentially an empty string but never \c nullptr. - */ -const char *NetworkGetErrorString(int error) -{ - static char buffer[512]; - if (FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, error, - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), buffer, sizeof(buffer), NULL) == 0) { - seprintf(buffer, lastof(buffer), "Unknown error %d", error); - } - return buffer; -} -#endif /* defined(_WIN32) */ diff --git a/src/network/core/os_abstraction.cpp b/src/network/core/os_abstraction.cpp new file mode 100644 index 0000000000..75f2224eb0 --- /dev/null +++ b/src/network/core/os_abstraction.cpp @@ -0,0 +1,125 @@ +/* + * 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 os_abstraction.cpp OS specific implementations of functions of the OS abstraction layer for network stuff. + * + * The general idea is to have simple abstracting functions for things that + * require different implementations for different environments. + * In here the functions, and their documentation, are defined only once + * and the implementation contains the #ifdefs to change the implementation. + * Since Windows is usually different that is usually the first case, after + * that the behaviour is usually Unix/BSD-like with occasional variation. + */ + +#include "stdafx.h" +#include "os_abstraction.h" +#include "../../string_func.h" +#include + +#include "../../safeguards.h" + +/** + * Construct the network error with the given error code. + * @param error The error code. + */ +NetworkError::NetworkError(int error) : error(error) +{ +} + +/** + * Check whether this error describes that the operation would block. + * @return True iff the operation would block. + */ +bool NetworkError::WouldBlock() const +{ +#if defined(_WIN32) + return this->error == WSAEWOULDBLOCK; +#else + /* Usually EWOULDBLOCK and EAGAIN are the same, but sometimes they are not + * and the POSIX.1 specification states that either should be checked. */ + return this->error == EWOULDBLOCK || this->error == EAGAIN; +#endif +} + +/** + * Check whether this error describes a connection reset. + * @return True iff the connection is reset. + */ +bool NetworkError::IsConnectionReset() const +{ +#if defined(_WIN32) + return this->error == WSAECONNRESET; +#else + return this->error == ECONNRESET; +#endif +} + +/** + * Check whether this error describes a connect is in progress. + * @return True iff the connect is already in progress. + */ +bool NetworkError::IsConnectInProgress() const +{ +#if defined(_WIN32) + return this->error == WSAEWOULDBLOCK; +#else + return this->error == EINPROGRESS; +#endif +} + +/** + * Get the string representation of the error message. + * @return The string representation that will get overwritten by next calls. + */ +const char *NetworkError::AsString() const +{ + if (this->message.empty()) { +#if defined(_WIN32) + char buffer[512]; + if (FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, this->error, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), buffer, sizeof(buffer), NULL) == 0) { + seprintf(buffer, lastof(buffer), "Unknown error %d", this->error); + } + this->message.assign(buffer); +#else + /* Make strerror thread safe by locking access to it. There is a thread safe strerror_r, however + * the non-POSIX variant is available due to defining _GNU_SOURCE meaning it is not portable. + * The problem with the non-POSIX variant is that it does not necessarily fill the buffer with + * the error message but can also return a pointer to a static bit of memory, whereas the POSIX + * variant always fills the buffer. This makes the behaviour too erratic to work with. */ + static std::mutex mutex; + std::lock_guard guard(mutex); + this->message.assign(strerror(this->error)); +#endif + } + return this->message.c_str(); +} + +/** + * Check whether an error was actually set. + * @return True iff an error was set. + */ +bool NetworkError::HasError() const +{ + return this->error != 0; +} + +/** + * Get the last network error. + * @return The network error. + */ +/* static */ NetworkError NetworkError::GetLast() +{ +#if defined(_WIN32) + return NetworkError(WSAGetLastError()); +#elif defined(__OS2__) + return NetworkError(sock_errno()); +#else + return NetworkError(errno); +#endif +} diff --git a/src/network/core/os_abstraction.h b/src/network/core/os_abstraction.h index 9bd0e321f7..e444bc78b4 100644 --- a/src/network/core/os_abstraction.h +++ b/src/network/core/os_abstraction.h @@ -14,6 +14,26 @@ #ifndef NETWORK_CORE_OS_ABSTRACTION_H #define NETWORK_CORE_OS_ABSTRACTION_H +/** + * Abstraction of a network error where all implementation details of the + * error codes are encapsulated in this class and the abstraction layer. + */ +class NetworkError { +private: + int error; ///< The underlying error number from errno or WSAGetLastError. + mutable std::string message; ///< The string representation of the error (set on first call to #AsString). +public: + NetworkError(int error); + + bool HasError() const; + bool WouldBlock() const; + bool IsConnectionReset() const; + bool IsConnectInProgress() const; + const char *AsString() const; + + static NetworkError GetLast(); +}; + /* Include standard stuff per OS */ /* Windows stuff */ @@ -23,21 +43,6 @@ #include #include -/** - * Get the last error code from any of the OS's network functions. - * What it returns and when it is reset, is implementation defined. - * @return The last error code. - */ -#define NetworkGetLastError() WSAGetLastError() -#undef EWOULDBLOCK -#define EWOULDBLOCK WSAEWOULDBLOCK -#undef ECONNRESET -#define ECONNRESET WSAECONNRESET -#undef EINPROGRESS -#define EINPROGRESS WSAEWOULDBLOCK - -const char *NetworkGetErrorString(int error); - /* Windows has some different names for some types */ typedef unsigned long in_addr_t; @@ -63,8 +68,6 @@ typedef unsigned long in_addr_t; # define INVALID_SOCKET -1 # define ioctlsocket ioctl # define closesocket close -# define NetworkGetLastError() (errno) -# define NetworkGetErrorString(error) (strerror(error)) /* Need this for FIONREAD on solaris */ # define BSD_COMP @@ -114,8 +117,6 @@ typedef unsigned long in_addr_t; # define INVALID_SOCKET -1 # define ioctlsocket ioctl # define closesocket close -# define NetworkGetLastError() (sock_errno()) -# define NetworkGetErrorString(error) (strerror(error)) /* Includes needed for OS/2 systems */ # include @@ -187,15 +188,6 @@ static inline socklen_t FixAddrLenForEmscripten(struct sockaddr_storage &address } #endif -/** - * Return the string representation of the last error from the OS's network functions. - * @return The error message, potentially an empty string but never \c nullptr. - */ -static inline const char *NetworkGetLastErrorString() -{ - return NetworkGetErrorString(NetworkGetLastError()); -} - /** * Try to set the socket into non-blocking mode. * @param d The socket to set the non-blocking more for. @@ -237,13 +229,13 @@ static inline bool SetNoDelay(SOCKET d) * @param d The socket to get the error from. * @return The errno on the socket. */ -static inline int GetSocketError(SOCKET d) +static inline NetworkError GetSocketError(SOCKET d) { int err; socklen_t len = sizeof(err); getsockopt(d, SOL_SOCKET, SO_ERROR, (char *)&err, &len); - return err; + return NetworkError(err); } /* Make sure these structures have the size we expect them to be */ diff --git a/src/network/core/tcp.cpp b/src/network/core/tcp.cpp index f23b202c8b..842e1a89b9 100644 --- a/src/network/core/tcp.cpp +++ b/src/network/core/tcp.cpp @@ -86,11 +86,11 @@ SendPacketsState NetworkTCPSocketHandler::SendPackets(bool closing_down) while ((p = this->packet_queue) != nullptr) { res = p->TransferOut(send, this->sock, 0); if (res == -1) { - int err = NetworkGetLastError(); - if (err != EWOULDBLOCK) { + NetworkError err = NetworkError::GetLast(); + if (!err.WouldBlock()) { /* Something went wrong.. close client! */ if (!closing_down) { - DEBUG(net, 0, "send failed with error %s", NetworkGetErrorString(err)); + DEBUG(net, 0, "send failed with error %s", err.AsString()); this->CloseConnection(); } return SPS_CLOSED; @@ -136,10 +136,10 @@ Packet *NetworkTCPSocketHandler::ReceivePacket() while (p->RemainingBytesToTransfer() != 0) { res = p->TransferIn(recv, this->sock, 0); if (res == -1) { - int err = NetworkGetLastError(); - if (err != EWOULDBLOCK) { - /* Something went wrong... (ECONNRESET is connection reset by peer) */ - if (err != ECONNRESET) DEBUG(net, 0, "recv failed with error %s", NetworkGetErrorString(err)); + NetworkError err = NetworkError::GetLast(); + if (!err.WouldBlock()) { + /* Something went wrong... */ + if (!err.IsConnectionReset()) DEBUG(net, 0, "recv failed with error %s", err.AsString()); this->CloseConnection(); return nullptr; } @@ -164,10 +164,10 @@ Packet *NetworkTCPSocketHandler::ReceivePacket() while (p->RemainingBytesToTransfer() != 0) { res = p->TransferIn(recv, this->sock, 0); if (res == -1) { - int err = NetworkGetLastError(); - if (err != EWOULDBLOCK) { - /* Something went wrong... (ECONNRESET is connection reset by peer) */ - if (err != ECONNRESET) DEBUG(net, 0, "recv failed with error %s", NetworkGetErrorString(err)); + NetworkError err = NetworkError::GetLast(); + if (!err.WouldBlock()) { + /* Something went wrong... */ + if (!err.IsConnectionReset()) DEBUG(net, 0, "recv failed with error %s", err.AsString()); this->CloseConnection(); return nullptr; } diff --git a/src/network/core/tcp_http.cpp b/src/network/core/tcp_http.cpp index e0c269fafb..4f29df1912 100644 --- a/src/network/core/tcp_http.cpp +++ b/src/network/core/tcp_http.cpp @@ -225,10 +225,10 @@ int NetworkHTTPSocketHandler::Receive() for (;;) { ssize_t res = recv(this->sock, (char *)this->recv_buffer + this->recv_pos, lengthof(this->recv_buffer) - this->recv_pos, 0); if (res == -1) { - int err = NetworkGetLastError(); - if (err != EWOULDBLOCK) { - /* Something went wrong... (ECONNRESET is connection reset by peer) */ - if (err != ECONNRESET) DEBUG(net, 0, "recv failed with error %s", NetworkGetErrorString(err)); + NetworkError err = NetworkError::GetLast(); + if (!err.WouldBlock()) { + /* Something went wrong... */ + if (!err.IsConnectionReset()) DEBUG(net, 0, "recv failed with error %s", err.AsString()); return -1; } /* Connection would block, so stop for now */ diff --git a/src/network/core/tcp_listen.h b/src/network/core/tcp_listen.h index 2ceea20aa9..e23ecae707 100644 --- a/src/network/core/tcp_listen.h +++ b/src/network/core/tcp_listen.h @@ -64,7 +64,7 @@ public: DEBUG(net, 1, "[%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", NetworkGetLastErrorString()); + DEBUG(net, 0, "send failed with error %s", 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", NetworkGetLastErrorString()); + DEBUG(net, 0, "send failed with error %s", NetworkError::GetLast().AsString()); } closesocket(s); diff --git a/src/network/core/udp.cpp b/src/network/core/udp.cpp index ffc86d825f..e7b99a53e8 100644 --- a/src/network/core/udp.cpp +++ b/src/network/core/udp.cpp @@ -95,7 +95,7 @@ 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", NetworkGetLastErrorString()); + DEBUG(net, 1, "[udp] setting broadcast failed with: %s", NetworkError::GetLast().AsString()); } } @@ -104,7 +104,7 @@ void NetworkUDPSocketHandler::SendPacket(Packet *p, NetworkAddress *recv, bool a DEBUG(net, 7, "[udp] 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(), NetworkGetLastErrorString()); + if (res == -1) DEBUG(net, 1, "[udp] sendto(%s) failed with: %s", send.GetAddressAsString().c_str(), NetworkError::GetLast().AsString()); if (!all) break; } diff --git a/src/safeguards.h b/src/safeguards.h index e3d6c4a3e4..aca461175f 100644 --- a/src/safeguards.h +++ b/src/safeguards.h @@ -70,15 +70,15 @@ #endif #if defined(NETWORK_CORE_OS_ABSTRACTION_H) && defined(_WIN32) -/* Use NetworkGetLastError() instead of errno, or do not (indirectly) include network/core/os_abstraction.h. - * Winsock does not set errno, but one should rather call WSAGetLastError. NetworkGetLastError abstracts that away. */ +/* Use NetworkError::GetLast() instead of errno, or do not (indirectly) include network/core/os_abstraction.h. + * Winsock does not set errno, but one should rather call WSAGetLastError. NetworkError::GetLast abstracts that away. */ #ifdef errno #undef errno #endif #define errno SAFEGUARD_DO_NOT_USE_THIS_METHOD -/* Use NetworkGetLastErrorString() instead of strerror, or do not (indirectly) include network/core/os_abstraction.h. - * Winsock errors are not handled by strerror, but one should rather call FormatMessage. NetworkGetLastErrorString abstracts that away. */ +/* Use NetworkError::AsString() instead of strerror, or do not (indirectly) include network/core/os_abstraction.h. + * Winsock errors are not handled by strerror, but one should rather call FormatMessage. NetworkError::AsString abstracts that away. */ #define strerror SAFEGUARD_DO_NOT_USE_THIS_METHOD #endif /* defined(NETWORK_CORE_OS_ABSTRACTION_H) && defined(_WIN32) */ From e097c83c83ac3be81041a67f8d641650045502fb Mon Sep 17 00:00:00 2001 From: rubidium42 Date: Fri, 30 Apr 2021 19:21:02 +0200 Subject: [PATCH 59/81] Codechange: move some OS abstraction method implementations out of the header --- src/network/core/os_abstraction.cpp | 49 ++++++++++++++++++++++++++ src/network/core/os_abstraction.h | 53 ++--------------------------- 2 files changed, 52 insertions(+), 50 deletions(-) diff --git a/src/network/core/os_abstraction.cpp b/src/network/core/os_abstraction.cpp index 75f2224eb0..b2d3475d01 100644 --- a/src/network/core/os_abstraction.cpp +++ b/src/network/core/os_abstraction.cpp @@ -123,3 +123,52 @@ bool NetworkError::HasError() const return NetworkError(errno); #endif } + + +/** + * Try to set the socket into non-blocking mode. + * @param d The socket to set the non-blocking more for. + * @return True if setting the non-blocking mode succeeded, otherwise false. + */ +bool SetNonBlocking(SOCKET d) +{ +#if defined(_WIN32) + u_long nonblocking = 1; + return ioctlsocket(d, FIONBIO, &nonblocking) == 0; +#elif defined __EMSCRIPTEN__ + return true; +#else + int nonblocking = 1; + return ioctl(d, FIONBIO, &nonblocking) == 0; +#endif +} + +/** + * Try to set the socket to not delay sending. + * @param d The socket to disable the delaying for. + * @return True if disabling the delaying succeeded, otherwise false. + */ +bool SetNoDelay(SOCKET d) +{ +#ifdef __EMSCRIPTEN__ + return true; +#else + int flags = 1; + /* The (const char*) cast is needed for windows */ + return setsockopt(d, IPPROTO_TCP, TCP_NODELAY, (const char *)&flags, sizeof(flags)) == 0; +#endif +} + +/** + * Get the error from a socket, if any. + * @param d The socket to get the error from. + * @return The errno on the socket. + */ +NetworkError GetSocketError(SOCKET d) +{ + int err; + socklen_t len = sizeof(err); + getsockopt(d, SOL_SOCKET, SO_ERROR, (char *)&err, &len); + + return NetworkError(err); +} diff --git a/src/network/core/os_abstraction.h b/src/network/core/os_abstraction.h index e444bc78b4..55d7335017 100644 --- a/src/network/core/os_abstraction.h +++ b/src/network/core/os_abstraction.h @@ -66,7 +66,6 @@ typedef unsigned long in_addr_t; # endif # define SOCKET int # define INVALID_SOCKET -1 -# define ioctlsocket ioctl # define closesocket close /* Need this for FIONREAD on solaris */ # define BSD_COMP @@ -115,7 +114,6 @@ typedef unsigned long in_addr_t; #if defined(__OS2__) # define SOCKET int # define INVALID_SOCKET -1 -# define ioctlsocket ioctl # define closesocket close /* Includes needed for OS/2 systems */ @@ -188,55 +186,10 @@ static inline socklen_t FixAddrLenForEmscripten(struct sockaddr_storage &address } #endif -/** - * Try to set the socket into non-blocking mode. - * @param d The socket to set the non-blocking more for. - * @return True if setting the non-blocking mode succeeded, otherwise false. - */ -static inline bool SetNonBlocking(SOCKET d) -{ -#ifdef __EMSCRIPTEN__ - return true; -#else -# ifdef _WIN32 - u_long nonblocking = 1; -# else - int nonblocking = 1; -# endif - return ioctlsocket(d, FIONBIO, &nonblocking) == 0; -#endif -} -/** - * Try to set the socket to not delay sending. - * @param d The socket to disable the delaying for. - * @return True if disabling the delaying succeeded, otherwise false. - */ -static inline bool SetNoDelay(SOCKET d) -{ -#ifdef __EMSCRIPTEN__ - return true; -#else - /* XXX should this be done at all? */ - int b = 1; - /* The (const char*) cast is needed for windows */ - return setsockopt(d, IPPROTO_TCP, TCP_NODELAY, (const char*)&b, sizeof(b)) == 0; -#endif -} - -/** - * Get the error from a socket, if any. - * @param d The socket to get the error from. - * @return The errno on the socket. - */ -static inline NetworkError GetSocketError(SOCKET d) -{ - int err; - socklen_t len = sizeof(err); - getsockopt(d, SOL_SOCKET, SO_ERROR, (char *)&err, &len); - - return NetworkError(err); -} +bool SetNonBlocking(SOCKET d); +bool SetNoDelay(SOCKET d); +NetworkError GetSocketError(SOCKET d); /* Make sure these structures have the size we expect them to be */ static_assert(sizeof(in_addr) == 4); ///< IPv4 addresses should be 4 bytes. From f785a70a2bc08eaefcb1e2a43b8e6d36a154a00e Mon Sep 17 00:00:00 2001 From: Peter Nelson Date: Sat, 1 May 2021 23:58:18 +0100 Subject: [PATCH 60/81] Cleanup: Use std::vector in RealSpriteGroup. --- src/newgrf.cpp | 9 ++------- src/newgrf_airport.cpp | 4 ++-- src/newgrf_canal.cpp | 2 +- src/newgrf_cargo.cpp | 4 ++-- src/newgrf_engine.cpp | 6 +++--- src/newgrf_generic.cpp | 2 +- src/newgrf_railtype.cpp | 4 ++-- src/newgrf_roadtype.cpp | 4 ++-- src/newgrf_spritegroup.cpp | 6 ------ src/newgrf_spritegroup.h | 7 ++----- src/newgrf_station.cpp | 8 ++++---- 11 files changed, 21 insertions(+), 35 deletions(-) diff --git a/src/newgrf.cpp b/src/newgrf.cpp index 19cc436dd7..1d30cb2d75 100644 --- a/src/newgrf.cpp +++ b/src/newgrf.cpp @@ -5181,23 +5181,18 @@ static void NewSpriteGroup(ByteReader *buf) group->nfo_line = _cur.nfo_line; act_group = group; - group->num_loaded = num_loaded; - group->num_loading = num_loading; - if (num_loaded > 0) group->loaded = CallocT(num_loaded); - if (num_loading > 0) group->loading = CallocT(num_loading); - grfmsg(6, "NewSpriteGroup: New SpriteGroup 0x%02X, %u loaded, %u loading", setid, num_loaded, num_loading); for (uint i = 0; i < num_loaded; i++) { uint16 spriteid = buf->ReadWord(); - group->loaded[i] = CreateGroupFromGroupID(feature, setid, type, spriteid); + group->loaded.push_back(CreateGroupFromGroupID(feature, setid, type, spriteid)); grfmsg(8, "NewSpriteGroup: + rg->loaded[%i] = subset %u", i, spriteid); } for (uint i = 0; i < num_loading; i++) { uint16 spriteid = buf->ReadWord(); - group->loading[i] = CreateGroupFromGroupID(feature, setid, type, spriteid); + group->loading.push_back(CreateGroupFromGroupID(feature, setid, type, spriteid)); grfmsg(8, "NewSpriteGroup: + rg->loading[%i] = subset %u", i, spriteid); } diff --git a/src/newgrf_airport.cpp b/src/newgrf_airport.cpp index 61ad46ac0e..86b65fd0e3 100644 --- a/src/newgrf_airport.cpp +++ b/src/newgrf_airport.cpp @@ -223,8 +223,8 @@ void AirportOverrideManager::SetEntitySpec(AirportSpec *as) { /* Airport action 2s should always have only 1 "loaded" state, but some * times things don't follow the spec... */ - if (group->num_loaded > 0) return group->loaded[0]; - if (group->num_loading > 0) return group->loading[0]; + if (!group->loaded.empty()) return group->loaded[0]; + if (!group->loading.empty()) return group->loading[0]; return nullptr; } diff --git a/src/newgrf_canal.cpp b/src/newgrf_canal.cpp index 7295e5551b..6b3c9b3d42 100644 --- a/src/newgrf_canal.cpp +++ b/src/newgrf_canal.cpp @@ -111,7 +111,7 @@ struct CanalResolverObject : public ResolverObject { /* virtual */ const SpriteGroup *CanalResolverObject::ResolveReal(const RealSpriteGroup *group) const { - if (group->num_loaded == 0) return nullptr; + if (group->loaded.empty()) return nullptr; return group->loaded[0]; } diff --git a/src/newgrf_cargo.cpp b/src/newgrf_cargo.cpp index c2859b71ef..105a2b2524 100644 --- a/src/newgrf_cargo.cpp +++ b/src/newgrf_cargo.cpp @@ -29,8 +29,8 @@ struct CargoResolverObject : public ResolverObject { { /* Cargo action 2s should always have only 1 "loaded" state, but some * times things don't follow the spec... */ - if (group->num_loaded > 0) return group->loaded[0]; - if (group->num_loading > 0) return group->loading[0]; + if (!group->loaded.empty()) return group->loaded[0]; + if (!group->loading.empty()) return group->loading[0]; return nullptr; } diff --git a/src/newgrf_engine.cpp b/src/newgrf_engine.cpp index cd12148558..e028b7a7d6 100644 --- a/src/newgrf_engine.cpp +++ b/src/newgrf_engine.cpp @@ -992,14 +992,14 @@ static uint32 VehicleGetVariable(Vehicle *v, const VehicleScopeResolver *object, const Vehicle *v = this->self_scope.v; if (v == nullptr) { - if (group->num_loading > 0) return group->loading[0]; - if (group->num_loaded > 0) return group->loaded[0]; + if (!group->loading.empty()) return group->loading[0]; + if (!group->loaded.empty()) return group->loaded[0]; return nullptr; } bool in_motion = !v->First()->current_order.IsType(OT_LOADING); - uint totalsets = in_motion ? group->num_loaded : group->num_loading; + uint totalsets = in_motion ? (uint)group->loaded.size() : (uint)group->loading.size(); if (totalsets == 0) return nullptr; diff --git a/src/newgrf_generic.cpp b/src/newgrf_generic.cpp index 6538b79b66..0a138f1d64 100644 --- a/src/newgrf_generic.cpp +++ b/src/newgrf_generic.cpp @@ -150,7 +150,7 @@ void AddGenericCallback(uint8 feature, const GRFFile *file, const SpriteGroup *g /* virtual */ const SpriteGroup *GenericResolverObject::ResolveReal(const RealSpriteGroup *group) const { - if (group->num_loaded == 0) return nullptr; + if (group->loaded.empty()) return nullptr; return group->loaded[0]; } diff --git a/src/newgrf_railtype.cpp b/src/newgrf_railtype.cpp index 326ee80baf..407acc9844 100644 --- a/src/newgrf_railtype.cpp +++ b/src/newgrf_railtype.cpp @@ -60,8 +60,8 @@ /* virtual */ const SpriteGroup *RailTypeResolverObject::ResolveReal(const RealSpriteGroup *group) const { - if (group->num_loading > 0) return group->loading[0]; - if (group->num_loaded > 0) return group->loaded[0]; + if (!group->loading.empty()) return group->loading[0]; + if (!group->loaded.empty()) return group->loaded[0]; return nullptr; } diff --git a/src/newgrf_roadtype.cpp b/src/newgrf_roadtype.cpp index 025d03bb6a..9243bf77e3 100644 --- a/src/newgrf_roadtype.cpp +++ b/src/newgrf_roadtype.cpp @@ -60,8 +60,8 @@ /* virtual */ const SpriteGroup *RoadTypeResolverObject::ResolveReal(const RealSpriteGroup *group) const { - if (group->num_loading > 0) return group->loading[0]; - if (group->num_loaded > 0) return group->loaded[0]; + if (!group->loading.empty()) return group->loading[0]; + if (!group->loaded.empty()) return group->loaded[0]; return nullptr; } diff --git a/src/newgrf_spritegroup.cpp b/src/newgrf_spritegroup.cpp index 871108ec47..fb0d65c319 100644 --- a/src/newgrf_spritegroup.cpp +++ b/src/newgrf_spritegroup.cpp @@ -53,12 +53,6 @@ TemporaryStorageArray _temp_store; } } -RealSpriteGroup::~RealSpriteGroup() -{ - free(this->loaded); - free(this->loading); -} - DeterministicSpriteGroup::~DeterministicSpriteGroup() { free(this->adjusts); diff --git a/src/newgrf_spritegroup.h b/src/newgrf_spritegroup.h index 80f70df55d..a931597a03 100644 --- a/src/newgrf_spritegroup.h +++ b/src/newgrf_spritegroup.h @@ -78,7 +78,6 @@ public: * groups. */ struct RealSpriteGroup : SpriteGroup { RealSpriteGroup() : SpriteGroup(SGT_REAL) {} - ~RealSpriteGroup(); /* Loaded = in motion, loading = not moving * Each group contains several spritesets, for various loading stages */ @@ -87,10 +86,8 @@ struct RealSpriteGroup : SpriteGroup { * with small amount of cargo whilst loading is for stations with a lot * of da stuff. */ - byte num_loaded; ///< Number of loaded groups - byte num_loading; ///< Number of loading groups - const SpriteGroup **loaded; ///< List of loaded groups (can be SpriteIDs or Callback results) - const SpriteGroup **loading; ///< List of loading groups (can be SpriteIDs or Callback results) + std::vector loaded; ///< List of loaded groups (can be SpriteIDs or Callback results) + std::vector loading; ///< List of loading groups (can be SpriteIDs or Callback results) protected: const SpriteGroup *Resolve(ResolverObject &object) const; diff --git a/src/newgrf_station.cpp b/src/newgrf_station.cpp index fa3f831aa5..9aa3ad43be 100644 --- a/src/newgrf_station.cpp +++ b/src/newgrf_station.cpp @@ -523,13 +523,13 @@ uint32 Waypoint::GetNewGRFVariable(const ResolverObject &object, byte variable, cargo = std::min(0xfffu, cargo); if (cargo > this->station_scope.statspec->cargo_threshold) { - if (group->num_loading > 0) { - uint set = ((cargo - this->station_scope.statspec->cargo_threshold) * group->num_loading) / (4096 - this->station_scope.statspec->cargo_threshold); + if (!group->loading.empty()) { + uint set = ((cargo - this->station_scope.statspec->cargo_threshold) * (uint)group->loading.size()) / (4096 - this->station_scope.statspec->cargo_threshold); return group->loading[set]; } } else { - if (group->num_loaded > 0) { - uint set = (cargo * group->num_loaded) / (this->station_scope.statspec->cargo_threshold + 1); + if (!group->loaded.empty()) { + uint set = (cargo * (uint)group->loaded.size()) / (this->station_scope.statspec->cargo_threshold + 1); return group->loaded[set]; } } From 1aeaf399541c229973b576f2f2423fa8c4b49f65 Mon Sep 17 00:00:00 2001 From: Peter Nelson Date: Sun, 2 May 2021 00:00:06 +0100 Subject: [PATCH 61/81] Cleanup: Use std::vector in DeterministicSpriteGroup. --- src/newgrf.cpp | 20 +++----------------- src/newgrf_spritegroup.cpp | 22 ++++++++-------------- src/newgrf_spritegroup.h | 7 ++----- 3 files changed, 13 insertions(+), 36 deletions(-) diff --git a/src/newgrf.cpp b/src/newgrf.cpp index 1d30cb2d75..ba5c098a2d 100644 --- a/src/newgrf.cpp +++ b/src/newgrf.cpp @@ -5024,16 +5024,13 @@ static void NewSpriteGroup(ByteReader *buf) case 2: group->size = DSG_SIZE_DWORD; varsize = 4; break; } - static std::vector adjusts; - adjusts.clear(); - /* Loop through the var adjusts. Unfortunately we don't know how many we have * from the outset, so we shall have to keep reallocing. */ do { - DeterministicSpriteGroupAdjust &adjust = adjusts.emplace_back(); + DeterministicSpriteGroupAdjust &adjust = group->adjusts.emplace_back(); /* The first var adjust doesn't have an operation specified, so we set it to add. */ - adjust.operation = adjusts.size() == 1 ? DSGA_OP_ADD : (DeterministicSpriteGroupAdjustOperation)buf->ReadByte(); + adjust.operation = group->adjusts.size() == 1 ? DSGA_OP_ADD : (DeterministicSpriteGroupAdjustOperation)buf->ReadByte(); adjust.variable = buf->ReadByte(); if (adjust.variable == 0x7E) { /* Link subroutine group */ @@ -5058,10 +5055,6 @@ static void NewSpriteGroup(ByteReader *buf) /* Continue reading var adjusts while bit 5 is set. */ } while (HasBit(varadjust, 5)); - group->num_adjusts = (uint)adjusts.size(); - group->adjusts = MallocT(group->num_adjusts); - MemCpyT(group->adjusts, adjusts.data(), group->num_adjusts); - std::vector ranges; ranges.resize(buf->ReadByte()); for (uint i = 0; i < ranges.size(); i++) { @@ -5098,27 +5091,20 @@ static void NewSpriteGroup(ByteReader *buf) } assert(target.size() == bounds.size()); - std::vector optimised; for (uint j = 0; j < bounds.size(); ) { if (target[j] != group->default_group) { - DeterministicSpriteGroupRange r; + DeterministicSpriteGroupRange &r = group->ranges.emplace_back(); r.group = target[j]; r.low = bounds[j]; while (j < bounds.size() && target[j] == r.group) { j++; } r.high = j < bounds.size() ? bounds[j] - 1 : UINT32_MAX; - optimised.push_back(r); } else { j++; } } - group->num_ranges = (uint)optimised.size(); // cast is safe, there should never be 2**31 elements here - if (group->num_ranges > 0) { - group->ranges = MallocT(group->num_ranges); - MemCpyT(group->ranges, &optimised.front(), group->num_ranges); - } break; } diff --git a/src/newgrf_spritegroup.cpp b/src/newgrf_spritegroup.cpp index fb0d65c319..058baf334e 100644 --- a/src/newgrf_spritegroup.cpp +++ b/src/newgrf_spritegroup.cpp @@ -53,12 +53,6 @@ TemporaryStorageArray _temp_store; } } -DeterministicSpriteGroup::~DeterministicSpriteGroup() -{ - free(this->adjusts); - free(this->ranges); -} - RandomizedSpriteGroup::~RandomizedSpriteGroup() { free(this->groups); @@ -205,8 +199,8 @@ const SpriteGroup *DeterministicSpriteGroup::Resolve(ResolverObject &object) con ScopeResolver *scope = object.GetScope(this->var_scope); - for (i = 0; i < this->num_adjusts; i++) { - DeterministicSpriteGroupAdjust *adjust = &this->adjusts[i]; + for (i = 0; i < this->adjusts.size(); i++) { + const DeterministicSpriteGroupAdjust *adjust = &this->adjusts[i]; /* Try to get the variable. We shall assume it is available, unless told otherwise. */ bool available = true; @@ -250,16 +244,16 @@ const SpriteGroup *DeterministicSpriteGroup::Resolve(ResolverObject &object) con return &nvarzero; } - if (this->num_ranges > 4) { - DeterministicSpriteGroupRange *lower = std::lower_bound(this->ranges + 0, this->ranges + this->num_ranges, value, RangeHighComparator); - if (lower != this->ranges + this->num_ranges && lower->low <= value) { + if (this->ranges.size() > 4) { + const auto &lower = std::lower_bound(this->ranges.begin(), this->ranges.end(), value, RangeHighComparator); + if (lower != this->ranges.end() && lower->low <= value) { assert(lower->low <= value && value <= lower->high); return SpriteGroup::Resolve(lower->group, object, false); } } else { - for (i = 0; i < this->num_ranges; i++) { - if (this->ranges[i].low <= value && value <= this->ranges[i].high) { - return SpriteGroup::Resolve(this->ranges[i].group, object, false); + for (const auto &range : this->ranges) { + if (range.low <= value && value <= range.high) { + return SpriteGroup::Resolve(range.group, object, false); } } } diff --git a/src/newgrf_spritegroup.h b/src/newgrf_spritegroup.h index a931597a03..e91987dbb6 100644 --- a/src/newgrf_spritegroup.h +++ b/src/newgrf_spritegroup.h @@ -166,15 +166,12 @@ struct DeterministicSpriteGroupRange { struct DeterministicSpriteGroup : SpriteGroup { DeterministicSpriteGroup() : SpriteGroup(SGT_DETERMINISTIC) {} - ~DeterministicSpriteGroup(); VarSpriteGroupScope var_scope; DeterministicSpriteGroupSize size; - uint num_adjusts; - uint num_ranges; bool calculated_result; - DeterministicSpriteGroupAdjust *adjusts; - DeterministicSpriteGroupRange *ranges; // Dynamically allocated + std::vector adjusts; + std::vector ranges; // Dynamically allocated /* Dynamically allocated, this is the sole owner */ const SpriteGroup *default_group; From 913d8a7f28cade14577fc147e3bb42fa7d75cad7 Mon Sep 17 00:00:00 2001 From: Peter Nelson Date: Sun, 2 May 2021 00:00:40 +0100 Subject: [PATCH 62/81] Cleanup: Use std::vector in RandomSpriteGroup. --- src/newgrf.cpp | 7 +++---- src/newgrf_spritegroup.cpp | 9 ++------- src/newgrf_spritegroup.h | 4 +--- 3 files changed, 6 insertions(+), 14 deletions(-) diff --git a/src/newgrf.cpp b/src/newgrf.cpp index ba5c098a2d..0377c0bc53 100644 --- a/src/newgrf.cpp +++ b/src/newgrf.cpp @@ -5128,11 +5128,10 @@ static void NewSpriteGroup(ByteReader *buf) group->triggers = GB(triggers, 0, 7); group->cmp_mode = HasBit(triggers, 7) ? RSG_CMP_ALL : RSG_CMP_ANY; group->lowest_randbit = buf->ReadByte(); - group->num_groups = buf->ReadByte(); - group->groups = CallocT(group->num_groups); - for (uint i = 0; i < group->num_groups; i++) { - group->groups[i] = GetGroupFromGroupID(setid, type, buf->ReadWord()); + byte num_groups = buf->ReadByte(); + for (uint i = 0; i < num_groups; i++) { + group->groups.push_back(GetGroupFromGroupID(setid, type, buf->ReadWord())); } break; diff --git a/src/newgrf_spritegroup.cpp b/src/newgrf_spritegroup.cpp index 058baf334e..eef531fb22 100644 --- a/src/newgrf_spritegroup.cpp +++ b/src/newgrf_spritegroup.cpp @@ -53,11 +53,6 @@ TemporaryStorageArray _temp_store; } } -RandomizedSpriteGroup::~RandomizedSpriteGroup() -{ - free(this->groups); -} - static inline uint32 GetVariable(const ResolverObject &object, ScopeResolver *scope, byte variable, uint32 parameter, bool *available) { uint32 value; @@ -272,11 +267,11 @@ const SpriteGroup *RandomizedSpriteGroup::Resolve(ResolverObject &object) const if (res) { object.used_triggers |= match; - object.reseed[this->var_scope] |= (this->num_groups - 1) << this->lowest_randbit; + object.reseed[this->var_scope] |= (this->groups.size() - 1) << this->lowest_randbit; } } - uint32 mask = (this->num_groups - 1) << this->lowest_randbit; + uint32 mask = ((uint)this->groups.size() - 1) << this->lowest_randbit; byte index = (scope->GetRandomBits() & mask) >> this->lowest_randbit; return SpriteGroup::Resolve(this->groups[index], object, false); diff --git a/src/newgrf_spritegroup.h b/src/newgrf_spritegroup.h index e91987dbb6..b172667612 100644 --- a/src/newgrf_spritegroup.h +++ b/src/newgrf_spritegroup.h @@ -189,7 +189,6 @@ enum RandomizedSpriteGroupCompareMode { struct RandomizedSpriteGroup : SpriteGroup { RandomizedSpriteGroup() : SpriteGroup(SGT_RANDOMIZED) {} - ~RandomizedSpriteGroup(); VarSpriteGroupScope var_scope; ///< Take this object: @@ -198,9 +197,8 @@ struct RandomizedSpriteGroup : SpriteGroup { byte count; byte lowest_randbit; ///< Look for this in the per-object randomized bitmask: - byte num_groups; ///< must be power of 2 - const SpriteGroup **groups; ///< Take the group with appropriate index: + std::vector groups; ///< Take the group with appropriate index: protected: const SpriteGroup *Resolve(ResolverObject &object) const; From 6b0b1bb3de17c92881d5a570736103d22c403368 Mon Sep 17 00:00:00 2001 From: Peter Nelson Date: Sun, 2 May 2021 00:04:34 +0100 Subject: [PATCH 63/81] Cleanup: Use range iterator to evaluate DeterministicSpriteGroup. --- src/newgrf_spritegroup.cpp | 29 +++++++++++++---------------- 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/src/newgrf_spritegroup.cpp b/src/newgrf_spritegroup.cpp index eef531fb22..29080894ec 100644 --- a/src/newgrf_spritegroup.cpp +++ b/src/newgrf_spritegroup.cpp @@ -141,18 +141,18 @@ static inline uint32 GetVariable(const ResolverObject &object, ScopeResolver *sc /* Evaluate an adjustment for a variable of the given size. * U is the unsigned type and S is the signed type to use. */ template -static U EvalAdjustT(const DeterministicSpriteGroupAdjust *adjust, ScopeResolver *scope, U last_value, uint32 value) +static U EvalAdjustT(const DeterministicSpriteGroupAdjust &adjust, ScopeResolver *scope, U last_value, uint32 value) { - value >>= adjust->shift_num; - value &= adjust->and_mask; + value >>= adjust.shift_num; + value &= adjust.and_mask; - switch (adjust->type) { - case DSGA_TYPE_DIV: value = ((S)value + (S)adjust->add_val) / (S)adjust->divmod_val; break; - case DSGA_TYPE_MOD: value = ((S)value + (S)adjust->add_val) % (S)adjust->divmod_val; break; + switch (adjust.type) { + case DSGA_TYPE_DIV: value = ((S)value + (S)adjust.add_val) / (S)adjust.divmod_val; break; + case DSGA_TYPE_MOD: value = ((S)value + (S)adjust.add_val) % (S)adjust.divmod_val; break; case DSGA_TYPE_NONE: break; } - switch (adjust->operation) { + switch (adjust.operation) { case DSGA_OP_ADD: return last_value + value; case DSGA_OP_SUB: return last_value - value; case DSGA_OP_SMIN: return std::min(last_value, value); @@ -190,17 +190,14 @@ const SpriteGroup *DeterministicSpriteGroup::Resolve(ResolverObject &object) con { uint32 last_value = 0; uint32 value = 0; - uint i; ScopeResolver *scope = object.GetScope(this->var_scope); - for (i = 0; i < this->adjusts.size(); i++) { - const DeterministicSpriteGroupAdjust *adjust = &this->adjusts[i]; - + for (const auto &adjust : this->adjusts) { /* Try to get the variable. We shall assume it is available, unless told otherwise. */ bool available = true; - if (adjust->variable == 0x7E) { - const SpriteGroup *subgroup = SpriteGroup::Resolve(adjust->subroutine, object, false); + if (adjust.variable == 0x7E) { + const SpriteGroup *subgroup = SpriteGroup::Resolve(adjust.subroutine, object, false); if (subgroup == nullptr) { value = CALLBACK_FAILED; } else { @@ -208,10 +205,10 @@ const SpriteGroup *DeterministicSpriteGroup::Resolve(ResolverObject &object) con } /* Note: 'last_value' and 'reseed' are shared between the main chain and the procedure */ - } else if (adjust->variable == 0x7B) { - value = GetVariable(object, scope, adjust->parameter, last_value, &available); + } else if (adjust.variable == 0x7B) { + value = GetVariable(object, scope, adjust.parameter, last_value, &available); } else { - value = GetVariable(object, scope, adjust->variable, adjust->parameter, &available); + value = GetVariable(object, scope, adjust.variable, adjust.parameter, &available); } if (!available) { From 18fb1c386689f12d2a7e767d9372020c0a8d9fea Mon Sep 17 00:00:00 2001 From: Peter Nelson Date: Sun, 2 May 2021 00:34:17 +0100 Subject: [PATCH 64/81] Codechange: Warn if randomaction2 group count is not a power of 2. Previously noted by a comment, this does not need to be guarded against as non-powers of 2 will not cause issues beyond the choice of results being reduced. --- src/newgrf.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/newgrf.cpp b/src/newgrf.cpp index 0377c0bc53..a9727d38de 100644 --- a/src/newgrf.cpp +++ b/src/newgrf.cpp @@ -5130,6 +5130,10 @@ static void NewSpriteGroup(ByteReader *buf) group->lowest_randbit = buf->ReadByte(); byte num_groups = buf->ReadByte(); + if (!HasExactlyOneBit(num_groups)) { + grfmsg(1, "NewSpriteGroup: Random Action 2 nrand should be power of 2"); + } + for (uint i = 0; i < num_groups; i++) { group->groups.push_back(GetGroupFromGroupID(setid, type, buf->ReadWord())); } From 256dbee25597f4da8726063084f94eb821babf14 Mon Sep 17 00:00:00 2001 From: PeterN Date: Sun, 2 May 2021 10:21:27 +0100 Subject: [PATCH 65/81] Fix: Crash when extra viewport height is zero with sign in view. (#9175) If a viewport sign straddles the top of a viewport, a crash will occur if the viewport height is zero. This is resolved by simply not attempting to draw the viewport in this situation, consistent with other widgets. --- src/widget.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/widget.cpp b/src/widget.cpp index 68bc43d78f..5c1c0ae8fc 100644 --- a/src/widget.cpp +++ b/src/widget.cpp @@ -2029,6 +2029,8 @@ void NWidgetViewport::SetupSmallestSize(Window *w, bool init_array) void NWidgetViewport::Draw(const Window *w) { + if (this->current_x == 0 || this->current_y == 0) return; + if (this->disp_flags & ND_NO_TRANSPARENCY) { TransparencyOptionBits to_backup = _transparency_opt; _transparency_opt &= (1 << TO_SIGNS) | (1 << TO_LOADING); // Disable all transparency, except textual stuff From 18651dd8b13d8a427ae71d8af00792d52ad9ed60 Mon Sep 17 00:00:00 2001 From: PeterN Date: Sun, 2 May 2021 10:43:14 +0100 Subject: [PATCH 66/81] Fix: Update text effect size when font zoom is changed. (#9174) --- src/saveload/afterload.cpp | 1 + src/texteff.cpp | 11 ++++++++++- src/texteff.hpp | 1 + 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/saveload/afterload.cpp b/src/saveload/afterload.cpp index d4b230dfd0..56680511cb 100644 --- a/src/saveload/afterload.cpp +++ b/src/saveload/afterload.cpp @@ -219,6 +219,7 @@ void UpdateAllVirtCoords() UpdateAllStationVirtCoords(); UpdateAllSignVirtCoords(); UpdateAllTownVirtCoords(); + UpdateAllTextEffectVirtCoords(); RebuildViewportKdtree(); } diff --git a/src/texteff.cpp b/src/texteff.cpp index f08701939f..e29326a90a 100644 --- a/src/texteff.cpp +++ b/src/texteff.cpp @@ -73,7 +73,16 @@ void UpdateTextEffect(TextEffectID te_id, StringID msg) te->params_1 = GetDParam(0); te->params_2 = GetDParam(1); - te->UpdatePosition(te->center, te->top, msg); + te->UpdatePosition(te->center, te->top, te->string_id, te->string_id - 1); +} + +void UpdateAllTextEffectVirtCoords() +{ + for (auto &te : _text_effects) { + SetDParam(0, te.params_1); + SetDParam(1, te.params_2); + te.UpdatePosition(te.center, te.top, te.string_id, te.string_id - 1); + } } void RemoveTextEffect(TextEffectID te_id) diff --git a/src/texteff.hpp b/src/texteff.hpp index 789b12d540..d122b17e9b 100644 --- a/src/texteff.hpp +++ b/src/texteff.hpp @@ -32,6 +32,7 @@ void InitTextEffects(); void DrawTextEffects(DrawPixelInfo *dpi); void UpdateTextEffect(TextEffectID effect_id, StringID msg); void RemoveTextEffect(TextEffectID effect_id); +void UpdateAllTextEffectVirtCoords(); /* misc_gui.cpp */ TextEffectID ShowFillingPercent(int x, int y, int z, uint8 percent, StringID colour); From 56aa6d0eddb60fac20cde82cd496005369ca1b8d Mon Sep 17 00:00:00 2001 From: rubidium42 Date: Sun, 2 May 2021 11:05:50 +0200 Subject: [PATCH 67/81] Fix: [Network] Reading beyond the length of the server's ID when hashing password Under normal circumstances the server's ID is 32 characters excluding '\0', however this can be changed at the server. This ID is sent to the server for company name hashing. The client reads it into a statically allocated buffer of 33 bytes, but fills only the bytes it received from the server. However, the hash assumes all 33 bytes are set, thus potentially reading uninitialized data, or a part of the server ID of a previous game in the hashing routine. It is still reading from memory assigned to the server ID, so nothing bad happens, except that company passwords might not work correctly. --- src/network/network.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/network/network.cpp b/src/network/network.cpp index af732facdb..c00a3650fb 100644 --- a/src/network/network.cpp +++ b/src/network/network.cpp @@ -176,12 +176,15 @@ const char *GenerateCompanyPasswordHash(const char *password, const char *passwo if (StrEmpty(password)) return password; char salted_password[NETWORK_SERVER_ID_LENGTH]; + size_t password_length = strlen(password); + size_t password_server_id_length = strlen(password_server_id); - memset(salted_password, 0, sizeof(salted_password)); - seprintf(salted_password, lastof(salted_password), "%s", password); /* Add the game seed and the server's ID as the salt. */ for (uint i = 0; i < NETWORK_SERVER_ID_LENGTH - 1; i++) { - salted_password[i] ^= password_server_id[i] ^ (password_game_seed >> (i % 32)); + char password_char = (i < password_length ? password[i] : 0); + char server_id_char = (i < password_server_id_length ? password_server_id[i] : 0); + char seed_char = password_game_seed >> (i % 32); + salted_password[i] = password_char ^ server_id_char ^ seed_char; } Md5 checksum; From 84aa17cea6f677f2bd6cb0e1c33457dbdcda77dc Mon Sep 17 00:00:00 2001 From: frosch Date: Sat, 1 May 2021 23:59:53 +0200 Subject: [PATCH 68/81] Fix: [NewGRF] industry variables 65 and 66 ignored the parameter, and always used the north tile. --- src/newgrf_industries.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/newgrf_industries.cpp b/src/newgrf_industries.cpp index 5e59634e84..68f28148c7 100644 --- a/src/newgrf_industries.cpp +++ b/src/newgrf_industries.cpp @@ -281,13 +281,17 @@ static uint32 GetCountAndDistanceOfClosestInstance(byte param_setID, byte layout if (this->tile == INVALID_TILE) break; return GetClosestIndustry(this->tile, MapNewGRFIndustryType(parameter, indspec->grf_prop.grffile->grfid), this->industry); /* Get town zone and Manhattan distance of closest town */ - case 0x65: + case 0x65: { if (this->tile == INVALID_TILE) break; - return GetTownRadiusGroup(this->industry->town, this->tile) << 16 | std::min(DistanceManhattan(this->tile, this->industry->town->xy), 0xFFFFu); + TileIndex tile = GetNearbyTile(parameter, this->tile, true); + return GetTownRadiusGroup(this->industry->town, tile) << 16 | std::min(DistanceManhattan(tile, this->industry->town->xy), 0xFFFFu); + } /* Get square of Euclidian distance of closes town */ - case 0x66: + case 0x66: { if (this->tile == INVALID_TILE) break; - return GetTownRadiusGroup(this->industry->town, this->tile) << 16 | std::min(DistanceSquare(this->tile, this->industry->town->xy), 0xFFFFu); + TileIndex tile = GetNearbyTile(parameter, this->tile, true); + return GetTownRadiusGroup(this->industry->town, tile) << 16 | std::min(DistanceSquare(tile, this->industry->town->xy), 0xFFFFu); + } /* Count of industry, distance of closest instance * 68 is the same as 67, but with a filtering on selected layout */ From 2cf5df2a50e9f28ef4ba93a765bfcf79afcbb20c Mon Sep 17 00:00:00 2001 From: frosch Date: Sun, 2 May 2021 00:01:06 +0200 Subject: [PATCH 69/81] Fix: [NewGRF] industry variable 66 and object variable 46 clamped the squared-euclidian distance to 16 bit, when they should not. --- src/newgrf_industries.cpp | 4 ++-- src/newgrf_object.cpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/newgrf_industries.cpp b/src/newgrf_industries.cpp index 68f28148c7..abae446040 100644 --- a/src/newgrf_industries.cpp +++ b/src/newgrf_industries.cpp @@ -286,11 +286,11 @@ static uint32 GetCountAndDistanceOfClosestInstance(byte param_setID, byte layout TileIndex tile = GetNearbyTile(parameter, this->tile, true); return GetTownRadiusGroup(this->industry->town, tile) << 16 | std::min(DistanceManhattan(tile, this->industry->town->xy), 0xFFFFu); } - /* Get square of Euclidian distance of closes town */ + /* Get square of Euclidian distance of closest town */ case 0x66: { if (this->tile == INVALID_TILE) break; TileIndex tile = GetNearbyTile(parameter, this->tile, true); - return GetTownRadiusGroup(this->industry->town, tile) << 16 | std::min(DistanceSquare(tile, this->industry->town->xy), 0xFFFFu); + return DistanceSquare(tile, this->industry->town->xy); } /* Count of industry, distance of closest instance diff --git a/src/newgrf_object.cpp b/src/newgrf_object.cpp index 649a4b58a1..3c069f4c95 100644 --- a/src/newgrf_object.cpp +++ b/src/newgrf_object.cpp @@ -303,8 +303,8 @@ static uint32 GetCountAndDistanceOfClosestInstance(byte local_id, uint32 grfid, /* Get town zone and Manhattan distance of closest town */ case 0x45: return GetTownRadiusGroup(t, this->tile) << 16 | std::min(DistanceManhattan(this->tile, t->xy), 0xFFFFu); - /* Get square of Euclidian distance of closes town */ - case 0x46: return GetTownRadiusGroup(t, this->tile) << 16 | std::min(DistanceSquare(this->tile, t->xy), 0xFFFFu); + /* Get square of Euclidian distance of closest town */ + case 0x46: return DistanceSquare(this->tile, t->xy); /* Object colour */ case 0x47: return this->obj->colour; From 91b8ce073f54dff48cd61186c32d0a720a2abb4d Mon Sep 17 00:00:00 2001 From: Michael Lutz Date: Sat, 1 May 2021 19:39:03 +0200 Subject: [PATCH 70/81] Codechange: Generalise the delayed blitter change to a generic video driver command queue. --- src/gfxinit.cpp | 24 ++++++++++++++++++++++- src/video/video_driver.cpp | 26 +------------------------ src/video/video_driver.hpp | 39 ++++++++++++++++++++++++++++++-------- 3 files changed, 55 insertions(+), 34 deletions(-) diff --git a/src/gfxinit.cpp b/src/gfxinit.cpp index b744eaa5ef..c0ba4c4d4d 100644 --- a/src/gfxinit.cpp +++ b/src/gfxinit.cpp @@ -244,6 +244,28 @@ static void LoadSpriteTables() } +static void RealChangeBlitter(const char *repl_blitter) +{ + const char *cur_blitter = BlitterFactory::GetCurrentBlitter()->GetName(); + if (strcmp(cur_blitter, repl_blitter) == 0) return; + + DEBUG(driver, 1, "Switching blitter from '%s' to '%s'... ", cur_blitter, repl_blitter); + Blitter *new_blitter = BlitterFactory::SelectBlitter(repl_blitter); + if (new_blitter == nullptr) NOT_REACHED(); + DEBUG(driver, 1, "Successfully switched to %s.", repl_blitter); + + if (!VideoDriver::GetInstance()->AfterBlitterChange()) { + /* Failed to switch blitter, let's hope we can return to the old one. */ + if (BlitterFactory::SelectBlitter(cur_blitter) == nullptr || !VideoDriver::GetInstance()->AfterBlitterChange()) usererror("Failed to reinitialize video driver. Specify a fixed blitter in the config"); + } + + /* Clear caches that might have sprites for another blitter. */ + VideoDriver::GetInstance()->ClearSystemSprites(); + ClearFontCache(); + GfxClearSpriteCache(); + ReInitAllWindows(false); +} + /** * Check blitter needed by NewGRF config and switch if needed. * @return False when nothing changed, true otherwise. @@ -309,7 +331,7 @@ static bool SwitchNewGRFBlitter() if (BlitterFactory::GetBlitterFactory(repl_blitter) == nullptr) continue; /* Inform the video driver we want to switch blitter as soon as possible. */ - VideoDriver::GetInstance()->ChangeBlitter(repl_blitter); + VideoDriver::GetInstance()->QueueOnMainThread(std::bind(&RealChangeBlitter, repl_blitter)); break; } diff --git a/src/video/video_driver.cpp b/src/video/video_driver.cpp index d8fbe0400a..2f8efc4ad4 100644 --- a/src/video/video_driver.cpp +++ b/src/video/video_driver.cpp @@ -97,27 +97,6 @@ void VideoDriver::StopGameThread() this->game_thread.join(); } -void VideoDriver::RealChangeBlitter(const char *repl_blitter) -{ - const char *cur_blitter = BlitterFactory::GetCurrentBlitter()->GetName(); - - DEBUG(driver, 1, "Switching blitter from '%s' to '%s'... ", cur_blitter, repl_blitter); - Blitter *new_blitter = BlitterFactory::SelectBlitter(repl_blitter); - if (new_blitter == nullptr) NOT_REACHED(); - DEBUG(driver, 1, "Successfully switched to %s.", repl_blitter); - - if (!this->AfterBlitterChange()) { - /* Failed to switch blitter, let's hope we can return to the old one. */ - if (BlitterFactory::SelectBlitter(cur_blitter) == nullptr || !this->AfterBlitterChange()) usererror("Failed to reinitialize video driver. Specify a fixed blitter in the config"); - } - - /* Clear caches that might have sprites for another blitter. */ - this->ClearSystemSprites(); - ClearFontCache(); - GfxClearSpriteCache(); - ReInitAllWindows(false); -} - void VideoDriver::Tick() { if (!this->is_game_threaded && std::chrono::steady_clock::now() >= this->next_game_tick) { @@ -159,10 +138,7 @@ void VideoDriver::Tick() this->LockVideoBuffer(); - if (this->change_blitter != nullptr) { - this->RealChangeBlitter(this->change_blitter); - this->change_blitter = nullptr; - } + this->DrainCommandQueue(); while (this->PollEvent()) {} ::InputLoop(); diff --git a/src/video/video_driver.hpp b/src/video/video_driver.hpp index 4964e01cb3..db522a761d 100644 --- a/src/video/video_driver.hpp +++ b/src/video/video_driver.hpp @@ -22,6 +22,7 @@ #include #include #include +#include extern std::string _ini_videodriver; extern std::vector _resolutions; @@ -36,7 +37,7 @@ class VideoDriver : public Driver { const uint DEFAULT_WINDOW_HEIGHT = 480u; ///< Default window height. public: - VideoDriver() : fast_forward_key_pressed(false), fast_forward_via_key(false), is_game_threaded(true), change_blitter(nullptr) {} + VideoDriver() : fast_forward_key_pressed(false), fast_forward_via_key(false), is_game_threaded(true) {} /** * Mark a particular area dirty. @@ -178,12 +179,16 @@ public: } /** - * Queue a request to change the blitter. This is not executed immediately, - * but instead on the next draw-tick. + * Queue a function to be called on the main thread with game state + * lock held and video buffer locked. Queued functions will be + * executed on the next draw tick. + * @param func Function to call. */ - void ChangeBlitter(const char *new_blitter) + void QueueOnMainThread(std::function &&func) { - this->change_blitter = new_blitter; + std::lock_guard lock(this->cmd_queue_mutex); + + this->cmd_queue.emplace_back(std::forward>(func)); } void GameLoopPause(); @@ -328,11 +333,29 @@ protected: static void GameThreadThunk(VideoDriver *drv); private: + std::mutex cmd_queue_mutex; + std::vector> cmd_queue; + + /** Execute all queued commands. */ + void DrainCommandQueue() + { + std::vector> cmds{}; + + { + /* Exchange queue with an empty one to limit the time we + * hold the mutex. This also ensures that queued functions can + * add new functions to the queue without everything blocking. */ + std::lock_guard lock(this->cmd_queue_mutex); + cmds.swap(this->cmd_queue); + } + + for (auto &f : cmds) { + f(); + } + } + void GameLoop(); void GameThread(); - void RealChangeBlitter(const char *repl_blitter); - - const char *change_blitter; ///< Request to change the blitter. nullptr if no pending request. }; #endif /* VIDEO_VIDEO_DRIVER_HPP */ From 1f159f79de7428cbaa6133ec9cf06ce559980ea6 Mon Sep 17 00:00:00 2001 From: Michael Lutz Date: Sat, 1 May 2021 19:55:46 +0200 Subject: [PATCH 71/81] Fix #9147: Delay making screenshots until the next draw tick as we may not access the video buffer from the game thread. --- src/console_cmds.cpp | 2 +- src/screenshot.cpp | 38 ++++++++++++++++++++++++++++++-------- src/screenshot.h | 2 +- 3 files changed, 32 insertions(+), 10 deletions(-) diff --git a/src/console_cmds.cpp b/src/console_cmds.cpp index 69a44e63de..99b3b54582 100644 --- a/src/console_cmds.cpp +++ b/src/console_cmds.cpp @@ -1416,7 +1416,7 @@ DEF_CONSOLE_CMD(ConScreenShot) ScreenshotType type = SC_VIEWPORT; uint32 width = 0; uint32 height = 0; - const char *name = nullptr; + std::string name{}; uint32 arg_index = 1; if (argc > arg_index) { diff --git a/src/screenshot.cpp b/src/screenshot.cpp index dc76996fbf..0e3bf3d8e2 100644 --- a/src/screenshot.cpp +++ b/src/screenshot.cpp @@ -864,7 +864,7 @@ static ScreenshotType _confirmed_screenshot_type; ///< Screenshot type the curre */ static void ScreenshotConfirmationCallback(Window *w, bool confirmed) { - if (confirmed) MakeScreenshot(_confirmed_screenshot_type, nullptr); + if (confirmed) MakeScreenshot(_confirmed_screenshot_type, {}); } /** @@ -890,24 +890,20 @@ void MakeScreenshotWithConfirm(ScreenshotType t) ShowQuery(STR_WARNING_SCREENSHOT_SIZE_CAPTION, STR_WARNING_SCREENSHOT_SIZE_MESSAGE, nullptr, ScreenshotConfirmationCallback); } else { /* Less than 64M pixels, just do it */ - MakeScreenshot(t, nullptr); + MakeScreenshot(t, {}); } } /** * Make a screenshot. - * Unconditionally take a screenshot of the requested type. * @param t the type of screenshot to make. * @param name the name to give to the screenshot. * @param width the width of the screenshot of, or 0 for current viewport width (only works for SC_ZOOMEDIN and SC_DEFAULTZOOM). * @param height the height of the screenshot of, or 0 for current viewport height (only works for SC_ZOOMEDIN and SC_DEFAULTZOOM). * @return true iff the screenshot was made successfully - * @see MakeScreenshotWithConfirm */ -bool MakeScreenshot(ScreenshotType t, const char *name, uint32 width, uint32 height) +static bool RealMakeScreenshot(ScreenshotType t, std::string name, uint32 width, uint32 height) { - VideoDriver::VideoBufferLocker lock; - if (t == SC_VIEWPORT) { /* First draw the dirty parts of the screen and only then change the name * of the screenshot. This way the screenshot will always show the name @@ -918,7 +914,7 @@ bool MakeScreenshot(ScreenshotType t, const char *name, uint32 width, uint32 hei } _screenshot_name[0] = '\0'; - if (name != nullptr) strecpy(_screenshot_name, name, lastof(_screenshot_name)); + if (!name.empty()) strecpy(_screenshot_name, name.c_str(), lastof(_screenshot_name)); bool ret; switch (t) { @@ -969,6 +965,32 @@ bool MakeScreenshot(ScreenshotType t, const char *name, uint32 width, uint32 hei return ret; } +/** + * Schedule making a screenshot. + * Unconditionally take a screenshot of the requested type. + * @param t the type of screenshot to make. + * @param name the name to give to the screenshot. + * @param width the width of the screenshot of, or 0 for current viewport width (only works for SC_ZOOMEDIN and SC_DEFAULTZOOM). + * @param height the height of the screenshot of, or 0 for current viewport height (only works for SC_ZOOMEDIN and SC_DEFAULTZOOM). + * @return true iff the screenshot was successfully made. + * @see MakeScreenshotWithConfirm + */ +bool MakeScreenshot(ScreenshotType t, std::string name, uint32 width, uint32 height) +{ + if (t == SC_CRASHLOG) { + /* Video buffer might or might not be locked. */ + VideoDriver::VideoBufferLocker lock; + + return RealMakeScreenshot(t, name, width, height); + } + + VideoDriver::GetInstance()->QueueOnMainThread([=] { // Capture by value to not break scope. + RealMakeScreenshot(t, name, width, height); + }); + + return true; +} + /** * Return the owner of a tile to display it with in the small map in mode "Owner". diff --git a/src/screenshot.h b/src/screenshot.h index 148c018e1e..e65813573f 100644 --- a/src/screenshot.h +++ b/src/screenshot.h @@ -28,7 +28,7 @@ enum ScreenshotType { void SetupScreenshotViewport(ScreenshotType t, struct Viewport *vp, uint32 width = 0, uint32 height = 0); bool MakeHeightmapScreenshot(const char *filename); void MakeScreenshotWithConfirm(ScreenshotType t); -bool MakeScreenshot(ScreenshotType t, const char *name, uint32 width = 0, uint32 height = 0); +bool MakeScreenshot(ScreenshotType t, std::string name, uint32 width = 0, uint32 height = 0); bool MakeMinimapWorldScreenshot(); extern char _screenshot_format_name[8]; From bd1a20f6eee6758f6a812af18fbe5b41b491b5c4 Mon Sep 17 00:00:00 2001 From: Peter Nelson Date: Sat, 1 May 2021 18:07:47 +0100 Subject: [PATCH 72/81] Codechange: Use std::vector for NewGRF station platform layouts. This avoids the need to custom memory management and additional members. This also resolves use-after-free if modifying copied layouts, so presumably nobody has ever done that. --- src/newgrf.cpp | 79 +++++++++++--------------------------------- src/newgrf_station.h | 22 +++++++----- src/station_cmd.cpp | 14 ++++---- src/waypoint_cmd.cpp | 2 +- 4 files changed, 42 insertions(+), 75 deletions(-) diff --git a/src/newgrf.cpp b/src/newgrf.cpp index a9727d38de..9e8bbda190 100644 --- a/src/newgrf.cpp +++ b/src/newgrf.cpp @@ -218,6 +218,19 @@ protected: public: ByteReader(byte *data, byte *end) : data(data), end(end) { } + inline byte *ReadBytes(size_t size) + { + if (data + size >= end) { + /* Put data at the end, as would happen if every byte had been individually read. */ + data = end; + throw OTTDByteReaderSignal(); + } + + byte *ret = data; + data += size; + return ret; + } + inline byte ReadByte() { if (data < end) return *(data)++; @@ -1883,7 +1896,7 @@ static ChangeInfoResult StationChangeInfo(uint stid, int numinfo, int prop, Byte StationSpec **spec = &_cur.grffile->stations[stid + i]; /* Property 0x08 is special; it is where the station is allocated */ - if (*spec == nullptr) *spec = CallocT(1); + if (*spec == nullptr) *spec = new StationSpec(); /* Swap classid because we read it in BE meaning WAYP or DFLT */ uint32 classid = buf->ReadDWord(); @@ -1966,54 +1979,17 @@ static ChangeInfoResult StationChangeInfo(uint stid, int numinfo, int prop, Byte break; case 0x0E: // Define custom layout - statspec->copied_layouts = false; - while (buf->HasData()) { byte length = buf->ReadByte(); byte number = buf->ReadByte(); - StationLayout layout; - uint l, p; if (length == 0 || number == 0) break; - if (length > statspec->lengths) { - byte diff_length = length - statspec->lengths; - statspec->platforms = ReallocT(statspec->platforms, length); - memset(statspec->platforms + statspec->lengths, 0, diff_length); - - statspec->layouts = ReallocT(statspec->layouts, length); - memset(statspec->layouts + statspec->lengths, 0, diff_length * sizeof(*statspec->layouts)); - - statspec->lengths = length; - } - l = length - 1; // index is zero-based - - if (number > statspec->platforms[l]) { - statspec->layouts[l] = ReallocT(statspec->layouts[l], number); - /* We expect nullptr being 0 here, but C99 guarantees that. */ - memset(statspec->layouts[l] + statspec->platforms[l], 0, - (number - statspec->platforms[l]) * sizeof(**statspec->layouts)); + if (statspec->layouts.size() < length) statspec->layouts.resize(length); + if (statspec->layouts[length - 1].size() < number) statspec->layouts[length - 1].resize(number); - statspec->platforms[l] = number; - } - - p = 0; - layout = MallocT(length * number); - try { - for (l = 0; l < length; l++) { - for (p = 0; p < number; p++) { - layout[l * number + p] = buf->ReadByte(); - } - } - } catch (...) { - free(layout); - throw; - } - - l--; - p--; - free(statspec->layouts[l][p]); - statspec->layouts[l][p] = layout; + const byte *layout = buf->ReadBytes(length * number); + statspec->layouts[length - 1][number - 1].assign(layout, layout + length * number); } break; @@ -2026,10 +2002,7 @@ static ChangeInfoResult StationChangeInfo(uint stid, int numinfo, int prop, Byte continue; } - statspec->lengths = srcstatspec->lengths; - statspec->platforms = srcstatspec->platforms; - statspec->layouts = srcstatspec->layouts; - statspec->copied_layouts = true; + statspec->layouts = srcstatspec->layouts; break; } @@ -8399,20 +8372,8 @@ static void ResetCustomStations() delete[] statspec->renderdata; - /* Release platforms and layouts */ - if (!statspec->copied_layouts) { - for (uint l = 0; l < statspec->lengths; l++) { - for (uint p = 0; p < statspec->platforms[l]; p++) { - free(statspec->layouts[l][p]); - } - free(statspec->layouts[l]); - } - free(statspec->layouts); - free(statspec->platforms); - } - /* Release this station */ - free(statspec); + delete statspec; } /* Free and reset the station data */ diff --git a/src/newgrf_station.h b/src/newgrf_station.h index fac5d64ddd..4c4a5831be 100644 --- a/src/newgrf_station.h +++ b/src/newgrf_station.h @@ -109,12 +109,13 @@ enum StationRandomTrigger { SRT_PATH_RESERVATION, ///< Trigger platform when train reserves path. }; -/* Station layout for given dimensions - it is a two-dimensional array - * where index is computed as (x * platforms) + platform. */ -typedef byte *StationLayout; - /** Station specification. */ struct StationSpec { + StationSpec() : cls_id(STAT_CLASS_DFLT), name(0), + disallowed_platforms(0), disallowed_lengths(0), tiles(0), + renderdata(nullptr), cargo_threshold(0), cargo_triggers(0), + callback_mask(0), flags(0), pylons(0), wires(0), blocked(0), + animation({0, 0, 0, 0}) {} /** * Properties related the the grf file. * NUM_CARGO real cargo plus three pseudo cargo sprite groups. @@ -165,10 +166,15 @@ struct StationSpec { AnimationInfo animation; - byte lengths; - byte *platforms; - StationLayout **layouts; - bool copied_layouts; + /** + * Custom platform layouts. + * This is a 2D array containing an array of tiles. + * 1st layer is platform lengths. + * 2nd layer is tracks (width). + * These can be sparsely populated, and the upper limit is not defined but + * limited to 255. + */ + std::vector>> layouts; }; /** Struct containing information relating to station classes. */ diff --git a/src/station_cmd.cpp b/src/station_cmd.cpp index 482b954625..ac78064d49 100644 --- a/src/station_cmd.cpp +++ b/src/station_cmd.cpp @@ -1109,13 +1109,13 @@ static inline byte *CreateMulti(byte *layout, int n, byte b) * @param plat_len The length of the platforms. * @param statspec The specification of the station to (possibly) get the layout from. */ -void GetStationLayout(byte *layout, int numtracks, int plat_len, const StationSpec *statspec) +void GetStationLayout(byte *layout, uint numtracks, uint plat_len, const StationSpec *statspec) { - if (statspec != nullptr && statspec->lengths >= plat_len && - statspec->platforms[plat_len - 1] >= numtracks && - statspec->layouts[plat_len - 1][numtracks - 1]) { + if (statspec != nullptr && statspec->layouts.size() >= plat_len && + statspec->layouts[plat_len - 1].size() >= numtracks && + !statspec->layouts[plat_len - 1][numtracks - 1].empty()) { /* Custom layout defined, follow it. */ - memcpy(layout, statspec->layouts[plat_len - 1][numtracks - 1], + memcpy(layout, statspec->layouts[plat_len - 1][numtracks - 1].data(), plat_len * numtracks); return; } @@ -1124,9 +1124,9 @@ void GetStationLayout(byte *layout, int numtracks, int plat_len, const StationSp CreateSingle(layout, numtracks); } else { if (numtracks & 1) layout = CreateSingle(layout, plat_len); - numtracks >>= 1; + int n = numtracks >> 1; - while (--numtracks >= 0) { + while (--n >= 0) { layout = CreateMulti(layout, plat_len, 4); layout = CreateMulti(layout, plat_len, 6); } diff --git a/src/waypoint_cmd.cpp b/src/waypoint_cmd.cpp index 01cdbc16e0..e8e9e69455 100644 --- a/src/waypoint_cmd.cpp +++ b/src/waypoint_cmd.cpp @@ -153,7 +153,7 @@ static CommandCost IsValidTileForWaypoint(TileIndex tile, Axis axis, StationID * return CommandCost(); } -extern void GetStationLayout(byte *layout, int numtracks, int plat_len, const StationSpec *statspec); +extern void GetStationLayout(byte *layout, uint numtracks, uint plat_len, const StationSpec *statspec); extern CommandCost FindJoiningWaypoint(StationID existing_station, StationID station_to_join, bool adjacent, TileArea ta, Waypoint **wp); extern CommandCost CanExpandRailStation(const BaseStation *st, TileArea &new_ta, Axis axis); From a3e49178d1d5d65e3ce91328d743d1642f324961 Mon Sep 17 00:00:00 2001 From: Peter Nelson Date: Sat, 1 May 2021 20:28:23 +0100 Subject: [PATCH 73/81] Codechange: Use std::vector for NewGRF station tile sprite layouts. --- src/newgrf.cpp | 38 +++++++++++++++++++------------------- src/newgrf_station.cpp | 4 ++-- src/newgrf_station.h | 7 +++---- src/station_cmd.cpp | 4 ++-- 4 files changed, 26 insertions(+), 27 deletions(-) diff --git a/src/newgrf.cpp b/src/newgrf.cpp index 9e8bbda190..a2671eff27 100644 --- a/src/newgrf.cpp +++ b/src/newgrf.cpp @@ -1904,13 +1904,13 @@ static ChangeInfoResult StationChangeInfo(uint stid, int numinfo, int prop, Byte break; } - case 0x09: // Define sprite layout - statspec->tiles = buf->ReadExtendedByte(); - delete[] statspec->renderdata; // delete earlier loaded stuff - statspec->renderdata = new NewGRFSpriteLayout[statspec->tiles]; + case 0x09: { // Define sprite layout + uint16 tiles = buf->ReadExtendedByte(); + statspec->renderdata.clear(); // delete earlier loaded stuff + statspec->renderdata.reserve(tiles); - for (uint t = 0; t < statspec->tiles; t++) { - NewGRFSpriteLayout *dts = &statspec->renderdata[t]; + for (uint t = 0; t < tiles; t++) { + NewGRFSpriteLayout *dts = &statspec->renderdata.emplace_back(); dts->consistent_max_offset = UINT16_MAX; // Spritesets are unknown, so no limit. if (buf->HasData(4) && *(uint32*)buf->Data() == 0) { @@ -1946,6 +1946,7 @@ static ChangeInfoResult StationChangeInfo(uint stid, int numinfo, int prop, Byte dts->Clone(tmp_layout.data()); } break; + } case 0x0A: { // Copy sprite layout byte srcid = buf->ReadByte(); @@ -1956,12 +1957,12 @@ static ChangeInfoResult StationChangeInfo(uint stid, int numinfo, int prop, Byte continue; } - delete[] statspec->renderdata; // delete earlier loaded stuff + statspec->renderdata.clear(); // delete earlier loaded stuff + statspec->renderdata.reserve(srcstatspec->renderdata.size()); - statspec->tiles = srcstatspec->tiles; - statspec->renderdata = new NewGRFSpriteLayout[statspec->tiles]; - for (uint t = 0; t < statspec->tiles; t++) { - statspec->renderdata[t].Clone(&srcstatspec->renderdata[t]); + for (const auto &it : srcstatspec->renderdata) { + NewGRFSpriteLayout *dts = &statspec->renderdata.emplace_back(); + dts->Clone(&it); } break; } @@ -2047,18 +2048,19 @@ static ChangeInfoResult StationChangeInfo(uint stid, int numinfo, int prop, Byte statspec->animation.triggers = buf->ReadWord(); break; - case 0x1A: // Advanced sprite layout - statspec->tiles = buf->ReadExtendedByte(); - delete[] statspec->renderdata; // delete earlier loaded stuff - statspec->renderdata = new NewGRFSpriteLayout[statspec->tiles]; + case 0x1A: { // Advanced sprite layout + uint16 tiles = buf->ReadExtendedByte(); + statspec->renderdata.clear(); // delete earlier loaded stuff + statspec->renderdata.reserve(tiles); - for (uint t = 0; t < statspec->tiles; t++) { - NewGRFSpriteLayout *dts = &statspec->renderdata[t]; + for (uint t = 0; t < tiles; t++) { + NewGRFSpriteLayout *dts = &statspec->renderdata.emplace_back(); uint num_building_sprites = buf->ReadByte(); /* On error, bail out immediately. Temporary GRF data was already freed */ if (ReadSpriteLayout(buf, num_building_sprites, false, GSF_STATIONS, true, false, dts)) return CIR_DISABLED; } break; + } default: ret = CIR_UNKNOWN; @@ -8370,8 +8372,6 @@ static void ResetCustomStations() if (stations[i] == nullptr) continue; StationSpec *statspec = stations[i]; - delete[] statspec->renderdata; - /* Release this station */ delete statspec; } diff --git a/src/newgrf_station.cpp b/src/newgrf_station.cpp index 9aa3ad43be..eff5ef2b2c 100644 --- a/src/newgrf_station.cpp +++ b/src/newgrf_station.cpp @@ -798,10 +798,10 @@ bool DrawStationTile(int x, int y, RailType railtype, Axis axis, StationClassID const NewGRFSpriteLayout *layout = nullptr; DrawTileSprites tmp_rail_layout; - if (statspec->renderdata == nullptr) { + if (statspec->renderdata.empty()) { sprites = GetStationTileLayout(STATION_RAIL, tile + axis); } else { - layout = &statspec->renderdata[(tile < statspec->tiles) ? tile + axis : (uint)axis]; + layout = &statspec->renderdata[(tile < statspec->renderdata.size()) ? tile + axis : (uint)axis]; if (!layout->NeedsPreprocessing()) { sprites = layout; layout = nullptr; diff --git a/src/newgrf_station.h b/src/newgrf_station.h index 4c4a5831be..5273625ae6 100644 --- a/src/newgrf_station.h +++ b/src/newgrf_station.h @@ -112,8 +112,8 @@ enum StationRandomTrigger { /** Station specification. */ struct StationSpec { StationSpec() : cls_id(STAT_CLASS_DFLT), name(0), - disallowed_platforms(0), disallowed_lengths(0), tiles(0), - renderdata(nullptr), cargo_threshold(0), cargo_triggers(0), + disallowed_platforms(0), disallowed_lengths(0), + cargo_threshold(0), cargo_triggers(0), callback_mask(0), flags(0), pylons(0), wires(0), blocked(0), animation({0, 0, 0, 0}) {} /** @@ -145,8 +145,7 @@ struct StationSpec { * 4-5 = platform with roof, left side * 6-7 = platform with roof, right side */ - uint tiles; - NewGRFSpriteLayout *renderdata; ///< Array of tile layouts. + std::vector renderdata; ///< Array of tile layouts. /** * Cargo threshold for choosing between little and lots of cargo diff --git a/src/station_cmd.cpp b/src/station_cmd.cpp index ac78064d49..f642a914f6 100644 --- a/src/station_cmd.cpp +++ b/src/station_cmd.cpp @@ -2844,8 +2844,8 @@ static void DrawTile_Station(TileInfo *ti) } /* Ensure the chosen tile layout is valid for this custom station */ - if (statspec->renderdata != nullptr) { - layout = &statspec->renderdata[tile_layout < statspec->tiles ? tile_layout : (uint)GetRailStationAxis(ti->tile)]; + if (!statspec->renderdata.empty()) { + layout = &statspec->renderdata[tile_layout < statspec->renderdata.size() ? tile_layout : (uint)GetRailStationAxis(ti->tile)]; if (!layout->NeedsPreprocessing()) { t = layout; layout = nullptr; From 756034fa279bb6c3b81efbe13385583eb8a42d0f Mon Sep 17 00:00:00 2001 From: Peter Nelson Date: Sun, 2 May 2021 10:47:05 +0100 Subject: [PATCH 74/81] Codechange: Validate custom station platform layout tiles are permitted values only. --- src/newgrf.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/newgrf.cpp b/src/newgrf.cpp index a2671eff27..3a4fe12619 100644 --- a/src/newgrf.cpp +++ b/src/newgrf.cpp @@ -1991,6 +1991,14 @@ static ChangeInfoResult StationChangeInfo(uint stid, int numinfo, int prop, Byte const byte *layout = buf->ReadBytes(length * number); statspec->layouts[length - 1][number - 1].assign(layout, layout + length * number); + + /* Validate tile values are only the permitted 00, 02, 04 and 06. */ + for (auto &tile : statspec->layouts[length - 1][number - 1]) { + if ((tile & 6) != tile) { + grfmsg(1, "StationChangeInfo: Invalid tile %u in layout %ux%u", tile, length, number); + tile &= 6; + } + } } break; From 20762f9117c8dfbc5cc72771926563b4893592c0 Mon Sep 17 00:00:00 2001 From: Milek7 Date: Sun, 2 May 2021 20:10:07 +0200 Subject: [PATCH 75/81] Codechange: Acquire video buffer before taking game state lock to prevent erratic fast forward behaviour (#9140) --- src/video/video_driver.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/video/video_driver.cpp b/src/video/video_driver.cpp index 2f8efc4ad4..0af716f05e 100644 --- a/src/video/video_driver.cpp +++ b/src/video/video_driver.cpp @@ -131,13 +131,14 @@ void VideoDriver::Tick() this->fast_forward_via_key = false; } + /* Locking video buffer can block (especially with vsync enabled), do it before taking game state lock. */ + this->LockVideoBuffer(); + { /* Tell the game-thread to stop so we can have a go. */ std::lock_guard lock_wait(this->game_thread_wait_mutex); std::lock_guard lock_state(this->game_state_mutex); - this->LockVideoBuffer(); - this->DrainCommandQueue(); while (this->PollEvent()) {} From 1a1def99dc0913d8887a04b8132f308934cd57ee Mon Sep 17 00:00:00 2001 From: translators Date: Sun, 2 May 2021 18:15:54 +0000 Subject: [PATCH 76/81] Update: Translations from eints norwegian (bokmal): 24 changes by Anolitt russian: 8 changes by Ln-Wolf dutch: 46 changes by Afoklala spanish: 43 changes by MontyMontana french: 44 changes by arikover --- src/lang/dutch.txt | 54 +++++++++++++++++++++++++++++++---- src/lang/french.txt | 50 +++++++++++++++++++++++++++++--- src/lang/norwegian_bokmal.txt | 26 +++++++++++++++-- src/lang/russian.txt | 8 ++++++ src/lang/spanish.txt | 49 ++++++++++++++++++++++++++++--- 5 files changed, 171 insertions(+), 16 deletions(-) diff --git a/src/lang/dutch.txt b/src/lang/dutch.txt index ae1af9673a..9e54ba3fdb 100644 --- a/src/lang/dutch.txt +++ b/src/lang/dutch.txt @@ -953,7 +953,7 @@ STR_GAME_OPTIONS_CURRENCY_MYR :Maleisische Rin STR_GAME_OPTIONS_ROAD_VEHICLES_DROPDOWN_LEFT :Links rijden STR_GAME_OPTIONS_ROAD_VEHICLES_DROPDOWN_RIGHT :Rechts rijden -STR_GAME_OPTIONS_TOWN_NAMES_FRAME :{BLACK}Plaatsnamen +STR_GAME_OPTIONS_TOWN_NAMES_FRAME :{BLACK}Plaatsnamen: STR_GAME_OPTIONS_TOWN_NAMES_DROPDOWN_TOOLTIP :{BLACK}Stijl voor plaatsnamen kiezen ############ start of townname region @@ -993,6 +993,7 @@ STR_GAME_OPTIONS_AUTOSAVE_DROPDOWN_EVERY_12_MONTHS :Iedere 12 maand STR_GAME_OPTIONS_LANGUAGE :{BLACK}Taal STR_GAME_OPTIONS_LANGUAGE_TOOLTIP :{BLACK}Taal selecteren voor gebruikersscherm +STR_GAME_OPTIONS_LANGUAGE_PERCENTAGE :{STRING} ({NUM}% voltooid) STR_GAME_OPTIONS_FULLSCREEN :{BLACK}Volledig scherm STR_GAME_OPTIONS_FULLSCREEN_TOOLTIP :{BLACK}Vink dit vakje aan om OpenTTD in het volledige scherm te spelen @@ -1990,6 +1991,8 @@ STR_FACE_TIE :Stropdas: STR_FACE_EARRING :Oorbel: STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}Verander das of oorbel +STR_NETWORK_SERVER_VISIBILITY_PRIVATE :Privé +STR_NETWORK_SERVER_VISIBILITY_PUBLIC :Openbaar # Network server list STR_NETWORK_SERVER_LIST_CAPTION :{WHITE}Netwerkspel @@ -2046,13 +2049,15 @@ STR_NETWORK_SERVER_LIST_PLAYER_NAME_OSKTITLE :{BLACK}Vul je n STR_NETWORK_SERVER_LIST_ENTER_IP :{BLACK}Voer het IP-adres van de server in # Start new multiplayer server -STR_NETWORK_START_SERVER_CAPTION :{WHITE}Start nieuw multiplayerspel +STR_NETWORK_START_SERVER_CAPTION :{WHITE}Nieuw spel met meerdere spelers starten STR_NETWORK_START_SERVER_NEW_GAME_NAME :{BLACK}Spelnaam: -STR_NETWORK_START_SERVER_NEW_GAME_NAME_TOOLTIP :{BLACK}De spelnaam wordt weergegeven aan andere spelers in het multiplayerspelselectiemenu +STR_NETWORK_START_SERVER_NEW_GAME_NAME_TOOLTIP :{BLACK}De spelnaam wordt weergegeven aan andere spelers in het spelselectiemenu voor meerdere spelers STR_NETWORK_START_SERVER_SET_PASSWORD :{BLACK}Wachtwoord instellen STR_NETWORK_START_SERVER_PASSWORD_TOOLTIP :{BLACK}Beveilig je spel met een wachtwoord als je niet wilt dat dit algemeen toegankelijk is +STR_NETWORK_START_SERVER_VISIBILITY_LABEL :{BLACK}Zichtbaarheid +STR_NETWORK_START_SERVER_VISIBILITY_TOOLTIP :{BLACK}Bepaalt of andere mensen je server kunnen zien in de openbare lijst STR_NETWORK_START_SERVER_CLIENTS_SELECT :{BLACK}{NUM} speler{P "" s} STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS :{BLACK}Maximumaantal spelers: STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS_TOOLTIP :{BLACK}Kies het maximaal aantal toegestane spelers. Niet alle posities hoeven gebruikt te worden. @@ -2116,11 +2121,44 @@ STR_NETWORK_NEED_GAME_PASSWORD_CAPTION :{WHITE}Server i STR_NETWORK_NEED_COMPANY_PASSWORD_CAPTION :{WHITE}Bedrijf is beveiligd. Voer wachtwoord in # Network company list added strings -STR_NETWORK_COMPANY_LIST_CLIENT_LIST :Spelerslijst +STR_NETWORK_COMPANY_LIST_CLIENT_LIST :Spelers online # Network client list - - +STR_NETWORK_CLIENT_LIST_CAPTION :{WHITE}Meerdere spelers +STR_NETWORK_CLIENT_LIST_SERVER :{BLACK}Server +STR_NETWORK_CLIENT_LIST_SERVER_NAME :{BLACK}Naam +STR_NETWORK_CLIENT_LIST_SERVER_NAME_TOOLTIP :{BLACK}De naam van de server waar je speelt +STR_NETWORK_CLIENT_LIST_SERVER_NAME_EDIT_TOOLTIP :{BLACK}De naam van je server bewerken +STR_NETWORK_CLIENT_LIST_SERVER_NAME_QUERY_CAPTION :Servernaam +STR_NETWORK_CLIENT_LIST_SERVER_VISIBILITY :{BLACK}Zichtbaarheid +STR_NETWORK_CLIENT_LIST_SERVER_VISIBILITY_TOOLTIP :{BLACK}Bepaalt of andere mensen je server kunnen zien in de openbare lijst +STR_NETWORK_CLIENT_LIST_PLAYER :{BLACK}Speler +STR_NETWORK_CLIENT_LIST_PLAYER_NAME :{BLACK}Naam +STR_NETWORK_CLIENT_LIST_PLAYER_NAME_TOOLTIP :{BLACK}Je spelernaam +STR_NETWORK_CLIENT_LIST_PLAYER_NAME_EDIT_TOOLTIP :{BLACK}Je spelernaam bewerken +STR_NETWORK_CLIENT_LIST_PLAYER_NAME_QUERY_CAPTION :Je spelernaam +STR_NETWORK_CLIENT_LIST_ADMIN_CLIENT_TOOLTIP :{BLACK}Beheeracties die nodig zijn voor deze client +STR_NETWORK_CLIENT_LIST_ADMIN_COMPANY_TOOLTIP :{BLACK}Beheeracties die nodig zijn voor dit bedrijf +STR_NETWORK_CLIENT_LIST_JOIN_TOOLTIP :{BLACK}Meedoen met dit bedrijf +STR_NETWORK_CLIENT_LIST_CHAT_CLIENT_TOOLTIP :{BLACK}Een bericht sturen naar deze speler +STR_NETWORK_CLIENT_LIST_CHAT_COMPANY_TOOLTIP :{BLACK}Een bericht versturen naar alle spelers van dit bedrijf +STR_NETWORK_CLIENT_LIST_CHAT_SPECTATOR_TOOLTIP :{BLACK}Een bericht sturen naar alle toeschouwers +STR_NETWORK_CLIENT_LIST_SPECTATORS :Toeschouwers +STR_NETWORK_CLIENT_LIST_NEW_COMPANY :(Nieuw bedrijf) +STR_NETWORK_CLIENT_LIST_NEW_COMPANY_TOOLTIP :{BLACK}Een nieuw bedrijf maken en meedoen +STR_NETWORK_CLIENT_LIST_PLAYER_ICON_SELF_TOOLTIP :{BLACK}Dit ben jij +STR_NETWORK_CLIENT_LIST_PLAYER_ICON_HOST_TOOLTIP :{BLACK}Dit is de host van het spel + +STR_NETWORK_CLIENT_LIST_ADMIN_CLIENT_KICK :Eruit schoppen +STR_NETWORK_CLIENT_LIST_ADMIN_CLIENT_BAN :Bannen +STR_NETWORK_CLIENT_LIST_ADMIN_COMPANY_RESET :Verwijderen +STR_NETWORK_CLIENT_LIST_ADMIN_COMPANY_UNLOCK :Wachtwoord ontgrendelen + +STR_NETWORK_CLIENT_LIST_ASK_CAPTION :{WHITE}Beheeractie +STR_NETWORK_CLIENT_LIST_ASK_CLIENT_KICK :{YELLOW}Weet je zeker dat je de speler '{STRING}' eruit wilt schoppen? +STR_NETWORK_CLIENT_LIST_ASK_CLIENT_BAN :{YELLOW}Weet je zeker dat je de speler '{STRING}' wilt bannen? +STR_NETWORK_CLIENT_LIST_ASK_COMPANY_RESET :{YELLOW}Weet je zeker dat je het bedrijf '{COMPANY}' wilt verwijderen? +STR_NETWORK_CLIENT_LIST_ASK_COMPANY_UNLOCK :{YELLOW}Weet je zeker dat je het wachtwoord voor bedrijf '{COMPANY}' wilt terugstellen? STR_NETWORK_SERVER :Server STR_NETWORK_CLIENT :Speler @@ -2165,6 +2203,7 @@ STR_NETWORK_ERROR_SERVER_START :{WHITE}Kan serv STR_NETWORK_ERROR_CLIENT_START :{WHITE}Kan geen verbinding maken STR_NETWORK_ERROR_TIMEOUT :{WHITE}Verbinding nr. {NUM} kostte te veel tijd STR_NETWORK_ERROR_SERVER_ERROR :{WHITE}Er is een protocolfout gedetecteerd en de verbinding werd gesloten +STR_NETWORK_ERROR_BAD_PLAYER_NAME :{WHITE}Je spelernaam is nog niet ingesteld. Je stelt de naam in bovenin het venster Meerdere spelers STR_NETWORK_ERROR_WRONG_REVISION :{WHITE}De revisie van deze client komt niet overeen met de revisie van de server STR_NETWORK_ERROR_WRONG_PASSWORD :{WHITE}Ongeldig wachtwoord STR_NETWORK_ERROR_SERVER_FULL :{WHITE}De server is vol @@ -2177,6 +2216,7 @@ STR_NETWORK_ERROR_TIMEOUT_PASSWORD :{WHITE}Het invo STR_NETWORK_ERROR_TIMEOUT_COMPUTER :{WHITE}Uw computer is te traag om de server bij te houden 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 ############ Leave those lines in this order!! STR_NETWORK_ERROR_CLIENT_GENERAL :algemene fout @@ -2199,6 +2239,7 @@ STR_NETWORK_ERROR_CLIENT_TIMEOUT_PASSWORD :wachtwoord niet STR_NETWORK_ERROR_CLIENT_TIMEOUT_COMPUTER :algemene time-out STR_NETWORK_ERROR_CLIENT_TIMEOUT_MAP :downloaden van de kaart duurde te lang STR_NETWORK_ERROR_CLIENT_TIMEOUT_JOIN :verwerken van de kaart duurde te lang +STR_NETWORK_ERROR_CLIENT_INVALID_CLIENT_NAME :ongeldige clientnaam ############ End of leave-in-this-order STR_NETWORK_ERROR_CLIENT_GUI_LOST_CONNECTION_CAPTION :{WHITE}Mogelijk verbinding verbroken @@ -3038,6 +3079,7 @@ STR_NEWGRF_ERROR_MSG_WARNING :{RED}Waarschuwi STR_NEWGRF_ERROR_MSG_ERROR :{RED}Fout: {SILVER}{STRING} STR_NEWGRF_ERROR_MSG_FATAL :{RED}Fatale fout: {SILVER}{STRING} STR_NEWGRF_ERROR_FATAL_POPUP :{WHITE}Een fatale NewGRF-fout is ontstaan:{}{STRING} +STR_NEWGRF_ERROR_POPUP :{WHITE}Er is een NewGRF-fout opgetreden:{}{STRING} STR_NEWGRF_ERROR_VERSION_NUMBER :{1:STRING} werkt niet met de TTDPatch-versie die is opgegeven door OpenTTD STR_NEWGRF_ERROR_DOS_OR_WINDOWS :{1:STRING} is voor versie {STRING} van TTD STR_NEWGRF_ERROR_UNSET_SWITCH :{1:STRING} is ontwikkeld voor {STRING} diff --git a/src/lang/french.txt b/src/lang/french.txt index a578ea2739..b0491a80cb 100644 --- a/src/lang/french.txt +++ b/src/lang/french.txt @@ -954,7 +954,7 @@ STR_GAME_OPTIONS_CURRENCY_MYR :Malaysian Ringg STR_GAME_OPTIONS_ROAD_VEHICLES_DROPDOWN_LEFT :Conduite à gauche STR_GAME_OPTIONS_ROAD_VEHICLES_DROPDOWN_RIGHT :Conduite à droite -STR_GAME_OPTIONS_TOWN_NAMES_FRAME :{BLACK}Noms des villes +STR_GAME_OPTIONS_TOWN_NAMES_FRAME :{BLACK}Nom des villes{NBSP}: STR_GAME_OPTIONS_TOWN_NAMES_DROPDOWN_TOOLTIP :{BLACK}Sélectionner la nationalité des noms des villes ############ start of townname region @@ -994,6 +994,7 @@ STR_GAME_OPTIONS_AUTOSAVE_DROPDOWN_EVERY_12_MONTHS :Tous les 12 moi STR_GAME_OPTIONS_LANGUAGE :{BLACK}Langue STR_GAME_OPTIONS_LANGUAGE_TOOLTIP :{BLACK}Sélectionner la langue à utiliser pour l'interface +STR_GAME_OPTIONS_LANGUAGE_PERCENTAGE :{STRING} ({NUM}{NBSP}% terminé{P "" s}) STR_GAME_OPTIONS_FULLSCREEN :{BLACK}Plein écran STR_GAME_OPTIONS_FULLSCREEN_TOOLTIP :{BLACK}Cocher cette case pour jouer à OpenTTD en plein écran @@ -1991,6 +1992,8 @@ STR_FACE_TIE :Cravate{NBSP}: STR_FACE_EARRING :Boucle d'oreille{NBSP}: STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}Modifier la cravate ou la boucle d'oreille +STR_NETWORK_SERVER_VISIBILITY_PRIVATE :Privé +STR_NETWORK_SERVER_VISIBILITY_PUBLIC :Public # Network server list STR_NETWORK_SERVER_LIST_CAPTION :{WHITE}Multijoueurs @@ -2054,6 +2057,8 @@ STR_NETWORK_START_SERVER_NEW_GAME_NAME_TOOLTIP :{BLACK}Les autr STR_NETWORK_START_SERVER_SET_PASSWORD :{BLACK}Choisir le mot de passe STR_NETWORK_START_SERVER_PASSWORD_TOOLTIP :{BLACK}Protégez votre partie avec un mot de passe si vous ne souhaitez pas que d'autres l'utilisent +STR_NETWORK_START_SERVER_VISIBILITY_LABEL :{BLACK}Visibilité +STR_NETWORK_START_SERVER_VISIBILITY_TOOLTIP :{BLACK}Possibilité pour les autres personnes de vous voir dans la liste publique STR_NETWORK_START_SERVER_CLIENTS_SELECT :{BLACK}{NUM} client{P "" s} STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS :{BLACK}Nombre de clients maximum{NBSP}: STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS_TOOLTIP :{BLACK}Choisir un nombre maximum de clients. Tous les emplacements n'auront pas besoin d'être remplis @@ -2117,11 +2122,44 @@ STR_NETWORK_NEED_GAME_PASSWORD_CAPTION :{WHITE}Le serve STR_NETWORK_NEED_COMPANY_PASSWORD_CAPTION :{WHITE}La compagnie est protégée. Entrez le mot de passe # Network company list added strings -STR_NETWORK_COMPANY_LIST_CLIENT_LIST :Liste des clients +STR_NETWORK_COMPANY_LIST_CLIENT_LIST :Joueurs en ligne # Network client list - - +STR_NETWORK_CLIENT_LIST_CAPTION :{WHITE}Multijoueur +STR_NETWORK_CLIENT_LIST_SERVER :{BLACK}Serveur +STR_NETWORK_CLIENT_LIST_SERVER_NAME :{BLACK}Nom +STR_NETWORK_CLIENT_LIST_SERVER_NAME_TOOLTIP :{BLACK}Nom du serveur sur lequel vous jouez +STR_NETWORK_CLIENT_LIST_SERVER_NAME_EDIT_TOOLTIP :{BLACK}Éditer le nom de votre serveur +STR_NETWORK_CLIENT_LIST_SERVER_NAME_QUERY_CAPTION :Nom du serveur +STR_NETWORK_CLIENT_LIST_SERVER_VISIBILITY :{BLACK}Visibilité +STR_NETWORK_CLIENT_LIST_SERVER_VISIBILITY_TOOLTIP :{BLACK}Possibilité pour les autres personnes de voir votre serveur dans la liste publique +STR_NETWORK_CLIENT_LIST_PLAYER :{BLACK}Joueur +STR_NETWORK_CLIENT_LIST_PLAYER_NAME :{BLACK}Nom +STR_NETWORK_CLIENT_LIST_PLAYER_NAME_TOOLTIP :{BLACK}Votre nom de jeu +STR_NETWORK_CLIENT_LIST_PLAYER_NAME_EDIT_TOOLTIP :{BLACK}Éditer votre nom +STR_NETWORK_CLIENT_LIST_PLAYER_NAME_QUERY_CAPTION :Votre nom de jeu +STR_NETWORK_CLIENT_LIST_ADMIN_CLIENT_TOOLTIP :{BLACK}Actions administratives à accomplir pour ce client +STR_NETWORK_CLIENT_LIST_ADMIN_COMPANY_TOOLTIP :{BLACK}Actions administratives à accomplir pour cette compagnie +STR_NETWORK_CLIENT_LIST_JOIN_TOOLTIP :{BLACK}Rejoindre cette compagnie +STR_NETWORK_CLIENT_LIST_CHAT_CLIENT_TOOLTIP :{BLACK}Envoyer un message à cette personne +STR_NETWORK_CLIENT_LIST_CHAT_COMPANY_TOOLTIP :{BLACK}Envoyer un message à tous les joueurs de cette compagnie +STR_NETWORK_CLIENT_LIST_CHAT_SPECTATOR_TOOLTIP :{BLACK}Envoyer un message à tous les spectateurs +STR_NETWORK_CLIENT_LIST_SPECTATORS :Spectateurs +STR_NETWORK_CLIENT_LIST_NEW_COMPANY :(Nouvelle compagnie) +STR_NETWORK_CLIENT_LIST_NEW_COMPANY_TOOLTIP :{BLACK}Créer une nouvelle compagnie et la rejoindre +STR_NETWORK_CLIENT_LIST_PLAYER_ICON_SELF_TOOLTIP :{BLACK}C'est vous +STR_NETWORK_CLIENT_LIST_PLAYER_ICON_HOST_TOOLTIP :{BLACK}C'est l'hôte du jeu + +STR_NETWORK_CLIENT_LIST_ADMIN_CLIENT_KICK :Exclure +STR_NETWORK_CLIENT_LIST_ADMIN_CLIENT_BAN :Bannir +STR_NETWORK_CLIENT_LIST_ADMIN_COMPANY_RESET :Supprimer +STR_NETWORK_CLIENT_LIST_ADMIN_COMPANY_UNLOCK :Débloquer le mot de passe + +STR_NETWORK_CLIENT_LIST_ASK_CAPTION :{WHITE}Action administrative +STR_NETWORK_CLIENT_LIST_ASK_CLIENT_KICK :{YELLOW}Êtes-vous sûr de vouloir exclure '{STRING}'{NBSP}? +STR_NETWORK_CLIENT_LIST_ASK_CLIENT_BAN :{YELLOW}Êtes-vous sûr de vouloir bannir '{STRING}'{NBSP}? +STR_NETWORK_CLIENT_LIST_ASK_COMPANY_RESET :{YELLOW}Êtes-vous sûr de vouloir supprimer la compagnie '{COMPANY}'{NBSP}? +STR_NETWORK_CLIENT_LIST_ASK_COMPANY_UNLOCK :{YELLOW}Êtes-vous sûr de vouloir réinitialiser le mot de passe de la compagnie '{COMPANY}'? STR_NETWORK_SERVER :Serveur STR_NETWORK_CLIENT :Client @@ -2166,6 +2204,7 @@ STR_NETWORK_ERROR_SERVER_START :{WHITE}Le serve STR_NETWORK_ERROR_CLIENT_START :{WHITE}Échec de la connexion STR_NETWORK_ERROR_TIMEOUT :{WHITE}La connexion n°{NBSP}{NUM} a dépassé le temps d'attente STR_NETWORK_ERROR_SERVER_ERROR :{WHITE}Une erreur de protocole a été détectée et la connexion a été fermée +STR_NETWORK_ERROR_BAD_PLAYER_NAME :{WHITE}Vous n'avez pas de nom. Il doit être entré en haut de la fenêtre Multijoueur STR_NETWORK_ERROR_WRONG_REVISION :{WHITE}Le numéro de version/révision de ce client ne correspond pas à celui du serveur STR_NETWORK_ERROR_WRONG_PASSWORD :{WHITE}Mot de passe incorrect STR_NETWORK_ERROR_SERVER_FULL :{WHITE}Le serveur est complet @@ -2178,6 +2217,7 @@ STR_NETWORK_ERROR_TIMEOUT_PASSWORD :{WHITE}Vous ave STR_NETWORK_ERROR_TIMEOUT_COMPUTER :{WHITE}Votre ordinateur est trop lent pour suivre le serveur STR_NETWORK_ERROR_TIMEOUT_MAP :{WHITE}Votre ordinateur a mis trop de temps pour télécharger la carte STR_NETWORK_ERROR_TIMEOUT_JOIN :{WHITE}Votre ordinateur a mis trop de temps pour rejoindre le serveur +STR_NETWORK_ERROR_INVALID_CLIENT_NAME :{WHITE}Votre nom n'est pas valide ############ Leave those lines in this order!! STR_NETWORK_ERROR_CLIENT_GENERAL :erreur générale @@ -2200,6 +2240,7 @@ STR_NETWORK_ERROR_CLIENT_TIMEOUT_PASSWORD :aucun mot de pa STR_NETWORK_ERROR_CLIENT_TIMEOUT_COMPUTER :délai dépassé STR_NETWORK_ERROR_CLIENT_TIMEOUT_MAP :télécharger la carte a pris trop de temps STR_NETWORK_ERROR_CLIENT_TIMEOUT_JOIN :le traitement de la carte a pris trop de temps +STR_NETWORK_ERROR_CLIENT_INVALID_CLIENT_NAME :nom client invalide ############ End of leave-in-this-order STR_NETWORK_ERROR_CLIENT_GUI_LOST_CONNECTION_CAPTION :{WHITE}Possible perte de connexion @@ -3039,6 +3080,7 @@ STR_NEWGRF_ERROR_MSG_WARNING :{RED}Attention{ STR_NEWGRF_ERROR_MSG_ERROR :{RED}Erreur{NBSP}: {SILVER}{STRING} STR_NEWGRF_ERROR_MSG_FATAL :{RED}Erreur fatale{NBSP}: {SILVER}{STRING} STR_NEWGRF_ERROR_FATAL_POPUP :{WHITE}Une erreur NewGRF fatale est survenue{NBSP}:{}{STRING} +STR_NEWGRF_ERROR_POPUP :{WHITE}Une erreur NewGRF est survenue{NBSP}:{}{STRING} STR_NEWGRF_ERROR_VERSION_NUMBER :{1:STRING} ne fonctionnera pas avec la version de TTDPatch rapportée par OpenTTD STR_NEWGRF_ERROR_DOS_OR_WINDOWS :{1:STRING} est conçu pour la version {STRING} de TTD STR_NEWGRF_ERROR_UNSET_SWITCH :{1:STRING} est conçu pour être utilisé avec {STRING} diff --git a/src/lang/norwegian_bokmal.txt b/src/lang/norwegian_bokmal.txt index b79089f237..1cbf14fbec 100644 --- a/src/lang/norwegian_bokmal.txt +++ b/src/lang/norwegian_bokmal.txt @@ -955,7 +955,7 @@ STR_GAME_OPTIONS_CURRENCY_MYR :Malaysisk Ringg STR_GAME_OPTIONS_ROAD_VEHICLES_DROPDOWN_LEFT :Venstrekjøring STR_GAME_OPTIONS_ROAD_VEHICLES_DROPDOWN_RIGHT :Høyrekjøring -STR_GAME_OPTIONS_TOWN_NAMES_FRAME :{BLACK}Bynavn +STR_GAME_OPTIONS_TOWN_NAMES_FRAME :{BLACK}Bynavn: STR_GAME_OPTIONS_TOWN_NAMES_DROPDOWN_TOOLTIP :{BLACK}Velg nasjonalitet på bynavn ############ start of townname region @@ -1994,6 +1994,7 @@ STR_FACE_TIE :Slips: STR_FACE_EARRING :Ørering: STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}Endre slips eller ørering +STR_NETWORK_SERVER_VISIBILITY_PRIVATE :Privat STR_NETWORK_SERVER_VISIBILITY_PUBLIC :Offentlig # Network server list @@ -2059,6 +2060,7 @@ STR_NETWORK_START_SERVER_SET_PASSWORD :{BLACK}Sett pas STR_NETWORK_START_SERVER_PASSWORD_TOOLTIP :{BLACK}Beskytt ditt spill med et passord hvis du ikke vil at hvem som helst skal bli med på det STR_NETWORK_START_SERVER_VISIBILITY_LABEL :{BLACK}Synlighet +STR_NETWORK_START_SERVER_VISIBILITY_TOOLTIP :{BLACK}Om andre mennesker kan se serveren din i den offentlige oppføringen STR_NETWORK_START_SERVER_CLIENTS_SELECT :{BLACK}{NUM} klient{P "" er} STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS :{BLACK}Maks antall klienter: STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS.small :dra og slipp @@ -2123,24 +2125,44 @@ STR_NETWORK_NEED_GAME_PASSWORD_CAPTION :{WHITE}Tjeneren STR_NETWORK_NEED_COMPANY_PASSWORD_CAPTION :{WHITE}Firmaet er beskyttet. Skriv inn passord # Network company list added strings -STR_NETWORK_COMPANY_LIST_CLIENT_LIST :Liste over klienter +STR_NETWORK_COMPANY_LIST_CLIENT_LIST :Påloggede spillere # Network client list +STR_NETWORK_CLIENT_LIST_CAPTION :{WHITE}Flerspiller +STR_NETWORK_CLIENT_LIST_SERVER :{BLACK}Server STR_NETWORK_CLIENT_LIST_SERVER_NAME :{BLACK}Navn +STR_NETWORK_CLIENT_LIST_SERVER_NAME_TOOLTIP :{BLACK}Navnet på serveren du spiller på +STR_NETWORK_CLIENT_LIST_SERVER_NAME_EDIT_TOOLTIP :{BLACK}Rediger navnet på serveren +STR_NETWORK_CLIENT_LIST_SERVER_NAME_QUERY_CAPTION :Servernavn +STR_NETWORK_CLIENT_LIST_SERVER_VISIBILITY :{BLACK}Synlighet STR_NETWORK_CLIENT_LIST_SERVER_VISIBILITY_TOOLTIP :{BLACK}Om andre mennesker kan se serveren din i den offentlige oppføringen STR_NETWORK_CLIENT_LIST_PLAYER :{BLACK}Spiller STR_NETWORK_CLIENT_LIST_PLAYER_NAME :{BLACK}Navn +STR_NETWORK_CLIENT_LIST_PLAYER_NAME_TOOLTIP :{BLACK}Ditt spillernavn STR_NETWORK_CLIENT_LIST_PLAYER_NAME_EDIT_TOOLTIP :{BLACK}Rediger ditt spillernavn STR_NETWORK_CLIENT_LIST_PLAYER_NAME_QUERY_CAPTION :Ditt spillernavn STR_NETWORK_CLIENT_LIST_ADMIN_CLIENT_TOOLTIP :{BLACK}Administrative handlinger å utføre for denne klienten +STR_NETWORK_CLIENT_LIST_ADMIN_COMPANY_TOOLTIP :{BLACK}Administrative tiltak å utføre for dette firmaet STR_NETWORK_CLIENT_LIST_JOIN_TOOLTIP :{BLACK}Bli med i dette firmaet +STR_NETWORK_CLIENT_LIST_CHAT_CLIENT_TOOLTIP :{BLACK}Send en melding til denne spilleren STR_NETWORK_CLIENT_LIST_CHAT_COMPANY_TOOLTIP :{BLACK}Send en melding til alle spillerne i dette firmaet STR_NETWORK_CLIENT_LIST_CHAT_SPECTATOR_TOOLTIP :{BLACK}Send en melding til alle tilskuerne +STR_NETWORK_CLIENT_LIST_SPECTATORS :Tilskuere +STR_NETWORK_CLIENT_LIST_NEW_COMPANY :(Nytt firma) STR_NETWORK_CLIENT_LIST_NEW_COMPANY_TOOLTIP :{BLACK}Opprett et nytt firma og bli med i det +STR_NETWORK_CLIENT_LIST_PLAYER_ICON_SELF_TOOLTIP :{BLACK}Dette er deg +STR_NETWORK_CLIENT_LIST_PLAYER_ICON_HOST_TOOLTIP :{BLACK}Dette er verten for spillet STR_NETWORK_CLIENT_LIST_ADMIN_CLIENT_KICK :Spark +STR_NETWORK_CLIENT_LIST_ADMIN_CLIENT_BAN :Utesteng +STR_NETWORK_CLIENT_LIST_ADMIN_COMPANY_RESET :Slett +STR_NETWORK_CLIENT_LIST_ADMIN_COMPANY_UNLOCK :Låse opp passord +STR_NETWORK_CLIENT_LIST_ASK_CAPTION :{WHITE}Administratorhandling +STR_NETWORK_CLIENT_LIST_ASK_CLIENT_KICK :{YELLOW}Er du sikker på at du vil utestenge spiller '{STRING}'? STR_NETWORK_CLIENT_LIST_ASK_CLIENT_BAN :{YELLOW}Er du sikker på at du vil utestenge spiller '{STRING}'? +STR_NETWORK_CLIENT_LIST_ASK_COMPANY_RESET :{YELLOW}Er du sikker på at du vil slette firmaet '{COMPANY}'? +STR_NETWORK_CLIENT_LIST_ASK_COMPANY_UNLOCK :{YELLOW}Er du sikker på at du vil tilbakestille passordet til firma '{COMPANY}'? STR_NETWORK_SERVER :Tjener STR_NETWORK_CLIENT :Klient diff --git a/src/lang/russian.txt b/src/lang/russian.txt index 1a8d94e9d1..bf9666d409 100644 --- a/src/lang/russian.txt +++ b/src/lang/russian.txt @@ -2142,6 +2142,8 @@ STR_FACE_TIE :Галстук: STR_FACE_EARRING :Серьга: STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}Изменить галстук или серьгу +STR_NETWORK_SERVER_VISIBILITY_PRIVATE :Публичный +STR_NETWORK_SERVER_VISIBILITY_PUBLIC :Частный # Network server list STR_NETWORK_SERVER_LIST_CAPTION :{WHITE}Сетевая игра @@ -2205,6 +2207,8 @@ STR_NETWORK_START_SERVER_NEW_GAME_NAME_TOOLTIP :{BLACK}Назв STR_NETWORK_START_SERVER_SET_PASSWORD :{BLACK}Установить пароль STR_NETWORK_START_SERVER_PASSWORD_TOOLTIP :{BLACK}Защитите вашу игру паролем, если не хотите, чтобы к ней могли подключиться посторонние. +STR_NETWORK_START_SERVER_VISIBILITY_LABEL :{BLACK}Видимость +STR_NETWORK_START_SERVER_VISIBILITY_TOOLTIP :{BLACK}Видимость вашего сервера в публичном списке STR_NETWORK_START_SERVER_CLIENTS_SELECT :{BLACK}{NUM} клиент{P "" а ов} STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS :{BLACK}Макс. количество клиентов: STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS_TOOLTIP :{BLACK}Выбор максимального числа клиентов. Не все места должны быть заняты @@ -2271,6 +2275,10 @@ STR_NETWORK_NEED_COMPANY_PASSWORD_CAPTION :{WHITE}Комп STR_NETWORK_COMPANY_LIST_CLIENT_LIST :Список клиентов # Network client list +STR_NETWORK_CLIENT_LIST_CAPTION :{WHITE}Сетевая игра +STR_NETWORK_CLIENT_LIST_PLAYER_NAME :{BLACK}Имя +STR_NETWORK_CLIENT_LIST_PLAYER_NAME_TOOLTIP :{BLACK}Ваше игровое имя +STR_NETWORK_CLIENT_LIST_PLAYER_NAME_QUERY_CAPTION :Ваше игровое имя STR_NETWORK_CLIENT_LIST_ADMIN_CLIENT_TOOLTIP :{BLACK}Действия администратора, применимые к этому клиенту STR_NETWORK_CLIENT_LIST_ADMIN_CLIENT_KICK :Отключить diff --git a/src/lang/spanish.txt b/src/lang/spanish.txt index 1af0a7fc4f..3ff09a3674 100644 --- a/src/lang/spanish.txt +++ b/src/lang/spanish.txt @@ -954,7 +954,7 @@ STR_GAME_OPTIONS_CURRENCY_MYR :Ringgit malasio STR_GAME_OPTIONS_ROAD_VEHICLES_DROPDOWN_LEFT :Conducir por la izquierda STR_GAME_OPTIONS_ROAD_VEHICLES_DROPDOWN_RIGHT :Conducir por la derecha -STR_GAME_OPTIONS_TOWN_NAMES_FRAME :{BLACK}Nombres de municipios +STR_GAME_OPTIONS_TOWN_NAMES_FRAME :{BLACK}Nombres de municipios: STR_GAME_OPTIONS_TOWN_NAMES_DROPDOWN_TOOLTIP :{BLACK}Selección del estilo del nombre de los municipios ############ start of townname region @@ -1992,6 +1992,8 @@ STR_FACE_TIE :Corbata: STR_FACE_EARRING :Pendientes: STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}Cambiar corbata o pendientes +STR_NETWORK_SERVER_VISIBILITY_PRIVATE :Privado +STR_NETWORK_SERVER_VISIBILITY_PUBLIC :Público # Network server list STR_NETWORK_SERVER_LIST_CAPTION :{WHITE}Multijugador @@ -2055,6 +2057,8 @@ STR_NETWORK_START_SERVER_NEW_GAME_NAME_TOOLTIP :{BLACK}La parti STR_NETWORK_START_SERVER_SET_PASSWORD :{BLACK}Establecer contraseña STR_NETWORK_START_SERVER_PASSWORD_TOOLTIP :{BLACK}Protege tu partida con una contraseña si no quieres que sea universalmente accesible +STR_NETWORK_START_SERVER_VISIBILITY_LABEL :{BLACK}Visibilidad +STR_NETWORK_START_SERVER_VISIBILITY_TOOLTIP :{BLACK}Establece si otras personas pueden ver el servidor en la lista de servidores públicos STR_NETWORK_START_SERVER_CLIENTS_SELECT :{BLACK}{NUM} cliente{P "" s} STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS :{BLACK}Número máximo de clientes: STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS_TOOLTIP :{BLACK}Selecciona el número máximo de clientes. No es necesario ocupar todos los espacios @@ -2118,11 +2122,44 @@ STR_NETWORK_NEED_GAME_PASSWORD_CAPTION :{WHITE}Servidor STR_NETWORK_NEED_COMPANY_PASSWORD_CAPTION :{WHITE}Empresa protegida. Introduce la contraseña # Network company list added strings -STR_NETWORK_COMPANY_LIST_CLIENT_LIST :Lista de clientes +STR_NETWORK_COMPANY_LIST_CLIENT_LIST :Jugadores en línea # Network client list - - +STR_NETWORK_CLIENT_LIST_CAPTION :{WHITE}Multijugador +STR_NETWORK_CLIENT_LIST_SERVER :{BLACK}Servidor +STR_NETWORK_CLIENT_LIST_SERVER_NAME :{BLACK}Nombre +STR_NETWORK_CLIENT_LIST_SERVER_NAME_TOOLTIP :{BLACK}Nombre del servidor en el que estás jugando +STR_NETWORK_CLIENT_LIST_SERVER_NAME_EDIT_TOOLTIP :{BLACK}Modifica el nombre de tu servidor +STR_NETWORK_CLIENT_LIST_SERVER_NAME_QUERY_CAPTION :Nombre del servidor +STR_NETWORK_CLIENT_LIST_SERVER_VISIBILITY :{BLACK}Visibilidad +STR_NETWORK_CLIENT_LIST_SERVER_VISIBILITY_TOOLTIP :{BLACK}Establece si otras personas pueden ver el servidor en la lista de servidores públicos +STR_NETWORK_CLIENT_LIST_PLAYER :{BLACK}Jugador +STR_NETWORK_CLIENT_LIST_PLAYER_NAME :{BLACK}Nombre +STR_NETWORK_CLIENT_LIST_PLAYER_NAME_TOOLTIP :{BLACK}Tu nombre de jugador +STR_NETWORK_CLIENT_LIST_PLAYER_NAME_EDIT_TOOLTIP :{BLACK}Modifica tu nombre de jugador +STR_NETWORK_CLIENT_LIST_PLAYER_NAME_QUERY_CAPTION :Tu nombre de jugador +STR_NETWORK_CLIENT_LIST_ADMIN_CLIENT_TOOLTIP :{BLACK}Acciones de administrador a realizar para este cliente +STR_NETWORK_CLIENT_LIST_ADMIN_COMPANY_TOOLTIP :{BLACK}Acciones de administrador a realizar para esta compañía +STR_NETWORK_CLIENT_LIST_JOIN_TOOLTIP :{BLACK}Unirse a esta compañía +STR_NETWORK_CLIENT_LIST_CHAT_CLIENT_TOOLTIP :{BLACK}Manda un mensaje a este jugador +STR_NETWORK_CLIENT_LIST_CHAT_COMPANY_TOOLTIP :{BLACK}Manda un mensaje a todos los jugadores de esta compañía +STR_NETWORK_CLIENT_LIST_CHAT_SPECTATOR_TOOLTIP :{BLACK}Manda un mensaje a todos los observadores +STR_NETWORK_CLIENT_LIST_SPECTATORS :Observadores +STR_NETWORK_CLIENT_LIST_NEW_COMPANY :(Nueva compañía) +STR_NETWORK_CLIENT_LIST_NEW_COMPANY_TOOLTIP :{BLACK}Crea una nueva companía y te une a ella +STR_NETWORK_CLIENT_LIST_PLAYER_ICON_SELF_TOOLTIP :{BLACK}Éste eres tú +STR_NETWORK_CLIENT_LIST_PLAYER_ICON_HOST_TOOLTIP :{BLACK}Éste es el servidor de la partida + +STR_NETWORK_CLIENT_LIST_ADMIN_CLIENT_KICK :Expulsar +STR_NETWORK_CLIENT_LIST_ADMIN_CLIENT_BAN :Prohibir el acceso +STR_NETWORK_CLIENT_LIST_ADMIN_COMPANY_RESET :Eliminar +STR_NETWORK_CLIENT_LIST_ADMIN_COMPANY_UNLOCK :Eliminar contraseña + +STR_NETWORK_CLIENT_LIST_ASK_CAPTION :{WHITE}Acción de administrador +STR_NETWORK_CLIENT_LIST_ASK_CLIENT_KICK :{YELLOW}¿Estás seguro de que deseas expulsar al jugador '{STRING}'? +STR_NETWORK_CLIENT_LIST_ASK_CLIENT_BAN :{YELLOW}¿Estás seguro de que deseas prohibir el acceso al jugador '{STRING}'? +STR_NETWORK_CLIENT_LIST_ASK_COMPANY_RESET :{YELLOW}¿Estás seguro de que quieres eliminar la compañía '{COMPANY}'? +STR_NETWORK_CLIENT_LIST_ASK_COMPANY_UNLOCK :{YELLOW}¿Estás seguro de que quieres eliminar la contraseña de la compañía '{COMPANY}'? STR_NETWORK_SERVER :Servidor STR_NETWORK_CLIENT :Cliente @@ -2167,6 +2204,7 @@ STR_NETWORK_ERROR_SERVER_START :{WHITE}No se ha STR_NETWORK_ERROR_CLIENT_START :{WHITE}No se pudo conectar STR_NETWORK_ERROR_TIMEOUT :{WHITE}Tiempo de espera agotado en conexión #{NUM} STR_NETWORK_ERROR_SERVER_ERROR :{WHITE}Se ha producido un error de protocolo y la conexión ha sido cerrada +STR_NETWORK_ERROR_BAD_PLAYER_NAME :{WHITE}No se ha establecido tu nombre de jugador. El nombre se puede establecer en la parte superior de la ventana de Multijugador STR_NETWORK_ERROR_WRONG_REVISION :{WHITE}La versión de este cliente no corresponde con la versión del servidor STR_NETWORK_ERROR_WRONG_PASSWORD :{WHITE}Contraseña incorrecta STR_NETWORK_ERROR_SERVER_FULL :{WHITE}El servidor está completo @@ -2179,6 +2217,7 @@ STR_NETWORK_ERROR_TIMEOUT_PASSWORD :{WHITE}Has tard STR_NETWORK_ERROR_TIMEOUT_COMPUTER :{WHITE}Su ordenador es demasiado lento para seguir la velocidad del servidor STR_NETWORK_ERROR_TIMEOUT_MAP :{WHITE}Su ordenador necesitó demasiado tiempo para descargar el mapa STR_NETWORK_ERROR_TIMEOUT_JOIN :{WHITE}Su ordenador necesitó demasiado tiempo para conectar al servidor +STR_NETWORK_ERROR_INVALID_CLIENT_NAME :{WHITE}Tu nombre de jugador no es válido ############ Leave those lines in this order!! STR_NETWORK_ERROR_CLIENT_GENERAL :error general @@ -2201,6 +2240,7 @@ STR_NETWORK_ERROR_CLIENT_TIMEOUT_PASSWORD :no se ha recibi STR_NETWORK_ERROR_CLIENT_TIMEOUT_COMPUTER :tiempo agotado en general STR_NETWORK_ERROR_CLIENT_TIMEOUT_MAP :la descarga del mapa ha necesitado demasiado tiempo STR_NETWORK_ERROR_CLIENT_TIMEOUT_JOIN :el procesado del mapa ha necesitado demasiado tiempo +STR_NETWORK_ERROR_CLIENT_INVALID_CLIENT_NAME :nombre de cliente inválido ############ End of leave-in-this-order STR_NETWORK_ERROR_CLIENT_GUI_LOST_CONNECTION_CAPTION :{WHITE}Posible pérdida de conexión @@ -3040,6 +3080,7 @@ STR_NEWGRF_ERROR_MSG_WARNING :{RED}Atención: STR_NEWGRF_ERROR_MSG_ERROR :{RED}Error: {SILVER}{STRING} STR_NEWGRF_ERROR_MSG_FATAL :{RED}Error Fatal: {SILVER}{STRING} STR_NEWGRF_ERROR_FATAL_POPUP :{WHITE}Ha ocurrido un error fatal de NewGRF:{}{STRING} +STR_NEWGRF_ERROR_POPUP :{WHITE}Ha ocurrido un error de NewGRF:{}{STRING} STR_NEWGRF_ERROR_VERSION_NUMBER :{1:STRING} no funcionará con la con la versión de TTDPatch informada por OpenTTD STR_NEWGRF_ERROR_DOS_OR_WINDOWS :{1:STRING} es para la versión {STRING} de TTD STR_NEWGRF_ERROR_UNSET_SWITCH :{1:STRING} está diseñado para ser usado con {STRING} From ece9a356dcae3c9f7fe85a0f7dab87e06d8eb299 Mon Sep 17 00:00:00 2001 From: Jonathan G Rennison Date: Mon, 3 May 2021 15:03:25 +0100 Subject: [PATCH 77/81] Fix #9113: Assertion failure when removing airport with order backup (#9182) --- src/station_cmd.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/station_cmd.cpp b/src/station_cmd.cpp index f642a914f6..451dedbad4 100644 --- a/src/station_cmd.cpp +++ b/src/station_cmd.cpp @@ -2394,9 +2394,9 @@ static CommandCost RemoveAirport(TileIndex tile, DoCommandFlag flags) if (flags & DC_EXEC) { for (uint i = 0; i < st->airport.GetNumHangars(); ++i) { - DeleteWindowById( - WC_VEHICLE_DEPOT, st->airport.GetHangarTile(i) - ); + TileIndex tile_cur = st->airport.GetHangarTile(i); + OrderBackup::Reset(tile_cur, false); + DeleteWindowById(WC_VEHICLE_DEPOT, tile_cur); } const AirportSpec *as = st->airport.GetSpec(); @@ -2418,7 +2418,6 @@ static CommandCost RemoveAirport(TileIndex tile, DoCommandFlag flags) cost.AddCost(_price[PR_CLEAR_STATION_AIRPORT]); if (flags & DC_EXEC) { - if (IsHangarTile(tile_cur)) OrderBackup::Reset(tile_cur, false); DeleteAnimatedTile(tile_cur); DoClearSquare(tile_cur); DeleteNewGRFInspectWindow(GSF_AIRPORTTILES, tile_cur); From 0bc6f3234664cbd71dab89ddf75b14616cced160 Mon Sep 17 00:00:00 2001 From: PeterN Date: Mon, 3 May 2021 15:12:47 +0100 Subject: [PATCH 78/81] Fix #9174: Don't update text effect if it has been reset. (#9183) --- src/texteff.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/texteff.cpp b/src/texteff.cpp index e29326a90a..1c4722ae61 100644 --- a/src/texteff.cpp +++ b/src/texteff.cpp @@ -79,6 +79,7 @@ void UpdateTextEffect(TextEffectID te_id, StringID msg) void UpdateAllTextEffectVirtCoords() { for (auto &te : _text_effects) { + if (te.string_id == INVALID_STRING_ID) continue; SetDParam(0, te.params_1); SetDParam(1, te.params_2); te.UpdatePosition(te.center, te.top, te.string_id, te.string_id - 1); From 08781d96ed385158a5b452be1779260a92204e4e Mon Sep 17 00:00:00 2001 From: PeterN Date: Mon, 3 May 2021 16:39:20 +0100 Subject: [PATCH 79/81] Fix: Query windows may be partially drawn initially. (#9184) Query window was not marked dirty after being moved on init. It was then marked dirty once the white border flash completed. --- src/misc_gui.cpp | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/misc_gui.cpp b/src/misc_gui.cpp index b2de451404..e9ee7b193b 100644 --- a/src/misc_gui.cpp +++ b/src/misc_gui.cpp @@ -1162,12 +1162,9 @@ struct QueryWindow : public Window { this->caption = caption; this->message = message; this->proc = callback; + this->parent = parent; this->InitNested(WN_CONFIRM_POPUP_QUERY); - - this->parent = parent; - this->left = parent->left + (parent->width / 2) - (this->width / 2); - this->top = parent->top + (parent->height / 2) - (this->height / 2); } ~QueryWindow() @@ -1175,6 +1172,14 @@ struct QueryWindow : public Window { if (this->proc != nullptr) this->proc(this->parent, false); } + void FindWindowPlacementAndResize(int def_width, int def_height) override + { + /* Position query window over the calling window, ensuring it's within screen bounds. */ + this->left = Clamp(parent->left + (parent->width / 2) - (this->width / 2), 0, _screen.width - this->width); + this->top = Clamp(parent->top + (parent->height / 2) - (this->height / 2), 0, _screen.height - this->height); + this->SetDirty(); + } + void SetStringParameters(int widget) const override { switch (widget) { From 6bd7f8816dfee1a5e697d18e30aad4b5ef7e320f Mon Sep 17 00:00:00 2001 From: rubidium42 Date: Mon, 3 May 2021 17:40:19 +0200 Subject: [PATCH 80/81] Fix #9117, 04ce1f07: [Fluidsynth] Infinite wait when stopping song (#9181) In FluidSynth 2.2.0 an extra state was added to denote stopping. To transition from this state to a stopped state the rendering needs to be running. Since 04ce1f07 locking was added that skipped the rendering when something else held a lock, so the state would never get to stopped and join would never return. --- src/music/fluidsynth.cpp | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/music/fluidsynth.cpp b/src/music/fluidsynth.cpp index 000432e1da..b5cbf46aa1 100644 --- a/src/music/fluidsynth.cpp +++ b/src/music/fluidsynth.cpp @@ -163,14 +163,21 @@ void MusicDriver_FluidSynth::PlaySong(const MusicSongInfo &song) void MusicDriver_FluidSynth::StopSong() { - std::lock_guard lock{ _midi.synth_mutex }; + { + std::lock_guard lock{ _midi.synth_mutex }; + + if (!_midi.player) return; - if (!_midi.player) return; + fluid_player_stop(_midi.player); + } - fluid_player_stop(_midi.player); + /* 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"); } + + std::lock_guard lock{ _midi.synth_mutex }; delete_fluid_player(_midi.player); fluid_synth_system_reset(_midi.synth); fluid_synth_all_sounds_off(_midi.synth, -1); From 8228021afea409ad41268f995ce6ab629f3cbc0d Mon Sep 17 00:00:00 2001 From: translators Date: Mon, 3 May 2021 15:50:01 +0000 Subject: [PATCH 81/81] Update: Translations from eints norwegian (bokmal): 1 change by Anolitt spanish (mexican): 1 change by absay korean: 1 change by telk5093 russian: 48 changes by Ln-Wolf catalan: 1 change by J0anJosep portuguese: 1 change by azulcosta portuguese (brazilian): 1 change by Vimerum --- src/lang/brazilian_portuguese.txt | 1 + src/lang/catalan.txt | 1 + src/lang/korean.txt | 1 + src/lang/norwegian_bokmal.txt | 1 + src/lang/portuguese.txt | 1 + src/lang/russian.txt | 71 +++++++++++++++++++++---------- src/lang/spanish_MX.txt | 1 + 7 files changed, 54 insertions(+), 23 deletions(-) diff --git a/src/lang/brazilian_portuguese.txt b/src/lang/brazilian_portuguese.txt index 96e9fbbd1c..3cf4a64be0 100644 --- a/src/lang/brazilian_portuguese.txt +++ b/src/lang/brazilian_portuguese.txt @@ -2218,6 +2218,7 @@ STR_NETWORK_ERROR_TIMEOUT_COMPUTER :{WHITE}Seu comp STR_NETWORK_ERROR_TIMEOUT_MAP :{WHITE}Seu computador demorou demais para baixar o mapa STR_NETWORK_ERROR_TIMEOUT_JOIN :{WHITE}Seu computador demorou demais para entrar no servidor STR_NETWORK_ERROR_INVALID_CLIENT_NAME :{WHITE}Seu nome de jogador não é válido +STR_NETWORK_ERROR_SERVER_TOO_OLD :{WHITE}O servidor requisitado é muito antigo para esse cliente ############ Leave those lines in this order!! STR_NETWORK_ERROR_CLIENT_GENERAL :erro geral diff --git a/src/lang/catalan.txt b/src/lang/catalan.txt index 34b6cfb0b4..bc89f55f40 100644 --- a/src/lang/catalan.txt +++ b/src/lang/catalan.txt @@ -2218,6 +2218,7 @@ STR_NETWORK_ERROR_TIMEOUT_COMPUTER :{WHITE}El teu o STR_NETWORK_ERROR_TIMEOUT_MAP :{WHITE}El teu ordinador ha tardat massa a descarregar el mapa STR_NETWORK_ERROR_TIMEOUT_JOIN :{WHITE}El teu ordinador ha tardat massa a unir-se al servidor STR_NETWORK_ERROR_INVALID_CLIENT_NAME :{WHITE}El vostre nom de jugador no és vàlid. +STR_NETWORK_ERROR_SERVER_TOO_OLD :{WHITE}El servidor és massa antic per a aquest client. ############ Leave those lines in this order!! STR_NETWORK_ERROR_CLIENT_GENERAL :error general diff --git a/src/lang/korean.txt b/src/lang/korean.txt index 2a51f3dfcc..78a46e95eb 100644 --- a/src/lang/korean.txt +++ b/src/lang/korean.txt @@ -2218,6 +2218,7 @@ STR_NETWORK_ERROR_TIMEOUT_COMPUTER :{WHITE}사용 STR_NETWORK_ERROR_TIMEOUT_MAP :{WHITE}지도 다운로드 시간을 초과하였습니다 STR_NETWORK_ERROR_TIMEOUT_JOIN :{WHITE}서버 접속 시간을 초과하였습니다 STR_NETWORK_ERROR_INVALID_CLIENT_NAME :{WHITE}사용할 수 없는 이름입니다 +STR_NETWORK_ERROR_SERVER_TOO_OLD :{WHITE}검색한 서버는 현재 버전에 비해 너무 오래된 서버입니다 ############ Leave those lines in this order!! STR_NETWORK_ERROR_CLIENT_GENERAL :일반 오류 diff --git a/src/lang/norwegian_bokmal.txt b/src/lang/norwegian_bokmal.txt index 1cbf14fbec..882605dcea 100644 --- a/src/lang/norwegian_bokmal.txt +++ b/src/lang/norwegian_bokmal.txt @@ -2221,6 +2221,7 @@ STR_NETWORK_ERROR_TIMEOUT_COMPUTER :{WHITE}Din data STR_NETWORK_ERROR_TIMEOUT_MAP :{WHITE}Din datamaskin brukte for lang tid på å laste ned kartet STR_NETWORK_ERROR_TIMEOUT_JOIN :{WHITE}Din datamaskin brukte for lang tid på å koble til tjeneren STR_NETWORK_ERROR_INVALID_CLIENT_NAME :{WHITE}Spillernavnet ditt er ugyldig +STR_NETWORK_ERROR_SERVER_TOO_OLD :{WHITE}Den forespurte serveren er for gammel for denne klienten ############ Leave those lines in this order!! STR_NETWORK_ERROR_CLIENT_GENERAL :generell feil diff --git a/src/lang/portuguese.txt b/src/lang/portuguese.txt index d97e52569a..4473b1cb39 100644 --- a/src/lang/portuguese.txt +++ b/src/lang/portuguese.txt @@ -2218,6 +2218,7 @@ STR_NETWORK_ERROR_TIMEOUT_COMPUTER :{WHITE}O seu co STR_NETWORK_ERROR_TIMEOUT_MAP :{WHITE}O seu computador demorou demasiado a transferir o mapa STR_NETWORK_ERROR_TIMEOUT_JOIN :{WHITE}O seu computador demorou demasiado a ligar ao servidor STR_NETWORK_ERROR_INVALID_CLIENT_NAME :{WHITE}O seu nome de jogador não é válido +STR_NETWORK_ERROR_SERVER_TOO_OLD :{WHITE}O servidor consultado é muito antigo para este cliente ############ Leave those lines in this order!! STR_NETWORK_ERROR_CLIENT_GENERAL :erro geral diff --git a/src/lang/russian.txt b/src/lang/russian.txt index bf9666d409..337d79ce9c 100644 --- a/src/lang/russian.txt +++ b/src/lang/russian.txt @@ -390,7 +390,7 @@ STR_SHOW_HIDDEN_ENGINES_VEHICLE_AIRCRAFT_TOOLTIP :{BLACK}Если STR_BUTTON_DEFAULT :{BLACK}По умолчанию STR_BUTTON_CANCEL :{BLACK}Отмена STR_BUTTON_OK :{BLACK}OK -STR_WARNING_PASSWORD_SECURITY :{YELLOW}Внимание: администраторы сервера могут увидеть ваш пароль. +STR_WARNING_PASSWORD_SECURITY :{YELLOW}Внимание: администраторы сервера могут увидеть текст, введённый в это поле. # On screen keyboard window STR_OSK_KEYBOARD_LAYOUT :`1234567890-=\qwertyuiop[]asdfghjkl;' zxcvbnm,./ . @@ -924,7 +924,7 @@ STR_SMALLMAP_TOOLTIP_ENABLE_ALL_CARGOS :{BLACK}Отоб STR_STATUSBAR_TOOLTIP_SHOW_LAST_NEWS :{BLACK}Показать последнее сообщение или новость STR_STATUSBAR_COMPANY_NAME :{SILVER}- - {COMPANY} - - STR_STATUSBAR_PAUSED :{YELLOW}* * ПАУЗА * * -STR_STATUSBAR_PAUSED_LINK_GRAPH :{ORANGE}* * ПАУЗА (ожидает обновления графы ссылок) * * +STR_STATUSBAR_PAUSED_LINK_GRAPH :{ORANGE}* * ПАУЗА (ожидает обновления графа распределения) * * STR_STATUSBAR_AUTOSAVE :{RED}АВТОСОХРАНЕНИЕ STR_STATUSBAR_SAVING_GAME :{RED}* * СОХРАНЕНИЕ ИГРЫ * * @@ -2162,7 +2162,7 @@ STR_NETWORK_SERVER_LIST_DATE_CAPTION :{BLACK}Дата STR_NETWORK_SERVER_LIST_DATE_CAPTION_TOOLTIP :{BLACK}Текущая дата STR_NETWORK_SERVER_LIST_YEARS_CAPTION :{BLACK}Года STR_NETWORK_SERVER_LIST_YEARS_CAPTION_TOOLTIP :{BLACK}Количество лет{}в игре -STR_NETWORK_SERVER_LIST_INFO_ICONS_TOOLTIP :{BLACK}Язык, версия сервера и т.п. +STR_NETWORK_SERVER_LIST_INFO_ICONS_TOOLTIP :{BLACK}Язык, версия сервера и{NBSP}т.{NBSP}п. STR_NETWORK_SERVER_LIST_CLICK_GAME_TO_SELECT :{BLACK}Выберите игру из списка STR_NETWORK_SERVER_LIST_LAST_JOINED_SERVER :{BLACK}Последний сервер, к которому вы подключались: @@ -2177,20 +2177,20 @@ STR_NETWORK_SERVER_LIST_SERVER_VERSION :{SILVER}Вер STR_NETWORK_SERVER_LIST_SERVER_ADDRESS :{SILVER}Адрес сервера: {WHITE}{STRING} STR_NETWORK_SERVER_LIST_START_DATE :{SILVER}Дата начала: {WHITE}{DATE_SHORT} STR_NETWORK_SERVER_LIST_CURRENT_DATE :{SILVER}Текущая дата: {WHITE}{DATE_SHORT} -STR_NETWORK_SERVER_LIST_PASSWORD :{SILVER}Защищено паролем! -STR_NETWORK_SERVER_LIST_SERVER_OFFLINE :{SILVER}СЕРВЕР ОТКЛЮЧЕН +STR_NETWORK_SERVER_LIST_PASSWORD :{SILVER}Защищён паролем! +STR_NETWORK_SERVER_LIST_SERVER_OFFLINE :{SILVER}СЕРВЕР ОТКЛЮЧЁН STR_NETWORK_SERVER_LIST_SERVER_FULL :{SILVER}СЕРВЕР ЗАПОЛНЕН STR_NETWORK_SERVER_LIST_VERSION_MISMATCH :{SILVER}ВЕРСИЯ НЕ ПОДХОДИТ STR_NETWORK_SERVER_LIST_GRF_MISMATCH :{SILVER}НЕ СОВПАДАЕТ НАБОР NEWGRF STR_NETWORK_SERVER_LIST_JOIN_GAME :{BLACK}Присоединиться -STR_NETWORK_SERVER_LIST_REFRESH :{BLACK}Обновить сервер +STR_NETWORK_SERVER_LIST_REFRESH :{BLACK}Обновить информацию STR_NETWORK_SERVER_LIST_REFRESH_TOOLTIP :{BLACK}Обновить информацию о сервере STR_NETWORK_SERVER_LIST_SEARCH_SERVER_INTERNET :{BLACK}Искать в интернете -STR_NETWORK_SERVER_LIST_SEARCH_SERVER_INTERNET_TOOLTIP :{BLACK} Искать в Интернете общедоступные серверы -STR_NETWORK_SERVER_LIST_SEARCH_SERVER_LAN :{BLACK}Искать LAN -STR_NETWORK_SERVER_LIST_SEARCH_SERVER_LAN_TOOLTIP :{BLACK} Поиск серверов в локальной сети +STR_NETWORK_SERVER_LIST_SEARCH_SERVER_INTERNET_TOOLTIP :{BLACK}Поиск общедоступных серверов в интернете +STR_NETWORK_SERVER_LIST_SEARCH_SERVER_LAN :{BLACK}Искать в ЛВС +STR_NETWORK_SERVER_LIST_SEARCH_SERVER_LAN_TOOLTIP :{BLACK}Поиск серверов в локальной сети STR_NETWORK_SERVER_LIST_ADD_SERVER :{BLACK}Добавить сервер STR_NETWORK_SERVER_LIST_ADD_SERVER_TOOLTIP :{BLACK}Добавить сервер в список, который будет автоматически проверяться на идущие игры STR_NETWORK_SERVER_LIST_START_SERVER :{BLACK}Запуск сервера @@ -2211,13 +2211,13 @@ STR_NETWORK_START_SERVER_VISIBILITY_LABEL :{BLACK}Види STR_NETWORK_START_SERVER_VISIBILITY_TOOLTIP :{BLACK}Видимость вашего сервера в публичном списке STR_NETWORK_START_SERVER_CLIENTS_SELECT :{BLACK}{NUM} клиент{P "" а ов} STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS :{BLACK}Макс. количество клиентов: -STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS_TOOLTIP :{BLACK}Выбор максимального числа клиентов. Не все места должны быть заняты +STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS_TOOLTIP :{BLACK}Выбор максимального числа клиентов. Не все места обязательно должны быть заняты. STR_NETWORK_START_SERVER_COMPANIES_SELECT :{BLACK}{NUM} компани{P я и й} STR_NETWORK_START_SERVER_NUMBER_OF_COMPANIES :{BLACK}Макс. количество компаний: STR_NETWORK_START_SERVER_NUMBER_OF_COMPANIES_TOOLTIP :{BLACK}Ограничить максимальное количество компаний на сервере -STR_NETWORK_START_SERVER_SPECTATORS_SELECT :{BLACK}{NUM} наблюдател{P ь я ей} -STR_NETWORK_START_SERVER_NUMBER_OF_SPECTATORS :{BLACK}Макс. количество наблюдателей: -STR_NETWORK_START_SERVER_NUMBER_OF_SPECTATORS_TOOLTIP :{BLACK}Ограничить максимальное количество наблюдателей на сервере +STR_NETWORK_START_SERVER_SPECTATORS_SELECT :{BLACK}{NUM} зрител{P ь я ей} +STR_NETWORK_START_SERVER_NUMBER_OF_SPECTATORS :{BLACK}Макс. количество зрителей: +STR_NETWORK_START_SERVER_NUMBER_OF_SPECTATORS_TOOLTIP :{BLACK}Ограничить максимальное количество зрителей на сервере STR_NETWORK_START_SERVER_LANGUAGE_SPOKEN :{BLACK}Язык общения: STR_NETWORK_START_SERVER_LANGUAGE_TOOLTIP :{BLACK}Другие игроки будут знать, на каком языке общаются на сервере @@ -2227,7 +2227,7 @@ STR_NETWORK_START_SERVER_NEW_GAME_NAME_OSKTITLE :{BLACK}Введ STR_NETWORK_GAME_LOBBY_CAPTION :{WHITE}Состояние сетевой игры STR_NETWORK_GAME_LOBBY_PREPARE_TO_JOIN :{BLACK}Подготовка соединения: {ORANGE}{STRING} -STR_NETWORK_GAME_LOBBY_COMPANY_LIST_TOOLTIP :{BLACK}Список компаний в игре. Вы можете присоединиться к существующей или основать новую, если есть свободный слот +STR_NETWORK_GAME_LOBBY_COMPANY_LIST_TOOLTIP :{BLACK}Список компаний в игре. Вы можете присоединиться к одной из существующих или основать новую, если есть свободная позиция. STR_NETWORK_GAME_LOBBY_COMPANY_INFO :{SILVER}ИНФОРМАЦИЯ О КОМПАНИИ STR_NETWORK_GAME_LOBBY_COMPANY_NAME :{SILVER}Название компании: {WHITE}{STRING} @@ -2272,25 +2272,48 @@ STR_NETWORK_NEED_GAME_PASSWORD_CAPTION :{WHITE}Серв STR_NETWORK_NEED_COMPANY_PASSWORD_CAPTION :{WHITE}Компания защищена. Введите пароль. # Network company list added strings -STR_NETWORK_COMPANY_LIST_CLIENT_LIST :Список клиентов +STR_NETWORK_COMPANY_LIST_CLIENT_LIST :Список игроков # Network client list STR_NETWORK_CLIENT_LIST_CAPTION :{WHITE}Сетевая игра +STR_NETWORK_CLIENT_LIST_SERVER :{BLACK}Сервер +STR_NETWORK_CLIENT_LIST_SERVER_NAME :{BLACK}Название +STR_NETWORK_CLIENT_LIST_SERVER_NAME_TOOLTIP :{BLACK}Название вашего сервера +STR_NETWORK_CLIENT_LIST_SERVER_NAME_EDIT_TOOLTIP :{BLACK}Сменить название сервера +STR_NETWORK_CLIENT_LIST_SERVER_NAME_QUERY_CAPTION :Название сервера +STR_NETWORK_CLIENT_LIST_SERVER_VISIBILITY :{BLACK}Видимость +STR_NETWORK_CLIENT_LIST_SERVER_VISIBILITY_TOOLTIP :{BLACK}Видимость вашего сервера в публичном списке +STR_NETWORK_CLIENT_LIST_PLAYER :{BLACK}Игрок STR_NETWORK_CLIENT_LIST_PLAYER_NAME :{BLACK}Имя STR_NETWORK_CLIENT_LIST_PLAYER_NAME_TOOLTIP :{BLACK}Ваше игровое имя +STR_NETWORK_CLIENT_LIST_PLAYER_NAME_EDIT_TOOLTIP :{BLACK}Изменить имя игрока STR_NETWORK_CLIENT_LIST_PLAYER_NAME_QUERY_CAPTION :Ваше игровое имя -STR_NETWORK_CLIENT_LIST_ADMIN_CLIENT_TOOLTIP :{BLACK}Действия администратора, применимые к этому клиенту +STR_NETWORK_CLIENT_LIST_ADMIN_CLIENT_TOOLTIP :{BLACK}Административные действия, применимые к этому игроку +STR_NETWORK_CLIENT_LIST_ADMIN_COMPANY_TOOLTIP :{BLACK}Административные действия, применимые к этой компании +STR_NETWORK_CLIENT_LIST_JOIN_TOOLTIP :{BLACK}Присоединиться к этой компании +STR_NETWORK_CLIENT_LIST_CHAT_CLIENT_TOOLTIP :{BLACK}Отправить сообщение этому игроку +STR_NETWORK_CLIENT_LIST_CHAT_COMPANY_TOOLTIP :{BLACK}Послать сообщение всем игрокам этой компании +STR_NETWORK_CLIENT_LIST_CHAT_SPECTATOR_TOOLTIP :{BLACK}Послать сообщение всем зрителям +STR_NETWORK_CLIENT_LIST_SPECTATORS :Зрители +STR_NETWORK_CLIENT_LIST_NEW_COMPANY :(Новая компания) +STR_NETWORK_CLIENT_LIST_NEW_COMPANY_TOOLTIP :{BLACK}Основать новую транспортную компанию и присоединиться к ней +STR_NETWORK_CLIENT_LIST_PLAYER_ICON_SELF_TOOLTIP :{BLACK}Это вы! +STR_NETWORK_CLIENT_LIST_PLAYER_ICON_HOST_TOOLTIP :{BLACK}Это организатор игры STR_NETWORK_CLIENT_LIST_ADMIN_CLIENT_KICK :Отключить STR_NETWORK_CLIENT_LIST_ADMIN_CLIENT_BAN :Заблокировать +STR_NETWORK_CLIENT_LIST_ADMIN_COMPANY_RESET :Удалить +STR_NETWORK_CLIENT_LIST_ADMIN_COMPANY_UNLOCK :Сбросить пароль STR_NETWORK_CLIENT_LIST_ASK_CAPTION :{WHITE}Подтверждение действия STR_NETWORK_CLIENT_LIST_ASK_CLIENT_KICK :{YELLOW}Отключить игрока «{STRING}»? STR_NETWORK_CLIENT_LIST_ASK_CLIENT_BAN :{YELLOW}Заблокировать игрока «{STRING}»? +STR_NETWORK_CLIENT_LIST_ASK_COMPANY_RESET :{YELLOW}Удалить компанию «{COMPANY}»? +STR_NETWORK_CLIENT_LIST_ASK_COMPANY_UNLOCK :{YELLOW}Сбросить пароль у компании «{COMPANY}»? STR_NETWORK_SERVER :Сервер STR_NETWORK_CLIENT :Клиент -STR_NETWORK_SPECTATORS :Наблюдатели +STR_NETWORK_SPECTATORS :Зрители # Network set password STR_COMPANY_PASSWORD_CANCEL :{BLACK}Не сохранять пароль @@ -2335,8 +2358,8 @@ STR_NETWORK_ERROR_BAD_PLAYER_NAME :{WHITE}Не у STR_NETWORK_ERROR_WRONG_REVISION :{WHITE}Версия этого клиента не совместима с версией сервера STR_NETWORK_ERROR_WRONG_PASSWORD :{WHITE}Неверный пароль STR_NETWORK_ERROR_SERVER_FULL :{WHITE}Сервер переполнен -STR_NETWORK_ERROR_SERVER_BANNED :{WHITE}Вас забанили на этом сервере -STR_NETWORK_ERROR_KICKED :{WHITE}Вас выкинули из игры +STR_NETWORK_ERROR_SERVER_BANNED :{WHITE}Вас заблокировали на этом сервере +STR_NETWORK_ERROR_KICKED :{WHITE}Вас отключили от игры STR_NETWORK_ERROR_KICK_MESSAGE :{WHITE}Причина: {STRING} STR_NETWORK_ERROR_CHEATER :{WHITE}Чит-коды не разрешены на этом сервере STR_NETWORK_ERROR_TOO_MANY_COMMANDS :{WHITE}Вы посылали на сервер слишком много команд @@ -2345,6 +2368,7 @@ STR_NETWORK_ERROR_TIMEOUT_COMPUTER :{WHITE}Ваш STR_NETWORK_ERROR_TIMEOUT_MAP :{WHITE}Ваш компьютер тратит много времени на загрузку карты STR_NETWORK_ERROR_TIMEOUT_JOIN :{WHITE}Ваш компьютер тратит много времени на подключение к серверу STR_NETWORK_ERROR_INVALID_CLIENT_NAME :{WHITE}Неверно указано имя игрока +STR_NETWORK_ERROR_SERVER_TOO_OLD :{WHITE}На сервере запущена устаревшая версия игры ############ Leave those lines in this order!! STR_NETWORK_ERROR_CLIENT_GENERAL :общая ошибка @@ -2387,13 +2411,13 @@ STR_NETWORK_SERVER_MESSAGE_GAME_REASON_NOT_ENOUGH_PLAYERS :количес STR_NETWORK_SERVER_MESSAGE_GAME_REASON_CONNECTING_CLIENTS :подключение клиентов STR_NETWORK_SERVER_MESSAGE_GAME_REASON_MANUAL :вручную STR_NETWORK_SERVER_MESSAGE_GAME_REASON_GAME_SCRIPT :игровой скрипт -STR_NETWORK_SERVER_MESSAGE_GAME_REASON_LINK_GRAPH :ожидает обновления графы ссылок +STR_NETWORK_SERVER_MESSAGE_GAME_REASON_LINK_GRAPH :ожидает обновления графа распределения ############ End of leave-in-this-order -STR_NETWORK_MESSAGE_CLIENT_LEAVING :покинул +STR_NETWORK_MESSAGE_CLIENT_LEAVING :отключение STR_NETWORK_MESSAGE_CLIENT_JOINED :*** {STRING} подключился к игре STR_NETWORK_MESSAGE_CLIENT_JOINED_ID :*** {STRING} подключился к игре (клиент #{2:NUM}) STR_NETWORK_MESSAGE_CLIENT_COMPANY_JOIN :*** {STRING} подключился к компании #{2:NUM} -STR_NETWORK_MESSAGE_CLIENT_COMPANY_SPECTATE :*** {STRING} подключился наблюдателем +STR_NETWORK_MESSAGE_CLIENT_COMPANY_SPECTATE :*** {STRING} подключился в качестве зрителя STR_NETWORK_MESSAGE_CLIENT_COMPANY_NEW :*** {STRING} основал новую компанию (#{2:NUM}) STR_NETWORK_MESSAGE_CLIENT_LEFT :*** {STRING} покинул игру ({2:STRING}) STR_NETWORK_MESSAGE_NAME_CHANGE :*** {STRING} изменил имя на {STRING} @@ -3231,6 +3255,7 @@ STR_NEWGRF_ERROR_MSG_WARNING :{RED}Внима STR_NEWGRF_ERROR_MSG_ERROR :{RED}Ошибка: {SILVER}{STRING} STR_NEWGRF_ERROR_MSG_FATAL :{RED}Критическая ошибка: {SILVER}{STRING} STR_NEWGRF_ERROR_FATAL_POPUP :{WHITE}Ошибка при работе с NewGRF:{}{STRING} +STR_NEWGRF_ERROR_POPUP :{WHITE}Ошибка, связанная с модулем NewGRF:{}{STRING} STR_NEWGRF_ERROR_VERSION_NUMBER :{1:STRING} не будет работать с версией TTDPatch, сообщенной OpenTTD. STR_NEWGRF_ERROR_DOS_OR_WINDOWS :Файл {1:STRING} требует {STRING}-версию TTD STR_NEWGRF_ERROR_UNSET_SWITCH :{1:STRING} сделан для использования совместно с {STRING} @@ -5344,7 +5369,7 @@ STR_DEFAULT_SIGN_NAME :Табличк STR_COMPANY_SOMEONE :кто-то STR_SAVEGAME_NAME_DEFAULT :{COMPANY}, {STRING} -STR_SAVEGAME_NAME_SPECTATOR :Наблюдатель, {1:STRING} +STR_SAVEGAME_NAME_SPECTATOR :Зритель, {1:STRING} # Viewport strings STR_VIEWPORT_TOWN_POP :{WHITE}{TOWN} ({COMMA}) diff --git a/src/lang/spanish_MX.txt b/src/lang/spanish_MX.txt index 08d5bdea6b..6cae2f782f 100644 --- a/src/lang/spanish_MX.txt +++ b/src/lang/spanish_MX.txt @@ -2218,6 +2218,7 @@ STR_NETWORK_ERROR_TIMEOUT_COMPUTER :{WHITE}Tu compu STR_NETWORK_ERROR_TIMEOUT_MAP :{WHITE}Tu computadora tardó demasiado en descargar el mapa STR_NETWORK_ERROR_TIMEOUT_JOIN :{WHITE}Tu computadora tardó demasiado en conectarse al servidor STR_NETWORK_ERROR_INVALID_CLIENT_NAME :{WHITE}Tu nombre de jugador no es válido +STR_NETWORK_ERROR_SERVER_TOO_OLD :{WHITE}El servidor es demasiado viejo para este cliente ############ Leave those lines in this order!! STR_NETWORK_ERROR_CLIENT_GENERAL :error general