Merge branch 'master' into jgrpp-beta
# Conflicts: # src/engine_base.h # src/gfxinit.cpp # src/graph_gui.cpp # src/lang/brazilian_portuguese.txt # src/lang/dutch.txt # src/lang/french.txt # src/lang/korean.txt # src/lang/norwegian_bokmal.txt # src/lang/portuguese.txt # src/lang/russian.txt # src/lang/spanish.txt # src/lang/spanish_MX.txt # src/network/core/address.cpp # src/network/core/game_info.h # src/network/core/os_abstraction.h # src/network/core/udp.cpp # src/network/network_client.cpp # src/network/network_client.h # src/network/network_internal.h # src/newgrf_engine.cpp # src/settings_gui.cpp # src/station_cmd.cpp # src/string_func.h # src/town_gui.cpp # src/video/video_driver.cpp # src/widget_type.h
This commit is contained in:
		@@ -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;
 | 
			
		||||
 
 | 
			
		||||
@@ -969,16 +969,15 @@ 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);
 | 
			
		||||
	return true;
 | 
			
		||||
	return NetworkClientConnectGame(_settings_client.network.last_joined, playas);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DEF_CONSOLE_CMD(ConNetworkConnect)
 | 
			
		||||
@@ -991,37 +990,8 @@ 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;
 | 
			
		||||
 | 
			
		||||
	ParseGameConnectionString(&company, &port, ip);
 | 
			
		||||
 | 
			
		||||
	IConsolePrintF(CC_DEFAULT, "Connecting to %s...", ip);
 | 
			
		||||
	if (company != nullptr) {
 | 
			
		||||
		join_as = (CompanyID)atoi(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--;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if (port != nullptr) {
 | 
			
		||||
		rport = atoi(port);
 | 
			
		||||
		IConsolePrintF(CC_DEFAULT, "    port: %s", port);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	NetworkClientConnectGame(ip, rport, join_as);
 | 
			
		||||
 | 
			
		||||
	return true;
 | 
			
		||||
	return NetworkClientConnectGame(argv[1], COMPANY_NEW_COMPANY);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*********************************
 | 
			
		||||
 
 | 
			
		||||
@@ -67,12 +67,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;
 | 
			
		||||
@@ -137,11 +131,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
 | 
			
		||||
 
 | 
			
		||||
@@ -18,6 +18,12 @@
 | 
			
		||||
 | 
			
		||||
#include "3rdparty/cpp-btree/btree_map.h"
 | 
			
		||||
 | 
			
		||||
struct WagonOverride {
 | 
			
		||||
	std::vector<EngineID> engines;
 | 
			
		||||
	CargoID cargo;
 | 
			
		||||
	const SpriteGroup *group;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
typedef Pool<Engine, EngineID, 64, 64000> EnginePool;
 | 
			
		||||
extern EnginePool _engine_pool;
 | 
			
		||||
 | 
			
		||||
@@ -59,17 +65,15 @@ struct Engine : EnginePool::PoolItem<&_engine_pool> {
 | 
			
		||||
	 * evaluating callbacks.
 | 
			
		||||
	 */
 | 
			
		||||
	GRFFilePropsBase<NUM_CARGO + 2> grf_prop;
 | 
			
		||||
	uint16 overrides_count;
 | 
			
		||||
	struct WagonOverride *overrides;
 | 
			
		||||
	std::vector<WagonOverride> overrides;
 | 
			
		||||
	uint16 list_position;
 | 
			
		||||
 | 
			
		||||
	SpriteGroupCallbacksUsed callbacks_used = SGCU_ALL;
 | 
			
		||||
	uint64 cb36_properties_used = UINT64_MAX;
 | 
			
		||||
	btree::btree_map<const SpriteGroup *, uint64> sprite_group_cb36_properties_used;
 | 
			
		||||
 | 
			
		||||
	Engine();
 | 
			
		||||
	Engine() {}
 | 
			
		||||
	Engine(VehicleType type, EngineID base);
 | 
			
		||||
	~Engine();
 | 
			
		||||
	bool IsEnabled() const;
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
 
 | 
			
		||||
@@ -960,7 +960,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
 | 
			
		||||
@@ -1000,6 +1000,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
 | 
			
		||||
@@ -1997,6 +1998,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
 | 
			
		||||
@@ -2060,6 +2063,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
 | 
			
		||||
@@ -2123,12 +2128,45 @@ 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
 | 
			
		||||
STR_NETWORK_COMPANY_LIST_SPECTATE                               :Assistir
 | 
			
		||||
 | 
			
		||||
# 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
 | 
			
		||||
@@ -2173,6 +2211,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
 | 
			
		||||
@@ -2185,6 +2224,8 @@ 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
 | 
			
		||||
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
 | 
			
		||||
@@ -2207,6 +2248,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
 | 
			
		||||
@@ -3050,6 +3092,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}
 | 
			
		||||
 
 | 
			
		||||
@@ -2225,6 +2225,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
 | 
			
		||||
@@ -3091,6 +3092,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}
 | 
			
		||||
 
 | 
			
		||||
@@ -959,7 +959,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
 | 
			
		||||
@@ -999,6 +999,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
 | 
			
		||||
@@ -1996,6 +1997,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
 | 
			
		||||
@@ -2052,13 +2055,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.
 | 
			
		||||
@@ -2122,12 +2127,45 @@ 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
 | 
			
		||||
STR_NETWORK_COMPANY_LIST_SPECTATE                               :Toekijken
 | 
			
		||||
 | 
			
		||||
# 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
 | 
			
		||||
@@ -2172,6 +2210,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
 | 
			
		||||
@@ -2184,6 +2223,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
 | 
			
		||||
@@ -2206,6 +2246,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
 | 
			
		||||
@@ -3049,6 +3090,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}
 | 
			
		||||
 
 | 
			
		||||
@@ -2649,6 +2649,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
 | 
			
		||||
 
 | 
			
		||||
@@ -3114,6 +3114,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}
 | 
			
		||||
 
 | 
			
		||||
@@ -3090,6 +3090,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ä
 | 
			
		||||
@@ -4473,7 +4474,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
 | 
			
		||||
@@ -4506,7 +4507,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...
 | 
			
		||||
 
 | 
			
		||||
@@ -960,7 +960,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
 | 
			
		||||
@@ -1000,6 +1000,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
 | 
			
		||||
@@ -1997,6 +1998,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
 | 
			
		||||
@@ -2060,6 +2063,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
 | 
			
		||||
@@ -2123,12 +2128,45 @@ 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
 | 
			
		||||
STR_NETWORK_COMPANY_LIST_SPECTATE                               :Spectateur
 | 
			
		||||
 | 
			
		||||
# 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
 | 
			
		||||
@@ -2173,6 +2211,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
 | 
			
		||||
@@ -2185,6 +2224,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
 | 
			
		||||
@@ -2207,6 +2247,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
 | 
			
		||||
@@ -3050,6 +3091,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}
 | 
			
		||||
 
 | 
			
		||||
