From a70aa5df4938a6a4459690c4666a5c39a3f5bf98 Mon Sep 17 00:00:00 2001 From: Stephan <5377412+Taschi120@users.noreply.github.com> Date: Fri, 9 Jul 2021 21:44:02 +0200 Subject: [PATCH 01/44] Add #9188: netsave now keeps multiple version around, similar to autosave (#9395) --- src/network/network_client.cpp | 8 ++------ src/openttd.cpp | 20 ++------------------ src/saveload/saveload.cpp | 34 ++++++++++++++++++++++++++++++++++ src/saveload/saveload.h | 2 ++ 4 files changed, 40 insertions(+), 24 deletions(-) diff --git a/src/network/network_client.cpp b/src/network/network_client.cpp index 779ffe6fd7..fe4d1249c7 100644 --- a/src/network/network_client.cpp +++ b/src/network/network_client.cpp @@ -132,12 +132,8 @@ struct PacketReader : LoadFilter { */ void ClientNetworkEmergencySave() { - if (!_settings_client.gui.autosave_on_network_disconnect) return; - if (!_networking) return; - - const char *filename = "netsave.sav"; - Debug(net, 3, "Performing emergency save: {}", filename); - SaveOrLoad(filename, SLO_SAVE, DFT_GAME_FILE, AUTOSAVE_DIR, false); + static int _netsave_ctr = 0; + DoAutoOrNetsave(_netsave_ctr, true); } diff --git a/src/openttd.cpp b/src/openttd.cpp index aba1ae986b..904cdbbb1a 100644 --- a/src/openttd.cpp +++ b/src/openttd.cpp @@ -1392,24 +1392,8 @@ void StateGameLoop() */ static void DoAutosave() { - char buf[MAX_PATH]; - - if (_settings_client.gui.keep_all_autosave) { - GenerateDefaultSaveName(buf, lastof(buf)); - strecat(buf, ".sav", lastof(buf)); - } else { - static int _autosave_ctr = 0; - - /* generate a savegame name and number according to _settings_client.gui.max_num_autosaves */ - seprintf(buf, lastof(buf), "autosave%d.sav", _autosave_ctr); - - if (++_autosave_ctr >= _settings_client.gui.max_num_autosaves) _autosave_ctr = 0; - } - - Debug(sl, 2, "Autosaving to '{}'", buf); - if (SaveOrLoad(buf, SLO_SAVE, DFT_GAME_FILE, AUTOSAVE_DIR) != SL_OK) { - ShowErrorMessage(STR_ERROR_AUTOSAVE_FAILED, INVALID_STRING_ID, WL_ERROR); - } + static int _autosave_ctr = 0; + DoAutoOrNetsave(_autosave_ctr); } /** diff --git a/src/saveload/saveload.cpp b/src/saveload/saveload.cpp index 74978c4db8..d771bf526d 100644 --- a/src/saveload/saveload.cpp +++ b/src/saveload/saveload.cpp @@ -3323,6 +3323,40 @@ SaveOrLoadResult SaveOrLoad(const std::string &filename, SaveLoadOperation fop, } } +/** + * Create an autosave or netsave. + * @param counter A reference to the counter variable to be used for rotating the file name. + * @param netsave Indicates if this is a regular autosave or a netsave. + */ +void DoAutoOrNetsave(int &counter, bool netsave) +{ + char buf[MAX_PATH]; + + if (_settings_client.gui.keep_all_autosave) { + GenerateDefaultSaveName(buf, lastof(buf)); + if (!netsave) { + strecat(buf, ".sav", lastof(buf)); + } else { + strecat(buf, "-netsave.sav", lastof(buf)); + } + } else { + /* Generate a savegame name and number according to _settings_client.gui.max_num_autosaves. */ + if (!netsave) { + seprintf(buf, lastof(buf), "autosave%d.sav", counter); + } else { + seprintf(buf, lastof(buf), "netsave%d.sav", counter); + } + + if (++counter >= _settings_client.gui.max_num_autosaves) counter = 0; + } + + Debug(sl, 2, "Autosaving to '{}'", buf); + if (SaveOrLoad(buf, SLO_SAVE, DFT_GAME_FILE, AUTOSAVE_DIR) != SL_OK) { + ShowErrorMessage(STR_ERROR_AUTOSAVE_FAILED, INVALID_STRING_ID, WL_ERROR); + } +} + + /** Do a save when exiting the game (_settings_client.gui.autosave_on_exit) */ void DoExitSave() { diff --git a/src/saveload/saveload.h b/src/saveload/saveload.h index 7fc8027132..d79bc1416e 100644 --- a/src/saveload/saveload.h +++ b/src/saveload/saveload.h @@ -381,6 +381,8 @@ void WaitTillSaved(); void ProcessAsyncSaveFinish(); void DoExitSave(); +void DoAutoOrNetsave(int &counter, bool netsave = false); + SaveOrLoadResult SaveWithFilter(struct SaveFilter *writer, bool threaded); SaveOrLoadResult LoadWithFilter(struct LoadFilter *reader); From 94881f5a34340c0398599e09d27c0c9a205294b4 Mon Sep 17 00:00:00 2001 From: Rubidium Date: Thu, 8 Jul 2021 19:12:10 +0200 Subject: [PATCH 02/44] Codechange: name a few unnamed settings as to not to have to check for that being empty --- src/saveload/compat/settings_sl_compat.h | 10 +++++----- src/saveload/settings_sl.cpp | 3 ++- src/table/settings/game_settings.ini | 10 +++++----- 3 files changed, 12 insertions(+), 11 deletions(-) diff --git a/src/saveload/compat/settings_sl_compat.h b/src/saveload/compat/settings_sl_compat.h index 081779bf5b..00bc026a43 100644 --- a/src/saveload/compat/settings_sl_compat.h +++ b/src/saveload/compat/settings_sl_compat.h @@ -107,11 +107,11 @@ const SaveLoadCompat _settings_sl_compat[] = { SLC_VAR("vehicle.max_roadveh"), SLC_VAR("vehicle.max_aircraft"), SLC_VAR("vehicle.max_ships"), - SLC_VAR("_old_vds.servint_ispercent"), - SLC_VAR("_old_vds.servint_trains"), - SLC_VAR("_old_vds.servint_roadveh"), - SLC_VAR("_old_vds.servint_ships"), - SLC_VAR("_old_vds.servint_aircraft"), + SLC_VAR("vehicle.servint_ispercent"), + SLC_VAR("vehicle.servint_trains"), + SLC_VAR("vehicle.servint_roadveh"), + SLC_VAR("vehicle.servint_ships"), + SLC_VAR("vehicle.servint_aircraft"), SLC_VAR("order.no_servicing_if_no_breakdowns"), SLC_VAR("vehicle.wagon_speed_limits"), SLC_VAR("vehicle.disable_elrails"), diff --git a/src/saveload/settings_sl.cpp b/src/saveload/settings_sl.cpp index f19ff910c1..8b683564b9 100644 --- a/src/saveload/settings_sl.cpp +++ b/src/saveload/settings_sl.cpp @@ -91,7 +91,8 @@ static std::vector GetSettingsDesc(const SettingTable &settings, bool SaveLoad sv = sd->save; /* Replace the name with the actual name of the setting. */ - if (!sd->name.empty()) sv.name = sd->name; + assert(!sd->name.empty()); + sv.name = sd->name; saveloads.push_back(sv); } diff --git a/src/table/settings/game_settings.ini b/src/table/settings/game_settings.ini index 2a51279e5b..d51fbbf021 100644 --- a/src/table/settings/game_settings.ini +++ b/src/table/settings/game_settings.ini @@ -360,14 +360,14 @@ cat = SC_EXPERT ## Old vehicle settings. [SDTG_BOOL] -name = """" +name = ""vehicle.servint_ispercent"" flags = SF_NO_NETWORK var = _old_vds.servint_ispercent def = false to = SLV_120 [SDTG_VAR] -name = """" +name = ""vehicle.servint_trains"" type = SLE_UINT16 flags = SF_GUI_0_IS_SPECIAL var = _old_vds.servint_trains @@ -377,7 +377,7 @@ max = 800 to = SLV_120 [SDTG_VAR] -name = """" +name = ""vehicle.servint_roadveh"" type = SLE_UINT16 flags = SF_GUI_0_IS_SPECIAL var = _old_vds.servint_roadveh @@ -387,7 +387,7 @@ max = 800 to = SLV_120 [SDTG_VAR] -name = """" +name = ""vehicle.servint_ships"" type = SLE_UINT16 flags = SF_GUI_0_IS_SPECIAL var = _old_vds.servint_ships @@ -397,7 +397,7 @@ max = 800 to = SLV_120 [SDTG_VAR] -name = """" +name = ""vehicle.servint_aircraft"" type = SLE_UINT16 flags = SF_GUI_0_IS_SPECIAL var = _old_vds.servint_aircraft From 01139d33686945db4ccb77b62acadf2caab3b93e Mon Sep 17 00:00:00 2001 From: Rubidium Date: Thu, 8 Jul 2021 19:20:41 +0200 Subject: [PATCH 03/44] Codechange: access the name of a setting via an accessor function --- src/saveload/settings_sl.cpp | 6 ++-- src/script/api/script_gamesettings.cpp | 2 +- src/settings.cpp | 38 +++++++++++++------------- src/settings_internal.h | 11 ++++++++ 4 files changed, 34 insertions(+), 23 deletions(-) diff --git a/src/saveload/settings_sl.cpp b/src/saveload/settings_sl.cpp index 8b683564b9..a939792387 100644 --- a/src/saveload/settings_sl.cpp +++ b/src/saveload/settings_sl.cpp @@ -84,15 +84,15 @@ static std::vector GetSettingsDesc(const SettingTable &settings, bool if (is_loading && (sd->flags & SF_NO_NETWORK_SYNC) && _networking && !_network_server) { if (IsSavegameVersionBefore(SLV_TABLE_CHUNKS)) { /* We don't want to read this setting, so we do need to skip over it. */ - saveloads.push_back({sd->name, sd->save.cmd, GetVarFileType(sd->save.conv) | SLE_VAR_NULL, sd->save.length, sd->save.version_from, sd->save.version_to, 0, nullptr, 0, nullptr}); + saveloads.push_back({sd->GetName(), sd->save.cmd, GetVarFileType(sd->save.conv) | SLE_VAR_NULL, sd->save.length, sd->save.version_from, sd->save.version_to, 0, nullptr, 0, nullptr}); } continue; } SaveLoad sv = sd->save; /* Replace the name with the actual name of the setting. */ - assert(!sd->name.empty()); - sv.name = sd->name; + assert(!sd->GetName().empty()); + sv.name = sd->GetName(); saveloads.push_back(sv); } diff --git a/src/script/api/script_gamesettings.cpp b/src/script/api/script_gamesettings.cpp index 35277e3036..78b26e9bb3 100644 --- a/src/script/api/script_gamesettings.cpp +++ b/src/script/api/script_gamesettings.cpp @@ -37,7 +37,7 @@ if ((sd->flags & SF_NO_NETWORK_SYNC) != 0) return false; - return ScriptObject::DoCommand(0, 0, value, CMD_CHANGE_SETTING, sd->name.c_str()); + return ScriptObject::DoCommand(0, 0, value, CMD_CHANGE_SETTING, sd->GetName().c_str()); } /* static */ bool ScriptGameSettings::IsDisabledVehicleType(ScriptVehicle::VehicleType vehicle_type) diff --git a/src/settings.cpp b/src/settings.cpp index 97a73dd13b..d3d93eadd9 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -374,13 +374,13 @@ size_t IntSettingDesc::ParseValue(const char *str) const if (end == str) { ErrorMessageData msg(STR_CONFIG_ERROR, STR_CONFIG_ERROR_INVALID_VALUE); msg.SetDParamStr(0, str); - msg.SetDParamStr(1, this->name); + msg.SetDParamStr(1, this->GetName()); _settings_error_list.push_back(msg); return this->def; } if (*end != '\0') { ErrorMessageData msg(STR_CONFIG_ERROR, STR_CONFIG_ERROR_TRAILING_CHARACTERS); - msg.SetDParamStr(0, this->name); + msg.SetDParamStr(0, this->GetName()); _settings_error_list.push_back(msg); } return val; @@ -396,7 +396,7 @@ size_t OneOfManySettingDesc::ParseValue(const char *str) const ErrorMessageData msg(STR_CONFIG_ERROR, STR_CONFIG_ERROR_INVALID_VALUE); msg.SetDParamStr(0, str); - msg.SetDParamStr(1, this->name); + msg.SetDParamStr(1, this->GetName()); _settings_error_list.push_back(msg); return this->def; } @@ -407,7 +407,7 @@ size_t ManyOfManySettingDesc::ParseValue(const char *str) const if (r != (size_t)-1) return r; ErrorMessageData msg(STR_CONFIG_ERROR, STR_CONFIG_ERROR_INVALID_VALUE); msg.SetDParamStr(0, str); - msg.SetDParamStr(1, this->name); + msg.SetDParamStr(1, this->GetName()); _settings_error_list.push_back(msg); return this->def; } @@ -419,7 +419,7 @@ size_t BoolSettingDesc::ParseValue(const char *str) const ErrorMessageData msg(STR_CONFIG_ERROR, STR_CONFIG_ERROR_INVALID_VALUE); msg.SetDParamStr(0, str); - msg.SetDParamStr(1, this->name); + msg.SetDParamStr(1, this->GetName()); _settings_error_list.push_back(msg); return this->def; } @@ -573,7 +573,7 @@ static void IniLoadSettings(IniFile &ini, const SettingTable &settings_table, co if (sd->startup != only_startup) continue; /* For settings.xx.yy load the settings from [xx] yy = ? */ - std::string s{ sd->name }; + std::string s{ sd->GetName() }; auto sc = s.find('.'); if (sc != std::string::npos) { group = ini.GetGroup(s.substr(0, sc)); @@ -618,7 +618,7 @@ void ListSettingDesc::ParseValue(const IniItem *item, void *object) const void *ptr = GetVariableAddress(object, this->save); if (!LoadIntList(str, ptr, this->save.length, GetVarMemType(this->save.conv))) { ErrorMessageData msg(STR_CONFIG_ERROR, STR_CONFIG_ERROR_ARRAY); - msg.SetDParamStr(0, this->name); + msg.SetDParamStr(0, this->GetName()); _settings_error_list.push_back(msg); /* Use default */ @@ -652,7 +652,7 @@ static void IniSaveSettings(IniFile &ini, const SettingTable &settings_table, co if (sd->flags & SF_NOT_IN_CONFIG) continue; /* XXX - wtf is this?? (group override?) */ - std::string s{ sd->name }; + std::string s{ sd->GetName() }; auto sc = s.find('.'); if (sc != std::string::npos) { group = ini.GetGroup(s.substr(0, sc)); @@ -1176,7 +1176,7 @@ static void RemoveEntriesFromIni(IniFile &ini, const SettingTable &table) const SettingDesc *sd = GetSettingDesc(desc); /* For settings.xx.yy load the settings from [xx] yy = ? */ - std::string s{ sd->name }; + std::string s{ sd->GetName() }; auto sc = s.find('.'); if (sc == std::string::npos) continue; @@ -1365,7 +1365,7 @@ void IntSettingDesc::ChangeValue(const void *object, int32 newval) const if (this->flags & SF_NO_NETWORK) { GamelogStartAction(GLAT_SETTING); - GamelogSetting(this->name, oldval, newval); + GamelogSetting(this->GetName(), oldval, newval); GamelogStopAction(); } @@ -1387,7 +1387,7 @@ static const SettingDesc *GetSettingFromName(const std::string_view name, const for (auto &desc : settings) { const SettingDesc *sd = GetSettingDesc(desc); if (!SlIsObjectCurrentlyValid(sd->save.version_from, sd->save.version_to)) continue; - if (sd->name == name) return sd; + if (sd->GetName() == name) return sd; } /* Then check the shortcut variant of the name. */ @@ -1395,7 +1395,7 @@ static const SettingDesc *GetSettingFromName(const std::string_view name, const for (auto &desc : settings) { const SettingDesc *sd = GetSettingDesc(desc); if (!SlIsObjectCurrentlyValid(sd->save.version_from, sd->save.version_to)) continue; - if (StrEndsWith(sd->name, short_name_suffix)) return sd; + if (StrEndsWith(sd->GetName(), short_name_suffix)) return sd; } return nullptr; @@ -1518,7 +1518,7 @@ bool SetSettingValue(const IntSettingDesc *sd, int32 value, bool force_newgame) const IntSettingDesc *setting = sd->AsIntSetting(); if ((setting->flags & SF_PER_COMPANY) != 0) { if (Company::IsValidID(_local_company) && _game_mode != GM_MENU) { - return DoCommandP(0, 0, value, CMD_CHANGE_COMPANY_SETTING, nullptr, setting->name); + return DoCommandP(0, 0, value, CMD_CHANGE_COMPANY_SETTING, nullptr, setting->GetName()); } setting->ChangeValue(&_settings_client.company, value); @@ -1544,7 +1544,7 @@ bool SetSettingValue(const IntSettingDesc *sd, int32 value, bool force_newgame) /* send non-company-based settings over the network */ if (!_networking || (_networking && _network_server)) { - return DoCommandP(0, 0, value, CMD_CHANGE_SETTING, nullptr, setting->name); + return DoCommandP(0, 0, value, CMD_CHANGE_SETTING, nullptr, setting->GetName()); } return false; } @@ -1572,7 +1572,7 @@ void SyncCompanySettings() const SettingDesc *sd = GetSettingDesc(desc); uint32 old_value = (uint32)sd->AsIntSetting()->Read(new_object); uint32 new_value = (uint32)sd->AsIntSetting()->Read(old_object); - if (old_value != new_value) NetworkSendCommand(0, 0, new_value, CMD_CHANGE_COMPANY_SETTING, nullptr, sd->name, _local_company); + if (old_value != new_value) NetworkSendCommand(0, 0, new_value, CMD_CHANGE_COMPANY_SETTING, nullptr, sd->GetName(), _local_company); } } @@ -1670,13 +1670,13 @@ void IConsoleGetSetting(const char *name, bool force_newgame) const void *object = (_game_mode == GM_MENU || force_newgame) ? &_settings_newgame : &_settings_game; if (sd->IsStringSetting()) { - IConsolePrint(CC_INFO, "Current value for '{}' is '{}'.", sd->name, sd->AsStringSetting()->Read(object)); + IConsolePrint(CC_INFO, "Current value for '{}' is '{}'.", sd->GetName(), sd->AsStringSetting()->Read(object)); } else if (sd->IsIntSetting()) { char value[20]; sd->FormatValue(value, lastof(value), object); const IntSettingDesc *int_setting = sd->AsIntSetting(); IConsolePrint(CC_INFO, "Current value for '{}' is '{}' (min: {}{}, max: {}).", - sd->name.c_str(), value, (sd->flags & SF_GUI_0_IS_SPECIAL) ? "(0) " : "", int_setting->min, int_setting->max); + sd->GetName(), value, (sd->flags & SF_GUI_0_IS_SPECIAL) ? "(0) " : "", int_setting->min, int_setting->max); } } @@ -1685,10 +1685,10 @@ static void IConsoleListSettingsTable(const SettingTable &table, const char *pre for (auto &desc : table) { const SettingDesc *sd = GetSettingDesc(desc); if (!SlIsObjectCurrentlyValid(sd->save.version_from, sd->save.version_to)) continue; - if (prefilter != nullptr && sd->name.find(prefilter) == std::string::npos) continue; + if (prefilter != nullptr && sd->GetName().find(prefilter) == std::string::npos) continue; char value[80]; sd->FormatValue(value, lastof(value), &GetGameSettings()); - IConsolePrint(CC_DEFAULT, "{} = {}", sd->name, value); + IConsolePrint(CC_DEFAULT, "{} = {}", sd->GetName(), value); } } diff --git a/src/settings_internal.h b/src/settings_internal.h index a9083ce402..f48245af43 100644 --- a/src/settings_internal.h +++ b/src/settings_internal.h @@ -74,7 +74,9 @@ struct SettingDesc { name(name), flags(flags), startup(startup), save(save) {} virtual ~SettingDesc() {} +private: std::string name; ///< Name of the setting. Used in configuration file and for console. +public: SettingFlag flags; ///< Handles how a setting would show up in the GUI (text/currency, etc.). bool startup; ///< Setting has to be loaded directly at startup?. SaveLoad save; ///< Internal structure (going to savegame, parts to config). @@ -82,6 +84,15 @@ struct SettingDesc { bool IsEditable(bool do_command = false) const; SettingType GetType() const; + /** + * Get the name of this setting. + * @return The name of the setting. + */ + constexpr const std::string &GetName() const + { + return this->name; + } + /** * Check whether this setting is an integer type setting. * @return True when the underlying type is an integer. From ede3f79475bc987a154783b874619c16d1aa643c Mon Sep 17 00:00:00 2001 From: Rubidium Date: Thu, 8 Jul 2021 19:26:21 +0200 Subject: [PATCH 04/44] Codechange: use the name string in SaveLoad for the name of the Setting as well --- src/saveload/settings_sl.cpp | 6 +----- src/settings_internal.h | 35 ++++++++++++++++------------------- src/table/settings.h.preamble | 24 ++++++++++++------------ 3 files changed, 29 insertions(+), 36 deletions(-) diff --git a/src/saveload/settings_sl.cpp b/src/saveload/settings_sl.cpp index a939792387..1b1db8fa61 100644 --- a/src/saveload/settings_sl.cpp +++ b/src/saveload/settings_sl.cpp @@ -89,11 +89,7 @@ static std::vector GetSettingsDesc(const SettingTable &settings, bool continue; } - SaveLoad sv = sd->save; - /* Replace the name with the actual name of the setting. */ - assert(!sd->GetName().empty()); - sv.name = sd->GetName(); - saveloads.push_back(sv); + saveloads.push_back(sd->save); } return saveloads; diff --git a/src/settings_internal.h b/src/settings_internal.h index f48245af43..75b520222c 100644 --- a/src/settings_internal.h +++ b/src/settings_internal.h @@ -70,13 +70,10 @@ struct IniItem; /** Properties of config file settings. */ struct SettingDesc { - SettingDesc(SaveLoad save, const char *name, SettingFlag flags, bool startup) : - name(name), flags(flags), startup(startup), save(save) {} + SettingDesc(SaveLoad save, SettingFlag flags, bool startup) : + flags(flags), startup(startup), save(save) {} virtual ~SettingDesc() {} -private: - std::string name; ///< Name of the setting. Used in configuration file and for console. -public: SettingFlag flags; ///< Handles how a setting would show up in the GUI (text/currency, etc.). bool startup; ///< Setting has to be loaded directly at startup?. SaveLoad save; ///< Internal structure (going to savegame, parts to config). @@ -90,7 +87,7 @@ public: */ constexpr const std::string &GetName() const { - return this->name; + return this->save.name; } /** @@ -152,10 +149,10 @@ struct IntSettingDesc : SettingDesc { */ typedef void PostChangeCallback(int32 value); - IntSettingDesc(SaveLoad save, const char *name, SettingFlag flags, bool startup, int32 def, + IntSettingDesc(SaveLoad save, SettingFlag flags, bool startup, int32 def, int32 min, uint32 max, int32 interval, StringID str, StringID str_help, StringID str_val, SettingCategory cat, PreChangeCheck pre_check, PostChangeCallback post_callback) : - SettingDesc(save, name, flags, startup), def(def), min(min), max(max), interval(interval), + SettingDesc(save, flags, startup), def(def), min(min), max(max), interval(interval), str(str), str_help(str_help), str_val(str_val), cat(cat), pre_check(pre_check), post_callback(post_callback) {} virtual ~IntSettingDesc() {} @@ -194,10 +191,10 @@ private: /** Boolean setting. */ struct BoolSettingDesc : IntSettingDesc { - BoolSettingDesc(SaveLoad save, const char *name, SettingFlag flags, bool startup, bool def, + BoolSettingDesc(SaveLoad save, SettingFlag flags, bool startup, bool def, StringID str, StringID str_help, StringID str_val, SettingCategory cat, PreChangeCheck pre_check, PostChangeCallback post_callback) : - IntSettingDesc(save, name, flags, startup, def, 0, 1, 0, str, str_help, str_val, cat, + IntSettingDesc(save, flags, startup, def, 0, 1, 0, str, str_help, str_val, cat, pre_check, post_callback) {} virtual ~BoolSettingDesc() {} @@ -210,11 +207,11 @@ struct BoolSettingDesc : IntSettingDesc { struct OneOfManySettingDesc : IntSettingDesc { typedef size_t OnConvert(const char *value); ///< callback prototype for conversion error - OneOfManySettingDesc(SaveLoad save, const char *name, SettingFlag flags, bool startup, int32 def, + OneOfManySettingDesc(SaveLoad save, SettingFlag flags, bool startup, int32 def, int32 max, StringID str, StringID str_help, StringID str_val, SettingCategory cat, PreChangeCheck pre_check, PostChangeCallback post_callback, std::initializer_list many, OnConvert *many_cnvt) : - IntSettingDesc(save, name, flags, startup, def, 0, max, 0, str, str_help, str_val, cat, + IntSettingDesc(save, flags, startup, def, 0, max, 0, str, str_help, str_val, cat, pre_check, post_callback), many_cnvt(many_cnvt) { for (auto one : many) this->many.push_back(one); @@ -234,11 +231,11 @@ struct OneOfManySettingDesc : IntSettingDesc { /** Many of many setting. */ struct ManyOfManySettingDesc : OneOfManySettingDesc { - ManyOfManySettingDesc(SaveLoad save, const char *name, SettingFlag flags, bool startup, + ManyOfManySettingDesc(SaveLoad save, SettingFlag flags, bool startup, int32 def, StringID str, StringID str_help, StringID str_val, SettingCategory cat, PreChangeCheck pre_check, PostChangeCallback post_callback, std::initializer_list many, OnConvert *many_cnvt) : - OneOfManySettingDesc(save, name, flags, startup, def, (1 << many.size()) - 1, str, str_help, + OneOfManySettingDesc(save, flags, startup, def, (1 << many.size()) - 1, str, str_help, str_val, cat, pre_check, post_callback, many, many_cnvt) {} virtual ~ManyOfManySettingDesc() {} @@ -263,9 +260,9 @@ struct StringSettingDesc : SettingDesc { */ typedef void PostChangeCallback(const std::string &value); - StringSettingDesc(SaveLoad save, const char *name, SettingFlag flags, bool startup, const char *def, + StringSettingDesc(SaveLoad save, SettingFlag flags, bool startup, const char *def, uint32 max_length, PreChangeCheck pre_check, PostChangeCallback post_callback) : - SettingDesc(save, name, flags, startup), def(def == nullptr ? "" : def), max_length(max_length), + SettingDesc(save, flags, startup), def(def == nullptr ? "" : def), max_length(max_length), pre_check(pre_check), post_callback(post_callback) {} virtual ~StringSettingDesc() {} @@ -289,8 +286,8 @@ private: /** List/array settings. */ struct ListSettingDesc : SettingDesc { - ListSettingDesc(SaveLoad save, const char *name, SettingFlag flags, bool startup, const char *def) : - SettingDesc(save, name, flags, startup), def(def) {} + ListSettingDesc(SaveLoad save, SettingFlag flags, bool startup, const char *def) : + SettingDesc(save, flags, startup), def(def) {} virtual ~ListSettingDesc() {} const char *def; ///< default value given when none is present @@ -303,7 +300,7 @@ struct ListSettingDesc : SettingDesc { /** Placeholder for settings that have been removed, but might still linger in the savegame. */ struct NullSettingDesc : SettingDesc { NullSettingDesc(SaveLoad save) : - SettingDesc(save, "", SF_NOT_IN_CONFIG, false) {} + SettingDesc(save, SF_NOT_IN_CONFIG, false) {} virtual ~NullSettingDesc() {} void FormatValue(char *buf, const char *last, const void *object) const override { NOT_REACHED(); } diff --git a/src/table/settings.h.preamble b/src/table/settings.h.preamble index 032714bafe..dca29cf88c 100644 --- a/src/table/settings.h.preamble +++ b/src/table/settings.h.preamble @@ -57,42 +57,42 @@ static size_t ConvertLandscape(const char *value); /* Macros for various objects to go in the configuration file. * This section is for global variables */ #define SDTG_VAR(name, type, flags, var, def, min, max, interval, str, strhelp, strval, pre_check, post_callback, from, to, cat, extra, startup)\ - NSD(Int, SLEG_GENERAL(#var, SL_VAR, var, type, 1, from, to, extra), name, flags, startup, def, min, max, interval, str, strhelp, strval, cat, pre_check, post_callback) + NSD(Int, SLEG_GENERAL(name, SL_VAR, var, type, 1, from, to, extra), flags, startup, def, min, max, interval, str, strhelp, strval, cat, pre_check, post_callback) #define SDTG_BOOL(name, flags, var, def, str, strhelp, strval, pre_check, post_callback, from, to, cat, extra, startup)\ - NSD(Bool, SLEG_GENERAL(#var, SL_VAR, var, SLE_BOOL, 1, from, to, extra), name, flags, startup, def, str, strhelp, strval, cat, pre_check, post_callback) + NSD(Bool, SLEG_GENERAL(name, SL_VAR, var, SLE_BOOL, 1, from, to, extra), flags, startup, def, str, strhelp, strval, cat, pre_check, post_callback) #define SDTG_LIST(name, type, flags, var, def, length, from, to, cat, extra, startup)\ - NSD(List, SLEG_GENERAL(#var, SL_ARR, var, type, length, from, to, extra), name, flags, startup, def) + NSD(List, SLEG_GENERAL(name, SL_ARR, var, type, length, from, to, extra),flags, startup, def) #define SDTG_SSTR(name, type, flags, var, def, max_length, pre_check, post_callback, from, to, cat, extra, startup)\ - NSD(String, SLEG_GENERAL(#var, SL_STDSTR, var, type, sizeof(var), from, to, extra), name, flags, startup, def, max_length, pre_check, post_callback) + NSD(String, SLEG_GENERAL(name, SL_STDSTR, var, type, sizeof(var), from, to, extra), flags, startup, def, max_length, pre_check, post_callback) #define SDTG_OMANY(name, type, flags, var, def, max, full, str, strhelp, strval, pre_check, post_callback, from, to, cat, extra, startup)\ - NSD(OneOfMany, SLEG_GENERAL(#var, SL_VAR, var, type, 1, from, to, extra), name, flags, startup, def, max, str, strhelp, strval, cat, pre_check, post_callback, full, nullptr) + NSD(OneOfMany, SLEG_GENERAL(name, SL_VAR, var, type, 1, from, to, extra), flags, startup, def, max, str, strhelp, strval, cat, pre_check, post_callback, full, nullptr) #define SDTG_MMANY(name, type, flags, var, def, full, str, strhelp, strval, pre_check, post_callback, from, to, cat, extra, startup)\ - NSD(ManyOfMany, SLEG_GENERAL(#var, SL_VAR, var, type, 1, from, to, extra), name, flags, startup, def, str, strhelp, strval, cat, pre_check, post_callback, full, nullptr) + NSD(ManyOfMany, SLEG_GENERAL(name, SL_VAR, var, type, 1, from, to, extra), flags, startup, def, str, strhelp, strval, cat, pre_check, post_callback, full, nullptr) /* Macros for various objects to go in the configuration file. * This section is for structures where their various members are saved */ #define SDT_VAR(base, var, type, flags, def, min, max, interval, str, strhelp, strval, pre_check, post_callback, from, to, cat, extra, startup)\ - NSD(Int, SLE_GENERAL(SL_VAR, base, var, type, 1, from, to, extra), #var, flags, startup, def, min, max, interval, str, strhelp, strval, cat, pre_check, post_callback) + NSD(Int, SLE_GENERAL(SL_VAR, base, var, type, 1, from, to, extra), flags, startup, def, min, max, interval, str, strhelp, strval, cat, pre_check, post_callback) #define SDT_BOOL(base, var, flags, def, str, strhelp, strval, pre_check, post_callback, from, to, cat, extra, startup)\ - NSD(Bool, SLE_GENERAL(SL_VAR, base, var, SLE_BOOL, 1, from, to, extra), #var, flags, startup, def, str, strhelp, strval, cat, pre_check, post_callback) + NSD(Bool, SLE_GENERAL(SL_VAR, base, var, SLE_BOOL, 1, from, to, extra), flags, startup, def, str, strhelp, strval, cat, pre_check, post_callback) #define SDT_LIST(base, var, type, flags, def, from, to, cat, extra, startup)\ - NSD(List, SLE_GENERAL(SL_ARR, base, var, type, lengthof(((base*)8)->var), from, to, extra), #var, flags, startup, def) + NSD(List, SLE_GENERAL(SL_ARR, base, var, type, lengthof(((base*)8)->var), from, to, extra), flags, startup, def) #define SDT_SSTR(base, var, type, flags, def, pre_check, post_callback, from, to, cat, extra, startup)\ - NSD(String, SLE_GENERAL(SL_STDSTR, base, var, type, sizeof(((base*)8)->var), from, to, extra), #var, flags, startup, def, 0, pre_check, post_callback) + NSD(String, SLE_GENERAL(SL_STDSTR, base, var, type, sizeof(((base*)8)->var), from, to, extra), flags, startup, def, 0, pre_check, post_callback) #define SDT_OMANY(base, var, type, flags, def, max, full, str, strhelp, strval, pre_check, post_callback, from, to, load, cat, extra, startup)\ - NSD(OneOfMany, SLE_GENERAL(SL_VAR, base, var, type, 1, from, to, extra), #var, flags, startup, def, max, str, strhelp, strval, cat, pre_check, post_callback, full, load) + NSD(OneOfMany, SLE_GENERAL(SL_VAR, base, var, type, 1, from, to, extra), flags, startup, def, max, str, strhelp, strval, cat, pre_check, post_callback, full, load) #define SDT_MMANY(base, var, type, flags, def, full, str, pre_check, post_callback, strhelp, strval, from, to, cat, extra, startup)\ - NSD(ManyOfMany, SLE_GENERAL(SL_VAR, base, var, type, 1, from, to, extra), #var, flags, startup, def, str, strhelp, strval, cat, pre_check, post_callback, full, nullptr) + NSD(ManyOfMany, SLE_GENERAL(SL_VAR, base, var, type, 1, from, to, extra), flags, startup, def, str, strhelp, strval, cat, pre_check, post_callback, full, nullptr) #define SDTC_VAR(var, type, flags, def, min, max, interval, str, strhelp, strval, pre_check, post_callback, from, to, cat, extra, startup)\ From 1c0c4637d2d65194dcc10ab727a99ec74ea5d962 Mon Sep 17 00:00:00 2001 From: Rubidium Date: Sat, 10 Jul 2021 10:14:05 +0200 Subject: [PATCH 05/44] Fix 11ab3c4: [NewGRF] Overflow when determining cargo mask for string code 9A 1E 9A 1E: Print unsigned word as name of a cargo type (translated for GRF version >= 7). --- src/newgrf_text.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/newgrf_text.cpp b/src/newgrf_text.cpp index 18a876a6ac..6617365f9c 100644 --- a/src/newgrf_text.cpp +++ b/src/newgrf_text.cpp @@ -949,7 +949,7 @@ uint RemapNewGRFStringControlCode(uint scc, char *buf_start, char **buff, const case SCC_NEWGRF_PRINT_WORD_CARGO_NAME: { CargoID cargo = GetCargoTranslation(_newgrf_textrefstack.PopUnsignedWord(), _newgrf_textrefstack.grffile); - *argv = cargo < NUM_CARGO ? 1 << cargo : 0; + *argv = cargo < NUM_CARGO ? 1ULL << cargo : 0; break; } } From 0d0375c0194c5cb060fd88781da9d724702d0c1b Mon Sep 17 00:00:00 2001 From: Rubidium Date: Sat, 10 Jul 2021 10:37:46 +0200 Subject: [PATCH 06/44] Remove: logically dead code from font cache The outer if statement checks for 'aa' being false, so within the inner statements anything checking aa will have a known result and the other branch from there will be dead code. --- src/fontcache.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/fontcache.cpp b/src/fontcache.cpp index bbdec4b725..deb651e37c 100644 --- a/src/fontcache.cpp +++ b/src/fontcache.cpp @@ -618,9 +618,9 @@ const Sprite *FreeTypeFontCache::InternalGetGlyph(GlyphID key, bool aa) if (this->fs == FS_NORMAL && !aa) { for (uint y = 0; y < (uint)slot->bitmap.rows; y++) { for (uint x = 0; x < (uint)slot->bitmap.width; x++) { - if (aa ? (slot->bitmap.buffer[x + y * slot->bitmap.pitch] > 0) : HasBit(slot->bitmap.buffer[(x / 8) + y * slot->bitmap.pitch], 7 - (x % 8))) { + if (HasBit(slot->bitmap.buffer[(x / 8) + y * slot->bitmap.pitch], 7 - (x % 8))) { sprite.data[1 + x + (1 + y) * sprite.width].m = SHADOW_COLOUR; - sprite.data[1 + x + (1 + y) * sprite.width].a = aa ? slot->bitmap.buffer[x + y * slot->bitmap.pitch] : 0xFF; + sprite.data[1 + x + (1 + y) * sprite.width].a = 0xFF; } } } From 85faa218ffe3cc8eab9c8822a9f5a5117937d8b6 Mon Sep 17 00:00:00 2001 From: Rubidium Date: Sat, 10 Jul 2021 10:41:11 +0200 Subject: [PATCH 07/44] Remove: logically dead code in graph UI --- src/graph_gui.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/graph_gui.cpp b/src/graph_gui.cpp index 00e1efd906..4779f811a8 100644 --- a/src/graph_gui.cpp +++ b/src/graph_gui.cpp @@ -963,7 +963,7 @@ struct PaymentRatesGraphWindow : BaseGraphWindow { bool lowered = !HasBit(_legend_excluded_cargo, cs->Index()); /* Redraw box if lowered */ - if (lowered) DrawFrameRect(r.left, y, r.right, y + this->line_height - 1, COLOUR_BROWN, lowered ? FR_LOWERED : FR_NONE); + if (lowered) DrawFrameRect(r.left, y, r.right, y + this->line_height - 1, COLOUR_BROWN, FR_LOWERED); byte clk_dif = lowered ? 1 : 0; int rect_x = clk_dif + (rtl ? r.right - this->legend_width - WD_FRAMERECT_RIGHT : r.left + WD_FRAMERECT_LEFT); From ddb6024bc69c63e3dafec61b3f642eedc57555cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Guilloux?= Date: Sat, 10 Jul 2021 14:59:17 +0200 Subject: [PATCH 08/44] Codechange: Don't explicitly unset _generating_world outside of genworld.cpp (#9418) --- src/industry_gui.cpp | 8 ++++---- src/terraform_cmd.cpp | 7 ++++--- src/terraform_gui.cpp | 9 +++++---- src/town_gui.cpp | 8 +++++--- 4 files changed, 18 insertions(+), 14 deletions(-) diff --git a/src/industry_gui.cpp b/src/industry_gui.cpp index 5757a30480..d3e3d2a161 100644 --- a/src/industry_gui.cpp +++ b/src/industry_gui.cpp @@ -608,9 +608,9 @@ public: ShowErrorMessage(STR_ERROR_CAN_T_GENERATE_INDUSTRIES, STR_ERROR_MUST_FOUND_TOWN_FIRST, WL_INFO); } else { extern void GenerateIndustries(); - _generating_world = true; + Backup old_generating_world(_generating_world, true, FILE_LINE); GenerateIndustries(); - _generating_world = false; + old_generating_world.Restore(); } } @@ -712,15 +712,15 @@ public: } Backup cur_company(_current_company, OWNER_NONE, FILE_LINE); - _generating_world = true; + Backup old_generating_world(_generating_world, true, FILE_LINE); _ignore_restrictions = true; DoCommandP(tile, (layout_index << 8) | this->selected_type, seed, CMD_BUILD_INDUSTRY | CMD_MSG(STR_ERROR_CAN_T_CONSTRUCT_THIS_INDUSTRY), &CcBuildIndustry); cur_company.Restore(); + old_generating_world.Restore(); _ignore_restrictions = false; - _generating_world = false; } else { success = DoCommandP(tile, (layout_index << 8) | this->selected_type, seed, CMD_BUILD_INDUSTRY | CMD_MSG(STR_ERROR_CAN_T_CONSTRUCT_THIS_INDUSTRY)); } diff --git a/src/terraform_cmd.cpp b/src/terraform_cmd.cpp index f23ba78c64..eaed9e71c1 100644 --- a/src/terraform_cmd.cpp +++ b/src/terraform_cmd.cpp @@ -16,6 +16,7 @@ #include "object_base.h" #include "company_base.h" #include "company_func.h" +#include "core/backup_type.hpp" #include "table/strings.h" @@ -279,8 +280,8 @@ CommandCost CmdTerraformLand(TileIndex tile, DoCommandFlag flags, uint32 p1, uin bool indirectly_cleared = coa != nullptr && coa->first_tile != t; /* Check tiletype-specific things, and add extra-cost */ - const bool curr_gen = _generating_world; - if (_game_mode == GM_EDITOR) _generating_world = true; // used to create green terraformed land + Backup old_generating_world(_generating_world, FILE_LINE); + if (_game_mode == GM_EDITOR) old_generating_world.Change(true); // used to create green terraformed land DoCommandFlag tile_flags = flags | DC_AUTO | DC_FORCE_CLEAR_TILE; if (pass == 0) { tile_flags &= ~DC_EXEC; @@ -292,7 +293,7 @@ CommandCost CmdTerraformLand(TileIndex tile, DoCommandFlag flags, uint32 p1, uin } else { cost = _tile_type_procs[GetTileType(t)]->terraform_tile_proc(t, tile_flags, z_min, tileh); } - _generating_world = curr_gen; + old_generating_world.Restore(); if (cost.Failed()) { _terraform_err_tile = t; return cost; diff --git a/src/terraform_gui.cpp b/src/terraform_gui.cpp index 6094c710fd..f710cecc5c 100644 --- a/src/terraform_gui.cpp +++ b/src/terraform_gui.cpp @@ -8,6 +8,7 @@ /** @file terraform_gui.cpp GUI related to terraforming the map. */ #include "stdafx.h" +#include "core/backup_type.hpp" #include "clear_map.h" #include "company_func.h" #include "company_base.h" @@ -54,7 +55,7 @@ static void GenerateDesertArea(TileIndex end, TileIndex start) { if (_game_mode != GM_EDITOR) return; - _generating_world = true; + Backup old_generating_world(_generating_world, true, FILE_LINE); TileArea ta(start, end); for (TileIndex tile : ta) { @@ -62,7 +63,7 @@ static void GenerateDesertArea(TileIndex end, TileIndex start) DoCommandP(tile, 0, 0, CMD_LANDSCAPE_CLEAR); MarkTileDirtyByTile(tile); } - _generating_world = false; + old_generating_world.Restore(); InvalidateWindowClassesData(WC_TOWN_VIEW, 0); } @@ -497,7 +498,7 @@ static void ResetLandscapeConfirmationCallback(Window *w, bool confirmed) if (confirmed) { /* Set generating_world to true to get instant-green grass after removing * company property. */ - _generating_world = true; + Backup old_generating_world(_generating_world, true, FILE_LINE); /* Delete all companies */ for (Company *c : Company::Iterate()) { @@ -505,7 +506,7 @@ static void ResetLandscapeConfirmationCallback(Window *w, bool confirmed) delete c; } - _generating_world = false; + old_generating_world.Restore(); /* Delete all station signs */ for (BaseStation *st : BaseStation::Iterate()) { diff --git a/src/town_gui.cpp b/src/town_gui.cpp index ec44b246b1..a4bb240637 100644 --- a/src/town_gui.cpp +++ b/src/town_gui.cpp @@ -27,6 +27,7 @@ #include "querystring_gui.h" #include "window_func.h" #include "townname_func.h" +#include "core/backup_type.hpp" #include "core/geometry_func.hpp" #include "genworld.h" #include "stringfilter_type.h" @@ -1184,15 +1185,16 @@ public: this->SetFocusedWidget(WID_TF_TOWN_NAME_EDITBOX); break; - case WID_TF_MANY_RANDOM_TOWNS: - _generating_world = true; + case WID_TF_MANY_RANDOM_TOWNS: { + Backup old_generating_world(_generating_world, true, FILE_LINE); UpdateNearestTownForRoadTiles(true); if (!GenerateTowns(this->town_layout)) { ShowErrorMessage(STR_ERROR_CAN_T_GENERATE_TOWN, STR_ERROR_NO_SPACE_FOR_TOWN, WL_INFO); } UpdateNearestTownForRoadTiles(false); - _generating_world = false; + old_generating_world.Restore(); break; + } case WID_TF_SIZE_SMALL: case WID_TF_SIZE_MEDIUM: case WID_TF_SIZE_LARGE: case WID_TF_SIZE_RANDOM: this->town_size = (TownSize)(widget - WID_TF_SIZE_SMALL); From 8b1737f68088d563d0f6b8080f44bc59c690cbe7 Mon Sep 17 00:00:00 2001 From: Tyler Trahan Date: Sat, 10 Jul 2021 12:37:45 -0400 Subject: [PATCH 09/44] Doc: Add appstore links to README (#9426) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ab51479e1b..75ef46faa9 100644 --- a/README.md +++ b/README.md @@ -34,7 +34,7 @@ Both 'stable' and 'nightly' versions are available for download: - most people should choose the 'stable' version, as this has been more extensively tested - the 'nightly' version includes the latest changes and features, but may sometimes be less reliable -On some platforms OpenTTD will also be available via your OS package manager or a similar service. +OpenTTD is also available for free on [Steam](https://store.steampowered.com/app/1536610/OpenTTD/), [GOG.com](https://www.gog.com/game/openttd), and the [Microsoft Store](https://www.microsoft.com/p/openttd-official/9ncjg5rvrr1c). On some platforms OpenTTD will be available via your OS package manager or a similar service. ## 1.2) OpenTTD gameplay manual From 92cdfffbd1743b6209526d5f406faa963b7c743e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Guilloux?= Date: Sat, 10 Jul 2021 18:38:28 +0200 Subject: [PATCH 10/44] Doc: explain the binary structure of scripts custom data (#9425) --- docs/savegame_format.md | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/docs/savegame_format.md b/docs/savegame_format.md index d0731cbbaa..fafbc64471 100644 --- a/docs/savegame_format.md +++ b/docs/savegame_format.md @@ -184,3 +184,31 @@ We advise you to call this setting `__mypp_auto_destroy_rivers` in the settings Doing it this way ensures that a savegame created by these patch-packs can still safely be loaded by unpatched clients. They will simply ignore the field and continue loading the savegame as usual. The prefix is strongly advised to avoid conflicts with future-settings in an unpatched client or conflicts with other patch-packs. + +## Scripts custom data format + +Script chunks (`AIPL` and `GSDT`) use `CH_TABLE` chunk type. + +At the end of each record there's an `uint8` to indicate if there's custom data (1) or not (0). + +There are 6 data types for scripts, called `script-data-type`. +When saving, each `script-data-type` starts with the type marker saved as `uint8` followed by the actual data. +- `0` - `SQSL_INT`: + - an `int64` with the actual value (`int32` before savegame version 296). +- `1` - `SQSL_STRING`: + - an `uint8` with the string length. + - a list of `int8` for the string itself. +- `2` - `SQSL_ARRAY`: + - each element saved as `script-data-type`. + - an `SQSL_ARRAY_TABLE_END` (0xFF) marker (`uint8`). +- `3` - `SQSL_TABLE`: + - for each element: + - key saved as `script-data-type`. + - value saved as `script-data-type`. + - an `SQSL_ARRAY_TABLE_END` (0xFF) marker (`uint8`). +- `4` - `SQSL_BOOL`: + - an `uint8` with 0 (false) or 1 (true). +- `5` - `SQSL_NULL`: + - (no data follows) + +The first data type is always a `SQSL_TABLE`. From cbaac5609fb9017790c5cc747b2c60df1eb2b4a9 Mon Sep 17 00:00:00 2001 From: Patric Stout Date: Tue, 11 May 2021 14:59:55 +0200 Subject: [PATCH 11/44] Codechange: use UpdateNetworkGameWindow() over manually marking window dirty --- src/network/network_client.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/network/network_client.cpp b/src/network/network_client.cpp index fe4d1249c7..9f08062f35 100644 --- a/src/network/network_client.cpp +++ b/src/network/network_client.cpp @@ -590,7 +590,7 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_GAME_INFO(Packe item->online = true; /* It could be either window, but only one is open, so redraw both. */ - SetWindowDirty(WC_NETWORK_WINDOW, WN_NETWORK_WINDOW_GAME); + UpdateNetworkGameWindow(); SetWindowDirty(WC_NETWORK_WINDOW, WN_NETWORK_WINDOW_LOBBY); /* We will receive company info next, so keep connection open. */ From e1e2212e0e09c7739f2eb8a4421a9ed7982801f5 Mon Sep 17 00:00:00 2001 From: Patric Stout Date: Thu, 29 Apr 2021 16:30:42 +0200 Subject: [PATCH 12/44] Codechange: track version of network servers to prune once out-of-date --- src/network/network.cpp | 7 +++++-- src/network/network_gamelist.cpp | 31 ++++++++++++++++++++++++++++++- src/network/network_gamelist.h | 4 +++- src/network/network_internal.h | 2 +- src/network/network_udp.cpp | 2 ++ 5 files changed, 41 insertions(+), 5 deletions(-) diff --git a/src/network/network.cpp b/src/network/network.cpp index 9304048726..c05634e003 100644 --- a/src/network/network.cpp +++ b/src/network/network.cpp @@ -699,9 +699,11 @@ void NetworkQueryLobbyServer(const std::string &connection_string) * the list. If you use this function, the games will be marked * as manually added. * @param connection_string The IP:port of the server to add. + * @param manually Whether the enter should be marked as manual added. + * @param never_expire Whether the entry can expire (removed when no longer found in the public listing). * @return The entry on the game list. */ -NetworkGameList *NetworkAddServer(const std::string &connection_string, bool manually) +NetworkGameList *NetworkAddServer(const std::string &connection_string, bool manually, bool never_expire) { if (connection_string.empty()) return nullptr; @@ -717,6 +719,7 @@ NetworkGameList *NetworkAddServer(const std::string &connection_string, bool man } if (manually) item->manually = true; + if (never_expire) item->version = INT32_MAX; return item; } @@ -1288,7 +1291,7 @@ extern "C" { void CDECL em_openttd_add_server(const char *connection_string) { - NetworkAddServer(connection_string, false); + NetworkAddServer(connection_string, false, true); } } diff --git a/src/network/network_gamelist.cpp b/src/network/network_gamelist.cpp index ae716070c6..b4f34983f4 100644 --- a/src/network/network_gamelist.cpp +++ b/src/network/network_gamelist.cpp @@ -20,7 +20,8 @@ #include "../safeguards.h" -NetworkGameList *_network_game_list = nullptr; +NetworkGameList *_network_game_list = nullptr; ///< Game list of this client. +int _network_game_list_version = 0; ///< Current version of all items in the list. /** The games to insert when the GUI thread has time for us. */ static std::atomic _network_game_delayed_insertion_list(nullptr); @@ -81,6 +82,7 @@ NetworkGameList *NetworkGameListAddItem(const std::string &connection_string) } item = new NetworkGameList(resolved_connection_string); + item->version = _network_game_list_version; if (prev_item == nullptr) { _network_game_list = item; @@ -120,6 +122,33 @@ void NetworkGameListRemoveItem(NetworkGameList *remove) } } +/** + * Remove all servers that have not recently been updated. + * Call this after you received all the servers from the Game Coordinator, so + * the ones that are no longer listed are removed. + */ +void NetworkGameListRemoveExpired() +{ + NetworkGameList **prev_item = &_network_game_list; + + for (NetworkGameList *item = _network_game_list; item != nullptr;) { + if (!item->manually && item->version < _network_game_list_version) { + NetworkGameList *remove = item; + item = item->next; + *prev_item = item; + + /* Remove GRFConfig information */ + ClearGRFConfigList(&remove->info.grfconfig); + delete remove; + } else { + prev_item = &item->next; + item = item->next; + } + } + + UpdateNetworkGameWindow(); +} + static const uint MAX_GAME_LIST_REQUERY_COUNT = 10; ///< How often do we requery in number of times per server? static const uint REQUERY_EVERY_X_GAMELOOPS = 60; ///< How often do we requery in time? static const uint REFRESH_GAMEINFO_X_REQUERIES = 50; ///< Refresh the game info itself after REFRESH_GAMEINFO_X_REQUERIES * REQUERY_EVERY_X_GAMELOOPS game loops diff --git a/src/network/network_gamelist.h b/src/network/network_gamelist.h index 6ef9d8e41f..2bab7626e9 100644 --- a/src/network/network_gamelist.h +++ b/src/network/network_gamelist.h @@ -26,15 +26,17 @@ struct NetworkGameList { bool online = false; ///< False if the server did not respond (default status) bool manually = false; ///< True if the server was added manually uint8 retries = 0; ///< Number of retries (to stop requerying) + int version = 0; ///< Used to see which servers are no longer available on the Game Coordinator and can be removed. NetworkGameList *next = nullptr; ///< Next pointer to make a linked game list }; -/** Game list of this client */ extern NetworkGameList *_network_game_list; +extern int _network_game_list_version; void NetworkGameListAddItemDelayed(NetworkGameList *item); NetworkGameList *NetworkGameListAddItem(const std::string &connection_string); void NetworkGameListRemoveItem(NetworkGameList *remove); +void NetworkGameListRemoveExpired(); void NetworkGameListRequery(); #endif /* NETWORK_GAMELIST_H */ diff --git a/src/network/network_internal.h b/src/network/network_internal.h index b7e6f4ecef..67e97bb07e 100644 --- a/src/network/network_internal.h +++ b/src/network/network_internal.h @@ -91,7 +91,7 @@ void NetworkQueryServer(const std::string &connection_string); void NetworkQueryLobbyServer(const std::string &connection_string); void GetBindAddresses(NetworkAddressList *addresses, uint16 port); -struct NetworkGameList *NetworkAddServer(const std::string &connection_string, bool manually = true); +struct NetworkGameList *NetworkAddServer(const std::string &connection_string, bool manually = true, bool never_expire = false); void NetworkRebuildHostList(); void UpdateNetworkGameWindow(); diff --git a/src/network/network_udp.cpp b/src/network/network_udp.cpp index 6ad84aa2ed..5e5da27bba 100644 --- a/src/network/network_udp.cpp +++ b/src/network/network_udp.cpp @@ -327,6 +327,8 @@ void ClientNetworkUDPSocketHandler::Receive_SERVER_RESPONSE(Packet *p, NetworkAd CheckGameCompatibility(item->info); /* Ensure we consider the server online. */ item->online = true; + /* Make sure this entry never expires. */ + item->version = INT32_MAX; { /* Checks whether there needs to be a request for names of GRFs and makes From b1280fd17ebaf9e4df086b63074d0bd8b8c9155d Mon Sep 17 00:00:00 2001 From: Patric Stout Date: Sat, 3 Jul 2021 11:04:32 +0200 Subject: [PATCH 13/44] Add: use Game Coordinator to annouce public servers --- src/lang/english.txt | 14 +- src/network/CMakeLists.txt | 2 + src/network/core/CMakeLists.txt | 2 + src/network/core/config.h | 73 +++++---- src/network/core/tcp_coordinator.cpp | 80 +++++++++ src/network/core/tcp_coordinator.h | 115 +++++++++++++ src/network/network.cpp | 8 + src/network/network_coordinator.cpp | 234 +++++++++++++++++++++++++++ src/network/network_coordinator.h | 51 ++++++ src/network/network_gui.cpp | 17 +- src/network/network_internal.h | 2 + src/network/network_type.h | 9 ++ src/widgets/network_widget.h | 1 + 13 files changed, 569 insertions(+), 39 deletions(-) create mode 100644 src/network/core/tcp_coordinator.cpp create mode 100644 src/network/core/tcp_coordinator.h create mode 100644 src/network/network_coordinator.cpp create mode 100644 src/network/network_coordinator.h diff --git a/src/lang/english.txt b/src/lang/english.txt index bd5cff8d36..2e84d4b3a1 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -1995,7 +1995,7 @@ STR_FACE_TIE :Tie: STR_FACE_EARRING :Earring: STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}Change tie or earring -STR_NETWORK_SERVER_VISIBILITY_PRIVATE :Private +STR_NETWORK_SERVER_VISIBILITY_LOCAL :Local STR_NETWORK_SERVER_VISIBILITY_PUBLIC :Public # Network server list @@ -2136,6 +2136,8 @@ STR_NETWORK_CLIENT_LIST_SERVER_NAME_EDIT_TOOLTIP :{BLACK}Edit the STR_NETWORK_CLIENT_LIST_SERVER_NAME_QUERY_CAPTION :Name of the server STR_NETWORK_CLIENT_LIST_SERVER_VISIBILITY :{BLACK}Visibility STR_NETWORK_CLIENT_LIST_SERVER_VISIBILITY_TOOLTIP :{BLACK}Whether other people can see your server in the public listing +STR_NETWORK_CLIENT_LIST_SERVER_CONNECTION_TYPE :{BLACK}Connection type +STR_NETWORK_CLIENT_LIST_SERVER_CONNECTION_TYPE_TOOLTIP :{BLACK}Whether and how your server can be reached by others STR_NETWORK_CLIENT_LIST_PLAYER :{BLACK}Player STR_NETWORK_CLIENT_LIST_PLAYER_NAME :{BLACK}Name STR_NETWORK_CLIENT_LIST_PLAYER_NAME_TOOLTIP :{BLACK}Your player name @@ -2154,6 +2156,12 @@ STR_NETWORK_CLIENT_LIST_PLAYER_ICON_SELF_TOOLTIP :{BLACK}This is STR_NETWORK_CLIENT_LIST_PLAYER_ICON_HOST_TOOLTIP :{BLACK}This is the host of the game STR_NETWORK_CLIENT_LIST_CLIENT_COMPANY_COUNT :{BLACK}{NUM} client{P "" s} / {NUM} compan{P y ies} +############ Begin of ConnectionType +STR_NETWORK_CLIENT_LIST_SERVER_CONNECTION_TYPE_UNKNOWN :{BLACK}Local +STR_NETWORK_CLIENT_LIST_SERVER_CONNECTION_TYPE_ISOLATED :{RED}Remote players can't connect +STR_NETWORK_CLIENT_LIST_SERVER_CONNECTION_TYPE_DIRECT :{BLACK}Public +############ End of ConnectionType + STR_NETWORK_CLIENT_LIST_ADMIN_CLIENT_KICK :Kick STR_NETWORK_CLIENT_LIST_ADMIN_CLIENT_BAN :Ban STR_NETWORK_CLIENT_LIST_ADMIN_COMPANY_RESET :Delete @@ -2281,6 +2289,10 @@ STR_NETWORK_MESSAGE_SERVER_SHUTDOWN :{WHITE}The serv STR_NETWORK_MESSAGE_SERVER_REBOOT :{WHITE}The server is restarting...{}Please wait... STR_NETWORK_MESSAGE_KICKED :*** {RAW_STRING} was kicked. Reason: ({RAW_STRING}) +STR_NETWORK_ERROR_COORDINATOR_REGISTRATION_FAILED :{WHITE}Server registration failed +STR_NETWORK_ERROR_COORDINATOR_ISOLATED :{WHITE}Your server doesn't allow remote connections +STR_NETWORK_ERROR_COORDINATOR_ISOLATED_DETAIL :{WHITE}Other players won't be able to connect to your server + # Content downloading window STR_CONTENT_TITLE :{WHITE}Content downloading STR_CONTENT_TYPE_CAPTION :{BLACK}Type diff --git a/src/network/CMakeLists.txt b/src/network/CMakeLists.txt index 1c27d62066..bf89b5c1d7 100644 --- a/src/network/CMakeLists.txt +++ b/src/network/CMakeLists.txt @@ -14,6 +14,8 @@ add_files( network_content.h network_content_gui.cpp network_content_gui.h + network_coordinator.cpp + network_coordinator.h network_func.h network_gamelist.cpp network_gamelist.h diff --git a/src/network/core/CMakeLists.txt b/src/network/core/CMakeLists.txt index bf713be99c..6eb255f6fb 100644 --- a/src/network/core/CMakeLists.txt +++ b/src/network/core/CMakeLists.txt @@ -20,6 +20,8 @@ add_files( tcp_content.cpp tcp_content.h tcp_content_type.h + tcp_coordinator.cpp + tcp_coordinator.h tcp_game.cpp tcp_game.h tcp_http.cpp diff --git a/src/network/core/config.h b/src/network/core/config.h index 6d6038ec75..e0ec63a0c9 100644 --- a/src/network/core/config.h +++ b/src/network/core/config.h @@ -14,6 +14,8 @@ /** DNS hostname of the masterserver */ static const char * const NETWORK_MASTER_SERVER_HOST = "master.openttd.org"; +/** DNS hostname of the Game Coordinator server */ +static const char * const NETWORK_COORDINATOR_SERVER_HOST = "coordinator.openttd.org"; /** DNS hostname of the content server */ static const char * const NETWORK_CONTENT_SERVER_HOST = "content.openttd.org"; /** DNS hostname of the HTTP-content mirror server */ @@ -23,14 +25,15 @@ static const char * const NETWORK_CONTENT_MIRROR_URL = "/bananas"; /** Message sent to the masterserver to 'identify' this client as OpenTTD */ static const char * const NETWORK_MASTER_SERVER_WELCOME_MESSAGE = "OpenTTDRegister"; -static const uint16 NETWORK_MASTER_SERVER_PORT = 3978; ///< The default port of the master server (UDP) -static const uint16 NETWORK_CONTENT_SERVER_PORT = 3978; ///< The default port of the content server (TCP) -static const uint16 NETWORK_CONTENT_MIRROR_PORT = 80; ///< The default port of the content mirror (TCP) -static const uint16 NETWORK_DEFAULT_PORT = 3979; ///< The default port of the game server (TCP & UDP) -static const uint16 NETWORK_ADMIN_PORT = 3977; ///< The default port for admin network -static const uint16 NETWORK_DEFAULT_DEBUGLOG_PORT = 3982; ///< The default port debug-log is sent to (TCP) +static const uint16 NETWORK_MASTER_SERVER_PORT = 3978; ///< The default port of the master server (UDP) +static const uint16 NETWORK_COORDINATOR_SERVER_PORT = 3976; ///< The default port of the Game Coordinator server (TCP) +static const uint16 NETWORK_CONTENT_SERVER_PORT = 3978; ///< The default port of the content server (TCP) +static const uint16 NETWORK_CONTENT_MIRROR_PORT = 80; ///< The default port of the content mirror (TCP) +static const uint16 NETWORK_DEFAULT_PORT = 3979; ///< The default port of the game server (TCP & UDP) +static const uint16 NETWORK_ADMIN_PORT = 3977; ///< The default port for admin network +static const uint16 NETWORK_DEFAULT_DEBUGLOG_PORT = 3982; ///< The default port debug-log is sent to (TCP) -static const uint16 UDP_MTU = 1460; ///< Number of bytes we can pack in a single UDP packet +static const uint16 UDP_MTU = 1460; ///< Number of bytes we can pack in a single UDP packet /* * Technically a TCP packet could become 64kiB, however the high bit is kept so it becomes possible in the future * to go to (significantly) larger packets if needed. This would entail a strategy such as employed for UTF-8. @@ -45,40 +48,42 @@ static const uint16 UDP_MTU = 1460; ///< Number of * Send_uint16(GB(size, 16, 14) | 0b10 << 14) * Send_uint16(GB(size, 0, 16)) */ -static const uint16 TCP_MTU = 32767; ///< Number of bytes we can pack in a single TCP packet -static const uint16 COMPAT_MTU = 1460; ///< Number of bytes we can pack in a single packet for backward compatibility +static const uint16 TCP_MTU = 32767; ///< Number of bytes we can pack in a single TCP packet +static const uint16 COMPAT_MTU = 1460; ///< Number of bytes we can pack in a single packet for backward compatibility -static const byte NETWORK_GAME_ADMIN_VERSION = 1; ///< What version of the admin network do we use? -static const byte NETWORK_GAME_INFO_VERSION = 4; ///< What version of game-info do we use? -static const byte NETWORK_COMPANY_INFO_VERSION = 6; ///< What version of company info is this? -static const byte NETWORK_MASTER_SERVER_VERSION = 2; ///< What version of master-server-protocol do we use? +static const byte NETWORK_GAME_ADMIN_VERSION = 1; ///< What version of the admin network do we use? +static const byte NETWORK_GAME_INFO_VERSION = 4; ///< What version of game-info do we use? +static const byte NETWORK_COMPANY_INFO_VERSION = 6; ///< What version of company info is this? +static const byte NETWORK_MASTER_SERVER_VERSION = 2; ///< What version of master-server-protocol do we use? +static const byte NETWORK_COORDINATOR_VERSION = 1; ///< What version of game-coordinator-protocol do we use? -static const uint NETWORK_NAME_LENGTH = 80; ///< The maximum length of the server name and map name, in bytes including '\0' -static const uint NETWORK_COMPANY_NAME_LENGTH = 128; ///< The maximum length of the company name, in bytes including '\0' -static const uint NETWORK_HOSTNAME_LENGTH = 80; ///< The maximum length of the host name, in bytes including '\0' -static const uint NETWORK_HOSTNAME_PORT_LENGTH = 80 + 6; ///< The maximum length of the host name + port, in bytes including '\0'. The extra six is ":" + port number (with a max of 65536) -static const uint NETWORK_SERVER_ID_LENGTH = 33; ///< The maximum length of the network id of the servers, in bytes including '\0' -static const uint NETWORK_REVISION_LENGTH = 33; ///< The maximum length of the revision, in bytes including '\0' -static const uint NETWORK_PASSWORD_LENGTH = 33; ///< The maximum length of the password, in bytes including '\0' (must be >= NETWORK_SERVER_ID_LENGTH) -static const uint NETWORK_CLIENTS_LENGTH = 200; ///< The maximum length for the list of clients that controls a company, in bytes including '\0' -static const uint NETWORK_CLIENT_NAME_LENGTH = 25; ///< The maximum length of a client's name, in bytes including '\0' -static const uint NETWORK_RCONCOMMAND_LENGTH = 500; ///< The maximum length of a rconsole command, in bytes including '\0' -static const uint NETWORK_GAMESCRIPT_JSON_LENGTH = COMPAT_MTU-3; ///< The maximum length of a gamescript json string, in bytes including '\0'. Must not be longer than COMPAT_MTU including header (3 bytes) -static const uint NETWORK_CHAT_LENGTH = 900; ///< The maximum length of a chat message, in bytes including '\0' -static const uint NETWORK_CONTENT_FILENAME_LENGTH = 48; ///< The maximum length of a content's filename, in bytes including '\0'. -static const uint NETWORK_CONTENT_NAME_LENGTH = 32; ///< The maximum length of a content's name, in bytes including '\0'. -static const uint NETWORK_CONTENT_VERSION_LENGTH = 16; ///< The maximum length of a content's version, in bytes including '\0'. -static const uint NETWORK_CONTENT_URL_LENGTH = 96; ///< The maximum length of a content's url, in bytes including '\0'. -static const uint NETWORK_CONTENT_DESC_LENGTH = 512; ///< The maximum length of a content's description, in bytes including '\0'. -static const uint NETWORK_CONTENT_TAG_LENGTH = 32; ///< The maximum length of a content's tag, in bytes including '\0'. +static const uint NETWORK_NAME_LENGTH = 80; ///< The maximum length of the server name and map name, in bytes including '\0' +static const uint NETWORK_COMPANY_NAME_LENGTH = 128; ///< The maximum length of the company name, in bytes including '\0' +static const uint NETWORK_HOSTNAME_LENGTH = 80; ///< The maximum length of the host name, in bytes including '\0' +static const uint NETWORK_HOSTNAME_PORT_LENGTH = 80 + 6; ///< The maximum length of the host name + port, in bytes including '\0'. The extra six is ":" + port number (with a max of 65536) +static const uint NETWORK_SERVER_ID_LENGTH = 33; ///< The maximum length of the network id of the servers, in bytes including '\0' +static const uint NETWORK_REVISION_LENGTH = 33; ///< The maximum length of the revision, in bytes including '\0' +static const uint NETWORK_PASSWORD_LENGTH = 33; ///< The maximum length of the password, in bytes including '\0' (must be >= NETWORK_SERVER_ID_LENGTH) +static const uint NETWORK_CLIENTS_LENGTH = 200; ///< The maximum length for the list of clients that controls a company, in bytes including '\0' +static const uint NETWORK_CLIENT_NAME_LENGTH = 25; ///< The maximum length of a client's name, in bytes including '\0' +static const uint NETWORK_RCONCOMMAND_LENGTH = 500; ///< The maximum length of a rconsole command, in bytes including '\0' +static const uint NETWORK_GAMESCRIPT_JSON_LENGTH = COMPAT_MTU - 3; ///< The maximum length of a gamescript json string, in bytes including '\0'. Must not be longer than COMPAT_MTU including header (3 bytes) +static const uint NETWORK_CHAT_LENGTH = 900; ///< The maximum length of a chat message, in bytes including '\0' +static const uint NETWORK_CONTENT_FILENAME_LENGTH = 48; ///< The maximum length of a content's filename, in bytes including '\0'. +static const uint NETWORK_CONTENT_NAME_LENGTH = 32; ///< The maximum length of a content's name, in bytes including '\0'. +static const uint NETWORK_CONTENT_VERSION_LENGTH = 16; ///< The maximum length of a content's version, in bytes including '\0'. +static const uint NETWORK_CONTENT_URL_LENGTH = 96; ///< The maximum length of a content's url, in bytes including '\0'. +static const uint NETWORK_CONTENT_DESC_LENGTH = 512; ///< The maximum length of a content's description, in bytes including '\0'. +static const uint NETWORK_CONTENT_TAG_LENGTH = 32; ///< The maximum length of a content's tag, in bytes including '\0'. +static const uint NETWORK_ERROR_DETAIL_LENGTH = 100; ///< The maximum length of the error detail, in bytes including '\0' -static const uint NETWORK_GRF_NAME_LENGTH = 80; ///< Maximum length of the name of a GRF +static const uint NETWORK_GRF_NAME_LENGTH = 80; ///< Maximum length of the name of a GRF /** * Maximum number of GRFs that can be sent. * This limit is reached when PACKET_UDP_SERVER_RESPONSE reaches the maximum size of UDP_MTU bytes. */ -static const uint NETWORK_MAX_GRF_COUNT = 62; +static const uint NETWORK_MAX_GRF_COUNT = 62; /** * The number of landscapes in OpenTTD. @@ -88,6 +93,6 @@ static const uint NETWORK_MAX_GRF_COUNT = 62; * there is a compile assertion to check that this NUM_LANDSCAPE is equal * to NETWORK_NUM_LANDSCAPES. */ -static const uint NETWORK_NUM_LANDSCAPES = 4; +static const uint NETWORK_NUM_LANDSCAPES = 4; #endif /* NETWORK_CORE_CONFIG_H */ diff --git a/src/network/core/tcp_coordinator.cpp b/src/network/core/tcp_coordinator.cpp new file mode 100644 index 0000000000..1cf5cd676f --- /dev/null +++ b/src/network/core/tcp_coordinator.cpp @@ -0,0 +1,80 @@ +/* + * This file is part of OpenTTD. + * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. + * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see . + */ + +/** + * @file tcp_coordinator.cpp Basic functions to receive and send Game Coordinator packets. + */ + +#include "../../stdafx.h" +#include "../../date_func.h" +#include "../../debug.h" +#include "tcp_coordinator.h" + +#include "../../safeguards.h" + +/** + * Handle the given packet, i.e. pass it to the right. + * parser receive command. + * @param p The packet to handle. + * @return True iff we should immediately handle further packets. + */ +bool NetworkCoordinatorSocketHandler::HandlePacket(Packet *p) +{ + PacketCoordinatorType type = (PacketCoordinatorType)p->Recv_uint8(); + + switch (type) { + case PACKET_COORDINATOR_GC_ERROR: return this->Receive_GC_ERROR(p); + case PACKET_COORDINATOR_SERVER_REGISTER: return this->Receive_SERVER_REGISTER(p); + case PACKET_COORDINATOR_GC_REGISTER_ACK: return this->Receive_GC_REGISTER_ACK(p); + case PACKET_COORDINATOR_SERVER_UPDATE: return this->Receive_SERVER_UPDATE(p); + + default: + Debug(net, 0, "[tcp/coordinator] Received invalid packet type {}", type); + return false; + } +} + +/** + * Receive a packet at TCP level. + * @return Whether at least one packet was received. + */ +bool NetworkCoordinatorSocketHandler::ReceivePackets() +{ + /* + * We read only a few of the packets. This allows the GUI to update when + * a large set of servers is being received. Otherwise the interface + * "hangs" while the game is updating the server-list. + * + * What arbitrary number to choose is the ultimate question though. + */ + Packet *p; + static const int MAX_PACKETS_TO_RECEIVE = 42; + int i = MAX_PACKETS_TO_RECEIVE; + while (--i != 0 && (p = this->ReceivePacket()) != nullptr) { + bool cont = this->HandlePacket(p); + delete p; + if (!cont) return true; + } + + return i != MAX_PACKETS_TO_RECEIVE - 1; +} + +/** + * Helper for logging receiving invalid packets. + * @param type The received packet type. + * @return Always false, as it's an error. + */ +bool NetworkCoordinatorSocketHandler::ReceiveInvalidPacket(PacketCoordinatorType type) +{ + Debug(net, 0, "[tcp/coordinator] Received illegal packet type {}", type); + return false; +} + +bool NetworkCoordinatorSocketHandler::Receive_GC_ERROR(Packet *p) { return this->ReceiveInvalidPacket(PACKET_COORDINATOR_GC_ERROR); } +bool NetworkCoordinatorSocketHandler::Receive_SERVER_REGISTER(Packet *p) { return this->ReceiveInvalidPacket(PACKET_COORDINATOR_SERVER_REGISTER); } +bool NetworkCoordinatorSocketHandler::Receive_GC_REGISTER_ACK(Packet *p) { return this->ReceiveInvalidPacket(PACKET_COORDINATOR_GC_REGISTER_ACK); } +bool NetworkCoordinatorSocketHandler::Receive_SERVER_UPDATE(Packet *p) { return this->ReceiveInvalidPacket(PACKET_COORDINATOR_SERVER_UPDATE); } diff --git a/src/network/core/tcp_coordinator.h b/src/network/core/tcp_coordinator.h new file mode 100644 index 0000000000..a438f07aa5 --- /dev/null +++ b/src/network/core/tcp_coordinator.h @@ -0,0 +1,115 @@ +/* + * This file is part of OpenTTD. + * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. + * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see . + */ + +/** + * @file tcp_coordinator.h Basic functions to receive and send TCP packets to/from the Game Coordinator server. + */ + +#ifndef NETWORK_CORE_TCP_COORDINATOR_H +#define NETWORK_CORE_TCP_COORDINATOR_H + +#include "os_abstraction.h" +#include "tcp.h" +#include "packet.h" +#include "game_info.h" + +/** + * Enum with all types of TCP Game Coordinator packets. The order MUST not be changed. + * + * GC -> packets from Game Coordinator to either Client or Server. + * SERVER -> packets from Server to Game Coordinator. + * CLIENT -> packets from Client to Game Coordinator. + **/ +enum PacketCoordinatorType { + PACKET_COORDINATOR_GC_ERROR, ///< Game Coordinator indicates there was an error. + PACKET_COORDINATOR_SERVER_REGISTER, ///< Server registration. + PACKET_COORDINATOR_GC_REGISTER_ACK, ///< Game Coordinator accepts the registration. + PACKET_COORDINATOR_SERVER_UPDATE, ///< Server sends an set intervals an update of the server. + PACKET_COORDINATOR_END, ///< Must ALWAYS be on the end of this list!! (period). +}; + +/** + * The type of connection the Game Coordinator can detect we have. + */ +enum ConnectionType { + CONNECTION_TYPE_UNKNOWN, ///< The Game Coordinator hasn't informed us yet what type of connection we have. + CONNECTION_TYPE_ISOLATED, ///< The Game Coordinator failed to find a way to connect to your server. Nobody will be able to join. + CONNECTION_TYPE_DIRECT, ///< The Game Coordinator can directly connect to your server. +}; + +/** + * The type of error from the Game Coordinator. + */ +enum NetworkCoordinatorErrorType { + NETWORK_COORDINATOR_ERROR_UNKNOWN, ///< There was an unknown error. + NETWORK_COORDINATOR_ERROR_REGISTRATION_FAILED, ///< Your request for registration failed. +}; + +/** Base socket handler for all Game Coordinator TCP sockets. */ +class NetworkCoordinatorSocketHandler : public NetworkTCPSocketHandler { +protected: + bool ReceiveInvalidPacket(PacketCoordinatorType type); + + /** + * Game Coordinator indicates there was an error. This can either be a + * permanent error causing the connection to be dropped, or in response + * to a request that is invalid. + * + * uint8 Type of error (see NetworkCoordinatorErrorType). + * string Details of the error. + * + * @param p The packet that was just received. + * @return True upon success, otherwise false. + */ + virtual bool Receive_GC_ERROR(Packet *p); + + /** + * Server is starting a multiplayer game and wants to let the + * Game Coordinator know. + * + * uint8 Game Coordinator protocol version. + * uint8 Type of game (see ServerGameType). + * uint16 Local port of the server. + * + * @param p The packet that was just received. + * @return True upon success, otherwise false. + */ + virtual bool Receive_SERVER_REGISTER(Packet *p); + + /** + * Game Coordinator acknowledges the registration. + * + * uint8 Type of connection was detected (see ConnectionType). + * + * @param p The packet that was just received. + * @return True upon success, otherwise false. + */ + virtual bool Receive_GC_REGISTER_ACK(Packet *p); + + /** + * Send an update of the current state of the server to the Game Coordinator. + * + * uint8 Game Coordinator protocol version. + * Serialized NetworkGameInfo. See game_info.hpp for details. + * + * @param p The packet that was just received. + * @return True upon success, otherwise false. + */ + virtual bool Receive_SERVER_UPDATE(Packet *p); + + bool HandlePacket(Packet *p); +public: + /** + * Create a new cs socket handler for a given cs. + * @param s The socket we are connected with. + */ + NetworkCoordinatorSocketHandler(SOCKET s = INVALID_SOCKET) : NetworkTCPSocketHandler(s) {} + + bool ReceivePackets(); +}; + +#endif /* NETWORK_CORE_TCP_COORDINATOR_H */ diff --git a/src/network/network.cpp b/src/network/network.cpp index c05634e003..361517c967 100644 --- a/src/network/network.cpp +++ b/src/network/network.cpp @@ -19,6 +19,7 @@ #include "network_udp.h" #include "network_gamelist.h" #include "network_base.h" +#include "network_coordinator.h" #include "core/udp.h" #include "core/host.h" #include "network_gui.h" @@ -591,6 +592,8 @@ void NetworkClose(bool close_admins) } ServerNetworkGameSocketHandler::CloseListeners(); ServerNetworkAdminSocketHandler::CloseListeners(); + + _network_coordinator_client.CloseConnection(); } else if (MyClient::my_client != nullptr) { MyClient::SendQuit(); MyClient::my_client->CloseConnection(NETWORK_RECV_STATUS_CLIENT_QUIT); @@ -932,6 +935,10 @@ bool NetworkServerStart() NetworkInitGameInfo(); + if (_settings_client.network.server_advertise) { + _network_coordinator_client.Register(); + } + /* execute server initialization script */ IConsoleCmdExec("exec scripts/on_server.scr 0"); /* if the server is dedicated ... add some other script */ @@ -1032,6 +1039,7 @@ static void NetworkSend() void NetworkBackgroundLoop() { _network_content_client.SendReceive(); + _network_coordinator_client.SendReceive(); TCPConnecter::CheckCallbacks(); NetworkHTTPSocketHandler::HTTPReceive(); diff --git a/src/network/network_coordinator.cpp b/src/network/network_coordinator.cpp new file mode 100644 index 0000000000..3e40797407 --- /dev/null +++ b/src/network/network_coordinator.cpp @@ -0,0 +1,234 @@ + +/* + * This file is part of OpenTTD. + * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. + * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see . + */ + +/** @file network_coordinator.cpp Game Coordinator sending/receiving part of the network protocol. */ + +#include "../stdafx.h" +#include "../debug.h" +#include "../error.h" +#include "../rev.h" +#include "../settings_type.h" +#include "../strings_func.h" +#include "../window_func.h" +#include "../window_type.h" +#include "network.h" +#include "network_coordinator.h" +#include "network_gamelist.h" +#include "table/strings.h" + +#include "../safeguards.h" + +static const auto NETWORK_COORDINATOR_DELAY_BETWEEN_UPDATES = std::chrono::seconds(30); ///< How many time between updates the server sends to the Game Coordinator. +ClientNetworkCoordinatorSocketHandler _network_coordinator_client; ///< The connection to the Game Coordinator. +ConnectionType _network_server_connection_type = CONNECTION_TYPE_UNKNOWN; ///< What type of connection the Game Coordinator detected we are on. + +/** Connect to the Game Coordinator server. */ +class NetworkCoordinatorConnecter : TCPConnecter { +public: + /** + * Initiate the connecting. + * @param address The address of the Game Coordinator server. + */ + NetworkCoordinatorConnecter(const std::string &connection_string) : TCPConnecter(connection_string, NETWORK_COORDINATOR_SERVER_PORT) {} + + void OnFailure() override + { + _network_coordinator_client.connecting = false; + _network_coordinator_client.CloseConnection(true); + } + + void OnConnect(SOCKET s) override + { + assert(_network_coordinator_client.sock == INVALID_SOCKET); + + _network_coordinator_client.sock = s; + _network_coordinator_client.connecting = false; + } +}; + +bool ClientNetworkCoordinatorSocketHandler::Receive_GC_ERROR(Packet *p) +{ + NetworkCoordinatorErrorType error = (NetworkCoordinatorErrorType)p->Recv_uint8(); + std::string detail = p->Recv_string(NETWORK_ERROR_DETAIL_LENGTH); + + switch (error) { + case NETWORK_COORDINATOR_ERROR_UNKNOWN: + this->CloseConnection(); + return false; + + case NETWORK_COORDINATOR_ERROR_REGISTRATION_FAILED: + SetDParamStr(0, detail); + ShowErrorMessage(STR_NETWORK_ERROR_COORDINATOR_REGISTRATION_FAILED, STR_JUST_RAW_STRING, WL_ERROR); + + /* To prevent that we constantly try to reconnect, switch to private game. */ + _settings_client.network.server_advertise = false; + + this->CloseConnection(); + return false; + + default: + Debug(net, 0, "Invalid error type {} received from Game Coordinator", error); + this->CloseConnection(); + return false; + } +} + +bool ClientNetworkCoordinatorSocketHandler::Receive_GC_REGISTER_ACK(Packet *p) +{ + /* Schedule sending an update. */ + this->next_update = std::chrono::steady_clock::now(); + + _network_server_connection_type = (ConnectionType)p->Recv_uint8(); + + if (_network_server_connection_type == CONNECTION_TYPE_ISOLATED) { + ShowErrorMessage(STR_NETWORK_ERROR_COORDINATOR_ISOLATED, STR_NETWORK_ERROR_COORDINATOR_ISOLATED_DETAIL, WL_ERROR); + } + + SetWindowDirty(WC_CLIENT_LIST, 0); + + if (_network_dedicated) { + std::string connection_type; + switch (_network_server_connection_type) { + case CONNECTION_TYPE_ISOLATED: connection_type = "Remote players can't connect"; break; + case CONNECTION_TYPE_DIRECT: connection_type = "Public"; break; + + case CONNECTION_TYPE_UNKNOWN: // Never returned from Game Coordinator. + default: connection_type = "Unknown"; break; // Should never happen, but don't fail if it does. + } + + Debug(net, 3, "----------------------------------------"); + Debug(net, 3, "Your server is now registered with the Game Coordinator:"); + Debug(net, 3, " Game type: Public"); + Debug(net, 3, " Connection type: {}", connection_type); + Debug(net, 3, "----------------------------------------"); + } + + return true; +} + +void ClientNetworkCoordinatorSocketHandler::Connect() +{ + /* We are either already connected or are trying to connect. */ + if (this->sock != INVALID_SOCKET || this->connecting) return; + + this->Reopen(); + + this->connecting = true; + new NetworkCoordinatorConnecter(NETWORK_COORDINATOR_SERVER_HOST); +} + +NetworkRecvStatus ClientNetworkCoordinatorSocketHandler::CloseConnection(bool error) +{ + NetworkCoordinatorSocketHandler::CloseConnection(error); + + this->CloseSocket(); + this->connecting = false; + + _network_server_connection_type = CONNECTION_TYPE_UNKNOWN; + this->next_update = {}; + + SetWindowDirty(WC_CLIENT_LIST, 0); + + return NETWORK_RECV_STATUS_OKAY; +} + +/** + * Register our server to receive our join-key. + */ +void ClientNetworkCoordinatorSocketHandler::Register() +{ + _network_server_connection_type = CONNECTION_TYPE_UNKNOWN; + this->next_update = {}; + + SetWindowDirty(WC_CLIENT_LIST, 0); + + this->Connect(); + + Packet *p = new Packet(PACKET_COORDINATOR_SERVER_REGISTER); + p->Send_uint8(NETWORK_COORDINATOR_VERSION); + p->Send_uint8(SERVER_GAME_TYPE_PUBLIC); + p->Send_uint16(_settings_client.network.server_port); + + this->SendPacket(p); +} + +/** + * Send an update of our server status to the Game Coordinator. + */ +void ClientNetworkCoordinatorSocketHandler::SendServerUpdate() +{ + Debug(net, 6, "Sending server update to Game Coordinator"); + this->next_update = std::chrono::steady_clock::now() + NETWORK_COORDINATOR_DELAY_BETWEEN_UPDATES; + + Packet *p = new Packet(PACKET_COORDINATOR_SERVER_UPDATE); + p->Send_uint8(NETWORK_COORDINATOR_VERSION); + SerializeNetworkGameInfo(p, GetCurrentNetworkServerGameInfo()); + + this->SendPacket(p); +} + +/** + * Check whether we received/can send some data from/to the Game Coordinator server and + * when that's the case handle it appropriately. + */ +void ClientNetworkCoordinatorSocketHandler::SendReceive() +{ + /* Private games are not listed via the Game Coordinator. */ + if (_network_server && !_settings_client.network.server_advertise) { + if (this->sock != INVALID_SOCKET) { + this->CloseConnection(); + } + return; + } + + static int last_attempt_backoff = 1; + static bool first_reconnect = true; + + if (this->sock == INVALID_SOCKET) { + static std::chrono::steady_clock::time_point last_attempt = {}; + + /* Don't auto-reconnect when we are not a server. */ + if (!_network_server) return; + /* Don't reconnect if we are connecting. */ + if (this->connecting) return; + /* Throttle how often we try to reconnect. */ + if (std::chrono::steady_clock::now() < last_attempt + std::chrono::seconds(1) * last_attempt_backoff) return; + + last_attempt = std::chrono::steady_clock::now(); + /* Delay reconnecting with up to 32 seconds. */ + if (last_attempt_backoff < 32) { + last_attempt_backoff *= 2; + } + + /* Do not reconnect on the first attempt, but only initialize the + * last_attempt variables. Otherwise after an outage all servers + * reconnect at the same time, potentially overwhelming the + * Game Coordinator. */ + if (first_reconnect) { + first_reconnect = false; + return; + } + + Debug(net, 1, "Connection with Game Coordinator lost; reconnecting..."); + this->Register(); + return; + } + + last_attempt_backoff = 1; + first_reconnect = true; + + if (_network_server && _network_server_connection_type != CONNECTION_TYPE_UNKNOWN && std::chrono::steady_clock::now() > this->next_update) { + this->SendServerUpdate(); + } + + if (this->CanSendReceive()) { + this->ReceivePackets(); + } + + this->SendPackets(); +} diff --git a/src/network/network_coordinator.h b/src/network/network_coordinator.h new file mode 100644 index 0000000000..1dbce063b5 --- /dev/null +++ b/src/network/network_coordinator.h @@ -0,0 +1,51 @@ + +/* + * This file is part of OpenTTD. + * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. + * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see . + */ + +/** @file network_coordinator.h Part of the network protocol handling Game Coordinator requests. */ + +#ifndef NETWORK_COORDINATOR_H +#define NETWORK_COORDINATOR_H + +#include "core/tcp_coordinator.h" + +/** + * Game Coordinator communication. + * + * For servers: + * - Server sends SERVER_REGISTER. + * - Game Coordinator probes server to check if it can directly connect. + * - Game Coordinator sends GC_REGISTER_ACK with type of connection. + * - Server sends every 30 seconds SERVER_UPDATE. + */ + +/** Class for handling the client side of the Game Coordinator connection. */ +class ClientNetworkCoordinatorSocketHandler : public NetworkCoordinatorSocketHandler { +private: + std::chrono::steady_clock::time_point next_update; ///< When to send the next update (if server and public). + +protected: + bool Receive_GC_ERROR(Packet *p) override; + bool Receive_GC_REGISTER_ACK(Packet *p) override; + +public: + bool connecting; ///< Are we connecting to the Game Coordinator? + + ClientNetworkCoordinatorSocketHandler() : connecting(false) {} + + NetworkRecvStatus CloseConnection(bool error = true) override; + void SendReceive(); + + void Connect(); + + void Register(); + void SendServerUpdate(); +}; + +extern ClientNetworkCoordinatorSocketHandler _network_coordinator_client; + +#endif /* NETWORK_COORDINATOR_H */ diff --git a/src/network/network_gui.cpp b/src/network/network_gui.cpp index e3e05db926..6f48bf022c 100644 --- a/src/network/network_gui.cpp +++ b/src/network/network_gui.cpp @@ -62,7 +62,7 @@ static CompanyID _admin_company_id = INVALID_COMPANY; ///< For what company a co * do not. */ static const StringID _server_visibility_dropdown[] = { - STR_NETWORK_SERVER_VISIBILITY_PRIVATE, + STR_NETWORK_SERVER_VISIBILITY_LOCAL, STR_NETWORK_SERVER_VISIBILITY_PUBLIC, INVALID_STRING_ID }; @@ -1607,21 +1607,26 @@ static const NWidgetPart _nested_client_list_widgets[] = { NWidget(WWT_FRAME, COLOUR_GREY), SetDataTip(STR_NETWORK_CLIENT_LIST_SERVER, STR_NULL), SetPadding(4, 4, 0, 4), SetPIP(0, 2, 0), NWidget(NWID_HORIZONTAL), SetPIP(0, 3, 0), NWidget(WWT_TEXT, COLOUR_GREY), SetMinimalTextLines(1, 0), SetDataTip(STR_NETWORK_CLIENT_LIST_SERVER_NAME, STR_NULL), - NWidget(NWID_SPACER), SetMinimalSize(20, 0), + NWidget(NWID_SPACER), SetMinimalSize(10, 0), NWidget(WWT_TEXT, COLOUR_GREY, WID_CL_SERVER_NAME), SetFill(1, 0), SetMinimalTextLines(1, 0), SetResize(1, 0), SetDataTip(STR_BLACK_RAW_STRING, STR_NETWORK_CLIENT_LIST_SERVER_NAME_TOOLTIP), SetAlignment(SA_VERT_CENTER | SA_RIGHT), NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_CL_SERVER_NAME_EDIT), SetMinimalSize(12, 14), SetDataTip(SPR_RENAME, STR_NETWORK_CLIENT_LIST_SERVER_NAME_EDIT_TOOLTIP), EndContainer(), NWidget(NWID_HORIZONTAL), SetPIP(0, 3, 0), NWidget(WWT_TEXT, COLOUR_GREY), SetMinimalTextLines(1, 0), SetDataTip(STR_NETWORK_CLIENT_LIST_SERVER_VISIBILITY, STR_NULL), - NWidget(NWID_SPACER), SetMinimalSize(20, 0), SetFill(1, 0), SetResize(1, 0), + NWidget(NWID_SPACER), SetMinimalSize(10, 0), SetFill(1, 0), SetResize(1, 0), NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_CL_SERVER_VISIBILITY), SetDataTip(STR_BLACK_STRING, STR_NETWORK_CLIENT_LIST_SERVER_VISIBILITY_TOOLTIP), EndContainer(), + NWidget(NWID_HORIZONTAL), SetPIP(0, 3, 0), + NWidget(WWT_TEXT, COLOUR_GREY), SetMinimalTextLines(1, 0), SetDataTip(STR_NETWORK_CLIENT_LIST_SERVER_CONNECTION_TYPE, STR_NULL), + NWidget(NWID_SPACER), SetMinimalSize(10, 0), + NWidget(WWT_TEXT, COLOUR_GREY, WID_CL_SERVER_CONNECTION_TYPE), SetFill(1, 0), SetMinimalTextLines(1, 0), SetResize(1, 0), SetDataTip(STR_BLACK_STRING, STR_NETWORK_CLIENT_LIST_SERVER_CONNECTION_TYPE_TOOLTIP), SetAlignment(SA_VERT_CENTER | SA_RIGHT), + EndContainer(), EndContainer(), EndContainer(), NWidget(WWT_FRAME, COLOUR_GREY), SetDataTip(STR_NETWORK_CLIENT_LIST_PLAYER, STR_NULL), SetPadding(4, 4, 4, 4), SetPIP(0, 2, 0), NWidget(NWID_HORIZONTAL), SetPIP(0, 3, 0), NWidget(WWT_TEXT, COLOUR_GREY), SetMinimalTextLines(1, 0), SetDataTip(STR_NETWORK_CLIENT_LIST_PLAYER_NAME, STR_NULL), - NWidget(NWID_SPACER), SetMinimalSize(20, 0), + NWidget(NWID_SPACER), SetMinimalSize(10, 0), NWidget(WWT_TEXT, COLOUR_GREY, WID_CL_CLIENT_NAME), SetFill(1, 0), SetMinimalTextLines(1, 0), SetResize(1, 0), SetDataTip(STR_BLACK_RAW_STRING, STR_NETWORK_CLIENT_LIST_PLAYER_NAME_TOOLTIP), SetAlignment(SA_VERT_CENTER | SA_RIGHT), NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_CL_CLIENT_NAME_EDIT), SetMinimalSize(12, 14), SetDataTip(SPR_RENAME, STR_NETWORK_CLIENT_LIST_PLAYER_NAME_EDIT_TOOLTIP), EndContainer(), @@ -2050,6 +2055,10 @@ public: SetDParam(0, _server_visibility_dropdown[_settings_client.network.server_advertise]); break; + case WID_CL_SERVER_CONNECTION_TYPE: + SetDParam(0, STR_NETWORK_CLIENT_LIST_SERVER_CONNECTION_TYPE_UNKNOWN + _network_server_connection_type); + break; + case WID_CL_CLIENT_NAME: SetDParamStr(0, _settings_client.network.client_name); break; diff --git a/src/network/network_internal.h b/src/network/network_internal.h index 67e97bb07e..c1e6aa7b94 100644 --- a/src/network/network_internal.h +++ b/src/network/network_internal.h @@ -11,6 +11,7 @@ #define NETWORK_INTERNAL_H #include "network_func.h" +#include "core/tcp_coordinator.h" #include "core/tcp_game.h" #include "../command_type.h" @@ -82,6 +83,7 @@ extern NetworkJoinStatus _network_join_status; extern uint8 _network_join_waiting; extern uint32 _network_join_bytes; extern uint32 _network_join_bytes_total; +extern ConnectionType _network_server_connection_type; extern uint8 _network_reconnect; diff --git a/src/network/network_type.h b/src/network/network_type.h index fb99536953..6e6fe33ded 100644 --- a/src/network/network_type.h +++ b/src/network/network_type.h @@ -35,6 +35,15 @@ enum NetworkVehicleType { NETWORK_VEH_END }; +/** + * Game type the server can be using. + * Used on the network protocol to communicate with Game Coordinator. + */ +enum ServerGameType : uint8 { + SERVER_GAME_TYPE_LOCAL = 0, + SERVER_GAME_TYPE_PUBLIC, +}; + /** 'Unique' identifier to be given to clients */ enum ClientID : uint32 { INVALID_CLIENT_ID = 0, ///< Client is not part of anything diff --git a/src/widgets/network_widget.h b/src/widgets/network_widget.h index a96a56fa81..a665afdb74 100644 --- a/src/widgets/network_widget.h +++ b/src/widgets/network_widget.h @@ -101,6 +101,7 @@ enum ClientListWidgets { WID_CL_SERVER_NAME, ///< Server name. WID_CL_SERVER_NAME_EDIT, ///< Edit button for server name. WID_CL_SERVER_VISIBILITY, ///< Server visibility. + WID_CL_SERVER_CONNECTION_TYPE, ///< The type of connection the Game Coordinator detected for this server. WID_CL_CLIENT_NAME, ///< Client name. WID_CL_CLIENT_NAME_EDIT, ///< Edit button for client name. WID_CL_MATRIX, ///< Company/client list. From aa93d76223f0411b54f5150adbe2c83c33fd61af Mon Sep 17 00:00:00 2001 From: Patric Stout Date: Sat, 3 Jul 2021 11:12:28 +0200 Subject: [PATCH 14/44] Add: use Game Coordinator to get latest public server listing --- src/network/core/tcp_coordinator.cpp | 4 ++ src/network/core/tcp_coordinator.h | 29 ++++++++++++ src/network/network_coordinator.cpp | 66 +++++++++++++++++++++++++++- src/network/network_coordinator.h | 10 +++++ src/network/network_gui.cpp | 40 ++++++++++++----- 5 files changed, 136 insertions(+), 13 deletions(-) diff --git a/src/network/core/tcp_coordinator.cpp b/src/network/core/tcp_coordinator.cpp index 1cf5cd676f..bbcb59b14d 100644 --- a/src/network/core/tcp_coordinator.cpp +++ b/src/network/core/tcp_coordinator.cpp @@ -31,6 +31,8 @@ bool NetworkCoordinatorSocketHandler::HandlePacket(Packet *p) case PACKET_COORDINATOR_SERVER_REGISTER: return this->Receive_SERVER_REGISTER(p); case PACKET_COORDINATOR_GC_REGISTER_ACK: return this->Receive_GC_REGISTER_ACK(p); case PACKET_COORDINATOR_SERVER_UPDATE: return this->Receive_SERVER_UPDATE(p); + case PACKET_COORDINATOR_CLIENT_LISTING: return this->Receive_CLIENT_LISTING(p); + case PACKET_COORDINATOR_GC_LISTING: return this->Receive_GC_LISTING(p); default: Debug(net, 0, "[tcp/coordinator] Received invalid packet type {}", type); @@ -78,3 +80,5 @@ bool NetworkCoordinatorSocketHandler::Receive_GC_ERROR(Packet *p) { return this- bool NetworkCoordinatorSocketHandler::Receive_SERVER_REGISTER(Packet *p) { return this->ReceiveInvalidPacket(PACKET_COORDINATOR_SERVER_REGISTER); } bool NetworkCoordinatorSocketHandler::Receive_GC_REGISTER_ACK(Packet *p) { return this->ReceiveInvalidPacket(PACKET_COORDINATOR_GC_REGISTER_ACK); } bool NetworkCoordinatorSocketHandler::Receive_SERVER_UPDATE(Packet *p) { return this->ReceiveInvalidPacket(PACKET_COORDINATOR_SERVER_UPDATE); } +bool NetworkCoordinatorSocketHandler::Receive_CLIENT_LISTING(Packet *p) { return this->ReceiveInvalidPacket(PACKET_COORDINATOR_CLIENT_LISTING); } +bool NetworkCoordinatorSocketHandler::Receive_GC_LISTING(Packet *p) { return this->ReceiveInvalidPacket(PACKET_COORDINATOR_GC_LISTING); } diff --git a/src/network/core/tcp_coordinator.h b/src/network/core/tcp_coordinator.h index a438f07aa5..e95916816f 100644 --- a/src/network/core/tcp_coordinator.h +++ b/src/network/core/tcp_coordinator.h @@ -29,6 +29,8 @@ enum PacketCoordinatorType { PACKET_COORDINATOR_SERVER_REGISTER, ///< Server registration. PACKET_COORDINATOR_GC_REGISTER_ACK, ///< Game Coordinator accepts the registration. PACKET_COORDINATOR_SERVER_UPDATE, ///< Server sends an set intervals an update of the server. + PACKET_COORDINATOR_CLIENT_LISTING, ///< Client is requesting a listing of all public servers. + PACKET_COORDINATOR_GC_LISTING, ///< Game Coordinator returns a listing of all public servers. PACKET_COORDINATOR_END, ///< Must ALWAYS be on the end of this list!! (period). }; @@ -101,6 +103,33 @@ protected: */ virtual bool Receive_SERVER_UPDATE(Packet *p); + /** + * Client requests a list of all public servers. + * + * uint8 Game Coordinator protocol version. + * uint8 Game-info version used by this client. + * string Revision of the client. + * + * @param p The packet that was just received. + * @return True upon success, otherwise false. + */ + virtual bool Receive_CLIENT_LISTING(Packet *p); + + /** + * Game Coordinator replies with a list of all public servers. Multiple + * of these packets are received after a request till all servers are + * sent over. Last packet will have server count of 0. + * + * uint16 Amount of public servers in this packet. + * For each server: + * string Connection string for this server. + * Serialized NetworkGameInfo. See game_info.hpp for details. + * + * @param p The packet that was just received. + * @return True upon success, otherwise false. + */ + virtual bool Receive_GC_LISTING(Packet *p); + bool HandlePacket(Packet *p); public: /** diff --git a/src/network/network_coordinator.cpp b/src/network/network_coordinator.cpp index 3e40797407..b9299d549f 100644 --- a/src/network/network_coordinator.cpp +++ b/src/network/network_coordinator.cpp @@ -19,6 +19,7 @@ #include "network.h" #include "network_coordinator.h" #include "network_gamelist.h" +#include "network_internal.h" #include "table/strings.h" #include "../safeguards.h" @@ -47,6 +48,7 @@ public: assert(_network_coordinator_client.sock == INVALID_SOCKET); _network_coordinator_client.sock = s; + _network_coordinator_client.last_activity = std::chrono::steady_clock::now(); _network_coordinator_client.connecting = false; } }; @@ -111,6 +113,42 @@ bool ClientNetworkCoordinatorSocketHandler::Receive_GC_REGISTER_ACK(Packet *p) return true; } +bool ClientNetworkCoordinatorSocketHandler::Receive_GC_LISTING(Packet *p) +{ + uint8 servers = p->Recv_uint16(); + + /* End of list; we can now remove all expired items from the list. */ + if (servers == 0) { + NetworkGameListRemoveExpired(); + return true; + } + + for (; servers > 0; servers--) { + std::string connection_string = p->Recv_string(NETWORK_HOSTNAME_PORT_LENGTH); + + /* Read the NetworkGameInfo from the packet. */ + NetworkGameInfo ngi = {}; + DeserializeNetworkGameInfo(p, &ngi); + + /* Now we know the join-key, we can add it to our list. */ + NetworkGameList *item = NetworkGameListAddItem(connection_string); + + /* Clear any existing GRFConfig chain. */ + ClearGRFConfigList(&item->info.grfconfig); + /* Copy the new NetworkGameInfo info. */ + item->info = ngi; + /* Check for compatability with the client. */ + CheckGameCompatibility(item->info); + /* Mark server as online. */ + item->online = true; + /* Mark the item as up-to-date. */ + item->version = _network_game_list_version; + } + + UpdateNetworkGameWindow(); + return true; +} + void ClientNetworkCoordinatorSocketHandler::Connect() { /* We are either already connected or are trying to connect. */ @@ -119,6 +157,8 @@ void ClientNetworkCoordinatorSocketHandler::Connect() this->Reopen(); this->connecting = true; + this->last_activity = std::chrono::steady_clock::now(); + new NetworkCoordinatorConnecter(NETWORK_COORDINATOR_SERVER_HOST); } @@ -172,6 +212,23 @@ void ClientNetworkCoordinatorSocketHandler::SendServerUpdate() this->SendPacket(p); } +/** + * Request a listing of all public servers. + */ +void ClientNetworkCoordinatorSocketHandler::GetListing() +{ + this->Connect(); + + _network_game_list_version++; + + Packet *p = new Packet(PACKET_COORDINATOR_CLIENT_LISTING); + p->Send_uint8(NETWORK_COORDINATOR_VERSION); + p->Send_uint8(NETWORK_GAME_INFO_VERSION); + p->Send_string(_openttd_revision); + + this->SendPacket(p); +} + /** * Check whether we received/can send some data from/to the Game Coordinator server and * when that's the case handle it appropriately. @@ -226,8 +283,15 @@ void ClientNetworkCoordinatorSocketHandler::SendReceive() this->SendServerUpdate(); } + if (!_network_server && std::chrono::steady_clock::now() > this->last_activity + IDLE_TIMEOUT) { + this->CloseConnection(); + return; + } + if (this->CanSendReceive()) { - this->ReceivePackets(); + if (this->ReceivePackets()) { + this->last_activity = std::chrono::steady_clock::now(); + } } this->SendPackets(); diff --git a/src/network/network_coordinator.h b/src/network/network_coordinator.h index 1dbce063b5..7399ce1fc1 100644 --- a/src/network/network_coordinator.h +++ b/src/network/network_coordinator.h @@ -21,6 +21,10 @@ * - Game Coordinator probes server to check if it can directly connect. * - Game Coordinator sends GC_REGISTER_ACK with type of connection. * - Server sends every 30 seconds SERVER_UPDATE. + * + * For clients (listing): + * - Client sends CLIENT_LISTING. + * - Game Coordinator returns the full list of public servers via GC_LISTING (multiple packets). */ /** Class for handling the client side of the Game Coordinator connection. */ @@ -31,8 +35,13 @@ private: protected: bool Receive_GC_ERROR(Packet *p) override; bool Receive_GC_REGISTER_ACK(Packet *p) override; + bool Receive_GC_LISTING(Packet *p) override; public: + /** The idle timeout; when to close the connection because it's idle. */ + static constexpr std::chrono::seconds IDLE_TIMEOUT = std::chrono::seconds(60); + + std::chrono::steady_clock::time_point last_activity; ///< The last time there was network activity. bool connecting; ///< Are we connecting to the Game Coordinator? ClientNetworkCoordinatorSocketHandler() : connecting(false) {} @@ -44,6 +53,7 @@ public: void Register(); void SendServerUpdate(); + void GetListing(); }; extern ClientNetworkCoordinatorSocketHandler _network_coordinator_client; diff --git a/src/network/network_gui.cpp b/src/network/network_gui.cpp index 6f48bf022c..24884ed165 100644 --- a/src/network/network_gui.cpp +++ b/src/network/network_gui.cpp @@ -18,6 +18,7 @@ #include "network_base.h" #include "network_content.h" #include "network_server.h" +#include "network_coordinator.h" #include "../gui.h" #include "network_udp.h" #include "../window_func.h" @@ -54,6 +55,8 @@ static void ShowNetworkStartServerWindow(); static void ShowNetworkLobbyWindow(NetworkGameList *ngl); +static const int NETWORK_LIST_REFRESH_DELAY = 30; ///< Time, in seconds, between updates of the network list. + static ClientID _admin_client_id = INVALID_CLIENT_ID; ///< For what client a confirmation window is open. static CompanyID _admin_company_id = INVALID_COMPANY; ///< For what company a confirmation window is open. @@ -219,14 +222,15 @@ protected: static GUIGameServerList::SortFunction * const sorter_funcs[]; static GUIGameServerList::FilterFunction * const filter_funcs[]; - NetworkGameList *server; ///< selected server - NetworkGameList *last_joined; ///< the last joined server - GUIGameServerList servers; ///< list with game servers. - ServerListPosition list_pos; ///< position of the selected server - Scrollbar *vscroll; ///< vertical scrollbar of the list of servers - QueryString name_editbox; ///< Client name editbox. - QueryString filter_editbox; ///< Editbox for filter on servers - GUITimer requery_timer; ///< Timer for network requery + NetworkGameList *server; ///< Selected server. + NetworkGameList *last_joined; ///< The last joined server. + GUIGameServerList servers; ///< List with game servers. + ServerListPosition list_pos; ///< Position of the selected server. + Scrollbar *vscroll; ///< Vertical scrollbar of the list of servers. + QueryString name_editbox; ///< Client name editbox. + QueryString filter_editbox; ///< Editbox for filter on servers. + GUITimer requery_timer; ///< Timer for network requery. + bool searched_internet = false; ///< Did we ever press "Search Internet" button? int lock_offset; ///< Left offset for lock icon. int blot_offset; ///< Left offset for green/yellow/red compatibility icon. @@ -244,8 +248,18 @@ protected: /* Create temporary array of games to use for listing */ this->servers.clear(); + bool found_current_server = false; for (NetworkGameList *ngl = _network_game_list; ngl != nullptr; ngl = ngl->next) { this->servers.push_back(ngl); + if (ngl == this->server) { + found_current_server = true; + } + } + /* A refresh can cause the current server to be delete; so unselect. */ + if (!found_current_server) { + if (this->server == this->last_joined) this->last_joined = nullptr; + this->server = nullptr; + this->list_pos = SLP_INVALID; } /* Apply the filter condition immediately, if a search string has been provided. */ @@ -479,7 +493,7 @@ public: this->last_joined = NetworkAddServer(_settings_client.network.last_joined, false); this->server = this->last_joined; - this->requery_timer.SetInterval(MILLISECONDS_PER_TICK); + this->requery_timer.SetInterval(NETWORK_LIST_REFRESH_DELAY * 1000); this->servers.SetListing(this->last_sorting); this->servers.SetSortFuncs(this->sorter_funcs); @@ -725,7 +739,8 @@ public: } case WID_NG_SEARCH_INTERNET: - NetworkUDPQueryMasterServer(); + _network_coordinator_client.GetListing(); + this->searched_internet = true; break; case WID_NG_SEARCH_LAN: @@ -841,10 +856,11 @@ public: void OnRealtimeTick(uint delta_ms) override { + if (!this->searched_internet) return; if (!this->requery_timer.Elapsed(delta_ms)) return; - this->requery_timer.SetInterval(MILLISECONDS_PER_TICK); + this->requery_timer.SetInterval(NETWORK_LIST_REFRESH_DELAY * 1000); - NetworkGameListRequery(); + _network_coordinator_client.GetListing(); } }; From 61fdef84577642122aa8fba58e8ceddfd5bdb67b Mon Sep 17 00:00:00 2001 From: Patric Stout Date: Thu, 29 Apr 2021 16:11:44 +0200 Subject: [PATCH 15/44] Remove: old server announcement to Master Server As we now use the Game Coordinator for announcements, there is no longer a need to use the Master Server for this. --- src/network/network.cpp | 8 -- src/network/network_func.h | 1 - src/network/network_server.cpp | 3 - src/network/network_udp.cpp | 160 +-------------------------------- src/network/network_udp.h | 2 - 5 files changed, 3 insertions(+), 171 deletions(-) diff --git a/src/network/network.cpp b/src/network/network.cpp index 361517c967..395c374b85 100644 --- a/src/network/network.cpp +++ b/src/network/network.cpp @@ -61,7 +61,6 @@ bool _is_network_server; ///< Does this client wants to be a network-server? NetworkCompanyState *_network_company_states = nullptr; ///< Statistics about some companies. ClientID _network_own_client_id; ///< Our client identifier. ClientID _redirect_console_to_client; ///< If not invalid, redirect the console output to a client. -bool _network_need_advertise; ///< Whether we need to advertise. uint8 _network_reconnect; ///< Reconnect timeout StringList _network_bind_list; ///< The addresses to bind on. StringList _network_host_list; ///< The servers we know. @@ -944,10 +943,6 @@ bool NetworkServerStart() /* if the server is dedicated ... add some other script */ if (_network_dedicated) IConsoleCmdExec("exec scripts/on_dedicated.scr 0"); - /* Try to register us to the master server */ - _network_need_advertise = true; - NetworkUDPAdvertise(); - /* welcome possibly still connected admins - this can only happen on a dedicated server. */ if (_network_dedicated) ServerNetworkAdminSocketHandler::WelcomeAll(); @@ -996,8 +991,6 @@ void NetworkDisconnect(bool blocking, bool close_admins) } } - if (_settings_client.network.server_advertise) NetworkUDPRemoveAdvertise(blocking); - CloseWindowById(WC_NETWORK_STATUS_WINDOW, WN_NETWORK_STATUS_WINDOW_JOIN); NetworkClose(close_admins); @@ -1269,7 +1262,6 @@ void NetworkStartUp() /* Network is available */ _network_available = NetworkCoreInitialize(); _network_dedicated = false; - _network_need_advertise = true; /* Generate an server id when there is none yet */ if (_settings_client.network.network_id.empty()) NetworkGenerateServerId(); diff --git a/src/network/network_func.h b/src/network/network_func.h index 0dd9328eb6..6da8fb5cc1 100644 --- a/src/network/network_func.h +++ b/src/network/network_func.h @@ -28,7 +28,6 @@ extern NetworkCompanyState *_network_company_states; extern ClientID _network_own_client_id; extern ClientID _redirect_console_to_client; -extern bool _network_need_advertise; extern uint8 _network_reconnect; extern StringList _network_bind_list; extern StringList _network_host_list; diff --git a/src/network/network_server.cpp b/src/network/network_server.cpp index d44b61d3df..46d6918acd 100644 --- a/src/network/network_server.cpp +++ b/src/network/network_server.cpp @@ -1879,9 +1879,6 @@ void NetworkServer_Tick(bool send_frame) #endif } } - - /* See if we need to advertise */ - NetworkUDPAdvertise(); } /** Yearly "callback". Called whenever the year changes. */ diff --git a/src/network/network_udp.cpp b/src/network/network_udp.cpp index 5e5da27bba..ffa07f087a 100644 --- a/src/network/network_udp.cpp +++ b/src/network/network_udp.cpp @@ -8,7 +8,7 @@ /** * @file network_udp.cpp This file handles the UDP related communication. * - * This is the GameServer <-> MasterServer and GameServer <-> GameClient + * This is the GameServer <-> GameClient * communication before the game is being joined. */ @@ -34,16 +34,8 @@ #include "../safeguards.h" -/** Session key to register ourselves to the master server */ -static uint64 _session_key = 0; - -static const std::chrono::minutes ADVERTISE_NORMAL_INTERVAL(15); ///< interval between advertising. -static const std::chrono::seconds ADVERTISE_RETRY_INTERVAL(10); ///< re-advertise when no response after this amount of time. -static const uint32 ADVERTISE_RETRY_TIMES = 3; ///< give up re-advertising after this much failed retries - static bool _network_udp_server; ///< Is the UDP server started? static uint16 _network_udp_broadcast; ///< Timeout for the UDP broadcasts. -static uint8 _network_advertise_retries; ///< The number of advertisement retries we did. /** Some information about a socket, which exists before the actual socket has been created to provide locking and the likes. */ struct UDPSocket { @@ -79,7 +71,6 @@ struct UDPSocket { static UDPSocket _udp_client("Client"); ///< udp client socket static UDPSocket _udp_server("Server"); ///< udp server socket -static UDPSocket _udp_master("Master"); ///< udp master socket /** * Helper function doing the actual work for querying the server. @@ -114,37 +105,6 @@ void NetworkUDPQueryServer(const std::string &connection_string, bool manually) } } -///*** Communication with the masterserver ***/ - -/** Helper class for connecting to the master server. */ -class MasterNetworkUDPSocketHandler : public NetworkUDPSocketHandler { -protected: - void Receive_MASTER_ACK_REGISTER(Packet *p, NetworkAddress *client_addr) override; - void Receive_MASTER_SESSION_KEY(Packet *p, NetworkAddress *client_addr) override; -public: - /** - * Create the socket. - * @param addresses The addresses to bind on. - */ - MasterNetworkUDPSocketHandler(NetworkAddressList *addresses) : NetworkUDPSocketHandler(addresses) {} - virtual ~MasterNetworkUDPSocketHandler() {} -}; - -void MasterNetworkUDPSocketHandler::Receive_MASTER_ACK_REGISTER(Packet *p, NetworkAddress *client_addr) -{ - _network_advertise_retries = 0; - Debug(net, 3, "Advertising on master server successful ({})", NetworkAddress::AddressFamilyAsString(client_addr->GetAddress()->ss_family)); - - /* We are advertised, but we don't want to! */ - if (!_settings_client.network.server_advertise) NetworkUDPRemoveAdvertise(false); -} - -void MasterNetworkUDPSocketHandler::Receive_MASTER_SESSION_KEY(Packet *p, NetworkAddress *client_addr) -{ - _session_key = p->Recv_uint64(); - Debug(net, 6, "Received new session key from master server ({})", NetworkAddress::AddressFamilyAsString(client_addr->GetAddress()->ss_family)); -} - ///*** Communication with clients (we are server) ***/ /** Helper class for handling all server side communication. */ @@ -477,113 +437,6 @@ void NetworkUDPSearchGame() _network_udp_broadcast = 300; // Stay searching for 300 ticks } -/** - * Thread entry point for de-advertising. - */ -static void NetworkUDPRemoveAdvertiseThread() -{ - Debug(net, 3, "Removing advertise from master server"); - - /* Find somewhere to send */ - NetworkAddress out_addr(NETWORK_MASTER_SERVER_HOST, NETWORK_MASTER_SERVER_PORT); - - /* Send the packet */ - Packet p(PACKET_UDP_SERVER_UNREGISTER); - /* Packet is: Version, server_port */ - p.Send_uint8 (NETWORK_MASTER_SERVER_VERSION); - p.Send_uint16(_settings_client.network.server_port); - - std::lock_guard lock(_udp_master.mutex); - if (_udp_master.socket != nullptr) _udp_master.socket->SendPacket(&p, &out_addr, true); -} - -/** - * Remove our advertise from the master-server. - * @param blocking whether to wait until the removal has finished. - */ -void NetworkUDPRemoveAdvertise(bool blocking) -{ - /* Check if we are advertising */ - if (!_networking || !_network_server || !_network_udp_server) return; - - if (blocking || !StartNewThread(nullptr, "ottd:udp-advert", &NetworkUDPRemoveAdvertiseThread)) { - NetworkUDPRemoveAdvertiseThread(); - } -} - -/** - * Thread entry point for advertising. - */ -static void NetworkUDPAdvertiseThread() -{ - /* Find somewhere to send */ - NetworkAddress out_addr(NETWORK_MASTER_SERVER_HOST, NETWORK_MASTER_SERVER_PORT); - - Debug(net, 3, "Advertising to master server"); - - /* Add a bit more messaging when we cannot get a session key */ - static byte session_key_retries = 0; - if (_session_key == 0 && session_key_retries++ == 2) { - Debug(net, 0, "Advertising to the master server is failing"); - Debug(net, 0, " we are not receiving the session key from the server"); - Debug(net, 0, " please allow udp packets from {} to you to be delivered", out_addr.GetAddressAsString(false)); - Debug(net, 0, " please allow udp packets from you to {} to be delivered", out_addr.GetAddressAsString(false)); - } - if (_session_key != 0 && _network_advertise_retries == 0) { - Debug(net, 0, "Advertising to the master server is failing"); - Debug(net, 0, " we are not receiving the acknowledgement from the server"); - Debug(net, 0, " this usually means that the master server cannot reach us"); - Debug(net, 0, " please allow udp and tcp packets to port {} to be delivered", _settings_client.network.server_port); - Debug(net, 0, " please allow udp and tcp packets from port {} to be delivered", _settings_client.network.server_port); - } - - /* Send the packet */ - Packet p(PACKET_UDP_SERVER_REGISTER); - /* Packet is: WELCOME_MESSAGE, Version, server_port */ - p.Send_string(NETWORK_MASTER_SERVER_WELCOME_MESSAGE); - p.Send_uint8 (NETWORK_MASTER_SERVER_VERSION); - p.Send_uint16(_settings_client.network.server_port); - p.Send_uint64(_session_key); - - std::lock_guard lock(_udp_master.mutex); - if (_udp_master.socket != nullptr) _udp_master.socket->SendPacket(&p, &out_addr, true); -} - -/** - * Register us to the master server - * This function checks if it needs to send an advertise - */ -void NetworkUDPAdvertise() -{ - static std::chrono::steady_clock::time_point _last_advertisement = {}; ///< The last time we performed an advertisement. - - /* Check if we should send an advertise */ - if (!_networking || !_network_server || !_network_udp_server || !_settings_client.network.server_advertise) return; - - if (_network_need_advertise) { - /* Forced advertisement. */ - _network_need_advertise = false; - _network_advertise_retries = ADVERTISE_RETRY_TIMES; - } else { - /* Only send once every ADVERTISE_NORMAL_INTERVAL ticks */ - if (_network_advertise_retries == 0) { - if (std::chrono::steady_clock::now() <= _last_advertisement + ADVERTISE_NORMAL_INTERVAL) return; - - _network_advertise_retries = ADVERTISE_RETRY_TIMES; - } else { - /* An actual retry. */ - if (std::chrono::steady_clock::now() <= _last_advertisement + ADVERTISE_RETRY_INTERVAL) return; - } - } - - _network_advertise_retries--; - _last_advertisement = std::chrono::steady_clock::now(); - - if (!StartNewThread(nullptr, "ottd:udp-advert", &NetworkUDPAdvertiseThread)) { - NetworkUDPAdvertiseThread(); - } -} - /** Initialize the whole UDP bit. */ void NetworkUDPInitialize() { @@ -591,9 +444,9 @@ void NetworkUDPInitialize() if (_udp_server.socket != nullptr) NetworkUDPClose(); Debug(net, 3, "Initializing UDP listeners"); - assert(_udp_client.socket == nullptr && _udp_server.socket == nullptr && _udp_master.socket == nullptr); + assert(_udp_client.socket == nullptr && _udp_server.socket == nullptr); - std::scoped_lock lock(_udp_client.mutex, _udp_server.mutex, _udp_master.mutex); + std::scoped_lock lock(_udp_client.mutex, _udp_server.mutex); _udp_client.socket = new ClientNetworkUDPSocketHandler(); @@ -601,13 +454,8 @@ void NetworkUDPInitialize() GetBindAddresses(&server, _settings_client.network.server_port); _udp_server.socket = new ServerNetworkUDPSocketHandler(&server); - server.clear(); - GetBindAddresses(&server, 0); - _udp_master.socket = new MasterNetworkUDPSocketHandler(&server); - _network_udp_server = false; _network_udp_broadcast = 0; - _network_advertise_retries = 0; } /** Start the listening of the UDP server component. */ @@ -622,7 +470,6 @@ void NetworkUDPClose() { _udp_client.CloseSocket(); _udp_server.CloseSocket(); - _udp_master.CloseSocket(); _network_udp_server = false; _network_udp_broadcast = 0; @@ -634,7 +481,6 @@ void NetworkBackgroundUDPLoop() { if (_network_udp_server) { _udp_server.ReceivePackets(); - _udp_master.ReceivePackets(); } else { _udp_client.ReceivePackets(); if (_network_udp_broadcast > 0) _network_udp_broadcast--; diff --git a/src/network/network_udp.h b/src/network/network_udp.h index ced949cd32..735e5f2230 100644 --- a/src/network/network_udp.h +++ b/src/network/network_udp.h @@ -16,8 +16,6 @@ void NetworkUDPInitialize(); void NetworkUDPSearchGame(); void NetworkUDPQueryMasterServer(); void NetworkUDPQueryServer(const std::string &connection_string, bool manually = false); -void NetworkUDPAdvertise(); -void NetworkUDPRemoveAdvertise(bool blocking); void NetworkUDPClose(); void NetworkUDPServerListen(); void NetworkBackgroundUDPLoop(); From 8a2da494138b9dda5ae94d83a95153f2c68afb58 Mon Sep 17 00:00:00 2001 From: Patric Stout Date: Thu, 29 Apr 2021 17:52:09 +0200 Subject: [PATCH 16/44] Remove: old server listing via Master Server This removes all UDP from the game except for a local broadcast to find LAN games. So long Master Server, and tnx for all the fish! --- src/network/core/config.h | 6 - src/network/network_gamelist.cpp | 64 ------ src/network/network_gamelist.h | 2 - src/network/network_udp.cpp | 333 +------------------------------ src/network/network_udp.h | 2 - src/settings_type.h | 2 +- 6 files changed, 8 insertions(+), 401 deletions(-) diff --git a/src/network/core/config.h b/src/network/core/config.h index e0ec63a0c9..ab1c3082eb 100644 --- a/src/network/core/config.h +++ b/src/network/core/config.h @@ -12,8 +12,6 @@ #ifndef NETWORK_CORE_CONFIG_H #define NETWORK_CORE_CONFIG_H -/** DNS hostname of the masterserver */ -static const char * const NETWORK_MASTER_SERVER_HOST = "master.openttd.org"; /** DNS hostname of the Game Coordinator server */ static const char * const NETWORK_COORDINATOR_SERVER_HOST = "coordinator.openttd.org"; /** DNS hostname of the content server */ @@ -22,10 +20,7 @@ static const char * const NETWORK_CONTENT_SERVER_HOST = "content.opent static const char * const NETWORK_CONTENT_MIRROR_HOST = "binaries.openttd.org"; /** URL of the HTTP mirror system */ static const char * const NETWORK_CONTENT_MIRROR_URL = "/bananas"; -/** Message sent to the masterserver to 'identify' this client as OpenTTD */ -static const char * const NETWORK_MASTER_SERVER_WELCOME_MESSAGE = "OpenTTDRegister"; -static const uint16 NETWORK_MASTER_SERVER_PORT = 3978; ///< The default port of the master server (UDP) static const uint16 NETWORK_COORDINATOR_SERVER_PORT = 3976; ///< The default port of the Game Coordinator server (TCP) static const uint16 NETWORK_CONTENT_SERVER_PORT = 3978; ///< The default port of the content server (TCP) static const uint16 NETWORK_CONTENT_MIRROR_PORT = 80; ///< The default port of the content mirror (TCP) @@ -54,7 +49,6 @@ static const uint16 COMPAT_MTU = 1460; ///< Numbe static const byte NETWORK_GAME_ADMIN_VERSION = 1; ///< What version of the admin network do we use? static const byte NETWORK_GAME_INFO_VERSION = 4; ///< What version of game-info do we use? static const byte NETWORK_COMPANY_INFO_VERSION = 6; ///< What version of company info is this? -static const byte NETWORK_MASTER_SERVER_VERSION = 2; ///< What version of master-server-protocol do we use? static const byte NETWORK_COORDINATOR_VERSION = 1; ///< What version of game-coordinator-protocol do we use? static const uint NETWORK_NAME_LENGTH = 80; ///< The maximum length of the server name and map name, in bytes including '\0' diff --git a/src/network/network_gamelist.cpp b/src/network/network_gamelist.cpp index b4f34983f4..92964bf25a 100644 --- a/src/network/network_gamelist.cpp +++ b/src/network/network_gamelist.cpp @@ -23,45 +23,6 @@ NetworkGameList *_network_game_list = nullptr; ///< Game list of this client. int _network_game_list_version = 0; ///< Current version of all items in the list. -/** The games to insert when the GUI thread has time for us. */ -static std::atomic _network_game_delayed_insertion_list(nullptr); - -/** - * Add a new item to the linked gamelist, but do it delayed in the next tick - * or so to prevent race conditions. - * @param item the item to add. Will be freed once added. - */ -void NetworkGameListAddItemDelayed(NetworkGameList *item) -{ - item->next = _network_game_delayed_insertion_list.load(std::memory_order_relaxed); - while (!_network_game_delayed_insertion_list.compare_exchange_weak(item->next, item, std::memory_order_acq_rel)) {} -} - -/** Perform the delayed (thread safe) insertion into the game list */ -static void NetworkGameListHandleDelayedInsert() -{ - while (true) { - NetworkGameList *ins_item = _network_game_delayed_insertion_list.load(std::memory_order_relaxed); - while (ins_item != nullptr && !_network_game_delayed_insertion_list.compare_exchange_weak(ins_item, ins_item->next, std::memory_order_acq_rel)) {} - if (ins_item == nullptr) break; // No item left. - - NetworkGameList *item = NetworkGameListAddItem(ins_item->connection_string); - - if (item != nullptr) { - if (item->info.server_name.empty()) { - ClearGRFConfigList(&item->info.grfconfig); - item->info = {}; - item->info.server_name = ins_item->info.server_name; - item->online = false; - } - item->manually |= ins_item->manually; - if (item->manually) NetworkRebuildHostList(); - UpdateNetworkGameWindow(); - } - delete ins_item; - } -} - /** * Add a new item to the linked gamelist. If the IP and Port match * return the existing item instead of adding it again @@ -149,31 +110,6 @@ void NetworkGameListRemoveExpired() UpdateNetworkGameWindow(); } -static const uint MAX_GAME_LIST_REQUERY_COUNT = 10; ///< How often do we requery in number of times per server? -static const uint REQUERY_EVERY_X_GAMELOOPS = 60; ///< How often do we requery in time? -static const uint REFRESH_GAMEINFO_X_REQUERIES = 50; ///< Refresh the game info itself after REFRESH_GAMEINFO_X_REQUERIES * REQUERY_EVERY_X_GAMELOOPS game loops - -/** Requeries the (game) servers we have not gotten a reply from */ -void NetworkGameListRequery() -{ - NetworkGameListHandleDelayedInsert(); - - static uint8 requery_cnt = 0; - - if (++requery_cnt < REQUERY_EVERY_X_GAMELOOPS) return; - requery_cnt = 0; - - for (NetworkGameList *item = _network_game_list; item != nullptr; item = item->next) { - item->retries++; - if (item->retries < REFRESH_GAMEINFO_X_REQUERIES && (item->online || item->retries >= MAX_GAME_LIST_REQUERY_COUNT)) continue; - - /* item gets mostly zeroed by NetworkUDPQueryServer */ - uint8 retries = item->retries; - NetworkUDPQueryServer(item->connection_string); - item->retries = (retries >= REFRESH_GAMEINFO_X_REQUERIES) ? 0 : retries; - } -} - /** * Rebuild the GRFConfig's of the servers in the game list as we did * a rescan and might have found new NewGRFs. diff --git a/src/network/network_gamelist.h b/src/network/network_gamelist.h index 2bab7626e9..7fdd37c669 100644 --- a/src/network/network_gamelist.h +++ b/src/network/network_gamelist.h @@ -33,10 +33,8 @@ struct NetworkGameList { extern NetworkGameList *_network_game_list; extern int _network_game_list_version; -void NetworkGameListAddItemDelayed(NetworkGameList *item); NetworkGameList *NetworkGameListAddItem(const std::string &connection_string); void NetworkGameListRemoveItem(NetworkGameList *remove); void NetworkGameListRemoveExpired(); -void NetworkGameListRequery(); #endif /* NETWORK_GAMELIST_H */ diff --git a/src/network/network_udp.cpp b/src/network/network_udp.cpp index ffa07f087a..3deb7e3129 100644 --- a/src/network/network_udp.cpp +++ b/src/network/network_udp.cpp @@ -23,12 +23,10 @@ #include "network.h" #include "../core/endian_func.hpp" #include "../company_base.h" -#include "../thread.h" #include "../rev.h" #include "../newgrf_text.h" #include "../strings_func.h" #include "table/strings.h" -#include #include "core/udp.h" @@ -40,79 +38,32 @@ static uint16 _network_udp_broadcast; ///< Timeout for the UDP broadcasts. /** Some information about a socket, which exists before the actual socket has been created to provide locking and the likes. */ struct UDPSocket { const std::string name; ///< The name of the socket. - std::mutex mutex; ///< Mutex for everything that (indirectly) touches the sockets within the handler. NetworkUDPSocketHandler *socket; ///< The actual socket, which may be nullptr when not initialized yet. - std::atomic receive_iterations_locked; ///< The number of receive iterations the mutex was locked. - UDPSocket(const std::string &name_) : name(name_), socket(nullptr) {} + UDPSocket(const std::string &name) : name(name), socket(nullptr) {} void CloseSocket() { - std::lock_guard lock(mutex); - socket->CloseSocket(); - delete socket; - socket = nullptr; + this->socket->CloseSocket(); + delete this->socket; + this->socket = nullptr; } void ReceivePackets() { - std::unique_lock lock(mutex, std::defer_lock); - if (!lock.try_lock()) { - if (++receive_iterations_locked % 32 == 0) { - Debug(net, 0, "{} background UDP loop processing appears to be blocked. Your OS may be low on UDP send buffers.", name); - } - return; - } - - receive_iterations_locked.store(0); - socket->ReceivePackets(); + this->socket->ReceivePackets(); } }; static UDPSocket _udp_client("Client"); ///< udp client socket static UDPSocket _udp_server("Server"); ///< udp server socket -/** - * Helper function doing the actual work for querying the server. - * @param connection_string The address of the server. - * @param needs_mutex Whether we need to acquire locks when sending the packet or not. - * @param manually Whether the address was entered manually. - */ -static void DoNetworkUDPQueryServer(const std::string &connection_string, bool needs_mutex, bool manually) -{ - /* Clear item in gamelist */ - NetworkGameList *item = new NetworkGameList(connection_string, manually); - item->info.server_name = connection_string; - NetworkGameListAddItemDelayed(item); - - std::unique_lock lock(_udp_client.mutex, std::defer_lock); - if (needs_mutex) lock.lock(); - /* Init the packet */ - NetworkAddress address = NetworkAddress(ParseConnectionString(connection_string, NETWORK_DEFAULT_PORT)); - Packet p(PACKET_UDP_CLIENT_FIND_SERVER); - if (_udp_client.socket != nullptr) _udp_client.socket->SendPacket(&p, &address); -} - -/** - * Query a specific server. - * @param connection_string The address of the server. - * @param manually Whether the address was entered manually. - */ -void NetworkUDPQueryServer(const std::string &connection_string, bool manually) -{ - if (!StartNewThread(nullptr, "ottd:udp-query", &DoNetworkUDPQueryServer, std::move(connection_string), true, std::move(manually))) { - DoNetworkUDPQueryServer(connection_string, true, manually); - } -} - ///*** Communication with clients (we are server) ***/ /** Helper class for handling all server side communication. */ class ServerNetworkUDPSocketHandler : public NetworkUDPSocketHandler { protected: void Receive_CLIENT_FIND_SERVER(Packet *p, NetworkAddress *client_addr) override; - void Receive_CLIENT_DETAIL_INFO(Packet *p, NetworkAddress *client_addr) override; - void Receive_CLIENT_GET_NEWGRFS(Packet *p, NetworkAddress *client_addr) override; public: /** * Create the socket. @@ -124,307 +75,40 @@ public: void ServerNetworkUDPSocketHandler::Receive_CLIENT_FIND_SERVER(Packet *p, NetworkAddress *client_addr) { - /* Just a fail-safe.. should never happen */ - if (!_network_udp_server) { - return; - } - Packet packet(PACKET_UDP_SERVER_RESPONSE); - SerializeNetworkGameInfo(&packet, GetCurrentNetworkServerGameInfo()); - - /* Let the client know that we are here */ this->SendPacket(&packet, client_addr); Debug(net, 7, "Queried from {}", client_addr->GetHostname()); } -void ServerNetworkUDPSocketHandler::Receive_CLIENT_DETAIL_INFO(Packet *p, NetworkAddress *client_addr) -{ - /* Just a fail-safe.. should never happen */ - if (!_network_udp_server) return; - - Packet packet(PACKET_UDP_SERVER_DETAIL_INFO); - - /* Send the amount of active companies */ - packet.Send_uint8 (NETWORK_COMPANY_INFO_VERSION); - packet.Send_uint8 ((uint8)Company::GetNumItems()); - - /* Fetch the latest version of the stats */ - NetworkCompanyStats company_stats[MAX_COMPANIES]; - NetworkPopulateCompanyStats(company_stats); - - /* The minimum company information "blob" size. */ - static const uint MIN_CI_SIZE = 54; - uint max_cname_length = NETWORK_COMPANY_NAME_LENGTH; - - if (!packet.CanWriteToPacket(Company::GetNumItems() * (MIN_CI_SIZE + NETWORK_COMPANY_NAME_LENGTH))) { - /* Assume we can at least put the company information in the packets. */ - assert(packet.CanWriteToPacket(Company::GetNumItems() * MIN_CI_SIZE)); - - /* At this moment the company names might not fit in the - * packet. Check whether that is really the case. */ - - for (;;) { - size_t required = 0; - for (const Company *company : Company::Iterate()) { - char company_name[NETWORK_COMPANY_NAME_LENGTH]; - SetDParam(0, company->index); - GetString(company_name, STR_COMPANY_NAME, company_name + max_cname_length - 1); - required += MIN_CI_SIZE; - required += strlen(company_name); - } - if (packet.CanWriteToPacket(required)) break; - - /* Try again, with slightly shorter strings. */ - assert(max_cname_length > 0); - max_cname_length--; - } - } - - /* Go through all the companies */ - for (const Company *company : Company::Iterate()) { - /* Send the information */ - this->SendCompanyInformation(&packet, company, &company_stats[company->index], max_cname_length); - } - - this->SendPacket(&packet, client_addr); -} - -/** - * A client has requested the names of some NewGRFs. - * - * Replying this can be tricky as we have a limit of UDP_MTU bytes - * in the reply packet and we can send up to 100 bytes per NewGRF - * (GRF ID, MD5sum and NETWORK_GRF_NAME_LENGTH bytes for the name). - * As UDP_MTU is _much_ less than 100 * NETWORK_MAX_GRF_COUNT, it - * could be that a packet overflows. To stop this we only reply - * with the first N NewGRFs so that if the first N + 1 NewGRFs - * would be sent, the packet overflows. - * in_reply and in_reply_count are used to keep a list of GRFs to - * send in the reply. - */ -void ServerNetworkUDPSocketHandler::Receive_CLIENT_GET_NEWGRFS(Packet *p, NetworkAddress *client_addr) -{ - uint8 num_grfs; - uint i; - - const GRFConfig *in_reply[NETWORK_MAX_GRF_COUNT]; - uint8 in_reply_count = 0; - size_t packet_len = 0; - - Debug(net, 7, "NewGRF data request from {}", client_addr->GetAddressAsString()); - - num_grfs = p->Recv_uint8 (); - if (num_grfs > NETWORK_MAX_GRF_COUNT) return; - - for (i = 0; i < num_grfs; i++) { - GRFIdentifier c; - const GRFConfig *f; - - DeserializeGRFIdentifier(p, &c); - - /* Find the matching GRF file */ - f = FindGRFConfig(c.grfid, FGCM_EXACT, c.md5sum); - if (f == nullptr) continue; // The GRF is unknown to this server - - /* If the reply might exceed the size of the packet, only reply - * the current list and do not send the other data. - * The name could be an empty string, if so take the filename. */ - packet_len += sizeof(c.grfid) + sizeof(c.md5sum) + - std::min(strlen(f->GetName()) + 1, (size_t)NETWORK_GRF_NAME_LENGTH); - if (packet_len > UDP_MTU - 4) { // 4 is 3 byte header + grf count in reply - break; - } - in_reply[in_reply_count] = f; - in_reply_count++; - } - - if (in_reply_count == 0) return; - - Packet packet(PACKET_UDP_SERVER_NEWGRFS); - packet.Send_uint8(in_reply_count); - for (i = 0; i < in_reply_count; i++) { - char name[NETWORK_GRF_NAME_LENGTH]; - - /* The name could be an empty string, if so take the filename */ - strecpy(name, in_reply[i]->GetName(), lastof(name)); - SerializeGRFIdentifier(&packet, &in_reply[i]->ident); - packet.Send_string(name); - } - - this->SendPacket(&packet, client_addr); -} - ///*** Communication with servers (we are client) ***/ /** Helper class for handling all client side communication. */ class ClientNetworkUDPSocketHandler : public NetworkUDPSocketHandler { protected: void Receive_SERVER_RESPONSE(Packet *p, NetworkAddress *client_addr) override; - void Receive_MASTER_RESPONSE_LIST(Packet *p, NetworkAddress *client_addr) override; - void Receive_SERVER_NEWGRFS(Packet *p, NetworkAddress *client_addr) override; public: virtual ~ClientNetworkUDPSocketHandler() {} }; void ClientNetworkUDPSocketHandler::Receive_SERVER_RESPONSE(Packet *p, NetworkAddress *client_addr) { - NetworkGameList *item; - - /* Just a fail-safe.. should never happen */ - if (_network_udp_server) return; - Debug(net, 3, "Server response from {}", client_addr->GetAddressAsString()); - /* Find next item */ - item = NetworkGameListAddItem(client_addr->GetAddressAsString(false)); - - /* Clear any existing GRFConfig chain. */ - ClearGRFConfigList(&item->info.grfconfig); - /* Retrieve the NetworkGameInfo from the packet. */ - DeserializeNetworkGameInfo(p, &item->info); - /* Check for compatability with the client. */ - CheckGameCompatibility(item->info); - /* Ensure we consider the server online. */ - item->online = true; - /* Make sure this entry never expires. */ - item->version = INT32_MAX; - - { - /* Checks whether there needs to be a request for names of GRFs and makes - * the request if necessary. GRFs that need to be requested are the GRFs - * that do not exist on the clients system and we do not have the name - * resolved of, i.e. the name is still UNKNOWN_GRF_NAME_PLACEHOLDER. - * The in_request array and in_request_count are used so there is no need - * to do a second loop over the GRF list, which can be relatively expensive - * due to the string comparisons. */ - const GRFConfig *in_request[NETWORK_MAX_GRF_COUNT]; - const GRFConfig *c; - uint in_request_count = 0; - - for (c = item->info.grfconfig; c != nullptr; c = c->next) { - if (c->status != GCS_NOT_FOUND || strcmp(c->GetName(), UNKNOWN_GRF_NAME_PLACEHOLDER) != 0) continue; - in_request[in_request_count] = c; - in_request_count++; - } - - if (in_request_count > 0) { - /* There are 'unknown' GRFs, now send a request for them */ - uint i; - Packet packet(PACKET_UDP_CLIENT_GET_NEWGRFS); - - packet.Send_uint8(in_request_count); - for (i = 0; i < in_request_count; i++) { - SerializeGRFIdentifier(&packet, &in_request[i]->ident); - } - - NetworkAddress address = NetworkAddress(ParseConnectionString(item->connection_string, NETWORK_DEFAULT_PORT)); - this->SendPacket(&packet, &address); - } - } - - if (client_addr->GetAddress()->ss_family == AF_INET6) { - item->info.server_name.append(" (IPv6)"); - } - - UpdateNetworkGameWindow(); -} - -void ClientNetworkUDPSocketHandler::Receive_MASTER_RESPONSE_LIST(Packet *p, NetworkAddress *client_addr) -{ - /* packet begins with the protocol version (uint8) - * then an uint16 which indicates how many - * ip:port pairs are in this packet, after that - * an uint32 (ip) and an uint16 (port) for each pair. - */ - - ServerListType type = (ServerListType)(p->Recv_uint8() - 1); - - if (type < SLT_END) { - for (int i = p->Recv_uint16(); i != 0 ; i--) { - sockaddr_storage addr_storage; - memset(&addr_storage, 0, sizeof(addr_storage)); - - if (type == SLT_IPv4) { - addr_storage.ss_family = AF_INET; - ((sockaddr_in*)&addr_storage)->sin_addr.s_addr = TO_LE32(p->Recv_uint32()); - } else { - assert(type == SLT_IPv6); - addr_storage.ss_family = AF_INET6; - byte *addr = (byte*)&((sockaddr_in6*)&addr_storage)->sin6_addr; - for (uint i = 0; i < sizeof(in6_addr); i++) *addr++ = p->Recv_uint8(); - } - NetworkAddress addr(addr_storage, type == SLT_IPv4 ? sizeof(sockaddr_in) : sizeof(sockaddr_in6)); - addr.SetPort(p->Recv_uint16()); - - /* Somehow we reached the end of the packet */ - if (this->HasClientQuit()) return; - - DoNetworkUDPQueryServer(addr.GetAddressAsString(false), false, false); - } - } -} - -/** The return of the client's request of the names of some NewGRFs */ -void ClientNetworkUDPSocketHandler::Receive_SERVER_NEWGRFS(Packet *p, NetworkAddress *client_addr) -{ - uint8 num_grfs; - uint i; - - Debug(net, 7, "NewGRF data reply from {}", client_addr->GetAddressAsString()); - - num_grfs = p->Recv_uint8 (); - if (num_grfs > NETWORK_MAX_GRF_COUNT) return; - - for (i = 0; i < num_grfs; i++) { - GRFIdentifier c; - - DeserializeGRFIdentifier(p, &c); - std::string name = p->Recv_string(NETWORK_GRF_NAME_LENGTH); - - /* An empty name is not possible under normal circumstances - * and causes problems when showing the NewGRF list. */ - if (name.empty()) continue; - - /* Try to find the GRFTextWrapper for the name of this GRF ID and MD5sum tuple. - * If it exists and not resolved yet, then name of the fake GRF is - * overwritten with the name from the reply. */ - GRFTextWrapper unknown_name = FindUnknownGRFName(c.grfid, c.md5sum, false); - if (unknown_name && strcmp(GetGRFStringFromGRFText(unknown_name), UNKNOWN_GRF_NAME_PLACEHOLDER) == 0) { - AddGRFTextToList(unknown_name, name); - } - } + NetworkAddServer(client_addr->GetAddressAsString(false), false, true); } /** Broadcast to all ips */ static void NetworkUDPBroadCast(NetworkUDPSocketHandler *socket) { for (NetworkAddress &addr : _broadcast_list) { - Packet p(PACKET_UDP_CLIENT_FIND_SERVER); - Debug(net, 5, "Broadcasting to {}", addr.GetHostname()); + Packet p(PACKET_UDP_CLIENT_FIND_SERVER); socket->SendPacket(&p, &addr, true, true); } } - -/** Request the the server-list from the master server */ -void NetworkUDPQueryMasterServer() -{ - Packet p(PACKET_UDP_CLIENT_GET_LIST); - NetworkAddress out_addr(NETWORK_MASTER_SERVER_HOST, NETWORK_MASTER_SERVER_PORT); - - /* packet only contains protocol version */ - p.Send_uint8(NETWORK_MASTER_SERVER_VERSION); - p.Send_uint8(SLT_AUTODETECT); - - std::lock_guard lock(_udp_client.mutex); - _udp_client.socket->SendPacket(&p, &out_addr, true); - - Debug(net, 6, "Master server queried at {}", out_addr.GetAddressAsString()); -} - /** Find all servers */ void NetworkUDPSearchGame() { @@ -446,8 +130,6 @@ void NetworkUDPInitialize() Debug(net, 3, "Initializing UDP listeners"); assert(_udp_client.socket == nullptr && _udp_server.socket == nullptr); - std::scoped_lock lock(_udp_client.mutex, _udp_server.mutex); - _udp_client.socket = new ClientNetworkUDPSocketHandler(); NetworkAddressList server; @@ -461,7 +143,6 @@ void NetworkUDPInitialize() /** Start the listening of the UDP server component. */ void NetworkUDPServerListen() { - std::lock_guard lock(_udp_server.mutex); _network_udp_server = _udp_server.socket->Listen(); } diff --git a/src/network/network_udp.h b/src/network/network_udp.h index 735e5f2230..b640da018d 100644 --- a/src/network/network_udp.h +++ b/src/network/network_udp.h @@ -14,8 +14,6 @@ void NetworkUDPInitialize(); void NetworkUDPSearchGame(); -void NetworkUDPQueryMasterServer(); -void NetworkUDPQueryServer(const std::string &connection_string, bool manually = false); void NetworkUDPClose(); void NetworkUDPServerListen(); void NetworkBackgroundUDPLoop(); diff --git a/src/settings_type.h b/src/settings_type.h index 05efc11e55..3377bdede4 100644 --- a/src/settings_type.h +++ b/src/settings_type.h @@ -270,7 +270,7 @@ struct NetworkSettings { std::string server_password; ///< password for joining this server std::string rcon_password; ///< password for rconsole (server side) std::string admin_password; ///< password for the admin network - bool server_advertise; ///< advertise the server to the masterserver + bool server_advertise; ///< Advertise the server to the game coordinator. std::string client_name; ///< name of the player (as client) std::string default_company_pass; ///< default password for new companies in encrypted form std::string connect_to_ip; ///< default for the "Add server" query From 6212d0a8da81f0f348bf4dcb03e332b3ef661a60 Mon Sep 17 00:00:00 2001 From: Patric Stout Date: Thu, 6 May 2021 13:12:11 +0200 Subject: [PATCH 17/44] Remove: unused UDP protocol parts --- src/network/core/udp.cpp | 20 ------ src/network/core/udp.h | 136 +-------------------------------------- 2 files changed, 1 insertion(+), 155 deletions(-) diff --git a/src/network/core/udp.cpp b/src/network/core/udp.cpp index 7a3fc19f35..b4fcec5b84 100644 --- a/src/network/core/udp.cpp +++ b/src/network/core/udp.cpp @@ -162,16 +162,6 @@ void NetworkUDPSocketHandler::HandleUDPPacket(Packet *p, NetworkAddress *client_ switch (this->HasClientQuit() ? PACKET_UDP_END : type) { case PACKET_UDP_CLIENT_FIND_SERVER: this->Receive_CLIENT_FIND_SERVER(p, client_addr); break; case PACKET_UDP_SERVER_RESPONSE: this->Receive_SERVER_RESPONSE(p, client_addr); break; - case PACKET_UDP_CLIENT_DETAIL_INFO: this->Receive_CLIENT_DETAIL_INFO(p, client_addr); break; - case PACKET_UDP_SERVER_DETAIL_INFO: this->Receive_SERVER_DETAIL_INFO(p, client_addr); break; - case PACKET_UDP_SERVER_REGISTER: this->Receive_SERVER_REGISTER(p, client_addr); break; - case PACKET_UDP_MASTER_ACK_REGISTER: this->Receive_MASTER_ACK_REGISTER(p, client_addr); break; - case PACKET_UDP_CLIENT_GET_LIST: this->Receive_CLIENT_GET_LIST(p, client_addr); break; - case PACKET_UDP_MASTER_RESPONSE_LIST: this->Receive_MASTER_RESPONSE_LIST(p, client_addr); break; - case PACKET_UDP_SERVER_UNREGISTER: this->Receive_SERVER_UNREGISTER(p, client_addr); break; - case PACKET_UDP_CLIENT_GET_NEWGRFS: this->Receive_CLIENT_GET_NEWGRFS(p, client_addr); break; - case PACKET_UDP_SERVER_NEWGRFS: this->Receive_SERVER_NEWGRFS(p, client_addr); break; - case PACKET_UDP_MASTER_SESSION_KEY: this->Receive_MASTER_SESSION_KEY(p, client_addr); break; default: if (this->HasClientQuit()) { @@ -195,13 +185,3 @@ void NetworkUDPSocketHandler::ReceiveInvalidPacket(PacketUDPType type, NetworkAd void NetworkUDPSocketHandler::Receive_CLIENT_FIND_SERVER(Packet *p, NetworkAddress *client_addr) { this->ReceiveInvalidPacket(PACKET_UDP_CLIENT_FIND_SERVER, client_addr); } void NetworkUDPSocketHandler::Receive_SERVER_RESPONSE(Packet *p, NetworkAddress *client_addr) { this->ReceiveInvalidPacket(PACKET_UDP_SERVER_RESPONSE, client_addr); } -void NetworkUDPSocketHandler::Receive_CLIENT_DETAIL_INFO(Packet *p, NetworkAddress *client_addr) { this->ReceiveInvalidPacket(PACKET_UDP_CLIENT_DETAIL_INFO, client_addr); } -void NetworkUDPSocketHandler::Receive_SERVER_DETAIL_INFO(Packet *p, NetworkAddress *client_addr) { this->ReceiveInvalidPacket(PACKET_UDP_SERVER_DETAIL_INFO, client_addr); } -void NetworkUDPSocketHandler::Receive_SERVER_REGISTER(Packet *p, NetworkAddress *client_addr) { this->ReceiveInvalidPacket(PACKET_UDP_SERVER_REGISTER, client_addr); } -void NetworkUDPSocketHandler::Receive_MASTER_ACK_REGISTER(Packet *p, NetworkAddress *client_addr) { this->ReceiveInvalidPacket(PACKET_UDP_MASTER_ACK_REGISTER, client_addr); } -void NetworkUDPSocketHandler::Receive_CLIENT_GET_LIST(Packet *p, NetworkAddress *client_addr) { this->ReceiveInvalidPacket(PACKET_UDP_CLIENT_GET_LIST, client_addr); } -void NetworkUDPSocketHandler::Receive_MASTER_RESPONSE_LIST(Packet *p, NetworkAddress *client_addr) { this->ReceiveInvalidPacket(PACKET_UDP_MASTER_RESPONSE_LIST, client_addr); } -void NetworkUDPSocketHandler::Receive_SERVER_UNREGISTER(Packet *p, NetworkAddress *client_addr) { this->ReceiveInvalidPacket(PACKET_UDP_SERVER_UNREGISTER, client_addr); } -void NetworkUDPSocketHandler::Receive_CLIENT_GET_NEWGRFS(Packet *p, NetworkAddress *client_addr) { this->ReceiveInvalidPacket(PACKET_UDP_CLIENT_GET_NEWGRFS, client_addr); } -void NetworkUDPSocketHandler::Receive_SERVER_NEWGRFS(Packet *p, NetworkAddress *client_addr) { this->ReceiveInvalidPacket(PACKET_UDP_SERVER_NEWGRFS, client_addr); } -void NetworkUDPSocketHandler::Receive_MASTER_SESSION_KEY(Packet *p, NetworkAddress *client_addr) { this->ReceiveInvalidPacket(PACKET_UDP_MASTER_SESSION_KEY, client_addr); } diff --git a/src/network/core/udp.h b/src/network/core/udp.h index 489e219856..61866351d8 100644 --- a/src/network/core/udp.h +++ b/src/network/core/udp.h @@ -19,28 +19,9 @@ enum PacketUDPType { PACKET_UDP_CLIENT_FIND_SERVER, ///< Queries a game server for game information PACKET_UDP_SERVER_RESPONSE, ///< Reply of the game server with game information - PACKET_UDP_CLIENT_DETAIL_INFO, ///< Queries a game server about details of the game, such as companies - PACKET_UDP_SERVER_DETAIL_INFO, ///< Reply of the game server about details of the game, such as companies - PACKET_UDP_SERVER_REGISTER, ///< Packet to register itself to the master server - PACKET_UDP_MASTER_ACK_REGISTER, ///< Packet indicating registration has succeeded - PACKET_UDP_CLIENT_GET_LIST, ///< Request for serverlist from master server - PACKET_UDP_MASTER_RESPONSE_LIST, ///< Response from master server with server ip's + port's - PACKET_UDP_SERVER_UNREGISTER, ///< Request to be removed from the server-list - PACKET_UDP_CLIENT_GET_NEWGRFS, ///< Requests the name for a list of GRFs (GRF_ID and MD5) - PACKET_UDP_SERVER_NEWGRFS, ///< Sends the list of NewGRF's requested. - PACKET_UDP_MASTER_SESSION_KEY, ///< Sends a fresh session key to the client PACKET_UDP_END, ///< Must ALWAYS be on the end of this list!! (period) }; -/** The types of server lists we can get */ -enum ServerListType { - SLT_IPv4 = 0, ///< Get the IPv4 addresses - SLT_IPv6 = 1, ///< Get the IPv6 addresses - SLT_AUTODETECT, ///< Autodetect the type based on the connection - - SLT_END = SLT_AUTODETECT, ///< End of 'arrays' marker -}; - /** Base socket handler for all UDP sockets */ class NetworkUDPSocketHandler : public NetworkSocketHandler { protected: @@ -59,127 +40,12 @@ protected: virtual void Receive_CLIENT_FIND_SERVER(Packet *p, NetworkAddress *client_addr); /** - * Return of server information to the client. - * Serialized NetworkGameInfo. See game_info.h for details. + * Response to a query letting the client know we are here. * @param p The received packet. * @param client_addr The origin of the packet. */ virtual void Receive_SERVER_RESPONSE(Packet *p, NetworkAddress *client_addr); - /** - * Query for detailed information about companies. - * @param p The received packet. - * @param client_addr The origin of the packet. - */ - virtual void Receive_CLIENT_DETAIL_INFO(Packet *p, NetworkAddress *client_addr); - - /** - * Reply with detailed company information. - * uint8 Version of the packet. - * uint8 Number of companies. - * For each company: - * uint8 ID of the company. - * string Name of the company. - * uint32 Year the company was inaugurated. - * uint64 Value. - * uint64 Money. - * uint64 Income. - * uint16 Performance (last quarter). - * bool Company is password protected. - * uint16 Number of trains. - * uint16 Number of lorries. - * uint16 Number of busses. - * uint16 Number of planes. - * uint16 Number of ships. - * uint16 Number of train stations. - * uint16 Number of lorry stations. - * uint16 Number of bus stops. - * uint16 Number of airports and heliports. - * uint16 Number of harbours. - * bool Company is an AI. - * @param p The received packet. - * @param client_addr The origin of the packet. - */ - virtual void Receive_SERVER_DETAIL_INFO(Packet *p, NetworkAddress *client_addr); - - /** - * Registers the server to the master server. - * string The "welcome" message to root out other binary packets. - * uint8 Version of the protocol. - * uint16 The port to unregister. - * uint64 The session key. - * @param p The received packet. - * @param client_addr The origin of the packet. - */ - virtual void Receive_SERVER_REGISTER(Packet *p, NetworkAddress *client_addr); - - /** - * The master server acknowledges the registration. - * @param p The received packet. - * @param client_addr The origin of the packet. - */ - virtual void Receive_MASTER_ACK_REGISTER(Packet *p, NetworkAddress *client_addr); - - /** - * The client requests a list of servers. - * uint8 The protocol version. - * uint8 The type of server to look for: IPv4, IPv6 or based on the received packet. - * @param p The received packet. - * @param client_addr The origin of the packet. - */ - virtual void Receive_CLIENT_GET_LIST(Packet *p, NetworkAddress *client_addr); - - /** - * The server sends a list of servers. - * uint8 The protocol version. - * For each server: - * 4 or 16 bytes of IPv4 or IPv6 address. - * uint8 The port. - * @param p The received packet. - * @param client_addr The origin of the packet. - */ - virtual void Receive_MASTER_RESPONSE_LIST(Packet *p, NetworkAddress *client_addr); - - /** - * A server unregisters itself at the master server. - * uint8 Version of the protocol. - * uint16 The port to unregister. - * @param p The received packet. - * @param client_addr The origin of the packet. - */ - virtual void Receive_SERVER_UNREGISTER(Packet *p, NetworkAddress *client_addr); - - /** - * The client requests information about some NewGRFs. - * uint8 The number of NewGRFs information is requested about. - * For each NewGRF: - * uint32 The GRFID. - * 16 * uint8 MD5 checksum of the GRF. - * @param p The received packet. - * @param client_addr The origin of the packet. - */ - virtual void Receive_CLIENT_GET_NEWGRFS(Packet *p, NetworkAddress *client_addr); - - /** - * The server returns information about some NewGRFs. - * uint8 The number of NewGRFs information is requested about. - * For each NewGRF: - * uint32 The GRFID. - * 16 * uint8 MD5 checksum of the GRF. - * string The name of the NewGRF. - * @param p The received packet. - * @param client_addr The origin of the packet. - */ - virtual void Receive_SERVER_NEWGRFS(Packet *p, NetworkAddress *client_addr); - - /** - * The master server sends us a session key. - * uint64 The session key. - * @param p The received packet. - * @param client_addr The origin of the packet. - */ - virtual void Receive_MASTER_SESSION_KEY(Packet *p, NetworkAddress *client_addr); - void HandleUDPPacket(Packet *p, NetworkAddress *client_addr); public: NetworkUDPSocketHandler(NetworkAddressList *bind = nullptr); From de7ab2eb1c8bd5695a3ffa4390b9fd46d34d9d71 Mon Sep 17 00:00:00 2001 From: Patric Stout Date: Sat, 8 May 2021 10:57:10 +0200 Subject: [PATCH 18/44] Cleanup: remove unused optional ctor parameter for NetworkGameList --- src/network/network_gamelist.h | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/network/network_gamelist.h b/src/network/network_gamelist.h index 7fdd37c669..9a98207d96 100644 --- a/src/network/network_gamelist.h +++ b/src/network/network_gamelist.h @@ -16,10 +16,7 @@ /** Structure with information shown in the game list (GUI) */ struct NetworkGameList { - NetworkGameList(const std::string &connection_string, bool manually = false) : - connection_string(connection_string), manually(manually) - { - } + NetworkGameList(const std::string &connection_string) : connection_string(connection_string) {} NetworkGameInfo info = {}; ///< The game information of this server std::string connection_string; ///< Address of the server From d158eba72c89ae580c55a8421d470528de7637f5 Mon Sep 17 00:00:00 2001 From: translators Date: Sat, 10 Jul 2021 18:49:56 +0000 Subject: [PATCH 19/44] Update: Translations from eints spanish (mexican): 74 changes by absay vietnamese: 62 changes by KhoiCanDev --- src/lang/afrikaans.txt | 4 + src/lang/arabic_egypt.txt | 4 + src/lang/basque.txt | 4 + src/lang/belarusian.txt | 4 + src/lang/brazilian_portuguese.txt | 5 +- src/lang/bulgarian.txt | 4 + src/lang/catalan.txt | 5 +- src/lang/chuvash.txt | 4 + src/lang/croatian.txt | 4 + src/lang/czech.txt | 4 + src/lang/danish.txt | 4 + src/lang/dutch.txt | 5 +- src/lang/english_AU.txt | 4 + src/lang/english_US.txt | 5 +- src/lang/esperanto.txt | 4 + src/lang/estonian.txt | 5 +- src/lang/faroese.txt | 4 + src/lang/finnish.txt | 5 +- src/lang/french.txt | 5 +- src/lang/frisian.txt | 4 + src/lang/gaelic.txt | 4 + src/lang/galician.txt | 4 + src/lang/german.txt | 5 +- src/lang/greek.txt | 4 + src/lang/hebrew.txt | 4 + src/lang/hindi.txt | 4 + src/lang/hungarian.txt | 5 +- src/lang/icelandic.txt | 4 + src/lang/ido.txt | 4 + src/lang/indonesian.txt | 5 +- src/lang/irish.txt | 4 + src/lang/italian.txt | 4 + src/lang/japanese.txt | 5 +- src/lang/korean.txt | 5 +- src/lang/latin.txt | 4 + src/lang/latvian.txt | 4 + src/lang/lithuanian.txt | 4 + src/lang/luxembourgish.txt | 4 + src/lang/macedonian.txt | 4 + src/lang/malay.txt | 4 + src/lang/maltese.txt | 4 + src/lang/marathi.txt | 4 + src/lang/norwegian_bokmal.txt | 5 +- src/lang/norwegian_nynorsk.txt | 4 + src/lang/persian.txt | 4 + src/lang/polish.txt | 4 + src/lang/portuguese.txt | 5 +- src/lang/romanian.txt | 5 +- src/lang/russian.txt | 5 +- src/lang/serbian.txt | 4 + src/lang/simplified_chinese.txt | 4 + src/lang/slovak.txt | 5 +- src/lang/slovenian.txt | 4 + src/lang/spanish.txt | 5 +- src/lang/spanish_MX.txt | 155 +++++++++++++++--------------- src/lang/swedish.txt | 5 +- src/lang/tamil.txt | 4 + src/lang/thai.txt | 4 + src/lang/traditional_chinese.txt | 4 + src/lang/turkish.txt | 5 +- src/lang/ukrainian.txt | 4 + src/lang/urdu.txt | 4 + src/lang/vietnamese.txt | 75 +++++++++++++-- src/lang/welsh.txt | 4 + 64 files changed, 394 insertions(+), 104 deletions(-) diff --git a/src/lang/afrikaans.txt b/src/lang/afrikaans.txt index cb3052a1bd..a6f380952c 100644 --- a/src/lang/afrikaans.txt +++ b/src/lang/afrikaans.txt @@ -2036,6 +2036,9 @@ STR_NETWORK_COMPANY_LIST_CLIENT_LIST :Klient Lys # Network client list +############ Begin of ConnectionType +############ End of ConnectionType + STR_NETWORK_SERVER :Verskaffer @@ -2146,6 +2149,7 @@ STR_NETWORK_MESSAGE_SERVER_SHUTDOWN :{WHITE}Die vers STR_NETWORK_MESSAGE_SERVER_REBOOT :{WHITE}Die verskaffer is besig om te weerbegin...{}Wag asb... STR_NETWORK_MESSAGE_KICKED :*** {STRING} is geskop. Rede: ({STRING}) + # Content downloading window STR_CONTENT_TITLE :{WHITE}Laai Inhoud af STR_CONTENT_TYPE_CAPTION :{BLACK}Tipe diff --git a/src/lang/arabic_egypt.txt b/src/lang/arabic_egypt.txt index ea1634a0a9..d7de2f4fca 100644 --- a/src/lang/arabic_egypt.txt +++ b/src/lang/arabic_egypt.txt @@ -1732,6 +1732,9 @@ STR_NETWORK_COMPANY_LIST_CLIENT_LIST :قائمة ال # Network client list +############ Begin of ConnectionType +############ End of ConnectionType + STR_NETWORK_SERVER :خادم @@ -1830,6 +1833,7 @@ STR_NETWORK_MESSAGE_NAME_CHANGE :*** {STRING} ق STR_NETWORK_MESSAGE_SERVER_SHUTDOWN :{WHITE}اقفل الخادم الجلسة STR_NETWORK_MESSAGE_SERVER_REBOOT :{WHITE}يتم بدأ الخادم من جديد ...{} الرجاء الأنتظار + # Content downloading window STR_CONTENT_TITLE :{WHITE}تنزيل المحتوى STR_CONTENT_TYPE_CAPTION :{BLACK} نوع diff --git a/src/lang/basque.txt b/src/lang/basque.txt index fc3e35f1a3..b20ad45af2 100644 --- a/src/lang/basque.txt +++ b/src/lang/basque.txt @@ -1911,6 +1911,9 @@ STR_NETWORK_COMPANY_LIST_CLIENT_LIST :Bezero zerrenda # Network client list +############ Begin of ConnectionType +############ End of ConnectionType + STR_NETWORK_SERVER :Zerbitzaria @@ -2019,6 +2022,7 @@ STR_NETWORK_MESSAGE_NAME_CHANGE :*** {STRING}-k STR_NETWORK_MESSAGE_SERVER_SHUTDOWN :{WHITE}Zebitzariak saioa itxi du STR_NETWORK_MESSAGE_SERVER_REBOOT :{WHITE}Zerbitzaria berriro hasten ari da...{}Mesedez itxaron... + # Content downloading window STR_CONTENT_TITLE :{WHITE}Edukiak deskargatzen STR_CONTENT_TYPE_CAPTION :{BLACK}Mota diff --git a/src/lang/belarusian.txt b/src/lang/belarusian.txt index 8ec4fbd9cd..3dc1e67713 100644 --- a/src/lang/belarusian.txt +++ b/src/lang/belarusian.txt @@ -2346,6 +2346,9 @@ STR_NETWORK_COMPANY_LIST_CLIENT_LIST :Сьпіс кл # Network client list +############ Begin of ConnectionType +############ End of ConnectionType + STR_NETWORK_SERVER :Сэрвэр @@ -2455,6 +2458,7 @@ STR_NETWORK_MESSAGE_NAME_CHANGE :*** {STRING} з STR_NETWORK_MESSAGE_SERVER_SHUTDOWN :{WHITE}Гэты сэрвэр закрыў сэсію STR_NETWORK_MESSAGE_SERVER_REBOOT :{WHITE}Гэты сэрвэр перазапускаецца...{}Пачакайце, калі ласка + # Content downloading window STR_CONTENT_TITLE :{WHITE}Кантэнт запампоўваецца STR_CONTENT_TYPE_CAPTION :{BLACK}Тып diff --git a/src/lang/brazilian_portuguese.txt b/src/lang/brazilian_portuguese.txt index 74f9276fb1..cd17b9ff62 100644 --- a/src/lang/brazilian_portuguese.txt +++ b/src/lang/brazilian_portuguese.txt @@ -1996,7 +1996,6 @@ 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 @@ -2155,6 +2154,9 @@ STR_NETWORK_CLIENT_LIST_PLAYER_ICON_SELF_TOOLTIP :{BLACK}Esse é STR_NETWORK_CLIENT_LIST_PLAYER_ICON_HOST_TOOLTIP :{BLACK}Esse é o hospedeiro do jogo STR_NETWORK_CLIENT_LIST_CLIENT_COMPANY_COUNT :{BLACK}{NUM} cliente{P "" s} / {NUM} empresa{P "" s} +############ Begin of ConnectionType +############ End of ConnectionType + STR_NETWORK_CLIENT_LIST_ADMIN_CLIENT_KICK :Expulsar STR_NETWORK_CLIENT_LIST_ADMIN_CLIENT_BAN :Banir STR_NETWORK_CLIENT_LIST_ADMIN_COMPANY_RESET :Excluir @@ -2282,6 +2284,7 @@ STR_NETWORK_MESSAGE_SERVER_SHUTDOWN :{WHITE}O servid STR_NETWORK_MESSAGE_SERVER_REBOOT :{WHITE}O servidor está reiniciando...{}Aguarde... STR_NETWORK_MESSAGE_KICKED :*** {STRING} foi kickado. Motivo: ({STRING}) + # Content downloading window STR_CONTENT_TITLE :{WHITE}Baixando conteúdo STR_CONTENT_TYPE_CAPTION :{BLACK}Tipo diff --git a/src/lang/bulgarian.txt b/src/lang/bulgarian.txt index 1fad584e6b..b412514a70 100644 --- a/src/lang/bulgarian.txt +++ b/src/lang/bulgarian.txt @@ -1957,6 +1957,9 @@ STR_NETWORK_COMPANY_LIST_CLIENT_LIST :Списък с # Network client list +############ Begin of ConnectionType +############ End of ConnectionType + STR_NETWORK_SERVER :Сървър @@ -2065,6 +2068,7 @@ STR_NETWORK_MESSAGE_NAME_CHANGE :*** {STRING} с STR_NETWORK_MESSAGE_SERVER_SHUTDOWN :{WHITE}Сървърът прекъсна сесията STR_NETWORK_MESSAGE_SERVER_REBOOT :{WHITE}Сървърът се рестартира...{}Моля изчакайте... + # Content downloading window STR_CONTENT_TITLE :{WHITE}Съдържание за сваляне STR_CONTENT_TYPE_CAPTION :{BLACK}Вид diff --git a/src/lang/catalan.txt b/src/lang/catalan.txt index 8e2e5b8e4a..0e7fa183be 100644 --- a/src/lang/catalan.txt +++ b/src/lang/catalan.txt @@ -1996,7 +1996,6 @@ STR_FACE_TIE :Corbata: STR_FACE_EARRING :Arracades: STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}Canvia la corbata o les arracades -STR_NETWORK_SERVER_VISIBILITY_PRIVATE :Privada STR_NETWORK_SERVER_VISIBILITY_PUBLIC :Pública # Network server list @@ -2155,6 +2154,9 @@ STR_NETWORK_CLIENT_LIST_PLAYER_ICON_SELF_TOOLTIP :{BLACK}Aquest e STR_NETWORK_CLIENT_LIST_PLAYER_ICON_HOST_TOOLTIP :{BLACK}Aquest és l'hoste de la partida. STR_NETWORK_CLIENT_LIST_CLIENT_COMPANY_COUNT :{BLACK}{NUM} client{P "" s} / {NUM} companyi{P a es} +############ Begin of ConnectionType +############ End of ConnectionType + STR_NETWORK_CLIENT_LIST_ADMIN_CLIENT_KICK :Treu STR_NETWORK_CLIENT_LIST_ADMIN_CLIENT_BAN :Expulsa STR_NETWORK_CLIENT_LIST_ADMIN_COMPANY_RESET :Esborra @@ -2282,6 +2284,7 @@ STR_NETWORK_MESSAGE_SERVER_SHUTDOWN :{WHITE}El servi STR_NETWORK_MESSAGE_SERVER_REBOOT :{WHITE}El servidor està reiniciant...{}Espera un moment... STR_NETWORK_MESSAGE_KICKED :*** S'ha expulsat {STRING}. Motiu: {STRING} + # Content downloading window STR_CONTENT_TITLE :{WHITE}Descàrregues de contingut STR_CONTENT_TYPE_CAPTION :{BLACK}Tipus diff --git a/src/lang/chuvash.txt b/src/lang/chuvash.txt index 37567cefc0..619dd1c3d7 100644 --- a/src/lang/chuvash.txt +++ b/src/lang/chuvash.txt @@ -788,6 +788,9 @@ STR_NETWORK_START_SERVER_SET_PASSWORD :{BLACK}Вырн # Network client list +############ Begin of ConnectionType +############ End of ConnectionType + @@ -809,6 +812,7 @@ STR_NETWORK_SERVER_MESSAGE :*** {1:STRING} ############ Leave those lines in this order!! ############ End of leave-in-this-order + # Content downloading window STR_CONTENT_DETAIL_VERSION :{SILVER}Верси: {WHITE}{STRING} diff --git a/src/lang/croatian.txt b/src/lang/croatian.txt index 0e9dec5382..976524da15 100644 --- a/src/lang/croatian.txt +++ b/src/lang/croatian.txt @@ -2141,6 +2141,9 @@ STR_NETWORK_COMPANY_LIST_CLIENT_LIST :Popis klijenata # Network client list +############ Begin of ConnectionType +############ End of ConnectionType + STR_NETWORK_SERVER :Poslužitelj @@ -2251,6 +2254,7 @@ STR_NETWORK_MESSAGE_SERVER_SHUTDOWN :{WHITE}Posluži STR_NETWORK_MESSAGE_SERVER_REBOOT :{WHITE}Poslužitelj se ponovno pokreće...{}Molimo pričekajte... STR_NETWORK_MESSAGE_KICKED :*** {STRING} je izbačen. Razlog: ({STRING}) + # Content downloading window STR_CONTENT_TITLE :{WHITE}Preuzimanje sadržaja STR_CONTENT_TYPE_CAPTION :{BLACK}Vrsta diff --git a/src/lang/czech.txt b/src/lang/czech.txt index f88eb3f0ba..83ce721c3e 100644 --- a/src/lang/czech.txt +++ b/src/lang/czech.txt @@ -2197,6 +2197,9 @@ STR_NETWORK_COMPANY_LIST_CLIENT_LIST :Seznam hráčů # Network client list +############ Begin of ConnectionType +############ End of ConnectionType + STR_NETWORK_SERVER :Server @@ -2310,6 +2313,7 @@ STR_NETWORK_MESSAGE_SERVER_SHUTDOWN :{WHITE}Server u STR_NETWORK_MESSAGE_SERVER_REBOOT :{WHITE}Server se restartuje...{}Počkejte prosím... STR_NETWORK_MESSAGE_KICKED :*** {STRING} byl vyhozen. Důvod: ({STRING}) + # Content downloading window STR_CONTENT_TITLE :{WHITE}Součásti ke stažení STR_CONTENT_TYPE_CAPTION :{BLACK}Druh diff --git a/src/lang/danish.txt b/src/lang/danish.txt index a3cad4ff5a..6965752686 100644 --- a/src/lang/danish.txt +++ b/src/lang/danish.txt @@ -2050,6 +2050,9 @@ STR_NETWORK_COMPANY_LIST_CLIENT_LIST :Klient liste # Network client list +############ Begin of ConnectionType +############ End of ConnectionType + STR_NETWORK_SERVER :Server @@ -2160,6 +2163,7 @@ STR_NETWORK_MESSAGE_SERVER_SHUTDOWN :{WHITE}Serveren STR_NETWORK_MESSAGE_SERVER_REBOOT :{WHITE}Serveren genstarter...{}Vent venligst... STR_NETWORK_MESSAGE_KICKED :*** {STRING} blev sparket ud. Grund: ({STRING}) + # Content downloading window STR_CONTENT_TITLE :{WHITE}Download af indhold STR_CONTENT_TYPE_CAPTION :{BLACK}Type diff --git a/src/lang/dutch.txt b/src/lang/dutch.txt index 2b7d3068df..cdf7ca322b 100644 --- a/src/lang/dutch.txt +++ b/src/lang/dutch.txt @@ -1995,7 +1995,6 @@ 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 @@ -2154,6 +2153,9 @@ STR_NETWORK_CLIENT_LIST_PLAYER_ICON_SELF_TOOLTIP :{BLACK}Dit ben STR_NETWORK_CLIENT_LIST_PLAYER_ICON_HOST_TOOLTIP :{BLACK}Dit is de host van het spel STR_NETWORK_CLIENT_LIST_CLIENT_COMPANY_COUNT :{BLACK}{NUM} klant{P "" en} / {NUM} bedrij{P f ven} +############ Begin of ConnectionType +############ End of ConnectionType + 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 @@ -2281,6 +2283,7 @@ STR_NETWORK_MESSAGE_SERVER_SHUTDOWN :{WHITE}De serve STR_NETWORK_MESSAGE_SERVER_REBOOT :{WHITE}De server wordt opnieuw gestart...{}Wacht alstublieft... STR_NETWORK_MESSAGE_KICKED :*** {STRING} is eruit geschopt. Reden: ({STRING}) + # Content downloading window STR_CONTENT_TITLE :{WHITE}Download extra inhoud STR_CONTENT_TYPE_CAPTION :{BLACK}Type diff --git a/src/lang/english_AU.txt b/src/lang/english_AU.txt index e2faa0e819..a368826769 100644 --- a/src/lang/english_AU.txt +++ b/src/lang/english_AU.txt @@ -1967,6 +1967,9 @@ STR_NETWORK_COMPANY_LIST_CLIENT_LIST :Client list # Network client list +############ Begin of ConnectionType +############ End of ConnectionType + STR_NETWORK_SERVER :Server @@ -2075,6 +2078,7 @@ STR_NETWORK_MESSAGE_NAME_CHANGE :*** {STRING} ha STR_NETWORK_MESSAGE_SERVER_SHUTDOWN :{WHITE}The server closed the session STR_NETWORK_MESSAGE_SERVER_REBOOT :{WHITE}The server is restarting...{}Please wait... + # Content downloading window STR_CONTENT_TITLE :{WHITE}Content downloading STR_CONTENT_TYPE_CAPTION :{BLACK}Type diff --git a/src/lang/english_US.txt b/src/lang/english_US.txt index 2833bdacee..df85e60de7 100644 --- a/src/lang/english_US.txt +++ b/src/lang/english_US.txt @@ -1995,7 +1995,6 @@ STR_FACE_TIE :Tie: STR_FACE_EARRING :Earring: STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}Change tie or earring -STR_NETWORK_SERVER_VISIBILITY_PRIVATE :Private STR_NETWORK_SERVER_VISIBILITY_PUBLIC :Public # Network server list @@ -2154,6 +2153,9 @@ STR_NETWORK_CLIENT_LIST_PLAYER_ICON_SELF_TOOLTIP :{BLACK}This is STR_NETWORK_CLIENT_LIST_PLAYER_ICON_HOST_TOOLTIP :{BLACK}This is the host of the game STR_NETWORK_CLIENT_LIST_CLIENT_COMPANY_COUNT :{BLACK}{NUM} client{P "" s} / {NUM} compan{P y ies} +############ Begin of ConnectionType +############ End of ConnectionType + STR_NETWORK_CLIENT_LIST_ADMIN_CLIENT_KICK :Kick STR_NETWORK_CLIENT_LIST_ADMIN_CLIENT_BAN :Ban STR_NETWORK_CLIENT_LIST_ADMIN_COMPANY_RESET :Delete @@ -2281,6 +2283,7 @@ STR_NETWORK_MESSAGE_SERVER_SHUTDOWN :{WHITE}The serv STR_NETWORK_MESSAGE_SERVER_REBOOT :{WHITE}The server is restarting...{}Please wait... STR_NETWORK_MESSAGE_KICKED :*** {STRING} was kicked. Reason: ({STRING}) + # Content downloading window STR_CONTENT_TITLE :{WHITE}Content downloading STR_CONTENT_TYPE_CAPTION :{BLACK}Type diff --git a/src/lang/esperanto.txt b/src/lang/esperanto.txt index 0505e59c3b..b7799fed27 100644 --- a/src/lang/esperanto.txt +++ b/src/lang/esperanto.txt @@ -1651,6 +1651,9 @@ STR_NETWORK_COMPANY_LIST_CLIENT_LIST :Klientlisto # Network client list +############ Begin of ConnectionType +############ End of ConnectionType + STR_NETWORK_SERVER :Servilo @@ -1747,6 +1750,7 @@ STR_NETWORK_MESSAGE_NAME_CHANGE :*** {STRING} ŝ STR_NETWORK_MESSAGE_SERVER_SHUTDOWN :{WHITE}La servilo fermis la seancon STR_NETWORK_MESSAGE_SERVER_REBOOT :{WHITE}La servilo restartiĝas...{}Bonvolu atendi... + # Content downloading window STR_CONTENT_TITLE :{WHITE}Enhavo elŝutiĝas STR_CONTENT_TYPE_CAPTION :{BLACK}Tipo diff --git a/src/lang/estonian.txt b/src/lang/estonian.txt index e36ed2a9b8..eaa380453c 100644 --- a/src/lang/estonian.txt +++ b/src/lang/estonian.txt @@ -2043,7 +2043,6 @@ STR_FACE_TIE :Lips: STR_FACE_EARRING :Kõrvarõngas: STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}Vaheta kraed või kõrvarõngast -STR_NETWORK_SERVER_VISIBILITY_PRIVATE :Privaatne STR_NETWORK_SERVER_VISIBILITY_PUBLIC :Avalik # Network server list @@ -2202,6 +2201,9 @@ STR_NETWORK_CLIENT_LIST_NEW_COMPANY_TOOLTIP :{BLACK}Loo uus STR_NETWORK_CLIENT_LIST_PLAYER_ICON_SELF_TOOLTIP :{BLACK}Tema oled sina STR_NETWORK_CLIENT_LIST_PLAYER_ICON_HOST_TOOLTIP :{BLACK}Tema on mängu korraldaja +############ Begin of ConnectionType +############ End of ConnectionType + STR_NETWORK_CLIENT_LIST_ADMIN_CLIENT_KICK :Viska välja STR_NETWORK_CLIENT_LIST_ADMIN_CLIENT_BAN :Keela STR_NETWORK_CLIENT_LIST_ADMIN_COMPANY_RESET :Kustuta @@ -2328,6 +2330,7 @@ STR_NETWORK_MESSAGE_SERVER_SHUTDOWN :{WHITE}Server s STR_NETWORK_MESSAGE_SERVER_REBOOT :{WHITE}Server restardib...{}Palun oota... STR_NETWORK_MESSAGE_KICKED :*** {STRING} visati välja. Põhjendus: ({STRING}) + # Content downloading window STR_CONTENT_TITLE :{WHITE}Aineste allalaadimine STR_CONTENT_TYPE_CAPTION :{BLACK}Liik diff --git a/src/lang/faroese.txt b/src/lang/faroese.txt index 678326311f..a2746ec480 100644 --- a/src/lang/faroese.txt +++ b/src/lang/faroese.txt @@ -1817,6 +1817,9 @@ STR_NETWORK_COMPANY_LIST_CLIENT_LIST :Listi yvir klie # Network client list +############ Begin of ConnectionType +############ End of ConnectionType + STR_NETWORK_SERVER :Servari @@ -1925,6 +1928,7 @@ STR_NETWORK_MESSAGE_NAME_CHANGE :*** {STRING} he STR_NETWORK_MESSAGE_SERVER_SHUTDOWN :{WHITE}Servarin endaði setuna STR_NETWORK_MESSAGE_SERVER_REBOOT :{WHITE}Servarin endurbyrjar...{}Vinarliga bíða... + # Content downloading window STR_CONTENT_TITLE :{WHITE}Tilfar verur heinta niður STR_CONTENT_TYPE_CAPTION :{BLACK}Slag diff --git a/src/lang/finnish.txt b/src/lang/finnish.txt index 5c85f86785..dae8ec05b7 100644 --- a/src/lang/finnish.txt +++ b/src/lang/finnish.txt @@ -1995,7 +1995,6 @@ STR_FACE_TIE :Solmio: STR_FACE_EARRING :Korvakoru: STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}Vaihda solmio tai korvakoru -STR_NETWORK_SERVER_VISIBILITY_PRIVATE :Yksityinen STR_NETWORK_SERVER_VISIBILITY_PUBLIC :Julkinen # Network server list @@ -2154,6 +2153,9 @@ STR_NETWORK_CLIENT_LIST_PLAYER_ICON_SELF_TOOLTIP :{BLACK}Tämä o STR_NETWORK_CLIENT_LIST_PLAYER_ICON_HOST_TOOLTIP :{BLACK}Tämä on pelin ylläpitäjä STR_NETWORK_CLIENT_LIST_CLIENT_COMPANY_COUNT :{BLACK}{NUM} pelaaja{P "" a} / {NUM} yhtiö{P "" tä} +############ Begin of ConnectionType +############ End of ConnectionType + STR_NETWORK_CLIENT_LIST_ADMIN_CLIENT_KICK :Potki STR_NETWORK_CLIENT_LIST_ADMIN_CLIENT_BAN :Estä STR_NETWORK_CLIENT_LIST_ADMIN_COMPANY_RESET :Poista @@ -2281,6 +2283,7 @@ STR_NETWORK_MESSAGE_SERVER_SHUTDOWN :{WHITE}Palvelin STR_NETWORK_MESSAGE_SERVER_REBOOT :{WHITE}Palvelin käynnistyy uudelleen...{}Odota, ole hyvä... STR_NETWORK_MESSAGE_KICKED :{STRING} potkaistiin ulos. Syy: ({STRING}) + # Content downloading window STR_CONTENT_TITLE :{WHITE}Sisällön lataus STR_CONTENT_TYPE_CAPTION :{BLACK}Tyyppi diff --git a/src/lang/french.txt b/src/lang/french.txt index 105c4c5600..f86540ce73 100644 --- a/src/lang/french.txt +++ b/src/lang/french.txt @@ -1996,7 +1996,6 @@ 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 @@ -2154,6 +2153,9 @@ STR_NETWORK_CLIENT_LIST_NEW_COMPANY_TOOLTIP :{BLACK}Créer u 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 +############ Begin of ConnectionType +############ End of ConnectionType + STR_NETWORK_CLIENT_LIST_ADMIN_CLIENT_KICK :Exclure STR_NETWORK_CLIENT_LIST_ADMIN_CLIENT_BAN :Bannir STR_NETWORK_CLIENT_LIST_ADMIN_COMPANY_RESET :Supprimer @@ -2281,6 +2283,7 @@ STR_NETWORK_MESSAGE_SERVER_SHUTDOWN :{WHITE}Le serve STR_NETWORK_MESSAGE_SERVER_REBOOT :{WHITE}Le serveur redémarre...{}Veuillez patienter... STR_NETWORK_MESSAGE_KICKED :*** {STRING} a été exclu. Raison{NBSP}: ({STRING}) + # Content downloading window STR_CONTENT_TITLE :{WHITE}Téléchargement de modules STR_CONTENT_TYPE_CAPTION :{BLACK}Type diff --git a/src/lang/frisian.txt b/src/lang/frisian.txt index a882028ebe..c7a96748dc 100644 --- a/src/lang/frisian.txt +++ b/src/lang/frisian.txt @@ -1893,6 +1893,9 @@ STR_NETWORK_COMPANY_LIST_CLIENT_LIST :{WHITE}Client l # Network client list +############ Begin of ConnectionType +############ End of ConnectionType + STR_NETWORK_SERVER :Tsjinner @@ -1971,6 +1974,7 @@ STR_NETWORK_MESSAGE_CLIENT_COMPANY_SPECTATE :*** {STRING} is STR_NETWORK_MESSAGE_NAME_CHANGE :*** {STRING} hat syn/har namme oanpast nei {STRING} STR_NETWORK_MESSAGE_SERVER_SHUTDOWN :{WHITE}Tsjinner hat de ferbining sluten + # Content downloading window STR_CONTENT_TITLE :{WHITE}Ynhâld delheljen STR_CONTENT_TYPE_CAPTION :{BLACK}Type diff --git a/src/lang/gaelic.txt b/src/lang/gaelic.txt index 2fa9fb0ec9..0bf2b2534a 100644 --- a/src/lang/gaelic.txt +++ b/src/lang/gaelic.txt @@ -2202,6 +2202,9 @@ STR_NETWORK_COMPANY_LIST_CLIENT_LIST :Liosta nan clia # Network client list +############ Begin of ConnectionType +############ End of ConnectionType + STR_NETWORK_SERVER :Frithealaiche @@ -2310,6 +2313,7 @@ STR_NETWORK_MESSAGE_NAME_CHANGE :*** Dh'atharrai STR_NETWORK_MESSAGE_SERVER_SHUTDOWN :{WHITE}Dhùin am frithealaiche an seisean STR_NETWORK_MESSAGE_SERVER_REBOOT :{WHITE}Tha am frithealaiche ag ath-thòiseachadh...{}Fuirich greis... + # Content downloading window STR_CONTENT_TITLE :{WHITE}Susbaint ri luchdadh a-nuas STR_CONTENT_TYPE_CAPTION :{BLACK}Seòrsa diff --git a/src/lang/galician.txt b/src/lang/galician.txt index 35d97a0691..4a186884b1 100644 --- a/src/lang/galician.txt +++ b/src/lang/galician.txt @@ -2038,6 +2038,9 @@ STR_NETWORK_COMPANY_LIST_CLIENT_LIST :Lista de client # Network client list +############ Begin of ConnectionType +############ End of ConnectionType + STR_NETWORK_SERVER :Servidor @@ -2147,6 +2150,7 @@ STR_NETWORK_MESSAGE_GIVE_MONEY :*** {STRING} de STR_NETWORK_MESSAGE_SERVER_SHUTDOWN :{WHITE}O servidor pechou a sesión STR_NETWORK_MESSAGE_SERVER_REBOOT :{WHITE}O servidor estase a reiniciar...{}Agarda por favor... + # Content downloading window STR_CONTENT_TITLE :{WHITE}Descargando contidos STR_CONTENT_TYPE_CAPTION :{BLACK}Tipo diff --git a/src/lang/german.txt b/src/lang/german.txt index 718dbe07e3..77147efc35 100644 --- a/src/lang/german.txt +++ b/src/lang/german.txt @@ -1996,7 +1996,6 @@ STR_FACE_TIE :Krawatte: STR_FACE_EARRING :Ohrring: STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}Krawatte oder Ohrring ändern -STR_NETWORK_SERVER_VISIBILITY_PRIVATE :Privat STR_NETWORK_SERVER_VISIBILITY_PUBLIC :Öffentlich # Network server list @@ -2155,6 +2154,9 @@ STR_NETWORK_CLIENT_LIST_PLAYER_ICON_SELF_TOOLTIP :{BLACK}Das sind STR_NETWORK_CLIENT_LIST_PLAYER_ICON_HOST_TOOLTIP :{BLACK}Dies ist der Host des Spiels STR_NETWORK_CLIENT_LIST_CLIENT_COMPANY_COUNT :{BLACK}{NUM} Client{P "" s} / {NUM} Firm{P a en} +############ Begin of ConnectionType +############ End of ConnectionType + STR_NETWORK_CLIENT_LIST_ADMIN_CLIENT_KICK :Hinauswerfen STR_NETWORK_CLIENT_LIST_ADMIN_CLIENT_BAN :Bannen STR_NETWORK_CLIENT_LIST_ADMIN_COMPANY_RESET :Löschen @@ -2282,6 +2284,7 @@ STR_NETWORK_MESSAGE_SERVER_SHUTDOWN :{WHITE}Der Serv STR_NETWORK_MESSAGE_SERVER_REBOOT :{WHITE}Der Server startet neu...{}Bitte warten... STR_NETWORK_MESSAGE_KICKED :*** {STRING} wurde vom Server hinausgeworfen. Grund: ({STRING}) + # Content downloading window STR_CONTENT_TITLE :{WHITE}Herunterladen von Erweiterungen STR_CONTENT_TYPE_CAPTION :{BLACK}Art diff --git a/src/lang/greek.txt b/src/lang/greek.txt index f28c2b20b0..e9d4370d96 100644 --- a/src/lang/greek.txt +++ b/src/lang/greek.txt @@ -2159,6 +2159,9 @@ STR_NETWORK_COMPANY_LIST_CLIENT_LIST :Λίστα συ # Network client list +############ Begin of ConnectionType +############ End of ConnectionType + STR_NETWORK_SERVER :Διακομιστής @@ -2272,6 +2275,7 @@ STR_NETWORK_MESSAGE_SERVER_SHUTDOWN :{WHITE}Ο δι STR_NETWORK_MESSAGE_SERVER_REBOOT :{WHITE}Ο διακομιστής επανεκκινεί...{}Παρακαλώ περιμένετε... STR_NETWORK_MESSAGE_KICKED :*** {STRING} εκδιώχθηκε. Λόγος: ({STRING}) + # Content downloading window STR_CONTENT_TITLE :{WHITE}Περιεχόμενο κατεβαίνει STR_CONTENT_TYPE_CAPTION :{BLACK}Τύπος diff --git a/src/lang/hebrew.txt b/src/lang/hebrew.txt index 5882c1490b..58288a1543 100644 --- a/src/lang/hebrew.txt +++ b/src/lang/hebrew.txt @@ -2016,6 +2016,9 @@ STR_NETWORK_COMPANY_LIST_CLIENT_LIST :רשימת מש # Network client list +############ Begin of ConnectionType +############ End of ConnectionType + STR_NETWORK_SERVER :שרת @@ -2124,6 +2127,7 @@ STR_NETWORK_MESSAGE_NAME_CHANGE :{STRING} שינ STR_NETWORK_MESSAGE_SERVER_SHUTDOWN :{WHITE}השרת סגר את המשחק STR_NETWORK_MESSAGE_SERVER_REBOOT :{WHITE}השרת מופעל מחדש...{}אנא המתן... + # Content downloading window STR_CONTENT_TITLE :{WHITE}הורדת תוכן STR_CONTENT_TYPE_CAPTION :{BLACK}סוג diff --git a/src/lang/hindi.txt b/src/lang/hindi.txt index 0a4246e00f..dbbb24c61c 100644 --- a/src/lang/hindi.txt +++ b/src/lang/hindi.txt @@ -433,6 +433,9 @@ STR_NETWORK_GAME_LOBBY_INAUGURATION_YEAR :{SILVER}उद # Network client list +############ Begin of ConnectionType +############ End of ConnectionType + @@ -456,6 +459,7 @@ STR_NETWORK_SERVER_MESSAGE_GAME_STILL_PAUSED_3 :खेल अ ############ End of leave-in-this-order STR_NETWORK_MESSAGE_CLIENT_LEAVING :छोड़ रहा है + # Content downloading window STR_CONTENT_DETAIL_SUBTITLE_ALREADY_HERE :{SILVER}यह आपके पास पहले से मौजूद है STR_CONTENT_DETAIL_SUBTITLE_DOES_NOT_EXIST :{SILVER}यह सामग्री अज्ञात है और इसे ओपनटीटीडी में प्राप्त नहीं किया जा सकता है diff --git a/src/lang/hungarian.txt b/src/lang/hungarian.txt index f5d41f3dcd..2d85e9b493 100644 --- a/src/lang/hungarian.txt +++ b/src/lang/hungarian.txt @@ -2050,7 +2050,6 @@ STR_FACE_TIE :Nyakkendő: STR_FACE_EARRING :Fülbevaló: STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}Nyakkendő vagy fülbevaló cseréje -STR_NETWORK_SERVER_VISIBILITY_PRIVATE :Privát STR_NETWORK_SERVER_VISIBILITY_PUBLIC :Nyilvános # Network server list @@ -2208,6 +2207,9 @@ STR_NETWORK_CLIENT_LIST_NEW_COMPANY_TOOLTIP :{BLACK}Hozz lé STR_NETWORK_CLIENT_LIST_PLAYER_ICON_SELF_TOOLTIP :{BLACK}Ez vagy te STR_NETWORK_CLIENT_LIST_PLAYER_ICON_HOST_TOOLTIP :{BLACK}Ez a játék elindítója +############ Begin of ConnectionType +############ End of ConnectionType + STR_NETWORK_CLIENT_LIST_ADMIN_CLIENT_KICK :Kirúgás STR_NETWORK_CLIENT_LIST_ADMIN_CLIENT_BAN :Tiltás STR_NETWORK_CLIENT_LIST_ADMIN_COMPANY_RESET :Törlés @@ -2334,6 +2336,7 @@ STR_NETWORK_MESSAGE_SERVER_SHUTDOWN :{WHITE}A szerve STR_NETWORK_MESSAGE_SERVER_REBOOT :{WHITE}A szerver újraindul...{}Türelem... STR_NETWORK_MESSAGE_KICKED :*** {STRING} ki lett rúgva. Oka: ({STRING}) + # Content downloading window STR_CONTENT_TITLE :{WHITE}Tartalom letöltés STR_CONTENT_TYPE_CAPTION :{BLACK}Típus diff --git a/src/lang/icelandic.txt b/src/lang/icelandic.txt index 9e2bc79cfb..c9afa5aecf 100644 --- a/src/lang/icelandic.txt +++ b/src/lang/icelandic.txt @@ -1855,6 +1855,9 @@ STR_NETWORK_COMPANY_LIST_CLIENT_LIST :Listi yfir leik # Network client list +############ Begin of ConnectionType +############ End of ConnectionType + STR_NETWORK_SERVER :Þjónn @@ -1963,6 +1966,7 @@ STR_NETWORK_MESSAGE_NAME_CHANGE :*** {STRING} he STR_NETWORK_MESSAGE_SERVER_SHUTDOWN :{WHITE}Þjónninn sleit tengingunni STR_NETWORK_MESSAGE_SERVER_REBOOT :{WHITE}Verið er að endurræsa þjóninn...{}Vinsamlega bíðið... + # Content downloading window STR_CONTENT_TITLE :{WHITE}Niðurhel efni STR_CONTENT_TYPE_CAPTION :{BLACK}Týpa diff --git a/src/lang/ido.txt b/src/lang/ido.txt index aad91f8978..c56cc78d34 100644 --- a/src/lang/ido.txt +++ b/src/lang/ido.txt @@ -636,6 +636,9 @@ STR_NETWORK_SERVER_LIST_MAP_SIZE_SHORT :{BLACK}{COMMA}x # Network client list +############ Begin of ConnectionType +############ End of ConnectionType + @@ -659,6 +662,7 @@ STR_NETWORK_SERVER_MESSAGE :*** {1:STRING} ############ Leave those lines in this order!! ############ End of leave-in-this-order + # Content downloading window # Order of these is important! diff --git a/src/lang/indonesian.txt b/src/lang/indonesian.txt index a6e167c03b..fed6a3f0a8 100644 --- a/src/lang/indonesian.txt +++ b/src/lang/indonesian.txt @@ -1986,7 +1986,6 @@ STR_FACE_TIE :Dasi: STR_FACE_EARRING :Anting-anting: STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}Ubah dasi atau anting-anting -STR_NETWORK_SERVER_VISIBILITY_PRIVATE :Privat STR_NETWORK_SERVER_VISIBILITY_PUBLIC :Umum # Network server list @@ -2144,6 +2143,9 @@ STR_NETWORK_CLIENT_LIST_NEW_COMPANY_TOOLTIP :{BLACK}Ciptakan STR_NETWORK_CLIENT_LIST_PLAYER_ICON_SELF_TOOLTIP :{BLACK}Ini adalah Anda STR_NETWORK_CLIENT_LIST_PLAYER_ICON_HOST_TOOLTIP :{BLACK}Ini adalah hos permainan +############ Begin of ConnectionType +############ End of ConnectionType + STR_NETWORK_CLIENT_LIST_ADMIN_CLIENT_KICK :Diusir STR_NETWORK_CLIENT_LIST_ADMIN_CLIENT_BAN :Melarang STR_NETWORK_CLIENT_LIST_ADMIN_COMPANY_RESET :Hapus @@ -2270,6 +2272,7 @@ STR_NETWORK_MESSAGE_SERVER_SHUTDOWN :{WHITE}Server m STR_NETWORK_MESSAGE_SERVER_REBOOT :{WHITE}Server memulai ulang...{}Tunggulah... STR_NETWORK_MESSAGE_KICKED :*** {STRING} telah dikeluarkan. Alasan: ({STRING}) + # Content downloading window STR_CONTENT_TITLE :{WHITE}Mengunduh konten STR_CONTENT_TYPE_CAPTION :{BLACK}Tipe diff --git a/src/lang/irish.txt b/src/lang/irish.txt index 665bdf8fcb..4f2dbff5ba 100644 --- a/src/lang/irish.txt +++ b/src/lang/irish.txt @@ -1990,6 +1990,9 @@ STR_NETWORK_COMPANY_LIST_CLIENT_LIST :Liosta na gclia # Network client list +############ Begin of ConnectionType +############ End of ConnectionType + STR_NETWORK_SERVER :Freastalaí @@ -2098,6 +2101,7 @@ STR_NETWORK_MESSAGE_NAME_CHANGE :*** D'athraigh STR_NETWORK_MESSAGE_SERVER_SHUTDOWN :{WHITE}Dhún an freastalaí an seisiún STR_NETWORK_MESSAGE_SERVER_REBOOT :{WHITE}Tá an freastalaí á atosú...{}Fan go fóill... + # Content downloading window STR_CONTENT_TITLE :{WHITE}Inneachar á íoslódáil STR_CONTENT_TYPE_CAPTION :{BLACK}Cineál diff --git a/src/lang/italian.txt b/src/lang/italian.txt index 5c1dd682d7..439a0ad8b3 100644 --- a/src/lang/italian.txt +++ b/src/lang/italian.txt @@ -2131,6 +2131,9 @@ STR_NETWORK_COMPANY_LIST_CLIENT_LIST :Elenco dei clie # Network client list +############ Begin of ConnectionType +############ End of ConnectionType + STR_NETWORK_CLIENT_LIST_ASK_COMPANY_RESET :{YELLOW}Sei sicuro di voler eliminare la compagnia '{COMPANY}'? @@ -2245,6 +2248,7 @@ STR_NETWORK_MESSAGE_SERVER_SHUTDOWN :{WHITE}Il serve STR_NETWORK_MESSAGE_SERVER_REBOOT :{WHITE}Il server si sta riavviando...{}Attendere prego... STR_NETWORK_MESSAGE_KICKED :*** {STRING} è stato espulso. Motivo: ({STRING}) + # Content downloading window STR_CONTENT_TITLE :{WHITE}Download contenuti STR_CONTENT_TYPE_CAPTION :{BLACK}Tipo diff --git a/src/lang/japanese.txt b/src/lang/japanese.txt index d86b8134a0..6311f0350e 100644 --- a/src/lang/japanese.txt +++ b/src/lang/japanese.txt @@ -1987,7 +1987,6 @@ 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 @@ -2145,6 +2144,9 @@ 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}ゲームのホストです +############ Begin of ConnectionType +############ End of ConnectionType + STR_NETWORK_CLIENT_LIST_ADMIN_CLIENT_KICK :キック STR_NETWORK_CLIENT_LIST_ADMIN_CLIENT_BAN :BAN STR_NETWORK_CLIENT_LIST_ADMIN_COMPANY_RESET :削除 @@ -2272,6 +2274,7 @@ STR_NETWORK_MESSAGE_SERVER_SHUTDOWN :{WHITE}サー STR_NETWORK_MESSAGE_SERVER_REBOOT :{WHITE}このサーバーは再起動中です…{}しばらくお待ちください… STR_NETWORK_MESSAGE_KICKED :*** {STRING}がキックされました。理由: ({STRING}) + # Content downloading window STR_CONTENT_TITLE :{WHITE}コンテンツをダウンロード中 STR_CONTENT_TYPE_CAPTION :{BLACK}種類 diff --git a/src/lang/korean.txt b/src/lang/korean.txt index 60faed06ab..1e19756de1 100644 --- a/src/lang/korean.txt +++ b/src/lang/korean.txt @@ -1996,7 +1996,6 @@ 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 @@ -2155,6 +2154,9 @@ STR_NETWORK_CLIENT_LIST_PLAYER_ICON_SELF_TOOLTIP :{BLACK}당신 STR_NETWORK_CLIENT_LIST_PLAYER_ICON_HOST_TOOLTIP :{BLACK}이 게임을 연 사람입니다 STR_NETWORK_CLIENT_LIST_CLIENT_COMPANY_COUNT :{BLACK}접속자 {NUM}명 / 회사 {NUM}개 +############ Begin of ConnectionType +############ End of ConnectionType + STR_NETWORK_CLIENT_LIST_ADMIN_CLIENT_KICK :추방 STR_NETWORK_CLIENT_LIST_ADMIN_CLIENT_BAN :차단 STR_NETWORK_CLIENT_LIST_ADMIN_COMPANY_RESET :삭제 @@ -2282,6 +2284,7 @@ STR_NETWORK_MESSAGE_SERVER_SHUTDOWN :{WHITE}서버 STR_NETWORK_MESSAGE_SERVER_REBOOT :{WHITE}서버가 재시작되고 있습니다...{}기다려주세요... STR_NETWORK_MESSAGE_KICKED :*** {STRING} - 서버에서 강제로 추방되었습니다. 사유: ({STRING}) + # Content downloading window STR_CONTENT_TITLE :{WHITE}콘텐츠 다운로드 STR_CONTENT_TYPE_CAPTION :{BLACK}종류 diff --git a/src/lang/latin.txt b/src/lang/latin.txt index 7985a6b4e5..af566e4593 100644 --- a/src/lang/latin.txt +++ b/src/lang/latin.txt @@ -2209,6 +2209,9 @@ STR_NETWORK_COMPANY_LIST_CLIENT_LIST :Index clientum # Network client list +############ Begin of ConnectionType +############ End of ConnectionType + STR_NETWORK_SERVER :Servatrum @@ -2317,6 +2320,7 @@ STR_NETWORK_MESSAGE_NAME_CHANGE :*** {STRING} no STR_NETWORK_MESSAGE_SERVER_SHUTDOWN :{WHITE}Servatrum iam clausum est STR_NETWORK_MESSAGE_SERVER_REBOOT :{WHITE}Servatrum resumit...{}Maneas... + # Content downloading window STR_CONTENT_TITLE :{WHITE}Arcessitus Rerum STR_CONTENT_TYPE_CAPTION :{BLACK}Typus diff --git a/src/lang/latvian.txt b/src/lang/latvian.txt index 2b5bf6ed4f..d8a738849e 100644 --- a/src/lang/latvian.txt +++ b/src/lang/latvian.txt @@ -2085,6 +2085,9 @@ STR_NETWORK_COMPANY_LIST_CLIENT_LIST :Spēlētāju sa # Network client list +############ Begin of ConnectionType +############ End of ConnectionType + STR_NETWORK_SERVER :Serveris @@ -2198,6 +2201,7 @@ STR_NETWORK_MESSAGE_SERVER_SHUTDOWN :{WHITE}Serveris STR_NETWORK_MESSAGE_SERVER_REBOOT :{WHITE}Serveris pārstartējas...{}Lūdzu uzgaidiet... STR_NETWORK_MESSAGE_KICKED :*** {STRING} tika izmests. Iemesls: ({STRING}) + # Content downloading window STR_CONTENT_TITLE :{WHITE}Satura lejupielāde STR_CONTENT_TYPE_CAPTION :{BLACK}Tips diff --git a/src/lang/lithuanian.txt b/src/lang/lithuanian.txt index f4225ae5ed..200c642401 100644 --- a/src/lang/lithuanian.txt +++ b/src/lang/lithuanian.txt @@ -2329,6 +2329,9 @@ STR_NETWORK_COMPANY_LIST_CLIENT_LIST :Žaidėjų sąr # Network client list +############ Begin of ConnectionType +############ End of ConnectionType + STR_NETWORK_SERVER :Serveris @@ -2442,6 +2445,7 @@ STR_NETWORK_MESSAGE_SERVER_SHUTDOWN :{WHITE}Serveris STR_NETWORK_MESSAGE_SERVER_REBOOT :{WHITE}Serveris persikrauna...{}Prašau palaukti... STR_NETWORK_MESSAGE_KICKED :*** {STRING} buvo išmestas. Priežastis: ({STRING}) + # Content downloading window STR_CONTENT_TITLE :{WHITE}Turinio atsisiuntimas STR_CONTENT_TYPE_CAPTION :{BLACK}Tipas diff --git a/src/lang/luxembourgish.txt b/src/lang/luxembourgish.txt index 9009431cf4..f18305b451 100644 --- a/src/lang/luxembourgish.txt +++ b/src/lang/luxembourgish.txt @@ -2110,6 +2110,9 @@ STR_NETWORK_COMPANY_LIST_CLIENT_LIST :Spillerlëscht # Network client list +############ Begin of ConnectionType +############ End of ConnectionType + STR_NETWORK_SERVER :Server @@ -2223,6 +2226,7 @@ STR_NETWORK_MESSAGE_SERVER_SHUTDOWN :{WHITE}De Serve STR_NETWORK_MESSAGE_SERVER_REBOOT :{WHITE}De Server gëtt nei gestart...{}W.e.g. waarden... STR_NETWORK_MESSAGE_KICKED :*** {STRING} gouf gekickt. Grond: ({STRING}) + # Content downloading window STR_CONTENT_TITLE :{WHITE}Lueden Inhalt erof STR_CONTENT_TYPE_CAPTION :{BLACK}Typ diff --git a/src/lang/macedonian.txt b/src/lang/macedonian.txt index 844bfd0a36..2fc2c60860 100644 --- a/src/lang/macedonian.txt +++ b/src/lang/macedonian.txt @@ -994,6 +994,9 @@ STR_NETWORK_SERVER_LIST_MAP_SIZE_SHORT :{BLACK}{COMMA}x # Network client list +############ Begin of ConnectionType +############ End of ConnectionType + @@ -1019,6 +1022,7 @@ STR_NETWORK_SERVER_MESSAGE_GAME_STILL_PAUSED_4 :Игра ушт STR_NETWORK_SERVER_MESSAGE_GAME_REASON_GAME_SCRIPT :игра скрипта ############ End of leave-in-this-order + # Content downloading window STR_CONTENT_SEARCH_EXTERNAL :{BLACK}Пребарај надворешни веб-страни STR_CONTENT_SEARCH_EXTERNAL_DISCLAIMER_CAPTION :{WHITE}Вие заминувате од OpenTTD! diff --git a/src/lang/malay.txt b/src/lang/malay.txt index 729c88fc41..cc0b75aaab 100644 --- a/src/lang/malay.txt +++ b/src/lang/malay.txt @@ -1752,6 +1752,9 @@ STR_NETWORK_COMPANY_LIST_CLIENT_LIST :Senarai klien # Network client list +############ Begin of ConnectionType +############ End of ConnectionType + STR_NETWORK_SERVER :Pelayan @@ -1860,6 +1863,7 @@ STR_NETWORK_MESSAGE_NAME_CHANGE :*** {STRING} te STR_NETWORK_MESSAGE_SERVER_SHUTDOWN :{WHITE}Pelayan telah menutup sesi ini STR_NETWORK_MESSAGE_SERVER_REBOOT :{WHITE}Pelayan sedang dimulakan semula...{}Harap bersabar... + # Content downloading window STR_CONTENT_TITLE :{WHITE}Kandungan sedang dimuat turun STR_CONTENT_TYPE_CAPTION :{BLACK}Jenis diff --git a/src/lang/maltese.txt b/src/lang/maltese.txt index 58f13463c4..df5a2456e1 100644 --- a/src/lang/maltese.txt +++ b/src/lang/maltese.txt @@ -564,6 +564,9 @@ STR_NETWORK_CONNECTING_DOWNLOADING_2 :{BLACK}{BYTES} # Network client list +############ Begin of ConnectionType +############ End of ConnectionType + @@ -588,6 +591,7 @@ STR_NETWORK_SERVER_MESSAGE :*** {1:STRING} ############ Leave those lines in this order!! ############ End of leave-in-this-order + # Content downloading window # Order of these is important! diff --git a/src/lang/marathi.txt b/src/lang/marathi.txt index 8f4c7fbac3..cb9fdb0b37 100644 --- a/src/lang/marathi.txt +++ b/src/lang/marathi.txt @@ -934,6 +934,9 @@ STR_NETWORK_CONNECTING_DOWNLOADING_2 :{BLACK}{BYTES} # Network client list +############ Begin of ConnectionType +############ End of ConnectionType + @@ -957,6 +960,7 @@ STR_NETWORK_SERVER_MESSAGE :*** {1:STRING} ############ Leave those lines in this order!! ############ End of leave-in-this-order + # Content downloading window # Order of these is important! diff --git a/src/lang/norwegian_bokmal.txt b/src/lang/norwegian_bokmal.txt index 796d75ecca..e17f3e36ae 100644 --- a/src/lang/norwegian_bokmal.txt +++ b/src/lang/norwegian_bokmal.txt @@ -1998,7 +1998,6 @@ 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 @@ -2158,6 +2157,9 @@ STR_NETWORK_CLIENT_LIST_PLAYER_ICON_SELF_TOOLTIP :{BLACK}Dette er STR_NETWORK_CLIENT_LIST_PLAYER_ICON_HOST_TOOLTIP :{BLACK}Dette er verten for spillet STR_NETWORK_CLIENT_LIST_CLIENT_COMPANY_COUNT :{BLACK}{NUM} klient{P "" s} / {NUM} firma{P et er} +############ Begin of ConnectionType +############ End of ConnectionType + STR_NETWORK_CLIENT_LIST_ADMIN_CLIENT_KICK :Spark STR_NETWORK_CLIENT_LIST_ADMIN_CLIENT_BAN :Utesteng STR_NETWORK_CLIENT_LIST_ADMIN_COMPANY_RESET :Slett @@ -2285,6 +2287,7 @@ STR_NETWORK_MESSAGE_SERVER_SHUTDOWN :{WHITE}Tjeneren STR_NETWORK_MESSAGE_SERVER_REBOOT :{WHITE}Tjeneren starter på nytt...{}Vennligst vent... STR_NETWORK_MESSAGE_KICKED :*** {STRING} ble kastet ut. Grunn: ({STRING}) + # Content downloading window STR_CONTENT_TITLE :{WHITE}Laster ned innhold STR_CONTENT_TYPE_CAPTION :{BLACK}Type diff --git a/src/lang/norwegian_nynorsk.txt b/src/lang/norwegian_nynorsk.txt index 17c7ffbb0a..563a6eaead 100644 --- a/src/lang/norwegian_nynorsk.txt +++ b/src/lang/norwegian_nynorsk.txt @@ -1914,6 +1914,9 @@ STR_NETWORK_COMPANY_LIST_CLIENT_LIST :Liste over klie # Network client list +############ Begin of ConnectionType +############ End of ConnectionType + STR_NETWORK_SERVER :Tenar @@ -2022,6 +2025,7 @@ STR_NETWORK_MESSAGE_NAME_CHANGE :*** {STRING} ha STR_NETWORK_MESSAGE_SERVER_SHUTDOWN :{WHITE}Tenaren avslutta spelet STR_NETWORK_MESSAGE_SERVER_REBOOT :{WHITE}Tenaren startar om att...{}Vær venleg og vent... + # Content downloading window STR_CONTENT_TITLE :{WHITE}Lastar ned innhald STR_CONTENT_TYPE_CAPTION :{BLACK}Type diff --git a/src/lang/persian.txt b/src/lang/persian.txt index be0d60a541..f37cc09e47 100644 --- a/src/lang/persian.txt +++ b/src/lang/persian.txt @@ -1712,6 +1712,9 @@ STR_NETWORK_COMPANY_LIST_CLIENT_LIST :{WHITE}لیست # Network client list +############ Begin of ConnectionType +############ End of ConnectionType + STR_NETWORK_SERVER :سرویس دهنده @@ -1820,6 +1823,7 @@ STR_NETWORK_MESSAGE_NAME_CHANGE :*** {STRING} ن STR_NETWORK_MESSAGE_SERVER_SHUTDOWN :{WHITE}سرویس دهنده جلسه را تعطیل کرد STR_NETWORK_MESSAGE_SERVER_REBOOT :{WHITE}راه اندازی مجدد سرویس دهنده...{}لطفا صبر کنید... + # Content downloading window STR_CONTENT_TITLE :{WHITE}در حال دانلود STR_CONTENT_TYPE_CAPTION :{BLACK}نوع diff --git a/src/lang/polish.txt b/src/lang/polish.txt index 98e0c30364..6eb80757f4 100644 --- a/src/lang/polish.txt +++ b/src/lang/polish.txt @@ -2496,6 +2496,9 @@ STR_NETWORK_COMPANY_LIST_CLIENT_LIST :Lista klientów # Network client list +############ Begin of ConnectionType +############ End of ConnectionType + STR_NETWORK_SERVER :Serwer @@ -2609,6 +2612,7 @@ STR_NETWORK_MESSAGE_SERVER_SHUTDOWN :{WHITE}Serwer z STR_NETWORK_MESSAGE_SERVER_REBOOT :{WHITE}Restart serwera...{}Proszę czekać... STR_NETWORK_MESSAGE_KICKED :*** {STRING} został wyrzucony. Powód: ({STRING}) + # Content downloading window STR_CONTENT_TITLE :{WHITE}Przeglądarka dodatkowej zawartości STR_CONTENT_TYPE_CAPTION :{BLACK}Typ diff --git a/src/lang/portuguese.txt b/src/lang/portuguese.txt index 4975058db4..511517c672 100644 --- a/src/lang/portuguese.txt +++ b/src/lang/portuguese.txt @@ -1996,7 +1996,6 @@ 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 @@ -2155,6 +2154,9 @@ STR_NETWORK_CLIENT_LIST_PLAYER_ICON_SELF_TOOLTIP :{BLACK}Este é STR_NETWORK_CLIENT_LIST_PLAYER_ICON_HOST_TOOLTIP :{BLACK}Este é o anfitrião do jogo STR_NETWORK_CLIENT_LIST_CLIENT_COMPANY_COUNT :{BLACK}{NUM} cliente{P "" s} / {NUM} companhi{P a as} +############ Begin of ConnectionType +############ End of ConnectionType + STR_NETWORK_CLIENT_LIST_ADMIN_CLIENT_KICK :Expulsar STR_NETWORK_CLIENT_LIST_ADMIN_CLIENT_BAN :Banir STR_NETWORK_CLIENT_LIST_ADMIN_COMPANY_RESET :Apagar @@ -2282,6 +2284,7 @@ STR_NETWORK_MESSAGE_SERVER_SHUTDOWN :{WHITE}O servid STR_NETWORK_MESSAGE_SERVER_REBOOT :{WHITE}O servidor está a reiniciar...{}Por favor espere... STR_NETWORK_MESSAGE_KICKED :*** {STRING} foi expulso. Motivo: ({STRING}) + # Content downloading window STR_CONTENT_TITLE :{WHITE}Descarregamento de conteúdo STR_CONTENT_TYPE_CAPTION :{BLACK}Tipo diff --git a/src/lang/romanian.txt b/src/lang/romanian.txt index 6e6f5a68ba..968950155d 100644 --- a/src/lang/romanian.txt +++ b/src/lang/romanian.txt @@ -1913,7 +1913,6 @@ STR_FACE_TIE :Cravată: STR_FACE_EARRING :Cercei: STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}Schimbă cravata sau cerceii -STR_NETWORK_SERVER_VISIBILITY_PRIVATE :Privat # Network server list STR_NETWORK_SERVER_LIST_CAPTION :{WHITE}Multiplayer @@ -2049,6 +2048,9 @@ STR_NETWORK_CLIENT_LIST_PLAYER_NAME_EDIT_TOOLTIP :{BLACK}Modific STR_NETWORK_CLIENT_LIST_CHAT_COMPANY_TOOLTIP :{BLACK}Trimite un mesaj tuturor jucătorilor acestei companii STR_NETWORK_CLIENT_LIST_CHAT_SPECTATOR_TOOLTIP :{BLACK}Trimite un mesaj tuturor spectatorilor +############ Begin of ConnectionType +############ End of ConnectionType + STR_NETWORK_CLIENT_LIST_ADMIN_COMPANY_UNLOCK :Deblocare cu parolă STR_NETWORK_CLIENT_LIST_ASK_CLIENT_KICK :{YELLOW}Sigur vrei să dai afară jucătorul '{STRING}'? @@ -2164,6 +2166,7 @@ STR_NETWORK_MESSAGE_SERVER_SHUTDOWN :{WHITE}Serverul STR_NETWORK_MESSAGE_SERVER_REBOOT :{WHITE}Serverul este repornit...{}Vă rugăm aşteptaţi... STR_NETWORK_MESSAGE_KICKED :*** {STRING} a fost dat afară. Motiv: ({STRING}) + # Content downloading window STR_CONTENT_TITLE :{WHITE}Descărcare resurse online STR_CONTENT_TYPE_CAPTION :{BLACK}Tip diff --git a/src/lang/russian.txt b/src/lang/russian.txt index fa367b1136..0ae886b266 100644 --- a/src/lang/russian.txt +++ b/src/lang/russian.txt @@ -2146,7 +2146,6 @@ 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 @@ -2305,6 +2304,9 @@ STR_NETWORK_CLIENT_LIST_PLAYER_ICON_SELF_TOOLTIP :{BLACK}Это STR_NETWORK_CLIENT_LIST_PLAYER_ICON_HOST_TOOLTIP :{BLACK}Это организатор игры STR_NETWORK_CLIENT_LIST_CLIENT_COMPANY_COUNT :{BLACK}{NUM} клиент{P "" а ов} / {NUM} компани{P я и й} +############ Begin of ConnectionType +############ End of ConnectionType + STR_NETWORK_CLIENT_LIST_ADMIN_CLIENT_KICK :Отключить STR_NETWORK_CLIENT_LIST_ADMIN_CLIENT_BAN :Заблокировать STR_NETWORK_CLIENT_LIST_ADMIN_COMPANY_RESET :Удалить @@ -2432,6 +2434,7 @@ STR_NETWORK_MESSAGE_SERVER_SHUTDOWN :{WHITE}Серв STR_NETWORK_MESSAGE_SERVER_REBOOT :{WHITE}Сервер перезапускается...{}Пожалуйста, подождите... STR_NETWORK_MESSAGE_KICKED :*** {STRING} был исключён из игры. Причина: ({STRING}) + # Content downloading window STR_CONTENT_TITLE :{WHITE}Загрузка контента STR_CONTENT_TYPE_CAPTION :{BLACK}Тип diff --git a/src/lang/serbian.txt b/src/lang/serbian.txt index a4ff5b0c36..0e2d0dd69c 100644 --- a/src/lang/serbian.txt +++ b/src/lang/serbian.txt @@ -2305,6 +2305,9 @@ STR_NETWORK_COMPANY_LIST_CLIENT_LIST :Spisak klijenat # Network client list +############ Begin of ConnectionType +############ End of ConnectionType + STR_NETWORK_SERVER :Server @@ -2418,6 +2421,7 @@ STR_NETWORK_MESSAGE_SERVER_SHUTDOWN :{WHITE}Server j STR_NETWORK_MESSAGE_SERVER_REBOOT :{WHITE}Server se ponovo pokreće...{}Molimo sačekajte... STR_NETWORK_MESSAGE_KICKED :*** {STRING} je izvačen. Razlog: ({STRING}) + # Content downloading window STR_CONTENT_TITLE :{WHITE}Dodaci za preuzimanje STR_CONTENT_TYPE_CAPTION :{BLACK}Vrsta diff --git a/src/lang/simplified_chinese.txt b/src/lang/simplified_chinese.txt index 38d2891127..006fd10440 100644 --- a/src/lang/simplified_chinese.txt +++ b/src/lang/simplified_chinese.txt @@ -2115,6 +2115,9 @@ STR_NETWORK_COMPANY_LIST_CLIENT_LIST :客户端列表 # Network client list +############ Begin of ConnectionType +############ End of ConnectionType + STR_NETWORK_SERVER :服务器 @@ -2228,6 +2231,7 @@ STR_NETWORK_MESSAGE_SERVER_SHUTDOWN :{WHITE}服务 STR_NETWORK_MESSAGE_SERVER_REBOOT :{WHITE}服务器正在重新启动。{}请等待…… STR_NETWORK_MESSAGE_KICKED :*** {STRING} 被踢出服务器。原因:({STRING}) + # Content downloading window STR_CONTENT_TITLE :{WHITE}下载内容 STR_CONTENT_TYPE_CAPTION :{BLACK}类型 diff --git a/src/lang/slovak.txt b/src/lang/slovak.txt index 473bf35647..9763287bc1 100644 --- a/src/lang/slovak.txt +++ b/src/lang/slovak.txt @@ -2054,7 +2054,6 @@ STR_FACE_TIE :Kravata: STR_FACE_EARRING :Náušnica: STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}Zmeniť kravatu alebo náušnicu -STR_NETWORK_SERVER_VISIBILITY_PRIVATE :Privátny STR_NETWORK_SERVER_VISIBILITY_PUBLIC :Verejný # Network server list @@ -2212,6 +2211,9 @@ STR_NETWORK_CLIENT_LIST_NEW_COMPANY_TOOLTIP :{BLACK}Založi STR_NETWORK_CLIENT_LIST_PLAYER_ICON_SELF_TOOLTIP :{BLACK}Toto ste vy STR_NETWORK_CLIENT_LIST_PLAYER_ICON_HOST_TOOLTIP :{BLACK}Toto je hosť hry +############ Begin of ConnectionType +############ End of ConnectionType + STR_NETWORK_CLIENT_LIST_ADMIN_CLIENT_KICK :Vyhodiť STR_NETWORK_CLIENT_LIST_ADMIN_CLIENT_BAN :Ban STR_NETWORK_CLIENT_LIST_ADMIN_COMPANY_RESET :Vymazať @@ -2338,6 +2340,7 @@ STR_NETWORK_MESSAGE_SERVER_SHUTDOWN :{WHITE}Server u STR_NETWORK_MESSAGE_SERVER_REBOOT :{WHITE}Server sa reštartuje...{}Čakajte prosím... STR_NETWORK_MESSAGE_KICKED :*** Hráč {STRING} bol vyhodený. Dôvod: ({STRING}) + # Content downloading window STR_CONTENT_TITLE :{WHITE}Sťahovanie obsahu STR_CONTENT_TYPE_CAPTION :{BLACK}Typ diff --git a/src/lang/slovenian.txt b/src/lang/slovenian.txt index 1931220d70..6dfe349c9e 100644 --- a/src/lang/slovenian.txt +++ b/src/lang/slovenian.txt @@ -2146,6 +2146,9 @@ STR_NETWORK_COMPANY_LIST_CLIENT_LIST :Seznam gostov # Network client list +############ Begin of ConnectionType +############ End of ConnectionType + STR_NETWORK_SERVER :Strežnik @@ -2254,6 +2257,7 @@ STR_NETWORK_MESSAGE_NAME_CHANGE :*** {STRING} je STR_NETWORK_MESSAGE_SERVER_SHUTDOWN :{WHITE}Strežnik je zaprl sejo STR_NETWORK_MESSAGE_SERVER_REBOOT :{WHITE}Strežnik se zaganja...{}Prosim počakaj... + # Content downloading window STR_CONTENT_TITLE :{WHITE}Prenos vsebin STR_CONTENT_TYPE_CAPTION :{BLACK}Vrsta diff --git a/src/lang/spanish.txt b/src/lang/spanish.txt index d688ffa9c1..34490996cb 100644 --- a/src/lang/spanish.txt +++ b/src/lang/spanish.txt @@ -1996,7 +1996,6 @@ 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 @@ -2154,6 +2153,9 @@ STR_NETWORK_CLIENT_LIST_NEW_COMPANY_TOOLTIP :{BLACK}Crea una 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 +############ Begin of ConnectionType +############ End of ConnectionType + 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 @@ -2281,6 +2283,7 @@ STR_NETWORK_MESSAGE_SERVER_SHUTDOWN :{WHITE}El servi STR_NETWORK_MESSAGE_SERVER_REBOOT :{WHITE}El servidor está reiniciando...{}Espera por favor... STR_NETWORK_MESSAGE_KICKED :*** {STRING} ha sido expulsado. Razón: ({STRING}) + # Content downloading window STR_CONTENT_TITLE :{WHITE}Descarga de contenido STR_CONTENT_TYPE_CAPTION :{BLACK}Tipo diff --git a/src/lang/spanish_MX.txt b/src/lang/spanish_MX.txt index a490db27b3..d590d35650 100644 --- a/src/lang/spanish_MX.txt +++ b/src/lang/spanish_MX.txt @@ -301,7 +301,7 @@ STR_SORT_BY_TIMETABLE_DELAY :Retraso en itin STR_SORT_BY_FACILITY :Tipo de estación STR_SORT_BY_WAITING_TOTAL :Carga total en espera STR_SORT_BY_WAITING_AVAILABLE :Carga disponible en espera -STR_SORT_BY_RATING_MAX :Valoración más alta de cargamento +STR_SORT_BY_RATING_MAX :Mayor índice de carga STR_SORT_BY_RATING_MIN :Menor índice de carga STR_SORT_BY_ENGINE_ID :Id. locomotora (orden clásico) STR_SORT_BY_COST :Costo @@ -330,7 +330,7 @@ STR_TOOLBAR_TOOLTIP_FORWARD :{BLACK}Avance r STR_TOOLBAR_TOOLTIP_OPTIONS :{BLACK}Opciones STR_TOOLBAR_TOOLTIP_SAVE_GAME_ABANDON_GAME :{BLACK}Guardar partida y salir del juego STR_TOOLBAR_TOOLTIP_DISPLAY_MAP :{BLACK}Mostrar mapa, ventana de vista adicional o lista de carteles -STR_TOOLBAR_TOOLTIP_DISPLAY_TOWN_DIRECTORY :{BLACK}Mostrar guía de pueblos +STR_TOOLBAR_TOOLTIP_DISPLAY_TOWN_DIRECTORY :{BLACK}Mostrar guía de localidades STR_TOOLBAR_TOOLTIP_DISPLAY_SUBSIDIES :{BLACK}Mostrar subsidios STR_TOOLBAR_TOOLTIP_DISPLAY_LIST_OF_COMPANY_STATIONS :{BLACK}Mostrar lista de estaciones STR_TOOLBAR_TOOLTIP_DISPLAY_COMPANY_FINANCES :{BLACK}Mostrar información financiera de la empresa @@ -364,9 +364,9 @@ STR_SCENEDIT_TOOLBAR_SCENARIO_EDITOR :{YELLOW}Editor STR_SCENEDIT_TOOLBAR_TOOLTIP_MOVE_THE_STARTING_DATE_BACKWARD :{BLACK}Retroceder un año la fecha de inicio STR_SCENEDIT_TOOLBAR_TOOLTIP_MOVE_THE_STARTING_DATE_FORWARD :{BLACK}Avanzar un año la fecha de inicio STR_SCENEDIT_TOOLBAR_TOOLTIP_SET_DATE :{BLACK}Clic para establecer el año inicial -STR_SCENEDIT_TOOLBAR_TOOLTIP_DISPLAY_MAP_TOWN_DIRECTORY :{BLACK}Mostrar mapa, guía de pueblos +STR_SCENEDIT_TOOLBAR_TOOLTIP_DISPLAY_MAP_TOWN_DIRECTORY :{BLACK}Mostrar mapa, guía de localidades STR_SCENEDIT_TOOLBAR_LANDSCAPE_GENERATION :{BLACK}Generación de terreno -STR_SCENEDIT_TOOLBAR_TOWN_GENERATION :{BLACK}Generación de pueblos +STR_SCENEDIT_TOOLBAR_TOWN_GENERATION :{BLACK}Generación de localidades STR_SCENEDIT_TOOLBAR_INDUSTRY_GENERATION :{BLACK}Generación de industrias STR_SCENEDIT_TOOLBAR_ROAD_CONSTRUCTION :{BLACK}Construcción de carreteras STR_SCENEDIT_TOOLBAR_TRAM_CONSTRUCTION :{BLACK}Construcción de tranvía @@ -390,7 +390,7 @@ STR_SETTINGS_MENU_CONFIG_SETTINGS_TREE :Configuración STR_SETTINGS_MENU_SCRIPT_SETTINGS :Configuración de scripts STR_SETTINGS_MENU_NEWGRF_SETTINGS :Configuración de NewGRF STR_SETTINGS_MENU_TRANSPARENCY_OPTIONS :Opciones de transparencia -STR_SETTINGS_MENU_TOWN_NAMES_DISPLAYED :Mostrar nombres de pueblos +STR_SETTINGS_MENU_TOWN_NAMES_DISPLAYED :Mostrar nombres de localidades STR_SETTINGS_MENU_STATION_NAMES_DISPLAYED :Mostrar nombres de estaciones STR_SETTINGS_MENU_WAYPOINTS_DISPLAYED :Mostrar puntos de ruta STR_SETTINGS_MENU_SIGNS_DISPLAYED :Mostrar carteles propios @@ -416,8 +416,8 @@ STR_MAP_MENU_LINGRAPH_LEGEND :Leyenda de fluj STR_MAP_MENU_SIGN_LIST :Lista de carteles ############ range for town menu starts -STR_TOWN_MENU_TOWN_DIRECTORY :Guía de pueblos -STR_TOWN_MENU_FOUND_TOWN :Fundar pueblo +STR_TOWN_MENU_TOWN_DIRECTORY :Guía de localidades +STR_TOWN_MENU_FOUND_TOWN :Fundar localidad ############ range ends here ############ range for subsidies menu starts @@ -641,8 +641,8 @@ STR_PERFORMANCE_DETAIL_STATIONS_TOOLTIP :{BLACK}Número STR_PERFORMANCE_DETAIL_MIN_PROFIT_TOOLTIP :{BLACK}Utilidad del vehículo con menores ingresos (de entre aquellos con más de 2 años) STR_PERFORMANCE_DETAIL_MIN_INCOME_TOOLTIP :{BLACK}Cantidad de efectivo ganado en el trimestre con la utilidad más baja de los últimos 12 trimestres STR_PERFORMANCE_DETAIL_MAX_INCOME_TOOLTIP :{BLACK}Cantidad de efectivo ganado en el trimestre con la utilidad más alta de los últimos 12 trimestres -STR_PERFORMANCE_DETAIL_DELIVERED_TOOLTIP :{BLACK}Unidades de cargamento entregadas en los últimos cuatro trimestres -STR_PERFORMANCE_DETAIL_CARGO_TOOLTIP :{BLACK}Número de tipos de cargamento entregados en el último trimestre +STR_PERFORMANCE_DETAIL_DELIVERED_TOOLTIP :{BLACK}Unidades de carga entregadas en los últimos cuatro trimestres +STR_PERFORMANCE_DETAIL_CARGO_TOOLTIP :{BLACK}Número de tipos de carga entregadas en el último trimestre STR_PERFORMANCE_DETAIL_MONEY_TOOLTIP :{BLACK}Cantidad de dinero que esta empresa tiene en el banco STR_PERFORMANCE_DETAIL_LOAN_TOOLTIP :{BLACK}Cantidad de dinero que esta empresa ha recibido como préstamo STR_PERFORMANCE_DETAIL_TOTAL_TOOLTIP :{BLACK}Total de puntos ganados del máximo posible @@ -754,12 +754,12 @@ STR_SMALLMAP_LEGENDA_TREES :{TINY_FONT}{BLA STR_SMALLMAP_LEGENDA_ROCKS :{TINY_FONT}{BLACK}Rocas STR_SMALLMAP_LEGENDA_WATER :{TINY_FONT}{BLACK}Agua STR_SMALLMAP_LEGENDA_NO_OWNER :{TINY_FONT}{BLACK}Sin propietario -STR_SMALLMAP_LEGENDA_TOWNS :{TINY_FONT}{BLACK}Pueblos +STR_SMALLMAP_LEGENDA_TOWNS :{TINY_FONT}{BLACK}Localidades STR_SMALLMAP_LEGENDA_INDUSTRIES :{TINY_FONT}{BLACK}Industrias STR_SMALLMAP_LEGENDA_DESERT :{TINY_FONT}{BLACK}Desierto STR_SMALLMAP_LEGENDA_SNOW :{TINY_FONT}{BLACK}Nieve -STR_SMALLMAP_TOOLTIP_TOGGLE_TOWN_NAMES_ON_OFF :{BLACK}Mostrar u ocultar nombres de pueblos en el mapa +STR_SMALLMAP_TOOLTIP_TOGGLE_TOWN_NAMES_ON_OFF :{BLACK}Mostrar u ocultar nombres de localidades en el mapa STR_SMALLMAP_CENTER :{BLACK}Centrar la vista en la ubicación actual STR_SMALLMAP_INDUSTRY :{TINY_FONT}{STRING} ({NUM}) STR_SMALLMAP_LINKSTATS :{TINY_FONT}{STRING} @@ -825,8 +825,8 @@ STR_NEWS_COMPANY_LAUNCH_DESCRIPTION :{BIG_FONT}{BLAC STR_NEWS_MERGER_TAKEOVER_TITLE :{BIG_FONT}{BLACK}¡{STRING} ha sido adquirida por {STRING}! STR_PRESIDENT_NAME_MANAGER :{BLACK}{PRESIDENT_NAME}{}(Presidente) -STR_NEWS_NEW_TOWN :{BLACK}{BIG_FONT}¡{STRING} patrocina la creación del nuevo pueblo de {TOWN}! -STR_NEWS_NEW_TOWN_UNSPONSORED :{BLACK}{BIG_FONT}¡El nuevo pueblo de {TOWN} ha sido formado! +STR_NEWS_NEW_TOWN :{BLACK}{BIG_FONT}¡{STRING} patrocinó la creación de la nueva localidad de {TOWN}! +STR_NEWS_NEW_TOWN_UNSPONSORED :{BLACK}{BIG_FONT}¡La nueva localidad de {TOWN} ha sido formada! STR_NEWS_INDUSTRY_CONSTRUCTION :{BIG_FONT}{BLACK}¡Nuev{G o a} {STRING} en construcción cerca de {TOWN}! STR_NEWS_INDUSTRY_PLANTED :{BIG_FONT}{BLACK}¡Nuev{G o a} {STRING} fundad{G o a} cerca de {TOWN}! @@ -955,7 +955,7 @@ STR_GAME_OPTIONS_ROAD_VEHICLES_DROPDOWN_LEFT :Manejar por la 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_DROPDOWN_TOOLTIP :{BLACK}Elegir el estilo de nombres para pueblos +STR_GAME_OPTIONS_TOWN_NAMES_DROPDOWN_TOOLTIP :{BLACK}Elegir el estilo de nombres para las localidades ############ start of townname region STR_GAME_OPTIONS_TOWN_NAME_ORIGINAL_ENGLISH :Inglés @@ -1307,8 +1307,8 @@ STR_CONFIG_SETTING_PLANE_CRASHES_HELPTEXT :Probabilidad de STR_CONFIG_SETTING_PLANE_CRASHES_NONE :Ninguno* STR_CONFIG_SETTING_PLANE_CRASHES_REDUCED :Reducida STR_CONFIG_SETTING_PLANE_CRASHES_NORMAL :Normal -STR_CONFIG_SETTING_STOP_ON_TOWN_ROAD :Permitir la construcción de paradas intermedias sobre carreteras en pueblos: {STRING} -STR_CONFIG_SETTING_STOP_ON_TOWN_ROAD_HELPTEXT :Construir paradas de autobuses intermedias en carreteras que sean propiedad de los pueblos +STR_CONFIG_SETTING_STOP_ON_TOWN_ROAD :Permitir la construcción de paradas intermedias en localidades: {STRING} +STR_CONFIG_SETTING_STOP_ON_TOWN_ROAD_HELPTEXT :Permitir la construcción de paradas intermedias en carreteras propiedad de las localidades STR_CONFIG_SETTING_STOP_ON_COMPETITOR_ROAD :Permitir la construcción de paradas intermedias sobre carreteras de la competencia: {STRING} STR_CONFIG_SETTING_STOP_ON_COMPETITOR_ROAD_HELPTEXT :Construir paradas de autobuses intermedias en carreteras que sean propiedad de otras empresas STR_CONFIG_SETTING_DYNAMIC_ENGINES_EXISTING_VEHICLES :{WHITE}No se puede cambiar esta opción si ya existen vehículos @@ -1367,7 +1367,7 @@ STR_CONFIG_SETTING_INDUSTRY_DENSITY_HELPTEXT :Número de indu STR_CONFIG_SETTING_OIL_REF_EDGE_DISTANCE :Distancia máxima de industrias petrolíferas al borde del mapa: {STRING} STR_CONFIG_SETTING_OIL_REF_EDGE_DISTANCE_HELPTEXT :Distancia límite desde el borde del mapa a partir del cual se pueden construir refinerías y plataformas de petróleo. En mapas con forma de isla esto garantiza que se ubiquen cerca de la costa. En mapas de más de 256 casillas el valor se amplía. STR_CONFIG_SETTING_SNOWLINE_HEIGHT :Nivel de inicio de nieve: {STRING} -STR_CONFIG_SETTING_SNOWLINE_HEIGHT_HELPTEXT :Controlar la altura donde la nieve empieza en mapas de clima Subártico, lo cual afectará la generación de industrias y los requisitos de crecimiento de pueblos. Este valor se puede cambiar en el Editor de mapas o se calculará según la "Extensión de nieve" +STR_CONFIG_SETTING_SNOWLINE_HEIGHT_HELPTEXT :Controlar la altura al que empieza la nieve en geografía de Subártico, lo cual afectará la generación de industrias y los requisitos de crecimiento de localidades. Solo puede cambiarse en el Editor de mapas, si no, se calculará según la "Extensión de nieve" STR_CONFIG_SETTING_SNOW_COVERAGE :Extensión de nieve: {STRING} STR_CONFIG_SETTING_SNOW_COVERAGE_HELPTEXT :Controlar la cantidad aproximada de nieve al generar un mapa de geografía de Subártico, la cual afectará la generación de industrias y los requisitos de crecimiento de las localidades. La superficie casi al ras del nivel del mar nunca tiene nieve STR_CONFIG_SETTING_SNOW_COVERAGE_VALUE :{NUM}% @@ -1523,7 +1523,7 @@ STR_CONFIG_SETTING_MAX_SHIPS :Número máximo STR_CONFIG_SETTING_MAX_SHIPS_HELPTEXT :Número máximo de barcos que una empresa puede tener STR_CONFIG_SETTING_AI_BUILDS_TRAINS :Desactivar trenes para la computadora: {STRING} -STR_CONFIG_SETTING_AI_BUILDS_TRAINS_HELPTEXT :Activar esta opción para deshabilitar la construcción de trenes por jugadores no humanos +STR_CONFIG_SETTING_AI_BUILDS_TRAINS_HELPTEXT :Activar esta opción deshabilita la construcción de trenes por jugadores no humanos STR_CONFIG_SETTING_AI_BUILDS_ROAD_VEHICLES :Desactivar vehículos de carretera para la computadora: {STRING} STR_CONFIG_SETTING_AI_BUILDS_ROAD_VEHICLES_HELPTEXT :Activar esta opción deshabilita la construcción de vehículos de carretera por jugadores no humanos STR_CONFIG_SETTING_AI_BUILDS_AIRCRAFT :Desactivar aeroplanos para la computadora: {STRING} @@ -1637,8 +1637,8 @@ STR_CONFIG_SETTING_CYCLE_SIGNAL_NORMAL :Solo señales d STR_CONFIG_SETTING_CYCLE_SIGNAL_PBS :Solo señales de ruta STR_CONFIG_SETTING_CYCLE_SIGNAL_ALL :Todas -STR_CONFIG_SETTING_TOWN_LAYOUT :Diseño urbano para los nuevos pueblos: {STRING} -STR_CONFIG_SETTING_TOWN_LAYOUT_HELPTEXT :Diseño de carreteras y calles para las redes de transporte en los pueblos +STR_CONFIG_SETTING_TOWN_LAYOUT :Diseño urbano para nuevas localidades: {STRING} +STR_CONFIG_SETTING_TOWN_LAYOUT_HELPTEXT :Diseño de carreteras dentro de las localidades STR_CONFIG_SETTING_TOWN_LAYOUT_DEFAULT :Original STR_CONFIG_SETTING_TOWN_LAYOUT_BETTER_ROADS :Mejorado STR_CONFIG_SETTING_TOWN_LAYOUT_2X2_GRID :Rejilla de 2×2 @@ -1650,8 +1650,8 @@ STR_CONFIG_SETTING_ALLOW_TOWN_LEVEL_CROSSINGS :Permitir a las STR_CONFIG_SETTING_ALLOW_TOWN_LEVEL_CROSSINGS_HELPTEXT :Activar esta opción permite a las localidades construir pasos a nivel STR_CONFIG_SETTING_NOISE_LEVEL :Permitir el ruido de aeropuertos controlado por localidades: {STRING} STR_CONFIG_SETTING_NOISE_LEVEL_HELPTEXT :Al desactivarse, puede haber hasta dos aeropuertos por localidad. Al activarse, el número de aeropuertos por localidad depende de su nivel de ruido permitido, el cual depende de la población, el tamaño de cada aeropuerto y la distancia -STR_CONFIG_SETTING_TOWN_FOUNDING :Fundar pueblos: {STRING} -STR_CONFIG_SETTING_TOWN_FOUNDING_HELPTEXT :Los jugadores podrán crear nuevos pueblos durante la partida +STR_CONFIG_SETTING_TOWN_FOUNDING :Fundar localidades: {STRING} +STR_CONFIG_SETTING_TOWN_FOUNDING_HELPTEXT :Activar esta opción permite a los jugadores funda nuevas localidades durante la partida STR_CONFIG_SETTING_TOWN_FOUNDING_FORBIDDEN :Prohibido STR_CONFIG_SETTING_TOWN_FOUNDING_ALLOWED :Permitido STR_CONFIG_SETTING_TOWN_FOUNDING_ALLOWED_CUSTOM_LAYOUT :Permitido, diseño urbano personalizado @@ -1694,8 +1694,8 @@ STR_CONFIG_SETTING_ZOOM_LVL_OUT_8X :8x STR_CONFIG_SETTING_SPRITE_ZOOM_LVL_MIN :4x STR_CONFIG_SETTING_SPRITE_ZOOM_LVL_IN_2X :2x STR_CONFIG_SETTING_SPRITE_ZOOM_LVL_NORMAL :1x -STR_CONFIG_SETTING_TOWN_GROWTH :Velocidad de crecimiento de pueblos: {STRING} -STR_CONFIG_SETTING_TOWN_GROWTH_HELPTEXT :A qué velocidad se expanden los pueblos +STR_CONFIG_SETTING_TOWN_GROWTH :Velocidad de crecimiento de localidades: {STRING} +STR_CONFIG_SETTING_TOWN_GROWTH_HELPTEXT :A qué velocidad se expanden las localidades STR_CONFIG_SETTING_TOWN_GROWTH_NONE :Ninguna STR_CONFIG_SETTING_TOWN_GROWTH_SLOW :Lenta STR_CONFIG_SETTING_TOWN_GROWTH_NORMAL :Normal @@ -1996,7 +1996,6 @@ 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 @@ -2155,6 +2154,9 @@ STR_NETWORK_CLIENT_LIST_PLAYER_ICON_SELF_TOOLTIP :{BLACK}Este ere STR_NETWORK_CLIENT_LIST_PLAYER_ICON_HOST_TOOLTIP :{BLACK}Este es el host del juego STR_NETWORK_CLIENT_LIST_CLIENT_COMPANY_COUNT :{BLACK}{NUM} cliente{P "" s}/{NUM} empresa{P "" s} +############ Begin of ConnectionType +############ End of ConnectionType + 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 @@ -2282,6 +2284,7 @@ STR_NETWORK_MESSAGE_SERVER_SHUTDOWN :{WHITE}El servi STR_NETWORK_MESSAGE_SERVER_REBOOT :{WHITE}Se está reiniciando el servidor...{}Espera por favor... STR_NETWORK_MESSAGE_KICKED :*** {STRING} ha sido expulsado. Razón: ({STRING}) + # Content downloading window STR_CONTENT_TITLE :{WHITE}Descarga de contenido STR_CONTENT_TYPE_CAPTION :{BLACK}Tipo @@ -2369,7 +2372,7 @@ STR_TRANSPARENT_BUILDINGS_TOOLTIP :{BLACK}Transpar STR_TRANSPARENT_BRIDGES_TOOLTIP :{BLACK}Transparencia de puentes. Ctrl+Clic para bloquear STR_TRANSPARENT_STRUCTURES_TOOLTIP :{BLACK}Transparencia de estructuras como faros o antenas. Ctrl+Clic para bloquear STR_TRANSPARENT_CATENARY_TOOLTIP :{BLACK}Transparencia de catenaria. Ctrl+Clic para bloquear -STR_TRANSPARENT_LOADING_TOOLTIP :{BLACK}Transparencia de indicadores de cargamento. Ctrl+Clic para bloquear +STR_TRANSPARENT_LOADING_TOOLTIP :{BLACK}Transparencia de indicadores de embarque. Ctrl+Clic para bloquear STR_TRANSPARENT_INVISIBLE_TOOLTIP :{BLACK}Ocultar objetos totalmente # Linkgraph legend window @@ -2464,7 +2467,7 @@ STR_BUILD_SIGNAL_ELECTRIC_EXIT_TOOLTIP :{BLACK}Señal d STR_BUILD_SIGNAL_ELECTRIC_COMBO_TOOLTIP :{BLACK}Señal combo (eléctrica){}Hace lo mismo que las señales de entrada y de salida, lo que permite construir amplios "árboles" de señales condicionales STR_BUILD_SIGNAL_ELECTRIC_PBS_TOOLTIP :{BLACK}Señal de ruta (eléctrica){}Permite a más de un tren entrar al mismo tiempo en un tramo de vía con señales, en tanto que el tren pueda reservar una ruta hasta un lugar seguro. Puede ser pasada por detrás STR_BUILD_SIGNAL_ELECTRIC_PBS_OWAY_TOOLTIP :{BLACK}Señal de ruta de un sentido (eléctrica){}Igual que una señal de ruta pero no puede ser pasada por detrás -STR_BUILD_SIGNAL_CONVERT_TOOLTIP :{BLACK}Conversión de señal{}Activar para pulsar sobre una señal existente y convertirla en el tipo y variante elegidos. Ctrl+Clic permite cambiar entre variantes de señales. Mayús+Clic muestra una estimación del precio de conversión +STR_BUILD_SIGNAL_CONVERT_TOOLTIP :{BLACK}Conversión de señal{}Activar para pulsar sobre una señal existente y convertirla en el tipo y variante elegidos. Ctrl+Clic permite cambiar entre variantes de señales. Mayús+Clic muestra un precio estimado STR_BUILD_SIGNAL_DRAG_SIGNALS_DENSITY_TOOLTIP :{BLACK}Distancia entre señales STR_BUILD_SIGNAL_DRAG_SIGNALS_DENSITY_DECREASE_TOOLTIP :{BLACK}Reducir distancia entre señales STR_BUILD_SIGNAL_DRAG_SIGNALS_DENSITY_INCREASE_TOOLTIP :{BLACK}Aumentar distancia entre señales @@ -2619,31 +2622,31 @@ STR_QUERY_RESET_LANDSCAPE_CAPTION :{WHITE}Restaura STR_RESET_LANDSCAPE_CONFIRMATION_TEXT :{WHITE}¿Eliminar todas las propiedades de las empresas? # Town generation window (SE) -STR_FOUND_TOWN_CAPTION :{WHITE}Creación de pueblos -STR_FOUND_TOWN_NEW_TOWN_BUTTON :{BLACK}Nuevo pueblo -STR_FOUND_TOWN_NEW_TOWN_TOOLTIP :{BLACK}Fundar un nuevo pueblo. Mayús muestra una estimación del precio -STR_FOUND_TOWN_RANDOM_TOWN_BUTTON :{BLACK}Pueblo aleatorio -STR_FOUND_TOWN_RANDOM_TOWN_TOOLTIP :{BLACK}Fundar pueblo en un lugar aleatorio -STR_FOUND_TOWN_MANY_RANDOM_TOWNS :{BLACK}Muchos pueblos aleatorios -STR_FOUND_TOWN_RANDOM_TOWNS_TOOLTIP :{BLACK}Cubrir el mapa con pueblos colocados al azar - -STR_FOUND_TOWN_NAME_TITLE :{YELLOW}Nombre del pueblo: -STR_FOUND_TOWN_NAME_EDITOR_TITLE :{BLACK}Indicar el nombre del pueblo -STR_FOUND_TOWN_NAME_EDITOR_HELP :{BLACK}Clic para indicar el nombre del pueblo +STR_FOUND_TOWN_CAPTION :{WHITE}Generación de localidades +STR_FOUND_TOWN_NEW_TOWN_BUTTON :{BLACK}Nueva localidad +STR_FOUND_TOWN_NEW_TOWN_TOOLTIP :{BLACK}Fundar una nueva localidad. Mayús muestra una estimación del precio +STR_FOUND_TOWN_RANDOM_TOWN_BUTTON :{BLACK}Localidad aleatoria +STR_FOUND_TOWN_RANDOM_TOWN_TOOLTIP :{BLACK}Fundar la localidad en un lugar aleatorio +STR_FOUND_TOWN_MANY_RANDOM_TOWNS :{BLACK}Muchas localidades aleatorias +STR_FOUND_TOWN_RANDOM_TOWNS_TOOLTIP :{BLACK}Cubrir el mapa con localidades colocadas al azar + +STR_FOUND_TOWN_NAME_TITLE :{YELLOW}Nombre de la localidad: +STR_FOUND_TOWN_NAME_EDITOR_TITLE :{BLACK}Ingresar el nombre de la localidad +STR_FOUND_TOWN_NAME_EDITOR_HELP :{BLACK}Clic para ingresar el nombre de la localidad STR_FOUND_TOWN_NAME_RANDOM_BUTTON :{BLACK}Nombre aleatorio STR_FOUND_TOWN_NAME_RANDOM_TOOLTIP :{BLACK}Generar nuevo nombre al azar -STR_FOUND_TOWN_INITIAL_SIZE_TITLE :{YELLOW}Tamaño del pueblo: +STR_FOUND_TOWN_INITIAL_SIZE_TITLE :{YELLOW}Tamaño de la localidad: STR_FOUND_TOWN_INITIAL_SIZE_SMALL_BUTTON :{BLACK}Pequeño STR_FOUND_TOWN_INITIAL_SIZE_MEDIUM_BUTTON :{BLACK}Mediano STR_FOUND_TOWN_INITIAL_SIZE_LARGE_BUTTON :{BLACK}Grande STR_FOUND_TOWN_SIZE_RANDOM :{BLACK}Aleatorio -STR_FOUND_TOWN_INITIAL_SIZE_TOOLTIP :{BLACK}Elegir el tamaño del pueblo +STR_FOUND_TOWN_INITIAL_SIZE_TOOLTIP :{BLACK}Elegir el tamaño de la localidad STR_FOUND_TOWN_CITY :{BLACK}Ciudad -STR_FOUND_TOWN_CITY_TOOLTIP :{BLACK}Las ciudades crecen más rápido que los pueblos{}Según la configuración del juego, al ser creadas tendrán mayor población +STR_FOUND_TOWN_CITY_TOOLTIP :{BLACK}Las ciudades crecen más rápido que las localidades{}Según la configuración del juego, al ser creadas son más grandes STR_FOUND_TOWN_ROAD_LAYOUT :{YELLOW}Diseño urbano: -STR_FOUND_TOWN_SELECT_TOWN_ROAD_LAYOUT :{BLACK}Elegir el diseño de carreteras y cuadras para este pueblo +STR_FOUND_TOWN_SELECT_TOWN_ROAD_LAYOUT :{BLACK}Elegir el diseño urbano de esta localidad STR_FOUND_TOWN_SELECT_LAYOUT_ORIGINAL :{BLACK}Original STR_FOUND_TOWN_SELECT_LAYOUT_BETTER_ROADS :{BLACK}Mejorado STR_FOUND_TOWN_SELECT_LAYOUT_2X2_GRID :{BLACK}Rejilla de 2×2 @@ -2905,7 +2908,7 @@ STR_MAPGEN_WORLD_GENERATION_CAPTION :{WHITE}Generaci STR_MAPGEN_MAPSIZE :{BLACK}Tamaño del mapa: STR_MAPGEN_MAPSIZE_TOOLTIP :{BLACK}Elegir el tamaño del mapa en número de casillas. El número real de casillas disponibles en el mapa será ligeramente inferior STR_MAPGEN_BY :{BLACK}× -STR_MAPGEN_NUMBER_OF_TOWNS :{BLACK}Núm. de pueblos: +STR_MAPGEN_NUMBER_OF_TOWNS :{BLACK}Núm. de localidades: STR_MAPGEN_DATE :{BLACK}Fecha: STR_MAPGEN_NUMBER_OF_INDUSTRIES :{BLACK}Núm. de industrias: STR_MAPGEN_HEIGHTMAP_HEIGHT :{BLACK}Cima más alta: @@ -3172,7 +3175,7 @@ STR_EDIT_SIGN_PREVIOUS_SIGN_TOOLTIP :{BLACK}Ir al ca STR_EDIT_SIGN_SIGN_OSKTITLE :{BLACK}Indicar un nombre para el cartel # Town directory window -STR_TOWN_DIRECTORY_CAPTION :{WHITE}Pueblos +STR_TOWN_DIRECTORY_CAPTION :{WHITE}Localidades STR_TOWN_DIRECTORY_NONE :{ORANGE}- Ninguno - STR_TOWN_DIRECTORY_TOWN :{ORANGE}{TOWN}{BLACK} ({COMMA}) STR_TOWN_DIRECTORY_CITY :{ORANGE}{TOWN}{YELLOW} (ciudad){BLACK} ({COMMA}) @@ -3190,19 +3193,19 @@ STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_REQUIRED_WINTER :{ORANGE}{STRING STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_DELIVERED_GENERAL :{ORANGE}{STRING}{GREEN} entregado STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_REQUIRED :{ORANGE}{CARGO_TINY}/{CARGO_LONG}{RED} (todavía requerido) STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_DELIVERED :{ORANGE}{CARGO_TINY}/{CARGO_LONG}{GREEN} (entregado) -STR_TOWN_VIEW_TOWN_GROWS_EVERY :{BLACK}El pueblo crece cada {ORANGE}{COMMA}{BLACK}{NBSP}día{P "" s} -STR_TOWN_VIEW_TOWN_GROWS_EVERY_FUNDED :{BLACK}El pueblo crece cada {ORANGE}{COMMA}{BLACK}{NBSP}días{P "" s} (obra negra) -STR_TOWN_VIEW_TOWN_GROW_STOPPED :{BLACK}El pueblo {RED}no{BLACK} está creciendo -STR_TOWN_VIEW_NOISE_IN_TOWN :{BLACK}Nivel de ruido en el pueblo: {ORANGE}{COMMA}{BLACK} Máx.: {ORANGE}{COMMA} +STR_TOWN_VIEW_TOWN_GROWS_EVERY :{BLACK}La localidad crece cada {ORANGE}{COMMA}{BLACK}{NBSP}día{P "" s} +STR_TOWN_VIEW_TOWN_GROWS_EVERY_FUNDED :{BLACK}La localidad crece cada {ORANGE}{COMMA}{BLACK}{NBSP}días{P "" s} (obra negra) +STR_TOWN_VIEW_TOWN_GROW_STOPPED :{BLACK}La localidad {RED}no{BLACK} está creciendo +STR_TOWN_VIEW_NOISE_IN_TOWN :{BLACK}Nivel de ruido en la localidad: {ORANGE}{COMMA}{BLACK} máx.: {ORANGE}{COMMA} STR_TOWN_VIEW_CENTER_TOOLTIP :{BLACK}Centrar la vista en el pueblo. Ctrl+Clic abre una vista aparte STR_TOWN_VIEW_LOCAL_AUTHORITY_BUTTON :{BLACK}Ayuntamiento STR_TOWN_VIEW_LOCAL_AUTHORITY_TOOLTIP :{BLACK}Mostrar información sobre el ayuntamiento local -STR_TOWN_VIEW_RENAME_TOOLTIP :{BLACK}Cambiar nombre del pueblo +STR_TOWN_VIEW_RENAME_TOOLTIP :{BLACK}Cambiar nombre de la localidad STR_TOWN_VIEW_EXPAND_BUTTON :{BLACK}Aumentar -STR_TOWN_VIEW_EXPAND_TOOLTIP :{BLACK}Aumentar la población del pueblo +STR_TOWN_VIEW_EXPAND_TOOLTIP :{BLACK}Aumentar la población de la localidad STR_TOWN_VIEW_DELETE_BUTTON :{BLACK}Eliminar -STR_TOWN_VIEW_DELETE_TOOLTIP :{BLACK}Eliminar este pueblo completamente +STR_TOWN_VIEW_DELETE_TOOLTIP :{BLACK}Eliminar esta localidad completamente STR_TOWN_VIEW_RENAME_TOWN_BUTTON :Cambiar nombre @@ -3213,7 +3216,7 @@ STR_LOCAL_AUTHORITY_ZONE_TOOLTIP :{BLACK}Mostrar STR_LOCAL_AUTHORITY_COMPANY_RATINGS :{BLACK}Evaluación de empresas de transporte: STR_LOCAL_AUTHORITY_COMPANY_RATING :{YELLOW}{COMPANY} {COMPANY_NUM}: {ORANGE}{STRING} STR_LOCAL_AUTHORITY_ACTIONS_TITLE :{BLACK}Acciones disponibles: -STR_LOCAL_AUTHORITY_ACTIONS_TOOLTIP :{BLACK}Lista de acciones que pueden llevarse a cabo en este pueblo. Clic en un elemento de la lista para obtener más detalles +STR_LOCAL_AUTHORITY_ACTIONS_TOOLTIP :{BLACK}Acciones que pueden realizarse en esta localidad. Clic muestra más detalles STR_LOCAL_AUTHORITY_DO_IT_BUTTON :{BLACK}Realizar STR_LOCAL_AUTHORITY_DO_IT_TOOLTIP :{BLACK}Llevar a cabo la acción elegida de la lista @@ -3226,9 +3229,9 @@ STR_LOCAL_AUTHORITY_ACTION_NEW_BUILDINGS :Pagar la constr STR_LOCAL_AUTHORITY_ACTION_EXCLUSIVE_TRANSPORT :Comprar los derechos exclusivos de transporte STR_LOCAL_AUTHORITY_ACTION_BRIBE :Sobornar al ayuntamiento -STR_LOCAL_AUTHORITY_ACTION_TOOLTIP_SMALL_ADVERTISING :{YELLOW}Iniciar una pequeña campaña publicitaria local para atraer más pasajeros y cargamento a tus servicios de transporte.{}Otorga un aumento temporal a la evaluación de estaciones en un radio pequeño alrededor del centro del pueblo.{}Costo: {CURRENCY_LONG} -STR_LOCAL_AUTHORITY_ACTION_TOOLTIP_MEDIUM_ADVERTISING :{YELLOW}Iniciar una campaña publicitaria local mediana para atraer más pasajeros y cargamento a tus servicios de transporte.{}Otorga un aumento temporal a la evaluación de estaciones en un radio mediano alrededor del centro del pueblo.{}Costo: {CURRENCY_LONG} -STR_LOCAL_AUTHORITY_ACTION_TOOLTIP_LARGE_ADVERTISING :{YELLOW}Iniciar una gran campaña publicitaria local para atraer más pasajeros y cargamento a tus servicios de transporte.{}Otorga un aumento temporal a la evaluación de estaciones en un radio grande alrededor del centro del pueblo.{}Costo: {CURRENCY_LONG} +STR_LOCAL_AUTHORITY_ACTION_TOOLTIP_SMALL_ADVERTISING :{YELLOW}Iniciar una pequeña campaña publicitaria local para atraer más pasajeros y carga a tus servicios de transporte.{}Otorga un aumento temporal a la evaluación de estaciones en un radio pequeño alrededor del centro de la localidad.{}Costo: {CURRENCY_LONG} +STR_LOCAL_AUTHORITY_ACTION_TOOLTIP_MEDIUM_ADVERTISING :{YELLOW}Iniciar una campaña publicitaria local mediana para atraer más pasajeros y carga a tus servicios de transporte.{}Otorga un aumento temporal a la evaluación de estaciones en un radio mediano alrededor del centro de la localidad.{}Costo: {CURRENCY_LONG} +STR_LOCAL_AUTHORITY_ACTION_TOOLTIP_LARGE_ADVERTISING :{YELLOW}Iniciar una gran campaña publicitaria local para atraer más pasajeros y carga a tus servicios de transporte.{}Otorga un aumento temporal a la evaluación de estaciones en un radio grande alrededor del centro de la localidad.{}Costo: {CURRENCY_LONG} STR_LOCAL_AUTHORITY_ACTION_TOOLTIP_ROAD_RECONSTRUCTION :{YELLOW}Pagar la reconstrucción de las carreteras locales.{}Provoca considerables complicaciones al tráfico hasta por 6 meses.{}Costo: {CURRENCY_LONG} STR_LOCAL_AUTHORITY_ACTION_TOOLTIP_STATUE_OF_COMPANY :{YELLOW}Construir una estatua en honor a tu empresa.{}Otorga un aumento permanente a la evaluación estaciones en este pueblo.{}Costo: {CURRENCY_LONG} STR_LOCAL_AUTHORITY_ACTION_TOOLTIP_NEW_BUILDINGS :{YELLOW}Pagar la construcción de nuevos edificios comerciales en el pueblo.{}Otorga un aumento temporal a su crecimiento.{}Costo: {CURRENCY_LONG} @@ -3319,8 +3322,8 @@ STR_STATION_VIEW_ACCEPTS_BUTTON :{BLACK}Acepta STR_STATION_VIEW_ACCEPTS_TOOLTIP :{BLACK}Lista de carga aceptada STR_STATION_VIEW_ACCEPTS_CARGO :{BLACK}Acepta: {WHITE}{CARGO_LIST} -STR_STATION_VIEW_EXCLUSIVE_RIGHTS_SELF :{BLACK}Esta estación tiene los derechos exclusivos de transporte en este pueblo. -STR_STATION_VIEW_EXCLUSIVE_RIGHTS_COMPANY :{YELLOW}{COMPANY}{BLACK} compró los derechos exclusivos de transporte en este pueblo. +STR_STATION_VIEW_EXCLUSIVE_RIGHTS_SELF :{BLACK}Esta estación tiene los derechos exclusivos de transporte en esta localidad. +STR_STATION_VIEW_EXCLUSIVE_RIGHTS_COMPANY :{YELLOW}{COMPANY}{BLACK} compró los derechos exclusivos de transporte en esta localidad. STR_STATION_VIEW_RATINGS_BUTTON :{BLACK}Evaluación STR_STATION_VIEW_RATINGS_TOOLTIP :{BLACK}Ver evaluación de la estación @@ -3646,7 +3649,7 @@ STR_BUY_VEHICLE_AIRCRAFT_BUY_VEHICLE_TOOLTIP :{BLACK}Comprar STR_BUY_VEHICLE_TRAIN_BUY_REFIT_VEHICLE_TOOLTIP :{BLACK}Comprar y reformar tren seleccionado. Mayús+Clic muestra una estimación del precio sin realizar la compra STR_BUY_VEHICLE_ROAD_VEHICLE_BUY_REFIT_VEHICLE_TOOLTIP :{BLACK}Comprar y reformar vehículo seleccionado. Mayús+Clic muestra una estimación del precio sin realizar la compra STR_BUY_VEHICLE_SHIP_BUY_REFIT_VEHICLE_TOOLTIP :{BLACK}Comprar y reformar barco seleccionado. Mayús+Clic muestra una estimación del precio sin realizar la compra -STR_BUY_VEHICLE_AIRCRAFT_BUY_REFIT_VEHICLE_TOOLTIP :{BLACK}Comprar y reformar areonave seleccionada. Mayús+Clic muestra una estimación del precio sin realizar la compra +STR_BUY_VEHICLE_AIRCRAFT_BUY_REFIT_VEHICLE_TOOLTIP :{BLACK}Comprar y reformar la areonave seleccionada. Mayús+Clic muestra un precio estimado sin realizar la compra STR_BUY_VEHICLE_TRAIN_RENAME_BUTTON :{BLACK}Cambiar nombre STR_BUY_VEHICLE_ROAD_VEHICLE_RENAME_BUTTON :{BLACK}Cambiar nombre @@ -4343,8 +4346,8 @@ STR_GAME_SAVELOAD_NOT_AVAILABLE : STR_WARNING_LOADGAME_REMOVED_TRAMS :{WHITE}La partida fue guardada en una versión que no admite tranvías. Todos los tranvías serán eliminados # Map generation messages -STR_ERROR_COULD_NOT_CREATE_TOWN :{WHITE}Generación de mapa abortada...{}... no hay ubicaciones apropiadas para colocar pueblos -STR_ERROR_NO_TOWN_IN_SCENARIO :{WHITE}... no hay pueblos en el mapa +STR_ERROR_COULD_NOT_CREATE_TOWN :{WHITE}Generación de mapa abortada...{}... no hay ubicaciones apropiadas para localidades +STR_ERROR_NO_TOWN_IN_SCENARIO :{WHITE}... no hay localidades en el mapa STR_ERROR_PNGMAP :{WHITE}No se puede cargar mapa desde PNG... STR_ERROR_PNGMAP_FILE_NOT_FOUND :{WHITE}... archivo no encontrado @@ -4396,7 +4399,7 @@ STR_ERROR_NOT_ALLOWED_WHILE_PAUSED :{WHITE}No permi # Local authority errors STR_ERROR_LOCAL_AUTHORITY_REFUSES_TO_ALLOW_THIS :{WHITE}El ayuntamiento de {TOWN} se opone a esta acción -STR_ERROR_LOCAL_AUTHORITY_REFUSES_AIRPORT :{WHITE}El ayuntamiento de {TOWN} se opone a la construcción de otro aeropuerto en este pueblo +STR_ERROR_LOCAL_AUTHORITY_REFUSES_AIRPORT :{WHITE}El ayuntamiento de {TOWN} se opone a la construcción de otro aeropuerto en esta localidad STR_ERROR_LOCAL_AUTHORITY_REFUSES_NOISE :{WHITE}El ayuntamiento de {TOWN} se opone a otorgar permiso para la construcción del aeropuerto debido a cuestiones de ruido STR_ERROR_BRIBE_FAILED :{WHITE}El intento de soborno ha sido descubierto por un investigador de la zona @@ -4428,18 +4431,18 @@ STR_ERROR_CAN_T_SELL_25_SHARE_IN :{WHITE}No se pu STR_ERROR_PROTECTED :{WHITE}Esta empresa es muy reciente para comerciar con acciones... # Town related errors -STR_ERROR_CAN_T_GENERATE_TOWN :{WHITE}No se puede crear ningún pueblo -STR_ERROR_CAN_T_RENAME_TOWN :{WHITE}No se puede cambiar nombre del pueblo... -STR_ERROR_CAN_T_FOUND_TOWN_HERE :{WHITE}No se puede crear pueblo aquí... -STR_ERROR_CAN_T_EXPAND_TOWN :{WHITE}No se puede expandir el pueblo... +STR_ERROR_CAN_T_GENERATE_TOWN :{WHITE}No se puede crear ninguna localidad +STR_ERROR_CAN_T_RENAME_TOWN :{WHITE}No se puede cambiar nombre de la localidad... +STR_ERROR_CAN_T_FOUND_TOWN_HERE :{WHITE}No se puede crear localidad aquí... +STR_ERROR_CAN_T_EXPAND_TOWN :{WHITE}No se puede expandir la localidad... STR_ERROR_TOO_CLOSE_TO_EDGE_OF_MAP_SUB :{WHITE}... demasiado cerca del borde del mapa -STR_ERROR_TOO_CLOSE_TO_ANOTHER_TOWN :{WHITE}... demasiado cerca de otro pueblo -STR_ERROR_TOO_MANY_TOWNS :{WHITE}... demasiados pueblos +STR_ERROR_TOO_CLOSE_TO_ANOTHER_TOWN :{WHITE}... demasiado cerca de otra localidad +STR_ERROR_TOO_MANY_TOWNS :{WHITE}... demasiadas localidades STR_ERROR_NO_SPACE_FOR_TOWN :{WHITE}... ya no hay espacio en el mapa -STR_ERROR_TOWN_EXPAND_WARN_NO_ROADS :{WHITE}El pueblo no construirá carreteras. La función de construcción de carreteras puede activarse en Configuración->Ambiente->Pueblos +STR_ERROR_TOWN_EXPAND_WARN_NO_ROADS :{WHITE}La localidad no construirá carreteras. Se puede activar esta función en Configuración->Ambiente->Localidades STR_ERROR_ROAD_WORKS_IN_PROGRESS :{WHITE}Obras de carretera en progreso -STR_ERROR_TOWN_CAN_T_DELETE :{WHITE}No se puede eliminar este pueblo...{}Aún tiene estaciones o depósitos vinculados, o una casilla en su jurisdicción no se puede quitar -STR_ERROR_STATUE_NO_SUITABLE_PLACE :{WHITE}... no hay ningún lugar apto para una estatua en el centro de este pueblo +STR_ERROR_TOWN_CAN_T_DELETE :{WHITE}No se puede eliminar esta localidad...{}Aún tiene una estación o depósito, o una de sus casillas no se puede quitar +STR_ERROR_STATUE_NO_SUITABLE_PLACE :{WHITE}... no hay lugar apto para una estatua en el centro de esta localidad # Industry related errors STR_ERROR_TOO_MANY_INDUSTRIES :{WHITE}... demasiadas industrias @@ -4447,13 +4450,13 @@ STR_ERROR_CAN_T_GENERATE_INDUSTRIES :{WHITE}No se pu STR_ERROR_CAN_T_BUILD_HERE :{WHITE}No se puede construir {STRING} aquí... STR_ERROR_CAN_T_CONSTRUCT_THIS_INDUSTRY :{WHITE}No se puede construir este tipo de industria aquí... STR_ERROR_INDUSTRY_TOO_CLOSE :{WHITE}... demasiado cerca de otra industria -STR_ERROR_MUST_FOUND_TOWN_FIRST :{WHITE}... primero se debe crear al menos un pueblo -STR_ERROR_ONLY_ONE_ALLOWED_PER_TOWN :{WHITE}... solo se permite una por pueblo -STR_ERROR_CAN_ONLY_BE_BUILT_IN_TOWNS_WITH_POPULATION_OF_1200 :{WHITE}... solo se puede construir en pueblos de al menos 1,200 habitantes +STR_ERROR_MUST_FOUND_TOWN_FIRST :{WHITE}... primero se debe crear al menos una localidad +STR_ERROR_ONLY_ONE_ALLOWED_PER_TOWN :{WHITE}... solo se permite una por localidad +STR_ERROR_CAN_ONLY_BE_BUILT_IN_TOWNS_WITH_POPULATION_OF_1200 :{WHITE}... solo se puede construir en localidades de al menos 1,200 habitantes STR_ERROR_CAN_ONLY_BE_BUILT_IN_RAINFOREST :{WHITE}... solo se puede construir en áreas de selva STR_ERROR_CAN_ONLY_BE_BUILT_IN_DESERT :{WHITE}... solo se puede construir en áreas desérticas -STR_ERROR_CAN_ONLY_BE_BUILT_IN_TOWNS :{WHITE}... solo se puede construir en pueblos (reemplazando casas) -STR_ERROR_CAN_ONLY_BE_BUILT_NEAR_TOWN_CENTER :{WHITE}... solo se puede construir cerca del centro de un pueblo +STR_ERROR_CAN_ONLY_BE_BUILT_IN_TOWNS :{WHITE}... solo se puede construir en localidades (reemplazando casas) +STR_ERROR_CAN_ONLY_BE_BUILT_NEAR_TOWN_CENTER :{WHITE}... solo se puede construir cerca del centro de una localidad STR_ERROR_CAN_ONLY_BE_BUILT_IN_LOW_AREAS :{WHITE}... solo se puede construir en zonas bajas STR_ERROR_CAN_ONLY_BE_POSITIONED :{WHITE}... solo se puede situar cerca de los bordes del mapa STR_ERROR_FOREST_CAN_ONLY_BE_PLANTED :{WHITE}... los bosques solo se pueden plantar sobre la nieve @@ -4481,7 +4484,7 @@ STR_ERROR_TOO_MANY_TRUCK_STOPS :{WHITE}Demasiad STR_ERROR_TOO_CLOSE_TO_ANOTHER_DOCK :{WHITE}Demasiado cerca de otro muelle STR_ERROR_TOO_CLOSE_TO_ANOTHER_AIRPORT :{WHITE}Demasiado cerca de otro aeropuerto STR_ERROR_CAN_T_RENAME_STATION :{WHITE}No se puede cambiar nombre de la estación... -STR_ERROR_DRIVE_THROUGH_ON_TOWN_ROAD :{WHITE}... esta carretera es propiedad de un pueblo +STR_ERROR_DRIVE_THROUGH_ON_TOWN_ROAD :{WHITE}... esta carretera es propiedad de una localidad STR_ERROR_DRIVE_THROUGH_DIRECTION :{WHITE}... carretera en el sentido incorrecto STR_ERROR_DRIVE_THROUGH_CORNER :{WHITE}... las estaciones y paradas intermedias no pueden ponerse sobre esquinas STR_ERROR_DRIVE_THROUGH_JUNCTION :{WHITE}... las estaciones y paradas intermedias no pueden ponerse sobre intersecciones diff --git a/src/lang/swedish.txt b/src/lang/swedish.txt index 7aa63245c3..8b3dfa73a1 100644 --- a/src/lang/swedish.txt +++ b/src/lang/swedish.txt @@ -1995,7 +1995,6 @@ STR_FACE_TIE :Slips: STR_FACE_EARRING :Örhänge: STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}Ändra slips eller örhänge -STR_NETWORK_SERVER_VISIBILITY_PRIVATE :Privat STR_NETWORK_SERVER_VISIBILITY_PUBLIC :Offentlig # Network server list @@ -2154,6 +2153,9 @@ STR_NETWORK_CLIENT_LIST_PLAYER_ICON_SELF_TOOLTIP :{BLACK}Det här STR_NETWORK_CLIENT_LIST_PLAYER_ICON_HOST_TOOLTIP :{BLACK}Det här är spelets värd STR_NETWORK_CLIENT_LIST_CLIENT_COMPANY_COUNT :{BLACK}{NUM} klient{P "" er} / {NUM} företag +############ Begin of ConnectionType +############ End of ConnectionType + STR_NETWORK_CLIENT_LIST_ADMIN_CLIENT_KICK :Kasta ut STR_NETWORK_CLIENT_LIST_ADMIN_CLIENT_BAN :Bannlys STR_NETWORK_CLIENT_LIST_ADMIN_COMPANY_RESET :Ta bort @@ -2281,6 +2283,7 @@ STR_NETWORK_MESSAGE_SERVER_SHUTDOWN :{WHITE}Servern STR_NETWORK_MESSAGE_SERVER_REBOOT :{WHITE}Servern startar om...{}Var vänlig vänta... STR_NETWORK_MESSAGE_KICKED :*** {STRING} kastades ut. Orsak: ({STRING}) + # Content downloading window STR_CONTENT_TITLE :{WHITE}Nedladdning av innehåll STR_CONTENT_TYPE_CAPTION :{BLACK}Typ diff --git a/src/lang/tamil.txt b/src/lang/tamil.txt index bef67c06c8..34fa28b7f0 100644 --- a/src/lang/tamil.txt +++ b/src/lang/tamil.txt @@ -1852,6 +1852,9 @@ STR_NETWORK_COMPANY_LIST_CLIENT_LIST :விளைய # Network client list +############ Begin of ConnectionType +############ End of ConnectionType + STR_NETWORK_SERVER :சர்வர் @@ -1960,6 +1963,7 @@ STR_NETWORK_MESSAGE_SERVER_SHUTDOWN :{WHITE}சர STR_NETWORK_MESSAGE_SERVER_REBOOT :{WHITE}சர்வர் மீண்டும் தொடங்குகிறது...{}சற்று பொறுக்கவும்... STR_NETWORK_MESSAGE_KICKED :*** {STRING} வெளியேற்றப்பட்டார். காரணம்: ({STRING}) + # Content downloading window STR_CONTENT_TITLE :{WHITE}கோப்புகள் பதிவிறக்கம் செய்யப்படுகின்றன STR_CONTENT_TYPE_CAPTION :{BLACK}வகை diff --git a/src/lang/thai.txt b/src/lang/thai.txt index 72772f5291..ee3b89f145 100644 --- a/src/lang/thai.txt +++ b/src/lang/thai.txt @@ -1941,6 +1941,9 @@ STR_NETWORK_COMPANY_LIST_CLIENT_LIST :รายกา # Network client list +############ Begin of ConnectionType +############ End of ConnectionType + STR_NETWORK_SERVER :เซิฟเวอร์ @@ -2049,6 +2052,7 @@ STR_NETWORK_MESSAGE_NAME_CHANGE :*** {STRING} STR_NETWORK_MESSAGE_SERVER_SHUTDOWN :{WHITE}เซิฟเวอร์ปืดเซสซั่นนี้ STR_NETWORK_MESSAGE_SERVER_REBOOT :{WHITE}เซิฟเวอร์กำลังทำการเริ่มต้นใหม่...{}กรุณารอซักครู่... + # Content downloading window STR_CONTENT_TITLE :{WHITE}กำลังโหลดเนื้อหา STR_CONTENT_TYPE_CAPTION :{BLACK}ประเภท diff --git a/src/lang/traditional_chinese.txt b/src/lang/traditional_chinese.txt index 90f18e8ba2..d61347a1f1 100644 --- a/src/lang/traditional_chinese.txt +++ b/src/lang/traditional_chinese.txt @@ -2006,6 +2006,9 @@ STR_NETWORK_COMPANY_LIST_CLIENT_LIST :用戶端清單 STR_NETWORK_CLIENT_LIST_CAPTION :{WHITE}多人遊戲 STR_NETWORK_CLIENT_LIST_SERVER :{BLACK}伺服器 +############ Begin of ConnectionType +############ End of ConnectionType + STR_NETWORK_CLIENT_LIST_ADMIN_CLIENT_KICK :踢出 @@ -2116,6 +2119,7 @@ STR_NETWORK_MESSAGE_SERVER_SHUTDOWN :{WHITE}伺服 STR_NETWORK_MESSAGE_SERVER_REBOOT :{WHITE}伺服器重新啟動中...{}請稍候... STR_NETWORK_MESSAGE_KICKED :*** {STRING} 已被踢出。原因:({STRING}) + # Content downloading window STR_CONTENT_TITLE :{WHITE}內容下載中 STR_CONTENT_TYPE_CAPTION :{BLACK}種類 diff --git a/src/lang/turkish.txt b/src/lang/turkish.txt index d047e8ea6e..43ec344f4e 100644 --- a/src/lang/turkish.txt +++ b/src/lang/turkish.txt @@ -1987,7 +1987,6 @@ STR_FACE_TIE :Kravat: STR_FACE_EARRING :Küpe: STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}Kravatı veya küpeyi değiştir -STR_NETWORK_SERVER_VISIBILITY_PRIVATE :Özel STR_NETWORK_SERVER_VISIBILITY_PUBLIC :Halka açık # Network server list @@ -2145,6 +2144,9 @@ STR_NETWORK_CLIENT_LIST_NEW_COMPANY_TOOLTIP :{BLACK}Yeni bir STR_NETWORK_CLIENT_LIST_PLAYER_ICON_SELF_TOOLTIP :{BLACK}Bu sensin STR_NETWORK_CLIENT_LIST_PLAYER_ICON_HOST_TOOLTIP :{BLACK}Bu, oyunun ev sahibi +############ Begin of ConnectionType +############ End of ConnectionType + STR_NETWORK_CLIENT_LIST_ADMIN_CLIENT_KICK :At STR_NETWORK_CLIENT_LIST_ADMIN_CLIENT_BAN :Yasakla STR_NETWORK_CLIENT_LIST_ADMIN_COMPANY_RESET :Sil @@ -2271,6 +2273,7 @@ STR_NETWORK_MESSAGE_SERVER_SHUTDOWN :{WHITE}Sunucu k STR_NETWORK_MESSAGE_SERVER_REBOOT :{WHITE}Sunucu baştan başlatılıyor...{}Lütfen bekleyin... STR_NETWORK_MESSAGE_KICKED :*** {STRING} atıldı. Sebep: ({STRING}) + # Content downloading window STR_CONTENT_TITLE :{WHITE}İçerik indirme STR_CONTENT_TYPE_CAPTION :{BLACK}Tür diff --git a/src/lang/ukrainian.txt b/src/lang/ukrainian.txt index 4ea3356a40..8d17d91b34 100644 --- a/src/lang/ukrainian.txt +++ b/src/lang/ukrainian.txt @@ -2238,6 +2238,9 @@ STR_NETWORK_COMPANY_LIST_CLIENT_LIST :Список к # Network client list +############ Begin of ConnectionType +############ End of ConnectionType + STR_NETWORK_SERVER :Сервер @@ -2351,6 +2354,7 @@ STR_NETWORK_MESSAGE_SERVER_SHUTDOWN :{WHITE}Серв STR_NETWORK_MESSAGE_SERVER_REBOOT :{WHITE}Перезавантаження сервера...{}Зачекайте... STR_NETWORK_MESSAGE_KICKED :*** {STRING} відключено. Причина: ({STRING}) + # Content downloading window STR_CONTENT_TITLE :{WHITE}Завантаження вмісту STR_CONTENT_TYPE_CAPTION :{BLACK}Тип diff --git a/src/lang/urdu.txt b/src/lang/urdu.txt index df5005cdba..b3bed8913d 100644 --- a/src/lang/urdu.txt +++ b/src/lang/urdu.txt @@ -1603,6 +1603,9 @@ STR_NETWORK_COMPANY_LIST_CLIENT_LIST :{WHITE}کلائ # Network client list +############ Begin of ConnectionType +############ End of ConnectionType + STR_NETWORK_SERVER :سرور @@ -1711,6 +1714,7 @@ STR_NETWORK_MESSAGE_NAME_CHANGE :*** {STRING} ن STR_NETWORK_MESSAGE_SERVER_SHUTDOWN :{WHITE}سرور نے سیشن بند کر دیا STR_NETWORK_MESSAGE_SERVER_REBOOT :{WHITE}سرور دوبارہ سے سٹارٹ ہو رہا ہے - - -{}برائے مہربانی انتظار فرمائیے ۔ ۔ ۔ + # Content downloading window STR_CONTENT_TITLE :{WHITE}مواد ڈاون لوڈ کیا جا رہا ہے STR_CONTENT_TYPE_CAPTION :{BLACK}طرذ diff --git a/src/lang/vietnamese.txt b/src/lang/vietnamese.txt index 12785f410c..8d4139fa81 100644 --- a/src/lang/vietnamese.txt +++ b/src/lang/vietnamese.txt @@ -883,6 +883,11 @@ STR_NEWS_STATION_NOW_ACCEPTS_CARGO_AND_CARGO :{WHITE}{STATION STR_NEWS_OFFER_OF_SUBSIDY_EXPIRED :{BIG_FONT}{BLACK}Lời đề nghị trợ cấp đã hết hạn:{}{}{STRING} từ {STRING} đến {STRING} bây giờ sẽ không có trợ cấp. STR_NEWS_SUBSIDY_WITHDRAWN_SERVICE :{BIG_FONT}{BLACK}Trợ cấp đã hết:{}{}Dịch vụ vận chuyển {STRING} từ {STRING} đến {STRING} sẽ không được trợ cấp nữa. +STR_NEWS_SERVICE_SUBSIDY_OFFERED :{BIG_FONT}{BLACK}Đề nghị trợ cấp:{}{}Dịch vụ vận tải {STRING} đầu tiên từ {STRING} đến {STRING} sẽ nhận được tiền trợ cấp trong {NUM} năm của chính quyền địa phương! +STR_NEWS_SERVICE_SUBSIDY_AWARDED_HALF :{BIG_FONT}{BLACK}Trợ cấp dịch vụ được trao cho {STRING}!{}{}Dịch vụ {STRING} từ {STRING} đến {STRING} sẽ thu lợi cao hơn 50% trong vòng {NUM} năm tới! +STR_NEWS_SERVICE_SUBSIDY_AWARDED_DOUBLE :{BIG_FONT}{BLACK}Trợ cấp dịch vụ được trao cho {STRING}!{}{}Dịch vụ {STRING} từ {STRING} đến {STRING} sẽ thu lợi gấp đôi trong vòng {NUM} năm tới! +STR_NEWS_SERVICE_SUBSIDY_AWARDED_TRIPLE :{BIG_FONT}{BLACK}Trợ cấp dịch vụ được trao cho {STRING}!{}{}Dịch vụ {STRING} từ {STRING} đến {STRING} sẽ thu lợi gấp ba trong vòng {NUM} năm tới! +STR_NEWS_SERVICE_SUBSIDY_AWARDED_QUADRUPLE :{BIG_FONT}{BLACK}Trợ cấp dịch vụ được trao cho {STRING}!{}{}Dịch vụ {STRING} từ {STRING} đến {STRING} sẽ thu lợi gấp tư trong vòng {NUM} năm tới! STR_NEWS_ROAD_REBUILDING :{BIG_FONT}{BLACK}Giao thông hỗn loạn tại {TOWN}!{}{}Chương trình sửa đường đầu tư bởi {STRING} mang 6 tháng khốn đốn cho người chạy mô tô! STR_NEWS_EXCLUSIVE_RIGHTS_TITLE :{BIG_FONT}{BLACK}Độc quyền vận tải! @@ -948,7 +953,7 @@ STR_GAME_OPTIONS_CURRENCY_MYR :Ringgit Malaysi STR_GAME_OPTIONS_ROAD_VEHICLES_DROPDOWN_LEFT :Lái bên trái STR_GAME_OPTIONS_ROAD_VEHICLES_DROPDOWN_RIGHT :Lái bên phải -STR_GAME_OPTIONS_TOWN_NAMES_FRAME :{BLACK}Tên thị trấn +STR_GAME_OPTIONS_TOWN_NAMES_FRAME :{BLACK}Tên thị trấn: STR_GAME_OPTIONS_TOWN_NAMES_DROPDOWN_TOOLTIP :{BLACK}Lựa chọn kiểu tên thị trấn ############ start of townname region @@ -988,6 +993,7 @@ STR_GAME_OPTIONS_AUTOSAVE_DROPDOWN_EVERY_12_MONTHS :Hàng năm STR_GAME_OPTIONS_LANGUAGE :{BLACK}Ngôn ngữ STR_GAME_OPTIONS_LANGUAGE_TOOLTIP :{BLACK}Lựa chọn sử dụng ngôn ngữ giao diện +STR_GAME_OPTIONS_LANGUAGE_PERCENTAGE :{STRING} (hoàn thành {NUM}%) STR_GAME_OPTIONS_FULLSCREEN :{BLACK}Toàn màn hình STR_GAME_OPTIONS_FULLSCREEN_TOOLTIP :{BLACK}Đánh dấu vào ô này để chơi OpenTTD ở chế độ fullscreen @@ -1001,6 +1007,8 @@ STR_GAME_OPTIONS_VIDEO_ACCELERATION :{BLACK}Tăng t STR_GAME_OPTIONS_VIDEO_ACCELERATION_TOOLTIP :{BLACK}Đánh dấu vào ô này để cho phép OpenTTD thử sử dụng tăng tốc phần cứng. Sẽ có tác dụng sau khi khởi động lại trò chơi STR_GAME_OPTIONS_VIDEO_ACCELERATION_RESTART :{WHITE}Thiết lập chỉ có tác dụng sau khi khởi động lại trò chơi +STR_GAME_OPTIONS_VIDEO_VSYNC :{BLACK}VSync +STR_GAME_OPTIONS_VIDEO_VSYNC_TOOLTIP :{BLACK}Đánh dấu vào ô này để bật tính năng v-sync cho màn hình. Sẽ có tác dụng sau khi khởi động lại trò chơi. Chỉ có thể hoạt động khi tăng tốc phần cứng được bật STR_GAME_OPTIONS_GUI_ZOOM_FRAME :{BLACK}Kích thước giao diện STR_GAME_OPTIONS_GUI_ZOOM_DROPDOWN_TOOLTIP :{BLACK}Chọn kích thước của các đối tượng trên giao diện @@ -1195,6 +1203,10 @@ STR_CONFIG_SETTING_VEHICLE_BREAKDOWNS :Mức hỏng h STR_CONFIG_SETTING_VEHICLE_BREAKDOWNS_HELPTEXT :Thiết lập mức độ hỏng hóc đối với phương tiện không bảo trì thường xuyên STR_CONFIG_SETTING_SUBSIDY_MULTIPLIER :Tỉ lệ chi trả: {STRING} STR_CONFIG_SETTING_SUBSIDY_MULTIPLIER_HELPTEXT :Thiết lập mức chi trả cho tuyến vận chuyển phụ trợ +STR_CONFIG_SETTING_SUBSIDY_DURATION :Thời hạn trợ cấp: {STRING} +STR_CONFIG_SETTING_SUBSIDY_DURATION_HELPTEXT :Đặt số năm được hưởng trợ cấp dịch vụ +STR_CONFIG_SETTING_SUBSIDY_DURATION_VALUE :{NUM} năm +STR_CONFIG_SETTING_SUBSIDY_DURATION_DISABLED :Không có trợ cấp STR_CONFIG_SETTING_CONSTRUCTION_COSTS :Chi phí xây dựng: {STRING} STR_CONFIG_SETTING_CONSTRUCTION_COSTS_HELPTEXT :Thiết lập mức độ xây dựng và chi phí mua sắm STR_CONFIG_SETTING_RECESSIONS :Suy thoái: {STRING} @@ -1648,7 +1660,7 @@ STR_CONFIG_SETTING_TOWN_CARGOGENMODE_ORIGINAL :Tỉ lệ bình STR_CONFIG_SETTING_TOWN_CARGOGENMODE_BITCOUNT :Tuyến tính STR_CONFIG_SETTING_EXTRA_TREE_PLACEMENT :Trồng cây trong trò chơi: {STRING} -STR_CONFIG_SETTING_EXTRA_TREE_PLACEMENT_HELPTEXT :Điều khiển sự xuất hiện tự dộng của cây cối khi đang chơi. Điều này có thể ảnh hưởng đến những nhà máy dựa vào cây cối, ví dự như nhà máy chế biến gỗgỗ +STR_CONFIG_SETTING_EXTRA_TREE_PLACEMENT_HELPTEXT :Điều khiển sự xuất hiện tự dộng của cây cối khi đang chơi. Điều này có thể ảnh hưởng đến những nhà máy dựa vào cây cối, ví dự như nhà máy chế biến gỗ STR_CONFIG_SETTING_EXTRA_TREE_PLACEMENT_NO_SPREAD :Mọc nhưng không trải {RED}(nhà máy chế biến gỗ sẽ không hoạt động) STR_CONFIG_SETTING_EXTRA_TREE_PLACEMENT_SPREAD_RAINFOREST :Mọc nhưng chỉ trải ở rừng nhiệt đới STR_CONFIG_SETTING_EXTRA_TREE_PLACEMENT_SPREAD_ALL :Mọc và trải mọi nơi @@ -1826,7 +1838,7 @@ STR_INTRO_LOAD_GAME :{BLACK}Nạp V STR_INTRO_PLAY_SCENARIO :{BLACK}Chơi Màn Chơi Kịch Bản STR_INTRO_PLAY_HEIGHTMAP :{BLACK}Chơi Bản Đồ Địa Hình STR_INTRO_SCENARIO_EDITOR :{BLACK}Biên Tập Màn Chơi Kịch Bản -STR_INTRO_MULTIPLAYER :{BLACK}Chơi Trên Mạng +STR_INTRO_MULTIPLAYER :{BLACK}Nhiều Người Chơi STR_INTRO_GAME_OPTIONS :{BLACK}Cấu Hình Trò Chơi STR_INTRO_HIGHSCORE :{BLACK}Bảng điểm chơi cao nhất @@ -1841,7 +1853,7 @@ STR_INTRO_TOOLTIP_LOAD_GAME :{BLACK}Tải tr STR_INTRO_TOOLTIP_PLAY_HEIGHTMAP :{BLACK}Chơi ván mới, dùng bản đồ địa hình làm nền đất STR_INTRO_TOOLTIP_PLAY_SCENARIO :{BLACK}Chơi ván mới, dùng màn chơi kịch bản theo ý riêng STR_INTRO_TOOLTIP_SCENARIO_EDITOR :{BLACK}Tạo màn chơi kịch bản/bản đồ theo ý riêng -STR_INTRO_TOOLTIP_MULTIPLAYER :{BLACK}Bắt đầu chơi trên mạng nhiều người +STR_INTRO_TOOLTIP_MULTIPLAYER :{BLACK}Bắt đầu ván chơi trên mạng nhiều người STR_INTRO_TOOLTIP_TEMPERATE :{BLACK}Chọn kiểu quang cảnh 'ôn hòa' STR_INTRO_TOOLTIP_SUB_ARCTIC_LANDSCAPE :{BLACK}Chọn kiểu quang cảnh 'giá rét' @@ -1983,6 +1995,7 @@ STR_FACE_TIE :Cà vạt: STR_FACE_EARRING :Bông tai: STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}Thay đổi cà vạt hoặc bông tai +STR_NETWORK_SERVER_VISIBILITY_PUBLIC :Công khai # Network server list STR_NETWORK_SERVER_LIST_CAPTION :{WHITE}Ván Chơi Mạng @@ -2046,6 +2059,8 @@ STR_NETWORK_START_SERVER_NEW_GAME_NAME_TOOLTIP :{BLACK}Tên c STR_NETWORK_START_SERVER_SET_PASSWORD :{BLACK}Đặt mật khẩu STR_NETWORK_START_SERVER_PASSWORD_TOOLTIP :{BLACK}Bảo vệ game của bạn bằng mật khẩu nếu bạn không muốn người khác vào tùy tiện +STR_NETWORK_START_SERVER_VISIBILITY_LABEL :{BLACK}Hiển thị +STR_NETWORK_START_SERVER_VISIBILITY_TOOLTIP :{BLACK}Cho phép người khác thấy server của bạn trong danh sách công khai STR_NETWORK_START_SERVER_CLIENTS_SELECT :{BLACK}{NUM} máy trạm STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS :{BLACK}Số máy trạm tối đa: STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS_TOOLTIP :{BLACK}Chọn số lượng máy trạm tối đa. Không nhất thiết phải chọn đầy các dòng @@ -2109,13 +2124,48 @@ STR_NETWORK_NEED_GAME_PASSWORD_CAPTION :{WHITE}Server y STR_NETWORK_NEED_COMPANY_PASSWORD_CAPTION :{WHITE}Công ty yêu cầu xác thực. Nhập mật khẩu # Network company list added strings -STR_NETWORK_COMPANY_LIST_CLIENT_LIST :Danh sách máy trạm +STR_NETWORK_COMPANY_LIST_CLIENT_LIST :Người chơi trực tuyến # Network client list +STR_NETWORK_CLIENT_LIST_CAPTION :{WHITE}Chế độ nhiều người chơi +STR_NETWORK_CLIENT_LIST_SERVER :{BLACK}Server +STR_NETWORK_CLIENT_LIST_SERVER_NAME :{BLACK}Tên +STR_NETWORK_CLIENT_LIST_SERVER_NAME_TOOLTIP :{BLACK}Tên server mà bạn đang chơi +STR_NETWORK_CLIENT_LIST_SERVER_NAME_EDIT_TOOLTIP :{BLACK}Sửa tên server +STR_NETWORK_CLIENT_LIST_SERVER_NAME_QUERY_CAPTION :Tên của server +STR_NETWORK_CLIENT_LIST_SERVER_VISIBILITY :{BLACK}Hiển thị +STR_NETWORK_CLIENT_LIST_SERVER_VISIBILITY_TOOLTIP :{BLACK}Cho phép người khác thấy server của bạn trong danh sách công khai +STR_NETWORK_CLIENT_LIST_PLAYER :{BLACK}Người chơi +STR_NETWORK_CLIENT_LIST_PLAYER_NAME :{BLACK}Tên +STR_NETWORK_CLIENT_LIST_PLAYER_NAME_TOOLTIP :{BLACK}Tên người chơi +STR_NETWORK_CLIENT_LIST_PLAYER_NAME_EDIT_TOOLTIP :{BLACK}Sửa tên người chơi +STR_NETWORK_CLIENT_LIST_PLAYER_NAME_QUERY_CAPTION :Tên người chơi +STR_NETWORK_CLIENT_LIST_ADMIN_CLIENT_TOOLTIP :{BLACK}Các tác vụ quản lý thực hiện cho client này +STR_NETWORK_CLIENT_LIST_ADMIN_COMPANY_TOOLTIP :{BLACK}Các tác vụ quản lý thực hiện cho công ty này STR_NETWORK_CLIENT_LIST_JOIN_TOOLTIP :{BLACK}Gia nhập công ty này - +STR_NETWORK_CLIENT_LIST_CHAT_CLIENT_TOOLTIP :{BLACK}Gửi tin nhắn tới người chơi này +STR_NETWORK_CLIENT_LIST_CHAT_COMPANY_TOOLTIP :{BLACK}Gửi tin nhắn tới tất cả người chơi trong công ty này +STR_NETWORK_CLIENT_LIST_CHAT_SPECTATOR_TOOLTIP :{BLACK}Gửi tin nhắn tới tất cả người xem +STR_NETWORK_CLIENT_LIST_SPECTATORS :Người xem +STR_NETWORK_CLIENT_LIST_NEW_COMPANY :(Công ty mới) +STR_NETWORK_CLIENT_LIST_NEW_COMPANY_TOOLTIP :{BLACK}Khai sinh một công ty và gia nhập +STR_NETWORK_CLIENT_LIST_PLAYER_ICON_SELF_TOOLTIP :{BLACK}Đây là bạn +STR_NETWORK_CLIENT_LIST_PLAYER_ICON_HOST_TOOLTIP :{BLACK}Đây là người tổ chức ván chơi +STR_NETWORK_CLIENT_LIST_CLIENT_COMPANY_COUNT :{BLACK}{NUM} người chơi / {NUM} công ty + +############ Begin of ConnectionType +############ End of ConnectionType + +STR_NETWORK_CLIENT_LIST_ADMIN_CLIENT_KICK :Đá ra STR_NETWORK_CLIENT_LIST_ADMIN_CLIENT_BAN :Cấm +STR_NETWORK_CLIENT_LIST_ADMIN_COMPANY_RESET :Xoá +STR_NETWORK_CLIENT_LIST_ADMIN_COMPANY_UNLOCK :Mật khẩu công ty +STR_NETWORK_CLIENT_LIST_ASK_CAPTION :{WHITE}Tác vụ quản lý +STR_NETWORK_CLIENT_LIST_ASK_CLIENT_KICK :{YELLOW}Bạn có muốn đá người chơi '{STRING}' ra? +STR_NETWORK_CLIENT_LIST_ASK_CLIENT_BAN :{YELLOW}Bạn có muốn cấm người chơi '{STRING}'? +STR_NETWORK_CLIENT_LIST_ASK_COMPANY_RESET :{YELLOW}Bạn có muốn xoá công ty '{COMPANY}'? +STR_NETWORK_CLIENT_LIST_ASK_COMPANY_UNLOCK :{YELLOW}Bạn có muốn đặt lại mật khẩu cho công ty '{COMPANY}'? STR_NETWORK_SERVER :Server STR_NETWORK_CLIENT :Máy trạm @@ -2160,6 +2210,8 @@ STR_NETWORK_ERROR_SERVER_START :{WHITE}Không t STR_NETWORK_ERROR_CLIENT_START :{WHITE}Không thể tạo kết nối STR_NETWORK_ERROR_TIMEOUT :{WHITE}Kết nối #{NUM} quá lâu STR_NETWORK_ERROR_SERVER_ERROR :{WHITE}Có lỗi trong giao thức và kết nối bị đóng +STR_NETWORK_ERROR_BAD_PLAYER_NAME :{WHITE}Bạn chưa đặt tên người chơi. Bạn có thể đặt tên này tại phía trên của cửa sổ Chơi trên mạng +STR_NETWORK_ERROR_BAD_SERVER_NAME :{WHITE}Bạn chưa đặt tên server. Bạn có thể đặt tên này tại phía trên của cửa sổ Chơi trên mạng STR_NETWORK_ERROR_WRONG_REVISION :{WHITE}Phiên bản của máy trạm không hợp với phiên bản máy server STR_NETWORK_ERROR_WRONG_PASSWORD :{WHITE}Sai mật khẩu STR_NETWORK_ERROR_SERVER_FULL :{WHITE}Server bị đầy @@ -2172,6 +2224,8 @@ STR_NETWORK_ERROR_TIMEOUT_PASSWORD :{WHITE}Bạn nh STR_NETWORK_ERROR_TIMEOUT_COMPUTER :{WHITE}Máy của bạn quá chậm để có thể theo kịp máy chủ STR_NETWORK_ERROR_TIMEOUT_MAP :{WHITE}Thời gian tải bản đồ quá lâu STR_NETWORK_ERROR_TIMEOUT_JOIN :{WHITE}Thời gian tham gia máy chủ quá lâu +STR_NETWORK_ERROR_INVALID_CLIENT_NAME :{WHITE}Tên người chơi không hợp lệ +STR_NETWORK_ERROR_SERVER_TOO_OLD :{WHITE}Server được yêu cầu dùng phiên bản cũ hơn so với client ############ Leave those lines in this order!! STR_NETWORK_ERROR_CLIENT_GENERAL :lỗi chung @@ -2194,6 +2248,7 @@ STR_NETWORK_ERROR_CLIENT_TIMEOUT_PASSWORD :không nhận STR_NETWORK_ERROR_CLIENT_TIMEOUT_COMPUTER :lỗi quá thời gian STR_NETWORK_ERROR_CLIENT_TIMEOUT_MAP :tải bản đồ quá thời gian STR_NETWORK_ERROR_CLIENT_TIMEOUT_JOIN :xử lý bản đồ quá thời gian +STR_NETWORK_ERROR_CLIENT_INVALID_CLIENT_NAME :tên client không đúng ############ End of leave-in-this-order STR_NETWORK_ERROR_CLIENT_GUI_LOST_CONNECTION_CAPTION :{WHITE}Kết nối có thể đã bị mất @@ -2222,12 +2277,13 @@ STR_NETWORK_MESSAGE_CLIENT_COMPANY_JOIN :*** {STRING} gi STR_NETWORK_MESSAGE_CLIENT_COMPANY_SPECTATE :*** {STRING} vào xem ván chơi STR_NETWORK_MESSAGE_CLIENT_COMPANY_NEW :*** {STRING} khai trương công ty mới (#{2:NUM}) STR_NETWORK_MESSAGE_CLIENT_LEFT :*** {STRING} rời bỏ ván chơi ({2:STRING}) -STR_NETWORK_MESSAGE_NAME_CHANGE :*** {STRING} đổi tên thành {STRING} +STR_NETWORK_MESSAGE_NAME_CHANGE :*** {STRING} đã đổi tên thành {STRING} STR_NETWORK_MESSAGE_GIVE_MONEY :*** {STRING} tặng {2:CURRENCY_LONG} cho {1:STRING} STR_NETWORK_MESSAGE_SERVER_SHUTDOWN :{WHITE}Server kết thúc phiên STR_NETWORK_MESSAGE_SERVER_REBOOT :{WHITE}Server khởi động lại...{}Xin chờ... STR_NETWORK_MESSAGE_KICKED :*** {STRING} đã bị đá khỏi ván chơi. Lý do: ({STRING}) + # Content downloading window STR_CONTENT_TITLE :{WHITE}Đang tải nội dung STR_CONTENT_TYPE_CAPTION :{BLACK}Kiểu @@ -3033,6 +3089,7 @@ STR_NEWGRF_ERROR_MSG_WARNING :{RED}Cảnh bá STR_NEWGRF_ERROR_MSG_ERROR :{RED}Lỗi: {SILVER}{STRING} STR_NEWGRF_ERROR_MSG_FATAL :{RED}Lỗi nghiêm trọng: {SILVER}{STRING} STR_NEWGRF_ERROR_FATAL_POPUP :{WHITE}Xảy ra lỗi NewGRF nghiêm trọng:{}{STRING} +STR_NEWGRF_ERROR_POPUP :{WHITE}Có lỗi NewGRF xảy ra:{}{STRING} STR_NEWGRF_ERROR_VERSION_NUMBER :{1:STRING} sẽ không hoạt động với phiên bản TTDPatch version theo như báo cáo của OpenTTD. STR_NEWGRF_ERROR_DOS_OR_WINDOWS :{1:STRING} để dành cho phiên bản {STRING} của TTD. STR_NEWGRF_ERROR_UNSET_SWITCH :{1:STRING} được thiết kế để xài với {STRING} @@ -3510,7 +3567,7 @@ STR_GROUP_CREATE_TOOLTIP :{BLACK}Ấn và STR_GROUP_DELETE_TOOLTIP :{BLACK}Xoá nhóm đã chọn STR_GROUP_RENAME_TOOLTIP :{BLACK}Đổi tên nhóm STR_GROUP_LIVERY_TOOLTIP :{BLACK}Thay đổi phục trang cho nhóm được chọn -STR_GROUP_REPLACE_PROTECTION_TOOLTIP :{BLACK}Không để nhóm này tự thay thế (thiết lập chung) khi hết hạn +STR_GROUP_REPLACE_PROTECTION_TOOLTIP :{BLACK}Không để nhóm này tự thay thế (thiết lập chung) khi hết hạn. Ctrl+Click để áp dụng lên nhóm con. STR_QUERY_GROUP_DELETE_CAPTION :{WHITE}Xóa Nhóm STR_GROUP_DELETE_QUERY_TEXT :{WHITE}Bạn có chắc chắn muốn xóa nhóm này và tất cả con của nó? @@ -3763,7 +3820,9 @@ STR_REPLACE_MAGLEV_VEHICLES :Đầu máy đ STR_REPLACE_ROAD_VEHICLES :Các xe ô-tô STR_REPLACE_TRAM_VEHICLES :Các xe điện +STR_REPLACE_REMOVE_WAGON :{BLACK}Xoá bỏ toa xe ({STRING}): {ORANGE}{STRING} STR_REPLACE_REMOVE_WAGON_HELP :{BLACK}Tự động thay thế sẽ giữ nguyên độ dài đoàn tàu bằng cách bỏ bớt toa xe (bỏ từ phía đầu), nếu như việc thay thế đầu máy làm đoàn tàu dài hơn. +STR_REPLACE_REMOVE_WAGON_GROUP_HELP :{STRING}. Ctrl+Click để áp dụng cho nhóm con # Vehicle view STR_VEHICLE_VIEW_CAPTION :{WHITE}{VEHICLE} diff --git a/src/lang/welsh.txt b/src/lang/welsh.txt index 695c08f651..30865c4d46 100644 --- a/src/lang/welsh.txt +++ b/src/lang/welsh.txt @@ -1998,6 +1998,9 @@ STR_NETWORK_COMPANY_LIST_CLIENT_LIST :Rhestr Cleienti # Network client list +############ Begin of ConnectionType +############ End of ConnectionType + STR_NETWORK_SERVER :Gweinydd @@ -2106,6 +2109,7 @@ STR_NETWORK_MESSAGE_NAME_CHANGE :*** Mae {STRING STR_NETWORK_MESSAGE_SERVER_SHUTDOWN :{WHITE}Fe gaewyd y sesiwn gan y gweinydd STR_NETWORK_MESSAGE_SERVER_REBOOT :{WHITE}Mae'r gweinydd yn ailgychwyn...{}Arhoswch... + # Content downloading window STR_CONTENT_TITLE :{WHITE}Llawrlwytho cynnwys STR_CONTENT_TYPE_CAPTION :{BLACK}Math From 3e4d3274517d6a5f3fede18e0e29eafcdc30d0c8 Mon Sep 17 00:00:00 2001 From: Rubidium Date: Sat, 10 Jul 2021 22:32:35 +0200 Subject: [PATCH 20/44] Codechange: use the C++ std::getenv over the POSIX/C getenv The C++ std::getenv is guaranteed thread-safe by the C++11 specification, whereas the POSIX/C getenv might not be thread-safe by the C11 specification. --- src/fileio.cpp | 6 +++--- src/strings.cpp | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/fileio.cpp b/src/fileio.cpp index c000362eff..23562cf6a5 100644 --- a/src/fileio.cpp +++ b/src/fileio.cpp @@ -822,7 +822,7 @@ static std::string GetHomeDir() find_directory(B_USER_SETTINGS_DIRECTORY, &path); return std::string(path.Path()); #else - const char *home_env = getenv("HOME"); // Stack var, shouldn't be freed + const char *home_env = std::getenv("HOME"); // Stack var, shouldn't be freed if (home_env != nullptr) return std::string(home_env); const struct passwd *pw = getpwuid(getuid()); @@ -840,7 +840,7 @@ void DetermineBasePaths(const char *exe) std::string tmp; const std::string homedir = GetHomeDir(); #ifdef USE_XDG - const char *xdg_data_home = getenv("XDG_DATA_HOME"); + const char *xdg_data_home = std::getenv("XDG_DATA_HOME"); if (xdg_data_home != nullptr) { tmp = xdg_data_home; tmp += PATHSEP; @@ -971,7 +971,7 @@ void DeterminePaths(const char *exe, bool only_local_path) #ifdef USE_XDG std::string config_home; const std::string homedir = GetHomeDir(); - const char *xdg_config_home = getenv("XDG_CONFIG_HOME"); + const char *xdg_config_home = std::getenv("XDG_CONFIG_HOME"); if (xdg_config_home != nullptr) { config_home = xdg_config_home; config_home += PATHSEP; diff --git a/src/strings.cpp b/src/strings.cpp index 423e2ebf09..7ff1cee315 100644 --- a/src/strings.cpp +++ b/src/strings.cpp @@ -1850,18 +1850,18 @@ const char *GetCurrentLocale(const char *param) { const char *env; - env = getenv("LANGUAGE"); + env = std::getenv("LANGUAGE"); if (env != nullptr) return env; - env = getenv("LC_ALL"); + env = std::getenv("LC_ALL"); if (env != nullptr) return env; if (param != nullptr) { - env = getenv(param); + env = std::getenv(param); if (env != nullptr) return env; } - return getenv("LANG"); + return std::getenv("LANG"); } #else const char *GetCurrentLocale(const char *param); From e99134654b4609d37d03098cb34d6b692fead9da Mon Sep 17 00:00:00 2001 From: Rubidium Date: Sat, 10 Jul 2021 22:16:03 +0200 Subject: [PATCH 21/44] Feature: allow setting (game) coordinator and content server connection strings using environment variables OTTD_COORDINATOR_CS for the game coordinator defaults to coordinator.openttd.org:3976 OTTD_CONTENT_SERVER_CS for the content server defaults to content.openttd.org:3978 OTTD_CONTENT_MIRROR_CS for the content mirror server defaults to binaries.openttd.org:80 --- src/network/core/CMakeLists.txt | 1 + src/network/core/config.cpp | 59 +++++++++++++++++++++++++++++ src/network/core/config.h | 10 ++--- src/network/network_content.cpp | 4 +- src/network/network_coordinator.cpp | 2 +- 5 files changed, 67 insertions(+), 9 deletions(-) create mode 100644 src/network/core/config.cpp diff --git a/src/network/core/CMakeLists.txt b/src/network/core/CMakeLists.txt index 6eb255f6fb..82bf7843d0 100644 --- a/src/network/core/CMakeLists.txt +++ b/src/network/core/CMakeLists.txt @@ -1,6 +1,7 @@ add_files( address.cpp address.h + config.cpp config.h core.cpp core.h diff --git a/src/network/core/config.cpp b/src/network/core/config.cpp new file mode 100644 index 0000000000..c5fe3adbd0 --- /dev/null +++ b/src/network/core/config.cpp @@ -0,0 +1,59 @@ +/* + * This file is part of OpenTTD. + * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. + * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see . + */ + +/** + * @file config.cpp Configuration of the connection strings for network stuff using environment variables. + */ + +#include "../../stdafx.h" + +#include +#include "../../string_func.h" + +#include "../../safeguards.h" + +/** + * Get the environment variable using std::getenv and when it is an empty string (or nullptr), return a fallback value instead. + * @param variable The environment variable to read from. + * @param fallback The fallback in case the environment variable is not set. + * @return The environment value, or when that does not exist the given fallback value. + */ +static const char *GetEnv(const char *variable, const char *fallback) +{ + const char *value = std::getenv(variable); + return StrEmpty(value) ? fallback : value; +} + +/** + * Get the connection string for the game coordinator from the environment variable OTTD_COORDINATOR_CS, + * or when it has not been set a hard coded default DNS hostname of the production server. + * @return The game coordinator's connection string. + */ +const char *NetworkCoordinatorConnectionString() +{ + return GetEnv("OTTD_COORDINATOR_CS", "coordinator.openttd.org"); +} + +/** + * Get the connection string for the content server from the environment variable OTTD_CONTENT_SERVER_CS, + * or when it has not been set a hard coded default DNS hostname of the production server. + * @return The game coordinator's connection string. + */ +const char *NetworkContentServerConnectionString() +{ + return GetEnv("OTTD_CONTENT_SERVER_CS", "content.openttd.org"); +} + +/** + * Get the connection string for the content mirror from the environment variable OTTD_CONTENT_MIRROR_CS, + * or when it has not been set a hard coded default DNS hostname of the production server. + * @return The game coordinator's connection string. + */ +const char *NetworkContentMirrorConnectionString() +{ + return GetEnv("OTTD_CONTENT_MIRROR_CS", "binaries.openttd.org"); +} diff --git a/src/network/core/config.h b/src/network/core/config.h index ab1c3082eb..d64dec2d9e 100644 --- a/src/network/core/config.h +++ b/src/network/core/config.h @@ -12,12 +12,10 @@ #ifndef NETWORK_CORE_CONFIG_H #define NETWORK_CORE_CONFIG_H -/** DNS hostname of the Game Coordinator server */ -static const char * const NETWORK_COORDINATOR_SERVER_HOST = "coordinator.openttd.org"; -/** DNS hostname of the content server */ -static const char * const NETWORK_CONTENT_SERVER_HOST = "content.openttd.org"; -/** DNS hostname of the HTTP-content mirror server */ -static const char * const NETWORK_CONTENT_MIRROR_HOST = "binaries.openttd.org"; +const char *NetworkCoordinatorConnectionString(); +const char *NetworkContentServerConnectionString(); +const char *NetworkContentMirrorConnectionString(); + /** URL of the HTTP mirror system */ static const char * const NETWORK_CONTENT_MIRROR_URL = "/bananas"; diff --git a/src/network/network_content.cpp b/src/network/network_content.cpp index eff54885de..13172f9ea4 100644 --- a/src/network/network_content.cpp +++ b/src/network/network_content.cpp @@ -342,7 +342,7 @@ void ClientNetworkContentSocketHandler::DownloadSelectedContentHTTP(const Conten this->http_response_index = -1; - new NetworkHTTPContentConnecter(NETWORK_CONTENT_MIRROR_HOST, this, NETWORK_CONTENT_MIRROR_URL, content_request); + new NetworkHTTPContentConnecter(NetworkContentMirrorConnectionString(), this, NETWORK_CONTENT_MIRROR_URL, content_request); /* NetworkHTTPContentConnecter takes over freeing of content_request! */ } @@ -774,7 +774,7 @@ void ClientNetworkContentSocketHandler::Connect() { if (this->sock != INVALID_SOCKET || this->isConnecting) return; this->isConnecting = true; - new NetworkContentConnecter(NETWORK_CONTENT_SERVER_HOST); + new NetworkContentConnecter(NetworkContentServerConnectionString()); } /** diff --git a/src/network/network_coordinator.cpp b/src/network/network_coordinator.cpp index b9299d549f..5f1f9f9d1c 100644 --- a/src/network/network_coordinator.cpp +++ b/src/network/network_coordinator.cpp @@ -159,7 +159,7 @@ void ClientNetworkCoordinatorSocketHandler::Connect() this->connecting = true; this->last_activity = std::chrono::steady_clock::now(); - new NetworkCoordinatorConnecter(NETWORK_COORDINATOR_SERVER_HOST); + new NetworkCoordinatorConnecter(NetworkCoordinatorConnectionString()); } NetworkRecvStatus ClientNetworkCoordinatorSocketHandler::CloseConnection(bool error) From f6955a304c52e4755e9dfcec5fe94370aef1896d Mon Sep 17 00:00:00 2001 From: Rubidium Date: Sun, 11 Jul 2021 09:16:54 +0200 Subject: [PATCH 22/44] Fix: ensure no more than the allowed number of NewGRFs are loaded from the configuration --- src/settings.cpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/settings.cpp b/src/settings.cpp index d3d93eadd9..a90dad5378 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -956,6 +956,7 @@ static GRFConfig *GRFLoadConfig(IniFile &ini, const char *grpname, bool is_stati if (group == nullptr) return nullptr; + uint num_grfs = 0; for (item = group->item; item != nullptr; item = item->next) { GRFConfig *c = nullptr; @@ -1030,8 +1031,14 @@ static GRFConfig *GRFLoadConfig(IniFile &ini, const char *grpname, bool is_stati continue; } - /* Mark file as static to avoid saving in savegame. */ - if (is_static) SetBit(c->flags, GCF_STATIC); + if (is_static) { + /* Mark file as static to avoid saving in savegame. */ + SetBit(c->flags, GCF_STATIC); + } else if (++num_grfs > NETWORK_MAX_GRF_COUNT) { + /* Check we will not load more non-static NewGRFs than allowed. This could trigger issues for game servers. */ + ShowErrorMessage(STR_CONFIG_ERROR, STR_NEWGRF_ERROR_TOO_MANY_NEWGRFS_LOADED, WL_CRITICAL); + break; + } /* Add item to list */ *curr = c; From a4987233454b65c7ebcee37fb22e37c13638476d Mon Sep 17 00:00:00 2001 From: rubidium42 Date: Sun, 11 Jul 2021 11:23:47 +0200 Subject: [PATCH 23/44] Remove: arbitrary limit on number of statically loaded NewGRFs (#9431) --- src/newgrf.cpp | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/newgrf.cpp b/src/newgrf.cpp index 023ad98901..17e5ee6a00 100644 --- a/src/newgrf.cpp +++ b/src/newgrf.cpp @@ -80,7 +80,6 @@ static uint32 _ttdpatch_flags[8]; GRFLoadedFeatures _loaded_newgrf_features; static const uint MAX_SPRITEGROUP = UINT8_MAX; ///< Maximum GRF-local ID for a spritegroup. -static const uint MAX_GRF_COUNT = 128; ///< Maximum number of NewGRF files that could be loaded. /** Temporary data during loading of GRFs */ struct GrfProcessingState { @@ -9854,14 +9853,6 @@ void LoadNewGRF(uint load_index, uint num_baseset) num_non_static++; } - if (num_grfs >= MAX_GRF_COUNT) { - Debug(grf, 0, "'{}' is not loaded as the maximum number of file slots has been reached", c->filename); - c->status = GCS_DISABLED; - c->error = new GRFError(STR_NEWGRF_ERROR_MSG_FATAL, STR_NEWGRF_ERROR_TOO_MANY_NEWGRFS_LOADED); - continue; - } - num_grfs++; - LoadNewGRFFile(c, stage, subdir, false); if (stage == GLS_RESERVE) { SetBit(c->flags, GCF_RESERVED); From 6f0c6fb2aee54ac1b05cecd3f2e205b77ee4d0fc Mon Sep 17 00:00:00 2001 From: Patric Stout Date: Sun, 11 Jul 2021 11:39:24 +0200 Subject: [PATCH 24/44] Fix a4987233: NewGRFs could no longer be loaded from the NewGRF folder. This statement was removed by accident, as it felt it could be removed. But it is used to know if the NewGRF is from the baseset folder or from the NewGRF folder. --- src/newgrf.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/newgrf.cpp b/src/newgrf.cpp index 17e5ee6a00..d5dfc427c6 100644 --- a/src/newgrf.cpp +++ b/src/newgrf.cpp @@ -9853,6 +9853,8 @@ void LoadNewGRF(uint load_index, uint num_baseset) num_non_static++; } + num_grfs++; + LoadNewGRFFile(c, stage, subdir, false); if (stage == GLS_RESERVE) { SetBit(c->flags, GCF_RESERVED); From f4dd2d88c721c085376f59908097500bc5f0c143 Mon Sep 17 00:00:00 2001 From: Patric Stout Date: Sun, 9 May 2021 19:07:58 +0200 Subject: [PATCH 25/44] Feature: raise the maximum NewGRF limit to 255 --- src/network/core/config.h | 15 +++++++++++++-- src/network/network_coordinator.cpp | 2 +- src/network/network_server.cpp | 4 ++-- 3 files changed, 16 insertions(+), 5 deletions(-) diff --git a/src/network/core/config.h b/src/network/core/config.h index d64dec2d9e..d37210e197 100644 --- a/src/network/core/config.h +++ b/src/network/core/config.h @@ -73,9 +73,20 @@ static const uint NETWORK_GRF_NAME_LENGTH = 80; ///< Maxim /** * Maximum number of GRFs that can be sent. - * This limit is reached when PACKET_UDP_SERVER_RESPONSE reaches the maximum size of UDP_MTU bytes. + * + * This limit exists to avoid that the SERVER_INFO packet exceeding the + * maximum MTU. At the time of writing this limit is 32767 (TCP_MTU). + * + * In the SERVER_INFO packet is the NetworkGameInfo struct, which is + * 142 bytes + 100 per NewGRF (under the assumption strings are used to + * their max). This brings us to roughly 326 possible NewGRFs. Round it + * down so people don't freak out because they see a weird value, and you + * get the limit: 255. + * + * PS: in case you ever want to raise this number, please be mindful that + * "amount of NewGRFs" in NetworkGameInfo is currently an uint8. */ -static const uint NETWORK_MAX_GRF_COUNT = 62; +static const uint NETWORK_MAX_GRF_COUNT = 255; /** * The number of landscapes in OpenTTD. diff --git a/src/network/network_coordinator.cpp b/src/network/network_coordinator.cpp index 5f1f9f9d1c..b3d3049524 100644 --- a/src/network/network_coordinator.cpp +++ b/src/network/network_coordinator.cpp @@ -205,7 +205,7 @@ void ClientNetworkCoordinatorSocketHandler::SendServerUpdate() Debug(net, 6, "Sending server update to Game Coordinator"); this->next_update = std::chrono::steady_clock::now() + NETWORK_COORDINATOR_DELAY_BETWEEN_UPDATES; - Packet *p = new Packet(PACKET_COORDINATOR_SERVER_UPDATE); + Packet *p = new Packet(PACKET_COORDINATOR_SERVER_UPDATE, TCP_MTU); p->Send_uint8(NETWORK_COORDINATOR_VERSION); SerializeNetworkGameInfo(p, GetCurrentNetworkServerGameInfo()); diff --git a/src/network/network_server.cpp b/src/network/network_server.cpp index 46d6918acd..25ce602f0a 100644 --- a/src/network/network_server.cpp +++ b/src/network/network_server.cpp @@ -354,7 +354,7 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::SendClientInfo(NetworkClientIn /** Send the client information about the server. */ NetworkRecvStatus ServerNetworkGameSocketHandler::SendGameInfo() { - Packet *p = new Packet(PACKET_SERVER_GAME_INFO); + Packet *p = new Packet(PACKET_SERVER_GAME_INFO, TCP_MTU); SerializeNetworkGameInfo(p, GetCurrentNetworkServerGameInfo()); this->SendPacket(p); @@ -470,7 +470,7 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::SendError(NetworkErrorCode err /** Send the check for the NewGRFs. */ NetworkRecvStatus ServerNetworkGameSocketHandler::SendNewGRFCheck() { - Packet *p = new Packet(PACKET_SERVER_CHECK_NEWGRFS); + Packet *p = new Packet(PACKET_SERVER_CHECK_NEWGRFS, TCP_MTU); const GRFConfig *c; uint grf_count = 0; From cee8174d02e38542548fc74de93450cfebefaa91 Mon Sep 17 00:00:00 2001 From: Patric Stout Date: Wed, 28 Apr 2021 14:36:14 +0200 Subject: [PATCH 26/44] Codechange: track servers with a ServerAddress instead of a NetworkAddress This allows future extensions to have different ways of referencing a server, instead of forcing to use IP:port. --- src/network/core/address.cpp | 18 +++++++++++++++++ src/network/core/address.h | 34 ++++++++++++++++++++++++++++++++ src/network/core/tcp.h | 12 +++++++++++ src/network/core/tcp_connect.cpp | 20 +++++++++++++++++++ src/network/network.cpp | 31 +++++++---------------------- src/network/network_gamelist.cpp | 4 ++-- 6 files changed, 93 insertions(+), 26 deletions(-) diff --git a/src/network/core/address.cpp b/src/network/core/address.cpp index 0c16a2c3c2..113dae6865 100644 --- a/src/network/core/address.cpp +++ b/src/network/core/address.cpp @@ -10,6 +10,7 @@ #include "../../stdafx.h" #include "address.h" +#include "../network_internal.h" #include "../../debug.h" #include "../../safeguards.h" @@ -411,3 +412,20 @@ void NetworkAddress::Listen(int socktype, SocketList *sockets) getpeername(sock, (sockaddr *)&addr, &addr_len); return NetworkAddress(addr, addr_len).GetAddressAsString(); } + +/** + * Convert a string containing either "hostname" or "hostname:ip" to a + * ServerAddress, where the string can be postfixed with "#company" to + * indicate the requested company. + * + * @param connection_string The string to parse. + * @param default_port The default port to set port to if not in connection_string. + * @param company Pointer to the company variable to set iff indicted. + * @return A valid ServerAddress of the parsed information. + */ +/* static */ ServerAddress ServerAddress::Parse(const std::string &connection_string, uint16 default_port, CompanyID *company_id) +{ + uint16 port = default_port; + std::string_view ip = ParseFullConnectionString(connection_string, port, company_id); + return ServerAddress(SERVER_ADDRESS_DIRECT, std::string(ip) + ":" + std::to_string(port)); +} diff --git a/src/network/core/address.h b/src/network/core/address.h index af10a27568..b22bcac0e7 100644 --- a/src/network/core/address.h +++ b/src/network/core/address.h @@ -12,6 +12,7 @@ #include "os_abstraction.h" #include "config.h" +#include "../../company_type.h" #include "../../string_func.h" #include "../../core/smallmap_type.hpp" @@ -177,4 +178,37 @@ public: static const std::string GetPeerName(SOCKET sock); }; +/** + * Types of server addresses we know. + * + * Sorting will prefer entries at the top of this list above ones at the bottom. + */ +enum ServerAddressType { + SERVER_ADDRESS_DIRECT, ///< Server-address is based on an hostname:port. +}; + +/** + * Address to a game server. + * + * This generalises addresses which are based on different identifiers. + */ +class ServerAddress { +private: + /** + * Create a new ServerAddress object. + * + * Please use ServerAddress::Parse() instead of calling this directly. + * + * @param type The type of the ServerAdress. + * @param connection_string The connection_string that belongs to this ServerAddress type. + */ + ServerAddress(ServerAddressType type, const std::string &connection_string) : type(type), connection_string(connection_string) {} + +public: + ServerAddressType type; ///< The type of this ServerAddress. + std::string connection_string; ///< The connection string for this ServerAddress. + + static ServerAddress Parse(const std::string &connection_string, uint16 default_port, CompanyID *company_id = nullptr); +}; + #endif /* NETWORK_CORE_ADDRESS_H */ diff --git a/src/network/core/tcp.h b/src/network/core/tcp.h index 3b217cb2e1..379ef8b926 100644 --- a/src/network/core/tcp.h +++ b/src/network/core/tcp.h @@ -103,9 +103,14 @@ private: void Connect(addrinfo *address); bool CheckActivity(); + /* We do not want any other derived classes from this class being able to + * access these private members, but it is okay for TCPServerConnecter. */ + friend class TCPServerConnecter; + static void ResolveThunk(TCPConnecter *connecter); public: + TCPConnecter() {}; TCPConnecter(const std::string &connection_string, uint16 default_port); virtual ~TCPConnecter(); @@ -124,4 +129,11 @@ public: static void KillAll(); }; +class TCPServerConnecter : public TCPConnecter { +public: + ServerAddress server_address; ///< Address we are connecting to. + + TCPServerConnecter(const std::string &connection_string, uint16 default_port); +}; + #endif /* NETWORK_CORE_TCP_H */ diff --git a/src/network/core/tcp_connect.cpp b/src/network/core/tcp_connect.cpp index dc7a23bb38..bb96e33f9e 100644 --- a/src/network/core/tcp_connect.cpp +++ b/src/network/core/tcp_connect.cpp @@ -33,6 +33,26 @@ TCPConnecter::TCPConnecter(const std::string &connection_string, uint16 default_ _tcp_connecters.push_back(this); } +/** + * Create a new connecter for the server. + * @param connection_string The address to connect to. + * @param default_port If not indicated in connection_string, what port to use. + */ +TCPServerConnecter::TCPServerConnecter(const std::string &connection_string, uint16 default_port) : + server_address(ServerAddress::Parse(connection_string, default_port)) +{ + switch (this->server_address.type) { + case SERVER_ADDRESS_DIRECT: + this->connection_string = this->server_address.connection_string; + break; + + default: + NOT_REACHED(); + } + + _tcp_connecters.push_back(this); +} + TCPConnecter::~TCPConnecter() { if (this->resolve_thread.joinable()) { diff --git a/src/network/network.cpp b/src/network/network.cpp index 395c374b85..4e48ce3512 100644 --- a/src/network/network.cpp +++ b/src/network/network.cpp @@ -531,23 +531,6 @@ NetworkAddress ParseConnectionString(const std::string &connection_string, uint1 return NetworkAddress(ip, port); } -/** - * 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 connection_string The string to parse. - * @param default_port The default port to set port to if not in connection_string. - * @param company Pointer to the company variable to set iff indicted. - * @return A valid NetworkAddress of the parsed information. - */ -static NetworkAddress ParseGameConnectionString(const std::string &connection_string, uint16 default_port, CompanyID *company) -{ - uint16 port = default_port; - std::string_view ip = ParseFullConnectionString(connection_string, port, company); - return NetworkAddress(ip, port); -} - /** * Handle the accepting of a connection to the server. * @param s The socket of the new connection. @@ -624,12 +607,12 @@ static void NetworkInitialize(bool close_admins = true) } /** Non blocking connection to query servers for their game info. */ -class TCPQueryConnecter : TCPConnecter { +class TCPQueryConnecter : TCPServerConnecter { private: std::string connection_string; public: - TCPQueryConnecter(const std::string &connection_string) : TCPConnecter(connection_string, NETWORK_DEFAULT_PORT), connection_string(connection_string) {} + TCPQueryConnecter(const std::string &connection_string) : TCPServerConnecter(connection_string, NETWORK_DEFAULT_PORT), connection_string(connection_string) {} void OnFailure() override { @@ -661,12 +644,12 @@ void NetworkQueryServer(const std::string &connection_string) } /** Non blocking connection to query servers for their game and company info. */ -class TCPLobbyQueryConnecter : TCPConnecter { +class TCPLobbyQueryConnecter : TCPServerConnecter { private: std::string connection_string; public: - TCPLobbyQueryConnecter(const std::string &connection_string) : TCPConnecter(connection_string, NETWORK_DEFAULT_PORT), connection_string(connection_string) {} + TCPLobbyQueryConnecter(const std::string &connection_string) : TCPServerConnecter(connection_string, NETWORK_DEFAULT_PORT), connection_string(connection_string) {} void OnFailure() override { @@ -756,12 +739,12 @@ void NetworkRebuildHostList() } /** Non blocking connection create to actually connect to servers */ -class TCPClientConnecter : TCPConnecter { +class TCPClientConnecter : TCPServerConnecter { private: std::string connection_string; public: - TCPClientConnecter(const std::string &connection_string) : TCPConnecter(connection_string, NETWORK_DEFAULT_PORT), connection_string(connection_string) {} + TCPClientConnecter(const std::string &connection_string) : TCPServerConnecter(connection_string, NETWORK_DEFAULT_PORT), connection_string(connection_string) {} void OnFailure() override { @@ -797,7 +780,7 @@ public: bool NetworkClientConnectGame(const std::string &connection_string, CompanyID default_company, const std::string &join_server_password, const std::string &join_company_password) { CompanyID join_as = default_company; - std::string resolved_connection_string = ParseGameConnectionString(connection_string, NETWORK_DEFAULT_PORT, &join_as).GetAddressAsString(false); + std::string resolved_connection_string = ServerAddress::Parse(connection_string, NETWORK_DEFAULT_PORT, &join_as).connection_string; if (!_network_available) return false; if (!NetworkValidateOurClientName()) return false; diff --git a/src/network/network_gamelist.cpp b/src/network/network_gamelist.cpp index 92964bf25a..d4843ff673 100644 --- a/src/network/network_gamelist.cpp +++ b/src/network/network_gamelist.cpp @@ -26,7 +26,7 @@ int _network_game_list_version = 0; ///< Current version of all items in the lis /** * Add a new item to the linked gamelist. If the IP and Port match * return the existing item instead of adding it again - * @param address the address of the to-be added item + * @param connection_string the address of the to-be added item * @return a point to the newly added or already existing item */ NetworkGameList *NetworkGameListAddItem(const std::string &connection_string) @@ -34,7 +34,7 @@ NetworkGameList *NetworkGameListAddItem(const std::string &connection_string) NetworkGameList *item, *prev_item; /* Parse the connection string to ensure the default port is there. */ - const std::string resolved_connection_string = ParseConnectionString(connection_string, NETWORK_DEFAULT_PORT).GetAddressAsString(false); + const std::string resolved_connection_string = ServerAddress::Parse(connection_string, NETWORK_DEFAULT_PORT).connection_string; prev_item = nullptr; for (item = _network_game_list; item != nullptr; item = item->next) { From 1baec41542780cf4fc898df7d2fc9925d823fb44 Mon Sep 17 00:00:00 2001 From: Patric Stout Date: Sun, 11 Jul 2021 12:08:03 +0200 Subject: [PATCH 27/44] Change: groundwork to allow ServerAddress to use invite codes Normally TCPConnecter will do a DNS resolving of the connection_string and connect to it. But for SERVER_ADDRESS_INVITE_CODE this is different: the Game Coordinator does the "resolving". This means we need to allow TCPConnecter to not setup a connection and allow it to be told when a connection has been setup by an external (to TCPConnecter) part of the code. We do this by telling the (active) socket for the connection. This means the rest of the code doesn't need to know the TCPConnecter is not doing a simple resolve+connect. The rest of the code only cares the connection is established; not how it was established. --- src/network/core/address.cpp | 11 +++-- src/network/core/address.h | 1 + src/network/core/tcp.h | 14 +++++- src/network/core/tcp_connect.cpp | 75 +++++++++++++++++++++++++++++++- src/network/network.cpp | 59 +++++++++++++++---------- src/network/network_internal.h | 1 + 6 files changed, 133 insertions(+), 28 deletions(-) diff --git a/src/network/core/address.cpp b/src/network/core/address.cpp index 113dae6865..4c090c14a6 100644 --- a/src/network/core/address.cpp +++ b/src/network/core/address.cpp @@ -414,17 +414,22 @@ void NetworkAddress::Listen(int socktype, SocketList *sockets) } /** - * Convert a string containing either "hostname" or "hostname:ip" to a - * ServerAddress, where the string can be postfixed with "#company" to + * Convert a string containing either "hostname", "hostname:port" or invite code + * to a ServerAddress, where the string can be postfixed with "#company" to * indicate the requested company. * * @param connection_string The string to parse. * @param default_port The default port to set port to if not in connection_string. - * @param company Pointer to the company variable to set iff indicted. + * @param company Pointer to the company variable to set iff indicated. * @return A valid ServerAddress of the parsed information. */ /* static */ ServerAddress ServerAddress::Parse(const std::string &connection_string, uint16 default_port, CompanyID *company_id) { + if (StrStartsWith(connection_string, "+")) { + std::string_view invite_code = ParseCompanyFromConnectionString(connection_string, company_id); + return ServerAddress(SERVER_ADDRESS_INVITE_CODE, std::string(invite_code)); + } + uint16 port = default_port; std::string_view ip = ParseFullConnectionString(connection_string, port, company_id); return ServerAddress(SERVER_ADDRESS_DIRECT, std::string(ip) + ":" + std::to_string(port)); diff --git a/src/network/core/address.h b/src/network/core/address.h index b22bcac0e7..9e09632d3d 100644 --- a/src/network/core/address.h +++ b/src/network/core/address.h @@ -185,6 +185,7 @@ public: */ enum ServerAddressType { SERVER_ADDRESS_DIRECT, ///< Server-address is based on an hostname:port. + SERVER_ADDRESS_INVITE_CODE, ///< Server-address is based on an invite code. }; /** diff --git a/src/network/core/tcp.h b/src/network/core/tcp.h index 379ef8b926..bbd0bc2a91 100644 --- a/src/network/core/tcp.h +++ b/src/network/core/tcp.h @@ -82,10 +82,12 @@ private: RESOLVING, ///< The hostname is being resolved (threaded). FAILURE, ///< Resolving failed. CONNECTING, ///< We are currently connecting. + CONNECTED, ///< The connection is established. }; std::thread resolve_thread; ///< Thread used during resolving. std::atomic status = Status::INIT; ///< The current status of the connecter. + std::atomic killed = false; ///< Whether this connecter is marked as killed. addrinfo *ai = nullptr; ///< getaddrinfo() allocated linked-list of resolved addresses. std::vector addresses; ///< Addresses we can connect to. @@ -101,7 +103,7 @@ private: void OnResolved(addrinfo *ai); bool TryNextAddress(); void Connect(addrinfo *address); - bool CheckActivity(); + virtual bool CheckActivity(); /* We do not want any other derived classes from this class being able to * access these private members, but it is okay for TCPServerConnecter. */ @@ -125,15 +127,25 @@ public: */ virtual void OnFailure() {} + void Kill(); + static void CheckCallbacks(); static void KillAll(); }; class TCPServerConnecter : public TCPConnecter { +private: + SOCKET socket = INVALID_SOCKET; ///< The socket when a connection is established. + + bool CheckActivity() override; + public: ServerAddress server_address; ///< Address we are connecting to. TCPServerConnecter(const std::string &connection_string, uint16 default_port); + + void SetConnected(SOCKET sock); + void SetFailure(); }; #endif /* NETWORK_CORE_TCP_H */ diff --git a/src/network/core/tcp_connect.cpp b/src/network/core/tcp_connect.cpp index bb96e33f9e..d9b6bb7818 100644 --- a/src/network/core/tcp_connect.cpp +++ b/src/network/core/tcp_connect.cpp @@ -46,6 +46,12 @@ TCPServerConnecter::TCPServerConnecter(const std::string &connection_string, uin this->connection_string = this->server_address.connection_string; break; + case SERVER_ADDRESS_INVITE_CODE: + this->status = Status::CONNECTING; + + // TODO -- The next commit will add this functionality. + break; + default: NOT_REACHED(); } @@ -68,6 +74,16 @@ TCPConnecter::~TCPConnecter() if (this->ai != nullptr) freeaddrinfo(this->ai); } +/** + * Kill this connecter. + * It will abort as soon as it can and not call any of the callbacks. + */ +void TCPConnecter::Kill() +{ + /* Delay the removing of the socket till the next CheckActivity(). */ + this->killed = true; +} + /** * Start a connection to the indicated address. * @param address The address to connection to. @@ -239,7 +255,9 @@ void TCPConnecter::Resolve() */ bool TCPConnecter::CheckActivity() { - switch (this->status.load()) { + if (this->killed) return true; + + switch (this->status) { case Status::INIT: /* Start the thread delayed, so the vtable is loaded. This allows classes * to overload functions used by Resolve() (in case threading is disabled). */ @@ -266,6 +284,7 @@ bool TCPConnecter::CheckActivity() return true; case Status::CONNECTING: + case Status::CONNECTED: break; } @@ -364,9 +383,63 @@ bool TCPConnecter::CheckActivity() } this->OnConnect(connected_socket); + this->status = Status::CONNECTED; return true; } +/** + * Check if there was activity for this connecter. + * @return True iff the TCPConnecter is done and can be cleaned up. + */ +bool TCPServerConnecter::CheckActivity() +{ + if (this->killed) return true; + + switch (this->server_address.type) { + case SERVER_ADDRESS_DIRECT: + return TCPConnecter::CheckActivity(); + + case SERVER_ADDRESS_INVITE_CODE: + /* Check if a result has come in. */ + switch (this->status) { + case Status::FAILURE: + this->OnFailure(); + return true; + + case Status::CONNECTED: + this->OnConnect(this->socket); + return true; + + default: + break; + } + + return false; + + default: + NOT_REACHED(); + } +} + +/** + * The connection was successfully established. + * This socket is fully setup and ready to send/recv game protocol packets. + * @param sock The socket of the established connection. + */ +void TCPServerConnecter::SetConnected(SOCKET sock) +{ + this->socket = sock; + this->status = Status::CONNECTED; +} + +/** + * The connection couldn't be established. + */ +void TCPServerConnecter::SetFailure() +{ + this->status = Status::FAILURE; +} + /** * Check whether we need to call the callback, i.e. whether we * have connected or aborted and call the appropriate callback diff --git a/src/network/network.cpp b/src/network/network.cpp index 4e48ce3512..3a33e50962 100644 --- a/src/network/network.cpp +++ b/src/network/network.cpp @@ -452,6 +452,41 @@ static void CheckPauseOnJoin() CheckPauseHelper(NetworkHasJoiningClient(), PM_PAUSED_JOIN); } +/** + * Parse the company part ("#company" postfix) of a connecting string. + * @param connection_string The string with the connection data. + * @param company_id The company ID to set, if available. + * @return A std::string_view into the connection string without the company part. + */ +std::string_view ParseCompanyFromConnectionString(const std::string &connection_string, CompanyID *company_id) +{ + std::string_view ip = connection_string; + if (company_id == nullptr) return ip; + + size_t offset = ip.find_last_of('#'); + if (offset != std::string::npos) { + std::string_view company_string = ip.substr(offset + 1); + ip = ip.substr(0, offset); + + uint8 company_value; + auto [_, err] = std::from_chars(company_string.data(), company_string.data() + company_string.size(), company_value); + if (err == std::errc()) { + if (company_value != COMPANY_NEW_COMPANY && company_value != COMPANY_SPECTATOR) { + if (company_value > MAX_COMPANIES || company_value == 0) { + *company_id = COMPANY_SPECTATOR; + } else { + /* "#1" means the first company, which has index 0. */ + *company_id = (CompanyID)(company_value - 1); + } + } else { + *company_id = (CompanyID)company_value; + } + } + } + + return ip; +} + /** * Converts a string to ip/port/company * Format: IP:port#company @@ -469,29 +504,7 @@ static void CheckPauseOnJoin() */ std::string_view ParseFullConnectionString(const std::string &connection_string, uint16 &port, CompanyID *company_id) { - std::string_view ip = connection_string; - if (company_id != nullptr) { - size_t offset = ip.find_last_of('#'); - if (offset != std::string::npos) { - std::string_view company_string = ip.substr(offset + 1); - ip = ip.substr(0, offset); - - uint8 company_value; - auto [_, err] = std::from_chars(company_string.data(), company_string.data() + company_string.size(), company_value); - if (err == std::errc()) { - if (company_value != COMPANY_NEW_COMPANY && company_value != COMPANY_SPECTATOR) { - if (company_value > MAX_COMPANIES || company_value == 0) { - *company_id = COMPANY_SPECTATOR; - } else { - /* "#1" means the first company, which has index 0. */ - *company_id = (CompanyID)(company_value - 1); - } - } else { - *company_id = (CompanyID)company_value; - } - } - } - } + std::string_view ip = ParseCompanyFromConnectionString(connection_string, company_id); size_t port_offset = ip.find_last_of(':'); size_t ipv6_close = ip.find_last_of(']'); diff --git a/src/network/network_internal.h b/src/network/network_internal.h index c1e6aa7b94..95226286c5 100644 --- a/src/network/network_internal.h +++ b/src/network/network_internal.h @@ -122,6 +122,7 @@ StringID GetNetworkErrorMsg(NetworkErrorCode err); bool NetworkMakeClientNameUnique(std::string &new_name); std::string GenerateCompanyPasswordHash(const std::string &password, const std::string &password_server_id, uint32 password_game_seed); +std::string_view ParseCompanyFromConnectionString(const std::string &connection_string, CompanyID *company_id); NetworkAddress ParseConnectionString(const std::string &connection_string, uint16 default_port); std::string NormalizeConnectionString(const std::string &connection_string, uint16 default_port); From e4d216e44b4a5d87094b4478ea4cf18109f99a35 Mon Sep 17 00:00:00 2001 From: Patric Stout Date: Thu, 29 Apr 2021 15:37:02 +0200 Subject: [PATCH 28/44] Feature: join servers based on their invite code This removes the need to know a server IP to join it. Invite codes are small (~7 characters) indentifiers for servers, which can be exchanged with other players to join the servers. --- src/console_cmds.cpp | 1 + src/lang/english.txt | 6 +- src/network/core/config.h | 7 +- src/network/core/game_info.cpp | 6 +- src/network/core/tcp_connect.cpp | 4 +- src/network/core/tcp_coordinator.cpp | 24 +- src/network/core/tcp_coordinator.h | 99 +++++++- src/network/network.cpp | 10 +- src/network/network_coordinator.cpp | 239 +++++++++++++++++- src/network/network_coordinator.h | 24 +- src/network/network_gui.cpp | 13 +- src/network/network_internal.h | 1 + src/settings_type.h | 2 + .../settings/network_secrets_settings.ini | 14 + src/widgets/network_widget.h | 1 + 15 files changed, 422 insertions(+), 29 deletions(-) diff --git a/src/console_cmds.cpp b/src/console_cmds.cpp index 9d03f3deba..0fcac44880 100644 --- a/src/console_cmds.cpp +++ b/src/console_cmds.cpp @@ -702,6 +702,7 @@ DEF_CONSOLE_CMD(ConServerInfo) return true; } + IConsolePrint(CC_DEFAULT, "Invite code: {}", _network_server_invite_code); IConsolePrint(CC_DEFAULT, "Current/maximum clients: {:3d}/{:3d}", _network_game_info.clients_on, _settings_client.network.max_clients); IConsolePrint(CC_DEFAULT, "Current/maximum companies: {:3d}/{:3d}", Company::GetNumItems(), _settings_client.network.max_companies); IConsolePrint(CC_DEFAULT, "Current/maximum spectators: {:3d}/{:3d}", NetworkSpectatorCount(), _settings_client.network.max_spectators); diff --git a/src/lang/english.txt b/src/lang/english.txt index 2e84d4b3a1..813705958b 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -2045,12 +2045,12 @@ STR_NETWORK_SERVER_LIST_SEARCH_SERVER_INTERNET_TOOLTIP :{BLACK}Search i STR_NETWORK_SERVER_LIST_SEARCH_SERVER_LAN :{BLACK}Search LAN STR_NETWORK_SERVER_LIST_SEARCH_SERVER_LAN_TOOLTIP :{BLACK}Search local area network for servers STR_NETWORK_SERVER_LIST_ADD_SERVER :{BLACK}Add server -STR_NETWORK_SERVER_LIST_ADD_SERVER_TOOLTIP :{BLACK}Adds a server to the list which will always be checked for running games +STR_NETWORK_SERVER_LIST_ADD_SERVER_TOOLTIP :{BLACK}Adds a server to the list. This can either be a server address or an invite code STR_NETWORK_SERVER_LIST_START_SERVER :{BLACK}Start server STR_NETWORK_SERVER_LIST_START_SERVER_TOOLTIP :{BLACK}Start your own server STR_NETWORK_SERVER_LIST_PLAYER_NAME_OSKTITLE :{BLACK}Enter your name -STR_NETWORK_SERVER_LIST_ENTER_IP :{BLACK}Enter the address of the host +STR_NETWORK_SERVER_LIST_ENTER_SERVER_ADDRESS :{BLACK}Enter server address or invite code # Start new multiplayer server STR_NETWORK_START_SERVER_CAPTION :{WHITE}Start new multiplayer game @@ -2136,6 +2136,8 @@ STR_NETWORK_CLIENT_LIST_SERVER_NAME_EDIT_TOOLTIP :{BLACK}Edit the STR_NETWORK_CLIENT_LIST_SERVER_NAME_QUERY_CAPTION :Name of the server STR_NETWORK_CLIENT_LIST_SERVER_VISIBILITY :{BLACK}Visibility STR_NETWORK_CLIENT_LIST_SERVER_VISIBILITY_TOOLTIP :{BLACK}Whether other people can see your server in the public listing +STR_NETWORK_CLIENT_LIST_SERVER_INVITE_CODE :{BLACK}Invite code +STR_NETWORK_CLIENT_LIST_SERVER_INVITE_CODE_TOOLTIP :{BLACK}Invite code other players can use to join this server STR_NETWORK_CLIENT_LIST_SERVER_CONNECTION_TYPE :{BLACK}Connection type STR_NETWORK_CLIENT_LIST_SERVER_CONNECTION_TYPE_TOOLTIP :{BLACK}Whether and how your server can be reached by others STR_NETWORK_CLIENT_LIST_PLAYER :{BLACK}Player diff --git a/src/network/core/config.h b/src/network/core/config.h index d37210e197..06df93140c 100644 --- a/src/network/core/config.h +++ b/src/network/core/config.h @@ -47,7 +47,7 @@ static const uint16 COMPAT_MTU = 1460; ///< Numbe static const byte NETWORK_GAME_ADMIN_VERSION = 1; ///< What version of the admin network do we use? static const byte NETWORK_GAME_INFO_VERSION = 4; ///< What version of game-info do we use? static const byte NETWORK_COMPANY_INFO_VERSION = 6; ///< What version of company info is this? -static const byte NETWORK_COORDINATOR_VERSION = 1; ///< What version of game-coordinator-protocol do we use? +static const byte NETWORK_COORDINATOR_VERSION = 2; ///< What version of game-coordinator-protocol do we use? 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' @@ -67,7 +67,10 @@ static const uint NETWORK_CONTENT_VERSION_LENGTH = 16; ///< The m static const uint NETWORK_CONTENT_URL_LENGTH = 96; ///< The maximum length of a content's url, in bytes including '\0'. static const uint NETWORK_CONTENT_DESC_LENGTH = 512; ///< The maximum length of a content's description, in bytes including '\0'. static const uint NETWORK_CONTENT_TAG_LENGTH = 32; ///< The maximum length of a content's tag, in bytes including '\0'. -static const uint NETWORK_ERROR_DETAIL_LENGTH = 100; ///< The maximum length of the error detail, in bytes including '\0' +static const uint NETWORK_ERROR_DETAIL_LENGTH = 100; ///< The maximum length of the error detail, in bytes including '\0'. +static const uint NETWORK_INVITE_CODE_LENGTH = 64; ///< The maximum length of the invite code, in bytes including '\0'. +static const uint NETWORK_INVITE_CODE_SECRET_LENGTH = 80; ///< The maximum length of the invite code secret, in bytes including '\0'. +static const uint NETWORK_TOKEN_LENGTH = 64; ///< The maximum length of a token, in bytes including '\0'. static const uint NETWORK_GRF_NAME_LENGTH = 80; ///< Maximum length of the name of a GRF diff --git a/src/network/core/game_info.cpp b/src/network/core/game_info.cpp index b4c487fba8..17f00c5f57 100644 --- a/src/network/core/game_info.cpp +++ b/src/network/core/game_info.cpp @@ -141,7 +141,11 @@ void FillStaticNetworkServerGameInfo() */ const NetworkServerGameInfo *GetCurrentNetworkServerGameInfo() { - /* Client_on is used as global variable to keep track on the number of clients. */ + /* These variables are updated inside _network_game_info as if they are global variables: + * - clients_on + * - invite_code + * These don't need to be updated manually here. + */ _network_game_info.companies_on = (byte)Company::GetNumItems(); _network_game_info.spectators_on = NetworkSpectatorCount(); _network_game_info.game_date = _date; diff --git a/src/network/core/tcp_connect.cpp b/src/network/core/tcp_connect.cpp index d9b6bb7818..0e8e2e1251 100644 --- a/src/network/core/tcp_connect.cpp +++ b/src/network/core/tcp_connect.cpp @@ -13,6 +13,7 @@ #include "../../thread.h" #include "tcp.h" +#include "../network_coordinator.h" #include "../network_internal.h" #include @@ -48,8 +49,7 @@ TCPServerConnecter::TCPServerConnecter(const std::string &connection_string, uin case SERVER_ADDRESS_INVITE_CODE: this->status = Status::CONNECTING; - - // TODO -- The next commit will add this functionality. + _network_coordinator_client.ConnectToServer(this->server_address.connection_string, this); break; default: diff --git a/src/network/core/tcp_coordinator.cpp b/src/network/core/tcp_coordinator.cpp index bbcb59b14d..dfd73147e1 100644 --- a/src/network/core/tcp_coordinator.cpp +++ b/src/network/core/tcp_coordinator.cpp @@ -27,12 +27,18 @@ bool NetworkCoordinatorSocketHandler::HandlePacket(Packet *p) PacketCoordinatorType type = (PacketCoordinatorType)p->Recv_uint8(); switch (type) { - case PACKET_COORDINATOR_GC_ERROR: return this->Receive_GC_ERROR(p); - case PACKET_COORDINATOR_SERVER_REGISTER: return this->Receive_SERVER_REGISTER(p); - case PACKET_COORDINATOR_GC_REGISTER_ACK: return this->Receive_GC_REGISTER_ACK(p); - case PACKET_COORDINATOR_SERVER_UPDATE: return this->Receive_SERVER_UPDATE(p); - case PACKET_COORDINATOR_CLIENT_LISTING: return this->Receive_CLIENT_LISTING(p); - case PACKET_COORDINATOR_GC_LISTING: return this->Receive_GC_LISTING(p); + case PACKET_COORDINATOR_GC_ERROR: return this->Receive_GC_ERROR(p); + case PACKET_COORDINATOR_SERVER_REGISTER: return this->Receive_SERVER_REGISTER(p); + case PACKET_COORDINATOR_GC_REGISTER_ACK: return this->Receive_GC_REGISTER_ACK(p); + case PACKET_COORDINATOR_SERVER_UPDATE: return this->Receive_SERVER_UPDATE(p); + case PACKET_COORDINATOR_CLIENT_LISTING: return this->Receive_CLIENT_LISTING(p); + case PACKET_COORDINATOR_GC_LISTING: return this->Receive_GC_LISTING(p); + case PACKET_COORDINATOR_CLIENT_CONNECT: return this->Receive_CLIENT_CONNECT(p); + case PACKET_COORDINATOR_GC_CONNECTING: return this->Receive_GC_CONNECTING(p); + case PACKET_COORDINATOR_SERCLI_CONNECT_FAILED: return this->Receive_SERCLI_CONNECT_FAILED(p); + case PACKET_COORDINATOR_GC_CONNECT_FAILED: return this->Receive_GC_CONNECT_FAILED(p); + case PACKET_COORDINATOR_CLIENT_CONNECTED: return this->Receive_CLIENT_CONNECTED(p); + case PACKET_COORDINATOR_GC_DIRECT_CONNECT: return this->Receive_GC_DIRECT_CONNECT(p); default: Debug(net, 0, "[tcp/coordinator] Received invalid packet type {}", type); @@ -82,3 +88,9 @@ bool NetworkCoordinatorSocketHandler::Receive_GC_REGISTER_ACK(Packet *p) { retur bool NetworkCoordinatorSocketHandler::Receive_SERVER_UPDATE(Packet *p) { return this->ReceiveInvalidPacket(PACKET_COORDINATOR_SERVER_UPDATE); } bool NetworkCoordinatorSocketHandler::Receive_CLIENT_LISTING(Packet *p) { return this->ReceiveInvalidPacket(PACKET_COORDINATOR_CLIENT_LISTING); } bool NetworkCoordinatorSocketHandler::Receive_GC_LISTING(Packet *p) { return this->ReceiveInvalidPacket(PACKET_COORDINATOR_GC_LISTING); } +bool NetworkCoordinatorSocketHandler::Receive_CLIENT_CONNECT(Packet *p) { return this->ReceiveInvalidPacket(PACKET_COORDINATOR_CLIENT_CONNECT); } +bool NetworkCoordinatorSocketHandler::Receive_GC_CONNECTING(Packet *p) { return this->ReceiveInvalidPacket(PACKET_COORDINATOR_GC_CONNECTING); } +bool NetworkCoordinatorSocketHandler::Receive_SERCLI_CONNECT_FAILED(Packet *p) { return this->ReceiveInvalidPacket(PACKET_COORDINATOR_SERCLI_CONNECT_FAILED); } +bool NetworkCoordinatorSocketHandler::Receive_GC_CONNECT_FAILED(Packet *p) { return this->ReceiveInvalidPacket(PACKET_COORDINATOR_GC_CONNECT_FAILED); } +bool NetworkCoordinatorSocketHandler::Receive_CLIENT_CONNECTED(Packet *p) { return this->ReceiveInvalidPacket(PACKET_COORDINATOR_CLIENT_CONNECTED); } +bool NetworkCoordinatorSocketHandler::Receive_GC_DIRECT_CONNECT(Packet *p) { return this->ReceiveInvalidPacket(PACKET_COORDINATOR_GC_DIRECT_CONNECT); } diff --git a/src/network/core/tcp_coordinator.h b/src/network/core/tcp_coordinator.h index e95916816f..2d793b1b68 100644 --- a/src/network/core/tcp_coordinator.h +++ b/src/network/core/tcp_coordinator.h @@ -23,15 +23,22 @@ * GC -> packets from Game Coordinator to either Client or Server. * SERVER -> packets from Server to Game Coordinator. * CLIENT -> packets from Client to Game Coordinator. + * SERCLI -> packets from either the Server or Client to Game Coordinator. **/ enum PacketCoordinatorType { - PACKET_COORDINATOR_GC_ERROR, ///< Game Coordinator indicates there was an error. - PACKET_COORDINATOR_SERVER_REGISTER, ///< Server registration. - PACKET_COORDINATOR_GC_REGISTER_ACK, ///< Game Coordinator accepts the registration. - PACKET_COORDINATOR_SERVER_UPDATE, ///< Server sends an set intervals an update of the server. - PACKET_COORDINATOR_CLIENT_LISTING, ///< Client is requesting a listing of all public servers. - PACKET_COORDINATOR_GC_LISTING, ///< Game Coordinator returns a listing of all public servers. - PACKET_COORDINATOR_END, ///< Must ALWAYS be on the end of this list!! (period). + PACKET_COORDINATOR_GC_ERROR, ///< Game Coordinator indicates there was an error. + PACKET_COORDINATOR_SERVER_REGISTER, ///< Server registration. + PACKET_COORDINATOR_GC_REGISTER_ACK, ///< Game Coordinator accepts the registration. + PACKET_COORDINATOR_SERVER_UPDATE, ///< Server sends an set intervals an update of the server. + PACKET_COORDINATOR_CLIENT_LISTING, ///< Client is requesting a listing of all public servers. + PACKET_COORDINATOR_GC_LISTING, ///< Game Coordinator returns a listing of all public servers. + PACKET_COORDINATOR_CLIENT_CONNECT, ///< Client wants to connect to a server based on an invite code. + PACKET_COORDINATOR_GC_CONNECTING, ///< Game Coordinator informs the client of the token assigned to the connection attempt. + PACKET_COORDINATOR_SERCLI_CONNECT_FAILED, ///< Client/server tells the Game Coordinator the current connection attempt failed. + PACKET_COORDINATOR_GC_CONNECT_FAILED, ///< Game Coordinator informs client/server it has given up on the connection attempt. + PACKET_COORDINATOR_CLIENT_CONNECTED, ///< Client informs the Game Coordinator the connection with the server is established. + PACKET_COORDINATOR_GC_DIRECT_CONNECT, ///< Game Coordinator tells client to directly connect to the hostname:port of the server. + PACKET_COORDINATOR_END, ///< Must ALWAYS be on the end of this list!! (period) }; /** @@ -49,6 +56,7 @@ enum ConnectionType { enum NetworkCoordinatorErrorType { NETWORK_COORDINATOR_ERROR_UNKNOWN, ///< There was an unknown error. NETWORK_COORDINATOR_ERROR_REGISTRATION_FAILED, ///< Your request for registration failed. + NETWORK_COORDINATOR_ERROR_INVALID_INVITE_CODE, ///< The invite code given is invalid. }; /** Base socket handler for all Game Coordinator TCP sockets. */ @@ -76,6 +84,8 @@ protected: * uint8 Game Coordinator protocol version. * uint8 Type of game (see ServerGameType). * uint16 Local port of the server. + * string Invite code the server wants to use (can be empty; coordinator will assign a new invite code). + * string Secret that belongs to the invite code (empty if invite code is empty). * * @param p The packet that was just received. * @return True upon success, otherwise false. @@ -85,6 +95,8 @@ protected: /** * Game Coordinator acknowledges the registration. * + * string Invite code that can be used to join this server. + * string Secret that belongs to the invite code (only needed if reusing the invite code on next SERVER_REGISTER). * uint8 Type of connection was detected (see ConnectionType). * * @param p The packet that was just received. @@ -130,6 +142,79 @@ protected: */ virtual bool Receive_GC_LISTING(Packet *p); + /** + * Client wants to connect to a Server. + * + * uint8 Game Coordinator protocol version. + * string Invite code of the Server to join. + * + * @param p The packet that was just received. + * @return True upon success, otherwise false. + */ + virtual bool Receive_CLIENT_CONNECT(Packet *p); + + /** + * Game Coordinator informs the Client under what token it will start the + * attempt to connect the Server and Client together. + * + * string Token to track the current connect request. + * string Invite code of the Server to join. + * + * @param p The packet that was just received. + * @return True upon success, otherwise false. + */ + virtual bool Receive_GC_CONNECTING(Packet *p); + + /** + * Client or Server failed to connect to the remote side. + * + * uint8 Game Coordinator protocol version. + * string Token to track the current connect request. + * uint8 Tracking number to track current connect request. + * + * @param p The packet that was just received. + * @return True upon success, otherwise false. + */ + virtual bool Receive_SERCLI_CONNECT_FAILED(Packet *p); + + /** + * Game Coordinator informs the Client that it failed to find a way to + * connect the Client to the Server. Any open connections for this token + * should be closed now. + * + * string Token to track the current connect request. + * + * @param p The packet that was just received. + * @return True upon success, otherwise false. + */ + virtual bool Receive_GC_CONNECT_FAILED(Packet *p); + + /** + * Client informs the Game Coordinator the connection with the Server is + * established. The Client will disconnect from the Game Coordinator next. + * + * uint8 Game Coordinator protocol version. + * string Token to track the current connect request. + * + * @param p The packet that was just received. + * @return True upon success, otherwise false. + */ + virtual bool Receive_CLIENT_CONNECTED(Packet *p); + + /** + * Game Coordinator requests that the Client makes a direct connection to + * the indicated peer, which is a Server. + * + * string Token to track the current connect request. + * uint8 Tracking number to track current connect request. + * string Hostname of the peer. + * uint16 Port of the peer. + * + * @param p The packet that was just received. + * @return True upon success, otherwise false. + */ + virtual bool Receive_GC_DIRECT_CONNECT(Packet *p); + bool HandlePacket(Packet *p); public: /** diff --git a/src/network/network.cpp b/src/network/network.cpp index 3a33e50962..f8138bbbc1 100644 --- a/src/network/network.cpp +++ b/src/network/network.cpp @@ -589,9 +589,13 @@ void NetworkClose(bool close_admins) ServerNetworkAdminSocketHandler::CloseListeners(); _network_coordinator_client.CloseConnection(); - } else if (MyClient::my_client != nullptr) { - MyClient::SendQuit(); - MyClient::my_client->CloseConnection(NETWORK_RECV_STATUS_CLIENT_QUIT); + } else { + if (MyClient::my_client != nullptr) { + MyClient::SendQuit(); + MyClient::my_client->CloseConnection(NETWORK_RECV_STATUS_CLIENT_QUIT); + } + + _network_coordinator_client.CloseAllTokens(); } TCPConnecter::KillAll(); diff --git a/src/network/network_coordinator.cpp b/src/network/network_coordinator.cpp index b3d3049524..8bd81b6f62 100644 --- a/src/network/network_coordinator.cpp +++ b/src/network/network_coordinator.cpp @@ -1,4 +1,3 @@ - /* * 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. @@ -27,13 +26,41 @@ static const auto NETWORK_COORDINATOR_DELAY_BETWEEN_UPDATES = std::chrono::seconds(30); ///< How many time between updates the server sends to the Game Coordinator. ClientNetworkCoordinatorSocketHandler _network_coordinator_client; ///< The connection to the Game Coordinator. ConnectionType _network_server_connection_type = CONNECTION_TYPE_UNKNOWN; ///< What type of connection the Game Coordinator detected we are on. +std::string _network_server_invite_code = ""; ///< Our invite code as indicated by the Game Coordinator. + +/** Connect to a game server by IP:port. */ +class NetworkDirectConnecter : public TCPConnecter { +private: + std::string token; ///< Token of this connection. + uint8 tracking_number; ///< Tracking number of this connection. + +public: + /** + * Try to establish a direct (hostname:port based) connection. + * @param hostname The hostname of the server. + * @param port The port of the server. + * @param token The token as given by the Game Coordinator to track this connection attempt. + * @param tracking_number The tracking number as given by the Game Coordinator to track this connection attempt. + */ + NetworkDirectConnecter(const std::string &hostname, uint16 port, const std::string &token, uint8 tracking_number) : TCPConnecter(hostname, port), token(token), tracking_number(tracking_number) {} + + void OnFailure() override + { + _network_coordinator_client.ConnectFailure(this->token, this->tracking_number); + } + + void OnConnect(SOCKET s) override + { + _network_coordinator_client.ConnectSuccess(this->token, s); + } +}; /** Connect to the Game Coordinator server. */ class NetworkCoordinatorConnecter : TCPConnecter { public: /** * Initiate the connecting. - * @param address The address of the Game Coordinator server. + * @param connection_string The address of the Game Coordinator server. */ NetworkCoordinatorConnecter(const std::string &connection_string) : TCPConnecter(connection_string, NETWORK_COORDINATOR_SERVER_PORT) {} @@ -73,6 +100,20 @@ bool ClientNetworkCoordinatorSocketHandler::Receive_GC_ERROR(Packet *p) this->CloseConnection(); return false; + case NETWORK_COORDINATOR_ERROR_INVALID_INVITE_CODE: { + /* Find the connecter based on the invite code. */ + auto connecter_it = this->connecter_pre.find(detail); + if (connecter_it == this->connecter_pre.end()) return true; + this->connecter_pre.erase(connecter_it); + + /* Mark the server as offline. */ + NetworkGameList *item = NetworkGameListAddItem(detail); + item->online = false; + + UpdateNetworkGameWindow(); + return true; + } + default: Debug(net, 0, "Invalid error type {} received from Game Coordinator", error); this->CloseConnection(); @@ -85,12 +126,21 @@ bool ClientNetworkCoordinatorSocketHandler::Receive_GC_REGISTER_ACK(Packet *p) /* Schedule sending an update. */ this->next_update = std::chrono::steady_clock::now(); + _settings_client.network.server_invite_code = p->Recv_string(NETWORK_INVITE_CODE_LENGTH); + _settings_client.network.server_invite_code_secret = p->Recv_string(NETWORK_INVITE_CODE_SECRET_LENGTH); _network_server_connection_type = (ConnectionType)p->Recv_uint8(); if (_network_server_connection_type == CONNECTION_TYPE_ISOLATED) { ShowErrorMessage(STR_NETWORK_ERROR_COORDINATOR_ISOLATED, STR_NETWORK_ERROR_COORDINATOR_ISOLATED_DETAIL, WL_ERROR); } + /* Users can change the invite code in the settings, but this has no effect + * on the invite code as assigned by the server. So + * _network_server_invite_code contains the current invite code, + * and _settings_client.network.server_invite_code contains the one we will + * attempt to re-use when registering again. */ + _network_server_invite_code = _settings_client.network.server_invite_code; + SetWindowDirty(WC_CLIENT_LIST, 0); if (_network_dedicated) { @@ -107,7 +157,10 @@ bool ClientNetworkCoordinatorSocketHandler::Receive_GC_REGISTER_ACK(Packet *p) Debug(net, 3, "Your server is now registered with the Game Coordinator:"); Debug(net, 3, " Game type: Public"); Debug(net, 3, " Connection type: {}", connection_type); + Debug(net, 3, " Invite code: {}", _network_server_invite_code); Debug(net, 3, "----------------------------------------"); + } else { + Debug(net, 3, "Game Coordinator registered our server with invite code '{}'", _network_server_invite_code); } return true; @@ -130,7 +183,7 @@ bool ClientNetworkCoordinatorSocketHandler::Receive_GC_LISTING(Packet *p) NetworkGameInfo ngi = {}; DeserializeNetworkGameInfo(p, &ngi); - /* Now we know the join-key, we can add it to our list. */ + /* Now we know the connection string, we can add it to our list. */ NetworkGameList *item = NetworkGameListAddItem(connection_string); /* Clear any existing GRFConfig chain. */ @@ -149,6 +202,58 @@ bool ClientNetworkCoordinatorSocketHandler::Receive_GC_LISTING(Packet *p) return true; } +bool ClientNetworkCoordinatorSocketHandler::Receive_GC_CONNECTING(Packet *p) +{ + std::string token = p->Recv_string(NETWORK_TOKEN_LENGTH); + std::string invite_code = p->Recv_string(NETWORK_INVITE_CODE_LENGTH); + + /* Find the connecter based on the invite code. */ + auto connecter_it = this->connecter_pre.find(invite_code); + if (connecter_it == this->connecter_pre.end()) { + this->CloseConnection(); + return false; + } + + /* Now store it based on the token. */ + this->connecter[token] = connecter_it->second; + this->connecter_pre.erase(connecter_it); + + return true; +} + +bool ClientNetworkCoordinatorSocketHandler::Receive_GC_CONNECT_FAILED(Packet *p) +{ + std::string token = p->Recv_string(NETWORK_TOKEN_LENGTH); + + auto connecter_it = this->connecter.find(token); + if (connecter_it != this->connecter.end()) { + connecter_it->second->SetFailure(); + this->connecter.erase(connecter_it); + } + + /* Close all remaining connections. */ + this->CloseToken(token); + + return true; +} + +bool ClientNetworkCoordinatorSocketHandler::Receive_GC_DIRECT_CONNECT(Packet *p) +{ + std::string token = p->Recv_string(NETWORK_TOKEN_LENGTH); + uint8 tracking_number = p->Recv_uint8(); + std::string hostname = p->Recv_string(NETWORK_HOSTNAME_LENGTH); + uint16 port = p->Recv_uint16(); + + /* Ensure all other pending connection attempts are killed. */ + if (this->game_connecter != nullptr) { + this->game_connecter->Kill(); + this->game_connecter = nullptr; + } + + this->game_connecter = new NetworkDirectConnecter(hostname, port, token, tracking_number); + return true; +} + void ClientNetworkCoordinatorSocketHandler::Connect() { /* We are either already connected or are trying to connect. */ @@ -172,13 +277,15 @@ NetworkRecvStatus ClientNetworkCoordinatorSocketHandler::CloseConnection(bool er _network_server_connection_type = CONNECTION_TYPE_UNKNOWN; this->next_update = {}; + this->CloseAllTokens(); + SetWindowDirty(WC_CLIENT_LIST, 0); return NETWORK_RECV_STATUS_OKAY; } /** - * Register our server to receive our join-key. + * Register our server to receive our invite code. */ void ClientNetworkCoordinatorSocketHandler::Register() { @@ -193,6 +300,13 @@ void ClientNetworkCoordinatorSocketHandler::Register() p->Send_uint8(NETWORK_COORDINATOR_VERSION); p->Send_uint8(SERVER_GAME_TYPE_PUBLIC); p->Send_uint16(_settings_client.network.server_port); + if (_settings_client.network.server_invite_code.empty() || _settings_client.network.server_invite_code_secret.empty()) { + p->Send_string(""); + p->Send_string(""); + } else { + p->Send_string(_settings_client.network.server_invite_code); + p->Send_string(_settings_client.network.server_invite_code_secret); + } this->SendPacket(p); } @@ -229,6 +343,123 @@ void ClientNetworkCoordinatorSocketHandler::GetListing() this->SendPacket(p); } +/** + * Join a server based on an invite code. + * @param invite_code The invite code of the server to connect to. + * @param connecter The connecter of the request. + */ +void ClientNetworkCoordinatorSocketHandler::ConnectToServer(const std::string &invite_code, TCPServerConnecter *connecter) +{ + assert(StrStartsWith(invite_code, "+")); + + if (this->connecter_pre.find(invite_code) != this->connecter_pre.end()) { + /* If someone is hammering the refresh key, one can sent out two + * requests for the same invite code. There isn't really a great way + * of handling this, so just ignore this request. */ + connecter->SetFailure(); + return; + } + + /* Initially we store based on invite code; on first reply we know the + * token, and will start using that key instead. */ + this->connecter_pre[invite_code] = connecter; + + this->Connect(); + + Packet *p = new Packet(PACKET_COORDINATOR_CLIENT_CONNECT); + p->Send_uint8(NETWORK_COORDINATOR_VERSION); + p->Send_string(invite_code); + + this->SendPacket(p); +} + +/** + * Callback from a Connecter to let the Game Coordinator know the connection failed. + * @param token Token of the connecter that failed. + * @param tracking_number Tracking number of the connecter that failed. + */ +void ClientNetworkCoordinatorSocketHandler::ConnectFailure(const std::string &token, uint8 tracking_number) +{ + /* Connecter will destroy itself. */ + this->game_connecter = nullptr; + + Packet *p = new Packet(PACKET_COORDINATOR_SERCLI_CONNECT_FAILED); + p->Send_uint8(NETWORK_COORDINATOR_VERSION); + p->Send_string(token); + p->Send_uint8(tracking_number); + + this->SendPacket(p); + + auto connecter_it = this->connecter.find(token); + assert(connecter_it != this->connecter.end()); + + connecter_it->second->SetFailure(); + this->connecter.erase(connecter_it); +} + +/** + * Callback from a Connecter to let the Game Coordinator know the connection + * to the game server is established. + * @param token Token of the connecter that succeeded. + * @param sock The socket that the connecter can now use. + */ +void ClientNetworkCoordinatorSocketHandler::ConnectSuccess(const std::string &token, SOCKET sock) +{ + /* Connecter will destroy itself. */ + this->game_connecter = nullptr; + + assert(!_network_server); + + Packet *p = new Packet(PACKET_COORDINATOR_CLIENT_CONNECTED); + p->Send_uint8(NETWORK_COORDINATOR_VERSION); + p->Send_string(token); + this->SendPacket(p); + + auto connecter_it = this->connecter.find(token); + assert(connecter_it != this->connecter.end()); + + connecter_it->second->SetConnected(sock); + this->connecter.erase(connecter_it); + + /* Close all remaining connections. */ + this->CloseToken(token); +} + +/** + * Close everything related to this connection token. + * @param token The connection token to close. + */ +void ClientNetworkCoordinatorSocketHandler::CloseToken(const std::string &token) +{ + /* Ensure all other pending connection attempts are also killed. */ + if (this->game_connecter != nullptr) { + this->game_connecter->Kill(); + this->game_connecter = nullptr; + } +} + +/** + * Close all pending connection tokens. + */ +void ClientNetworkCoordinatorSocketHandler::CloseAllTokens() +{ + /* Ensure all other pending connection attempts are also killed. */ + if (this->game_connecter != nullptr) { + this->game_connecter->Kill(); + this->game_connecter = nullptr; + } + + /* Mark any pending connecters as failed. */ + for (auto &[token, it] : this->connecter) { + it->SetFailure(); + } + for (auto &[invite_code, it] : this->connecter_pre) { + it->SetFailure(); + } + this->connecter.clear(); + this->connecter_pre.clear(); +} + /** * Check whether we received/can send some data from/to the Game Coordinator server and * when that's the case handle it appropriately. diff --git a/src/network/network_coordinator.h b/src/network/network_coordinator.h index 7399ce1fc1..f846958bf8 100644 --- a/src/network/network_coordinator.h +++ b/src/network/network_coordinator.h @@ -1,4 +1,3 @@ - /* * 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. @@ -12,6 +11,7 @@ #define NETWORK_COORDINATOR_H #include "core/tcp_coordinator.h" +#include /** * Game Coordinator communication. @@ -25,17 +25,32 @@ * For clients (listing): * - Client sends CLIENT_LISTING. * - Game Coordinator returns the full list of public servers via GC_LISTING (multiple packets). + * + * For clients (connecting): + * - Client sends CLIENT_CONNECT. + * - Game Coordinator checks what type of connections the servers supports: + * 1) Direct connect? + * - Send the client a GC_CONNECT with the peer address. + * - a) Client connects, client sends CLIENT_CONNECTED to Game Coordinator. + * - b) Client connect fails, client sends CLIENT_CONNECT_FAILED to Game Coordinator. + * - If all fails, Game Coordinator sends GC_CONNECT_FAILED to indicate no connection is possible. */ /** Class for handling the client side of the Game Coordinator connection. */ class ClientNetworkCoordinatorSocketHandler : public NetworkCoordinatorSocketHandler { private: std::chrono::steady_clock::time_point next_update; ///< When to send the next update (if server and public). + std::map connecter; ///< Based on tokens, the current connecters that are pending. + std::map connecter_pre; ///< Based on invite codes, the current connecters that are pending. + TCPConnecter *game_connecter = nullptr; ///< Pending connecter to the game server. protected: bool Receive_GC_ERROR(Packet *p) override; bool Receive_GC_REGISTER_ACK(Packet *p) override; bool Receive_GC_LISTING(Packet *p) override; + bool Receive_GC_CONNECTING(Packet *p) override; + bool Receive_GC_CONNECT_FAILED(Packet *p) override; + bool Receive_GC_DIRECT_CONNECT(Packet *p) override; public: /** The idle timeout; when to close the connection because it's idle. */ @@ -49,11 +64,18 @@ public: NetworkRecvStatus CloseConnection(bool error = true) override; void SendReceive(); + void ConnectFailure(const std::string &token, uint8 tracking_number); + void ConnectSuccess(const std::string &token, SOCKET sock); + void Connect(); + void CloseToken(const std::string &token); + void CloseAllTokens(); void Register(); void SendServerUpdate(); void GetListing(); + + void ConnectToServer(const std::string &invite_code, TCPServerConnecter *connecter); }; extern ClientNetworkCoordinatorSocketHandler _network_coordinator_client; diff --git a/src/network/network_gui.cpp b/src/network/network_gui.cpp index 24884ed165..a0d2d47c84 100644 --- a/src/network/network_gui.cpp +++ b/src/network/network_gui.cpp @@ -751,7 +751,7 @@ public: SetDParamStr(0, _settings_client.network.connect_to_ip); ShowQueryString( STR_JUST_RAW_STRING, - STR_NETWORK_SERVER_LIST_ENTER_IP, + STR_NETWORK_SERVER_LIST_ENTER_SERVER_ADDRESS, NETWORK_HOSTNAME_PORT_LENGTH, // maximum number of characters including '\0' this, CS_ALPHANUMERAL, QSF_ACCEPT_UNCHANGED); break; @@ -1632,6 +1632,11 @@ static const NWidgetPart _nested_client_list_widgets[] = { NWidget(NWID_SPACER), SetMinimalSize(10, 0), SetFill(1, 0), SetResize(1, 0), NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_CL_SERVER_VISIBILITY), SetDataTip(STR_BLACK_STRING, STR_NETWORK_CLIENT_LIST_SERVER_VISIBILITY_TOOLTIP), EndContainer(), + NWidget(NWID_HORIZONTAL), SetPIP(0, 3, 0), + NWidget(WWT_TEXT, COLOUR_GREY), SetMinimalTextLines(1, 0), SetDataTip(STR_NETWORK_CLIENT_LIST_SERVER_INVITE_CODE, STR_NULL), + NWidget(NWID_SPACER), SetMinimalSize(10, 0), + NWidget(WWT_TEXT, COLOUR_GREY, WID_CL_SERVER_INVITE_CODE), SetFill(1, 0), SetMinimalTextLines(1, 0), SetResize(1, 0), SetDataTip(STR_BLACK_RAW_STRING, STR_NETWORK_CLIENT_LIST_SERVER_INVITE_CODE_TOOLTIP), SetAlignment(SA_VERT_CENTER | SA_RIGHT), + EndContainer(), NWidget(NWID_HORIZONTAL), SetPIP(0, 3, 0), NWidget(WWT_TEXT, COLOUR_GREY), SetMinimalTextLines(1, 0), SetDataTip(STR_NETWORK_CLIENT_LIST_SERVER_CONNECTION_TYPE, STR_NULL), NWidget(NWID_SPACER), SetMinimalSize(10, 0), @@ -2071,6 +2076,12 @@ public: SetDParam(0, _server_visibility_dropdown[_settings_client.network.server_advertise]); break; + case WID_CL_SERVER_INVITE_CODE: { + static std::string empty = {}; + SetDParamStr(0, _network_server_connection_type == CONNECTION_TYPE_UNKNOWN ? empty : _network_server_invite_code); + break; + } + case WID_CL_SERVER_CONNECTION_TYPE: SetDParam(0, STR_NETWORK_CLIENT_LIST_SERVER_CONNECTION_TYPE_UNKNOWN + _network_server_connection_type); break; diff --git a/src/network/network_internal.h b/src/network/network_internal.h index 95226286c5..2c6639961d 100644 --- a/src/network/network_internal.h +++ b/src/network/network_internal.h @@ -84,6 +84,7 @@ extern uint8 _network_join_waiting; extern uint32 _network_join_bytes; extern uint32 _network_join_bytes_total; extern ConnectionType _network_server_connection_type; +extern std::string _network_server_invite_code; extern uint8 _network_reconnect; diff --git a/src/settings_type.h b/src/settings_type.h index 3377bdede4..79e462c3e2 100644 --- a/src/settings_type.h +++ b/src/settings_type.h @@ -266,6 +266,8 @@ struct NetworkSettings { uint16 server_port; ///< port the server listens on uint16 server_admin_port; ///< port the server listens on for the admin network bool server_admin_chat; ///< allow private chat for the server to be distributed to the admin network + std::string server_invite_code; ///< Invite code to use when registering as server. + std::string server_invite_code_secret; ///< Secret to proof we got this invite code from the Game Coordinator. std::string server_name; ///< name of the server std::string server_password; ///< password for joining this server std::string rcon_password; ///< password for rconsole (server side) diff --git a/src/table/settings/network_secrets_settings.ini b/src/table/settings/network_secrets_settings.ini index fced9240e0..4613636a86 100644 --- a/src/table/settings/network_secrets_settings.ini +++ b/src/table/settings/network_secrets_settings.ini @@ -74,3 +74,17 @@ type = SLE_STR length = NETWORK_SERVER_ID_LENGTH flags = SF_NOT_IN_SAVE | SF_NO_NETWORK_SYNC | SF_NETWORK_ONLY def = nullptr + +[SDTC_SSTR] +var = network.server_invite_code +type = SLE_STR +length = NETWORK_INVITE_CODE_LENGTH +flags = SF_NOT_IN_SAVE | SF_NO_NETWORK_SYNC | SF_NETWORK_ONLY +def = nullptr + +[SDTC_SSTR] +var = network.server_invite_code_secret +type = SLE_STR +length = NETWORK_INVITE_CODE_SECRET_LENGTH +flags = SF_NOT_IN_SAVE | SF_NO_NETWORK_SYNC | SF_NETWORK_ONLY +def = nullptr diff --git a/src/widgets/network_widget.h b/src/widgets/network_widget.h index a665afdb74..c8ec22e861 100644 --- a/src/widgets/network_widget.h +++ b/src/widgets/network_widget.h @@ -101,6 +101,7 @@ enum ClientListWidgets { WID_CL_SERVER_NAME, ///< Server name. WID_CL_SERVER_NAME_EDIT, ///< Edit button for server name. WID_CL_SERVER_VISIBILITY, ///< Server visibility. + WID_CL_SERVER_INVITE_CODE, ///< Invite code for this server. WID_CL_SERVER_CONNECTION_TYPE, ///< The type of connection the Game Coordinator detected for this server. WID_CL_CLIENT_NAME, ///< Client name. WID_CL_CLIENT_NAME_EDIT, ///< Edit button for client name. From 8bb9c3f6466c6d15a1a02e963b06b95f9ffb6397 Mon Sep 17 00:00:00 2001 From: translators Date: Sun, 11 Jul 2021 18:50:16 +0000 Subject: [PATCH 29/44] Update: Translations from eints spanish (mexican): 29 changes by absay czech: 11 changes by JustImagine436 korean: 9 changes by telk5093 portuguese: 9 changes by azulcosta hindi: 28 changes by ritwikraghav14 --- src/lang/afrikaans.txt | 1 - src/lang/arabic_egypt.txt | 1 - src/lang/basque.txt | 1 - src/lang/belarusian.txt | 1 - src/lang/brazilian_portuguese.txt | 1 - src/lang/bulgarian.txt | 1 - src/lang/catalan.txt | 1 - src/lang/croatian.txt | 1 - src/lang/czech.txt | 12 +++++++- src/lang/danish.txt | 1 - src/lang/dutch.txt | 1 - src/lang/english_AU.txt | 1 - src/lang/english_US.txt | 1 - src/lang/esperanto.txt | 1 - src/lang/estonian.txt | 1 - src/lang/faroese.txt | 1 - src/lang/finnish.txt | 1 - src/lang/french.txt | 1 - src/lang/frisian.txt | 1 - src/lang/gaelic.txt | 1 - src/lang/galician.txt | 1 - src/lang/german.txt | 1 - src/lang/greek.txt | 1 - src/lang/hebrew.txt | 1 - src/lang/hindi.txt | 30 +++++++++++++++++-- src/lang/hungarian.txt | 1 - src/lang/icelandic.txt | 1 - src/lang/indonesian.txt | 1 - src/lang/irish.txt | 1 - src/lang/italian.txt | 1 - src/lang/japanese.txt | 1 - src/lang/korean.txt | 10 ++++++- src/lang/latin.txt | 1 - src/lang/latvian.txt | 1 - src/lang/lithuanian.txt | 1 - src/lang/luxembourgish.txt | 1 - src/lang/malay.txt | 1 - src/lang/norwegian_bokmal.txt | 1 - src/lang/norwegian_nynorsk.txt | 1 - src/lang/persian.txt | 1 - src/lang/polish.txt | 1 - src/lang/portuguese.txt | 10 ++++++- src/lang/romanian.txt | 1 - src/lang/russian.txt | 1 - src/lang/serbian.txt | 1 - src/lang/simplified_chinese.txt | 1 - src/lang/slovak.txt | 1 - src/lang/slovenian.txt | 1 - src/lang/spanish.txt | 1 - src/lang/spanish_MX.txt | 50 ++++++++++++++++++------------- src/lang/swedish.txt | 1 - src/lang/thai.txt | 1 - src/lang/traditional_chinese.txt | 1 - src/lang/turkish.txt | 1 - src/lang/ukrainian.txt | 1 - src/lang/urdu.txt | 1 - src/lang/vietnamese.txt | 1 - src/lang/welsh.txt | 1 - 58 files changed, 86 insertions(+), 79 deletions(-) diff --git a/src/lang/afrikaans.txt b/src/lang/afrikaans.txt index a6f380952c..0db03910db 100644 --- a/src/lang/afrikaans.txt +++ b/src/lang/afrikaans.txt @@ -1959,7 +1959,6 @@ STR_NETWORK_SERVER_LIST_START_SERVER :{BLACK}Begin be STR_NETWORK_SERVER_LIST_START_SERVER_TOOLTIP :{BLACK}Begin u eie verskaffer STR_NETWORK_SERVER_LIST_PLAYER_NAME_OSKTITLE :{BLACK}Tik in jou naam -STR_NETWORK_SERVER_LIST_ENTER_IP :{BLACK}Invoeg die adres van die gasheer # Start new multiplayer server STR_NETWORK_START_SERVER_CAPTION :{WHITE}Begin nuwe multispeler speletjie diff --git a/src/lang/arabic_egypt.txt b/src/lang/arabic_egypt.txt index d7de2f4fca..a1bddea785 100644 --- a/src/lang/arabic_egypt.txt +++ b/src/lang/arabic_egypt.txt @@ -1655,7 +1655,6 @@ STR_NETWORK_SERVER_LIST_START_SERVER :{BLACK}أبدأ STR_NETWORK_SERVER_LIST_START_SERVER_TOOLTIP :{BLACK}أبدأ خادمك الخاص STR_NETWORK_SERVER_LIST_PLAYER_NAME_OSKTITLE :{BLACK}أدخل أسمك -STR_NETWORK_SERVER_LIST_ENTER_IP :{BLACK}أدخل عنوان المضيف # Start new multiplayer server STR_NETWORK_START_SERVER_CAPTION :{WHITE}أبدا لعبة متعددة اللاعبين جديدة diff --git a/src/lang/basque.txt b/src/lang/basque.txt index b20ad45af2..8c22e341dd 100644 --- a/src/lang/basque.txt +++ b/src/lang/basque.txt @@ -1834,7 +1834,6 @@ STR_NETWORK_SERVER_LIST_START_SERVER :{BLACK}Zerbitza STR_NETWORK_SERVER_LIST_START_SERVER_TOOLTIP :{BLACK}Zure zerbitzaria sortu STR_NETWORK_SERVER_LIST_PLAYER_NAME_OSKTITLE :{BLACK}Sartu zure izena -STR_NETWORK_SERVER_LIST_ENTER_IP :{BLACK}Sartu antolatzailearen helbidea # Start new multiplayer server STR_NETWORK_START_SERVER_CAPTION :{WHITE}Multijokalari joko berri bat hasi diff --git a/src/lang/belarusian.txt b/src/lang/belarusian.txt index 3dc1e67713..03b09e7eae 100644 --- a/src/lang/belarusian.txt +++ b/src/lang/belarusian.txt @@ -2269,7 +2269,6 @@ STR_NETWORK_SERVER_LIST_START_SERVER :{BLACK}Запу STR_NETWORK_SERVER_LIST_START_SERVER_TOOLTIP :{BLACK}Запуск сэрвэра на вашым кампутары. Да гэтае гульні змогуць далучыцца іншыя гульцы. STR_NETWORK_SERVER_LIST_PLAYER_NAME_OSKTITLE :{BLACK}Увядзіце вашае імя -STR_NETWORK_SERVER_LIST_ENTER_IP :{BLACK}Увядзіце адрас сэрвэра # Start new multiplayer server STR_NETWORK_START_SERVER_CAPTION :{WHITE}Запуск новай сеткавай гульні diff --git a/src/lang/brazilian_portuguese.txt b/src/lang/brazilian_portuguese.txt index cd17b9ff62..c63a0fb833 100644 --- a/src/lang/brazilian_portuguese.txt +++ b/src/lang/brazilian_portuguese.txt @@ -2050,7 +2050,6 @@ STR_NETWORK_SERVER_LIST_START_SERVER :{BLACK}Iniciar STR_NETWORK_SERVER_LIST_START_SERVER_TOOLTIP :{BLACK}Iniciar um servidor próprio STR_NETWORK_SERVER_LIST_PLAYER_NAME_OSKTITLE :{BLACK}Coloque seu nome -STR_NETWORK_SERVER_LIST_ENTER_IP :{BLACK}Coloque o endereço IP do servidor # Start new multiplayer server STR_NETWORK_START_SERVER_CAPTION :{WHITE}Iniciar novo jogo diff --git a/src/lang/bulgarian.txt b/src/lang/bulgarian.txt index b412514a70..c4ecac5290 100644 --- a/src/lang/bulgarian.txt +++ b/src/lang/bulgarian.txt @@ -1880,7 +1880,6 @@ STR_NETWORK_SERVER_LIST_START_SERVER :{BLACK}Пуск STR_NETWORK_SERVER_LIST_START_SERVER_TOOLTIP :{BLACK}Пуснете ваш собствен сървър STR_NETWORK_SERVER_LIST_PLAYER_NAME_OSKTITLE :{BLACK}Въведете името си -STR_NETWORK_SERVER_LIST_ENTER_IP :{BLACK}Въведете адреса на хоста # Start new multiplayer server STR_NETWORK_START_SERVER_CAPTION :{WHITE}Започни нова мрежова игра diff --git a/src/lang/catalan.txt b/src/lang/catalan.txt index 0e7fa183be..adbb9b827d 100644 --- a/src/lang/catalan.txt +++ b/src/lang/catalan.txt @@ -2050,7 +2050,6 @@ STR_NETWORK_SERVER_LIST_START_SERVER :{BLACK}Inicia e STR_NETWORK_SERVER_LIST_START_SERVER_TOOLTIP :{BLACK}Inicia en un servidor propi STR_NETWORK_SERVER_LIST_PLAYER_NAME_OSKTITLE :{BLACK}Posa el teu nom -STR_NETWORK_SERVER_LIST_ENTER_IP :{BLACK}Entra l'adreça IP del servidor # Start new multiplayer server STR_NETWORK_START_SERVER_CAPTION :{WHITE}Inicia una partida multijugador nova diff --git a/src/lang/croatian.txt b/src/lang/croatian.txt index 976524da15..5dedf1eaae 100644 --- a/src/lang/croatian.txt +++ b/src/lang/croatian.txt @@ -2064,7 +2064,6 @@ STR_NETWORK_SERVER_LIST_START_SERVER :{BLACK}Pokreni STR_NETWORK_SERVER_LIST_START_SERVER_TOOLTIP :{BLACK}Pokreni vlastiti poslužitelj STR_NETWORK_SERVER_LIST_PLAYER_NAME_OSKTITLE :{BLACK}Upiši svoje ime -STR_NETWORK_SERVER_LIST_ENTER_IP :{BLACK}Unesi adresu domaćina # Start new multiplayer server STR_NETWORK_START_SERVER_CAPTION :{WHITE}Započni novu igru za više igrača diff --git a/src/lang/czech.txt b/src/lang/czech.txt index 83ce721c3e..c9da6147fa 100644 --- a/src/lang/czech.txt +++ b/src/lang/czech.txt @@ -1229,6 +1229,7 @@ STR_CONFIG_SETTING_TYPE_GAME_MENU :Nastavení hry STR_CONFIG_SETTING_TYPE_GAME_INGAME :Nastavení hry (uchováno v uložené hře; ovlivní pouze stávající hru) STR_CONFIG_SETTING_TYPE_COMPANY_MENU :Nastavení společnosti (uchováno v uložených hrách; ovlivní pouze nové hry) STR_CONFIG_SETTING_TYPE_COMPANY_INGAME :Nastavení společnosti (uchováno v uložené hře; ovlivní pouze stávající společnost) +STR_CONFIG_SETTING_RESET_ALL_CONFIRMATION_DIALOG_TEXT :{WHITE}Tato akce resetuje všechna nastavení hry na výchozí hodnoty.{}Jste si jistý, že chcete pokračovat? STR_CONFIG_SETTING_RESTRICT_CATEGORY :{BLACK}Kategorie: STR_CONFIG_SETTING_RESTRICT_TYPE :{BLACK}Druh: @@ -2120,7 +2121,6 @@ STR_NETWORK_SERVER_LIST_START_SERVER :{BLACK}Spustit STR_NETWORK_SERVER_LIST_START_SERVER_TOOLTIP :{BLACK}Spustit vlastní server STR_NETWORK_SERVER_LIST_PLAYER_NAME_OSKTITLE :{BLACK}Zadej své jméno -STR_NETWORK_SERVER_LIST_ENTER_IP :{BLACK}Vlož IP adresu serveru # Start new multiplayer server STR_NETWORK_START_SERVER_CAPTION :{WHITE}Začít novou hru @@ -2196,11 +2196,19 @@ STR_NETWORK_NEED_COMPANY_PASSWORD_CAPTION :{WHITE}Společn STR_NETWORK_COMPANY_LIST_CLIENT_LIST :Seznam hráčů # Network client list +STR_NETWORK_CLIENT_LIST_SERVER_NAME_TOOLTIP :{BLACK}Název serveru, na kterém hrajete +STR_NETWORK_CLIENT_LIST_SERVER_NAME_EDIT_TOOLTIP :{BLACK}Upravte název svého serveru +STR_NETWORK_CLIENT_LIST_SERVER_NAME_QUERY_CAPTION :Název serveru +STR_NETWORK_CLIENT_LIST_PLAYER_NAME_TOOLTIP :{BLACK}Jméno vašeho hráče +STR_NETWORK_CLIENT_LIST_JOIN_TOOLTIP :{BLACK}Připojit se a hrát za tuto společnost +STR_NETWORK_CLIENT_LIST_CHAT_CLIENT_TOOLTIP :{BLACK}Poslat zprávu tomuto hráči +STR_NETWORK_CLIENT_LIST_CHAT_COMPANY_TOOLTIP :{BLACK}Poslat zprávu všem hráčům této firmy ############ Begin of ConnectionType ############ End of ConnectionType +STR_NETWORK_CLIENT_LIST_ASK_COMPANY_RESET :{YELLOW}Jste si jisti, že chcete smazat společnost '{COMPANY}'? STR_NETWORK_SERVER :Server STR_NETWORK_CLIENT :Klient @@ -2257,6 +2265,7 @@ STR_NETWORK_ERROR_TIMEOUT_PASSWORD :{WHITE}Vložen STR_NETWORK_ERROR_TIMEOUT_COMPUTER :{WHITE}Tvůj počítač je přiliš pomalý pro udržení kroku se serverem STR_NETWORK_ERROR_TIMEOUT_MAP :{WHITE}Tvému počítači trvalo stahování mapy příliš dlouho STR_NETWORK_ERROR_TIMEOUT_JOIN :{WHITE}Tvému počítači trvalo připojování na server příliš dlouho +STR_NETWORK_ERROR_INVALID_CLIENT_NAME :{WHITE}Jméno vašeho hráče není platné ############ Leave those lines in this order!! STR_NETWORK_ERROR_CLIENT_GENERAL :všeobecná chyba @@ -2313,6 +2322,7 @@ STR_NETWORK_MESSAGE_SERVER_SHUTDOWN :{WHITE}Server u STR_NETWORK_MESSAGE_SERVER_REBOOT :{WHITE}Server se restartuje...{}Počkejte prosím... STR_NETWORK_MESSAGE_KICKED :*** {STRING} byl vyhozen. Důvod: ({STRING}) +STR_NETWORK_ERROR_COORDINATOR_ISOLATED :{WHITE}Váš server neumožňuje vzdálené připojení # Content downloading window STR_CONTENT_TITLE :{WHITE}Součásti ke stažení diff --git a/src/lang/danish.txt b/src/lang/danish.txt index 6965752686..52c69a6d27 100644 --- a/src/lang/danish.txt +++ b/src/lang/danish.txt @@ -1973,7 +1973,6 @@ STR_NETWORK_SERVER_LIST_START_SERVER :{BLACK}Start se STR_NETWORK_SERVER_LIST_START_SERVER_TOOLTIP :{BLACK}Start en ny server STR_NETWORK_SERVER_LIST_PLAYER_NAME_OSKTITLE :{BLACK}Indtast dit navn -STR_NETWORK_SERVER_LIST_ENTER_IP :{BLACK}Skriv adressen på en server # Start new multiplayer server STR_NETWORK_START_SERVER_CAPTION :{WHITE}Start et nyt netværksspil diff --git a/src/lang/dutch.txt b/src/lang/dutch.txt index cdf7ca322b..17bf954bc3 100644 --- a/src/lang/dutch.txt +++ b/src/lang/dutch.txt @@ -2049,7 +2049,6 @@ STR_NETWORK_SERVER_LIST_START_SERVER :{BLACK}Start se STR_NETWORK_SERVER_LIST_START_SERVER_TOOLTIP :{BLACK}Start eigen server STR_NETWORK_SERVER_LIST_PLAYER_NAME_OSKTITLE :{BLACK}Vul je naam in -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}Nieuw spel met meerdere spelers starten diff --git a/src/lang/english_AU.txt b/src/lang/english_AU.txt index a368826769..a1e96ce14f 100644 --- a/src/lang/english_AU.txt +++ b/src/lang/english_AU.txt @@ -1890,7 +1890,6 @@ STR_NETWORK_SERVER_LIST_START_SERVER :{BLACK}Start se STR_NETWORK_SERVER_LIST_START_SERVER_TOOLTIP :{BLACK}Start your own server STR_NETWORK_SERVER_LIST_PLAYER_NAME_OSKTITLE :{BLACK}Enter your name -STR_NETWORK_SERVER_LIST_ENTER_IP :{BLACK}Enter the address of the host # Start new multiplayer server STR_NETWORK_START_SERVER_CAPTION :{WHITE}Start new multiplayer game diff --git a/src/lang/english_US.txt b/src/lang/english_US.txt index df85e60de7..9e7b06b21c 100644 --- a/src/lang/english_US.txt +++ b/src/lang/english_US.txt @@ -2049,7 +2049,6 @@ STR_NETWORK_SERVER_LIST_START_SERVER :{BLACK}Start se STR_NETWORK_SERVER_LIST_START_SERVER_TOOLTIP :{BLACK}Start your own server STR_NETWORK_SERVER_LIST_PLAYER_NAME_OSKTITLE :{BLACK}Enter your name -STR_NETWORK_SERVER_LIST_ENTER_IP :{BLACK}Enter the IP address of the server # Start new multiplayer server STR_NETWORK_START_SERVER_CAPTION :{WHITE}Start new multiplayer game diff --git a/src/lang/esperanto.txt b/src/lang/esperanto.txt index b7799fed27..67495b09f7 100644 --- a/src/lang/esperanto.txt +++ b/src/lang/esperanto.txt @@ -1574,7 +1574,6 @@ STR_NETWORK_SERVER_LIST_START_SERVER :{BLACK}Startu s STR_NETWORK_SERVER_LIST_START_SERVER_TOOLTIP :{BLACK}Startu propran servilon STR_NETWORK_SERVER_LIST_PLAYER_NAME_OSKTITLE :{BLACK}Tajpu vian nomon -STR_NETWORK_SERVER_LIST_ENTER_IP :{BLACK}Enigu la adreson de la gastiganto # Start new multiplayer server STR_NETWORK_START_SERVER_CAPTION :{WHITE}Komencu novan ludon por pluraj ludantoj diff --git a/src/lang/estonian.txt b/src/lang/estonian.txt index eaa380453c..60896e7671 100644 --- a/src/lang/estonian.txt +++ b/src/lang/estonian.txt @@ -2097,7 +2097,6 @@ STR_NETWORK_SERVER_LIST_START_SERVER :{BLACK}Tee serv STR_NETWORK_SERVER_LIST_START_SERVER_TOOLTIP :{BLACK}Tee oma server STR_NETWORK_SERVER_LIST_PLAYER_NAME_OSKTITLE :{BLACK}Sisesta enda nimi -STR_NETWORK_SERVER_LIST_ENTER_IP :{BLACK}Sisesta serveri aadress # Start new multiplayer server STR_NETWORK_START_SERVER_CAPTION :{WHITE}Mitmikmängu alustamine diff --git a/src/lang/faroese.txt b/src/lang/faroese.txt index a2746ec480..ee5ae34045 100644 --- a/src/lang/faroese.txt +++ b/src/lang/faroese.txt @@ -1740,7 +1740,6 @@ STR_NETWORK_SERVER_LIST_START_SERVER :{BLACK}Set serv STR_NETWORK_SERVER_LIST_START_SERVER_TOOLTIP :{BLACK}Set tín egna servara í gongd STR_NETWORK_SERVER_LIST_PLAYER_NAME_OSKTITLE :{BLACK}Skriva títt navn -STR_NETWORK_SERVER_LIST_ENTER_IP :{BLACK}Skriva bústað hjá verti # Start new multiplayer server STR_NETWORK_START_SERVER_CAPTION :{WHITE}Byrja eitt nýtt hópspæl diff --git a/src/lang/finnish.txt b/src/lang/finnish.txt index dae8ec05b7..70cdcfc01a 100644 --- a/src/lang/finnish.txt +++ b/src/lang/finnish.txt @@ -2049,7 +2049,6 @@ STR_NETWORK_SERVER_LIST_START_SERVER :{BLACK}Käynnis STR_NETWORK_SERVER_LIST_START_SERVER_TOOLTIP :{BLACK}Käynnistä oma palvelin STR_NETWORK_SERVER_LIST_PLAYER_NAME_OSKTITLE :{BLACK}Kirjoita nimesi -STR_NETWORK_SERVER_LIST_ENTER_IP :{BLACK}Syötä palvelimen IP-osoite # Start new multiplayer server STR_NETWORK_START_SERVER_CAPTION :{WHITE}Aloita uusi peli diff --git a/src/lang/french.txt b/src/lang/french.txt index f86540ce73..db26884d21 100644 --- a/src/lang/french.txt +++ b/src/lang/french.txt @@ -2050,7 +2050,6 @@ STR_NETWORK_SERVER_LIST_START_SERVER :{BLACK}Héberge STR_NETWORK_SERVER_LIST_START_SERVER_TOOLTIP :{BLACK}Démarrer son propre serveur, sur cet ordinateur-ci STR_NETWORK_SERVER_LIST_PLAYER_NAME_OSKTITLE :{BLACK}Entrer votre nom -STR_NETWORK_SERVER_LIST_ENTER_IP :{BLACK}Saisir l'adresse IP du serveur # Start new multiplayer server STR_NETWORK_START_SERVER_CAPTION :{WHITE}Commencer une nouvelle partie diff --git a/src/lang/frisian.txt b/src/lang/frisian.txt index c7a96748dc..a644b32f6b 100644 --- a/src/lang/frisian.txt +++ b/src/lang/frisian.txt @@ -1829,7 +1829,6 @@ STR_NETWORK_SERVER_LIST_START_SERVER :{BLACK}Start ts STR_NETWORK_SERVER_LIST_START_SERVER_TOOLTIP :{BLACK}Start dien eigen tsjinner STR_NETWORK_SERVER_LIST_PLAYER_NAME_OSKTITLE :{BLACK}Fier dyn namme in -STR_NETWORK_SERVER_LIST_ENTER_IP :{BLACK}Fier de namme fan de host yn # Start new multiplayer server STR_NETWORK_START_SERVER_CAPTION :{WHITE}Start in spul foar multispilers diff --git a/src/lang/gaelic.txt b/src/lang/gaelic.txt index 0bf2b2534a..c2b4fd4f3e 100644 --- a/src/lang/gaelic.txt +++ b/src/lang/gaelic.txt @@ -2125,7 +2125,6 @@ STR_NETWORK_SERVER_LIST_START_SERVER :{BLACK}Tòisich STR_NETWORK_SERVER_LIST_START_SERVER_TOOLTIP :{BLACK}Tòisich frithealaiche agad fhèin STR_NETWORK_SERVER_LIST_PLAYER_NAME_OSKTITLE :{BLACK}Cuir d' ainm a-steach -STR_NETWORK_SERVER_LIST_ENTER_IP :{BLACK}Cuir seòladh an òstair a-steach # Start new multiplayer server STR_NETWORK_START_SERVER_CAPTION :{WHITE}Tòisich geama ioma-chluicheadair ùr diff --git a/src/lang/galician.txt b/src/lang/galician.txt index 4a186884b1..8d86c740c6 100644 --- a/src/lang/galician.txt +++ b/src/lang/galician.txt @@ -1961,7 +1961,6 @@ STR_NETWORK_SERVER_LIST_START_SERVER :{BLACK}Iniciar STR_NETWORK_SERVER_LIST_START_SERVER_TOOLTIP :{BLACK}Comezar o teu propio servidor STR_NETWORK_SERVER_LIST_PLAYER_NAME_OSKTITLE :{BLACK}Escribe o teu nome -STR_NETWORK_SERVER_LIST_ENTER_IP :{BLACK}Introduce a dirección IP do servidor # Start new multiplayer server STR_NETWORK_START_SERVER_CAPTION :{WHITE}Comezar nova partida multixogador diff --git a/src/lang/german.txt b/src/lang/german.txt index 77147efc35..f58dafe9b3 100644 --- a/src/lang/german.txt +++ b/src/lang/german.txt @@ -2050,7 +2050,6 @@ STR_NETWORK_SERVER_LIST_START_SERVER :{BLACK}Server s STR_NETWORK_SERVER_LIST_START_SERVER_TOOLTIP :{BLACK}Einen eigenen Server starten STR_NETWORK_SERVER_LIST_PLAYER_NAME_OSKTITLE :{BLACK}Bitte eigenen Namen eingeben -STR_NETWORK_SERVER_LIST_ENTER_IP :{BLACK}IP-Adresse des Servers eingeben # Start new multiplayer server STR_NETWORK_START_SERVER_CAPTION :{WHITE}Neues Mehrspieler-Spiel beginnen diff --git a/src/lang/greek.txt b/src/lang/greek.txt index e9d4370d96..7fce5846aa 100644 --- a/src/lang/greek.txt +++ b/src/lang/greek.txt @@ -2082,7 +2082,6 @@ STR_NETWORK_SERVER_LIST_START_SERVER :{BLACK}Εκκί STR_NETWORK_SERVER_LIST_START_SERVER_TOOLTIP :{BLACK}Ξεκινήστε το δικό σας διακομιστή STR_NETWORK_SERVER_LIST_PLAYER_NAME_OSKTITLE :{BLACK}Εισάγετε το όνομά σας -STR_NETWORK_SERVER_LIST_ENTER_IP :{BLACK}Εισάγετε τη διεύθυνση του οικοδεσπότη # Start new multiplayer server STR_NETWORK_START_SERVER_CAPTION :{WHITE}Έναρξη νέου παιχνιδιού για πολλούς παίκτες diff --git a/src/lang/hebrew.txt b/src/lang/hebrew.txt index 58288a1543..a200114a02 100644 --- a/src/lang/hebrew.txt +++ b/src/lang/hebrew.txt @@ -1939,7 +1939,6 @@ STR_NETWORK_SERVER_LIST_START_SERVER :{BLACK}הפעל STR_NETWORK_SERVER_LIST_START_SERVER_TOOLTIP :{BLACK}הפעל שרת חדש STR_NETWORK_SERVER_LIST_PLAYER_NAME_OSKTITLE :{BLACK} :שמך -STR_NETWORK_SERVER_LIST_ENTER_IP :{BLACK}הזן את הכתובת של השרת # Start new multiplayer server STR_NETWORK_START_SERVER_CAPTION :{WHITE}התחל משחק רב-משתתפים חדש diff --git a/src/lang/hindi.txt b/src/lang/hindi.txt index dbbb24c61c..2e52e4982b 100644 --- a/src/lang/hindi.txt +++ b/src/lang/hindi.txt @@ -44,6 +44,7 @@ STR_CARGO_PLURAL_FRUIT :फल STR_CARGO_SINGULAR_NOTHING : STR_CARGO_SINGULAR_MAIZE :मक्का STR_CARGO_SINGULAR_SWEETS :मिठाई +STR_CARGO_SINGULAR_BUBBLE :बुलबुला STR_CARGO_SINGULAR_TOFFEE :टॉफी # Quantity of cargo @@ -73,6 +74,7 @@ STR_UNITS_POWER_METRIC :{COMMA}{NBSP}hp # Common window strings +STR_TOOLTIP_RESIZE :{BLACK}खिड़की का आकार बदलने के लिये क्लिक करके खींचें # Show engines button @@ -107,6 +109,7 @@ STR_SCENEDIT_FILE_MENU_QUIT :निकास ############ range for SE file menu starts ############ range for settings menu starts +STR_SETTINGS_MENU_CONFIG_SETTINGS_TREE :समायोजन ############ range ends here ############ range for file menu starts @@ -161,13 +164,16 @@ STR_ORDINAL_NUMBER_2ND :द्वित ############ range for ordinal numbers ends ############ range for days starts -STR_DAY_NUMBER_15TH :१५वीं +STR_DAY_NUMBER_14TH :१४ +STR_DAY_NUMBER_15TH :१५ +STR_DAY_NUMBER_23RD :२३ ############ range for days ends ############ range for months starts STR_MONTH_ABBREV_JAN :जन STR_MONTH_ABBREV_NOV :नव +STR_MONTH_AUG :अगस्त ############ range for months ends # Graph window @@ -201,6 +207,7 @@ STR_HIGHSCORE_PRESIDENT_OF_COMPANY_ACHIEVES_STATUS :{BIG_FONT}{WHIT # Smallmap window +STR_SMALLMAP_LEGENDA_TRUCK_LOADING_BAY :{TINY_FONT}{BLACK}ट्रक लदान वीथी STR_SMALLMAP_LEGENDA_BARE_LAND :{TINY_FONT}{BLACK}रिक्त भूमि STR_SMALLMAP_LEGENDA_TOWNS :{TINY_FONT}{BLACK}नगर @@ -290,6 +297,7 @@ STR_CURRENCY_DECREASE_EXCHANGE_RATE_TOOLTIP :{BLACK}एक +STR_VARIETY_MEDIUM :मध्यम STR_AI_SPEED_SLOW :धीमा @@ -321,17 +329,19 @@ STR_CONFIG_SETTING_BRIBE_HELPTEXT :कंपनि -STR_CONFIG_SETTING_ORDER_REVIEW_HELPTEXT :सक्षम होने पर वाहनों के निर्देशों की आवधिक जाँच की जाती है, और कुछ सुस्पष्ट मामलों को पता चलते ही एक संदेश द्वारा बताया जाता है। +STR_CONFIG_SETTING_ORDER_REVIEW_HELPTEXT :सक्षम होने पर वाहनों के निर्देशों की आवधिक जाँच की जाती है, और कुछ सुस्पष्ट मामलों का पता चलते ही एक संदेश द्वारा बताया जाता है। STR_CONFIG_SETTING_ORDER_REVIEW_OFF :नहीं STR_CONFIG_SETTING_STATION_SPREAD_HELPTEXT :एक स्टेशन के हिस्सों के विस्तार हेतु अधिकतम क्षेत्र निर्धारित करें। ऊंची संख्या खेल को धीमा कर सकती है। STR_CONFIG_SETTING_SMALLMAP_LAND_COLOUR_VIOLET :बैंगनी +STR_CONFIG_SETTING_OSK_ACTIVATION_DISABLED :अक्षम +STR_CONFIG_SETTING_FAST_FORWARD_SPEED_LIMIT_ZERO :सीमाहीन (आपके कंप्यूटर द्वारा सीमित) STR_CONFIG_SETTING_SOUND_NEWS :समाचार पत्र: {STRING} @@ -397,6 +407,7 @@ STR_ABANDON_SCENARIO_QUERY :{YELLOW}क् # Face selection window STR_FACE_LOAD_DONE :{WHITE}ओपनटीटीडी प्रारूप पत्र से आपका प्रिय चेहरा भर लिया गया है +STR_FACE_COLLAR_TOOLTIP :{BLACK}कॉलर बदलें # Network server list @@ -461,12 +472,14 @@ STR_NETWORK_MESSAGE_CLIENT_LEAVING :छोड़ र # Content downloading window +STR_CONTENT_SEARCH_EXTERNAL_TOOLTIP :{BLACK}खोजी गयी सामग्री OpenTTD की सामग्री सेवा में उपलब्ध नहीं है, आप OpenTTD से असंबद्ध वेबसाइटों पर खोज सकते हैं STR_CONTENT_DETAIL_SUBTITLE_ALREADY_HERE :{SILVER}यह आपके पास पहले से मौजूद है STR_CONTENT_DETAIL_SUBTITLE_DOES_NOT_EXIST :{SILVER}यह सामग्री अज्ञात है और इसे ओपनटीटीडी में प्राप्त नहीं किया जा सकता है # Order of these is important! # Content downloading progress window +STR_CONTENT_DOWNLOAD_COMPLETE :{WHITE}डाउनलोड पूरा हुआ # Content downloading error messages STR_CONTENT_ERROR_COULD_NOT_DOWNLOAD :{WHITE}डाउनलोड असफल हुआ... @@ -571,6 +584,7 @@ STR_LAI_STATION_DESCRIPTION_RAILROAD_STATION :रेलवे STR_LAI_BRIDGE_DESCRIPTION_RAIL_SUSPENSION_STEEL :इस्पात का रेल झूला पुल +STR_LAI_BRIDGE_DESCRIPTION_RAIL_CANTILEVER_STEEL :इस्पात बाहुधरण रेल पुल @@ -578,6 +592,7 @@ STR_LAI_BRIDGE_DESCRIPTION_RAIL_SUSPENSION_STEEL :इस्पा # About OpenTTD window # Framerate display window +STR_FRAMERATE_MS_GOOD :{LTBLUE}{DECIMAL} ms STR_FRAMERATE_MS_WARN :{YELLOW}{DECIMAL} ms STR_FRAMERATE_BYTES_GOOD :{LTBLUE}{BYTES} STR_FRAMERATE_BYTES_WARN :{YELLOW}{BYTES} @@ -607,6 +622,7 @@ STR_MAPGEN_BORDER_RANDOM :{BLACK}या STR_GENERATION_PROGRESS_NUM :{BLACK}{NUM} / {NUM} # NewGRF settings +STR_NEWGRF_SETTINGS_PRESET_LIST_TOOLTIP :{BLACK}चुने गये पूर्वनिश्चित समायोजन को खोलें @@ -641,6 +657,7 @@ STR_NEWGRF_ERROR_MSG_INFO :{SILVER}{STRING # Placeholders for other invalid stuff, e.g. vehicles that have gone (Game Script). # NewGRF scanning window +STR_NEWGRF_SCAN_MESSAGE :{BLACK}NewGRF खोजा जा रहा है। संख्या के अनुसार इसमें कुछ समय लग सकता है... # Sign list window @@ -662,6 +679,7 @@ STR_TOWN_VIEW_EXPAND_BUTTON :{BLACK}फै STR_LOCAL_AUTHORITY_ACTION_SMALL_ADVERTISING_CAMPAIGN :लघु विज्ञापन अभियान +STR_LOCAL_AUTHORITY_ACTION_TOOLTIP_ROAD_RECONSTRUCTION :{YELLOW}नगरों के सड़क तंत्र के पुनर्निर्माण में निवेश करें।{}इससे अधिकतम ६ महीने तक यातायात में बहुत विघ्न पड़ता है।{}मूल्य : {CURRENCY_LONG} # Goal window STR_GOALS_TEXT :{ORANGE}{STRING} @@ -708,6 +726,7 @@ STR_WAYPOINT_VIEW_CAPTION :{WHITE}{WAYPOIN # Finances window STR_FINANCES_YEAR :{WHITE}{NUM} +STR_FINANCES_SECTION_PROPERTY_MAINTENANCE :{GOLD}संपत्ति का रखरखाव STR_FINANCES_POSITIVE_INCOME :{BLACK}+{CURRENCY_LONG} STR_FINANCES_TOTAL_CURRENCY :{BLACK}{CURRENCY_LONG} STR_FINANCES_BORROW_BUTTON :{BLACK}{CURRENCY_LONG} उधार @@ -860,6 +879,7 @@ STR_VEHICLE_DETAILS_TRAIN_ENGINE_BUILT_AND_VALUE :{LTBLUE}{ENGINE # Order view +STR_ORDER_INDEX :{COMMA}:{NBSP} STR_ORDER_TEXT :{STRING} {STRING} {STRING} @@ -983,6 +1003,7 @@ STR_ERROR_FOREST_CAN_ONLY_BE_PLANTED :{WHITE}... व # Station construction related errors +STR_ERROR_TOO_MANY_STATIONS_LOADING :{WHITE}अत्यधिक स्टेशन/लादन क्षेत्र # Station destruction related errors @@ -1006,6 +1027,7 @@ STR_ERROR_CAN_T_REMOVE_RAILROAD_TRACK :{WHITE}पट # Road construction errors +STR_ERROR_INCOMPATIBLE_TRAMWAY :{WHITE}... असंगत ट्रामवे # Waterway construction errors @@ -1057,6 +1079,7 @@ STR_BASESOUNDS_WIN_DESCRIPTION :ट्रां ##id 0x4800 # industry names STR_INDUSTRY_NAME_POWER_STATION :बिजलीघर +STR_INDUSTRY_NAME_STEEL_MILL :इस्पात चक्की STR_INDUSTRY_NAME_BANK_TROPIC_ARCTIC :बैंक ############ WARNING, using range 0x6000 for strings that are stored in the savegame @@ -1066,6 +1089,7 @@ STR_SV_EMPTY : STR_SV_STNAME :{STRING} STR_SV_STNAME_NORTH :{STRING} उत्तर +STR_SV_STNAME_CENTRAL :{STRING} केंद्रीय STR_SV_STNAME_AIRPORT :{STRING} हवाई अड्डा STR_SV_STNAME_BUOY :{STRING} STR_SV_STNAME_WAYPOINT :{STRING} @@ -1078,8 +1102,10 @@ STR_VEHICLE_NAME_TRAIN_WAGON_RAIL_COAL_CAR :कोयला STR_VEHICLE_NAME_TRAIN_WAGON_RAIL_FRUIT_TRUCK :फल वाहन STR_VEHICLE_NAME_TRAIN_ENGINE_MONORAIL_X2001_ELECTRIC :'X2001' (विद्युतीय) STR_VEHICLE_NAME_TRAIN_WAGON_MONORAIL_TOY_VAN :खिलौनों का डब्बा +STR_VEHICLE_NAME_TRAIN_WAGON_MAGLEV_WATER_TANKER :पानी का टैंकर STR_VEHICLE_NAME_TRAIN_WAGON_MAGLEV_BUBBLE_VAN :बबल वैन STR_VEHICLE_NAME_ROAD_VEHICLE_PLODDYPHUT_MKIII_BUS :प्लॉडीपीहट एमके३ बस +STR_VEHICLE_NAME_ROAD_VEHICLE_MORELAND_WOOD_TRUCK :मोरलैंड काष्ठ ट्रक STR_VEHICLE_NAME_ROAD_VEHICLE_FOSTER_ARMORED_TRUCK :फोस्टर कवचयुक्त ट्रक STR_VEHICLE_NAME_ROAD_VEHICLE_POWERNAUGHT_CANDY_TRUCK :पावरनोट मिष्ठान्न ट्रक STR_VEHICLE_NAME_AIRCRAFT_BAKEWELL_COTSWALD_LB_3 :बेकवेल कॉट्सवॉल्ड एलबी-३ diff --git a/src/lang/hungarian.txt b/src/lang/hungarian.txt index 2d85e9b493..2897ea8d90 100644 --- a/src/lang/hungarian.txt +++ b/src/lang/hungarian.txt @@ -2104,7 +2104,6 @@ STR_NETWORK_SERVER_LIST_START_SERVER :{BLACK}Szerver STR_NETWORK_SERVER_LIST_START_SERVER_TOOLTIP :{BLACK}Saját szervert indít STR_NETWORK_SERVER_LIST_PLAYER_NAME_OSKTITLE :{BLACK}Add meg a neved -STR_NETWORK_SERVER_LIST_ENTER_IP :{BLACK}Írd be a szerver IP címét # Start new multiplayer server STR_NETWORK_START_SERVER_CAPTION :{WHITE}Új játékot kezd diff --git a/src/lang/icelandic.txt b/src/lang/icelandic.txt index c9afa5aecf..40972a3e38 100644 --- a/src/lang/icelandic.txt +++ b/src/lang/icelandic.txt @@ -1778,7 +1778,6 @@ STR_NETWORK_SERVER_LIST_START_SERVER :{BLACK}Hefja þ STR_NETWORK_SERVER_LIST_START_SERVER_TOOLTIP :{BLACK}Hefja eigin þjón STR_NETWORK_SERVER_LIST_PLAYER_NAME_OSKTITLE :{BLACK}Sláðu inn nafn þitt -STR_NETWORK_SERVER_LIST_ENTER_IP :{BLACK}Sláðu inn IP tölu þjónsins # Start new multiplayer server STR_NETWORK_START_SERVER_CAPTION :{WHITE}Hefja nýjan fjölspilunarleik diff --git a/src/lang/indonesian.txt b/src/lang/indonesian.txt index fed6a3f0a8..7607f7bfbc 100644 --- a/src/lang/indonesian.txt +++ b/src/lang/indonesian.txt @@ -2040,7 +2040,6 @@ STR_NETWORK_SERVER_LIST_START_SERVER :{BLACK}Mulai se STR_NETWORK_SERVER_LIST_START_SERVER_TOOLTIP :{BLACK}Mulai melayani STR_NETWORK_SERVER_LIST_PLAYER_NAME_OSKTITLE :{BLACK}Masukkan nama anda -STR_NETWORK_SERVER_LIST_ENTER_IP :{BLACK}Masukkan alamat IP server # Start new multiplayer server STR_NETWORK_START_SERVER_CAPTION :{WHITE}Mulai permainan bersama baru diff --git a/src/lang/irish.txt b/src/lang/irish.txt index 4f2dbff5ba..3080b6362b 100644 --- a/src/lang/irish.txt +++ b/src/lang/irish.txt @@ -1913,7 +1913,6 @@ STR_NETWORK_SERVER_LIST_START_SERVER :{BLACK}Tosaigh STR_NETWORK_SERVER_LIST_START_SERVER_TOOLTIP :{BLACK}Tosaigh d'fhreastalaí féin STR_NETWORK_SERVER_LIST_PLAYER_NAME_OSKTITLE :{BLACK}Iontráil d'ainm -STR_NETWORK_SERVER_LIST_ENTER_IP :{BLACK}Iontráil seoladh an óstaigh # Start new multiplayer server STR_NETWORK_START_SERVER_CAPTION :{WHITE}Tosaigh cluiche ilimreora nua diff --git a/src/lang/italian.txt b/src/lang/italian.txt index 439a0ad8b3..fa4dae8fc5 100644 --- a/src/lang/italian.txt +++ b/src/lang/italian.txt @@ -2053,7 +2053,6 @@ STR_NETWORK_SERVER_LIST_START_SERVER :{BLACK}Avvia se STR_NETWORK_SERVER_LIST_START_SERVER_TOOLTIP :{BLACK}Avvia il proprio server STR_NETWORK_SERVER_LIST_PLAYER_NAME_OSKTITLE :{BLACK}Inserire il nome del giocatore -STR_NETWORK_SERVER_LIST_ENTER_IP :{BLACK}Digitare l'indirizzo del server # Start new multiplayer server STR_NETWORK_START_SERVER_CAPTION :{WHITE}Avvia nuova partita multigiocatore diff --git a/src/lang/japanese.txt b/src/lang/japanese.txt index 6311f0350e..fbb1a84cb0 100644 --- a/src/lang/japanese.txt +++ b/src/lang/japanese.txt @@ -2041,7 +2041,6 @@ STR_NETWORK_SERVER_LIST_START_SERVER :{BLACK}サー STR_NETWORK_SERVER_LIST_START_SERVER_TOOLTIP :{BLACK}自分でサーバーを立ち上げます STR_NETWORK_SERVER_LIST_PLAYER_NAME_OSKTITLE :{BLACK}プレイヤー名を入力 -STR_NETWORK_SERVER_LIST_ENTER_IP :{BLACK}ホストアドレスを入力します # Start new multiplayer server STR_NETWORK_START_SERVER_CAPTION :{WHITE}新規マルチプレイヤーゲームを開始 diff --git a/src/lang/korean.txt b/src/lang/korean.txt index 1e19756de1..f34f0b3eb5 100644 --- a/src/lang/korean.txt +++ b/src/lang/korean.txt @@ -1996,6 +1996,7 @@ STR_FACE_TIE :넥타이: STR_FACE_EARRING :귀걸이: STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}넥타이/귀걸이 변경 +STR_NETWORK_SERVER_VISIBILITY_LOCAL :로컬 STR_NETWORK_SERVER_VISIBILITY_PUBLIC :공개 # Network server list @@ -2050,7 +2051,6 @@ STR_NETWORK_SERVER_LIST_START_SERVER :{BLACK}서버 STR_NETWORK_SERVER_LIST_START_SERVER_TOOLTIP :{BLACK}당신이 서버가 되어 게임을 진행합니다. STR_NETWORK_SERVER_LIST_PLAYER_NAME_OSKTITLE :{BLACK}이름을 입력하세요 -STR_NETWORK_SERVER_LIST_ENTER_IP :{BLACK}호스트(IP) 주소 입력 # Start new multiplayer server STR_NETWORK_START_SERVER_CAPTION :{WHITE}새 멀티플레이 게임 시작하기 @@ -2136,6 +2136,8 @@ 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_SERVER_CONNECTION_TYPE :{BLACK}접속 방식 +STR_NETWORK_CLIENT_LIST_SERVER_CONNECTION_TYPE_TOOLTIP :{BLACK}다른 사람이 이 서버에 들어올 수 있는지 여부를 나타냅니다 STR_NETWORK_CLIENT_LIST_PLAYER :{BLACK}플레이어 STR_NETWORK_CLIENT_LIST_PLAYER_NAME :{BLACK}이름 STR_NETWORK_CLIENT_LIST_PLAYER_NAME_TOOLTIP :{BLACK}당신의 접속자 이름입니다 @@ -2155,6 +2157,9 @@ STR_NETWORK_CLIENT_LIST_PLAYER_ICON_HOST_TOOLTIP :{BLACK}이 게 STR_NETWORK_CLIENT_LIST_CLIENT_COMPANY_COUNT :{BLACK}접속자 {NUM}명 / 회사 {NUM}개 ############ Begin of ConnectionType +STR_NETWORK_CLIENT_LIST_SERVER_CONNECTION_TYPE_UNKNOWN :{BLACK}로컬 +STR_NETWORK_CLIENT_LIST_SERVER_CONNECTION_TYPE_ISOLATED :{RED}다른 플레이어가 접속할 수 없게 됩니다 +STR_NETWORK_CLIENT_LIST_SERVER_CONNECTION_TYPE_DIRECT :{BLACK}공개 ############ End of ConnectionType STR_NETWORK_CLIENT_LIST_ADMIN_CLIENT_KICK :추방 @@ -2284,6 +2289,9 @@ STR_NETWORK_MESSAGE_SERVER_SHUTDOWN :{WHITE}서버 STR_NETWORK_MESSAGE_SERVER_REBOOT :{WHITE}서버가 재시작되고 있습니다...{}기다려주세요... STR_NETWORK_MESSAGE_KICKED :*** {STRING} - 서버에서 강제로 추방되었습니다. 사유: ({STRING}) +STR_NETWORK_ERROR_COORDINATOR_REGISTRATION_FAILED :{WHITE}서버 등록 실패 +STR_NETWORK_ERROR_COORDINATOR_ISOLATED :{WHITE}서버가 원격 접속을 허용하지 않습니다 +STR_NETWORK_ERROR_COORDINATOR_ISOLATED_DETAIL :{WHITE}다른 플레이어가 이 서버에 들어올 수 없게 될 것입니다 # Content downloading window STR_CONTENT_TITLE :{WHITE}콘텐츠 다운로드 diff --git a/src/lang/latin.txt b/src/lang/latin.txt index af566e4593..edb0cfab77 100644 --- a/src/lang/latin.txt +++ b/src/lang/latin.txt @@ -2132,7 +2132,6 @@ STR_NETWORK_SERVER_LIST_START_SERVER :{BLACK}Servatru STR_NETWORK_SERVER_LIST_START_SERVER_TOOLTIP :{BLACK}Servatrum tuum proprium incohare STR_NETWORK_SERVER_LIST_PLAYER_NAME_OSKTITLE :{BLACK}Nomen tuum inscribe -STR_NETWORK_SERVER_LIST_ENTER_IP :{BLACK}Inscribe inscriptionem IP servatri # Start new multiplayer server STR_NETWORK_START_SERVER_CAPTION :{WHITE}Incipere novum ludum cum pluribus lusoribus diff --git a/src/lang/latvian.txt b/src/lang/latvian.txt index d8a738849e..e1131be431 100644 --- a/src/lang/latvian.txt +++ b/src/lang/latvian.txt @@ -2008,7 +2008,6 @@ STR_NETWORK_SERVER_LIST_START_SERVER :{BLACK}Palaist STR_NETWORK_SERVER_LIST_START_SERVER_TOOLTIP :{BLACK}Palaist jūsu personīgo serveri STR_NETWORK_SERVER_LIST_PLAYER_NAME_OSKTITLE :{BLACK}Ievadīt savu vārdu -STR_NETWORK_SERVER_LIST_ENTER_IP :{BLACK}Ievadīt servera adresi # Start new multiplayer server STR_NETWORK_START_SERVER_CAPTION :{WHITE}Sākt jaunu vairākspēlētāju spēli diff --git a/src/lang/lithuanian.txt b/src/lang/lithuanian.txt index 200c642401..485e94f59e 100644 --- a/src/lang/lithuanian.txt +++ b/src/lang/lithuanian.txt @@ -2252,7 +2252,6 @@ STR_NETWORK_SERVER_LIST_START_SERVER :{BLACK}Sukurti STR_NETWORK_SERVER_LIST_START_SERVER_TOOLTIP :{BLACK}Sukurti savo serverį STR_NETWORK_SERVER_LIST_PLAYER_NAME_OSKTITLE :{BLACK}Įrašykite savo vardą -STR_NETWORK_SERVER_LIST_ENTER_IP :{BLACK}Įvesk adresą (IP) # Start new multiplayer server STR_NETWORK_START_SERVER_CAPTION :{WHITE}Pradėti naują daugiažaidėjinį žaidimą diff --git a/src/lang/luxembourgish.txt b/src/lang/luxembourgish.txt index f18305b451..6b72e791bc 100644 --- a/src/lang/luxembourgish.txt +++ b/src/lang/luxembourgish.txt @@ -2033,7 +2033,6 @@ STR_NETWORK_SERVER_LIST_START_SERVER :{BLACK}Server s STR_NETWORK_SERVER_LIST_START_SERVER_TOOLTIP :{BLACK}Eegenen Server starten STR_NETWORK_SERVER_LIST_PLAYER_NAME_OSKTITLE :{BLACK}Gëff däin Numm an -STR_NETWORK_SERVER_LIST_ENTER_IP :{BLACK}D'Address vum Host uginn # Start new multiplayer server STR_NETWORK_START_SERVER_CAPTION :{WHITE}Nei Multiplayerpartie starten diff --git a/src/lang/malay.txt b/src/lang/malay.txt index cc0b75aaab..c792823170 100644 --- a/src/lang/malay.txt +++ b/src/lang/malay.txt @@ -1675,7 +1675,6 @@ STR_NETWORK_SERVER_LIST_START_SERVER :{BLACK}Mulakan STR_NETWORK_SERVER_LIST_START_SERVER_TOOLTIP :{BLACK}Mulakan pelayan sendiri STR_NETWORK_SERVER_LIST_PLAYER_NAME_OSKTITLE :{BLACK}Masukkan nama anda -STR_NETWORK_SERVER_LIST_ENTER_IP :{BLACK}Masukkan alamat hos # Start new multiplayer server STR_NETWORK_START_SERVER_CAPTION :{WHITE}Mulakan permainan baru berbilang pemain diff --git a/src/lang/norwegian_bokmal.txt b/src/lang/norwegian_bokmal.txt index e17f3e36ae..bee88edbbf 100644 --- a/src/lang/norwegian_bokmal.txt +++ b/src/lang/norwegian_bokmal.txt @@ -2052,7 +2052,6 @@ STR_NETWORK_SERVER_LIST_START_SERVER :{BLACK}Start tj STR_NETWORK_SERVER_LIST_START_SERVER_TOOLTIP :{BLACK}Start en egen tjener STR_NETWORK_SERVER_LIST_PLAYER_NAME_OSKTITLE :{BLACK}Skriv inn ditt navn -STR_NETWORK_SERVER_LIST_ENTER_IP :{BLACK}Skriv inn IP-adressen til verten # Start new multiplayer server STR_NETWORK_START_SERVER_CAPTION :{WHITE}Start et nytt flerspillerspill diff --git a/src/lang/norwegian_nynorsk.txt b/src/lang/norwegian_nynorsk.txt index 563a6eaead..ec0ff3b156 100644 --- a/src/lang/norwegian_nynorsk.txt +++ b/src/lang/norwegian_nynorsk.txt @@ -1837,7 +1837,6 @@ STR_NETWORK_SERVER_LIST_START_SERVER :{BLACK}Start te STR_NETWORK_SERVER_LIST_START_SERVER_TOOLTIP :{BLACK}Start ein eigen tenar STR_NETWORK_SERVER_LIST_PLAYER_NAME_OSKTITLE :{BLACK}Set inn namnet ditt -STR_NETWORK_SERVER_LIST_ENTER_IP :{BLACK}Skriv inn IP-adressa til tenaren # Start new multiplayer server STR_NETWORK_START_SERVER_CAPTION :{WHITE}Start eit nytt fleirspelarspel diff --git a/src/lang/persian.txt b/src/lang/persian.txt index f37cc09e47..3aea70ef8b 100644 --- a/src/lang/persian.txt +++ b/src/lang/persian.txt @@ -1635,7 +1635,6 @@ STR_NETWORK_SERVER_LIST_START_SERVER :{BLACK}اجرا STR_NETWORK_SERVER_LIST_START_SERVER_TOOLTIP :{BLACK}راه اندازی سرویس دهنده خود STR_NETWORK_SERVER_LIST_PLAYER_NAME_OSKTITLE :{BLACK}نام خود را وارد نماببد -STR_NETWORK_SERVER_LIST_ENTER_IP :{BLACK}نشانی میزبان را وارد کنید # Start new multiplayer server STR_NETWORK_START_SERVER_CAPTION :{WHITE}آغاز بازی چندنفره diff --git a/src/lang/polish.txt b/src/lang/polish.txt index 6eb80757f4..32ffe5cade 100644 --- a/src/lang/polish.txt +++ b/src/lang/polish.txt @@ -2419,7 +2419,6 @@ STR_NETWORK_SERVER_LIST_START_SERVER :{BLACK}Uruchom STR_NETWORK_SERVER_LIST_START_SERVER_TOOLTIP :{BLACK}Uruchom własny serwer STR_NETWORK_SERVER_LIST_PLAYER_NAME_OSKTITLE :{BLACK}Wprowadź swoje imię -STR_NETWORK_SERVER_LIST_ENTER_IP :{BLACK}Wpisz adres IP serwera # Start new multiplayer server STR_NETWORK_START_SERVER_CAPTION :{WHITE}Rozpocznij nową grę wieloosobową diff --git a/src/lang/portuguese.txt b/src/lang/portuguese.txt index 511517c672..60a959618a 100644 --- a/src/lang/portuguese.txt +++ b/src/lang/portuguese.txt @@ -1996,6 +1996,7 @@ STR_FACE_TIE :Gravata: STR_FACE_EARRING :Brinco: STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}Mudar gravata ou brinco +STR_NETWORK_SERVER_VISIBILITY_LOCAL :Local STR_NETWORK_SERVER_VISIBILITY_PUBLIC :Público # Network server list @@ -2050,7 +2051,6 @@ STR_NETWORK_SERVER_LIST_START_SERVER :{BLACK}Iniciar STR_NETWORK_SERVER_LIST_START_SERVER_TOOLTIP :{BLACK}Iniciar um servidor próprio 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 STR_NETWORK_START_SERVER_CAPTION :{WHITE}Iniciar novo jogo @@ -2136,6 +2136,8 @@ STR_NETWORK_CLIENT_LIST_SERVER_NAME_EDIT_TOOLTIP :{BLACK}Editar o 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_SERVER_CONNECTION_TYPE :{BLACK}Tipo de conexão +STR_NETWORK_CLIENT_LIST_SERVER_CONNECTION_TYPE_TOOLTIP :{BLACK}Se e como o seu servidor pode ser alcançado por outros 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 @@ -2155,6 +2157,9 @@ STR_NETWORK_CLIENT_LIST_PLAYER_ICON_HOST_TOOLTIP :{BLACK}Este é STR_NETWORK_CLIENT_LIST_CLIENT_COMPANY_COUNT :{BLACK}{NUM} cliente{P "" s} / {NUM} companhi{P a as} ############ Begin of ConnectionType +STR_NETWORK_CLIENT_LIST_SERVER_CONNECTION_TYPE_UNKNOWN :{BLACK}Local +STR_NETWORK_CLIENT_LIST_SERVER_CONNECTION_TYPE_ISOLATED :{RED}Jogadores remotos não conseguem conetar +STR_NETWORK_CLIENT_LIST_SERVER_CONNECTION_TYPE_DIRECT :{BLACK}Público ############ End of ConnectionType STR_NETWORK_CLIENT_LIST_ADMIN_CLIENT_KICK :Expulsar @@ -2284,6 +2289,9 @@ STR_NETWORK_MESSAGE_SERVER_SHUTDOWN :{WHITE}O servid STR_NETWORK_MESSAGE_SERVER_REBOOT :{WHITE}O servidor está a reiniciar...{}Por favor espere... STR_NETWORK_MESSAGE_KICKED :*** {STRING} foi expulso. Motivo: ({STRING}) +STR_NETWORK_ERROR_COORDINATOR_REGISTRATION_FAILED :{WHITE}Falha ao registar o servidor +STR_NETWORK_ERROR_COORDINATOR_ISOLATED :{WHITE}O seu servidor não permite conexões remotas +STR_NETWORK_ERROR_COORDINATOR_ISOLATED_DETAIL :{WHITE}Outros jogadores não conseguirão conetar ao seu servidor # Content downloading window STR_CONTENT_TITLE :{WHITE}Descarregamento de conteúdo diff --git a/src/lang/romanian.txt b/src/lang/romanian.txt index 968950155d..5528456e53 100644 --- a/src/lang/romanian.txt +++ b/src/lang/romanian.txt @@ -1966,7 +1966,6 @@ STR_NETWORK_SERVER_LIST_START_SERVER :{BLACK}Porneşt STR_NETWORK_SERVER_LIST_START_SERVER_TOOLTIP :{BLACK}Porneşte un server propriu STR_NETWORK_SERVER_LIST_PLAYER_NAME_OSKTITLE :{BLACK}Introduceţi numele dvs. -STR_NETWORK_SERVER_LIST_ENTER_IP :{BLACK}Introduceţi adresa serverului # Start new multiplayer server STR_NETWORK_START_SERVER_CAPTION :{WHITE}Începe un joc nou diff --git a/src/lang/russian.txt b/src/lang/russian.txt index 0ae886b266..59d990a9f6 100644 --- a/src/lang/russian.txt +++ b/src/lang/russian.txt @@ -2200,7 +2200,6 @@ STR_NETWORK_SERVER_LIST_START_SERVER :{BLACK}Запу STR_NETWORK_SERVER_LIST_START_SERVER_TOOLTIP :{BLACK}Запуск сервера на вашем компьютере. К этой игре смогут подсоединяться другие игроки. STR_NETWORK_SERVER_LIST_PLAYER_NAME_OSKTITLE :{BLACK}Введите ваше имя -STR_NETWORK_SERVER_LIST_ENTER_IP :{BLACK}Введите адрес сервера # Start new multiplayer server STR_NETWORK_START_SERVER_CAPTION :{WHITE}Запуск новой сетевой игры diff --git a/src/lang/serbian.txt b/src/lang/serbian.txt index 0e2d0dd69c..fda419bd14 100644 --- a/src/lang/serbian.txt +++ b/src/lang/serbian.txt @@ -2228,7 +2228,6 @@ STR_NETWORK_SERVER_LIST_START_SERVER :{BLACK}Pokreni STR_NETWORK_SERVER_LIST_START_SERVER_TOOLTIP :{BLACK}Pokrenite sopstveni server STR_NETWORK_SERVER_LIST_PLAYER_NAME_OSKTITLE :{BLACK}Unesite Vaše ime -STR_NETWORK_SERVER_LIST_ENTER_IP :{BLACK}Unesite adresu računara-servera # Start new multiplayer server STR_NETWORK_START_SERVER_CAPTION :{WHITE}Pokreni novu mrežnu partiju diff --git a/src/lang/simplified_chinese.txt b/src/lang/simplified_chinese.txt index 006fd10440..07d8c62006 100644 --- a/src/lang/simplified_chinese.txt +++ b/src/lang/simplified_chinese.txt @@ -2038,7 +2038,6 @@ STR_NETWORK_SERVER_LIST_START_SERVER :{BLACK}启动 STR_NETWORK_SERVER_LIST_START_SERVER_TOOLTIP :{BLACK}启动本机作为服务器 STR_NETWORK_SERVER_LIST_PLAYER_NAME_OSKTITLE :{BLACK}输入姓名 -STR_NETWORK_SERVER_LIST_ENTER_IP :{BLACK}输入服务器地址 # Start new multiplayer server STR_NETWORK_START_SERVER_CAPTION :{WHITE}开始新的联机游戏 diff --git a/src/lang/slovak.txt b/src/lang/slovak.txt index 9763287bc1..e091c2efb8 100644 --- a/src/lang/slovak.txt +++ b/src/lang/slovak.txt @@ -2108,7 +2108,6 @@ STR_NETWORK_SERVER_LIST_START_SERVER :{BLACK}Spustiť STR_NETWORK_SERVER_LIST_START_SERVER_TOOLTIP :{BLACK}Spustiť vlastný server STR_NETWORK_SERVER_LIST_PLAYER_NAME_OSKTITLE :{BLACK}Zadajte vaše meno -STR_NETWORK_SERVER_LIST_ENTER_IP :{BLACK}Zadajte IP adresu serveru # Start new multiplayer server STR_NETWORK_START_SERVER_CAPTION :{WHITE}Spustiť novú hru pre viacero hráčov diff --git a/src/lang/slovenian.txt b/src/lang/slovenian.txt index 6dfe349c9e..78f250b6bb 100644 --- a/src/lang/slovenian.txt +++ b/src/lang/slovenian.txt @@ -2069,7 +2069,6 @@ STR_NETWORK_SERVER_LIST_START_SERVER :{BLACK}Poženi STR_NETWORK_SERVER_LIST_START_SERVER_TOOLTIP :{BLACK}Poženi lasten streznik STR_NETWORK_SERVER_LIST_PLAYER_NAME_OSKTITLE :{BLACK}Vpiši tvoje ime -STR_NETWORK_SERVER_LIST_ENTER_IP :{BLACK}Vnesi naslov gostitelja # Start new multiplayer server STR_NETWORK_START_SERVER_CAPTION :{WHITE}Poženi novo igro za več igralcev diff --git a/src/lang/spanish.txt b/src/lang/spanish.txt index 34490996cb..516dd2db96 100644 --- a/src/lang/spanish.txt +++ b/src/lang/spanish.txt @@ -2050,7 +2050,6 @@ STR_NETWORK_SERVER_LIST_START_SERVER :{BLACK}Iniciar STR_NETWORK_SERVER_LIST_START_SERVER_TOOLTIP :{BLACK}Inicia un servidor nuevo STR_NETWORK_SERVER_LIST_PLAYER_NAME_OSKTITLE :{BLACK}Introduce tu nombre -STR_NETWORK_SERVER_LIST_ENTER_IP :{BLACK}Introduce la dirección IP del servidor # Start new multiplayer server STR_NETWORK_START_SERVER_CAPTION :{WHITE}Empezar nueva partida en multijugador diff --git a/src/lang/spanish_MX.txt b/src/lang/spanish_MX.txt index d590d35650..47c3edad57 100644 --- a/src/lang/spanish_MX.txt +++ b/src/lang/spanish_MX.txt @@ -832,7 +832,7 @@ STR_NEWS_INDUSTRY_CONSTRUCTION :{BIG_FONT}{BLAC STR_NEWS_INDUSTRY_PLANTED :{BIG_FONT}{BLACK}¡Nuev{G o a} {STRING} fundad{G o a} cerca de {TOWN}! STR_NEWS_INDUSTRY_CLOSURE_GENERAL :{BIG_FONT}{BLACK}¡La industria {STRING} anuncia su inminente cierre! -STR_NEWS_INDUSTRY_CLOSURE_SUPPLY_PROBLEMS :{BIG_FONT}{BLACK}¡Problemas con suministros provocan que {STRING} anuncie su inminente cierre! +STR_NEWS_INDUSTRY_CLOSURE_SUPPLY_PROBLEMS :{BIG_FONT}{BLACK}¡La falta de abastecimiento provoca que {STRING} anuncie su inminente cierre! STR_NEWS_INDUSTRY_CLOSURE_LACK_OF_TREES :{BIG_FONT}{BLACK}¡La falta de árboles hace que {STRING} anuncie su inminente cierre! STR_NEWS_EURO_INTRODUCTION :{BIG_FONT}{BLACK}¡Unión Monetaria Europea!{}{}¡El Euro es introducido como la nueva moneda oficial de todas las transacciones! @@ -1996,6 +1996,7 @@ STR_FACE_TIE :Corbata: STR_FACE_EARRING :Aretes: STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}Cambiar corbata o aretes +STR_NETWORK_SERVER_VISIBILITY_LOCAL :Local STR_NETWORK_SERVER_VISIBILITY_PUBLIC :Público # Network server list @@ -2050,7 +2051,6 @@ STR_NETWORK_SERVER_LIST_START_SERVER :{BLACK}Iniciar STR_NETWORK_SERVER_LIST_START_SERVER_TOOLTIP :{BLACK}Iniciar un nuevo servidor STR_NETWORK_SERVER_LIST_PLAYER_NAME_OSKTITLE :{BLACK}Introducir nombre del jugador -STR_NETWORK_SERVER_LIST_ENTER_IP :{BLACK}Introducir la dirección IP del servidor # Start new multiplayer server STR_NETWORK_START_SERVER_CAPTION :{WHITE}Empezar nueva partida multijugador @@ -2136,6 +2136,8 @@ STR_NETWORK_CLIENT_LIST_SERVER_NAME_EDIT_TOOLTIP :{BLACK}Modifica 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_SERVER_CONNECTION_TYPE :{BLACK}Tipo de conexión +STR_NETWORK_CLIENT_LIST_SERVER_CONNECTION_TYPE_TOOLTIP :{BLACK}Quién puede conectarse a tu servidor 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 @@ -2155,6 +2157,9 @@ STR_NETWORK_CLIENT_LIST_PLAYER_ICON_HOST_TOOLTIP :{BLACK}Este es STR_NETWORK_CLIENT_LIST_CLIENT_COMPANY_COUNT :{BLACK}{NUM} cliente{P "" s}/{NUM} empresa{P "" s} ############ Begin of ConnectionType +STR_NETWORK_CLIENT_LIST_SERVER_CONNECTION_TYPE_UNKNOWN :{BLACK}Local +STR_NETWORK_CLIENT_LIST_SERVER_CONNECTION_TYPE_ISOLATED :{RED}Los jugadores remotos no pueden conectarse +STR_NETWORK_CLIENT_LIST_SERVER_CONNECTION_TYPE_DIRECT :{BLACK}Pública ############ End of ConnectionType STR_NETWORK_CLIENT_LIST_ADMIN_CLIENT_KICK :Expulsar @@ -2284,6 +2289,9 @@ STR_NETWORK_MESSAGE_SERVER_SHUTDOWN :{WHITE}El servi STR_NETWORK_MESSAGE_SERVER_REBOOT :{WHITE}Se está reiniciando el servidor...{}Espera por favor... STR_NETWORK_MESSAGE_KICKED :*** {STRING} ha sido expulsado. Razón: ({STRING}) +STR_NETWORK_ERROR_COORDINATOR_REGISTRATION_FAILED :{WHITE}El registro del servidor falló +STR_NETWORK_ERROR_COORDINATOR_ISOLATED :{WHITE}Tu servidor no permite conexiones remotas +STR_NETWORK_ERROR_COORDINATOR_ISOLATED_DETAIL :{WHITE}Otros jugadores no podrán conectarse a tu servidor # Content downloading window STR_CONTENT_TITLE :{WHITE}Descarga de contenido @@ -2579,13 +2587,13 @@ STR_STATION_BUILD_NOISE :{BLACK}Ruido ge # Landscaping toolbar STR_LANDSCAPING_TOOLBAR :{WHITE}Modificación de terreno STR_LANDSCAPING_TOOLTIP_LOWER_A_CORNER_OF_LAND :{BLACK}Disminuir una esquina del terreno. Al arrastrar el ratón se reduce la primera esquina elegida y se nivela el resto del terreno seleccionado a dicha altura. Ctrl para seleccionar un área en diagonal. -STR_LANDSCAPING_TOOLTIP_RAISE_A_CORNER_OF_LAND :{BLACK}Elevar una esquina del terreno. Al arrastrar el ratón se eleva la primera esquina elegida y se nivela el resto del terreno seleccionado a dicha altura. Ctrl para seleccionar un área en diagonal. Mayús muestra una estimación del precio -STR_LANDSCAPING_LEVEL_LAND_TOOLTIP :{BLACK}Nivela un área de terreno a la altura de la primera esquina seleccionada. Ctrl para seleccionar un área en diagonal. Mayús muestra una estimación del precio -STR_LANDSCAPING_TOOLTIP_PURCHASE_LAND :{BLACK}Comprar terreno para usos futuros. Mayús muestra una estimación del precio +STR_LANDSCAPING_TOOLTIP_RAISE_A_CORNER_OF_LAND :{BLACK}Elevar una esquina del terreno. Al arrastrar el ratón se eleva la primera esquina elegida y se nivela el resto del terreno seleccionado a dicha altura. Ctrl para seleccionar un área en diagonal. Mayús muestra un precio estimado +STR_LANDSCAPING_LEVEL_LAND_TOOLTIP :{BLACK}Nivelar un área de terreno a la altura de la primera esquina seleccionada. Ctrl para seleccionar un área en diagonal. Mayús muestra un precio estimado +STR_LANDSCAPING_TOOLTIP_PURCHASE_LAND :{BLACK}Comprar terreno para usos futuros. Mayús muestra un precio estimado # Object construction window STR_OBJECT_BUILD_CAPTION :{WHITE}Selección de objeto -STR_OBJECT_BUILD_TOOLTIP :{BLACK}Elegir una estructura u objeto a construir. Mayús muestra una estimación del precio +STR_OBJECT_BUILD_TOOLTIP :{BLACK}Elegir una estructura u objeto a construir. Mayús muestra un precio estimado STR_OBJECT_BUILD_CLASS_TOOLTIP :{BLACK}Elegir el tipo de objeto o estructura a construir STR_OBJECT_BUILD_PREVIEW_TOOLTIP :{BLACK}Vista previa del objeto STR_OBJECT_BUILD_SIZE :{BLACK}Tamaño: {GOLD}{NUM}×{NUM} casillas @@ -3327,7 +3335,7 @@ STR_STATION_VIEW_EXCLUSIVE_RIGHTS_COMPANY :{YELLOW}{COMPAN STR_STATION_VIEW_RATINGS_BUTTON :{BLACK}Evaluación STR_STATION_VIEW_RATINGS_TOOLTIP :{BLACK}Ver evaluación de la estación -STR_STATION_VIEW_SUPPLY_RATINGS_TITLE :{BLACK}Suministro mensual y evaluación local: +STR_STATION_VIEW_SUPPLY_RATINGS_TITLE :{BLACK}Abastecimiento mensual y evaluación local: STR_STATION_VIEW_CARGO_SUPPLY_RATING :{WHITE}{STRING}: {YELLOW}{COMMA}/{STRING} ({COMMA}%) STR_STATION_VIEW_GROUP :{BLACK}Agrupar por @@ -3443,7 +3451,7 @@ STR_COMPANY_VIEW_BUILD_HQ_TOOLTIP :{BLACK}Construi STR_COMPANY_VIEW_VIEW_HQ_BUTTON :{BLACK}Ver sede STR_COMPANY_VIEW_VIEW_HQ_TOOLTIP :{BLACK}Ver edificio de las oficinas centrales de la empresa STR_COMPANY_VIEW_RELOCATE_HQ :{BLACK}Reubicar sede -STR_COMPANY_VIEW_RELOCATE_COMPANY_HEADQUARTERS :{BLACK}Reubicar la sede de la empresa a cualquier otro lugar con el costo del 1% del valor total de la empresa. Mayús+Clic muestra una estimación del precio sin mover la sede +STR_COMPANY_VIEW_RELOCATE_COMPANY_HEADQUARTERS :{BLACK}Reubicar la sede de la empresa pagando el 1% del valor total de la empresa. Mayús+Clic muestra un precio estimado sin reubicar la sede STR_COMPANY_VIEW_INFRASTRUCTURE_BUTTON :{BLACK}Detalles STR_COMPANY_VIEW_INFRASTRUCTURE_TOOLTIP :{BLACK}Ver informe detallado de infraestructura STR_COMPANY_VIEW_GIVE_MONEY_BUTTON :{BLACK}Transferir capital @@ -3460,8 +3468,8 @@ STR_COMPANY_VIEW_PRESIDENT_NAME_TOOLTIP :{BLACK}Cambiar STR_COMPANY_VIEW_BUY_SHARE_BUTTON :{BLACK}Comprar un 25% de las acciones STR_COMPANY_VIEW_SELL_SHARE_BUTTON :{BLACK}Vender 25% de las acciones -STR_COMPANY_VIEW_BUY_SHARE_TOOLTIP :{BLACK}Comprar 25% de las acciones de esta empresa. Mayús+Clic muestra una estimación del precio sin comprar ninguna acción -STR_COMPANY_VIEW_SELL_SHARE_TOOLTIP :{BLACK}Vender 25% de las acciones de esta empresa. Mayús+Clic muestra una estimación del beneficio sin vender ninguna acción +STR_COMPANY_VIEW_BUY_SHARE_TOOLTIP :{BLACK}Comprar 25% de las acciones de esta empresa. Mayús+Clic muestra un precio estimado sin comprar nada +STR_COMPANY_VIEW_SELL_SHARE_TOOLTIP :{BLACK}Vender 25% de las acciones de esta empresa. Mayús+Clic muestra el ingreso estimado sin vender ninguna acción STR_COMPANY_VIEW_COMPANY_NAME_QUERY_CAPTION :Nombre de la empresa STR_COMPANY_VIEW_PRESIDENT_S_NAME_QUERY_CAPTION :Nombre del presidente @@ -3641,14 +3649,14 @@ STR_BUY_VEHICLE_ROAD_VEHICLE_BUY_REFIT_VEHICLE_BUTTON :{BLACK}Comprar STR_BUY_VEHICLE_SHIP_BUY_REFIT_VEHICLE_BUTTON :{BLACK}Comprar y reformar barco STR_BUY_VEHICLE_AIRCRAFT_BUY_REFIT_VEHICLE_BUTTON :{BLACK}Comprar y reformar aeronave -STR_BUY_VEHICLE_TRAIN_BUY_VEHICLE_TOOLTIP :{BLACK}Comprar el tren elegido. Mayús+Clic muestra una estimación del precio sin realizar la compra -STR_BUY_VEHICLE_ROAD_VEHICLE_BUY_VEHICLE_TOOLTIP :{BLACK}Comprar el vehículo de carretera elegido. Mayús+Clic muestra una estimación del precio sin realizar la compra -STR_BUY_VEHICLE_SHIP_BUY_VEHICLE_TOOLTIP :{BLACK}Comprar el barco elegido. Mayús+Clic muestra una estimación del precio sin realizar la compra -STR_BUY_VEHICLE_AIRCRAFT_BUY_VEHICLE_TOOLTIP :{BLACK}Comprar la aeronave elegida. Mayús+Clic muestra una estimación del precio sin realizar la compra +STR_BUY_VEHICLE_TRAIN_BUY_VEHICLE_TOOLTIP :{BLACK}Comprar el tren elegido. Mayús+Clic muestra un precio estimado sin comprar nada +STR_BUY_VEHICLE_ROAD_VEHICLE_BUY_VEHICLE_TOOLTIP :{BLACK}Comprar el vehículo de carretera elegido. Mayús+Clic muestra un precio estimado sin comprar nada +STR_BUY_VEHICLE_SHIP_BUY_VEHICLE_TOOLTIP :{BLACK}Comprar el barco elegido. Mayús+Clic muestra un precio estimado sin comprar nada +STR_BUY_VEHICLE_AIRCRAFT_BUY_VEHICLE_TOOLTIP :{BLACK}Comprar la aeronave elegida. Mayús+Clic muestra un precio estimado sin comprar nada -STR_BUY_VEHICLE_TRAIN_BUY_REFIT_VEHICLE_TOOLTIP :{BLACK}Comprar y reformar tren seleccionado. Mayús+Clic muestra una estimación del precio sin realizar la compra -STR_BUY_VEHICLE_ROAD_VEHICLE_BUY_REFIT_VEHICLE_TOOLTIP :{BLACK}Comprar y reformar vehículo seleccionado. Mayús+Clic muestra una estimación del precio sin realizar la compra -STR_BUY_VEHICLE_SHIP_BUY_REFIT_VEHICLE_TOOLTIP :{BLACK}Comprar y reformar barco seleccionado. Mayús+Clic muestra una estimación del precio sin realizar la compra +STR_BUY_VEHICLE_TRAIN_BUY_REFIT_VEHICLE_TOOLTIP :{BLACK}Comprar y reformar el tren elegido. Mayús+Clic muestra un precio estimado sin comprar nada +STR_BUY_VEHICLE_ROAD_VEHICLE_BUY_REFIT_VEHICLE_TOOLTIP :{BLACK}Comprar y reformar el vehículo de carretera elegido. Mayús+Clic muestra un precio estimado sin comprar nada +STR_BUY_VEHICLE_SHIP_BUY_REFIT_VEHICLE_TOOLTIP :{BLACK}Comprar y reformar el barco elegido. Mayús+Clic muestra un precio estimado sin comprar nada STR_BUY_VEHICLE_AIRCRAFT_BUY_REFIT_VEHICLE_TOOLTIP :{BLACK}Comprar y reformar la areonave seleccionada. Mayús+Clic muestra un precio estimado sin realizar la compra STR_BUY_VEHICLE_TRAIN_RENAME_BUTTON :{BLACK}Cambiar nombre @@ -3729,10 +3737,10 @@ STR_DEPOT_CLONE_ROAD_VEHICLE :{BLACK}Clonar STR_DEPOT_CLONE_SHIP :{BLACK}Clonar STR_DEPOT_CLONE_AIRCRAFT :{BLACK}Clonar -STR_DEPOT_CLONE_TRAIN_DEPOT_INFO :{BLACK}Esto comprará una copia idéntica del tren, incluyendo todos sus vagones y carros. Clic en este botón y después en el tren a copiar. Ctrl+Clic para compartir las órdenes. Mayús+Clic muestra una estimación del precio sin realizar la compra -STR_DEPOT_CLONE_ROAD_VEHICLE_DEPOT_INFO :{BLACK}Esto comprará una copia idéntica del vehículo de carretera. Clic en este botón y después en el vehículo a copiar. Ctrl+Clic para compartir las órdenes. Mayús+Clic muestra una estimación del precio sin realizar la compra -STR_DEPOT_CLONE_SHIP_DEPOT_INFO :{BLACK}Esto comprará una copia idéntica del barco. Clic en este botón y después en el barco a copiar. Ctrl+Clic para compartir las órdenes. Mayús+Clic muestra una estimación del precio sin realizar la compra -STR_DEPOT_CLONE_AIRCRAFT_INFO_HANGAR_WINDOW :{BLACK}Esto comprará una copia idéntica de la aeronave. Clic en este botón y después en la aeronave a copiar. Ctrl+Clic para compartir las órdenes. Mayús+Clic muestra una estimación del precio sin realizar la compra +STR_DEPOT_CLONE_TRAIN_DEPOT_INFO :{BLACK}Esto comprará una copia del tren y sus vagones. Clic en este botón y después en el tren a copiar. Ctrl+Clic para compartir las órdenes. Mayús+Clic muestra un precio estimado sin comprar nada +STR_DEPOT_CLONE_ROAD_VEHICLE_DEPOT_INFO :{BLACK}Esto comprará una copia del vehículo de carretera. Clic en este botón y después en el vehículo a copiar. Ctrl+Clic para compartir las órdenes. Mayús+Clic muestra un precio estimado sin comprar nada +STR_DEPOT_CLONE_SHIP_DEPOT_INFO :{BLACK}Esto comprará una copia del barco. Clic en este botón y después en el barco a copiar. Ctrl+Clic para compartir las órdenes. Mayús+Clic muestra un precio estimado sin comprar nada +STR_DEPOT_CLONE_AIRCRAFT_INFO_HANGAR_WINDOW :{BLACK}Esto comprará una copia de la aeronave. Clic en este botón y después en la aeronave a copiar. Ctrl+Clic para compartir las órdenes. Mayús+Clic muestra un precio estimado sin comprar nada STR_DEPOT_TRAIN_LOCATION_TOOLTIP :{BLACK}Centrar la vista en el depósito de trenes. Ctrl+Clic abre una vista aparte STR_DEPOT_ROAD_VEHICLE_LOCATION_TOOLTIP :{BLACK}Centrar la vista en el depósito de vehículos. Ctrl+Clic abre una vista aparte diff --git a/src/lang/swedish.txt b/src/lang/swedish.txt index 8b3dfa73a1..467a608f00 100644 --- a/src/lang/swedish.txt +++ b/src/lang/swedish.txt @@ -2049,7 +2049,6 @@ STR_NETWORK_SERVER_LIST_START_SERVER :{BLACK}Starta s STR_NETWORK_SERVER_LIST_START_SERVER_TOOLTIP :{BLACK}Starta en server för andra att ansluta till STR_NETWORK_SERVER_LIST_PLAYER_NAME_OSKTITLE :{BLACK}Mata in ditt namn -STR_NETWORK_SERVER_LIST_ENTER_IP :{BLACK}IP-adressen till servern # Start new multiplayer server STR_NETWORK_START_SERVER_CAPTION :{WHITE}Starta ett nytt spel i flerspelarläge diff --git a/src/lang/thai.txt b/src/lang/thai.txt index ee3b89f145..a5e9ba63bf 100644 --- a/src/lang/thai.txt +++ b/src/lang/thai.txt @@ -1864,7 +1864,6 @@ STR_NETWORK_SERVER_LIST_START_SERVER :{BLACK}เร STR_NETWORK_SERVER_LIST_START_SERVER_TOOLTIP :{BLACK}เริ่มเซิร์ฟเวอร์ใหม่ของคุณ STR_NETWORK_SERVER_LIST_PLAYER_NAME_OSKTITLE :{BLACK}ป้อนชื่อของคุณ -STR_NETWORK_SERVER_LIST_ENTER_IP :{BLACK}ป้อนที่อยู่ของโฮสต์ # Start new multiplayer server STR_NETWORK_START_SERVER_CAPTION :{WHITE}เริ่มเกมผู้เล่นหลายคนใหม่ diff --git a/src/lang/traditional_chinese.txt b/src/lang/traditional_chinese.txt index d61347a1f1..0b2e8c88ca 100644 --- a/src/lang/traditional_chinese.txt +++ b/src/lang/traditional_chinese.txt @@ -1927,7 +1927,6 @@ STR_NETWORK_SERVER_LIST_START_SERVER :{BLACK}起動 STR_NETWORK_SERVER_LIST_START_SERVER_TOOLTIP :{BLACK}起動您自己的伺服器 STR_NETWORK_SERVER_LIST_PLAYER_NAME_OSKTITLE :{BLACK}輸入您的名稱 -STR_NETWORK_SERVER_LIST_ENTER_IP :{BLACK}輸入主機位址 # Start new multiplayer server STR_NETWORK_START_SERVER_CAPTION :{WHITE}開始新的多人遊戲 diff --git a/src/lang/turkish.txt b/src/lang/turkish.txt index 43ec344f4e..ae36780e1a 100644 --- a/src/lang/turkish.txt +++ b/src/lang/turkish.txt @@ -2041,7 +2041,6 @@ STR_NETWORK_SERVER_LIST_START_SERVER :{BLACK}Sunucu b STR_NETWORK_SERVER_LIST_START_SERVER_TOOLTIP :{BLACK}Kendi sunucunu başlat STR_NETWORK_SERVER_LIST_PLAYER_NAME_OSKTITLE :{BLACK}İsminizi girin -STR_NETWORK_SERVER_LIST_ENTER_IP :{BLACK}Sunucunun adresini girin # Start new multiplayer server STR_NETWORK_START_SERVER_CAPTION :{WHITE}Yeni çok oyunculu oyun başlat diff --git a/src/lang/ukrainian.txt b/src/lang/ukrainian.txt index 8d17d91b34..5faa22317a 100644 --- a/src/lang/ukrainian.txt +++ b/src/lang/ukrainian.txt @@ -2161,7 +2161,6 @@ STR_NETWORK_SERVER_LIST_START_SERVER :{BLACK}Ство STR_NETWORK_SERVER_LIST_START_SERVER_TOOLTIP :{BLACK}Створити ваш власний сервер STR_NETWORK_SERVER_LIST_PLAYER_NAME_OSKTITLE :{BLACK}Введіть ваше ім'я -STR_NETWORK_SERVER_LIST_ENTER_IP :{BLACK}Введіть адресу сервера # Start new multiplayer server STR_NETWORK_START_SERVER_CAPTION :{WHITE}Створити нову мережеву гру diff --git a/src/lang/urdu.txt b/src/lang/urdu.txt index b3bed8913d..111332245c 100644 --- a/src/lang/urdu.txt +++ b/src/lang/urdu.txt @@ -1526,7 +1526,6 @@ STR_NETWORK_SERVER_LIST_START_SERVER :{BLACK}سرور STR_NETWORK_SERVER_LIST_START_SERVER_TOOLTIP :{BLACK}اپنا سرور خود شروع کریں STR_NETWORK_SERVER_LIST_PLAYER_NAME_OSKTITLE :{BLACK}اپنا نام لکھیں -STR_NETWORK_SERVER_LIST_ENTER_IP :{BLACK}ہوسٹ کا ایڈریس لکھیں # Start new multiplayer server STR_NETWORK_START_SERVER_CAPTION :{WHITE}نیا زیادہ کھلاڑیوں والا کھیل شروع کریں diff --git a/src/lang/vietnamese.txt b/src/lang/vietnamese.txt index 8d4139fa81..e008681657 100644 --- a/src/lang/vietnamese.txt +++ b/src/lang/vietnamese.txt @@ -2049,7 +2049,6 @@ STR_NETWORK_SERVER_LIST_START_SERVER :{BLACK}Tạo se STR_NETWORK_SERVER_LIST_START_SERVER_TOOLTIP :{BLACK}Tạo server của riêng bạn STR_NETWORK_SERVER_LIST_PLAYER_NAME_OSKTITLE :{BLACK}Gõ tên của bạn -STR_NETWORK_SERVER_LIST_ENTER_IP :{BLACK}Gõ địa chỉ của server # Start new multiplayer server STR_NETWORK_START_SERVER_CAPTION :{WHITE}Bắt đầu ván chơi mạng nhiều người diff --git a/src/lang/welsh.txt b/src/lang/welsh.txt index 30865c4d46..b05c6e6815 100644 --- a/src/lang/welsh.txt +++ b/src/lang/welsh.txt @@ -1921,7 +1921,6 @@ STR_NETWORK_SERVER_LIST_START_SERVER :{BLACK}Dechrau STR_NETWORK_SERVER_LIST_START_SERVER_TOOLTIP :{BLACK}Dechrau eich gweinydd eich hun STR_NETWORK_SERVER_LIST_PLAYER_NAME_OSKTITLE :{BLACK}Rhowch eich enw -STR_NETWORK_SERVER_LIST_ENTER_IP :{BLACK}Rhowch gyfeiriad y gwesteiwr # Start new multiplayer server STR_NETWORK_START_SERVER_CAPTION :{WHITE}Dechrau gêm newydd amlchwaraewr From b6a116a2479eb62aece540633c41d0f54a481fd6 Mon Sep 17 00:00:00 2001 From: Patric Stout Date: Sun, 11 Jul 2021 21:57:05 +0200 Subject: [PATCH 30/44] Add: allow setting your server visibility to "invite-only" (#9434) In this mode you do register to the Game Coordinator, but your server will not show up in the public server listing. You can give your friends the invite code of the server with which they can join. --- docs/multiplayer.md | 11 +++++-- src/console_cmds.cpp | 1 - src/lang/english.txt | 3 ++ src/network/network.cpp | 25 +++++++++++++++- src/network/network_coordinator.cpp | 19 +++++++++---- src/network/network_func.h | 1 + src/network/network_gui.cpp | 38 +++++++++++++------------ src/network/network_type.h | 1 + src/settings_type.h | 2 ++ src/table/settings/network_settings.ini | 16 +++++++++-- 10 files changed, 86 insertions(+), 31 deletions(-) diff --git a/docs/multiplayer.md b/docs/multiplayer.md index daccbf06d6..ece0c04dfe 100644 --- a/docs/multiplayer.md +++ b/docs/multiplayer.md @@ -48,6 +48,10 @@ Last updated: 2011-02-16 - click add server - type in the ip address or hostname - if you want to add a port use : + - If you want to play and you have the invite code of the game server you + want connect to. + - click add server + - type in the invite code - Now you can select a company and press: "Join company", to help that company - Or you can press "Spectate game", to spectate the game - Or you can press "New company", and start your own company (if there are @@ -110,9 +114,10 @@ Last updated: 2011-02-16 - You can let your server automatically restart a map when, let's say, year 2030 is reached. See 'set restart_game_date' for detail. - - If you want to be on the server-list, enable Advertising. To do this, select - 'Internet (advertise)' in the Start Server menu, or type in console: - 'set server_advertise 1'. + - If you want to be on the server-list, make your server public. You can do + this either from the Start Server GUI, via the in-game Online Players GUI, + or by typing in the console: + 'set server_game_type 1'. - You can protect your server with a password via the console: 'set server_pw', or via the Start Server menu. diff --git a/src/console_cmds.cpp b/src/console_cmds.cpp index 0fcac44880..fd7f09e3c4 100644 --- a/src/console_cmds.cpp +++ b/src/console_cmds.cpp @@ -2420,7 +2420,6 @@ void IConsoleStdLibRegister() IConsole::AliasRegister("name", "setting client_name %+"); IConsole::AliasRegister("server_name", "setting server_name %+"); IConsole::AliasRegister("server_port", "setting server_port %+"); - IConsole::AliasRegister("server_advertise", "setting server_advertise %+"); IConsole::AliasRegister("max_clients", "setting max_clients %+"); IConsole::AliasRegister("max_companies", "setting max_companies %+"); IConsole::AliasRegister("max_spectators", "setting max_spectators %+"); diff --git a/src/lang/english.txt b/src/lang/english.txt index 813705958b..baf94c2ee4 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -1995,8 +1995,11 @@ STR_FACE_TIE :Tie: STR_FACE_EARRING :Earring: STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}Change tie or earring +############ Next lines match ServerGameType STR_NETWORK_SERVER_VISIBILITY_LOCAL :Local STR_NETWORK_SERVER_VISIBILITY_PUBLIC :Public +STR_NETWORK_SERVER_VISIBILITY_INVITE_ONLY :Invite only +############ End of leave-in-this-order # Network server list STR_NETWORK_SERVER_LIST_CAPTION :{WHITE}Multiplayer diff --git a/src/network/network.cpp b/src/network/network.cpp index f8138bbbc1..d35fbe7469 100644 --- a/src/network/network.cpp +++ b/src/network/network.cpp @@ -934,7 +934,7 @@ bool NetworkServerStart() NetworkInitGameInfo(); - if (_settings_client.network.server_advertise) { + if (_settings_client.network.server_game_type != SERVER_GAME_TYPE_LOCAL) { _network_coordinator_client.Register(); } @@ -999,6 +999,29 @@ void NetworkDisconnect(bool blocking, bool close_admins) NetworkUDPInitialize(); } +/** + * The setting server_game_type was updated; possibly we need to take some + * action. + */ +void NetworkUpdateServerGameType() +{ + if (!_networking) return; + + switch (_settings_client.network.server_game_type) { + case SERVER_GAME_TYPE_LOCAL: + _network_coordinator_client.CloseConnection(); + break; + + case SERVER_GAME_TYPE_INVITE_ONLY: + case SERVER_GAME_TYPE_PUBLIC: + _network_coordinator_client.Register(); + break; + + default: + NOT_REACHED(); + } +} + /** * Receives something from the network. * @return true if everything went fine, false when the connection got closed. diff --git a/src/network/network_coordinator.cpp b/src/network/network_coordinator.cpp index 8bd81b6f62..22b303f005 100644 --- a/src/network/network_coordinator.cpp +++ b/src/network/network_coordinator.cpp @@ -94,8 +94,8 @@ bool ClientNetworkCoordinatorSocketHandler::Receive_GC_ERROR(Packet *p) SetDParamStr(0, detail); ShowErrorMessage(STR_NETWORK_ERROR_COORDINATOR_REGISTRATION_FAILED, STR_JUST_RAW_STRING, WL_ERROR); - /* To prevent that we constantly try to reconnect, switch to private game. */ - _settings_client.network.server_advertise = false; + /* To prevent that we constantly try to reconnect, switch to local game. */ + _settings_client.network.server_game_type = SERVER_GAME_TYPE_LOCAL; this->CloseConnection(); return false; @@ -153,9 +153,18 @@ bool ClientNetworkCoordinatorSocketHandler::Receive_GC_REGISTER_ACK(Packet *p) default: connection_type = "Unknown"; break; // Should never happen, but don't fail if it does. } + std::string game_type; + switch (_settings_client.network.server_game_type) { + case SERVER_GAME_TYPE_INVITE_ONLY: game_type = "Invite only"; break; + case SERVER_GAME_TYPE_PUBLIC: game_type = "Public"; break; + + case SERVER_GAME_TYPE_LOCAL: // Impossible to register local servers. + default: game_type = "Unknown"; break; // Should never happen, but don't fail if it does. + } + Debug(net, 3, "----------------------------------------"); Debug(net, 3, "Your server is now registered with the Game Coordinator:"); - Debug(net, 3, " Game type: Public"); + Debug(net, 3, " Game type: {}", game_type); Debug(net, 3, " Connection type: {}", connection_type); Debug(net, 3, " Invite code: {}", _network_server_invite_code); Debug(net, 3, "----------------------------------------"); @@ -298,7 +307,7 @@ void ClientNetworkCoordinatorSocketHandler::Register() Packet *p = new Packet(PACKET_COORDINATOR_SERVER_REGISTER); p->Send_uint8(NETWORK_COORDINATOR_VERSION); - p->Send_uint8(SERVER_GAME_TYPE_PUBLIC); + p->Send_uint8(_settings_client.network.server_game_type); p->Send_uint16(_settings_client.network.server_port); if (_settings_client.network.server_invite_code.empty() || _settings_client.network.server_invite_code_secret.empty()) { p->Send_string(""); @@ -467,7 +476,7 @@ void ClientNetworkCoordinatorSocketHandler::CloseAllTokens() void ClientNetworkCoordinatorSocketHandler::SendReceive() { /* Private games are not listed via the Game Coordinator. */ - if (_network_server && !_settings_client.network.server_advertise) { + if (_network_server && _settings_client.network.server_game_type == SERVER_GAME_TYPE_LOCAL) { if (this->sock != INVALID_SOCKET) { this->CloseConnection(); } diff --git a/src/network/network_func.h b/src/network/network_func.h index 6da8fb5cc1..c0f68f9157 100644 --- a/src/network/network_func.h +++ b/src/network/network_func.h @@ -39,6 +39,7 @@ bool NetworkValidateOurClientName(); bool NetworkValidateClientName(std::string &client_name); bool NetworkValidateServerName(std::string &server_name); void NetworkUpdateClientName(const std::string &client_name); +void NetworkUpdateServerGameType(); bool NetworkCompanyHasClients(CompanyID company); std::string NetworkChangeCompanyPassword(CompanyID company_id, std::string password); void NetworkReboot(); diff --git a/src/network/network_gui.cpp b/src/network/network_gui.cpp index a0d2d47c84..d2fefb5cc3 100644 --- a/src/network/network_gui.cpp +++ b/src/network/network_gui.cpp @@ -60,16 +60,6 @@ static const int NETWORK_LIST_REFRESH_DELAY = 30; ///< Time, in seconds, between static ClientID _admin_client_id = INVALID_CLIENT_ID; ///< For what client a confirmation window is open. static CompanyID _admin_company_id = INVALID_COMPANY; ///< For what company a confirmation window is open. -/** - * Visibility of the server. Public servers advertise, where private servers - * do not. - */ -static const StringID _server_visibility_dropdown[] = { - STR_NETWORK_SERVER_VISIBILITY_LOCAL, - STR_NETWORK_SERVER_VISIBILITY_PUBLIC, - INVALID_STRING_ID -}; - /** * Update the network new window because a new server is * found on the network. @@ -79,6 +69,17 @@ void UpdateNetworkGameWindow() InvalidateWindowData(WC_NETWORK_WINDOW, WN_NETWORK_WINDOW_GAME, 0); } +static DropDownList BuildVisibilityDropDownList() +{ + DropDownList list; + + list.emplace_back(new DropDownListStringItem(STR_NETWORK_SERVER_VISIBILITY_LOCAL, SERVER_GAME_TYPE_LOCAL, false)); + list.emplace_back(new DropDownListStringItem(STR_NETWORK_SERVER_VISIBILITY_INVITE_ONLY, SERVER_GAME_TYPE_INVITE_ONLY, false)); + list.emplace_back(new DropDownListStringItem(STR_NETWORK_SERVER_VISIBILITY_PUBLIC, SERVER_GAME_TYPE_PUBLIC, false)); + + return list; +} + typedef GUIList GUIGameServerList; typedef int ServerListPosition; static const ServerListPosition SLP_INVALID = -1; @@ -1015,7 +1016,7 @@ struct NetworkStartServerWindow : public Window { { switch (widget) { case WID_NSS_CONNTYPE_BTN: - SetDParam(0, _server_visibility_dropdown[_settings_client.network.server_advertise]); + SetDParam(0, STR_NETWORK_SERVER_VISIBILITY_LOCAL + _settings_client.network.server_game_type); break; case WID_NSS_CLIENTS_TXT: @@ -1036,7 +1037,7 @@ struct NetworkStartServerWindow : public Window { { switch (widget) { case WID_NSS_CONNTYPE_BTN: - *size = maxdim(GetStringBoundingBox(_server_visibility_dropdown[0]), GetStringBoundingBox(_server_visibility_dropdown[1])); + *size = maxdim(maxdim(GetStringBoundingBox(STR_NETWORK_SERVER_VISIBILITY_LOCAL), GetStringBoundingBox(STR_NETWORK_SERVER_VISIBILITY_PUBLIC)), GetStringBoundingBox(STR_NETWORK_SERVER_VISIBILITY_INVITE_ONLY)); size->width += padding.width; size->height += padding.height; break; @@ -1066,7 +1067,7 @@ struct NetworkStartServerWindow : public Window { break; case WID_NSS_CONNTYPE_BTN: // Connection type - ShowDropDownMenu(this, _server_visibility_dropdown, _settings_client.network.server_advertise, WID_NSS_CONNTYPE_BTN, 0, 0); // do it for widget WID_NSS_CONNTYPE_BTN + ShowDropDownList(this, BuildVisibilityDropDownList(), _settings_client.network.server_game_type, WID_NSS_CONNTYPE_BTN); break; case WID_NSS_CLIENTS_BTND: case WID_NSS_CLIENTS_BTNU: // Click on up/down button for number of clients @@ -1144,7 +1145,7 @@ struct NetworkStartServerWindow : public Window { { switch (widget) { case WID_NSS_CONNTYPE_BTN: - _settings_client.network.server_advertise = (index != 0); + _settings_client.network.server_game_type = (ServerGameType)index; break; default: NOT_REACHED(); @@ -2041,7 +2042,7 @@ public: { switch (widget) { case WID_CL_SERVER_VISIBILITY: - *size = maxdim(GetStringBoundingBox(_server_visibility_dropdown[0]), GetStringBoundingBox(_server_visibility_dropdown[1])); + *size = maxdim(maxdim(GetStringBoundingBox(STR_NETWORK_SERVER_VISIBILITY_LOCAL), GetStringBoundingBox(STR_NETWORK_SERVER_VISIBILITY_PUBLIC)), GetStringBoundingBox(STR_NETWORK_SERVER_VISIBILITY_INVITE_ONLY)); size->width += padding.width; size->height += padding.height; break; @@ -2073,7 +2074,7 @@ public: break; case WID_CL_SERVER_VISIBILITY: - SetDParam(0, _server_visibility_dropdown[_settings_client.network.server_advertise]); + SetDParam(0, STR_NETWORK_SERVER_VISIBILITY_LOCAL + _settings_client.network.server_game_type); break; case WID_CL_SERVER_INVITE_CODE: { @@ -2117,7 +2118,7 @@ public: case WID_CL_SERVER_VISIBILITY: if (!_network_server) break; - ShowDropDownMenu(this, _server_visibility_dropdown, _settings_client.network.server_advertise, WID_CL_SERVER_VISIBILITY, 0, 0); + ShowDropDownList(this, BuildVisibilityDropDownList(), _settings_client.network.server_game_type, WID_CL_SERVER_VISIBILITY); break; case WID_CL_MATRIX: { @@ -2183,7 +2184,8 @@ public: case WID_CL_SERVER_VISIBILITY: if (!_network_server) break; - _settings_client.network.server_advertise = (index != 0); + _settings_client.network.server_game_type = (ServerGameType)index; + NetworkUpdateServerGameType(); break; case WID_CL_MATRIX: { diff --git a/src/network/network_type.h b/src/network/network_type.h index 6e6fe33ded..a3fb217fe4 100644 --- a/src/network/network_type.h +++ b/src/network/network_type.h @@ -42,6 +42,7 @@ enum NetworkVehicleType { enum ServerGameType : uint8 { SERVER_GAME_TYPE_LOCAL = 0, SERVER_GAME_TYPE_PUBLIC, + SERVER_GAME_TYPE_INVITE_ONLY, }; /** 'Unique' identifier to be given to clients */ diff --git a/src/settings_type.h b/src/settings_type.h index 79e462c3e2..93e2f049ff 100644 --- a/src/settings_type.h +++ b/src/settings_type.h @@ -15,6 +15,7 @@ #include "town_type.h" #include "transport_type.h" #include "network/core/config.h" +#include "network/network_type.h" #include "company_type.h" #include "cargotype.h" #include "linkgraph/linkgraph_type.h" @@ -266,6 +267,7 @@ struct NetworkSettings { uint16 server_port; ///< port the server listens on uint16 server_admin_port; ///< port the server listens on for the admin network bool server_admin_chat; ///< allow private chat for the server to be distributed to the admin network + ServerGameType server_game_type; ///< Server type: local / public / invite-only. std::string server_invite_code; ///< Invite code to use when registering as server. std::string server_invite_code_secret; ///< Secret to proof we got this invite code from the Game Coordinator. std::string server_name; ///< name of the server diff --git a/src/table/settings/network_settings.ini b/src/table/settings/network_settings.ini index 45459b6e1f..552f588234 100644 --- a/src/table/settings/network_settings.ini +++ b/src/table/settings/network_settings.ini @@ -9,14 +9,18 @@ [pre-amble] static void UpdateClientConfigValues(); +static std::initializer_list _server_game_type{"local", "public", "invite-only"}; + static const SettingVariant _network_settings_table[] = { [post-amble] }; [templates] SDTC_BOOL = SDTC_BOOL( $var, $flags, $def, $str, $strhelp, $strval, $pre_cb, $post_cb, $from, $to, $cat, $extra, $startup), +SDTC_OMANY = SDTC_OMANY( $var, $type, $flags, $def, $max, $full, $str, $strhelp, $strval, $pre_cb, $post_cb, $from, $to, $cat, $extra, $startup), SDTC_VAR = SDTC_VAR( $var, $type, $flags, $def, $min, $max, $interval, $str, $strhelp, $strval, $pre_cb, $post_cb, $from, $to, $cat, $extra, $startup), [validation] +SDTC_OMANY = static_assert($max <= MAX_$type, "Maximum value for $var exceeds storage size"); SDTC_VAR = static_assert($max <= MAX_$type, "Maximum value for $var exceeds storage size"); [defaults] @@ -159,10 +163,16 @@ flags = SF_NOT_IN_SAVE | SF_NO_NETWORK_SYNC | SF_NETWORK_ONLY def = true cat = SC_EXPERT -[SDTC_BOOL] -var = network.server_advertise +[SDTC_OMANY] +var = network.server_game_type +type = SLE_UINT8 flags = SF_NOT_IN_SAVE | SF_NO_NETWORK_SYNC | SF_NETWORK_ONLY -def = false +def = SERVER_GAME_TYPE_LOCAL +min = SERVER_GAME_TYPE_LOCAL +max = SERVER_GAME_TYPE_INVITE_ONLY +full = _server_game_type +post_cb = [](auto) { NetworkUpdateServerGameType(); } +cat = SC_BASIC [SDTC_BOOL] var = network.autoclean_companies From 178ea3196b0aed0a2a081801f92e1deb06d12628 Mon Sep 17 00:00:00 2001 From: Rubidium Date: Sun, 11 Jul 2021 21:22:29 +0200 Subject: [PATCH 31/44] Remove: includes to network/core/config.h from headers when only three cpp files need it --- src/network/network_type.h | 2 -- src/newgrf.cpp | 2 +- src/settings.cpp | 1 + src/settings_table.cpp | 1 + src/settings_type.h | 1 - 5 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/network/network_type.h b/src/network/network_type.h index a3fb217fe4..8564bef83b 100644 --- a/src/network/network_type.h +++ b/src/network/network_type.h @@ -10,8 +10,6 @@ #ifndef NETWORK_TYPE_H #define NETWORK_TYPE_H -#include "core/config.h" - /** How many clients can we have */ static const uint MAX_CLIENTS = 255; diff --git a/src/newgrf.cpp b/src/newgrf.cpp index d5dfc427c6..7c7d55dea7 100644 --- a/src/newgrf.cpp +++ b/src/newgrf.cpp @@ -38,7 +38,7 @@ #include "strings_func.h" #include "date_func.h" #include "string_func.h" -#include "network/network.h" +#include "network/core/config.h" #include #include "smallmap_gui.h" #include "genworld.h" diff --git a/src/settings.cpp b/src/settings.cpp index a90dad5378..d7a8550654 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -28,6 +28,7 @@ #include "currency.h" #include "network/network.h" #include "network/network_func.h" +#include "network/core/config.h" #include "command_func.h" #include "console_func.h" #include "genworld.h" diff --git a/src/settings_table.cpp b/src/settings_table.cpp index 045d8e56c4..27fb4ef08c 100644 --- a/src/settings_table.cpp +++ b/src/settings_table.cpp @@ -15,6 +15,7 @@ #include "screenshot.h" #include "network/network.h" #include "network/network_func.h" +#include "network/core/config.h" #include "pathfinder/pathfinder_type.h" #include "genworld.h" #include "train.h" diff --git a/src/settings_type.h b/src/settings_type.h index 93e2f049ff..8db0febad6 100644 --- a/src/settings_type.h +++ b/src/settings_type.h @@ -14,7 +14,6 @@ #include "economy_type.h" #include "town_type.h" #include "transport_type.h" -#include "network/core/config.h" #include "network/network_type.h" #include "company_type.h" #include "cargotype.h" From dd7f69be6ed6ea90ec806c86957f10332206bcd6 Mon Sep 17 00:00:00 2001 From: Patric Stout Date: Sun, 11 Jul 2021 22:37:14 +0200 Subject: [PATCH 32/44] Fix: "Search LAN games" used the socket after it was closed (#9437) Every outgoing connection, either TCP or UDP, triggered NetworkInitialize(), which triggered NetworkUDPInitialize() which first closes all connections. Now the problem was that "Search LAN games" found a server, added it to the list, after which (over TCP) it queries the server. This closes all UDP sockets (as that makes sense, I guess?), while the UDP was still reading from it. Solve this by simply stop initializing UDP every time we make an outgoing TCP connection; instead only do it on start-up. --- src/network/network.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/network/network.cpp b/src/network/network.cpp index d35fbe7469..d5d6d8282d 100644 --- a/src/network/network.cpp +++ b/src/network/network.cpp @@ -615,7 +615,6 @@ void NetworkClose(bool close_admins) static void NetworkInitialize(bool close_admins = true) { InitializeNetworkPools(close_admins); - NetworkUDPInitialize(); _sync_frame = 0; _network_first_time = true; @@ -907,6 +906,7 @@ bool NetworkServerStart() NetworkDisconnect(false, false); NetworkInitialize(false); + NetworkUDPInitialize(); Debug(net, 5, "Starting listeners for clients"); if (!ServerNetworkGameSocketHandler::Listen(_settings_client.network.server_port)) return false; @@ -1292,6 +1292,7 @@ void NetworkStartUp() _network_game_info = {}; NetworkInitialize(); + NetworkUDPInitialize(); Debug(net, 3, "Network online, multiplayer available"); NetworkFindBroadcastIPs(&_broadcast_list); } From c71f06e59b57f256ee8089f44bc8a5f14234db29 Mon Sep 17 00:00:00 2001 From: Rubidium Date: Sun, 11 Jul 2021 22:41:04 +0200 Subject: [PATCH 33/44] Feature: parse the console settings the same way as config settings Now you can use things like `set server_game_type public` instead of having to guess the number, which would not be written into the configuration file nor would it be shown when doing `set server_game_type`. --- docs/multiplayer.md | 2 +- src/settings.cpp | 13 ++++++------- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/docs/multiplayer.md b/docs/multiplayer.md index ece0c04dfe..c7e1839cf3 100644 --- a/docs/multiplayer.md +++ b/docs/multiplayer.md @@ -117,7 +117,7 @@ Last updated: 2011-02-16 - If you want to be on the server-list, make your server public. You can do this either from the Start Server GUI, via the in-game Online Players GUI, or by typing in the console: - 'set server_game_type 1'. + 'set server_game_type public'. - You can protect your server with a password via the console: 'set server_pw', or via the Start Server menu. diff --git a/src/settings.cpp b/src/settings.cpp index d7a8550654..0d379778b8 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -1635,15 +1635,14 @@ void IConsoleSetSetting(const char *name, const char *value, bool force_newgame) if (sd->IsStringSetting()) { success = SetSettingValue(sd->AsStringSetting(), value, force_newgame); } else if (sd->IsIntSetting()) { - uint32 val; - extern bool GetArgumentInteger(uint32 *value, const char *arg); - success = GetArgumentInteger(&val, value); - if (!success) { - IConsolePrint(CC_ERROR, "'{}' is not an integer.", value); + const IntSettingDesc *isd = sd->AsIntSetting(); + size_t val = isd->ParseValue(value); + if (!_settings_error_list.empty()) { + IConsolePrint(CC_ERROR, "'{}' is not a valid value for this setting.", value); + _settings_error_list.clear(); return; } - - success = SetSettingValue(sd->AsIntSetting(), val, force_newgame); + success = SetSettingValue(isd, (int32)val, force_newgame); } if (!success) { From 80f4e42627dd48f14fa9b4b8358a1e7c33d0a76d Mon Sep 17 00:00:00 2001 From: translators Date: Mon, 12 Jul 2021 18:51:48 +0000 Subject: [PATCH 34/44] Update: Translations from eints spanish (mexican): 4 changes by absay english (us): 13 changes by 2TallTyler korean: 5 changes by telk5093 german: 13 changes by Wuzzy2 portuguese: 4 changes by azulcosta hindi: 6 changes by ritwikraghav14 --- src/lang/afrikaans.txt | 2 ++ src/lang/arabic_egypt.txt | 2 ++ src/lang/basque.txt | 2 ++ src/lang/belarusian.txt | 2 ++ src/lang/brazilian_portuguese.txt | 2 ++ src/lang/bulgarian.txt | 2 ++ src/lang/catalan.txt | 2 ++ src/lang/chuvash.txt | 2 ++ src/lang/croatian.txt | 2 ++ src/lang/czech.txt | 2 ++ src/lang/danish.txt | 2 ++ src/lang/dutch.txt | 2 ++ src/lang/english_AU.txt | 2 ++ src/lang/english_US.txt | 16 +++++++++++++++- src/lang/esperanto.txt | 2 ++ src/lang/estonian.txt | 2 ++ src/lang/faroese.txt | 2 ++ src/lang/finnish.txt | 2 ++ src/lang/french.txt | 2 ++ src/lang/frisian.txt | 2 ++ src/lang/gaelic.txt | 2 ++ src/lang/galician.txt | 2 ++ src/lang/german.txt | 16 +++++++++++++++- src/lang/greek.txt | 2 ++ src/lang/hebrew.txt | 2 ++ src/lang/hindi.txt | 8 ++++++++ src/lang/hungarian.txt | 2 ++ src/lang/icelandic.txt | 2 ++ src/lang/ido.txt | 2 ++ src/lang/indonesian.txt | 2 ++ src/lang/irish.txt | 2 ++ src/lang/italian.txt | 2 ++ src/lang/japanese.txt | 2 ++ src/lang/korean.txt | 9 +++++++-- src/lang/latin.txt | 2 ++ src/lang/latvian.txt | 2 ++ src/lang/lithuanian.txt | 2 ++ src/lang/luxembourgish.txt | 2 ++ src/lang/macedonian.txt | 2 ++ src/lang/malay.txt | 2 ++ src/lang/maltese.txt | 2 ++ src/lang/marathi.txt | 2 ++ src/lang/norwegian_bokmal.txt | 2 ++ src/lang/norwegian_nynorsk.txt | 2 ++ src/lang/persian.txt | 2 ++ src/lang/polish.txt | 2 ++ src/lang/portuguese.txt | 7 ++++++- src/lang/romanian.txt | 2 ++ src/lang/russian.txt | 2 ++ src/lang/serbian.txt | 2 ++ src/lang/simplified_chinese.txt | 2 ++ src/lang/slovak.txt | 2 ++ src/lang/slovenian.txt | 2 ++ src/lang/spanish.txt | 2 ++ src/lang/spanish_MX.txt | 7 ++++++- src/lang/swedish.txt | 2 ++ src/lang/tamil.txt | 2 ++ src/lang/thai.txt | 2 ++ src/lang/traditional_chinese.txt | 2 ++ src/lang/turkish.txt | 2 ++ src/lang/ukrainian.txt | 2 ++ src/lang/urdu.txt | 2 ++ src/lang/vietnamese.txt | 2 ++ src/lang/welsh.txt | 2 ++ 64 files changed, 173 insertions(+), 6 deletions(-) diff --git a/src/lang/afrikaans.txt b/src/lang/afrikaans.txt index 0db03910db..02c81e2dd4 100644 --- a/src/lang/afrikaans.txt +++ b/src/lang/afrikaans.txt @@ -1910,6 +1910,8 @@ STR_FACE_TIE :Das: STR_FACE_EARRING :Oorbel: STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}Verander das of oorbel +############ Next lines match ServerGameType +############ End of leave-in-this-order # Network server list STR_NETWORK_SERVER_LIST_CAPTION :{WHITE}Multispeler diff --git a/src/lang/arabic_egypt.txt b/src/lang/arabic_egypt.txt index a1bddea785..5a1643dfc0 100644 --- a/src/lang/arabic_egypt.txt +++ b/src/lang/arabic_egypt.txt @@ -1606,6 +1606,8 @@ STR_FACE_TIE :الربطة: STR_FACE_EARRING :أقراط الأذان: STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}غير الربطة أو أقراط الأذن +############ Next lines match ServerGameType +############ End of leave-in-this-order # Network server list STR_NETWORK_SERVER_LIST_CAPTION :{WHITE}تعدد اللاعبين diff --git a/src/lang/basque.txt b/src/lang/basque.txt index 8c22e341dd..27b544899d 100644 --- a/src/lang/basque.txt +++ b/src/lang/basque.txt @@ -1785,6 +1785,8 @@ STR_FACE_TIE :Korbata: STR_FACE_EARRING :Belarritakoak: STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}Aldatu korbata eta belarritakoak +############ Next lines match ServerGameType +############ End of leave-in-this-order # Network server list STR_NETWORK_SERVER_LIST_CAPTION :{WHITE}Multijokalaria diff --git a/src/lang/belarusian.txt b/src/lang/belarusian.txt index 03b09e7eae..1e69efaa99 100644 --- a/src/lang/belarusian.txt +++ b/src/lang/belarusian.txt @@ -2220,6 +2220,8 @@ STR_FACE_TIE :Гальшту STR_FACE_EARRING :Завушніца: STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}Зьмяніць гальштук або завушніцу +############ Next lines match ServerGameType +############ End of leave-in-this-order # Network server list STR_NETWORK_SERVER_LIST_CAPTION :{WHITE}Сеткавая гульня diff --git a/src/lang/brazilian_portuguese.txt b/src/lang/brazilian_portuguese.txt index c63a0fb833..91b25fc1c9 100644 --- a/src/lang/brazilian_portuguese.txt +++ b/src/lang/brazilian_portuguese.txt @@ -1996,7 +1996,9 @@ STR_FACE_TIE :Gravata: STR_FACE_EARRING :Brinco: STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}Alterar gravata ou brinco +############ Next lines match ServerGameType STR_NETWORK_SERVER_VISIBILITY_PUBLIC :Público +############ End of leave-in-this-order # Network server list STR_NETWORK_SERVER_LIST_CAPTION :{WHITE}Multi-jogador diff --git a/src/lang/bulgarian.txt b/src/lang/bulgarian.txt index c4ecac5290..fdd520a2b8 100644 --- a/src/lang/bulgarian.txt +++ b/src/lang/bulgarian.txt @@ -1831,6 +1831,8 @@ STR_FACE_TIE :Вратовр STR_FACE_EARRING :Oбица: STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}Cмени вратовръзкатa или oбицатa +############ Next lines match ServerGameType +############ End of leave-in-this-order # Network server list STR_NETWORK_SERVER_LIST_CAPTION :{WHITE}Онлайн играчи diff --git a/src/lang/catalan.txt b/src/lang/catalan.txt index adbb9b827d..74e6955be3 100644 --- a/src/lang/catalan.txt +++ b/src/lang/catalan.txt @@ -1996,7 +1996,9 @@ STR_FACE_TIE :Corbata: STR_FACE_EARRING :Arracades: STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}Canvia la corbata o les arracades +############ Next lines match ServerGameType STR_NETWORK_SERVER_VISIBILITY_PUBLIC :Pública +############ End of leave-in-this-order # Network server list STR_NETWORK_SERVER_LIST_CAPTION :{WHITE}Multijugador diff --git a/src/lang/chuvash.txt b/src/lang/chuvash.txt index 619dd1c3d7..35fa59dae6 100644 --- a/src/lang/chuvash.txt +++ b/src/lang/chuvash.txt @@ -726,6 +726,8 @@ STR_FACE_COLLAR :Ҫуха: STR_FACE_TIE :Галстук: STR_FACE_EARRING :Алка: +############ Next lines match ServerGameType +############ End of leave-in-this-order # Network server list STR_NETWORK_SERVER_LIST_PLAYER_NAME :{BLACK}Вӑйӑҫӑ ят: diff --git a/src/lang/croatian.txt b/src/lang/croatian.txt index 5dedf1eaae..006f6631cd 100644 --- a/src/lang/croatian.txt +++ b/src/lang/croatian.txt @@ -2015,6 +2015,8 @@ STR_FACE_TIE :Kravata: STR_FACE_EARRING :Naušnica: STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}Promijeni kravatu ili naušnicu +############ Next lines match ServerGameType +############ End of leave-in-this-order # Network server list STR_NETWORK_SERVER_LIST_CAPTION :{WHITE}Više igrača diff --git a/src/lang/czech.txt b/src/lang/czech.txt index c9da6147fa..3e9c872271 100644 --- a/src/lang/czech.txt +++ b/src/lang/czech.txt @@ -2068,6 +2068,8 @@ STR_FACE_TIE :Kravata: STR_FACE_EARRING :Náušnice: STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}Změnit kravatu nebo náušnice +############ Next lines match ServerGameType +############ End of leave-in-this-order # Network server list STR_NETWORK_SERVER_LIST_CAPTION :{WHITE}Multiplayer diff --git a/src/lang/danish.txt b/src/lang/danish.txt index 52c69a6d27..34fd9f74e0 100644 --- a/src/lang/danish.txt +++ b/src/lang/danish.txt @@ -1924,6 +1924,8 @@ STR_FACE_TIE :Slips: STR_FACE_EARRING :Ørering: STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}Ændre slips eller ørering +############ Next lines match ServerGameType +############ End of leave-in-this-order # Network server list STR_NETWORK_SERVER_LIST_CAPTION :{WHITE}Netværksspil diff --git a/src/lang/dutch.txt b/src/lang/dutch.txt index 17bf954bc3..7047dd950b 100644 --- a/src/lang/dutch.txt +++ b/src/lang/dutch.txt @@ -1995,7 +1995,9 @@ STR_FACE_TIE :Stropdas: STR_FACE_EARRING :Oorbel: STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}Verander das of oorbel +############ Next lines match ServerGameType STR_NETWORK_SERVER_VISIBILITY_PUBLIC :Openbaar +############ End of leave-in-this-order # Network server list STR_NETWORK_SERVER_LIST_CAPTION :{WHITE}Netwerkspel diff --git a/src/lang/english_AU.txt b/src/lang/english_AU.txt index a1e96ce14f..fd587f5d39 100644 --- a/src/lang/english_AU.txt +++ b/src/lang/english_AU.txt @@ -1841,6 +1841,8 @@ STR_FACE_TIE :Tie: STR_FACE_EARRING :Earring: STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}Change tie or earring +############ Next lines match ServerGameType +############ End of leave-in-this-order # Network server list STR_NETWORK_SERVER_LIST_CAPTION :{WHITE}Multiplayer diff --git a/src/lang/english_US.txt b/src/lang/english_US.txt index 9e7b06b21c..b26763913d 100644 --- a/src/lang/english_US.txt +++ b/src/lang/english_US.txt @@ -1995,7 +1995,10 @@ STR_FACE_TIE :Tie: STR_FACE_EARRING :Earring: STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}Change tie or earring +############ Next lines match ServerGameType +STR_NETWORK_SERVER_VISIBILITY_LOCAL :Local STR_NETWORK_SERVER_VISIBILITY_PUBLIC :Public +############ End of leave-in-this-order # Network server list STR_NETWORK_SERVER_LIST_CAPTION :{WHITE}Multiplayer @@ -2044,11 +2047,12 @@ STR_NETWORK_SERVER_LIST_SEARCH_SERVER_INTERNET_TOOLTIP :{BLACK}Search i STR_NETWORK_SERVER_LIST_SEARCH_SERVER_LAN :{BLACK}Search LAN STR_NETWORK_SERVER_LIST_SEARCH_SERVER_LAN_TOOLTIP :{BLACK}Search local area network for servers STR_NETWORK_SERVER_LIST_ADD_SERVER :{BLACK}Add server -STR_NETWORK_SERVER_LIST_ADD_SERVER_TOOLTIP :{BLACK}Adds a server to the list which will always be checked for running games +STR_NETWORK_SERVER_LIST_ADD_SERVER_TOOLTIP :{BLACK}Adds a server to the list. This can either be a server address or an invite code STR_NETWORK_SERVER_LIST_START_SERVER :{BLACK}Start server STR_NETWORK_SERVER_LIST_START_SERVER_TOOLTIP :{BLACK}Start your own server STR_NETWORK_SERVER_LIST_PLAYER_NAME_OSKTITLE :{BLACK}Enter your name +STR_NETWORK_SERVER_LIST_ENTER_SERVER_ADDRESS :{BLACK}Enter server address or invite code # Start new multiplayer server STR_NETWORK_START_SERVER_CAPTION :{WHITE}Start new multiplayer game @@ -2134,6 +2138,10 @@ STR_NETWORK_CLIENT_LIST_SERVER_NAME_EDIT_TOOLTIP :{BLACK}Edit the STR_NETWORK_CLIENT_LIST_SERVER_NAME_QUERY_CAPTION :Name of the server STR_NETWORK_CLIENT_LIST_SERVER_VISIBILITY :{BLACK}Visibility STR_NETWORK_CLIENT_LIST_SERVER_VISIBILITY_TOOLTIP :{BLACK}Whether other people can see your server in the public listing +STR_NETWORK_CLIENT_LIST_SERVER_INVITE_CODE :{BLACK}Invite code +STR_NETWORK_CLIENT_LIST_SERVER_INVITE_CODE_TOOLTIP :{BLACK}Invite code other players can use to join this server +STR_NETWORK_CLIENT_LIST_SERVER_CONNECTION_TYPE :{BLACK}Connection type +STR_NETWORK_CLIENT_LIST_SERVER_CONNECTION_TYPE_TOOLTIP :{BLACK}Whether and how your server can be reached by others STR_NETWORK_CLIENT_LIST_PLAYER :{BLACK}Player STR_NETWORK_CLIENT_LIST_PLAYER_NAME :{BLACK}Name STR_NETWORK_CLIENT_LIST_PLAYER_NAME_TOOLTIP :{BLACK}Your player name @@ -2153,6 +2161,9 @@ STR_NETWORK_CLIENT_LIST_PLAYER_ICON_HOST_TOOLTIP :{BLACK}This is STR_NETWORK_CLIENT_LIST_CLIENT_COMPANY_COUNT :{BLACK}{NUM} client{P "" s} / {NUM} compan{P y ies} ############ Begin of ConnectionType +STR_NETWORK_CLIENT_LIST_SERVER_CONNECTION_TYPE_UNKNOWN :{BLACK}Local +STR_NETWORK_CLIENT_LIST_SERVER_CONNECTION_TYPE_ISOLATED :{RED}Remote players can't connect +STR_NETWORK_CLIENT_LIST_SERVER_CONNECTION_TYPE_DIRECT :{BLACK}Public ############ End of ConnectionType STR_NETWORK_CLIENT_LIST_ADMIN_CLIENT_KICK :Kick @@ -2282,6 +2293,9 @@ STR_NETWORK_MESSAGE_SERVER_SHUTDOWN :{WHITE}The serv STR_NETWORK_MESSAGE_SERVER_REBOOT :{WHITE}The server is restarting...{}Please wait... STR_NETWORK_MESSAGE_KICKED :*** {STRING} was kicked. Reason: ({STRING}) +STR_NETWORK_ERROR_COORDINATOR_REGISTRATION_FAILED :{WHITE}Server registration failed +STR_NETWORK_ERROR_COORDINATOR_ISOLATED :{WHITE}Your server doesn't allow remote connections +STR_NETWORK_ERROR_COORDINATOR_ISOLATED_DETAIL :{WHITE}Other players won't be able to connect to your server # Content downloading window STR_CONTENT_TITLE :{WHITE}Content downloading diff --git a/src/lang/esperanto.txt b/src/lang/esperanto.txt index 67495b09f7..32f7ef8511 100644 --- a/src/lang/esperanto.txt +++ b/src/lang/esperanto.txt @@ -1525,6 +1525,8 @@ STR_FACE_TIE :Kravato: STR_FACE_EARRING :Orelringo: STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}Ŝanĝi kravaton aŭ orelringon. +############ Next lines match ServerGameType +############ End of leave-in-this-order # Network server list STR_NETWORK_SERVER_LIST_CAPTION :{WHITE}Pluraj ludantoj diff --git a/src/lang/estonian.txt b/src/lang/estonian.txt index 60896e7671..2e6aaf0bc9 100644 --- a/src/lang/estonian.txt +++ b/src/lang/estonian.txt @@ -2043,7 +2043,9 @@ STR_FACE_TIE :Lips: STR_FACE_EARRING :Kõrvarõngas: STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}Vaheta kraed või kõrvarõngast +############ Next lines match ServerGameType STR_NETWORK_SERVER_VISIBILITY_PUBLIC :Avalik +############ End of leave-in-this-order # Network server list STR_NETWORK_SERVER_LIST_CAPTION :{WHITE}Mitmikmäng diff --git a/src/lang/faroese.txt b/src/lang/faroese.txt index ee5ae34045..3da8d8472d 100644 --- a/src/lang/faroese.txt +++ b/src/lang/faroese.txt @@ -1691,6 +1691,8 @@ STR_FACE_TIE :Slips: STR_FACE_EARRING :Oyraringur: STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}Broyt slips ella oyraring +############ Next lines match ServerGameType +############ End of leave-in-this-order # Network server list STR_NETWORK_SERVER_LIST_CAPTION :{WHITE}Hópspæl diff --git a/src/lang/finnish.txt b/src/lang/finnish.txt index 70cdcfc01a..b601b7ece7 100644 --- a/src/lang/finnish.txt +++ b/src/lang/finnish.txt @@ -1995,7 +1995,9 @@ STR_FACE_TIE :Solmio: STR_FACE_EARRING :Korvakoru: STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}Vaihda solmio tai korvakoru +############ Next lines match ServerGameType STR_NETWORK_SERVER_VISIBILITY_PUBLIC :Julkinen +############ End of leave-in-this-order # Network server list STR_NETWORK_SERVER_LIST_CAPTION :{WHITE}Moninpeli diff --git a/src/lang/french.txt b/src/lang/french.txt index db26884d21..f0d0e89b0c 100644 --- a/src/lang/french.txt +++ b/src/lang/french.txt @@ -1996,7 +1996,9 @@ 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 +############ Next lines match ServerGameType STR_NETWORK_SERVER_VISIBILITY_PUBLIC :Public +############ End of leave-in-this-order # Network server list STR_NETWORK_SERVER_LIST_CAPTION :{WHITE}Multijoueurs diff --git a/src/lang/frisian.txt b/src/lang/frisian.txt index a644b32f6b..58b7020bd1 100644 --- a/src/lang/frisian.txt +++ b/src/lang/frisian.txt @@ -1790,6 +1790,8 @@ STR_FACE_TIE :Strik: STR_FACE_EARRING :Earbel: STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}Feroarje strik of earbel +############ Next lines match ServerGameType +############ End of leave-in-this-order # Network server list STR_NETWORK_SERVER_LIST_CAPTION :{WHITE}Multiplayer diff --git a/src/lang/gaelic.txt b/src/lang/gaelic.txt index c2b4fd4f3e..387b0de079 100644 --- a/src/lang/gaelic.txt +++ b/src/lang/gaelic.txt @@ -2076,6 +2076,8 @@ STR_FACE_TIE :Tàidh: STR_FACE_EARRING :Fàinne-chluaise: STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}Atharraich an tàidh no an fhàinne-chluaise +############ Next lines match ServerGameType +############ End of leave-in-this-order # Network server list STR_NETWORK_SERVER_LIST_CAPTION :{WHITE}Ioma-chluicheadair diff --git a/src/lang/galician.txt b/src/lang/galician.txt index 8d86c740c6..fc5d56a9fc 100644 --- a/src/lang/galician.txt +++ b/src/lang/galician.txt @@ -1912,6 +1912,8 @@ STR_FACE_TIE :Garavata: STR_FACE_EARRING :Pendentes: STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}Cambia-la garavata ou os pendentes +############ Next lines match ServerGameType +############ End of leave-in-this-order # Network server list STR_NETWORK_SERVER_LIST_CAPTION :{WHITE}Multixogador diff --git a/src/lang/german.txt b/src/lang/german.txt index f58dafe9b3..bd41434e1e 100644 --- a/src/lang/german.txt +++ b/src/lang/german.txt @@ -1996,7 +1996,10 @@ STR_FACE_TIE :Krawatte: STR_FACE_EARRING :Ohrring: STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}Krawatte oder Ohrring ändern +############ Next lines match ServerGameType +STR_NETWORK_SERVER_VISIBILITY_LOCAL :Lokal STR_NETWORK_SERVER_VISIBILITY_PUBLIC :Öffentlich +############ End of leave-in-this-order # Network server list STR_NETWORK_SERVER_LIST_CAPTION :{WHITE}Mehrspieler @@ -2045,11 +2048,12 @@ STR_NETWORK_SERVER_LIST_SEARCH_SERVER_INTERNET_TOOLTIP :{BLACK}Internet STR_NETWORK_SERVER_LIST_SEARCH_SERVER_LAN :{BLACK}LAN durchsuchen STR_NETWORK_SERVER_LIST_SEARCH_SERVER_LAN_TOOLTIP :{BLACK}Lokales Netzwerk nach Spielservern durchsuchen STR_NETWORK_SERVER_LIST_ADD_SERVER :{BLACK}Server hinzufügen -STR_NETWORK_SERVER_LIST_ADD_SERVER_TOOLTIP :{BLACK}Fügt einen Server zu der Liste von Servern hinzu, die immer nach laufenden Spielen kontrolliert werden +STR_NETWORK_SERVER_LIST_ADD_SERVER_TOOLTIP :{BLACK}Fügt einen Server zur Liste hinzu. Dies kann entweder eine Serveradresse oder ein Einladungscode ein STR_NETWORK_SERVER_LIST_START_SERVER :{BLACK}Server starten STR_NETWORK_SERVER_LIST_START_SERVER_TOOLTIP :{BLACK}Einen eigenen Server starten STR_NETWORK_SERVER_LIST_PLAYER_NAME_OSKTITLE :{BLACK}Bitte eigenen Namen eingeben +STR_NETWORK_SERVER_LIST_ENTER_SERVER_ADDRESS :{BLACK}Serveradresse oder Einladungscode eingeben # Start new multiplayer server STR_NETWORK_START_SERVER_CAPTION :{WHITE}Neues Mehrspieler-Spiel beginnen @@ -2135,6 +2139,10 @@ STR_NETWORK_CLIENT_LIST_SERVER_NAME_EDIT_TOOLTIP :{BLACK}Ihren Se STR_NETWORK_CLIENT_LIST_SERVER_NAME_QUERY_CAPTION :Name des Servers STR_NETWORK_CLIENT_LIST_SERVER_VISIBILITY :{BLACK}Sichtbarkeit STR_NETWORK_CLIENT_LIST_SERVER_VISIBILITY_TOOLTIP :{BLACK}Ob andere Personen Ihren Server in der öffentlichen Liste sehen können +STR_NETWORK_CLIENT_LIST_SERVER_INVITE_CODE :{BLACK}Einladungscode +STR_NETWORK_CLIENT_LIST_SERVER_INVITE_CODE_TOOLTIP :{BLACK}Einladungscode, den andere Spieler benutzen können, um diesem Server beizutreten +STR_NETWORK_CLIENT_LIST_SERVER_CONNECTION_TYPE :{BLACK}Verbindungstyp +STR_NETWORK_CLIENT_LIST_SERVER_CONNECTION_TYPE_TOOLTIP :{BLACK}Ob und wie dein Server von anderen erreicht werden kann STR_NETWORK_CLIENT_LIST_PLAYER :{BLACK}Spieler STR_NETWORK_CLIENT_LIST_PLAYER_NAME :{BLACK}Name STR_NETWORK_CLIENT_LIST_PLAYER_NAME_TOOLTIP :{BLACK}Ihr Spielername @@ -2154,6 +2162,9 @@ STR_NETWORK_CLIENT_LIST_PLAYER_ICON_HOST_TOOLTIP :{BLACK}Dies ist STR_NETWORK_CLIENT_LIST_CLIENT_COMPANY_COUNT :{BLACK}{NUM} Client{P "" s} / {NUM} Firm{P a en} ############ Begin of ConnectionType +STR_NETWORK_CLIENT_LIST_SERVER_CONNECTION_TYPE_UNKNOWN :{BLACK}Lokal +STR_NETWORK_CLIENT_LIST_SERVER_CONNECTION_TYPE_ISOLATED :{RED}Nicht-lokale Spieler können sich nicht verbinden +STR_NETWORK_CLIENT_LIST_SERVER_CONNECTION_TYPE_DIRECT :{BLACK}Öffentlich ############ End of ConnectionType STR_NETWORK_CLIENT_LIST_ADMIN_CLIENT_KICK :Hinauswerfen @@ -2283,6 +2294,9 @@ STR_NETWORK_MESSAGE_SERVER_SHUTDOWN :{WHITE}Der Serv STR_NETWORK_MESSAGE_SERVER_REBOOT :{WHITE}Der Server startet neu...{}Bitte warten... STR_NETWORK_MESSAGE_KICKED :*** {STRING} wurde vom Server hinausgeworfen. Grund: ({STRING}) +STR_NETWORK_ERROR_COORDINATOR_REGISTRATION_FAILED :{WHITE}Serverregistration fehlgeschlagen +STR_NETWORK_ERROR_COORDINATOR_ISOLATED :{WHITE}Dein Server lässt keine nicht-lokalen Verbindungen zu +STR_NETWORK_ERROR_COORDINATOR_ISOLATED_DETAIL :{WHITE}Andere Spieler werden nicht in der Lage sein, sich zu deinem Server zu verbinden # Content downloading window STR_CONTENT_TITLE :{WHITE}Herunterladen von Erweiterungen diff --git a/src/lang/greek.txt b/src/lang/greek.txt index 7fce5846aa..637ebdafe1 100644 --- a/src/lang/greek.txt +++ b/src/lang/greek.txt @@ -2032,6 +2032,8 @@ STR_FACE_TIE :Γραβάτα: STR_FACE_EARRING :Σκουλαρίκι: STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}Αλλαγή γραβάτας ή σκουλαρικιού +############ Next lines match ServerGameType +############ End of leave-in-this-order # Network server list STR_NETWORK_SERVER_LIST_CAPTION :{WHITE}Παιχνίδι πολλών παικτών diff --git a/src/lang/hebrew.txt b/src/lang/hebrew.txt index a200114a02..073ad733d2 100644 --- a/src/lang/hebrew.txt +++ b/src/lang/hebrew.txt @@ -1890,6 +1890,8 @@ STR_FACE_TIE ::עניבה STR_FACE_EARRING ::עגילים STR_FACE_TIE_EARRING_TOOLTIP :{BLACK} שנה עניבה/עגילים +############ Next lines match ServerGameType +############ End of leave-in-this-order # Network server list STR_NETWORK_SERVER_LIST_CAPTION :{WHITE}משחק רשת diff --git a/src/lang/hindi.txt b/src/lang/hindi.txt index 2e52e4982b..cd76a92a69 100644 --- a/src/lang/hindi.txt +++ b/src/lang/hindi.txt @@ -110,6 +110,7 @@ STR_SCENEDIT_FILE_MENU_QUIT :निकास ############ range for settings menu starts STR_SETTINGS_MENU_CONFIG_SETTINGS_TREE :समायोजन +STR_SETTINGS_MENU_WAYPOINTS_DISPLAYED :पथ-संकेतों के नाम दिखायें ############ range ends here ############ range for file menu starts @@ -135,6 +136,7 @@ STR_INDUSTRY_MENU_INDUSTRY_CHAIN :औद्यो ############ range ends here ############ range for railway construction menu starts +STR_RAIL_MENU_ELRAIL_CONSTRUCTION :विद्युतिकृत रेलवे निर्माण ############ range ends here ############ range for road construction menu starts @@ -362,6 +364,7 @@ STR_CONFIG_SETTING_SOFT_LIMIT_VALUE :{COMMA} STR_CONFIG_SETTING_SPRITE_ZOOM_LVL_IN_2X :२x +STR_CONFIG_SETTING_LOCALISATION_UNITS_VELOCITY_SI :अंतर्राष्ट्रीय मानक (m/s) @@ -409,6 +412,8 @@ STR_ABANDON_SCENARIO_QUERY :{YELLOW}क् STR_FACE_LOAD_DONE :{WHITE}ओपनटीटीडी प्रारूप पत्र से आपका प्रिय चेहरा भर लिया गया है STR_FACE_COLLAR_TOOLTIP :{BLACK}कॉलर बदलें +############ Next lines match ServerGameType +############ End of leave-in-this-order # Network server list @@ -779,6 +784,7 @@ STR_INDUSTRY_VIEW_ACCEPT_CARGO :{YELLOW}{STRING ############ range for vehicle availability starts +STR_BUY_VEHICLE_TRAIN_ALL_CAPTION :नई ट्रेनें ############ range for vehicle availability ends STR_PURCHASE_INFO_COST_WEIGHT :{BLACK}मूल्य : {GOLD}{CURRENCY_LONG}{BLACK} भार : {GOLD}{WEIGHT_SHORT} @@ -992,6 +998,7 @@ STR_ERROR_TOO_HIGH :{WHITE}... ब # Company related errors +STR_ERROR_CAN_T_SELL_25_SHARE_IN :{WHITE}इस कंपनी का २५% अंश नहीं बेच सकते... # Town related errors STR_ERROR_CAN_T_RENAME_TOWN :{WHITE}नगर का नाम नहीं बदला जा सकता है... @@ -1030,6 +1037,7 @@ STR_ERROR_CAN_T_REMOVE_RAILROAD_TRACK :{WHITE}पट STR_ERROR_INCOMPATIBLE_TRAMWAY :{WHITE}... असंगत ट्रामवे # Waterway construction errors +STR_ERROR_MUST_BE_BUILT_ON_WATER :{WHITE}... पानी पर बनाया जाना चाहिये। # Tree related errors diff --git a/src/lang/hungarian.txt b/src/lang/hungarian.txt index 2897ea8d90..b7f93544ae 100644 --- a/src/lang/hungarian.txt +++ b/src/lang/hungarian.txt @@ -2050,7 +2050,9 @@ STR_FACE_TIE :Nyakkendő: STR_FACE_EARRING :Fülbevaló: STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}Nyakkendő vagy fülbevaló cseréje +############ Next lines match ServerGameType STR_NETWORK_SERVER_VISIBILITY_PUBLIC :Nyilvános +############ End of leave-in-this-order # Network server list STR_NETWORK_SERVER_LIST_CAPTION :{WHITE}Hálózati játék diff --git a/src/lang/icelandic.txt b/src/lang/icelandic.txt index 40972a3e38..f5b59582d9 100644 --- a/src/lang/icelandic.txt +++ b/src/lang/icelandic.txt @@ -1729,6 +1729,8 @@ STR_FACE_TIE :Bindi: STR_FACE_EARRING :Eyrnalokkur: STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}Breyta bindi eða eyrnalokk +############ Next lines match ServerGameType +############ End of leave-in-this-order # Network server list STR_NETWORK_SERVER_LIST_CAPTION :{WHITE}Fjölspilun diff --git a/src/lang/ido.txt b/src/lang/ido.txt index c56cc78d34..c58ec2a322 100644 --- a/src/lang/ido.txt +++ b/src/lang/ido.txt @@ -602,6 +602,8 @@ STR_QUIT_NO :{BLACK}Ne # Face selection window +############ Next lines match ServerGameType +############ End of leave-in-this-order # Network server list diff --git a/src/lang/indonesian.txt b/src/lang/indonesian.txt index 7607f7bfbc..99c4473b65 100644 --- a/src/lang/indonesian.txt +++ b/src/lang/indonesian.txt @@ -1986,7 +1986,9 @@ STR_FACE_TIE :Dasi: STR_FACE_EARRING :Anting-anting: STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}Ubah dasi atau anting-anting +############ Next lines match ServerGameType STR_NETWORK_SERVER_VISIBILITY_PUBLIC :Umum +############ End of leave-in-this-order # Network server list STR_NETWORK_SERVER_LIST_CAPTION :{WHITE}Bermain bersama diff --git a/src/lang/irish.txt b/src/lang/irish.txt index 3080b6362b..dfd5a0cf00 100644 --- a/src/lang/irish.txt +++ b/src/lang/irish.txt @@ -1864,6 +1864,8 @@ STR_FACE_TIE :Carbhat: STR_FACE_EARRING :Fáinne cluaise: STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}Athraigh carbhat nó fáinne cluaise +############ Next lines match ServerGameType +############ End of leave-in-this-order # Network server list STR_NETWORK_SERVER_LIST_CAPTION :{WHITE}Ilimreoirí diff --git a/src/lang/italian.txt b/src/lang/italian.txt index fa4dae8fc5..f8817d32bd 100644 --- a/src/lang/italian.txt +++ b/src/lang/italian.txt @@ -2000,6 +2000,8 @@ STR_FACE_TIE :Cravatta: STR_FACE_EARRING :Orecchino: STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}Cambia la cravatta o l'orecchino +############ Next lines match ServerGameType +############ End of leave-in-this-order # Network server list STR_NETWORK_SERVER_LIST_CAPTION :{WHITE}Multigiocatore diff --git a/src/lang/japanese.txt b/src/lang/japanese.txt index fbb1a84cb0..5b9628862c 100644 --- a/src/lang/japanese.txt +++ b/src/lang/japanese.txt @@ -1987,7 +1987,9 @@ STR_FACE_TIE :ネクタイ: STR_FACE_EARRING :イヤリング: STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}ネクタイ/イヤリングを変更します +############ Next lines match ServerGameType STR_NETWORK_SERVER_VISIBILITY_PUBLIC :公開 +############ End of leave-in-this-order # Network server list STR_NETWORK_SERVER_LIST_CAPTION :{WHITE}マルチプレイヤーゲーム diff --git a/src/lang/korean.txt b/src/lang/korean.txt index f34f0b3eb5..e3b64f44d4 100644 --- a/src/lang/korean.txt +++ b/src/lang/korean.txt @@ -1719,7 +1719,7 @@ STR_CONFIG_SETTING_DISTRIBUTION_PAX :승객에 대 STR_CONFIG_SETTING_DISTRIBUTION_PAX_HELPTEXT :"대칭"은 A역에서 B역으로 가려는 승객의 수가 B에서 A로 가려는 승객의 수와 비슷하다는 뜻입니다. "비대칭"은 승객이 아무 방향이나 임의의 양만큼 가게 됨을 뜻합니다. "수동"은 자동적인 승객 분배가 일어나지 않고 기존 방식을 사용하겠음을 뜻합니다. STR_CONFIG_SETTING_DISTRIBUTION_MAIL :우편에 대한 분배 형식: {STRING} STR_CONFIG_SETTING_DISTRIBUTION_MAIL_HELPTEXT :"대칭"은 A역에서 B역으로 가려는 우편물의 수가 B에서 A로 가려는 우편물의 수와 비슷하다는 뜻입니다. "비대칭"은 우편물이 아무 방향이나 임의의 양만큼 가게 됨을 뜻합니다. "수동"은 자동적인 우편물 분배가 일어나지 않고 기존 방식을 사용하겠음을 뜻합니다. -STR_CONFIG_SETTING_DISTRIBUTION_ARMOURED :장갑 화물에 대한 분배 형식: {STRING} +STR_CONFIG_SETTING_DISTRIBUTION_ARMOURED :귀금속 화물에 대한 분배 형식: {STRING} STR_CONFIG_SETTING_DISTRIBUTION_ARMOURED_HELPTEXT :장갑 화물은 온대 기후의 귀금속, 아열대 기후의 다이아몬드 또는 아한대 기후의 금을 말합니다. NewGRF을 사용하면 달라질 수 있습니다. "대칭"은 A역에서 B역으로 가려는 화물의 수가 B에서 A로 가려는 화물의 수와 비슷하다는 뜻입니다. "비대칭"은 화물이 아무 방향이나 임의의 양만큼 가게 됨을 뜻합니다. "수동"은 자동적인 화물 분배가 일어나지 않고 기존 방식을 사용하겠음을 뜻합니다. 아한대 기후에서는 대칭으로 설정하면 은행이 금광으로 금을 보내지 않으려 하기 때문에 비대칭이나 수동으로 설정하는 것을 추천합니다. 온대 기후나 아열대 기후에서는 은행이 일부 적재한 귀금속을 원래 은행으로 보내려고 하기 때문에 대칭을 선택해도 됩니다. STR_CONFIG_SETTING_DISTRIBUTION_DEFAULT :다른 화물에 대한 분배 형식: {STRING} STR_CONFIG_SETTING_DISTRIBUTION_DEFAULT_HELPTEXT :"비대칭"은 화물이 아무 방향이나 임의의 양만큼 가게 됨을 뜻합니다. "수동"은 자동적인 화물 분배가 일어나지 않고 기존 방식을 사용하겠음을 뜻합니다. 특별한 이유가 없는 한, "비대칭"이나 "수동"으로 설정하십시오. @@ -1996,8 +1996,10 @@ STR_FACE_TIE :넥타이: STR_FACE_EARRING :귀걸이: STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}넥타이/귀걸이 변경 +############ Next lines match ServerGameType STR_NETWORK_SERVER_VISIBILITY_LOCAL :로컬 STR_NETWORK_SERVER_VISIBILITY_PUBLIC :공개 +############ End of leave-in-this-order # Network server list STR_NETWORK_SERVER_LIST_CAPTION :{WHITE}멀티 플레이 @@ -2046,11 +2048,12 @@ 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_ADD_SERVER :{BLACK}서버 추가 -STR_NETWORK_SERVER_LIST_ADD_SERVER_TOOLTIP :{BLACK}서버를 목록에 수동으로 추가합니다. +STR_NETWORK_SERVER_LIST_ADD_SERVER_TOOLTIP :{BLACK}서버를 목록에 추가합니다. 서버 주소나 초대 코드를 입력하세요. STR_NETWORK_SERVER_LIST_START_SERVER :{BLACK}서버 열기 STR_NETWORK_SERVER_LIST_START_SERVER_TOOLTIP :{BLACK}당신이 서버가 되어 게임을 진행합니다. STR_NETWORK_SERVER_LIST_PLAYER_NAME_OSKTITLE :{BLACK}이름을 입력하세요 +STR_NETWORK_SERVER_LIST_ENTER_SERVER_ADDRESS :{BLACK}서버 주소나 초대 코드를 입력하세요 # Start new multiplayer server STR_NETWORK_START_SERVER_CAPTION :{WHITE}새 멀티플레이 게임 시작하기 @@ -2136,6 +2139,8 @@ 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_SERVER_INVITE_CODE :{BLACK}초대 코드 +STR_NETWORK_CLIENT_LIST_SERVER_INVITE_CODE_TOOLTIP :{BLACK}다른 플레이어가 이 서버에 들어오기 위한 초대 코드입니다 STR_NETWORK_CLIENT_LIST_SERVER_CONNECTION_TYPE :{BLACK}접속 방식 STR_NETWORK_CLIENT_LIST_SERVER_CONNECTION_TYPE_TOOLTIP :{BLACK}다른 사람이 이 서버에 들어올 수 있는지 여부를 나타냅니다 STR_NETWORK_CLIENT_LIST_PLAYER :{BLACK}플레이어 diff --git a/src/lang/latin.txt b/src/lang/latin.txt index edb0cfab77..5f29e1d508 100644 --- a/src/lang/latin.txt +++ b/src/lang/latin.txt @@ -2083,6 +2083,8 @@ STR_FACE_TIE :Focale: STR_FACE_EARRING :Inauris: STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}Mutare focale vel inaurem +############ Next lines match ServerGameType +############ End of leave-in-this-order # Network server list STR_NETWORK_SERVER_LIST_CAPTION :{WHITE}Modus Plurium Lusorum diff --git a/src/lang/latvian.txt b/src/lang/latvian.txt index e1131be431..3480ee5e6f 100644 --- a/src/lang/latvian.txt +++ b/src/lang/latvian.txt @@ -1955,6 +1955,8 @@ STR_FACE_TIE :Kaklasaite: STR_FACE_EARRING :Auskars: STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}Mainīt kaklasaiti vai auskarus +############ Next lines match ServerGameType +############ End of leave-in-this-order # Network server list STR_NETWORK_SERVER_LIST_CAPTION :{WHITE}Vairākspēlētāju spēle diff --git a/src/lang/lithuanian.txt b/src/lang/lithuanian.txt index 485e94f59e..a7b1249f94 100644 --- a/src/lang/lithuanian.txt +++ b/src/lang/lithuanian.txt @@ -2199,6 +2199,8 @@ STR_FACE_TIE :Kaklaraištis: STR_FACE_EARRING :Auskaras: STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}Pakeisti kaklaraištį arba auskarą +############ Next lines match ServerGameType +############ End of leave-in-this-order # Network server list STR_NETWORK_SERVER_LIST_CAPTION :{WHITE}Žaidimas tinkle diff --git a/src/lang/luxembourgish.txt b/src/lang/luxembourgish.txt index 6b72e791bc..749439392c 100644 --- a/src/lang/luxembourgish.txt +++ b/src/lang/luxembourgish.txt @@ -1980,6 +1980,8 @@ STR_FACE_TIE :Krawatt: STR_FACE_EARRING :Ouerréng: STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}Krawatt oder Ouerréng änneren +############ Next lines match ServerGameType +############ End of leave-in-this-order # Network server list STR_NETWORK_SERVER_LIST_CAPTION :{WHITE}Multiplayer diff --git a/src/lang/macedonian.txt b/src/lang/macedonian.txt index 2fc2c60860..a164bc935d 100644 --- a/src/lang/macedonian.txt +++ b/src/lang/macedonian.txt @@ -960,6 +960,8 @@ STR_ABANDON_GAME_QUERY :{YELLOW}Дал # Face selection window +############ Next lines match ServerGameType +############ End of leave-in-this-order # Network server list diff --git a/src/lang/malay.txt b/src/lang/malay.txt index c792823170..9e1bb06704 100644 --- a/src/lang/malay.txt +++ b/src/lang/malay.txt @@ -1626,6 +1626,8 @@ STR_FACE_TIE :Tali leher: STR_FACE_EARRING :Anting-anting: STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}Tukar tali leher atau anting-anting +############ Next lines match ServerGameType +############ End of leave-in-this-order # Network server list STR_NETWORK_SERVER_LIST_CAPTION :{WHITE}Pemain berbilang diff --git a/src/lang/maltese.txt b/src/lang/maltese.txt index df5a2456e1..617806cf34 100644 --- a/src/lang/maltese.txt +++ b/src/lang/maltese.txt @@ -528,6 +528,8 @@ STR_CHEAT_CHANGE_DATE_QUERY_CAPT :{WHITE}Ibdel is # Face selection window +############ Next lines match ServerGameType +############ End of leave-in-this-order # Network server list diff --git a/src/lang/marathi.txt b/src/lang/marathi.txt index cb9fdb0b37..3dce26c937 100644 --- a/src/lang/marathi.txt +++ b/src/lang/marathi.txt @@ -892,6 +892,8 @@ STR_FACE_LIPS_MOUSTACHE_TOOLTIP :{BLACK}ओठ STR_FACE_CHIN :हनुवटी: STR_FACE_CHIN_TOOLTIP :{BLACK}हनुवटी बदला +############ Next lines match ServerGameType +############ End of leave-in-this-order # Network server list diff --git a/src/lang/norwegian_bokmal.txt b/src/lang/norwegian_bokmal.txt index bee88edbbf..6ef70c3fa0 100644 --- a/src/lang/norwegian_bokmal.txt +++ b/src/lang/norwegian_bokmal.txt @@ -1998,7 +1998,9 @@ STR_FACE_TIE :Slips: STR_FACE_EARRING :Ørering: STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}Endre slips eller ørering +############ Next lines match ServerGameType STR_NETWORK_SERVER_VISIBILITY_PUBLIC :Offentlig +############ End of leave-in-this-order # Network server list STR_NETWORK_SERVER_LIST_CAPTION :{WHITE}Flerspiller diff --git a/src/lang/norwegian_nynorsk.txt b/src/lang/norwegian_nynorsk.txt index ec0ff3b156..57de575329 100644 --- a/src/lang/norwegian_nynorsk.txt +++ b/src/lang/norwegian_nynorsk.txt @@ -1788,6 +1788,8 @@ STR_FACE_TIE :Slips: STR_FACE_EARRING :Øyrering: STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}Byt slips eller øyrering +############ Next lines match ServerGameType +############ End of leave-in-this-order # Network server list STR_NETWORK_SERVER_LIST_CAPTION :{WHITE}Fleirspelar diff --git a/src/lang/persian.txt b/src/lang/persian.txt index 3aea70ef8b..db150a4716 100644 --- a/src/lang/persian.txt +++ b/src/lang/persian.txt @@ -1586,6 +1586,8 @@ STR_FACE_TIE :کراوات: STR_FACE_EARRING :گوشواره: STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}تغییر کراوات یا گوشواره +############ Next lines match ServerGameType +############ End of leave-in-this-order # Network server list STR_NETWORK_SERVER_LIST_CAPTION :{WHITE}چندنفره diff --git a/src/lang/polish.txt b/src/lang/polish.txt index 32ffe5cade..b84bfce185 100644 --- a/src/lang/polish.txt +++ b/src/lang/polish.txt @@ -2366,6 +2366,8 @@ STR_FACE_TIE :Krawat: STR_FACE_EARRING :Kolczyk: STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}Zmień krawat lub kolczyk +############ Next lines match ServerGameType +############ End of leave-in-this-order # Network server list STR_NETWORK_SERVER_LIST_CAPTION :{WHITE}Gra wieloosobowa diff --git a/src/lang/portuguese.txt b/src/lang/portuguese.txt index 60a959618a..1344a63d62 100644 --- a/src/lang/portuguese.txt +++ b/src/lang/portuguese.txt @@ -1996,8 +1996,10 @@ STR_FACE_TIE :Gravata: STR_FACE_EARRING :Brinco: STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}Mudar gravata ou brinco +############ Next lines match ServerGameType STR_NETWORK_SERVER_VISIBILITY_LOCAL :Local STR_NETWORK_SERVER_VISIBILITY_PUBLIC :Público +############ End of leave-in-this-order # Network server list STR_NETWORK_SERVER_LIST_CAPTION :{WHITE}Multi-jogador @@ -2046,11 +2048,12 @@ STR_NETWORK_SERVER_LIST_SEARCH_SERVER_INTERNET_TOOLTIP :{BLACK}Pesquisa STR_NETWORK_SERVER_LIST_SEARCH_SERVER_LAN :{BLACK}Search LAN STR_NETWORK_SERVER_LIST_SEARCH_SERVER_LAN_TOOLTIP :{BLACK} Pesquisa de rede local para servidores STR_NETWORK_SERVER_LIST_ADD_SERVER :{BLACK}Adicionar servidor -STR_NETWORK_SERVER_LIST_ADD_SERVER_TOOLTIP :{BLACK}Adiciona um servidor à lista que será sempre verificado se existem jogos a decorrer. +STR_NETWORK_SERVER_LIST_ADD_SERVER_TOOLTIP :{BLACK}Adiciona um servidor à lista. Pode ser um endereço de servidor ou um código de convite 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}Introduza o seu nome +STR_NETWORK_SERVER_LIST_ENTER_SERVER_ADDRESS :{BLACK}Introduza o endereço de servidor ou código de convite # Start new multiplayer server STR_NETWORK_START_SERVER_CAPTION :{WHITE}Iniciar novo jogo @@ -2136,6 +2139,8 @@ STR_NETWORK_CLIENT_LIST_SERVER_NAME_EDIT_TOOLTIP :{BLACK}Editar o 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_SERVER_INVITE_CODE :{BLACK}Código de convite +STR_NETWORK_CLIENT_LIST_SERVER_INVITE_CODE_TOOLTIP :{BLACK}Código de convite que os outros jogadores podem usar para entrar neste servidor STR_NETWORK_CLIENT_LIST_SERVER_CONNECTION_TYPE :{BLACK}Tipo de conexão STR_NETWORK_CLIENT_LIST_SERVER_CONNECTION_TYPE_TOOLTIP :{BLACK}Se e como o seu servidor pode ser alcançado por outros STR_NETWORK_CLIENT_LIST_PLAYER :{BLACK}Jogador diff --git a/src/lang/romanian.txt b/src/lang/romanian.txt index 5528456e53..bf9b47a0cb 100644 --- a/src/lang/romanian.txt +++ b/src/lang/romanian.txt @@ -1913,6 +1913,8 @@ STR_FACE_TIE :Cravată: STR_FACE_EARRING :Cercei: STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}Schimbă cravata sau cerceii +############ Next lines match ServerGameType +############ End of leave-in-this-order # Network server list STR_NETWORK_SERVER_LIST_CAPTION :{WHITE}Multiplayer diff --git a/src/lang/russian.txt b/src/lang/russian.txt index 59d990a9f6..3cd55cf755 100644 --- a/src/lang/russian.txt +++ b/src/lang/russian.txt @@ -2146,7 +2146,9 @@ STR_FACE_TIE :Галстук: STR_FACE_EARRING :Серьга: STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}Изменить галстук или серьгу +############ Next lines match ServerGameType STR_NETWORK_SERVER_VISIBILITY_PUBLIC :Частный +############ End of leave-in-this-order # Network server list STR_NETWORK_SERVER_LIST_CAPTION :{WHITE}Сетевая игра diff --git a/src/lang/serbian.txt b/src/lang/serbian.txt index fda419bd14..fb19ab2b3b 100644 --- a/src/lang/serbian.txt +++ b/src/lang/serbian.txt @@ -2175,6 +2175,8 @@ STR_FACE_TIE :Kravata: STR_FACE_EARRING :Minđuše: STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}Promena kravate ili minđuša +############ Next lines match ServerGameType +############ End of leave-in-this-order # Network server list STR_NETWORK_SERVER_LIST_CAPTION :{WHITE}Mrežna partija diff --git a/src/lang/simplified_chinese.txt b/src/lang/simplified_chinese.txt index 07d8c62006..61cf71c8df 100644 --- a/src/lang/simplified_chinese.txt +++ b/src/lang/simplified_chinese.txt @@ -1985,6 +1985,8 @@ STR_FACE_TIE :领带 STR_FACE_EARRING :耳环 STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}选择领带或是耳环 +############ Next lines match ServerGameType +############ End of leave-in-this-order # Network server list STR_NETWORK_SERVER_LIST_CAPTION :{WHITE}联机游戏 diff --git a/src/lang/slovak.txt b/src/lang/slovak.txt index e091c2efb8..07c3800f1c 100644 --- a/src/lang/slovak.txt +++ b/src/lang/slovak.txt @@ -2054,7 +2054,9 @@ STR_FACE_TIE :Kravata: STR_FACE_EARRING :Náušnica: STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}Zmeniť kravatu alebo náušnicu +############ Next lines match ServerGameType STR_NETWORK_SERVER_VISIBILITY_PUBLIC :Verejný +############ End of leave-in-this-order # Network server list STR_NETWORK_SERVER_LIST_CAPTION :{WHITE}Hra pre viac hráčov diff --git a/src/lang/slovenian.txt b/src/lang/slovenian.txt index 78f250b6bb..6eac4baa57 100644 --- a/src/lang/slovenian.txt +++ b/src/lang/slovenian.txt @@ -2020,6 +2020,8 @@ STR_FACE_TIE :Kravata: STR_FACE_EARRING :Uhani: STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}Spremeni kravato ali uhane +############ Next lines match ServerGameType +############ End of leave-in-this-order # Network server list STR_NETWORK_SERVER_LIST_CAPTION :{WHITE}Več igralcev diff --git a/src/lang/spanish.txt b/src/lang/spanish.txt index 516dd2db96..5d51dace84 100644 --- a/src/lang/spanish.txt +++ b/src/lang/spanish.txt @@ -1996,7 +1996,9 @@ STR_FACE_TIE :Corbata: STR_FACE_EARRING :Pendientes: STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}Cambiar corbata o pendientes +############ Next lines match ServerGameType STR_NETWORK_SERVER_VISIBILITY_PUBLIC :Público +############ End of leave-in-this-order # Network server list STR_NETWORK_SERVER_LIST_CAPTION :{WHITE}Multijugador diff --git a/src/lang/spanish_MX.txt b/src/lang/spanish_MX.txt index 47c3edad57..a079c5fd93 100644 --- a/src/lang/spanish_MX.txt +++ b/src/lang/spanish_MX.txt @@ -1996,8 +1996,10 @@ STR_FACE_TIE :Corbata: STR_FACE_EARRING :Aretes: STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}Cambiar corbata o aretes +############ Next lines match ServerGameType STR_NETWORK_SERVER_VISIBILITY_LOCAL :Local STR_NETWORK_SERVER_VISIBILITY_PUBLIC :Público +############ End of leave-in-this-order # Network server list STR_NETWORK_SERVER_LIST_CAPTION :{WHITE}Multijugador @@ -2046,11 +2048,12 @@ STR_NETWORK_SERVER_LIST_SEARCH_SERVER_INTERNET_TOOLTIP :{BLACK}Buscar s STR_NETWORK_SERVER_LIST_SEARCH_SERVER_LAN :{BLACK}Buscar en red local STR_NETWORK_SERVER_LIST_SEARCH_SERVER_LAN_TOOLTIP :{BLACK}Buscar servidores en la red local STR_NETWORK_SERVER_LIST_ADD_SERVER :{BLACK}Añadir servidor -STR_NETWORK_SERVER_LIST_ADD_SERVER_TOOLTIP :{BLACK}Añadir el servidor a una lista que siempre será analizada en busca de partidas activas +STR_NETWORK_SERVER_LIST_ADD_SERVER_TOOLTIP :{BLACK}Añadir el servidor a la lista. Puede ser una dirección o un código de invitación STR_NETWORK_SERVER_LIST_START_SERVER :{BLACK}Iniciar servidor STR_NETWORK_SERVER_LIST_START_SERVER_TOOLTIP :{BLACK}Iniciar un nuevo servidor STR_NETWORK_SERVER_LIST_PLAYER_NAME_OSKTITLE :{BLACK}Introducir nombre del jugador +STR_NETWORK_SERVER_LIST_ENTER_SERVER_ADDRESS :{BLACK}Dirección del servidor o código de invitación # Start new multiplayer server STR_NETWORK_START_SERVER_CAPTION :{WHITE}Empezar nueva partida multijugador @@ -2136,6 +2139,8 @@ STR_NETWORK_CLIENT_LIST_SERVER_NAME_EDIT_TOOLTIP :{BLACK}Modifica 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_SERVER_INVITE_CODE :{BLACK}Código de invitación +STR_NETWORK_CLIENT_LIST_SERVER_INVITE_CODE_TOOLTIP :{BLACK}Código para que otros jugadores se unan a este servidor STR_NETWORK_CLIENT_LIST_SERVER_CONNECTION_TYPE :{BLACK}Tipo de conexión STR_NETWORK_CLIENT_LIST_SERVER_CONNECTION_TYPE_TOOLTIP :{BLACK}Quién puede conectarse a tu servidor STR_NETWORK_CLIENT_LIST_PLAYER :{BLACK}Jugador diff --git a/src/lang/swedish.txt b/src/lang/swedish.txt index 467a608f00..26c3311a58 100644 --- a/src/lang/swedish.txt +++ b/src/lang/swedish.txt @@ -1995,7 +1995,9 @@ STR_FACE_TIE :Slips: STR_FACE_EARRING :Örhänge: STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}Ändra slips eller örhänge +############ Next lines match ServerGameType STR_NETWORK_SERVER_VISIBILITY_PUBLIC :Offentlig +############ End of leave-in-this-order # Network server list STR_NETWORK_SERVER_LIST_CAPTION :{WHITE}Flera spelare diff --git a/src/lang/tamil.txt b/src/lang/tamil.txt index 34fa28b7f0..8ae6e6493a 100644 --- a/src/lang/tamil.txt +++ b/src/lang/tamil.txt @@ -1732,6 +1732,8 @@ STR_FACE_TIE :Tie: STR_FACE_EARRING :கம்மல்: STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}Tie அல்லது காதணியை மாற்றவும் +############ Next lines match ServerGameType +############ End of leave-in-this-order # Network server list STR_NETWORK_SERVER_LIST_CAPTION :{WHITE}பல்வீரர் diff --git a/src/lang/thai.txt b/src/lang/thai.txt index a5e9ba63bf..bb77d64b2e 100644 --- a/src/lang/thai.txt +++ b/src/lang/thai.txt @@ -1815,6 +1815,8 @@ STR_FACE_TIE :เนคไท STR_FACE_EARRING :ต่างหู: STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}เปลี่ยนเนคไทหรือต่างหู +############ Next lines match ServerGameType +############ End of leave-in-this-order # Network server list STR_NETWORK_SERVER_LIST_CAPTION :{WHITE}เล่นหลายคน diff --git a/src/lang/traditional_chinese.txt b/src/lang/traditional_chinese.txt index 0b2e8c88ca..ba3d0ad057 100644 --- a/src/lang/traditional_chinese.txt +++ b/src/lang/traditional_chinese.txt @@ -1876,6 +1876,8 @@ STR_FACE_TIE :領帶: STR_FACE_EARRING :耳飾: STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}改變領帶或耳飾 +############ Next lines match ServerGameType +############ End of leave-in-this-order # Network server list STR_NETWORK_SERVER_LIST_CAPTION :{WHITE}多人遊戲 diff --git a/src/lang/turkish.txt b/src/lang/turkish.txt index ae36780e1a..7f04a26b6b 100644 --- a/src/lang/turkish.txt +++ b/src/lang/turkish.txt @@ -1987,7 +1987,9 @@ STR_FACE_TIE :Kravat: STR_FACE_EARRING :Küpe: STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}Kravatı veya küpeyi değiştir +############ Next lines match ServerGameType STR_NETWORK_SERVER_VISIBILITY_PUBLIC :Halka açık +############ End of leave-in-this-order # Network server list STR_NETWORK_SERVER_LIST_CAPTION :{WHITE}Çok Oyunculu diff --git a/src/lang/ukrainian.txt b/src/lang/ukrainian.txt index 5faa22317a..e44894cef6 100644 --- a/src/lang/ukrainian.txt +++ b/src/lang/ukrainian.txt @@ -2108,6 +2108,8 @@ STR_FACE_TIE :Краватк STR_FACE_EARRING :Сережки: STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}Змінити комір або сережки +############ Next lines match ServerGameType +############ End of leave-in-this-order # Network server list STR_NETWORK_SERVER_LIST_CAPTION :{WHITE}Мережева гра diff --git a/src/lang/urdu.txt b/src/lang/urdu.txt index 111332245c..7ff0660d8d 100644 --- a/src/lang/urdu.txt +++ b/src/lang/urdu.txt @@ -1477,6 +1477,8 @@ STR_FACE_TIE :ٹائی: STR_FACE_EARRING :بالیاں: STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}ٹائی یا بالیاں بدلیں +############ Next lines match ServerGameType +############ End of leave-in-this-order # Network server list STR_NETWORK_SERVER_LIST_CAPTION :{WHITE}زیادہ کھلاڑی diff --git a/src/lang/vietnamese.txt b/src/lang/vietnamese.txt index e008681657..37bb12c95b 100644 --- a/src/lang/vietnamese.txt +++ b/src/lang/vietnamese.txt @@ -1995,7 +1995,9 @@ STR_FACE_TIE :Cà vạt: STR_FACE_EARRING :Bông tai: STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}Thay đổi cà vạt hoặc bông tai +############ Next lines match ServerGameType STR_NETWORK_SERVER_VISIBILITY_PUBLIC :Công khai +############ End of leave-in-this-order # Network server list STR_NETWORK_SERVER_LIST_CAPTION :{WHITE}Ván Chơi Mạng diff --git a/src/lang/welsh.txt b/src/lang/welsh.txt index b05c6e6815..bf0b35d100 100644 --- a/src/lang/welsh.txt +++ b/src/lang/welsh.txt @@ -1872,6 +1872,8 @@ STR_FACE_TIE :Tei: STR_FACE_EARRING :Clustlws: STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}Newid tei neu glustlws +############ Next lines match ServerGameType +############ End of leave-in-this-order # Network server list STR_NETWORK_SERVER_LIST_CAPTION :{WHITE}Amlchwaraewr From e9b157f1f52ce3a6df85aec114238d85e3ba4a8c Mon Sep 17 00:00:00 2001 From: translators Date: Tue, 13 Jul 2021 18:52:35 +0000 Subject: [PATCH 35/44] Update: Translations from eints korean: 1 change by telk5093 portuguese: 1 change by azulcosta --- src/lang/korean.txt | 1 + src/lang/portuguese.txt | 1 + 2 files changed, 2 insertions(+) diff --git a/src/lang/korean.txt b/src/lang/korean.txt index e3b64f44d4..0274d18058 100644 --- a/src/lang/korean.txt +++ b/src/lang/korean.txt @@ -1999,6 +1999,7 @@ STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}넥타 ############ Next lines match ServerGameType STR_NETWORK_SERVER_VISIBILITY_LOCAL :로컬 STR_NETWORK_SERVER_VISIBILITY_PUBLIC :공개 +STR_NETWORK_SERVER_VISIBILITY_INVITE_ONLY :초대만 ############ End of leave-in-this-order # Network server list diff --git a/src/lang/portuguese.txt b/src/lang/portuguese.txt index 1344a63d62..2a6c55c6a9 100644 --- a/src/lang/portuguese.txt +++ b/src/lang/portuguese.txt @@ -1999,6 +1999,7 @@ STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}Mudar gr ############ Next lines match ServerGameType STR_NETWORK_SERVER_VISIBILITY_LOCAL :Local STR_NETWORK_SERVER_VISIBILITY_PUBLIC :Público +STR_NETWORK_SERVER_VISIBILITY_INVITE_ONLY :Apenas convites ############ End of leave-in-this-order # Network server list From 333cba6a619d8d99a2508e5cb9cd1cc72c4360d1 Mon Sep 17 00:00:00 2001 From: translators Date: Wed, 14 Jul 2021 18:53:00 +0000 Subject: [PATCH 36/44] Update: Translations from eints spanish (mexican): 1 change by absay finnish: 14 changes by hpiirai --- src/lang/finnish.txt | 15 ++++++++++++++- src/lang/spanish_MX.txt | 1 + 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/src/lang/finnish.txt b/src/lang/finnish.txt index b601b7ece7..a7ea360d13 100644 --- a/src/lang/finnish.txt +++ b/src/lang/finnish.txt @@ -1996,7 +1996,9 @@ STR_FACE_EARRING :Korvakoru: STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}Vaihda solmio tai korvakoru ############ Next lines match ServerGameType +STR_NETWORK_SERVER_VISIBILITY_LOCAL :Paikallinen STR_NETWORK_SERVER_VISIBILITY_PUBLIC :Julkinen +STR_NETWORK_SERVER_VISIBILITY_INVITE_ONLY :Vain kutsutut ############ End of leave-in-this-order # Network server list @@ -2046,11 +2048,12 @@ STR_NETWORK_SERVER_LIST_SEARCH_SERVER_INTERNET_TOOLTIP :{BLACK}Etsi jul STR_NETWORK_SERVER_LIST_SEARCH_SERVER_LAN :{BLACK}Etsi lähiverkosta STR_NETWORK_SERVER_LIST_SEARCH_SERVER_LAN_TOOLTIP :{BLACK}Etsi palvelimia lähiverkosta STR_NETWORK_SERVER_LIST_ADD_SERVER :{BLACK}Lisää palvelin -STR_NETWORK_SERVER_LIST_ADD_SERVER_TOOLTIP :{BLACK}Lisää palvelin listaan, joka käydään läpi aina uusia pelejä haettaessa +STR_NETWORK_SERVER_LIST_ADD_SERVER_TOOLTIP :{BLACK}Lisää palvelimen luetteloon. Tämä voi olla palvelimen osoite tai kutsukoodi. STR_NETWORK_SERVER_LIST_START_SERVER :{BLACK}Käynnistä palvelin STR_NETWORK_SERVER_LIST_START_SERVER_TOOLTIP :{BLACK}Käynnistä oma palvelin STR_NETWORK_SERVER_LIST_PLAYER_NAME_OSKTITLE :{BLACK}Kirjoita nimesi +STR_NETWORK_SERVER_LIST_ENTER_SERVER_ADDRESS :{BLACK}Anna palvelimen osoite tai kutsukoodi # Start new multiplayer server STR_NETWORK_START_SERVER_CAPTION :{WHITE}Aloita uusi peli @@ -2136,6 +2139,10 @@ STR_NETWORK_CLIENT_LIST_SERVER_NAME_EDIT_TOOLTIP :{BLACK}Muokkaa STR_NETWORK_CLIENT_LIST_SERVER_NAME_QUERY_CAPTION :Palvelimen nimi STR_NETWORK_CLIENT_LIST_SERVER_VISIBILITY :{BLACK}Näkyvyys STR_NETWORK_CLIENT_LIST_SERVER_VISIBILITY_TOOLTIP :{BLACK}Näkyykö palvelimesi muille julkisessa listauksessa +STR_NETWORK_CLIENT_LIST_SERVER_INVITE_CODE :{BLACK}Kutsukoodi +STR_NETWORK_CLIENT_LIST_SERVER_INVITE_CODE_TOOLTIP :{BLACK}Kutsukoodi, jolla muut pelaajat voivat liittyä tälle palvelimelle +STR_NETWORK_CLIENT_LIST_SERVER_CONNECTION_TYPE :{BLACK}Yhteystyyppi +STR_NETWORK_CLIENT_LIST_SERVER_CONNECTION_TYPE_TOOLTIP :{BLACK}Voivatko muut ottaa yhteyden palvelimeesi, ja miten STR_NETWORK_CLIENT_LIST_PLAYER :{BLACK}Pelaaja STR_NETWORK_CLIENT_LIST_PLAYER_NAME :{BLACK}Nimi STR_NETWORK_CLIENT_LIST_PLAYER_NAME_TOOLTIP :{BLACK}Pelaajanimesi @@ -2155,6 +2162,9 @@ STR_NETWORK_CLIENT_LIST_PLAYER_ICON_HOST_TOOLTIP :{BLACK}Tämä o STR_NETWORK_CLIENT_LIST_CLIENT_COMPANY_COUNT :{BLACK}{NUM} pelaaja{P "" a} / {NUM} yhtiö{P "" tä} ############ Begin of ConnectionType +STR_NETWORK_CLIENT_LIST_SERVER_CONNECTION_TYPE_UNKNOWN :{BLACK}Paikallinen +STR_NETWORK_CLIENT_LIST_SERVER_CONNECTION_TYPE_ISOLATED :{RED}Etäpelaajat eivät voi ottaa yhteyttä +STR_NETWORK_CLIENT_LIST_SERVER_CONNECTION_TYPE_DIRECT :{BLACK}Julkinen ############ End of ConnectionType STR_NETWORK_CLIENT_LIST_ADMIN_CLIENT_KICK :Potki @@ -2284,6 +2294,9 @@ STR_NETWORK_MESSAGE_SERVER_SHUTDOWN :{WHITE}Palvelin STR_NETWORK_MESSAGE_SERVER_REBOOT :{WHITE}Palvelin käynnistyy uudelleen...{}Odota, ole hyvä... STR_NETWORK_MESSAGE_KICKED :{STRING} potkaistiin ulos. Syy: ({STRING}) +STR_NETWORK_ERROR_COORDINATOR_REGISTRATION_FAILED :{WHITE}Palvelimen rekisteröinti epäonnistui +STR_NETWORK_ERROR_COORDINATOR_ISOLATED :{WHITE}Palvelimesi ei salli etäyhteyksiä +STR_NETWORK_ERROR_COORDINATOR_ISOLATED_DETAIL :{WHITE}Muut pelaajat eivät voi saada yhteyttä palvelimeesi # Content downloading window STR_CONTENT_TITLE :{WHITE}Sisällön lataus diff --git a/src/lang/spanish_MX.txt b/src/lang/spanish_MX.txt index a079c5fd93..bb39a0d6f4 100644 --- a/src/lang/spanish_MX.txt +++ b/src/lang/spanish_MX.txt @@ -1999,6 +1999,7 @@ STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}Cambiar ############ Next lines match ServerGameType STR_NETWORK_SERVER_VISIBILITY_LOCAL :Local STR_NETWORK_SERVER_VISIBILITY_PUBLIC :Público +STR_NETWORK_SERVER_VISIBILITY_INVITE_ONLY :Solo con invitación ############ End of leave-in-this-order # Network server list From c921f6d81794223590abc4ced95daba0b91e4767 Mon Sep 17 00:00:00 2001 From: Patric Stout Date: Wed, 14 Jul 2021 21:23:44 +0200 Subject: [PATCH 37/44] Add: inform clients what game-script a server is running (#9441) Co-authored-by: The Dude --- src/lang/english.txt | 1 + src/network/core/config.h | 2 +- src/network/core/game_info.cpp | 13 +++++++++++++ src/network/core/game_info.h | 2 ++ src/network/network_gui.cpp | 7 +++++++ 5 files changed, 24 insertions(+), 1 deletion(-) diff --git a/src/lang/english.txt b/src/lang/english.txt index baf94c2ee4..23ec632394 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -2033,6 +2033,7 @@ STR_NETWORK_SERVER_LIST_SERVER_VERSION :{SILVER}Server STR_NETWORK_SERVER_LIST_SERVER_ADDRESS :{SILVER}Server address: {WHITE}{RAW_STRING} STR_NETWORK_SERVER_LIST_START_DATE :{SILVER}Start date: {WHITE}{DATE_SHORT} STR_NETWORK_SERVER_LIST_CURRENT_DATE :{SILVER}Current date: {WHITE}{DATE_SHORT} +STR_NETWORK_SERVER_LIST_GAMESCRIPT :{SILVER}Game Script: {WHITE}{RAW_STRING} (v{NUM}) STR_NETWORK_SERVER_LIST_PASSWORD :{SILVER}Password protected! STR_NETWORK_SERVER_LIST_SERVER_OFFLINE :{SILVER}SERVER OFFLINE STR_NETWORK_SERVER_LIST_SERVER_FULL :{SILVER}SERVER FULL diff --git a/src/network/core/config.h b/src/network/core/config.h index 06df93140c..757556993d 100644 --- a/src/network/core/config.h +++ b/src/network/core/config.h @@ -45,7 +45,7 @@ static const uint16 TCP_MTU = 32767; ///< Numbe static const uint16 COMPAT_MTU = 1460; ///< Number of bytes we can pack in a single packet for backward compatibility static const byte NETWORK_GAME_ADMIN_VERSION = 1; ///< What version of the admin network do we use? -static const byte NETWORK_GAME_INFO_VERSION = 4; ///< What version of game-info do we use? +static const byte NETWORK_GAME_INFO_VERSION = 5; ///< What version of game-info do we use? static const byte NETWORK_COMPANY_INFO_VERSION = 6; ///< What version of company info is this? static const byte NETWORK_COORDINATOR_VERSION = 2; ///< What version of game-coordinator-protocol do we use? diff --git a/src/network/core/game_info.cpp b/src/network/core/game_info.cpp index 17f00c5f57..32b8fdb75b 100644 --- a/src/network/core/game_info.cpp +++ b/src/network/core/game_info.cpp @@ -16,6 +16,8 @@ #include "../../date_func.h" #include "../../debug.h" #include "../../map_func.h" +#include "../../game/game.hpp" +#include "../../game/game_info.hpp" #include "../../settings_type.h" #include "../../string_func.h" #include "../../rev.h" @@ -195,6 +197,11 @@ void SerializeNetworkGameInfo(Packet *p, const NetworkServerGameInfo *info) /* Update the documentation in game_info.h on changes * to the NetworkGameInfo wire-protocol! */ + /* NETWORK_GAME_INFO_VERSION = 5 */ + GameInfo *game_info = Game::GetInfo(); + p->Send_uint32(game_info == nullptr ? -1 : (uint32)game_info->GetVersion()); + p->Send_string(game_info == nullptr ? "" : game_info->GetName()); + /* NETWORK_GAME_INFO_VERSION = 4 */ { /* Only send the GRF Identification (GRF_ID and MD5 checksum) of @@ -260,6 +267,12 @@ void DeserializeNetworkGameInfo(Packet *p, NetworkGameInfo *info) * to the NetworkGameInfo wire-protocol! */ switch (game_info_version) { + case 5: { + info->gamescript_version = (int)p->Recv_uint32(); + info->gamescript_name = p->Recv_string(NETWORK_NAME_LENGTH); + FALLTHROUGH; + } + case 4: { GRFConfig **dst = &info->grfconfig; uint i; diff --git a/src/network/core/game_info.h b/src/network/core/game_info.h index 1ea77ae773..8d9824b188 100644 --- a/src/network/core/game_info.h +++ b/src/network/core/game_info.h @@ -76,6 +76,8 @@ struct NetworkServerGameInfo { byte spectators_on; ///< How many spectators do we have? byte spectators_max; ///< Max spectators allowed on server byte landscape; ///< The used landscape + int gamescript_version; ///< Version of the gamescript. + std::string gamescript_name; ///< Name of the gamescript. }; /** diff --git a/src/network/network_gui.cpp b/src/network/network_gui.cpp index d2fefb5cc3..1368ac0ad9 100644 --- a/src/network/network_gui.cpp +++ b/src/network/network_gui.cpp @@ -675,6 +675,13 @@ public: DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, STR_NETWORK_SERVER_LIST_CURRENT_DATE); // current date y += FONT_HEIGHT_NORMAL; + if (sel->info.gamescript_version != -1) { + SetDParamStr(0, sel->info.gamescript_name); + SetDParam(1, sel->info.gamescript_version); + DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, STR_NETWORK_SERVER_LIST_GAMESCRIPT); // gamescript name and version + y += FONT_HEIGHT_NORMAL; + } + y += WD_PAR_VSEP_NORMAL; if (!sel->info.compatible) { From 695e1493c9cfb81b942213a7a23488b0ccfaae57 Mon Sep 17 00:00:00 2001 From: translators Date: Thu, 15 Jul 2021 18:49:29 +0000 Subject: [PATCH 38/44] Update: Translations from eints swedish: 14 changes by joeax910 --- src/lang/swedish.txt | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/lang/swedish.txt b/src/lang/swedish.txt index 26c3311a58..73f8d149fd 100644 --- a/src/lang/swedish.txt +++ b/src/lang/swedish.txt @@ -1996,7 +1996,9 @@ STR_FACE_EARRING :Örhänge: STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}Ändra slips eller örhänge ############ Next lines match ServerGameType +STR_NETWORK_SERVER_VISIBILITY_LOCAL :Lokal STR_NETWORK_SERVER_VISIBILITY_PUBLIC :Offentlig +STR_NETWORK_SERVER_VISIBILITY_INVITE_ONLY :Endast inbjudna ############ End of leave-in-this-order # Network server list @@ -2046,11 +2048,12 @@ STR_NETWORK_SERVER_LIST_SEARCH_SERVER_INTERNET_TOOLTIP :{BLACK}Sök onl STR_NETWORK_SERVER_LIST_SEARCH_SERVER_LAN :{BLACK}Sök LAN STR_NETWORK_SERVER_LIST_SEARCH_SERVER_LAN_TOOLTIP :{BLACK}Sök i lokalt nätverk för servrar STR_NETWORK_SERVER_LIST_ADD_SERVER :{BLACK}Lägg till server -STR_NETWORK_SERVER_LIST_ADD_SERVER_TOOLTIP :{BLACK}Lägg till en server till listan som alltid kommer kontrolleras för aktiva spel +STR_NETWORK_SERVER_LIST_ADD_SERVER_TOOLTIP :{BLACK}Lägger till en server i listan. Denna kan antingen vara en serveradress eller en inbjudningskod STR_NETWORK_SERVER_LIST_START_SERVER :{BLACK}Starta server STR_NETWORK_SERVER_LIST_START_SERVER_TOOLTIP :{BLACK}Starta en server för andra att ansluta till STR_NETWORK_SERVER_LIST_PLAYER_NAME_OSKTITLE :{BLACK}Mata in ditt namn +STR_NETWORK_SERVER_LIST_ENTER_SERVER_ADDRESS :{BLACK}Ange serveradress eller inbjudningskod # Start new multiplayer server STR_NETWORK_START_SERVER_CAPTION :{WHITE}Starta ett nytt spel i flerspelarläge @@ -2136,6 +2139,10 @@ STR_NETWORK_CLIENT_LIST_SERVER_NAME_EDIT_TOOLTIP :{BLACK}Ändra d STR_NETWORK_CLIENT_LIST_SERVER_NAME_QUERY_CAPTION :Serverns namn STR_NETWORK_CLIENT_LIST_SERVER_VISIBILITY :{BLACK}Synlighet STR_NETWORK_CLIENT_LIST_SERVER_VISIBILITY_TOOLTIP :{BLACK}Huruvida andra människor kan se din server i den offentliga listan +STR_NETWORK_CLIENT_LIST_SERVER_INVITE_CODE :{BLACK}Inbjudningskod +STR_NETWORK_CLIENT_LIST_SERVER_INVITE_CODE_TOOLTIP :{BLACK}Inbjudningskod som andra spelare kan använda för att ansluta till denna server +STR_NETWORK_CLIENT_LIST_SERVER_CONNECTION_TYPE :{BLACK}Anslutningstyp +STR_NETWORK_CLIENT_LIST_SERVER_CONNECTION_TYPE_TOOLTIP :{BLACK}Huruvida, och i så fall hur, din server kan nås av andra STR_NETWORK_CLIENT_LIST_PLAYER :{BLACK}Spelare STR_NETWORK_CLIENT_LIST_PLAYER_NAME :{BLACK}Namn STR_NETWORK_CLIENT_LIST_PLAYER_NAME_TOOLTIP :{BLACK}Ditt spelarnamn @@ -2155,6 +2162,9 @@ STR_NETWORK_CLIENT_LIST_PLAYER_ICON_HOST_TOOLTIP :{BLACK}Det här STR_NETWORK_CLIENT_LIST_CLIENT_COMPANY_COUNT :{BLACK}{NUM} klient{P "" er} / {NUM} företag ############ Begin of ConnectionType +STR_NETWORK_CLIENT_LIST_SERVER_CONNECTION_TYPE_UNKNOWN :{BLACK}Lokal +STR_NETWORK_CLIENT_LIST_SERVER_CONNECTION_TYPE_ISOLATED :{RED}Fjärran belägna spelare kan inte ansluta +STR_NETWORK_CLIENT_LIST_SERVER_CONNECTION_TYPE_DIRECT :{BLACK}Offentlig ############ End of ConnectionType STR_NETWORK_CLIENT_LIST_ADMIN_CLIENT_KICK :Kasta ut @@ -2284,6 +2294,9 @@ STR_NETWORK_MESSAGE_SERVER_SHUTDOWN :{WHITE}Servern STR_NETWORK_MESSAGE_SERVER_REBOOT :{WHITE}Servern startar om...{}Var vänlig vänta... STR_NETWORK_MESSAGE_KICKED :*** {STRING} kastades ut. Orsak: ({STRING}) +STR_NETWORK_ERROR_COORDINATOR_REGISTRATION_FAILED :{WHITE}Serverregistrering misslyckades +STR_NETWORK_ERROR_COORDINATOR_ISOLATED :{WHITE}Din server tillåter inte fjärranslutningar +STR_NETWORK_ERROR_COORDINATOR_ISOLATED_DETAIL :{WHITE}Andra spelare kommer ej ha möjlighet att ansluta till din server # Content downloading window STR_CONTENT_TITLE :{WHITE}Nedladdning av innehåll From 55eed246b842d372cd32784c1afcc904aef67f65 Mon Sep 17 00:00:00 2001 From: Patric Stout Date: Wed, 28 Apr 2021 10:55:35 +0200 Subject: [PATCH 39/44] Codechange: allow Connect() to bind to a local address --- src/network/core/tcp.h | 3 ++- src/network/core/tcp_connect.cpp | 17 ++++++++++++++--- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/src/network/core/tcp.h b/src/network/core/tcp.h index bbd0bc2a91..b4b4398ded 100644 --- a/src/network/core/tcp.h +++ b/src/network/core/tcp.h @@ -98,6 +98,7 @@ private: std::chrono::steady_clock::time_point last_attempt; ///< Time we last tried to connect. std::string connection_string; ///< Current address we are connecting to (before resolving). + NetworkAddress bind_address; ///< Address we're binding to, if any. void Resolve(); void OnResolved(addrinfo *ai); @@ -113,7 +114,7 @@ private: public: TCPConnecter() {}; - TCPConnecter(const std::string &connection_string, uint16 default_port); + TCPConnecter(const std::string &connection_string, uint16 default_port, NetworkAddress bind_address = {}); virtual ~TCPConnecter(); /** diff --git a/src/network/core/tcp_connect.cpp b/src/network/core/tcp_connect.cpp index 0e8e2e1251..6db2500f3b 100644 --- a/src/network/core/tcp_connect.cpp +++ b/src/network/core/tcp_connect.cpp @@ -24,10 +24,13 @@ static std::vector _tcp_connecters; /** - * Create a new connecter for the given address - * @param connection_string the address to connect to + * Create a new connecter for the given address. + * @param connection_string The address to connect to. + * @param default_port If not indicated in connection_string, what port to use. + * @param bind_address The local bind address to use. Defaults to letting the OS find one. */ -TCPConnecter::TCPConnecter(const std::string &connection_string, uint16 default_port) +TCPConnecter::TCPConnecter(const std::string &connection_string, uint16 default_port, NetworkAddress bind_address) : + bind_address(bind_address) { this->connection_string = NormalizeConnectionString(connection_string, default_port); @@ -96,6 +99,14 @@ void TCPConnecter::Connect(addrinfo *address) return; } + if (this->bind_address.GetPort() > 0) { + if (bind(sock, (const sockaddr *)this->bind_address.GetAddress(), this->bind_address.GetAddressLength()) != 0) { + Debug(net, 1, "Could not bind socket on {}: {}", this->bind_address.GetAddressAsString(), NetworkError::GetLast().AsString()); + closesocket(sock); + return; + } + } + if (!SetNoDelay(sock)) { Debug(net, 1, "Setting TCP_NODELAY failed: {}", NetworkError::GetLast().AsString()); } From 8adade26ed0354e5357803cf19ea9839c2eb785c Mon Sep 17 00:00:00 2001 From: Patric Stout Date: Tue, 27 Apr 2021 11:51:00 +0200 Subject: [PATCH 40/44] Feature: allow the use of STUN to connect client and server together This method doesn't require port-forwarding to be used, and works for most common NAT routers in home setups. But, for sure it doesn't work for all setups, and not everyone will be able to use this. --- src/lang/english.txt | 1 + src/network/CMakeLists.txt | 2 + src/network/core/CMakeLists.txt | 2 + src/network/core/address.cpp | 42 +++++- src/network/core/address.h | 2 + src/network/core/config.cpp | 14 +- src/network/core/config.h | 4 +- src/network/core/os_abstraction.cpp | 17 +++ src/network/core/os_abstraction.h | 1 + src/network/core/tcp.h | 3 +- src/network/core/tcp_connect.cpp | 12 +- src/network/core/tcp_coordinator.cpp | 6 + src/network/core/tcp_coordinator.h | 48 +++++++ src/network/core/tcp_listen.h | 72 ++++++----- src/network/core/tcp_stun.cpp | 29 +++++ src/network/core/tcp_stun.h | 53 ++++++++ src/network/network_coordinator.cpp | 187 ++++++++++++++++++++++++--- src/network/network_coordinator.h | 14 +- src/network/network_stun.cpp | 140 ++++++++++++++++++++ src/network/network_stun.h | 34 +++++ 20 files changed, 615 insertions(+), 68 deletions(-) create mode 100644 src/network/core/tcp_stun.cpp create mode 100644 src/network/core/tcp_stun.h create mode 100644 src/network/network_stun.cpp create mode 100644 src/network/network_stun.h diff --git a/src/lang/english.txt b/src/lang/english.txt index 23ec632394..277190e1a8 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -2166,6 +2166,7 @@ STR_NETWORK_CLIENT_LIST_CLIENT_COMPANY_COUNT :{BLACK}{NUM} cl STR_NETWORK_CLIENT_LIST_SERVER_CONNECTION_TYPE_UNKNOWN :{BLACK}Local STR_NETWORK_CLIENT_LIST_SERVER_CONNECTION_TYPE_ISOLATED :{RED}Remote players can't connect STR_NETWORK_CLIENT_LIST_SERVER_CONNECTION_TYPE_DIRECT :{BLACK}Public +STR_NETWORK_CLIENT_LIST_SERVER_CONNECTION_TYPE_STUN :{BLACK}Behind NAT ############ End of ConnectionType STR_NETWORK_CLIENT_LIST_ADMIN_CLIENT_KICK :Kick diff --git a/src/network/CMakeLists.txt b/src/network/CMakeLists.txt index bf89b5c1d7..8b01579189 100644 --- a/src/network/CMakeLists.txt +++ b/src/network/CMakeLists.txt @@ -24,6 +24,8 @@ add_files( network_internal.h network_server.cpp network_server.h + network_stun.cpp + network_stun.h network_type.h network_udp.cpp network_udp.h diff --git a/src/network/core/CMakeLists.txt b/src/network/core/CMakeLists.txt index 82bf7843d0..bcecad38c8 100644 --- a/src/network/core/CMakeLists.txt +++ b/src/network/core/CMakeLists.txt @@ -28,6 +28,8 @@ add_files( tcp_http.cpp tcp_http.h tcp_listen.h + tcp_stun.cpp + tcp_stun.h udp.cpp udp.h ) diff --git a/src/network/core/address.cpp b/src/network/core/address.cpp index 4c090c14a6..9a2d3dc487 100644 --- a/src/network/core/address.cpp +++ b/src/network/core/address.cpp @@ -313,13 +313,12 @@ static SOCKET ListenLoopProc(addrinfo *runp) Debug(net, 1, "Setting no-delay mode failed: {}", NetworkError::GetLast().AsString()); } - int on = 1; - /* The (const char*) cast is needed for windows!! */ - if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (const char*)&on, sizeof(on)) == -1) { + if (!SetReusePort(sock)) { Debug(net, 0, "Setting reuse-address mode failed: {}", NetworkError::GetLast().AsString()); } #ifndef __OS2__ + int on = 1; if (runp->ai_family == AF_INET6 && setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, (const char*)&on, sizeof(on)) == -1) { Debug(net, 3, "Could not disable IPv4 over IPv6: {}", NetworkError::GetLast().AsString()); @@ -400,6 +399,38 @@ void NetworkAddress::Listen(int socktype, SocketList *sockets) } } +/** + * Get the peer address of a socket as NetworkAddress. + * @param sock The socket to get the peer address of. + * @return The NetworkAddress of the peer address. + */ +/* static */ NetworkAddress NetworkAddress::GetPeerAddress(SOCKET sock) +{ + sockaddr_storage addr = {}; + socklen_t addr_len = sizeof(addr); + if (getpeername(sock, (sockaddr *)&addr, &addr_len) != 0) { + Debug(net, 0, "Failed to get address of the peer: {}", NetworkError::GetLast().AsString()); + return NetworkAddress(); + } + return NetworkAddress(addr, addr_len); +} + +/** + * Get the local address of a socket as NetworkAddress. + * @param sock The socket to get the local address of. + * @return The NetworkAddress of the local address. + */ +/* static */ NetworkAddress NetworkAddress::GetSockAddress(SOCKET sock) +{ + sockaddr_storage addr = {}; + socklen_t addr_len = sizeof(addr); + if (getsockname(sock, (sockaddr *)&addr, &addr_len) != 0) { + Debug(net, 0, "Failed to get address of the socket: {}", NetworkError::GetLast().AsString()); + return NetworkAddress(); + } + return NetworkAddress(addr, addr_len); +} + /** * Get the peer name of a socket in string format. * @param sock The socket to get the peer name of. @@ -407,10 +438,7 @@ void NetworkAddress::Listen(int socktype, SocketList *sockets) */ /* static */ const std::string NetworkAddress::GetPeerName(SOCKET sock) { - sockaddr_storage addr; - socklen_t addr_len = sizeof(addr); - getpeername(sock, (sockaddr *)&addr, &addr_len); - return NetworkAddress(addr, addr_len).GetAddressAsString(); + return NetworkAddress::GetPeerAddress(sock).GetAddressAsString(); } /** diff --git a/src/network/core/address.h b/src/network/core/address.h index 9e09632d3d..f9c2eaef66 100644 --- a/src/network/core/address.h +++ b/src/network/core/address.h @@ -175,6 +175,8 @@ public: static const char *SocketTypeAsString(int socktype); static const char *AddressFamilyAsString(int family); + static NetworkAddress GetPeerAddress(SOCKET sock); + static NetworkAddress GetSockAddress(SOCKET sock); static const std::string GetPeerName(SOCKET sock); }; diff --git a/src/network/core/config.cpp b/src/network/core/config.cpp index c5fe3adbd0..6e7f8a11c3 100644 --- a/src/network/core/config.cpp +++ b/src/network/core/config.cpp @@ -38,10 +38,20 @@ const char *NetworkCoordinatorConnectionString() return GetEnv("OTTD_COORDINATOR_CS", "coordinator.openttd.org"); } +/** + * Get the connection string for the STUN server from the environment variable OTTD_STUN_CS, + * or when it has not been set a hard coded default DNS hostname of the production server. + * @return The STUN server's connection string. + */ +const char *NetworkStunConnectionString() +{ + return GetEnv("OTTD_STUN_CS", "stun.openttd.org"); +} + /** * Get the connection string for the content server from the environment variable OTTD_CONTENT_SERVER_CS, * or when it has not been set a hard coded default DNS hostname of the production server. - * @return The game coordinator's connection string. + * @return The content server's connection string. */ const char *NetworkContentServerConnectionString() { @@ -51,7 +61,7 @@ const char *NetworkContentServerConnectionString() /** * Get the connection string for the content mirror from the environment variable OTTD_CONTENT_MIRROR_CS, * or when it has not been set a hard coded default DNS hostname of the production server. - * @return The game coordinator's connection string. + * @return The content mirror's connection string. */ const char *NetworkContentMirrorConnectionString() { diff --git a/src/network/core/config.h b/src/network/core/config.h index 757556993d..6039c42523 100644 --- a/src/network/core/config.h +++ b/src/network/core/config.h @@ -13,6 +13,7 @@ #define NETWORK_CORE_CONFIG_H const char *NetworkCoordinatorConnectionString(); +const char *NetworkStunConnectionString(); const char *NetworkContentServerConnectionString(); const char *NetworkContentMirrorConnectionString(); @@ -20,6 +21,7 @@ const char *NetworkContentMirrorConnectionString(); static const char * const NETWORK_CONTENT_MIRROR_URL = "/bananas"; static const uint16 NETWORK_COORDINATOR_SERVER_PORT = 3976; ///< The default port of the Game Coordinator server (TCP) +static const uint16 NETWORK_STUN_SERVER_PORT = 3975; ///< The default port of the STUN server (TCP) static const uint16 NETWORK_CONTENT_SERVER_PORT = 3978; ///< The default port of the content server (TCP) static const uint16 NETWORK_CONTENT_MIRROR_PORT = 80; ///< The default port of the content mirror (TCP) static const uint16 NETWORK_DEFAULT_PORT = 3979; ///< The default port of the game server (TCP & UDP) @@ -47,7 +49,7 @@ static const uint16 COMPAT_MTU = 1460; ///< Numbe static const byte NETWORK_GAME_ADMIN_VERSION = 1; ///< What version of the admin network do we use? static const byte NETWORK_GAME_INFO_VERSION = 5; ///< What version of game-info do we use? static const byte NETWORK_COMPANY_INFO_VERSION = 6; ///< What version of company info is this? -static const byte NETWORK_COORDINATOR_VERSION = 2; ///< What version of game-coordinator-protocol do we use? +static const byte NETWORK_COORDINATOR_VERSION = 3; ///< What version of game-coordinator-protocol do we use? 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' diff --git a/src/network/core/os_abstraction.cpp b/src/network/core/os_abstraction.cpp index 202806a345..225c95d931 100644 --- a/src/network/core/os_abstraction.cpp +++ b/src/network/core/os_abstraction.cpp @@ -159,6 +159,23 @@ bool SetNoDelay(SOCKET d) #endif } +/** + * Try to set the socket to reuse ports. + * @param d The socket to reuse ports on. + * @return True if disabling the delaying succeeded, otherwise false. + */ +bool SetReusePort(SOCKET d) +{ +#ifdef _WIN32 + /* Windows has no SO_REUSEPORT, but for our usecases SO_REUSEADDR does the same job. */ + int reuse_port = 1; + return setsockopt(d, SOL_SOCKET, SO_REUSEADDR, (const char *)&reuse_port, sizeof(reuse_port)) == 0; +#else + int reuse_port = 1; + return setsockopt(d, SOL_SOCKET, SO_REUSEPORT, &reuse_port, sizeof(reuse_port)) == 0; +#endif +} + /** * Get the error from a socket, if any. * @param d The socket to get the error from. diff --git a/src/network/core/os_abstraction.h b/src/network/core/os_abstraction.h index fbde92c051..6bb6101a17 100644 --- a/src/network/core/os_abstraction.h +++ b/src/network/core/os_abstraction.h @@ -196,6 +196,7 @@ static inline socklen_t FixAddrLenForEmscripten(struct sockaddr_storage &address bool SetNonBlocking(SOCKET d); bool SetNoDelay(SOCKET d); +bool SetReusePort(SOCKET d); NetworkError GetSocketError(SOCKET d); /* Make sure these structures have the size we expect them to be */ diff --git a/src/network/core/tcp.h b/src/network/core/tcp.h index b4b4398ded..52d9cfddb3 100644 --- a/src/network/core/tcp.h +++ b/src/network/core/tcp.h @@ -99,6 +99,7 @@ private: std::string connection_string; ///< Current address we are connecting to (before resolving). NetworkAddress bind_address; ///< Address we're binding to, if any. + int family = AF_UNSPEC; ///< Family we are using to connect with. void Resolve(); void OnResolved(addrinfo *ai); @@ -114,7 +115,7 @@ private: public: TCPConnecter() {}; - TCPConnecter(const std::string &connection_string, uint16 default_port, NetworkAddress bind_address = {}); + TCPConnecter(const std::string &connection_string, uint16 default_port, NetworkAddress bind_address = {}, int family = AF_UNSPEC); virtual ~TCPConnecter(); /** diff --git a/src/network/core/tcp_connect.cpp b/src/network/core/tcp_connect.cpp index 6db2500f3b..29e9048d93 100644 --- a/src/network/core/tcp_connect.cpp +++ b/src/network/core/tcp_connect.cpp @@ -29,8 +29,9 @@ static std::vector _tcp_connecters; * @param default_port If not indicated in connection_string, what port to use. * @param bind_address The local bind address to use. Defaults to letting the OS find one. */ -TCPConnecter::TCPConnecter(const std::string &connection_string, uint16 default_port, NetworkAddress bind_address) : - bind_address(bind_address) +TCPConnecter::TCPConnecter(const std::string &connection_string, uint16 default_port, NetworkAddress bind_address, int family) : + bind_address(bind_address), + family(family) { this->connection_string = NormalizeConnectionString(connection_string, default_port); @@ -99,6 +100,10 @@ void TCPConnecter::Connect(addrinfo *address) return; } + if (!SetReusePort(sock)) { + Debug(net, 0, "Setting reuse-port mode failed: {}", NetworkError::GetLast().AsString()); + } + if (this->bind_address.GetPort() > 0) { if (bind(sock, (const sockaddr *)this->bind_address.GetAddress(), this->bind_address.GetAddressLength()) != 0) { Debug(net, 1, "Could not bind socket on {}: {}", this->bind_address.GetAddressAsString(), NetworkError::GetLast().AsString()); @@ -170,6 +175,9 @@ void TCPConnecter::OnResolved(addrinfo *ai) /* Convert the addrinfo into NetworkAddresses. */ for (addrinfo *runp = ai; runp != nullptr; runp = runp->ai_next) { + /* Skip entries if the family is set and it is not matching. */ + if (this->family != AF_UNSPEC && this->family != runp->ai_family) continue; + if (resort) { if (runp->ai_family == AF_INET6) { addresses_ipv6.emplace_back(runp); diff --git a/src/network/core/tcp_coordinator.cpp b/src/network/core/tcp_coordinator.cpp index dfd73147e1..abcff9579c 100644 --- a/src/network/core/tcp_coordinator.cpp +++ b/src/network/core/tcp_coordinator.cpp @@ -39,6 +39,9 @@ bool NetworkCoordinatorSocketHandler::HandlePacket(Packet *p) case PACKET_COORDINATOR_GC_CONNECT_FAILED: return this->Receive_GC_CONNECT_FAILED(p); case PACKET_COORDINATOR_CLIENT_CONNECTED: return this->Receive_CLIENT_CONNECTED(p); case PACKET_COORDINATOR_GC_DIRECT_CONNECT: return this->Receive_GC_DIRECT_CONNECT(p); + case PACKET_COORDINATOR_GC_STUN_REQUEST: return this->Receive_GC_STUN_REQUEST(p); + case PACKET_COORDINATOR_SERCLI_STUN_RESULT: return this->Receive_SERCLI_STUN_RESULT(p); + case PACKET_COORDINATOR_GC_STUN_CONNECT: return this->Receive_GC_STUN_CONNECT(p); default: Debug(net, 0, "[tcp/coordinator] Received invalid packet type {}", type); @@ -94,3 +97,6 @@ bool NetworkCoordinatorSocketHandler::Receive_SERCLI_CONNECT_FAILED(Packet *p) { bool NetworkCoordinatorSocketHandler::Receive_GC_CONNECT_FAILED(Packet *p) { return this->ReceiveInvalidPacket(PACKET_COORDINATOR_GC_CONNECT_FAILED); } bool NetworkCoordinatorSocketHandler::Receive_CLIENT_CONNECTED(Packet *p) { return this->ReceiveInvalidPacket(PACKET_COORDINATOR_CLIENT_CONNECTED); } bool NetworkCoordinatorSocketHandler::Receive_GC_DIRECT_CONNECT(Packet *p) { return this->ReceiveInvalidPacket(PACKET_COORDINATOR_GC_DIRECT_CONNECT); } +bool NetworkCoordinatorSocketHandler::Receive_GC_STUN_REQUEST(Packet *p) { return this->ReceiveInvalidPacket(PACKET_COORDINATOR_GC_STUN_REQUEST); } +bool NetworkCoordinatorSocketHandler::Receive_SERCLI_STUN_RESULT(Packet *p) { return this->ReceiveInvalidPacket(PACKET_COORDINATOR_SERCLI_STUN_RESULT); } +bool NetworkCoordinatorSocketHandler::Receive_GC_STUN_CONNECT(Packet *p) { return this->ReceiveInvalidPacket(PACKET_COORDINATOR_GC_STUN_CONNECT); } diff --git a/src/network/core/tcp_coordinator.h b/src/network/core/tcp_coordinator.h index 2d793b1b68..40502e7e3f 100644 --- a/src/network/core/tcp_coordinator.h +++ b/src/network/core/tcp_coordinator.h @@ -38,6 +38,9 @@ enum PacketCoordinatorType { PACKET_COORDINATOR_GC_CONNECT_FAILED, ///< Game Coordinator informs client/server it has given up on the connection attempt. PACKET_COORDINATOR_CLIENT_CONNECTED, ///< Client informs the Game Coordinator the connection with the server is established. PACKET_COORDINATOR_GC_DIRECT_CONNECT, ///< Game Coordinator tells client to directly connect to the hostname:port of the server. + PACKET_COORDINATOR_GC_STUN_REQUEST, ///< Game Coordinator tells client/server to initiate a STUN request. + PACKET_COORDINATOR_SERCLI_STUN_RESULT, ///< Client/server informs the Game Coordinator of the result of the STUN request. + PACKET_COORDINATOR_GC_STUN_CONNECT, ///< Game Coordinator tells client/server to connect() reusing the STUN local address. PACKET_COORDINATOR_END, ///< Must ALWAYS be on the end of this list!! (period) }; @@ -48,6 +51,7 @@ enum ConnectionType { CONNECTION_TYPE_UNKNOWN, ///< The Game Coordinator hasn't informed us yet what type of connection we have. CONNECTION_TYPE_ISOLATED, ///< The Game Coordinator failed to find a way to connect to your server. Nobody will be able to join. CONNECTION_TYPE_DIRECT, ///< The Game Coordinator can directly connect to your server. + CONNECTION_TYPE_STUN, ///< The Game Coordinator can connect to your server via a STUN request. }; /** @@ -215,6 +219,50 @@ protected: */ virtual bool Receive_GC_DIRECT_CONNECT(Packet *p); + /** + * Game Coordinator requests the client/server to do a STUN request to the + * STUN server. Important is to remember the local port these STUN requests + * are sent from, as this will be needed for later conenctions too. + * The client/server should do multiple STUN requests for every available + * interface that connects to the Internet (e.g., once for IPv4 and once + * for IPv6). + * + * string Token to track the current connect request. + * + * @param p The packet that was just received. + * @return True upon success, otherwise false. + */ + virtual bool Receive_GC_STUN_REQUEST(Packet *p); + + /** + * Client/server informs the Game Coordinator the result of a STUN request. + * + * uint8 Game Coordinator protocol version. + * string Token to track the current connect request. + * uint8 Interface number, as given during STUN request. + * bool Whether the STUN connection was successful. + * + * @param p The packet that was just received. + * @return True upon success, otherwise false. + */ + virtual bool Receive_SERCLI_STUN_RESULT(Packet *p); + + /** + * Game Coordinator informs the client/server of its STUN peer (the host:ip + * of the other side). It should start a connect() to this peer ASAP with + * the local address as used with the STUN request. + * + * string Token to track the current connect request. + * uint8 Tracking number to track current connect request. + * uint8 Interface number, as given during STUN request. + * string Host of the peer. + * uint16 Port of the peer. + * + * @param p The packet that was just received. + * @return True upon success, otherwise false. + */ + virtual bool Receive_GC_STUN_CONNECT(Packet *p); + bool HandlePacket(Packet *p); public: /** diff --git a/src/network/core/tcp_listen.h b/src/network/core/tcp_listen.h index 03945e230f..0c7b11df1f 100644 --- a/src/network/core/tcp_listen.h +++ b/src/network/core/tcp_listen.h @@ -30,6 +30,42 @@ class TCPListenHandler { static SocketList sockets; public: + static bool ValidateClient(SOCKET s, NetworkAddress &address) + { + /* Check if the client is banned. */ + for (const auto &entry : _network_ban_list) { + if (address.IsInNetmask(entry)) { + Packet p(Tban_packet); + p.PrepareToSend(); + + Debug(net, 2, "[{}] Banned ip tried to join ({}), refused", Tsocket::GetName(), entry); + + if (p.TransferOut(send, s, 0) < 0) { + Debug(net, 0, "[{}] send failed: {}", Tsocket::GetName(), NetworkError::GetLast().AsString()); + } + closesocket(s); + return false; + } + } + + /* Can we handle a new client? */ + if (!Tsocket::AllowConnection()) { + /* No more clients allowed? + * Send to the client that we are full! */ + Packet p(Tfull_packet); + p.PrepareToSend(); + + if (p.TransferOut(send, s, 0) < 0) { + Debug(net, 0, "[{}] send failed: {}", Tsocket::GetName(), NetworkError::GetLast().AsString()); + } + closesocket(s); + + return false; + } + + return true; + } + /** * Accepts clients from the sockets. * @param ls Socket to accept clients from. @@ -53,41 +89,7 @@ public: SetNoDelay(s); // XXX error handling? - /* Check if the client is banned */ - bool banned = false; - for (const auto &entry : _network_ban_list) { - banned = address.IsInNetmask(entry); - if (banned) { - Packet p(Tban_packet); - p.PrepareToSend(); - - Debug(net, 2, "[{}] Banned ip tried to join ({}), refused", Tsocket::GetName(), entry); - - if (p.TransferOut(send, s, 0) < 0) { - Debug(net, 0, "[{}] send failed: {}", Tsocket::GetName(), NetworkError::GetLast().AsString()); - } - closesocket(s); - break; - } - } - /* If this client is banned, continue with next client */ - if (banned) continue; - - /* Can we handle a new client? */ - if (!Tsocket::AllowConnection()) { - /* no more clients allowed? - * Send to the client that we are full! */ - Packet p(Tfull_packet); - p.PrepareToSend(); - - if (p.TransferOut(send, s, 0) < 0) { - Debug(net, 0, "[{}] send failed: {}", Tsocket::GetName(), NetworkError::GetLast().AsString()); - } - closesocket(s); - - continue; - } - + if (!Tsocket::ValidateClient(s, address)) continue; Tsocket::AcceptConnection(s, address); } } diff --git a/src/network/core/tcp_stun.cpp b/src/network/core/tcp_stun.cpp new file mode 100644 index 0000000000..b6aeff6836 --- /dev/null +++ b/src/network/core/tcp_stun.cpp @@ -0,0 +1,29 @@ +/* + * This file is part of OpenTTD. + * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. + * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see . + */ + +/** + * @file tcp_stun.cpp Basic functions to receive and send STUN packets. + */ + +#include "../../stdafx.h" +#include "../../debug.h" +#include "tcp_stun.h" + +#include "../../safeguards.h" + +/** + * Helper for logging receiving invalid packets. + * @param type The received packet type. + * @return Always false, as it's an error. + */ +bool NetworkStunSocketHandler::ReceiveInvalidPacket(PacketStunType type) +{ + Debug(net, 0, "[tcp/stun] Received illegal packet type {}", type); + return false; +} + +bool NetworkStunSocketHandler::Receive_SERCLI_STUN(Packet *p) { return this->ReceiveInvalidPacket(PACKET_STUN_SERCLI_STUN); } diff --git a/src/network/core/tcp_stun.h b/src/network/core/tcp_stun.h new file mode 100644 index 0000000000..e96a97ef36 --- /dev/null +++ b/src/network/core/tcp_stun.h @@ -0,0 +1,53 @@ +/* + * This file is part of OpenTTD. + * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. + * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see . + */ + +/** + * @file tcp_stun.h Basic functions to receive and send TCP packets to/from the STUN server. + */ + +#ifndef NETWORK_CORE_TCP_STUN_H +#define NETWORK_CORE_TCP_STUN_H + +#include "os_abstraction.h" +#include "tcp.h" +#include "packet.h" + +/** Enum with all types of TCP STUN packets. The order MUST not be changed. **/ +enum PacketStunType { + PACKET_STUN_SERCLI_STUN, ///< Send a STUN request to the STUN server. + PACKET_STUN_END, ///< Must ALWAYS be on the end of this list!! (period) +}; + +/** Base socket handler for all STUN TCP sockets. */ +class NetworkStunSocketHandler : public NetworkTCPSocketHandler { +protected: + bool ReceiveInvalidPacket(PacketStunType type); + + /** + * Send a STUN request to the STUN server letting the Game Coordinator know + * what our actually public IP:port is. + * + * uint8 Game Coordinator protocol version. + * string Token to track the current STUN request. + * uint8 Which interface number this is (for example, IPv4 or IPv6). + * The Game Coordinator relays this number back in later packets. + * + * @param p The packet that was just received. + * @return True upon success, otherwise false. + */ + virtual bool Receive_SERCLI_STUN(Packet *p); + +public: + /** + * Create a new cs socket handler for a given cs. + * @param s the socket we are connected with. + * @param address IP etc. of the client. + */ + NetworkStunSocketHandler(SOCKET s = INVALID_SOCKET) : NetworkTCPSocketHandler(s) {} +}; + +#endif /* NETWORK_CORE_TCP_STUN_H */ diff --git a/src/network/network_coordinator.cpp b/src/network/network_coordinator.cpp index 22b303f005..bf8e062611 100644 --- a/src/network/network_coordinator.cpp +++ b/src/network/network_coordinator.cpp @@ -19,6 +19,8 @@ #include "network_coordinator.h" #include "network_gamelist.h" #include "network_internal.h" +#include "network_server.h" +#include "network_stun.h" #include "table/strings.h" #include "../safeguards.h" @@ -51,7 +53,48 @@ public: void OnConnect(SOCKET s) override { - _network_coordinator_client.ConnectSuccess(this->token, s); + NetworkAddress address = NetworkAddress::GetPeerAddress(s); + _network_coordinator_client.ConnectSuccess(this->token, s, address); + } +}; + +/** Connecter used after STUN exchange to connect from both sides to each other. */ +class NetworkReuseStunConnecter : public TCPConnecter { +private: + std::string token; ///< Token of this connection. + uint8 tracking_number; ///< Tracking number of this connection. + uint8 family; ///< Family of this connection. + +public: + /** + * Try to establish a STUN-based connection. + * @param hostname The hostname of the peer. + * @param port The port of the peer. + * @param bind_address The local bind address used for this connection. + * @param token The connection token. + * @param tracking_number The tracking number of the connection. + * @param family The family this connection is using. + */ + NetworkReuseStunConnecter(const std::string &hostname, uint16 port, const NetworkAddress &bind_address, std::string token, uint8 tracking_number, uint8 family) : + TCPConnecter(hostname, port, bind_address), + token(token), + tracking_number(tracking_number), + family(family) + { + } + + void OnFailure() override + { + /* Close the STUN connection too, as it is no longer of use. */ + _network_coordinator_client.CloseStunHandler(this->token, this->family); + + _network_coordinator_client.ConnectFailure(this->token, this->tracking_number); + } + + void OnConnect(SOCKET s) override + { + NetworkAddress address = NetworkAddress::GetPeerAddress(s); + _network_coordinator_client.ConnectSuccess(this->token, s, address); } }; @@ -101,10 +144,7 @@ bool ClientNetworkCoordinatorSocketHandler::Receive_GC_ERROR(Packet *p) return false; case NETWORK_COORDINATOR_ERROR_INVALID_INVITE_CODE: { - /* Find the connecter based on the invite code. */ - auto connecter_it = this->connecter_pre.find(detail); - if (connecter_it == this->connecter_pre.end()) return true; - this->connecter_pre.erase(connecter_it); + this->CloseToken(detail); /* Mark the server as offline. */ NetworkGameList *item = NetworkGameListAddItem(detail); @@ -148,6 +188,7 @@ bool ClientNetworkCoordinatorSocketHandler::Receive_GC_REGISTER_ACK(Packet *p) switch (_network_server_connection_type) { case CONNECTION_TYPE_ISOLATED: connection_type = "Remote players can't connect"; break; case CONNECTION_TYPE_DIRECT: connection_type = "Public"; break; + case CONNECTION_TYPE_STUN: connection_type = "Behind NAT"; break; case CONNECTION_TYPE_UNKNOWN: // Never returned from Game Coordinator. default: connection_type = "Unknown"; break; // Should never happen, but don't fail if it does. @@ -263,6 +304,49 @@ bool ClientNetworkCoordinatorSocketHandler::Receive_GC_DIRECT_CONNECT(Packet *p) return true; } +bool ClientNetworkCoordinatorSocketHandler::Receive_GC_STUN_REQUEST(Packet *p) +{ + std::string token = p->Recv_string(NETWORK_TOKEN_LENGTH); + + this->stun_handlers[token][AF_INET6] = ClientNetworkStunSocketHandler::Stun(token, AF_INET6); + this->stun_handlers[token][AF_INET] = ClientNetworkStunSocketHandler::Stun(token, AF_INET); + return true; +} + +bool ClientNetworkCoordinatorSocketHandler::Receive_GC_STUN_CONNECT(Packet *p) +{ + std::string token = p->Recv_string(NETWORK_TOKEN_LENGTH); + uint8 tracking_number = p->Recv_uint8(); + uint8 family = p->Recv_uint8(); + std::string host = p->Recv_string(NETWORK_HOSTNAME_PORT_LENGTH); + uint16 port = p->Recv_uint16(); + + /* Check if we know this token. */ + auto stun_it = this->stun_handlers.find(token); + if (stun_it == this->stun_handlers.end()) return true; + auto family_it = stun_it->second.find(family); + if (family_it == stun_it->second.end()) return true; + + /* Ensure all other pending connection attempts are killed. */ + if (this->game_connecter != nullptr) { + this->game_connecter->Kill(); + this->game_connecter = nullptr; + } + + /* We now mark the connection as closed, but we do not really close the + * socket yet. We do this when the NetworkReuseStunConnecter is connected. + * This prevents any NAT to already remove the route while we create the + * second connection on top of the first. */ + family_it->second->CloseConnection(false); + + /* Connect to our peer from the same local address as we use for the + * STUN server. This means that if there is any NAT in the local network, + * the public ip:port is still pointing to the local address, and as such + * a connection can be established. */ + this->game_connecter = new NetworkReuseStunConnecter(host, port, family_it->second->local_addr, token, tracking_number, family); + return true; +} + void ClientNetworkCoordinatorSocketHandler::Connect() { /* We are either already connected or are trying to connect. */ @@ -399,11 +483,8 @@ void ClientNetworkCoordinatorSocketHandler::ConnectFailure(const std::string &to this->SendPacket(p); - auto connecter_it = this->connecter.find(token); - assert(connecter_it != this->connecter.end()); - - connecter_it->second->SetFailure(); - this->connecter.erase(connecter_it); + /* We do not close the associated connecter here yet, as the + * Game Coordinator might have other methods of connecting available. */ } /** @@ -412,26 +493,72 @@ void ClientNetworkCoordinatorSocketHandler::ConnectFailure(const std::string &to * @param token Token of the connecter that succeeded. * @param sock The socket that the connecter can now use. */ -void ClientNetworkCoordinatorSocketHandler::ConnectSuccess(const std::string &token, SOCKET sock) +void ClientNetworkCoordinatorSocketHandler::ConnectSuccess(const std::string &token, SOCKET sock, NetworkAddress &address) { /* Connecter will destroy itself. */ this->game_connecter = nullptr; - assert(!_network_server); + if (_network_server) { + if (!ServerNetworkGameSocketHandler::ValidateClient(sock, address)) return; + Debug(net, 3, "[{}] Client connected from {} on frame {}", ServerNetworkGameSocketHandler::GetName(), address.GetHostname(), _frame_counter); + ServerNetworkGameSocketHandler::AcceptConnection(sock, address); + } else { + /* The client informs the Game Coordinator about the success. The server + * doesn't have to, as it is implied by the client telling. */ + Packet *p = new Packet(PACKET_COORDINATOR_CLIENT_CONNECTED); + p->Send_uint8(NETWORK_COORDINATOR_VERSION); + p->Send_string(token); + this->SendPacket(p); + + auto connecter_it = this->connecter.find(token); + assert(connecter_it != this->connecter.end()); + + connecter_it->second->SetConnected(sock); + this->connecter.erase(connecter_it); + } - Packet *p = new Packet(PACKET_COORDINATOR_CLIENT_CONNECTED); + /* Close all remaining connections. */ + this->CloseToken(token); +} + +/** + * Callback from the STUN connecter to inform the Game Coordinator about the + * result of the STUN. + * + * This helps the Game Coordinator not to wait for a timeout on its end, but + * rather react as soon as the client/server knows the result. + */ +void ClientNetworkCoordinatorSocketHandler::StunResult(const std::string &token, uint8 family, bool result) +{ + Packet *p = new Packet(PACKET_COORDINATOR_SERCLI_STUN_RESULT); p->Send_uint8(NETWORK_COORDINATOR_VERSION); p->Send_string(token); + p->Send_uint8(family); + p->Send_bool(result); this->SendPacket(p); +} - auto connecter_it = this->connecter.find(token); - assert(connecter_it != this->connecter.end()); +void ClientNetworkCoordinatorSocketHandler::CloseStunHandler(const std::string &token, uint8 family) +{ + auto stun_it = this->stun_handlers.find(token); + if (stun_it == this->stun_handlers.end()) return; - connecter_it->second->SetConnected(sock); - this->connecter.erase(connecter_it); + if (family == AF_UNSPEC) { + for (auto &[family, stun_handler] : stun_it->second) { + stun_handler->CloseConnection(); + stun_handler->CloseSocket(); + } - /* Close all remaining connections. */ - this->CloseToken(token); + this->stun_handlers.erase(stun_it); + } else { + auto family_it = stun_it->second.find(family); + if (family_it == stun_it->second.end()) return; + + family_it->second->CloseConnection(); + family_it->second->CloseSocket(); + + stun_it->second.erase(family_it); + } } /** @@ -445,6 +572,21 @@ void ClientNetworkCoordinatorSocketHandler::CloseToken(const std::string &token) this->game_connecter->Kill(); this->game_connecter = nullptr; } + + /* Close all remaining STUN connections. */ + this->CloseStunHandler(token); + + /* Close the caller of the connection attempt. */ + auto connecter_it = this->connecter.find(token); + if (connecter_it != this->connecter.end()) { + connecter_it->second->SetFailure(); + this->connecter.erase(connecter_it); + } + auto connecter_pre_it = this->connecter_pre.find(token); + if (connecter_pre_it != this->connecter_pre.end()) { + connecter_pre_it->second->SetFailure(); + this->connecter_pre.erase(connecter_pre_it); + } } /** @@ -460,6 +602,7 @@ void ClientNetworkCoordinatorSocketHandler::CloseAllTokens() /* Mark any pending connecters as failed. */ for (auto &[token, it] : this->connecter) { + this->CloseStunHandler(token); it->SetFailure(); } for (auto &[invite_code, it] : this->connecter_pre) { @@ -535,4 +678,10 @@ void ClientNetworkCoordinatorSocketHandler::SendReceive() } this->SendPackets(); + + for (const auto &[token, families] : this->stun_handlers) { + for (const auto &[family, stun_handler] : families) { + stun_handler->SendReceive(); + } + } } diff --git a/src/network/network_coordinator.h b/src/network/network_coordinator.h index f846958bf8..9dd4c9b392 100644 --- a/src/network/network_coordinator.h +++ b/src/network/network_coordinator.h @@ -11,6 +11,7 @@ #define NETWORK_COORDINATOR_H #include "core/tcp_coordinator.h" +#include "network_stun.h" #include /** @@ -33,6 +34,12 @@ * - Send the client a GC_CONNECT with the peer address. * - a) Client connects, client sends CLIENT_CONNECTED to Game Coordinator. * - b) Client connect fails, client sends CLIENT_CONNECT_FAILED to Game Coordinator. + * 2) STUN? (see https://en.wikipedia.org/wiki/STUN) + * - Game Coordinator sends GC_STUN_REQUEST to server/client (asking for both IPv4 and IPv6 STUN requests). + * - Game Coordinator collects what combination works and sends GC_STUN_CONNECT to server/client. + * - a) Server/client connect, client sends CLIENT_CONNECTED to Game Coordinator. + * - b) Server/client connect fails, both send SERCLI_CONNECT_FAILED to Game Coordinator. + * - Game Coordinator tries other combination if available. * - If all fails, Game Coordinator sends GC_CONNECT_FAILED to indicate no connection is possible. */ @@ -42,6 +49,7 @@ private: std::chrono::steady_clock::time_point next_update; ///< When to send the next update (if server and public). std::map connecter; ///< Based on tokens, the current connecters that are pending. std::map connecter_pre; ///< Based on invite codes, the current connecters that are pending. + std::map>> stun_handlers; ///< All pending STUN handlers, stored by token:family. TCPConnecter *game_connecter = nullptr; ///< Pending connecter to the game server. protected: @@ -51,6 +59,8 @@ protected: bool Receive_GC_CONNECTING(Packet *p) override; bool Receive_GC_CONNECT_FAILED(Packet *p) override; bool Receive_GC_DIRECT_CONNECT(Packet *p) override; + bool Receive_GC_STUN_REQUEST(Packet *p) override; + bool Receive_GC_STUN_CONNECT(Packet *p) override; public: /** The idle timeout; when to close the connection because it's idle. */ @@ -65,11 +75,13 @@ public: void SendReceive(); void ConnectFailure(const std::string &token, uint8 tracking_number); - void ConnectSuccess(const std::string &token, SOCKET sock); + void ConnectSuccess(const std::string &token, SOCKET sock, NetworkAddress &address); + void StunResult(const std::string &token, uint8 family, bool result); void Connect(); void CloseToken(const std::string &token); void CloseAllTokens(); + void CloseStunHandler(const std::string &token, uint8 family = AF_UNSPEC); void Register(); void SendServerUpdate(); diff --git a/src/network/network_stun.cpp b/src/network/network_stun.cpp new file mode 100644 index 0000000000..4af9b1e9d4 --- /dev/null +++ b/src/network/network_stun.cpp @@ -0,0 +1,140 @@ +/* + * This file is part of OpenTTD. + * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. + * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see . + */ + +/** @file network_stun.cpp STUN sending/receiving part of the network protocol. */ + +#include "../stdafx.h" +#include "../debug.h" +#include "network.h" +#include "network_coordinator.h" +#include "network_stun.h" + +#include "../safeguards.h" + +/** Connect to the STUN server. */ +class NetworkStunConnecter : public TCPConnecter { +private: + ClientNetworkStunSocketHandler *stun_handler; + std::string token; + uint8 family; + +public: + /** + * Initiate the connecting. + * @param stun_handler The handler for this request. + * @param connection_string The address of the server. + */ + NetworkStunConnecter(ClientNetworkStunSocketHandler *stun_handler, const std::string &connection_string, const std::string &token, uint8 family) : + TCPConnecter(connection_string, NETWORK_STUN_SERVER_PORT, NetworkAddress(), family), + stun_handler(stun_handler), + token(token), + family(family) + { + } + + void OnFailure() override + { + this->stun_handler->connecter = nullptr; + + /* Connection to STUN server failed. For example, the client doesn't + * support IPv6, which means it will fail that attempt. */ + + _network_coordinator_client.StunResult(this->token, this->family, false); + } + + void OnConnect(SOCKET s) override + { + this->stun_handler->connecter = nullptr; + + assert(this->stun_handler->sock == INVALID_SOCKET); + this->stun_handler->sock = s; + + /* Store the local address; later connects will reuse it again. + * This is what makes STUN work for most NATs. */ + this->stun_handler->local_addr = NetworkAddress::GetSockAddress(s); + + /* We leave the connection open till the real connection is setup later. */ + } +}; + +/** + * Connect to the STUN server over either IPv4 or IPv6. + * @param token The token as received from the Game Coordinator. + * @param family What IP family to use. + */ +void ClientNetworkStunSocketHandler::Connect(const std::string &token, uint8 family) +{ + this->token = token; + this->family = family; + + this->connecter = new NetworkStunConnecter(this, NetworkStunConnectionString(), token, family); +} + +/** + * Send a STUN packet to the STUN server. + * @param token The token as received from the Game Coordinator. + * @param family What IP family this STUN request is for. + * @return The handler for this STUN request. + */ +std::unique_ptr ClientNetworkStunSocketHandler::Stun(const std::string &token, uint8 family) +{ + auto stun_handler = std::make_unique(); + + stun_handler->Connect(token, family); + + Packet *p = new Packet(PACKET_STUN_SERCLI_STUN); + p->Send_uint8(NETWORK_COORDINATOR_VERSION); + p->Send_string(token); + p->Send_uint8(family); + + stun_handler->SendPacket(p); + + return stun_handler; +} + +NetworkRecvStatus ClientNetworkStunSocketHandler::CloseConnection(bool error) +{ + NetworkStunSocketHandler::CloseConnection(error); + + /* If our connecter is still pending, shut it down too. Otherwise the + * callback of the connecter can call into us, and our object is most + * likely about to be destroyed. */ + if (this->connecter != nullptr) { + this->connecter->Kill(); + this->connecter = nullptr; + } + + return NETWORK_RECV_STATUS_OKAY; +} + +/** + * Check whether we received/can send some data from/to the STUN server and + * when that's the case handle it appropriately. + */ +void ClientNetworkStunSocketHandler::SendReceive() +{ + if (this->sock == INVALID_SOCKET) return; + + /* We never attempt to receive anything on a STUN socket. After + * connecting a STUN connection, the local address will be reused to + * to establish the connection with the real server. If we would be to + * read this socket, some OSes get confused and deliver us packets meant + * for the real connection. It appears most OSes play best when we simply + * never attempt to read it to start with (and the packets will remain + * available on the other socket). + * Protocol-wise, the STUN server will never send any packet back anyway. */ + + this->CanSendReceive(); + if (this->SendPackets() == SPS_ALL_SENT && !this->sent_result) { + /* We delay giving the GC the result this long, as to make sure we + * have sent the STUN packet first. This means the GC is more likely + * to have the result ready by the time our StunResult() packet + * arrives. */ + this->sent_result = true; + _network_coordinator_client.StunResult(this->token, this->family, true); + } +} diff --git a/src/network/network_stun.h b/src/network/network_stun.h new file mode 100644 index 0000000000..8ffbff5002 --- /dev/null +++ b/src/network/network_stun.h @@ -0,0 +1,34 @@ +/* + * This file is part of OpenTTD. + * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. + * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see . + */ + +/** @file network_stun.h Part of the network protocol handling STUN requests. */ + +#ifndef NETWORK_STUN_H +#define NETWORK_STUN_H + +#include "core/tcp_stun.h" + +/** Class for handling the client side of the STUN connection. */ +class ClientNetworkStunSocketHandler : public NetworkStunSocketHandler { +private: + std::string token; ///< Token of this STUN handler. + uint8 family = AF_UNSPEC; ///< Family of this STUN handler. + bool sent_result = false; ///< Did we sent the result of the STUN connection? + +public: + TCPConnecter *connecter = nullptr; ///< Connecter instance. + NetworkAddress local_addr; ///< Local addresses of the socket. + + NetworkRecvStatus CloseConnection(bool error = true) override; + void SendReceive(); + + void Connect(const std::string &token, uint8 family); + + static std::unique_ptr Stun(const std::string &token, uint8 family); +}; + +#endif /* NETWORK_STUN_H */ From 29cceb59a5b58d8c00a78022091f78838392cdce Mon Sep 17 00:00:00 2001 From: Patric Stout Date: Thu, 15 Jul 2021 19:43:01 +0200 Subject: [PATCH 41/44] Doc: explain in a bit more detail how we implemented STUN --- docs/game_coordinator.md | 64 +++++++++++++++++++++++++++++++ src/network/network_coordinator.h | 4 +- 2 files changed, 67 insertions(+), 1 deletion(-) create mode 100644 docs/game_coordinator.md diff --git a/docs/game_coordinator.md b/docs/game_coordinator.md new file mode 100644 index 0000000000..e8260f2e1c --- /dev/null +++ b/docs/game_coordinator.md @@ -0,0 +1,64 @@ +# Game Coordinator + +To allow two players to play together, OpenTTD uses a Game Coordinator to +facilitate this. + +When a server starts, it can register itself to the Game Coordinator. This +happens when `server_game_type` is set to either `invite-only` or `public`. +Upon registration, the Game Coordinator probes the network of the server, and +assigns the server an unique code, called an `invite code`. + +When a client wants to join a server, it asks the Game Coordinator to help +with this by giving it the `invite code` of the server. The Game Coordinator +will, in this order, attempt several ways to connect the client and server +together: + +## 1) Via direct IPv4 / IPv6 + +Upon registration, the Game Coordinator probes the server to see if a +direction connection to the server is possible based on the game port. It +tries this over the public IPv4 and IPv6, as announced by the server. +If either (or both) are successful, a client will always be asked to try +these direct IPs first when it wants to connect to this server. + +- If the server is IPv4 only, the client is only asked to connect via IPv4. +- If the server is IPv6 only, the client is only asked to connect via IPv6. +- If the server is both IPv4 and IPv6, the client is asked to connect to to + one of those first. If that fails, it is asked to connect to the other. + Whether it tries IPv4 or IPv6 first, strongly depends on several network + infrastructure related events. The biggest influence is the network + latency over both protocols to the OpenTTD infrastructure. + +In the end, if either the server is not reachable directly from the Internet +or the client fails to connect to either one of them, the connection attempt +continues with the next available method. + +## 2) Via STUN + +When a client wants to join a server via STUN, both the client and server +are asked to create a connection to the STUN server (normally +`stun.openttd.org`) via both IPv4 and IPv6. If the client or server has no +IPv4 or IPv6, it will not make a connection attempt via that protocol. + +The STUN server collects the public IPv4 and/or IPv6 of the client and server, +together with the port the connection came in from. For most NAT gateways it +holds true that as long as you use the same local IP + port, your public +IP + port will remain the same, and allow for bi-directional communication. +And this fact is used to later on pair the client and server. + +The STUN server reports this information directly to the Game Coordinator +(this in contrast to most STUN implementation, where this information is +first reported back to the peer itself, which has to relay it back to the +coordinator. OpenTTD skips this step, as the STUN server can directly talk to +the Game Coordinator). When the Game Coordinator sees a matching pair (in +terms of IPv4 / IPv6), it will ask both the client and server to connect to +their peer based on this public IP + port. + +As the NAT gateway forwards the traffic on the public IP + port to the local +port, this creates a bi-directional socket between client and server. The +connection to the STUN server can now safely be closed, and the client and +server can continue to talk to each other. + +Some NAT gateways do not allow this method; for those this attempt will fail, +and this also means that it is not possible to create a connection between +the client and server. diff --git a/src/network/network_coordinator.h b/src/network/network_coordinator.h index 9dd4c9b392..f6859f859f 100644 --- a/src/network/network_coordinator.h +++ b/src/network/network_coordinator.h @@ -16,6 +16,8 @@ /** * Game Coordinator communication. + * For more detail about what the Game Coordinator does, please see + * docs/game_coordinator.md. * * For servers: * - Server sends SERVER_REGISTER. @@ -34,7 +36,7 @@ * - Send the client a GC_CONNECT with the peer address. * - a) Client connects, client sends CLIENT_CONNECTED to Game Coordinator. * - b) Client connect fails, client sends CLIENT_CONNECT_FAILED to Game Coordinator. - * 2) STUN? (see https://en.wikipedia.org/wiki/STUN) + * 2) STUN? * - Game Coordinator sends GC_STUN_REQUEST to server/client (asking for both IPv4 and IPv6 STUN requests). * - Game Coordinator collects what combination works and sends GC_STUN_CONNECT to server/client. * - a) Server/client connect, client sends CLIENT_CONNECTED to Game Coordinator. From 96add9c36d0b5e578fe926d98ae35e8f14d5ec9e Mon Sep 17 00:00:00 2001 From: translators Date: Fri, 16 Jul 2021 18:51:17 +0000 Subject: [PATCH 42/44] Update: Translations from eints spanish (mexican): 1 change by absay dutch: 15 changes by Afoklala portuguese: 1 change by azulcosta --- src/lang/dutch.txt | 16 +++++++++++++++- src/lang/portuguese.txt | 1 + src/lang/spanish_MX.txt | 1 + 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/src/lang/dutch.txt b/src/lang/dutch.txt index 7047dd950b..37628b3839 100644 --- a/src/lang/dutch.txt +++ b/src/lang/dutch.txt @@ -1996,7 +1996,9 @@ STR_FACE_EARRING :Oorbel: STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}Verander das of oorbel ############ Next lines match ServerGameType +STR_NETWORK_SERVER_VISIBILITY_LOCAL :Lokaal STR_NETWORK_SERVER_VISIBILITY_PUBLIC :Openbaar +STR_NETWORK_SERVER_VISIBILITY_INVITE_ONLY :Alleen op uitnodiging ############ End of leave-in-this-order # Network server list @@ -2031,6 +2033,7 @@ STR_NETWORK_SERVER_LIST_SERVER_VERSION :{SILVER}Serverv STR_NETWORK_SERVER_LIST_SERVER_ADDRESS :{SILVER}Serveradres: {WHITE}{STRING} STR_NETWORK_SERVER_LIST_START_DATE :{SILVER}Startdatum: {WHITE}{DATE_SHORT} STR_NETWORK_SERVER_LIST_CURRENT_DATE :{SILVER}Huidige datum: {WHITE}{DATE_SHORT} +STR_NETWORK_SERVER_LIST_GAMESCRIPT :{SILVER}Spelscript: {WHITE}{STRING} (v{NUM}) STR_NETWORK_SERVER_LIST_PASSWORD :{SILVER}Beveiligd met wachtwoord! STR_NETWORK_SERVER_LIST_SERVER_OFFLINE :{SILVER}SERVER OFFLINE STR_NETWORK_SERVER_LIST_SERVER_FULL :{SILVER}SERVER VOL @@ -2046,11 +2049,12 @@ STR_NETWORK_SERVER_LIST_SEARCH_SERVER_INTERNET_TOOLTIP :{BLACK}Op het i STR_NETWORK_SERVER_LIST_SEARCH_SERVER_LAN :{BLACK}Op LAN zoeken STR_NETWORK_SERVER_LIST_SEARCH_SERVER_LAN_TOOLTIP :{BLACK}Op lokaal netwerk zoeken naar servers STR_NETWORK_SERVER_LIST_ADD_SERVER :{BLACK}Voeg server toe -STR_NETWORK_SERVER_LIST_ADD_SERVER_TOOLTIP :{BLACK}Voegt een server toe aan de lijst die altijd gecontroleerd zal worden op draaiende spellen +STR_NETWORK_SERVER_LIST_ADD_SERVER_TOOLTIP :{BLACK}Voegt een server toe aan de lijst. Dit kan een serveradres of een uitnodigingscode zijn STR_NETWORK_SERVER_LIST_START_SERVER :{BLACK}Start server STR_NETWORK_SERVER_LIST_START_SERVER_TOOLTIP :{BLACK}Start eigen server STR_NETWORK_SERVER_LIST_PLAYER_NAME_OSKTITLE :{BLACK}Vul je naam in +STR_NETWORK_SERVER_LIST_ENTER_SERVER_ADDRESS :{BLACK}Voer serveradres of uitnodigingscode in # Start new multiplayer server STR_NETWORK_START_SERVER_CAPTION :{WHITE}Nieuw spel met meerdere spelers starten @@ -2136,6 +2140,10 @@ STR_NETWORK_CLIENT_LIST_SERVER_NAME_EDIT_TOOLTIP :{BLACK}De naam 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_SERVER_INVITE_CODE :{BLACK}Uitnodigingscode +STR_NETWORK_CLIENT_LIST_SERVER_INVITE_CODE_TOOLTIP :{BLACK}Uitnodigingscode waarmee andere spelers toegang krijgen tot deze server +STR_NETWORK_CLIENT_LIST_SERVER_CONNECTION_TYPE :{BLACK}Type verbinding +STR_NETWORK_CLIENT_LIST_SERVER_CONNECTION_TYPE_TOOLTIP :{BLACK}Of en hoe je server toegankelijk is voor anderen 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 @@ -2155,6 +2163,9 @@ STR_NETWORK_CLIENT_LIST_PLAYER_ICON_HOST_TOOLTIP :{BLACK}Dit is d STR_NETWORK_CLIENT_LIST_CLIENT_COMPANY_COUNT :{BLACK}{NUM} klant{P "" en} / {NUM} bedrij{P f ven} ############ Begin of ConnectionType +STR_NETWORK_CLIENT_LIST_SERVER_CONNECTION_TYPE_UNKNOWN :{BLACK}Lokaal +STR_NETWORK_CLIENT_LIST_SERVER_CONNECTION_TYPE_ISOLATED :{RED}Externe spelers kunnen geen verbinding maken +STR_NETWORK_CLIENT_LIST_SERVER_CONNECTION_TYPE_DIRECT :{BLACK}Openbaar ############ End of ConnectionType STR_NETWORK_CLIENT_LIST_ADMIN_CLIENT_KICK :Eruit schoppen @@ -2284,6 +2295,9 @@ STR_NETWORK_MESSAGE_SERVER_SHUTDOWN :{WHITE}De serve STR_NETWORK_MESSAGE_SERVER_REBOOT :{WHITE}De server wordt opnieuw gestart...{}Wacht alstublieft... STR_NETWORK_MESSAGE_KICKED :*** {STRING} is eruit geschopt. Reden: ({STRING}) +STR_NETWORK_ERROR_COORDINATOR_REGISTRATION_FAILED :{WHITE}Serverregistratie mislukt +STR_NETWORK_ERROR_COORDINATOR_ISOLATED :{WHITE}Je server staat geen externe verbindingen toe +STR_NETWORK_ERROR_COORDINATOR_ISOLATED_DETAIL :{WHITE}Andere spelers krijgen geen toegang tot je server # Content downloading window STR_CONTENT_TITLE :{WHITE}Download extra inhoud diff --git a/src/lang/portuguese.txt b/src/lang/portuguese.txt index 2a6c55c6a9..d31ae34386 100644 --- a/src/lang/portuguese.txt +++ b/src/lang/portuguese.txt @@ -2034,6 +2034,7 @@ STR_NETWORK_SERVER_LIST_SERVER_VERSION :{SILVER}Versão STR_NETWORK_SERVER_LIST_SERVER_ADDRESS :{SILVER}Endereço do servidor: {WHITE}{STRING} STR_NETWORK_SERVER_LIST_START_DATE :{SILVER}Data de início: {WHITE}{DATE_SHORT} STR_NETWORK_SERVER_LIST_CURRENT_DATE :{SILVER}Data actual: {WHITE}{DATE_SHORT} +STR_NETWORK_SERVER_LIST_GAMESCRIPT :{SILVER}Script de Jogo: {WHITE}{STRING} (v{NUM}) STR_NETWORK_SERVER_LIST_PASSWORD :{SILVER}Protegido por palavra-chave! STR_NETWORK_SERVER_LIST_SERVER_OFFLINE :{SILVER}SERVIDOR DESLIGADO STR_NETWORK_SERVER_LIST_SERVER_FULL :{SILVER}SERVIDOR CHEIO diff --git a/src/lang/spanish_MX.txt b/src/lang/spanish_MX.txt index bb39a0d6f4..632d538fb1 100644 --- a/src/lang/spanish_MX.txt +++ b/src/lang/spanish_MX.txt @@ -2034,6 +2034,7 @@ STR_NETWORK_SERVER_LIST_SERVER_VERSION :{SILVER}Versió STR_NETWORK_SERVER_LIST_SERVER_ADDRESS :{SILVER}Dirección del servidor: {WHITE}{STRING} STR_NETWORK_SERVER_LIST_START_DATE :{SILVER}Fecha de inicio: {WHITE}{DATE_SHORT} STR_NETWORK_SERVER_LIST_CURRENT_DATE :{SILVER}Fecha actual: {WHITE}{DATE_SHORT} +STR_NETWORK_SERVER_LIST_GAMESCRIPT :{SILVER}Script de juego: {WHITE}{STRING} (versión {NUM}) STR_NETWORK_SERVER_LIST_PASSWORD :{SILVER}¡Protegido por contraseña! STR_NETWORK_SERVER_LIST_SERVER_OFFLINE :{SILVER}SERVIDOR APAGADO STR_NETWORK_SERVER_LIST_SERVER_FULL :{SILVER}SERVIDOR LLENO From afea5e85aeff20077161cc37500755e9ee9a6b14 Mon Sep 17 00:00:00 2001 From: glx22 Date: Fri, 16 Jul 2021 18:10:43 +0200 Subject: [PATCH 43/44] Fix 433f74e: GetString() requires a language pack --- src/openttd.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/openttd.cpp b/src/openttd.cpp index 904cdbbb1a..711df58c43 100644 --- a/src/openttd.cpp +++ b/src/openttd.cpp @@ -626,6 +626,7 @@ int openttd_main(int argc, char *argv[]) if (res != SL_OK || _load_check_data.HasErrors()) { fprintf(stderr, "Failed to open savegame\n"); if (_load_check_data.HasErrors()) { + InitializeLanguagePacks(); // A language pack is needed for GetString() char buf[256]; SetDParamStr(0, _load_check_data.error_data); GetString(buf, _load_check_data.error, lastof(buf)); From 16abdd52546eb3cfa9d51d674c41711c2c170029 Mon Sep 17 00:00:00 2001 From: glx22 Date: Fri, 16 Jul 2021 18:12:17 +0200 Subject: [PATCH 44/44] Change: [Win32] Set the console codepage to UTF-8 --- src/debug.cpp | 6 ------ src/os/windows/win32.cpp | 3 +++ 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/src/debug.cpp b/src/debug.cpp index 418598d5d0..2ac1dcc69c 100644 --- a/src/debug.cpp +++ b/src/debug.cpp @@ -128,13 +128,7 @@ void DebugPrint(const char *level, const std::string &message) #endif } else { std::string msg = fmt::format("{}dbg: [{}] {}\n", GetLogPrefix(), level, message); -#if defined(_WIN32) - wchar_t system_buf[512]; - convert_to_fs(msg.c_str(), system_buf, lengthof(system_buf)); - fputws(system_buf, stderr); -#else fputs(msg.c_str(), stderr); -#endif NetworkAdminConsole(level, message); if (_settings_client.gui.developer >= 2) IConsolePrint(CC_DEBUG, "dbg: [{}] {}", level, message); diff --git a/src/os/windows/win32.cpp b/src/os/windows/win32.cpp index 938aee5f9f..6b612ac404 100644 --- a/src/os/windows/win32.cpp +++ b/src/os/windows/win32.cpp @@ -393,6 +393,9 @@ int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLi * be available between subsequent calls to FS2OTTD(). */ char *cmdline = stredup(FS2OTTD(GetCommandLine()).c_str()); + /* Set the console codepage to UTF-8. */ + SetConsoleOutputCP(CP_UTF8); + #if defined(_DEBUG) CreateConsole(); #endif