@@ -3785,6 +3785,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
 | 
			
		||||
 
 | 
			
		||||
@@ -2231,8 +2231,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 시스템
 | 
			
		||||
@@ -2649,6 +2649,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                                :일반 오류
 | 
			
		||||
@@ -3927,7 +3928,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_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 "와" "과"} 같이 사용해야 합니다
 | 
			
		||||
 
 | 
			
		||||
@@ -961,7 +961,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
 | 
			
		||||
@@ -2000,6 +2000,8 @@ 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
 | 
			
		||||
STR_NETWORK_SERVER_LIST_CAPTION                                 :{WHITE}Flerspiller
 | 
			
		||||
@@ -2063,6 +2065,8 @@ 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_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
 | 
			
		||||
@@ -2127,12 +2131,45 @@ 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
 | 
			
		||||
STR_NETWORK_COMPANY_LIST_SPECTATE                               :Vær tilskuer
 | 
			
		||||
 | 
			
		||||
# 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
 | 
			
		||||
@@ -2191,6 +2228,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
 | 
			
		||||
@@ -3057,6 +3095,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}
 | 
			
		||||
 
 | 
			
		||||
@@ -960,7 +960,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
 | 
			
		||||
@@ -1000,6 +1000,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
 | 
			
		||||
@@ -1997,6 +1998,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
 | 
			
		||||
@@ -2049,7 +2052,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
 | 
			
		||||
@@ -2060,6 +2063,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.
 | 
			
		||||
@@ -2123,12 +2128,45 @@ 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"
 | 
			
		||||
STR_NETWORK_COMPANY_LIST_SPECTATE                               :Assistir
 | 
			
		||||
 | 
			
		||||
# 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
 | 
			
		||||
@@ -2173,6 +2211,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
 | 
			
		||||
@@ -2185,6 +2224,8 @@ 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
 | 
			
		||||
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
 | 
			
		||||
@@ -2207,6 +2248,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
 | 
			
		||||
@@ -3050,6 +3092,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}
 | 
			
		||||
 
 | 
			
		||||
@@ -396,7 +396,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,./ .
 | 
			
		||||
@@ -931,7 +931,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}*  *  СОХРАНЕНИЕ ИГРЫ  *  *
 | 
			
		||||
 | 
			
		||||
@@ -2151,6 +2151,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}Сетевая игра
 | 
			
		||||
@@ -2169,7 +2171,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}Последний сервер, к которому вы подключались:
 | 
			
		||||
@@ -2184,20 +2186,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}Запуск сервера
 | 
			
		||||
@@ -2214,15 +2216,17 @@ 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}Выбор максимального числа клиентов. Не все места должны быть заняты
 | 
			
		||||
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}Другие игроки будут знать, на каком языке общаются на сервере
 | 
			
		||||
 | 
			
		||||
@@ -2232,7 +2236,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}
 | 
			
		||||
@@ -2277,22 +2281,49 @@ 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                            :Список игроков
 | 
			
		||||
STR_NETWORK_COMPANY_LIST_SPECTATE                               :Наблюдать
 | 
			
		||||
 | 
			
		||||
# Network client list
 | 
			
		||||
STR_NETWORK_CLIENT_LIST_ADMIN_CLIENT_TOOLTIP                    :{BLACK}Действия администратора, применимые к этому клиенту
 | 
			
		||||
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_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}Не сохранять пароль
 | 
			
		||||
@@ -2337,8 +2368,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}Вы посылали на сервер слишком много команд
 | 
			
		||||
@@ -2347,6 +2378,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                                :общая ошибка
 | 
			
		||||
@@ -2389,13 +2421,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}
 | 
			
		||||
@@ -3240,6 +3272,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}
 | 
			
		||||
@@ -5371,7 +5404,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})
 | 
			
		||||
 
 | 
			
		||||
@@ -960,7 +960,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
 | 
			
		||||
@@ -1998,6 +1998,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
 | 
			
		||||
@@ -2061,6 +2063,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
 | 
			
		||||
@@ -2124,12 +2128,45 @@ 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
 | 
			
		||||
STR_NETWORK_COMPANY_LIST_SPECTATE                               :Observar
 | 
			
		||||
 | 
			
		||||
# 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
 | 
			
		||||
@@ -2174,6 +2211,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
 | 
			
		||||
@@ -2186,6 +2224,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
 | 
			
		||||
@@ -2208,6 +2247,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
 | 
			
		||||
@@ -3051,6 +3091,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}
 | 
			
		||||
 
 | 
			
		||||
@@ -960,7 +960,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
 | 
			
		||||
@@ -1998,6 +1998,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
 | 
			
		||||
@@ -2061,6 +2063,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
 | 
			
		||||
@@ -2124,12 +2128,45 @@ 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
 | 
			
		||||
STR_NETWORK_COMPANY_LIST_SPECTATE                               :Observar
 | 
			
		||||
 | 
			
		||||
# 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
 | 
			
		||||
@@ -2188,6 +2225,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
 | 
			
		||||
@@ -3054,6 +3092,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}
 | 
			
		||||
 
 | 
			
		||||
@@ -8,6 +8,7 @@ add_files(
 | 
			
		||||
    game_info.h
 | 
			
		||||
    host.cpp
 | 
			
		||||
    host.h
 | 
			
		||||
    os_abstraction.cpp
 | 
			
		||||
    os_abstraction.h
 | 
			
		||||
    packet.cpp
 | 
			
		||||
    packet.h
 | 
			
		||||
 
 | 
			
		||||
@@ -94,6 +94,18 @@ void NetworkAddress::GetAddressAsString(char *buffer, const char *last, bool wit
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 /**
 | 
			
		||||
  * Get the address as a string, e.g. 127.0.0.1:12345.
 | 
			
		||||
  * @param with_family whether to add the family (e.g. IPvX).
 | 
			
		||||
  * @return the address
 | 
			
		||||
  */
 | 
			
		||||
std::string NetworkAddress::GetAddressAsString(bool with_family)
 | 
			
		||||
{
 | 
			
		||||
	char buf[NETWORK_HOSTNAME_LENGTH + 6 + 7];
 | 
			
		||||
	this->GetAddressAsString(buf, lastof(buf), with_family);
 | 
			
		||||
	return buf;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Get the address as a string, e.g. 127.0.0.1:12345.
 | 
			
		||||
 * @param with_family whether to add the family (e.g. IPvX).
 | 
			
		||||
@@ -311,12 +323,11 @@ 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) {
 | 
			
		||||
		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;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -325,8 +336,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, 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;
 | 
			
		||||
	}
 | 
			
		||||
@@ -342,28 +353,28 @@ 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(), NetworkError::GetLast().AsString());
 | 
			
		||||
		closesocket(sock);
 | 
			
		||||
		return INVALID_SOCKET;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* If no fd is selected, the timeout has been reached. */
 | 
			
		||||
	if (n == 0) {
 | 
			
		||||
		DEBUG(net, 1, "[%s] timed out while connecting to %s", type, address);
 | 
			
		||||
		DEBUG(net, 1, "[%s] timed out while connecting to %s", type, address.c_str());
 | 
			
		||||
		closesocket(sock);
 | 
			
		||||
		return INVALID_SOCKET;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Retrieve last error, if any, on the socket. */
 | 
			
		||||
	err = GetSocketError(sock);
 | 
			
		||||
	if (err != 0) {
 | 
			
		||||
		DEBUG(net, 1, "[%s] could not connect to %s: %s", type, address, 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;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Connection succeeded. */
 | 
			
		||||
	DEBUG(net, 1, "[%s] connected to %s", type, address);
 | 
			
		||||
	DEBUG(net, 1, "[%s] connected to %s", type, address.c_str());
 | 
			
		||||
 | 
			
		||||
	return sock;
 | 
			
		||||
}
 | 
			
		||||
@@ -388,48 +399,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(), NetworkError::GetLast().AsString());
 | 
			
		||||
		return INVALID_SOCKET;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (runp->ai_socktype == SOCK_STREAM && !SetNoDelay(sock)) {
 | 
			
		||||
		DEBUG(net, 3, "[%s] setting TCP_NODELAY failed for port %s", type, address);
 | 
			
		||||
		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(), 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, 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, 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, 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;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* 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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -190,8 +190,8 @@ struct NetworkAddressDumper {
 | 
			
		||||
	const char *GetAddressAsString(NetworkAddress *addr, bool with_family = true);
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
	/* 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];
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#endif /* NETWORK_CORE_ADDRESS_H */
 | 
			
		||||
 
 | 
			
		||||
@@ -57,6 +57,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_LONG_REVISION_LENGTH    =   64;         ///< The maximum length of the revision, in bytes including '\0'
 | 
			
		||||
 
 | 
			
		||||
@@ -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) */
 | 
			
		||||
 
 | 
			
		||||
@@ -74,7 +74,6 @@ struct NetworkGameInfo : NetworkServerGameInfo {
 | 
			
		||||
	uint32 map_width;                               ///< Map width
 | 
			
		||||
	uint32 map_height;                              ///< Map height
 | 
			
		||||
	char server_name[NETWORK_NAME_LENGTH];          ///< Server name
 | 
			
		||||
	char hostname[NETWORK_HOSTNAME_LENGTH];         ///< Hostname of the server (if any)
 | 
			
		||||
	char short_server_revision[NETWORK_REVISION_LENGTH]; ///< The version number the server is using (e.g.: 'r304' or 0.5.0) (truncated)
 | 
			
		||||
	char server_revision[NETWORK_LONG_REVISION_LENGTH];  ///< The version number the server is using (e.g.: 'r304' or 0.5.0)
 | 
			
		||||
	bool dedicated;                                 ///< Is this a dedicated server?
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										217
									
								
								src/network/core/os_abstraction.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										217
									
								
								src/network/core/os_abstraction.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,217 @@
 | 
			
		||||
/*
 | 
			
		||||
 * 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 <http://www.gnu.org/licenses/>.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @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 <mutex>
 | 
			
		||||
 | 
			
		||||
#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<std::mutex> 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
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 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 into blocking mode.
 | 
			
		||||
 * @param d The socket to set the blocking more for.
 | 
			
		||||
 * @return True if setting the blocking mode succeeded, otherwise false.
 | 
			
		||||
 */
 | 
			
		||||
bool SetBlocking(SOCKET d)
 | 
			
		||||
{
 | 
			
		||||
#if defined(_WIN32)
 | 
			
		||||
	u_long nonblocking = 0;
 | 
			
		||||
	return ioctlsocket(d, FIONBIO, &nonblocking) == 0;
 | 
			
		||||
#elif defined __EMSCRIPTEN__
 | 
			
		||||
	return true;
 | 
			
		||||
#else
 | 
			
		||||
	int nonblocking = 0;
 | 
			
		||||
	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
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Try to shutdown the socket in one or both directions.
 | 
			
		||||
 * @param d The socket to disable the delaying for.
 | 
			
		||||
 * @param read Whether to shutdown the read direction.
 | 
			
		||||
 * @param write Whether to shutdown the write direction.
 | 
			
		||||
 * @param linger_timeout The socket linger timeout.
 | 
			
		||||
 * @return True if successful
 | 
			
		||||
 */
 | 
			
		||||
bool ShutdownSocket(SOCKET d, bool read, bool write, uint linger_timeout)
 | 
			
		||||
{
 | 
			
		||||
	if (!read && !write) return true;
 | 
			
		||||
#ifdef _WIN32
 | 
			
		||||
	LINGER ln = { 1U, (uint16) linger_timeout };
 | 
			
		||||
#else
 | 
			
		||||
	struct linger ln = { 1, (int) linger_timeout };
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
	setsockopt(d, SOL_SOCKET, SO_LINGER, (const char*)&ln, sizeof(ln));
 | 
			
		||||
 | 
			
		||||
	int how = SD_BOTH;
 | 
			
		||||
	if (!read) how = SD_SEND;
 | 
			
		||||
	if (!write) how = SD_RECEIVE;
 | 
			
		||||
	return shutdown(d, how) == 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 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);
 | 
			
		||||
}
 | 
			
		||||
@@ -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 <ws2tcpip.h>
 | 
			
		||||
#include <windows.h>
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 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;
 | 
			
		||||
 | 
			
		||||
@@ -74,10 +79,7 @@ typedef unsigned long in_addr_t;
 | 
			
		||||
#	endif
 | 
			
		||||
#	define SOCKET int
 | 
			
		||||
#	define INVALID_SOCKET -1
 | 
			
		||||
#	define ioctlsocket ioctl
 | 
			
		||||
#	define closesocket close
 | 
			
		||||
#	define NetworkGetLastError() (errno)
 | 
			
		||||
#	define NetworkGetErrorString(error) (strerror(error))
 | 
			
		||||
#	define SD_RECEIVE SHUT_RD
 | 
			
		||||
#	define SD_SEND SHUT_WR
 | 
			
		||||
#	define SD_BOTH SHUT_RDWR
 | 
			
		||||
@@ -128,10 +130,7 @@ typedef unsigned long in_addr_t;
 | 
			
		||||
#if defined(__OS2__)
 | 
			
		||||
#	define SOCKET int
 | 
			
		||||
#	define INVALID_SOCKET -1
 | 
			
		||||
#	define ioctlsocket ioctl
 | 
			
		||||
#	define closesocket close
 | 
			
		||||
#	define NetworkGetLastError() (sock_errno())
 | 
			
		||||
#	define NetworkGetErrorString(error) (strerror(error))
 | 
			
		||||
#	define SD_RECEIVE SHUT_RD
 | 
			
		||||
#	define SD_SEND SHUT_WR
 | 
			
		||||
#	define SD_BOTH SHUT_RDWR
 | 
			
		||||
@@ -206,105 +205,11 @@ 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.
 | 
			
		||||
 * @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 into blocking mode.
 | 
			
		||||
 * @param d The socket to set the blocking more for.
 | 
			
		||||
 * @return True if setting the blocking mode succeeded, otherwise false.
 | 
			
		||||
 */
 | 
			
		||||
static inline bool SetBlocking(SOCKET d)
 | 
			
		||||
{
 | 
			
		||||
#ifdef _WIN32
 | 
			
		||||
	u_long nonblocking = 0;
 | 
			
		||||
#else
 | 
			
		||||
	int nonblocking = 0;
 | 
			
		||||
#endif
 | 
			
		||||
	return ioctlsocket(d, FIONBIO, &nonblocking) == 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 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
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Try to shutdown the socket in one or both directions.
 | 
			
		||||
 * @param d The socket to disable the delaying for.
 | 
			
		||||
 * @param read Whether to shutdown the read direction.
 | 
			
		||||
 * @param write Whether to shutdown the write direction.
 | 
			
		||||
 * @param linger_timeout The socket linger timeout.
 | 
			
		||||
 * @return True if successful
 | 
			
		||||
 */
 | 
			
		||||
static inline bool ShutdownSocket(SOCKET d, bool read, bool write, uint linger_timeout)
 | 
			
		||||
{
 | 
			
		||||
	if (!read && !write) return true;
 | 
			
		||||
#ifdef _WIN32
 | 
			
		||||
	LINGER ln = { 1U, (uint16) linger_timeout };
 | 
			
		||||
#else
 | 
			
		||||
	struct linger ln = { 1, (int) linger_timeout };
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
	setsockopt(d, SOL_SOCKET, SO_LINGER, (const char*)&ln, sizeof(ln));
 | 
			
		||||
 | 
			
		||||
	int how = SD_BOTH;
 | 
			
		||||
	if (!read) how = SD_SEND;
 | 
			
		||||
	if (!write) how = SD_RECEIVE;
 | 
			
		||||
	return shutdown(d, how) == 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 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 int GetSocketError(SOCKET d)
 | 
			
		||||
{
 | 
			
		||||
	int err;
 | 
			
		||||
	socklen_t len = sizeof(err);
 | 
			
		||||
	getsockopt(d, SOL_SOCKET, SO_ERROR, (char *)&err, &len);
 | 
			
		||||
 | 
			
		||||
	return err;
 | 
			
		||||
}
 | 
			
		||||
bool SetNonBlocking(SOCKET d);
 | 
			
		||||
bool SetBlocking(SOCKET d);
 | 
			
		||||
bool SetNoDelay(SOCKET d);
 | 
			
		||||
bool ShutdownSocket(SOCKET d, bool read, bool write, uint linger_timeout);
 | 
			
		||||
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.
 | 
			
		||||
 
 | 
			
		||||
@@ -114,11 +114,11 @@ SendPacketsState NetworkTCPSocketHandler::SendPackets(bool closing_down)
 | 
			
		||||
		Packet *p = this->packet_queue.front().get();
 | 
			
		||||
		res = p->TransferOut<int>(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;
 | 
			
		||||
@@ -165,10 +165,10 @@ std::unique_ptr<Packet> NetworkTCPSocketHandler::ReceivePacket()
 | 
			
		||||
		while (p->RemainingBytesToTransfer() != 0) {
 | 
			
		||||
			res = p->TransferIn<int>(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;
 | 
			
		||||
				}
 | 
			
		||||
@@ -194,10 +194,10 @@ std::unique_ptr<Packet> NetworkTCPSocketHandler::ReceivePacket()
 | 
			
		||||
	while (p->RemainingBytesToTransfer() != 0) {
 | 
			
		||||
		res = p->TransferIn<int>(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;
 | 
			
		||||
			}
 | 
			
		||||
 
 | 
			
		||||
@@ -27,8 +27,8 @@ static const char* _packet_game_type_names[] {
 | 
			
		||||
	"SERVER_ERROR",
 | 
			
		||||
	"CLIENT_COMPANY_INFO",
 | 
			
		||||
	"SERVER_COMPANY_INFO",
 | 
			
		||||
	"CLIENT_GAME_INFO",
 | 
			
		||||
	"SERVER_GAME_INFO",
 | 
			
		||||
	"CLIENT_GAME_INFO",
 | 
			
		||||
	"SERVER_GAME_INFO_EXTENDED",
 | 
			
		||||
	"SERVER_CHECK_NEWGRFS",
 | 
			
		||||
	"CLIENT_NEWGRFS_CHECKED",
 | 
			
		||||
 
 | 
			
		||||
@@ -44,8 +44,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
 | 
			
		||||
 
 | 
			
		||||
@@ -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"
 | 
			
		||||
@@ -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 = '/';
 | 
			
		||||
@@ -229,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 */
 | 
			
		||||
 
 | 
			
		||||
@@ -64,7 +64,7 @@ public:
 | 
			
		||||
					DEBUG(net, 1, "[%s] Banned ip tried to join (%s), refused", Tsocket::GetName(), entry.c_str());
 | 
			
		||||
 | 
			
		||||
					if (p.TransferOut<int>(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<int>(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);
 | 
			
		||||
 | 
			
		||||
@@ -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;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -127,7 +127,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());
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
@@ -136,7 +136,7 @@ void NetworkUDPSocketHandler::SendPacket(Packet *p, NetworkAddress *recv, bool a
 | 
			
		||||
		DEBUG(net, 7, "[udp] sendto(%s)",  NetworkAddressDumper().GetAddressAsString(&send));
 | 
			
		||||
 | 
			
		||||
		/* Check for any errors, but ignore it otherwise */
 | 
			
		||||
		if (res == -1) DEBUG(net, 1, "[udp] sendto(%s) failed with: %s", NetworkAddressDumper().GetAddressAsString(&send), NetworkGetLastErrorString());
 | 
			
		||||
		if (res == -1) DEBUG(net, 1, "[udp] sendto(%s) failed with: %s", NetworkAddressDumper().GetAddressAsString(&send), NetworkError::GetLast().AsString());
 | 
			
		||||
 | 
			
		||||
		if (!all) break;
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
@@ -306,7 +306,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);
 | 
			
		||||
@@ -475,45 +475,15 @@ static void CheckPauseOnJoin()
 | 
			
		||||
	CheckPauseHelper(NetworkHasJoiningClient(), PM_PAUSED_JOIN);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Converts a string to ip/port
 | 
			
		||||
 *  Format: IP:port
 | 
			
		||||
 *
 | 
			
		||||
 * 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.
 | 
			
		||||
 */
 | 
			
		||||
void ParseConnectionString(const char **port, char *connection_string)
 | 
			
		||||
{
 | 
			
		||||
	bool ipv6 = (strchr(connection_string, ':') != strrchr(connection_string, ':'));
 | 
			
		||||
	for (char *p = connection_string; *p != '\0'; p++) {
 | 
			
		||||
		switch (*p) {
 | 
			
		||||
			case '[':
 | 
			
		||||
				ipv6 = true;
 | 
			
		||||
				break;
 | 
			
		||||
 | 
			
		||||
			case ']':
 | 
			
		||||
				ipv6 = false;
 | 
			
		||||
				break;
 | 
			
		||||
 | 
			
		||||
			case ':':
 | 
			
		||||
				if (ipv6) break;
 | 
			
		||||
				*port = p + 1;
 | 
			
		||||
				*p = '\0';
 | 
			
		||||
				break;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Converts a string to ip/port/company
 | 
			
		||||
 *  Format: IP:port#company
 | 
			
		||||
 *
 | 
			
		||||
 * 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.
 | 
			
		||||
 * 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. Similar for company, if set.
 | 
			
		||||
 */
 | 
			
		||||
void ParseGameConnectionString(const char **company, 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++) {
 | 
			
		||||
@@ -527,6 +497,7 @@ void ParseGameConnectionString(const char **company, const char **port, char *co
 | 
			
		||||
				break;
 | 
			
		||||
 | 
			
		||||
			case '#':
 | 
			
		||||
				if (company == nullptr) continue;
 | 
			
		||||
				*company = p + 1;
 | 
			
		||||
				*p = '\0';
 | 
			
		||||
				break;
 | 
			
		||||
@@ -540,6 +511,51 @@ void ParseGameConnectionString(const char **company, const char **port, char *co
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Convert a string containing either "hostname" or "hostname:ip" to a
 | 
			
		||||
 * NetworkAddress.
 | 
			
		||||
 *
 | 
			
		||||
 * @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 ParseConnectionString(const std::string &connection_string, int default_port)
 | 
			
		||||
{
 | 
			
		||||
	char internal_connection_string[NETWORK_HOSTNAME_PORT_LENGTH];
 | 
			
		||||
	strecpy(internal_connection_string, connection_string.c_str(), lastof(internal_connection_string));
 | 
			
		||||
 | 
			
		||||
	const char *port = nullptr;
 | 
			
		||||
	ParseFullConnectionString(nullptr, &port, internal_connection_string);
 | 
			
		||||
 | 
			
		||||
	int rport = port != nullptr ? atoi(port) : default_port;
 | 
			
		||||
	return NetworkAddress(internal_connection_string, rport);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 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 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.c_str(), 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);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Handle the accepting of a connection to the server.
 | 
			
		||||
 * @param s The socket of the new connection.
 | 
			
		||||
@@ -619,8 +635,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
 | 
			
		||||
	{
 | 
			
		||||
@@ -630,45 +649,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
 | 
			
		||||
/**
 | 
			
		||||
 * 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 of the server to add.
 | 
			
		||||
 * @return The entry on the game list.
 | 
			
		||||
 */
 | 
			
		||||
NetworkGameList *NetworkAddServer(const std::string &connection_string)
 | 
			
		||||
{
 | 
			
		||||
	if (*b != '\0') {
 | 
			
		||||
		const char *port = nullptr;
 | 
			
		||||
		char host[NETWORK_HOSTNAME_LENGTH];
 | 
			
		||||
		uint16 rport;
 | 
			
		||||
	if (connection_string.empty()) return nullptr;
 | 
			
		||||
 | 
			
		||||
		strecpy(host, b, lastof(host));
 | 
			
		||||
	NetworkAddress address = ParseConnectionString(connection_string, NETWORK_DEFAULT_PORT);
 | 
			
		||||
 | 
			
		||||
		strecpy(_settings_client.network.connect_to_ip, b, lastof(_settings_client.network.connect_to_ip));
 | 
			
		||||
		rport = 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;
 | 
			
		||||
 | 
			
		||||
		ParseConnectionString(&port, host);
 | 
			
		||||
		if (port != nullptr) rport = atoi(port);
 | 
			
		||||
 | 
			
		||||
		NetworkUDPQueryServer(NetworkAddress(host, rport), true);
 | 
			
		||||
		NetworkRebuildHostList();
 | 
			
		||||
		UpdateNetworkGameWindow();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	NetworkTCPQueryServer(address);
 | 
			
		||||
 | 
			
		||||
	return item;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
@@ -707,41 +734,99 @@ public:
 | 
			
		||||
 | 
			
		||||
	void OnFailure() override
 | 
			
		||||
	{
 | 
			
		||||
		NetworkError(STR_NETWORK_ERROR_NOCONNECTION);
 | 
			
		||||
		ShowNetworkError(STR_NETWORK_ERROR_NOCONNECTION);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void OnConnect(SOCKET s) override
 | 
			
		||||
	{
 | 
			
		||||
		_networking = true;
 | 
			
		||||
		new ClientNetworkGameSocketHandler(s);
 | 
			
		||||
		new ClientNetworkGameSocketHandler(s, this->address);
 | 
			
		||||
		IConsoleCmdExec("exec scripts/on_client.scr 0");
 | 
			
		||||
		NetworkClient_Connected();
 | 
			
		||||
	}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/* 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)
 | 
			
		||||
/**
 | 
			
		||||
 * 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)
 | 
			
		||||
{
 | 
			
		||||
	if (!_network_available) return;
 | 
			
		||||
	CompanyID join_as = default_company;
 | 
			
		||||
	NetworkAddress address = ParseGameConnectionString(&join_as, connection_string, NETWORK_DEFAULT_PORT);
 | 
			
		||||
 | 
			
		||||
	if (port == 0) return;
 | 
			
		||||
	if (join_as != COMPANY_NEW_COMPANY && join_as != COMPANY_SPECTATOR) {
 | 
			
		||||
		join_as--;
 | 
			
		||||
		if (join_as >= MAX_COMPANIES) {
 | 
			
		||||
			return false;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (!NetworkValidateClientName()) return;
 | 
			
		||||
	return NetworkClientConnectGame(address, join_as, join_server_password, join_company_password);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
	strecpy(_settings_client.network.last_host, hostname, lastof(_settings_client.network.last_host));
 | 
			
		||||
	_settings_client.network.last_port = port;
 | 
			
		||||
	_network_join_as = join_as;
 | 
			
		||||
	_network_join_server_password = join_server_password;
 | 
			
		||||
	_network_join_company_password = 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 false;
 | 
			
		||||
	if (!NetworkValidateClientName()) return false;
 | 
			
		||||
 | 
			
		||||
	_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(NetworkAddress(hostname, port));
 | 
			
		||||
	new TCPClientConnecter(_network_join.address);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void NetworkInitGameInfo()
 | 
			
		||||
@@ -1126,13 +1211,14 @@ 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(const std::string &connection_string)
 | 
			
		||||
{
 | 
			
		||||
	extern SOCKET _debug_socket;  // Comes from debug.c
 | 
			
		||||
 | 
			
		||||
	DEBUG(net, 0, "Redirecting DEBUG() to %s:%d", hostname, port);
 | 
			
		||||
	NetworkAddress address = ParseConnectionString(connection_string, NETWORK_DEFAULT_DEBUGLOG_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()");
 | 
			
		||||
@@ -1180,9 +1266,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);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -150,9 +150,9 @@ 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), token (0), status (STATUS_INACTIVE)
 | 
			
		||||
ClientNetworkGameSocketHandler::ClientNetworkGameSocketHandler (SOCKET s, NetworkAddress address)
 | 
			
		||||
	: NetworkGameSocketHandler(s),
 | 
			
		||||
	  address(address), savegame(nullptr), token(0), status(STATUS_INACTIVE)
 | 
			
		||||
{
 | 
			
		||||
	assert(ClientNetworkGameSocketHandler::my_client == nullptr);
 | 
			
		||||
	ClientNetworkGameSocketHandler::my_client = this;
 | 
			
		||||
@@ -324,7 +324,7 @@ void ClientNetworkGameSocketHandler::ClientError(NetworkRecvStatus res)
 | 
			
		||||
#endif
 | 
			
		||||
				if (_sync_state_checksum != _state_checksum.state) info.flags |= DesyncExtraInfo::DEIF_STATE;
 | 
			
		||||
 | 
			
		||||
				NetworkError(STR_NETWORK_ERROR_DESYNC);
 | 
			
		||||
				ShowNetworkError(STR_NETWORK_ERROR_DESYNC);
 | 
			
		||||
				DEBUG(desync, 1, "sync_err: date{%08x; %02x; %02x} {%x, " OTTD_PRINTFHEX64 "} != {%x, " OTTD_PRINTFHEX64 "}"
 | 
			
		||||
						, _date, _date_fract, _tick_skip_counter, _sync_seed_1, _sync_state_checksum, _random.state[0], _state_checksum.state);
 | 
			
		||||
				DEBUG(net, 0, "Sync error detected!");
 | 
			
		||||
@@ -389,13 +389,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);
 | 
			
		||||
@@ -408,11 +403,9 @@ 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;
 | 
			
		||||
 | 
			
		||||
	Packet *p = new Packet(PACKET_CLIENT_GAME_INFO);
 | 
			
		||||
	p->Send_uint32(FIND_SERVER_EXTENDED_TOKEN);
 | 
			
		||||
@@ -421,7 +414,13 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::SendInformationQuery()
 | 
			
		||||
	p->Send_uint16(0); // version
 | 
			
		||||
	my_client->SendPacket(p);
 | 
			
		||||
 | 
			
		||||
	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;
 | 
			
		||||
}
 | 
			
		||||
@@ -437,7 +436,7 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::SendJoin()
 | 
			
		||||
	p->Send_string(_openttd_revision);
 | 
			
		||||
	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;
 | 
			
		||||
@@ -698,9 +697,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);
 | 
			
		||||
@@ -711,6 +714,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. */
 | 
			
		||||
@@ -870,6 +875,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 */
 | 
			
		||||
@@ -933,7 +947,7 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_NEED_GAME_PASSW
 | 
			
		||||
	p->Recv_string(_password_server_id, sizeof(_password_server_id));
 | 
			
		||||
	if (this->HasClientQuit()) return NETWORK_RECV_STATUS_MALFORMED_PACKET;
 | 
			
		||||
 | 
			
		||||
	const char *password = _network_join_server_password;
 | 
			
		||||
	const char *password = _network_join.server_password;
 | 
			
		||||
	if (!StrEmpty(password)) {
 | 
			
		||||
		return SendGamePassword(password);
 | 
			
		||||
	}
 | 
			
		||||
@@ -952,7 +966,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);
 | 
			
		||||
	}
 | 
			
		||||
@@ -1077,10 +1091,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;
 | 
			
		||||
@@ -1089,7 +1103,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;
 | 
			
		||||
@@ -1420,6 +1434,7 @@ const char *ClientNetworkGameSocketHandler::GetServerStatusName(ServerStatus sta
 | 
			
		||||
{
 | 
			
		||||
	static const char* _server_status_names[] {
 | 
			
		||||
		"INACTIVE",
 | 
			
		||||
		"GAME_INFO",
 | 
			
		||||
		"COMPANY_INFO",
 | 
			
		||||
		"JOIN",
 | 
			
		||||
		"NEWGRFS_CHECK",
 | 
			
		||||
 
 | 
			
		||||
@@ -15,12 +15,14 @@
 | 
			
		||||
/** Class for handling the client side of the game connection. */
 | 
			
		||||
class ClientNetworkGameSocketHandler : 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.
 | 
			
		||||
@@ -84,7 +86,7 @@ protected:
 | 
			
		||||
	static NetworkRecvStatus SendMapOk();
 | 
			
		||||
	void CheckConnection();
 | 
			
		||||
public:
 | 
			
		||||
	ClientNetworkGameSocketHandler(SOCKET s);
 | 
			
		||||
	ClientNetworkGameSocketHandler(SOCKET s, NetworkAddress address);
 | 
			
		||||
	~ClientNetworkGameSocketHandler();
 | 
			
		||||
 | 
			
		||||
	NetworkRecvStatus CloseConnection(NetworkRecvStatus status) override;
 | 
			
		||||
@@ -92,7 +94,7 @@ public:
 | 
			
		||||
 | 
			
		||||
	std::string GetDebugInfo() const override;
 | 
			
		||||
 | 
			
		||||
	static NetworkRecvStatus SendInformationQuery();
 | 
			
		||||
	static NetworkRecvStatus SendInformationQuery(bool request_company_info);
 | 
			
		||||
 | 
			
		||||
	static NetworkRecvStatus SendJoin();
 | 
			
		||||
	static NetworkRecvStatus SendCommand(const CommandPacket *cp);
 | 
			
		||||
@@ -127,9 +129,15 @@ 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) {}
 | 
			
		||||
	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.
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
extern const char *_network_join_server_password;
 | 
			
		||||
extern const char *_network_join_company_password;
 | 
			
		||||
extern NetworkJoinInfo _network_join;
 | 
			
		||||
 | 
			
		||||
#endif /* NETWORK_CLIENT_H */
 | 
			
		||||
 
 | 
			
		||||
@@ -45,14 +45,14 @@ 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);
 | 
			
		||||
void NetworkStartDebugLog(const std::string &connection_string);
 | 
			
		||||
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);
 | 
			
		||||
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 NetworkClientSendSettingsPassword(const char *password);
 | 
			
		||||
 
 | 
			
		||||
@@ -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;
 | 
			
		||||
@@ -70,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;
 | 
			
		||||
@@ -96,7 +86,6 @@ NetworkGameList *NetworkGameListAddItem(NetworkAddress address)
 | 
			
		||||
	} else {
 | 
			
		||||
		prev_item->next = item;
 | 
			
		||||
	}
 | 
			
		||||
	DEBUG(net, 4, "[gamelist] added server to list");
 | 
			
		||||
 | 
			
		||||
	UpdateNetworkGameWindow();
 | 
			
		||||
 | 
			
		||||
@@ -152,7 +141,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;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -489,9 +489,8 @@ 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 = 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);
 | 
			
		||||
 | 
			
		||||
@@ -662,9 +661,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;
 | 
			
		||||
 | 
			
		||||
@@ -753,7 +751,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;
 | 
			
		||||
 | 
			
		||||
@@ -763,14 +761,12 @@ 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;
 | 
			
		||||
 | 
			
		||||
			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
 | 
			
		||||
@@ -845,7 +841,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
 | 
			
		||||
@@ -988,7 +987,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);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -1487,22 +1486,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, true);
 | 
			
		||||
				break;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
@@ -1570,7 +1569,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, true);
 | 
			
		||||
 | 
			
		||||
	new NetworkLobbyWindow(&_network_lobby_window_desc, ngl);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -94,10 +94,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();
 | 
			
		||||
 | 
			
		||||
@@ -119,11 +119,15 @@ 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 = "", NetworkTextMessageData data = NetworkTextMessageData());
 | 
			
		||||
uint NetworkCalculateLag(const NetworkClientSocket *cs);
 | 
			
		||||
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);
 | 
			
		||||
 | 
			
		||||
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);
 | 
			
		||||
 | 
			
		||||
#endif /* NETWORK_INTERNAL_H */
 | 
			
		||||
 
 | 
			
		||||
@@ -111,7 +111,6 @@ static void DoNetworkUDPQueryServer(NetworkAddress &address, bool needs_mutex, b
 | 
			
		||||
	/* Clear item in gamelist */
 | 
			
		||||
	NetworkGameList *item = CallocT<NetworkGameList>(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);
 | 
			
		||||
@@ -453,10 +452,6 @@ void ClientNetworkUDPSocketHandler::Receive_SERVER_RESPONSE_Common(Packet *p, Ne
 | 
			
		||||
		if (in_request_count > 0) flush_request();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	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));
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
@@ -31,62 +31,29 @@
 | 
			
		||||
bool _sprite_group_resolve_check_veh_check = false;
 | 
			
		||||
bool _sprite_group_resolve_check_veh_curvature_check = false;
 | 
			
		||||
 | 
			
		||||
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<EngineID>(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);
 | 
			
		||||
@@ -1623,8 +1590,8 @@ void AnalyseEngineCallbacks()
 | 
			
		||||
		for (uint i = 0; i < NUM_CARGO + 2; i++) {
 | 
			
		||||
			process_sg(e->grf_prop.spritegroup[i]);
 | 
			
		||||
		}
 | 
			
		||||
		for (uint i = 0; i < e->overrides_count; i++) {
 | 
			
		||||
			process_sg(e->overrides[i].group);
 | 
			
		||||
		for (const WagonOverride &wo : e->overrides) {
 | 
			
		||||
			process_sg(wo.group);
 | 
			
		||||
		}
 | 
			
		||||
		e->callbacks_used = callbacks_used;
 | 
			
		||||
		e->cb36_properties_used = cb36_properties_used;
 | 
			
		||||
 
 | 
			
		||||
@@ -116,8 +116,6 @@ enum VehicleTrigger {
 | 
			
		||||
};
 | 
			
		||||
void TriggerVehicle(Vehicle *veh, VehicleTrigger trigger);
 | 
			
		||||
 | 
			
		||||
void UnloadWagonOverrides(Engine *e);
 | 
			
		||||
 | 
			
		||||
void AlterVehicleListOrder(EngineID engine, uint target);
 | 
			
		||||
void CommitVehicleListOrderChanges();
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -623,29 +623,10 @@ 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;
 | 
			
		||||
 | 
			
		||||
			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 (port != nullptr) rport = atoi(port);
 | 
			
		||||
 | 
			
		||||
			LoadIntroGame();
 | 
			
		||||
			_switch_mode = SM_NONE;
 | 
			
		||||
			NetworkClientConnectGame(network_conn, rport, 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. */
 | 
			
		||||
@@ -740,7 +721,7 @@ int openttd_main(int argc, char *argv[])
 | 
			
		||||
			SetDebugString("net=3");
 | 
			
		||||
			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);
 | 
			
		||||
			}
 | 
			
		||||
@@ -939,15 +920,7 @@ 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);
 | 
			
		||||
		NetworkStartDebugLog(debuglog_conn);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (!HandleBootstrap()) {
 | 
			
		||||
@@ -1298,6 +1271,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()) {
 | 
			
		||||
@@ -1975,7 +1953,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 */
 | 
			
		||||
			NetworkClientConnectGame(_settings_client.network.last_host, _settings_client.network.last_port, COMPANY_SPECTATOR);
 | 
			
		||||
			NetworkClientConnectGame(_settings_client.network.last_joined, COMPANY_SPECTATOR);
 | 
			
		||||
		}
 | 
			
		||||
		/* Singleplayer */
 | 
			
		||||
		StateGameLoop();
 | 
			
		||||
 
 | 
			
		||||
@@ -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 */
 | 
			
		||||
 
 | 
			
		||||
@@ -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) */
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -14,6 +14,8 @@
 | 
			
		||||
 | 
			
		||||
#include <time.h>
 | 
			
		||||
 | 
			
		||||
#include <time.h>
 | 
			
		||||
 | 
			
		||||
#include "../../safeguards.h"
 | 
			
		||||
 | 
			
		||||
/* static */ bool ScriptDate::IsValidDate(Date date)
 | 
			
		||||
 
 | 
			
		||||
@@ -337,7 +337,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
 | 
			
		||||
@@ -349,8 +349,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
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -144,6 +144,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
 | 
			
		||||
 
 | 
			
		||||
@@ -30,30 +30,30 @@
 | 
			
		||||
#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, bool quiet_mode = false);
 | 
			
		||||
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, bool quiet_mode = false) 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);
 | 
			
		||||
char *str_vfmt(const char *str, va_list ap) WARN_FORMAT(1, 0);
 | 
			
		||||
std::string CDECL stdstr_fmt(const char *str, ...) WARN_FORMAT(1, 2);
 | 
			
		||||
std::string stdstr_vfmt(const char *str, va_list va) WARN_FORMAT(1, 0);
 | 
			
		||||
 | 
			
		||||
char *str_validate(char *str, const char *last, StringValidationSettings settings = SVS_REPLACE_WITH_QUESTION_MARK);
 | 
			
		||||
char *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);
 | 
			
		||||
 | 
			
		||||
const char *str_fix_scc_encoded(char *str, const char *last);
 | 
			
		||||
const char *str_fix_scc_encoded(char *str, const char *last) NOACCESS(2);
 | 
			
		||||
void str_strip_colours(char *str);
 | 
			
		||||
std::string str_strip_all_scc(const char *str);
 | 
			
		||||
char *str_replace_wchar(char *str, const char *last, WChar find, WChar replace);
 | 
			
		||||
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);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 
 | 
			
		||||
@@ -6340,21 +6340,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
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user