diff --git a/src/fios.cpp b/src/fios.cpp index 2c2f5168bb..3cd0a9993e 100644 --- a/src/fios.cpp +++ b/src/fios.cpp @@ -392,7 +392,7 @@ static void FiosGetFileList(SaveLoadOperation fop, fios_getlist_callback_proc *c fios->mtime = 0; strecpy(fios->name, d_name, lastof(fios->name)); std::string dirname = std::string(d_name) + PATHSEP; - SetDParamStr(0, dirname.c_str()); + SetDParamStr(0, dirname); GetString(fios->title, STR_SAVELOAD_DIRECTORY, lastof(fios->title)); str_validate(fios->title, lastof(fios->title)); } diff --git a/src/fios_gui.cpp b/src/fios_gui.cpp index 288f7308d7..0e80ba60ff 100644 --- a/src/fios_gui.cpp +++ b/src/fios_gui.cpp @@ -553,7 +553,7 @@ public: const CompanyProperties &c = *pair.second; if (!c.name.empty()) { SetDParam(1, STR_JUST_RAW_STRING); - SetDParamStr(2, c.name.c_str()); + SetDParamStr(2, c.name); } else { SetDParam(1, c.name_1); SetDParam(2, c.name_2); diff --git a/src/gfx.cpp b/src/gfx.cpp index fc9d1455cd..7cf68af80a 100644 --- a/src/gfx.cpp +++ b/src/gfx.cpp @@ -675,6 +675,28 @@ int DrawString(int left, int right, int top, const char *str, TextColour colour, return DrawLayoutLine(*layout.front(), top, left, right, align, underline, true); } +/** + * Draw string, possibly truncated to make it fit in its allocated space + * + * @param left The left most position to draw on. + * @param right The right most position to draw on. + * @param top The top most position to draw on. + * @param str String to draw. + * @param colour Colour used for drawing the string, for details see _string_colourmap in + * table/palettes.h or docs/ottd-colourtext-palette.png or the enum TextColour in gfx_type.h + * @param align The alignment of the string when drawing left-to-right. In the + * case a right-to-left language is chosen this is inverted so it + * will be drawn in the right direction. + * @param underline Whether to underline what has been drawn or not. + * @param fontsize The size of the initial characters. + * @return In case of left or center alignment the right most pixel we have drawn to. + * In case of right alignment the left most pixel we have drawn to. + */ +int DrawString(int left, int right, int top, const std::string &str, TextColour colour, StringAlignment align, bool underline, FontSize fontsize) +{ + return DrawString(left, right, top, str.c_str(), colour, align, underline, fontsize); +} + /** * Draw string, possibly truncated to make it fit in its allocated space * @@ -825,6 +847,28 @@ int DrawStringMultiLine(int left, int right, int top, int bottom, const char *st return ((align & SA_VERT_MASK) == SA_BOTTOM) ? first_line : last_line; } + +/** + * Draw string, possibly over multiple lines. + * + * @param left The left most position to draw on. + * @param right The right most position to draw on. + * @param top The top most position to draw on. + * @param bottom The bottom most position to draw on. + * @param str String to draw. + * @param colour Colour used for drawing the string, for details see _string_colourmap in + * table/palettes.h or docs/ottd-colourtext-palette.png or the enum TextColour in gfx_type.h + * @param align The horizontal and vertical alignment of the string. + * @param underline Whether to underline all strings + * @param fontsize The size of the initial characters. + * + * @return If \a align is #SA_BOTTOM, the top to where we have written, else the bottom to where we have written. + */ +int DrawStringMultiLine(int left, int right, int top, int bottom, const std::string &str, TextColour colour, StringAlignment align, bool underline, FontSize fontsize) +{ + return DrawStringMultiLine(left, right, top, bottom, str.c_str(), colour, align, underline, fontsize); +} + /** * Draw string, possibly over multiple lines. * diff --git a/src/gfx_func.h b/src/gfx_func.h index a549e01425..2a7ec27ab0 100644 --- a/src/gfx_func.h +++ b/src/gfx_func.h @@ -93,8 +93,10 @@ void DrawSpriteViewport(SpriteID img, PaletteID pal, int x, int y, const SubSpri void DrawSprite(SpriteID img, PaletteID pal, int x, int y, const SubSprite *sub = nullptr, ZoomLevel zoom = ZOOM_LVL_GUI); int DrawString(int left, int right, int top, const char *str, TextColour colour = TC_FROMSTRING, StringAlignment align = SA_LEFT, bool underline = false, FontSize fontsize = FS_NORMAL); +int DrawString(int left, int right, int top, const std::string &str, TextColour colour = TC_FROMSTRING, StringAlignment align = SA_LEFT, bool underline = false, FontSize fontsize = FS_NORMAL); int DrawString(int left, int right, int top, StringID str, TextColour colour = TC_FROMSTRING, StringAlignment align = SA_LEFT, bool underline = false, FontSize fontsize = FS_NORMAL); int DrawStringMultiLine(int left, int right, int top, int bottom, const char *str, TextColour colour = TC_FROMSTRING, StringAlignment align = (SA_TOP | SA_LEFT), bool underline = false, FontSize fontsize = FS_NORMAL); +int DrawStringMultiLine(int left, int right, int top, int bottom, const std::string &str, TextColour colour = TC_FROMSTRING, StringAlignment align = (SA_TOP | SA_LEFT), bool underline = false, FontSize fontsize = FS_NORMAL); int DrawStringMultiLine(int left, int right, int top, int bottom, StringID str, TextColour colour = TC_FROMSTRING, StringAlignment align = (SA_TOP | SA_LEFT), bool underline = false, FontSize fontsize = FS_NORMAL); void DrawCharCentered(WChar c, const Rect &r, TextColour colour); diff --git a/src/industry_gui.cpp b/src/industry_gui.cpp index 9d91de9080..53e514f1b0 100644 --- a/src/industry_gui.cpp +++ b/src/industry_gui.cpp @@ -572,12 +572,12 @@ public: /* Draw the accepted cargoes, if any. Otherwise, will print "Nothing". */ GetAllCargoSuffixes(CARGOSUFFIX_IN, CST_FUND, nullptr, this->selected_type, indsp, indsp->accepts_cargo, cargo_suffix); std::string cargostring = this->MakeCargoListString(indsp->accepts_cargo, cargo_suffix, lengthof(indsp->accepts_cargo), STR_INDUSTRY_VIEW_REQUIRES_N_CARGO); - y = DrawStringMultiLine(left, right, y, bottom, cargostring.c_str()); + y = DrawStringMultiLine(left, right, y, bottom, cargostring); /* Draw the produced cargoes, if any. Otherwise, will print "Nothing". */ GetAllCargoSuffixes(CARGOSUFFIX_OUT, CST_FUND, nullptr, this->selected_type, indsp, indsp->produced_cargo, cargo_suffix); cargostring = this->MakeCargoListString(indsp->produced_cargo, cargo_suffix, lengthof(indsp->produced_cargo), STR_INDUSTRY_VIEW_PRODUCES_N_CARGO); - y = DrawStringMultiLine(left, right, y, bottom, cargostring.c_str()); + y = DrawStringMultiLine(left, right, y, bottom, cargostring); /* Get the additional purchase info text, if it has not already been queried. */ if (HasBit(indsp->callback_mask, CBM_IND_FUND_MORE_TEXT)) { @@ -972,7 +972,7 @@ public: } if (!i->text.empty()) { - SetDParamStr(0, i->text.c_str()); + SetDParamStr(0, i->text); y += WD_PAR_VSEP_WIDE; y = DrawStringMultiLine(left + WD_FRAMERECT_LEFT, right - WD_FRAMERECT_RIGHT, y, UINT16_MAX, STR_JUST_RAW_STRING, TC_BLACK); } diff --git a/src/lang/english_US.txt b/src/lang/english_US.txt index f61ccdd38d..6ab8b323c1 100644 --- a/src/lang/english_US.txt +++ b/src/lang/english_US.txt @@ -2248,6 +2248,7 @@ STR_NETWORK_ERROR_TIMEOUT_COMPUTER :{WHITE}Your com STR_NETWORK_ERROR_TIMEOUT_MAP :{WHITE}Your computer took too long to download the map STR_NETWORK_ERROR_TIMEOUT_JOIN :{WHITE}Your computer took too long to join the server STR_NETWORK_ERROR_INVALID_CLIENT_NAME :{WHITE}Your player name is not valid +STR_NETWORK_ERROR_SERVER_TOO_OLD :{WHITE}The queried server is too old for this client ############ Leave those lines in this order!! STR_NETWORK_ERROR_CLIENT_GENERAL :general error @@ -2299,7 +2300,7 @@ STR_NETWORK_MESSAGE_CLIENT_COMPANY_JOIN :*** {STRING} ha STR_NETWORK_MESSAGE_CLIENT_COMPANY_SPECTATE :*** {STRING} has joined spectators STR_NETWORK_MESSAGE_CLIENT_COMPANY_NEW :*** {STRING} has started a new company (#{2:NUM}) STR_NETWORK_MESSAGE_CLIENT_LEFT :*** {STRING} has left the game ({2:STRING}) -STR_NETWORK_MESSAGE_NAME_CHANGE :*** {STRING} has changed his/her name to {STRING} +STR_NETWORK_MESSAGE_NAME_CHANGE :*** {STRING} has changed their name to {STRING} STR_NETWORK_MESSAGE_GIVE_MONEY :*** {STRING} gave {2:CURRENCY_LONG} to {1:STRING} STR_NETWORK_MESSAGE_SERVER_SHUTDOWN :{WHITE}The server closed the session STR_NETWORK_MESSAGE_SERVER_REBOOT :{WHITE}The server is restarting...{}Please wait... diff --git a/src/lang/estonian.txt b/src/lang/estonian.txt index 6970febda1..cc57922010 100644 --- a/src/lang/estonian.txt +++ b/src/lang/estonian.txt @@ -1056,6 +1056,7 @@ STR_GAME_OPTIONS_AUTOSAVE_DROPDOWN_EVERY_12_MONTHS :12 kuud STR_GAME_OPTIONS_LANGUAGE :{BLACK}Keel STR_GAME_OPTIONS_LANGUAGE_TOOLTIP :{BLACK}Valib kasutajaliideses kasutatava keele +STR_GAME_OPTIONS_LANGUAGE_PERCENTAGE :{STRING} ({NUM}% valmis) STR_GAME_OPTIONS_FULLSCREEN :{BLACK}Täisekraan STR_GAME_OPTIONS_FULLSCREEN_TOOLTIP :{BLACK}Märgi see kast, et OpenTTD täisekraanirežiimis mängida @@ -1069,6 +1070,8 @@ STR_GAME_OPTIONS_VIDEO_ACCELERATION :{BLACK}Riistvar STR_GAME_OPTIONS_VIDEO_ACCELERATION_TOOLTIP :{BLACK}Märkides selle ruudu, lubad OpenTTD-l üritada kasutada riistvarakiirendust. Muudetud seade omab mõju pärast mängu taaskäivitust STR_GAME_OPTIONS_VIDEO_ACCELERATION_RESTART :{WHITE}Seade omab mõju alles pärast mängu taaskäivitust +STR_GAME_OPTIONS_VIDEO_VSYNC :{BLACK}VSync +STR_GAME_OPTIONS_VIDEO_VSYNC_TOOLTIP :{BLACK}Märgi, et ekraani v-sync sisse lülitada. Seade kohaldub alles pärast mängu taaskäivitust. Töötab vaid, kui riistvarakiirendus on peal STR_GAME_OPTIONS_GUI_ZOOM_FRAME :{BLACK}Liidese suurus STR_GAME_OPTIONS_GUI_ZOOM_DROPDOWN_TOOLTIP :{BLACK}Vali kasutatav liideseelementide suurus @@ -1202,6 +1205,7 @@ STR_CONFIG_SETTING_TREE_CAPTION :{WHITE}Seaded STR_CONFIG_SETTING_FILTER_TITLE :{BLACK}Märksõna: STR_CONFIG_SETTING_EXPAND_ALL :{BLACK}Ava kõik STR_CONFIG_SETTING_COLLAPSE_ALL :{BLACK}Sulge kõik +STR_CONFIG_SETTING_RESET_ALL :{BLACK}Nulli kõik väärtused STR_CONFIG_SETTING_NO_EXPLANATION_AVAILABLE_HELPTEXT :(seletus puudub) STR_CONFIG_SETTING_DEFAULT_VALUE :{LTBLUE}Vaikeväärtus: {ORANGE}{STRING} STR_CONFIG_SETTING_TYPE :{LTBLUE}Seade liik: {ORANGE}{STRING} @@ -1210,6 +1214,8 @@ STR_CONFIG_SETTING_TYPE_GAME_MENU :Mängu seade (s STR_CONFIG_SETTING_TYPE_GAME_INGAME :Mängu seaded (hoitakse salvestuses; mõjutab ainult praegust mängu) STR_CONFIG_SETTING_TYPE_COMPANY_MENU :Ettevõtte seaded (hoitakse salvestuses; mõjutab ainult uusi mänge) STR_CONFIG_SETTING_TYPE_COMPANY_INGAME :Ettevõtte seaded (hoitakse salvestuses; mõjutab ainult praegust ettevõtet) +STR_CONFIG_SETTING_RESET_ALL_CONFIRMATION_DIALOG_CAPTION :{WHITE}Ettevaatust! +STR_CONFIG_SETTING_RESET_ALL_CONFIRMATION_DIALOG_TEXT :{WHITE}See toiming nullib kõik mänguseaded.{}Oled sa kindel, et jätkata? STR_CONFIG_SETTING_RESTRICT_CATEGORY :{BLACK}Jagu: STR_CONFIG_SETTING_RESTRICT_TYPE :{BLACK}Liik: @@ -2048,6 +2054,8 @@ 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 STR_NETWORK_SERVER_LIST_CAPTION :{WHITE}Mitmikmäng @@ -2111,6 +2119,8 @@ STR_NETWORK_START_SERVER_NEW_GAME_NAME_TOOLTIP :{BLACK}Serveril STR_NETWORK_START_SERVER_SET_PASSWORD :{BLACK}Määra salasõna STR_NETWORK_START_SERVER_PASSWORD_TOOLTIP :{BLACK}Et server ei oleks avalik, kaitse oma mäng salasõnaga +STR_NETWORK_START_SERVER_VISIBILITY_LABEL :{BLACK}Nähtavus +STR_NETWORK_START_SERVER_VISIBILITY_TOOLTIP :{BLACK}Kas sinu server on kõikidele teistele avalikult nähtav STR_NETWORK_START_SERVER_CLIENTS_SELECT :{BLACK}{NUM} klient{P "" i} STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS :{BLACK}Kliente kuni: STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS.in :sees @@ -2175,12 +2185,45 @@ STR_NETWORK_NEED_GAME_PASSWORD_CAPTION :{WHITE}Server o STR_NETWORK_NEED_COMPANY_PASSWORD_CAPTION :{WHITE}Ettevõte on kaitstud. Sisesta salasõna # Network company list added strings -STR_NETWORK_COMPANY_LIST_CLIENT_LIST :Klientide nimekiri +STR_NETWORK_COMPANY_LIST_CLIENT_LIST :Ühendatud mängijad STR_NETWORK_COMPANY_LIST_SPECTATE :Jälgi # Network client list - - +STR_NETWORK_CLIENT_LIST_CAPTION :{WHITE}Mitmikmäng +STR_NETWORK_CLIENT_LIST_SERVER :{BLACK}Server +STR_NETWORK_CLIENT_LIST_SERVER_NAME :{BLACK}Nimi +STR_NETWORK_CLIENT_LIST_SERVER_NAME_TOOLTIP :{BLACK}Serveri nimi, kus sa mängid +STR_NETWORK_CLIENT_LIST_SERVER_NAME_EDIT_TOOLTIP :{BLACK}Muuda oma serveri nime +STR_NETWORK_CLIENT_LIST_SERVER_NAME_QUERY_CAPTION :Serveri nimi +STR_NETWORK_CLIENT_LIST_SERVER_VISIBILITY :{BLACK}Nähtavus +STR_NETWORK_CLIENT_LIST_SERVER_VISIBILITY_TOOLTIP :{BLACK}Kas sinu server on kõikidele teistele avalikult nähtav +STR_NETWORK_CLIENT_LIST_PLAYER :{BLACK}Mängija +STR_NETWORK_CLIENT_LIST_PLAYER_NAME :{BLACK}Nimi +STR_NETWORK_CLIENT_LIST_PLAYER_NAME_TOOLTIP :{BLACK}Sinu mängija nimi +STR_NETWORK_CLIENT_LIST_PLAYER_NAME_EDIT_TOOLTIP :{BLACK}Muuda oma mängija nime +STR_NETWORK_CLIENT_LIST_PLAYER_NAME_QUERY_CAPTION :Sinu mängija nimi +STR_NETWORK_CLIENT_LIST_ADMIN_CLIENT_TOOLTIP :{BLACK}Haldustegevused, mida teha selle ettevõtte peal +STR_NETWORK_CLIENT_LIST_ADMIN_COMPANY_TOOLTIP :{BLACK}Haldustegevused, mida teha selle ettevõtte peal +STR_NETWORK_CLIENT_LIST_JOIN_TOOLTIP :{BLACK}Liitu selle ettevõttega +STR_NETWORK_CLIENT_LIST_CHAT_CLIENT_TOOLTIP :{BLACK}Saada sellele mängijale sõnum +STR_NETWORK_CLIENT_LIST_CHAT_COMPANY_TOOLTIP :{BLACK}Saada sõnum igale selle ettevõtte mängijale +STR_NETWORK_CLIENT_LIST_CHAT_SPECTATOR_TOOLTIP :{BLACK}Saada sõnum igale vaatlejale +STR_NETWORK_CLIENT_LIST_SPECTATORS :Vaatlejad +STR_NETWORK_CLIENT_LIST_NEW_COMPANY :(Uus ettevõte) +STR_NETWORK_CLIENT_LIST_NEW_COMPANY_TOOLTIP :{BLACK}Loo uus ettevõte ja liitu sellega +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 + +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 +STR_NETWORK_CLIENT_LIST_ADMIN_COMPANY_UNLOCK :Salasõnaga avamine + +STR_NETWORK_CLIENT_LIST_ASK_CAPTION :{WHITE}Haldustoiming +STR_NETWORK_CLIENT_LIST_ASK_CLIENT_KICK :{YELLOW}Kas oled kindel, et välja visata mängija '{STRING}'? +STR_NETWORK_CLIENT_LIST_ASK_CLIENT_BAN :{YELLOW}Kas oled kindel, et sa tahad keelata mängija '{STRING}'? +STR_NETWORK_CLIENT_LIST_ASK_COMPANY_RESET :{YELLOW}Kas oled kindel, et tahad kustutada ettevõtte '{COMPANY}'? +STR_NETWORK_CLIENT_LIST_ASK_COMPANY_UNLOCK :{YELLOW}Kas oled kindel, et soovid nullida ettevõtte '{COMPANY}' salasõna? STR_NETWORK_SERVER :Server STR_NETWORK_CLIENT :Klient @@ -2225,6 +2268,7 @@ STR_NETWORK_ERROR_SERVER_START :{WHITE}Ei saa u STR_NETWORK_ERROR_CLIENT_START :{WHITE}Ei õnnestu ühendada STR_NETWORK_ERROR_TIMEOUT :{WHITE}Ühendus nr {NUM} aegus STR_NETWORK_ERROR_SERVER_ERROR :{WHITE}Tekkis protokolliviga ja ühendus katkes +STR_NETWORK_ERROR_BAD_PLAYER_NAME :{WHITE}Sinu mängijale ei ole nime määratud. Nime saab määrata mitmikmägu akna ülaosas STR_NETWORK_ERROR_WRONG_REVISION :{WHITE}Kliendi osa ei vasta serveri osaga STR_NETWORK_ERROR_WRONG_PASSWORD :{WHITE}Vale salasõna STR_NETWORK_ERROR_SERVER_FULL :{WHITE}Server on täis @@ -2237,6 +2281,8 @@ STR_NETWORK_ERROR_TIMEOUT_PASSWORD :{WHITE}Sisestas STR_NETWORK_ERROR_TIMEOUT_COMPUTER :{WHITE}Arvutil võttis liitumisega liiga kaua aega STR_NETWORK_ERROR_TIMEOUT_MAP :{WHITE}Kaardi allalaadimine võttis liiga kaua aega STR_NETWORK_ERROR_TIMEOUT_JOIN :{WHITE}Serveriga liitumine võttis liiga kaua aega +STR_NETWORK_ERROR_INVALID_CLIENT_NAME :{WHITE}Sinu mängija nimi ei vasta nõuetele +STR_NETWORK_ERROR_SERVER_TOO_OLD :{WHITE}Päritud server kasutab liiga vana versiooni ############ Leave those lines in this order!! STR_NETWORK_ERROR_CLIENT_GENERAL :üldine viga @@ -2259,6 +2305,7 @@ STR_NETWORK_ERROR_CLIENT_TIMEOUT_PASSWORD :ei saanud õige STR_NETWORK_ERROR_CLIENT_TIMEOUT_COMPUTER :üldine aegumine STR_NETWORK_ERROR_CLIENT_TIMEOUT_MAP :kaardi laadimine võttis liiga kaua aega STR_NETWORK_ERROR_CLIENT_TIMEOUT_JOIN :kaardi töötlemine võttis liiga kaua aega +STR_NETWORK_ERROR_CLIENT_INVALID_CLIENT_NAME :kliendinimi ei vasta nõuetele ############ End of leave-in-this-order STR_NETWORK_ERROR_CLIENT_GUI_LOST_CONNECTION_CAPTION :{WHITE}Võimalik ühenduse katkemine @@ -3102,6 +3149,7 @@ STR_NEWGRF_ERROR_MSG_WARNING :{RED}Hoiatus: { STR_NEWGRF_ERROR_MSG_ERROR :{RED}Viga: {SILVER}{STRING} STR_NEWGRF_ERROR_MSG_FATAL :{RED}Saatuslik viga: {SILVER}{STRING} STR_NEWGRF_ERROR_FATAL_POPUP :{WHITE}Esines raske NewGRF-i tõrge:{}{STRING} +STR_NEWGRF_ERROR_POPUP :{WHITE}Esines viga NewGRF-iga:{}{STRING} STR_NEWGRF_ERROR_VERSION_NUMBER :OpenTTD väitel {1:STRING} ei tööta selle TTDPatch versiooniga. STR_NEWGRF_ERROR_DOS_OR_WINDOWS :{1:STRING} on {STRING} TTD osa jaoks. STR_NEWGRF_ERROR_UNSET_SWITCH :{1:STRING} on mõeldud kasutamiseks {STRING} diff --git a/src/lang/finnish.txt b/src/lang/finnish.txt index c7a557ccab..b50ae29b01 100644 --- a/src/lang/finnish.txt +++ b/src/lang/finnish.txt @@ -1176,9 +1176,9 @@ STR_CONFIG_SETTING_TYPE_DROPDOWN_GAME_MENU :Pelin asetukset STR_CONFIG_SETTING_TYPE_DROPDOWN_GAME_INGAME :Pelin asetukset (tallennetaan pelitallenteeseen; vaikuttavat vain nykyiseen peliin) STR_CONFIG_SETTING_TYPE_DROPDOWN_COMPANY_MENU :Yhtiön asetukset (tallennetaan pelitallenteisiin; vaikuttavat vain uusiin peleihin) STR_CONFIG_SETTING_TYPE_DROPDOWN_COMPANY_INGAME :Yhtiön asetukset (tallennetaan pelitallenteeseen; vaikuttavat ainoastaan nykyiseen yhtiöön) -STR_CONFIG_SETTING_CATEGORY_HIDES :{BLACK}Näytä kaikki tulokset muuttamalla{}{SILVER}Kategoriaksi {WHITE}{STRING} +STR_CONFIG_SETTING_CATEGORY_HIDES :{BLACK}Näytä kaikki tulokset muuttamalla{}{SILVER}kategoriaksi {WHITE}{STRING} STR_CONFIG_SETTING_TYPE_HIDES :{BLACK}Näytä kaikki hakutulokset muuttamalla{}{SILVER}Tyypiksi {WHITE}Kaikki asetustyypit -STR_CONFIG_SETTING_CATEGORY_AND_TYPE_HIDES :{BLACK}Näytä kaikki tulokset muuttamalla{}{SILVER}Kategoriaksi {WHITE}{STRING} {BLACK}ja {SILVER}Tyypiksi {WHITE}Kaikki asetustyypit +STR_CONFIG_SETTING_CATEGORY_AND_TYPE_HIDES :{BLACK}Näytä kaikki tulokset muuttamalla{}{SILVER}kategoriaksi {WHITE}{STRING} {BLACK}ja {SILVER}tyypiksi {WHITE}kaikki asetustyypit STR_CONFIG_SETTINGS_NONE :{WHITE}- Ei mitään - STR_CONFIG_SETTING_OFF :pois @@ -1340,7 +1340,7 @@ STR_CONFIG_SETTING_AUTORENEW_MONTHS_HELPTEXT :Suhteellinen ik STR_CONFIG_SETTING_AUTORENEW_MONTHS_VALUE_BEFORE :{COMMA} kuukau{P 0 si tta} ennen kulkuneuvon käyttöiän loppua STR_CONFIG_SETTING_AUTORENEW_MONTHS_VALUE_AFTER :{COMMA} kuukau{P 0 si tta} jälkeen kulkuneuvon käyttöiän lopun STR_CONFIG_SETTING_AUTORENEW_MONEY :Vähimmäisrahamäärä kulkuneuvon automaattiseen uudistukseen: {STRING} -STR_CONFIG_SETTING_AUTORENEW_MONEY_HELPTEXT :Pienin rahamäärä, joka on oltava pankissa ennen kulkuneuvojen automaattista uudistamista +STR_CONFIG_SETTING_AUTORENEW_MONEY_HELPTEXT :Vähimmäisrahamäärä, jonka on jäätävä pankkiin kulkuneuvoja automaattisesti uudistettaessa STR_CONFIG_SETTING_ERRMSG_DURATION :Virheilmoitusten näyttöaika: {STRING} STR_CONFIG_SETTING_ERRMSG_DURATION_HELPTEXT :Aika virheilmoitusten näyttämiseen punaisessa ikkunassa. Huomaa, että jotkut (kriittiset) virheilmoitukset eivät sulkeudu automaattisesti tämän ajan jälkeen, vaan ne on suljettava käsin STR_CONFIG_SETTING_ERRMSG_DURATION_VALUE :{COMMA} sekunti{P 0 "" a} @@ -1464,7 +1464,7 @@ STR_CONFIG_SETTING_PAUSE_ON_NEW_GAME_HELPTEXT :Mikäli käytö STR_CONFIG_SETTING_COMMAND_PAUSE_LEVEL :Salli pelin ollessa pysäytettynä: {STRING} STR_CONFIG_SETTING_COMMAND_PAUSE_LEVEL_HELPTEXT :Valitse käytössä olevat toiminnot pelin ollessa pysäytettynä STR_CONFIG_SETTING_COMMAND_PAUSE_LEVEL_NO_ACTIONS :Ei mitään toimintoja -STR_CONFIG_SETTING_COMMAND_PAUSE_LEVEL_ALL_NON_CONSTRUCTION :Kaikki ei-rakennustoiminnot +STR_CONFIG_SETTING_COMMAND_PAUSE_LEVEL_ALL_NON_CONSTRUCTION :Kaikki paitsi rakennustoiminnot STR_CONFIG_SETTING_COMMAND_PAUSE_LEVEL_ALL_NON_LANDSCAPING :Kaikki paitsi maastonmuokkaustoiminnot STR_CONFIG_SETTING_COMMAND_PAUSE_LEVEL_ALL_ACTIONS :Kaikki toiminnot STR_CONFIG_SETTING_ADVANCED_VEHICLE_LISTS :Käytä ryhmiä kulkuneuvolistassa: {STRING} @@ -1601,7 +1601,7 @@ STR_CONFIG_SETTING_NEWS_MESSAGES_SUMMARY :Tiivistelmä STR_CONFIG_SETTING_NEWS_MESSAGES_FULL :Täysi STR_CONFIG_SETTING_COLOURED_NEWS_YEAR :Värilliset uutiset ilmestyvät: {STRING} -STR_CONFIG_SETTING_COLOURED_NEWS_YEAR_HELPTEXT :Vuosi, jonka jälkeen sanomalehdet ovat värillisiä. Ennen tätä vuotta sanomalehdet käyttävät mustavalkoisia kuvia +STR_CONFIG_SETTING_COLOURED_NEWS_YEAR_HELPTEXT :Vuosi, josta alkaen sanomalehdet painetaan värillisinä. Ennen tätä vuotta lehdet ovat mustavalkoiset. STR_CONFIG_SETTING_STARTING_YEAR :Aloitusvuosi: {STRING} STR_CONFIG_SETTING_ENDING_YEAR :Pistelaskun päättymisvuosi: {STRING} STR_CONFIG_SETTING_ENDING_YEAR_HELPTEXT :Pelin päättymisvuosi pisteiden laskemista varten. Tämän vuoden lopussa talletetaan yhtiön pistemäärä ja näytetään ennätysluettelo; pelaajat voivat jatkaa pelaamista tämän jälkeenkin.{}Jos päättymisvuosi on ennen alkamisvuotta, ennätyksiä ei näytetä koskaan. @@ -1717,13 +1717,13 @@ STR_CONFIG_SETTING_DISTRIBUTION_MANUAL :manuaalinen STR_CONFIG_SETTING_DISTRIBUTION_ASYMMETRIC :epäsymmetrinen STR_CONFIG_SETTING_DISTRIBUTION_SYMMETRIC :symmetrinen STR_CONFIG_SETTING_DISTRIBUTION_PAX :Matkustajien jakautuminen: {STRING} -STR_CONFIG_SETTING_DISTRIBUTION_PAX_HELPTEXT :"symmetrinen" tarkoittaa, että suunnilleen sama määrä matkustajia kulkee asemalta A asemalle B kuin asemalta B asemalle A. "epäsymmetrinen" tarkoittaa, että eri suuntiin voi kulkea eriävä määrä matkustajia. "manuaalinen" tarkoittaa, että automaattinen rahdin jakautuminen on pois käytöstä matkustajilla. +STR_CONFIG_SETTING_DISTRIBUTION_PAX_HELPTEXT :”Symmetrinen” tarkoittaa, että suunnilleen sama määrä matkustajia kulkee asemalta A asemalle B kuin asemalta B asemalle A. ”Epäsymmetrinen” tarkoittaa, että eri suuntiin voi kulkea eriävä määrä matkustajia. ”Manuaalinen” tarkoittaa, että automaattinen rahdin jakautuminen on pois käytöstä matkustajilla. STR_CONFIG_SETTING_DISTRIBUTION_MAIL :Postin jakautuminen: {STRING} -STR_CONFIG_SETTING_DISTRIBUTION_MAIL_HELPTEXT :"symmetrinen" tarkoittaa, että suunnilleen sama määrä postia lähetetään asemalta A asemalle B kuin asemalta B asemalle A. "epäsymmetrinen" tarkoittaa, että eri suuntiin voidaan lähettää eriävä määrä postia. "manuaalinen" tarkoittaa, että automaattinen rahdin jakautuminen on pois käytöstä postilla. +STR_CONFIG_SETTING_DISTRIBUTION_MAIL_HELPTEXT :”Symmetrinen” tarkoittaa, että suunnilleen sama määrä postia lähetetään asemalta A asemalle B kuin asemalta B asemalle A. ”Epäsymmetrinen” tarkoittaa, että eri suuntiin voidaan lähettää eriävä määrä postia. ”Manuaalinen” tarkoittaa, että automaattinen rahdin jakautuminen on pois käytöstä postilla. STR_CONFIG_SETTING_DISTRIBUTION_ARMOURED :Arvokuljetusten jakautuminen: {STRING} -STR_CONFIG_SETTING_DISTRIBUTION_ARMOURED_HELPTEXT :Arvokuljetuksiin kuuluvat arvotavarat lauhkeassa ilmastossa, timantit subtrooppisessa ilmastossa ja kulta pohjoisessa ilmastossa. NewGRF:t voivat kuitenkin muuttaa näitä rahteja. "symmetrinen" tarkoittaa, että suunnilleen sama määrä rahtia lähetetään asemalta A asemalle B kuin asemalta B asemalle A. "epäsymmetrinen" tarkoittaa, että eri suuntiin voidaan lähettää eriävä määrä rahtia. "manuaalinen" tarkoittaa, että automaattinen rahdin jakautuminen on poistettu käytöstä rahdilta. Suositeltavia asetuksia ovat "epäsymmetrinen" tai "manuaalinen" pelattaessa pohjoisessa ilmastossa, sillä pankit eivät lähetä kultaa takaisin kaivoksille. Lauhkeassa ja subtrooppisessa ilmastossa pelatessa voidaan myös valita "symmetrinen", sillä pankit lähettävät arvotavaroita takaisin alkuperäiselle pankille. +STR_CONFIG_SETTING_DISTRIBUTION_ARMOURED_HELPTEXT :Arvokuljetuksiin kuuluvat arvotavarat lauhkeassa ilmastossa, timantit subtrooppisessa ilmastossa ja kulta pohjoisessa ilmastossa. NewGRF:t voivat kuitenkin muuttaa näitä rahteja. ”Symmetrinen” tarkoittaa, että suunnilleen sama määrä rahtia lähetetään asemalta A asemalle B kuin asemalta B asemalle A. ”Epäsymmetrinen” tarkoittaa, että eri suuntiin voidaan lähettää eriävä määrä rahtia. ”Manuaalinen” tarkoittaa, että automaattinen rahdin jakautuminen on poistettu käytöstä rahdilta. Suositeltavia asetuksia ovat ”epäsymmetrinen” tai ”manuaalinen” pelattaessa pohjoisessa ilmastossa, sillä pankit eivät lähetä kultaa takaisin kaivoksille. Lauhkeassa ja subtrooppisessa ilmastossa pelatessa voidaan myös valita ”symmetrinen”, sillä pankit lähettävät arvotavaroita takaisin alkuperäiselle pankille. STR_CONFIG_SETTING_DISTRIBUTION_DEFAULT :Muiden rahtityyppien jakautuminen: {STRING} -STR_CONFIG_SETTING_DISTRIBUTION_DEFAULT_HELPTEXT :"epäsymmetrinen" tarkoittaa, että eri suuntiin voidaan lähettää eriäviä määriä rahtia. "manuaalinen" tarkoittaa, että automaattinen jakautuminen ei ole käytössä rahdille. +STR_CONFIG_SETTING_DISTRIBUTION_DEFAULT_HELPTEXT :”Epäsymmetrinen” tarkoittaa, että eri suuntiin voidaan lähettää eriäviä määriä rahtia. ”Manuaalinen” tarkoittaa, että automaattinen jakautuminen ei ole käytössä rahdille. STR_CONFIG_SETTING_LINKGRAPH_ACCURACY :Jakautumisen tarkkuus: {STRING} STR_CONFIG_SETTING_LINKGRAPH_ACCURACY_HELPTEXT :Mitä suuremmaksi tämä asetus on määritetty, sitä enemmän prosessoriaikaa yhteyskuvaajan laskemiseen kuluu. Mikäli tähän kuluu liian paljon aikaa, saatat havaita pelin nykimistä. Jos arvo on määritetty liian pieneksi, jakauman laskeminen ei ole tarkka ja rahtia ei välttämättä lähetetä odotetuille asemille. STR_CONFIG_SETTING_DEMAND_DISTANCE :Välimatkan vaikutus kysyntään: {STRING} @@ -1777,7 +1777,7 @@ STR_CONFIG_SETTING_INTERFACE :{ORANGE}Käytt STR_CONFIG_SETTING_INTERFACE_GENERAL :{ORANGE}Yleinen STR_CONFIG_SETTING_INTERFACE_VIEWPORTS :{ORANGE}Näkymät STR_CONFIG_SETTING_INTERFACE_CONSTRUCTION :{ORANGE}Rakentaminen -STR_CONFIG_SETTING_ADVISORS :{ORANGE}Uutiset / Neuvonantajat +STR_CONFIG_SETTING_ADVISORS :{ORANGE}Uutiset ja neuvonantajat STR_CONFIG_SETTING_COMPANY :{ORANGE}Yhtiö STR_CONFIG_SETTING_ACCOUNTING :{ORANGE}Talous STR_CONFIG_SETTING_VEHICLES :{ORANGE}Kulkuneuvot @@ -1882,7 +1882,7 @@ STR_QUIT_NO :{BLACK}Ei # Abandon game STR_ABANDON_GAME_CAPTION :{WHITE}Pelin lopetus STR_ABANDON_GAME_QUERY :{YELLOW}Lopetetaanko peli? -STR_ABANDON_SCENARIO_QUERY :{YELLOW}Lopetetaanko skenaario? +STR_ABANDON_SCENARIO_QUERY :{YELLOW}Haluatko varmasti hylätä tämän skenaarion? # Cheat window STR_CHEATS :{WHITE}Huijaukset @@ -2214,7 +2214,7 @@ STR_NETWORK_ERROR_BAD_PLAYER_NAME :{WHITE}Et ole a STR_NETWORK_ERROR_WRONG_REVISION :{WHITE}Tämän asiakkaan versio ei vastaa palvelimen versiota STR_NETWORK_ERROR_WRONG_PASSWORD :{WHITE}Väärä salasana STR_NETWORK_ERROR_SERVER_FULL :{WHITE}Palvelin on täynnä -STR_NETWORK_ERROR_SERVER_BANNED :{WHITE}Sinut on kielletty palvelimelta +STR_NETWORK_ERROR_SERVER_BANNED :{WHITE}Sinut on estetty palvelimelta STR_NETWORK_ERROR_KICKED :{WHITE}Sinut potkittiin pihalle palvelimelta STR_NETWORK_ERROR_KICK_MESSAGE :{WHITE}Syy: {STRING} STR_NETWORK_ERROR_CHEATER :{WHITE}Huijaaminen ei ole sallittua tällä palvelimella @@ -2224,6 +2224,7 @@ STR_NETWORK_ERROR_TIMEOUT_COMPUTER :{WHITE}Tietokon STR_NETWORK_ERROR_TIMEOUT_MAP :{WHITE}Kartan lataus kesti liian kauan STR_NETWORK_ERROR_TIMEOUT_JOIN :{WHITE}Palvelimelle liittyminen kesti liian kauan STR_NETWORK_ERROR_INVALID_CLIENT_NAME :{WHITE}Pelaajanimesi ei kelpaa +STR_NETWORK_ERROR_SERVER_TOO_OLD :{WHITE}Kysytty palvelin on liian vanha tälle asiakkaalle ############ Leave those lines in this order!! STR_NETWORK_ERROR_CLIENT_GENERAL :yleinen virhe @@ -2807,7 +2808,7 @@ STR_LAI_OBJECT_DESCRIPTION_COMPANY_OWNED_LAND :Yhtiön omistam STR_ABOUT_OPENTTD :{WHITE}Tietoja OpenTTD:stä STR_ABOUT_ORIGINAL_COPYRIGHT :{BLACK}Alkuperäiset oikeudet {COPYRIGHT} 1995 Chris Sawyer, kaikki oikeudet pidätetään STR_ABOUT_VERSION :{BLACK}OpenTTD-versio {REV} -STR_ABOUT_COPYRIGHT_OPENTTD :{BLACK}OpenTTD {COPYRIGHT} 2002-{STRING} The OpenTTD team +STR_ABOUT_COPYRIGHT_OPENTTD :{BLACK}OpenTTD {COPYRIGHT} 2002–{STRING} The OpenTTD team # Framerate display window STR_FRAMERATE_CAPTION :{WHITE}Kuvataajuus @@ -3513,8 +3514,8 @@ STR_INDUSTRY_VIEW_REQUIRES :{BLACK}Tarvitse STR_INDUSTRY_VIEW_ACCEPT_CARGO :{YELLOW}{STRING}{BLACK}{3:STRING} STR_INDUSTRY_VIEW_ACCEPT_CARGO_AMOUNT :{YELLOW}{STRING}{BLACK}: {CARGO_SHORT} odottamassa{STRING} -STR_CONFIG_GAME_PRODUCTION :{WHITE}Muokkaa tuotantoa (8:n kerroin, 2040 asti) -STR_CONFIG_GAME_PRODUCTION_LEVEL :{WHITE}Muuta tuotantotasoa (prosentteina, 800{NBSP}% asti) +STR_CONFIG_GAME_PRODUCTION :{WHITE}Muokkaa tuotantoa (8:n kerroin, 2040:een asti) +STR_CONFIG_GAME_PRODUCTION_LEVEL :{WHITE}Muuta tuotantotasoa (prosentteina, 800{NBSP}%:iin asti) # Vehicle lists STR_VEHICLE_LIST_TRAIN_CAPTION :{WHITE}{STRING} – {COMMA} juna{P "" a} diff --git a/src/lang/hungarian.txt b/src/lang/hungarian.txt index b173828d6a..a780368cad 100644 --- a/src/lang/hungarian.txt +++ b/src/lang/hungarian.txt @@ -1023,7 +1023,7 @@ STR_GAME_OPTIONS_CURRENCY_MYR :Maláj ringgit STR_GAME_OPTIONS_ROAD_VEHICLES_DROPDOWN_LEFT :Balra hajtás STR_GAME_OPTIONS_ROAD_VEHICLES_DROPDOWN_RIGHT :Jobbra hajtás -STR_GAME_OPTIONS_TOWN_NAMES_FRAME :{BLACK}Városnevek +STR_GAME_OPTIONS_TOWN_NAMES_FRAME :{BLACK}Városnevek: STR_GAME_OPTIONS_TOWN_NAMES_DROPDOWN_TOOLTIP :{BLACK}A városnevek stílusának kiválasztása ############ start of townname region @@ -1063,6 +1063,7 @@ STR_GAME_OPTIONS_AUTOSAVE_DROPDOWN_EVERY_12_MONTHS :12 havonta STR_GAME_OPTIONS_LANGUAGE :{BLACK}Nyelv STR_GAME_OPTIONS_LANGUAGE_TOOLTIP :{BLACK}Válassz nyelvet +STR_GAME_OPTIONS_LANGUAGE_PERCENTAGE :{STRING} ({NUM}% teljesítve) STR_GAME_OPTIONS_FULLSCREEN :{BLACK}Teljes képernyő STR_GAME_OPTIONS_FULLSCREEN_TOOLTIP :{BLACK}Jelöld be ezt, ha teljes képernyős módban szeretnél játszani az OpenTTD-vel @@ -2060,6 +2061,8 @@ 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 STR_NETWORK_SERVER_LIST_CAPTION :{WHITE}Hálózati játék @@ -2123,6 +2126,8 @@ STR_NETWORK_START_SERVER_NEW_GAME_NAME_TOOLTIP :{BLACK}A játé STR_NETWORK_START_SERVER_SET_PASSWORD :{BLACK}Jelszó beállítása STR_NETWORK_START_SERVER_PASSWORD_TOOLTIP :{BLACK}Védd le a játékodat jelszóval, ha nem akarod hogy illetéktelenek csatlakozzanak +STR_NETWORK_START_SERVER_VISIBILITY_LABEL :{BLACK}Láthatóság +STR_NETWORK_START_SERVER_VISIBILITY_TOOLTIP :{BLACK}Mások láthassák-e a szerveredet a publikus listában STR_NETWORK_START_SERVER_CLIENTS_SELECT :{BLACK}{NUM} kliens STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS :{BLACK}Játékosok max. száma: STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS_TOOLTIP :{BLACK}A maximálisan felcsatlakozható kliensek számának kiválasztása. Nem szükséges pont ennyi embernek éppen kapcsolódva lennie @@ -2186,12 +2191,45 @@ STR_NETWORK_NEED_GAME_PASSWORD_CAPTION :{WHITE}A szerve STR_NETWORK_NEED_COMPANY_PASSWORD_CAPTION :{WHITE}A vállalat jelszóval van védve. Írd be # Network company list added strings -STR_NETWORK_COMPANY_LIST_CLIENT_LIST :Kliens lista +STR_NETWORK_COMPANY_LIST_CLIENT_LIST :Online játékosok STR_NETWORK_COMPANY_LIST_SPECTATE :Megfigyelés # Network client list - - +STR_NETWORK_CLIENT_LIST_CAPTION :{WHITE}Többjátékos +STR_NETWORK_CLIENT_LIST_SERVER :{BLACK}Szerver +STR_NETWORK_CLIENT_LIST_SERVER_NAME :{BLACK}Név +STR_NETWORK_CLIENT_LIST_SERVER_NAME_TOOLTIP :{BLACK}A szerver neve amin játszol +STR_NETWORK_CLIENT_LIST_SERVER_NAME_EDIT_TOOLTIP :{BLACK}A szervered nevének szerkesztése +STR_NETWORK_CLIENT_LIST_SERVER_NAME_QUERY_CAPTION :Szerver neve +STR_NETWORK_CLIENT_LIST_SERVER_VISIBILITY :{BLACK}Láthatóság +STR_NETWORK_CLIENT_LIST_SERVER_VISIBILITY_TOOLTIP :{BLACK}Mások láthassák-e a szerveredet a publikus listában +STR_NETWORK_CLIENT_LIST_PLAYER :{BLACK}Játékos +STR_NETWORK_CLIENT_LIST_PLAYER_NAME :{BLACK}Név +STR_NETWORK_CLIENT_LIST_PLAYER_NAME_TOOLTIP :{BLACK}A játékos neved +STR_NETWORK_CLIENT_LIST_PLAYER_NAME_EDIT_TOOLTIP :{BLACK}A játékos neved szerkesztése +STR_NETWORK_CLIENT_LIST_PLAYER_NAME_QUERY_CAPTION :Játékos neve +STR_NETWORK_CLIENT_LIST_ADMIN_CLIENT_TOOLTIP :{BLACK}Adminisztrátori műveletek ehhez a vállalathoz +STR_NETWORK_CLIENT_LIST_ADMIN_COMPANY_TOOLTIP :{BLACK}Adminisztrátori műveletek ehhez a vállalathoz +STR_NETWORK_CLIENT_LIST_JOIN_TOOLTIP :Csatlakozz ehhez a vállalathoz +STR_NETWORK_CLIENT_LIST_CHAT_CLIENT_TOOLTIP :{BLACK}Üzenet küldése ennek a játékosnak +STR_NETWORK_CLIENT_LIST_CHAT_COMPANY_TOOLTIP :{BLACK}Üzenet küldése az összes játékosnak ebben a vállalatban +STR_NETWORK_CLIENT_LIST_CHAT_SPECTATOR_TOOLTIP :{BLACK}Üzenet küldése az összes megfigyelőnek +STR_NETWORK_CLIENT_LIST_SPECTATORS :Megfigyelők +STR_NETWORK_CLIENT_LIST_NEW_COMPANY :(Új vállalat) +STR_NETWORK_CLIENT_LIST_NEW_COMPANY_TOOLTIP :{BLACK}Hozz létre egy új vállalatot és csatlakozz +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 + +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 +STR_NETWORK_CLIENT_LIST_ADMIN_COMPANY_UNLOCK :Jelszó feloldása + +STR_NETWORK_CLIENT_LIST_ASK_CAPTION :{WHITE}Adminisztrátori művelet +STR_NETWORK_CLIENT_LIST_ASK_CLIENT_KICK :{YELLOW}Biztos ki akarod rúgni ezt a játékost: '{STRING}'? +STR_NETWORK_CLIENT_LIST_ASK_CLIENT_BAN :{YELLOW}Biztos ki akarod tiltani ezt a játékost: '{STRING}'? +STR_NETWORK_CLIENT_LIST_ASK_COMPANY_RESET :{YELLOW}Biztos ki akarod törölni a '{COMPANY}' vállalatot? +STR_NETWORK_CLIENT_LIST_ASK_COMPANY_UNLOCK :{YELLOW}Biztos vissza akarod állítani a '{COMPANY}' vállalat jelszavát? STR_NETWORK_SERVER :Szerver STR_NETWORK_CLIENT :Kliens @@ -2236,6 +2274,7 @@ STR_NETWORK_ERROR_SERVER_START :{WHITE}Nem tudt STR_NETWORK_ERROR_CLIENT_START :{WHITE}Nem tudtam kapcsolódni STR_NETWORK_ERROR_TIMEOUT :{WHITE}A(z) {NUM}. játékos kapcsolata elveszett STR_NETWORK_ERROR_SERVER_ERROR :{WHITE}Protokoll-hiba keletkezett és megszakadt a kapcsolat +STR_NETWORK_ERROR_BAD_PLAYER_NAME :{WHITE}A játékos neved nem lett megadva. A nevet a többjátékos ablak tetején tudod beállítani. STR_NETWORK_ERROR_WRONG_REVISION :{WHITE}A gépeden és a szerveren lévő programnak nem egyezik meg a verziója STR_NETWORK_ERROR_WRONG_PASSWORD :{WHITE}Rossz jelszó STR_NETWORK_ERROR_SERVER_FULL :{WHITE}A szerver tele van @@ -2248,6 +2287,8 @@ STR_NETWORK_ERROR_TIMEOUT_PASSWORD :{WHITE}Túl sok STR_NETWORK_ERROR_TIMEOUT_COMPUTER :{WHITE}A számítógéped túl lassú, hogy tartsa a szerver sebességét STR_NETWORK_ERROR_TIMEOUT_MAP :{WHITE}Túl sokáig tartott a térkép letöltése a számítógépednek STR_NETWORK_ERROR_TIMEOUT_JOIN :{WHITE}Túl sokáig tartott a szerverhez való csatlakozása a számítógépednek +STR_NETWORK_ERROR_INVALID_CLIENT_NAME :{WHITE}A játékos neved nem megfelelő +STR_NETWORK_ERROR_SERVER_TOO_OLD :{WHITE}A lekérdezett szerver túl régi ehhez a klienshez ############ Leave those lines in this order!! STR_NETWORK_ERROR_CLIENT_GENERAL :általános hiba @@ -2270,6 +2311,7 @@ STR_NETWORK_ERROR_CLIENT_TIMEOUT_PASSWORD :nem érkezett j STR_NETWORK_ERROR_CLIENT_TIMEOUT_COMPUTER :általános időtúllépés STR_NETWORK_ERROR_CLIENT_TIMEOUT_MAP :térkép letöltés túl sokáig tartott STR_NETWORK_ERROR_CLIENT_TIMEOUT_JOIN :térkép feldolgozás túl sokáig tartott +STR_NETWORK_ERROR_CLIENT_INVALID_CLIENT_NAME :Érvénytelen kliens név ############ End of leave-in-this-order STR_NETWORK_ERROR_CLIENT_GUI_LOST_CONNECTION_CAPTION :{WHITE}Lehetséges, hogy elveszett a kapcsolat @@ -3113,6 +3155,7 @@ STR_NEWGRF_ERROR_MSG_WARNING :{RED}Figyelmezt STR_NEWGRF_ERROR_MSG_ERROR :{RED}Hiba: {SILVER}{STRING} STR_NEWGRF_ERROR_MSG_FATAL :{RED}Végzetes hiba: {SILVER}{STRING} STR_NEWGRF_ERROR_FATAL_POPUP :{WHITE}Egy végzetes NewGRF hiba történt:{}{STRING} +STR_NEWGRF_ERROR_POPUP :{WHITE}NewGRF hiba történt:{}{STRING} STR_NEWGRF_ERROR_VERSION_NUMBER :{1:STRING} nem fog működni az OpenTTD által jelentett TTDPatch verzióval STR_NEWGRF_ERROR_DOS_OR_WINDOWS :{1:STRING} a TTD {STRING} verziójához van STR_NEWGRF_ERROR_UNSET_SWITCH :{1:STRING} úgy lett tervezve, hogy együtt lesz használva ezzel: {STRING} diff --git a/src/lang/korean.txt b/src/lang/korean.txt index ea0d5d4cf6..1032327555 100644 --- a/src/lang/korean.txt +++ b/src/lang/korean.txt @@ -409,7 +409,7 @@ STR_SETTINGS_MENU_SIGNS_DISPLAYED :팻말을 표 STR_SETTINGS_MENU_SHOW_COMPETITOR_SIGNS :경쟁사의 팻말과 역 이름을 표시 STR_SETTINGS_MENU_FULL_ANIMATION :완전한 애니메이션 STR_SETTINGS_MENU_FULL_DETAIL :그래픽을 아주 상세하게 -STR_SETTINGS_MENU_TRANSPARENT_BUILDINGS :건물 숨기기 +STR_SETTINGS_MENU_TRANSPARENT_BUILDINGS :건물 감추기 STR_SETTINGS_MENU_TRANSPARENT_SIGNS :역명판 감추기 STR_SETTINGS_MENU_MONEY_TEXT_EFFECTS :수입/지출 표시 ############ range ends here diff --git a/src/lang/portuguese.txt b/src/lang/portuguese.txt index e8e3d3a9a2..330b6b1c48 100644 --- a/src/lang/portuguese.txt +++ b/src/lang/portuguese.txt @@ -2277,7 +2277,7 @@ STR_NETWORK_MESSAGE_CLIENT_COMPANY_JOIN :*** {STRING} en STR_NETWORK_MESSAGE_CLIENT_COMPANY_SPECTATE :*** {STRING} entrou como espectador STR_NETWORK_MESSAGE_CLIENT_COMPANY_NEW :*** {STRING} iniciou uma nova empresa (#{2:NUM}) STR_NETWORK_MESSAGE_CLIENT_LEFT :*** {STRING} deixou o jogo ({2:STRING}) -STR_NETWORK_MESSAGE_NAME_CHANGE :*** {STRING} mudou o seu nome para {STRING} +STR_NETWORK_MESSAGE_NAME_CHANGE :*** {STRING} mudou o nome para {STRING} STR_NETWORK_MESSAGE_GIVE_MONEY :*** {STRING} deu {2:CURRENCY_LONG} a {1:STRING} STR_NETWORK_MESSAGE_SERVER_SHUTDOWN :{WHITE}O servidor fechou a sessão STR_NETWORK_MESSAGE_SERVER_REBOOT :{WHITE}O servidor está a reiniciar...{}Por favor espere... diff --git a/src/lang/russian.txt b/src/lang/russian.txt index 56aaa95642..80fdb5bb5d 100644 --- a/src/lang/russian.txt +++ b/src/lang/russian.txt @@ -2430,7 +2430,7 @@ STR_NETWORK_MESSAGE_CLIENT_COMPANY_JOIN :*** {STRING} п STR_NETWORK_MESSAGE_CLIENT_COMPANY_SPECTATE :*** {STRING} подключился в качестве зрителя STR_NETWORK_MESSAGE_CLIENT_COMPANY_NEW :*** {STRING} основал новую компанию (#{2:NUM}) STR_NETWORK_MESSAGE_CLIENT_LEFT :*** {STRING} покинул игру ({2:STRING}) -STR_NETWORK_MESSAGE_NAME_CHANGE :*** {STRING} изменил имя на {STRING} +STR_NETWORK_MESSAGE_NAME_CHANGE :*** {STRING} сменил(а) имя на {STRING} STR_NETWORK_MESSAGE_GIVE_MONEY :*** Компания «{STRING}» передала «{STRING}» {CURRENCY_LONG} STR_NETWORK_MESSAGE_SERVER_SHUTDOWN :{WHITE}Сервер закрыл сессию STR_NETWORK_MESSAGE_SERVER_REBOOT :{WHITE}Сервер перезапускается...{}Пожалуйста, подождите... diff --git a/src/lang/spanish.txt b/src/lang/spanish.txt index b3dad8b805..b58e2a55e4 100644 --- a/src/lang/spanish.txt +++ b/src/lang/spanish.txt @@ -2225,6 +2225,7 @@ STR_NETWORK_ERROR_TIMEOUT_COMPUTER :{WHITE}Su orden STR_NETWORK_ERROR_TIMEOUT_MAP :{WHITE}Su ordenador necesitó demasiado tiempo para descargar el mapa STR_NETWORK_ERROR_TIMEOUT_JOIN :{WHITE}Su ordenador necesitó demasiado tiempo para conectar al servidor STR_NETWORK_ERROR_INVALID_CLIENT_NAME :{WHITE}Tu nombre de jugador no es válido +STR_NETWORK_ERROR_SERVER_TOO_OLD :{WHITE}El servidor es demasiado antiguo para este cliente. ############ Leave those lines in this order!! STR_NETWORK_ERROR_CLIENT_GENERAL :error general diff --git a/src/music_gui.cpp b/src/music_gui.cpp index 805589f5e2..80d55eb4ba 100644 --- a/src/music_gui.cpp +++ b/src/music_gui.cpp @@ -462,7 +462,7 @@ struct MusicTrackSelectionWindow : public Window { SetDParam(0, STR_MUSIC_PLAYLIST_ALL + _settings_client.music.playlist); break; case WID_MTS_CAPTION: - SetDParamStr(0, BaseMusic::GetUsedSet()->name.c_str()); + SetDParamStr(0, BaseMusic::GetUsedSet()->name); break; } } diff --git a/src/network/core/address.cpp b/src/network/core/address.cpp index 3b0e76edd9..ab5e18daaf 100644 --- a/src/network/core/address.cpp +++ b/src/network/core/address.cpp @@ -23,11 +23,13 @@ static const int DEFAULT_CONNECT_TIMEOUT_SECONDS = 3; ///< Allow connect() three */ const char *NetworkAddress::GetHostname() { - if (StrEmpty(this->hostname) && this->address.ss_family != AF_UNSPEC) { + if (this->hostname.empty() && this->address.ss_family != AF_UNSPEC) { assert(this->address_length != 0); - getnameinfo((struct sockaddr *)&this->address, this->address_length, this->hostname, sizeof(this->hostname), nullptr, 0, NI_NUMERICHOST); + char buffer[NETWORK_HOSTNAME_LENGTH]; + getnameinfo((struct sockaddr *)&this->address, this->address_length, buffer, sizeof(buffer), nullptr, 0, NI_NUMERICHOST); + this->hostname = buffer; } - return this->hostname; + return this->hostname.c_str(); } /** @@ -244,32 +246,32 @@ SOCKET NetworkAddress::Resolve(int family, int socktype, int flags, SocketList * /* Setting both hostname to nullptr and port to 0 is not allowed. * As port 0 means bind to any port, the other must mean that * we want to bind to 'all' IPs. */ - if (StrEmpty(this->hostname) && this->address_length == 0 && this->GetPort() == 0) { + if (this->hostname.empty() && this->address_length == 0 && this->GetPort() == 0) { reset_hostname = true; int fam = this->address.ss_family; if (fam == AF_UNSPEC) fam = family; - strecpy(this->hostname, fam == AF_INET ? "0.0.0.0" : "::", lastof(this->hostname)); + this->hostname = fam == AF_INET ? "0.0.0.0" : "::"; } static bool _resolve_timeout_error_message_shown = false; auto start = std::chrono::steady_clock::now(); - int e = getaddrinfo(StrEmpty(this->hostname) ? nullptr : this->hostname, port_name, &hints, &ai); + int e = getaddrinfo(this->hostname.empty() ? nullptr : this->hostname.c_str(), port_name, &hints, &ai); auto end = std::chrono::steady_clock::now(); std::chrono::seconds duration = std::chrono::duration_cast(end - start); if (!_resolve_timeout_error_message_shown && duration >= std::chrono::seconds(5)) { DEBUG(net, 0, "getaddrinfo for hostname \"%s\", port %s, address family %s and socket type %s took %i seconds", - this->hostname, port_name, AddressFamilyAsString(family), SocketTypeAsString(socktype), (int)duration.count()); + this->hostname.c_str(), port_name, AddressFamilyAsString(family), SocketTypeAsString(socktype), (int)duration.count()); DEBUG(net, 0, " this is likely an issue in the DNS name resolver's configuration causing it to time out"); _resolve_timeout_error_message_shown = true; } - if (reset_hostname) strecpy(this->hostname, "", lastof(this->hostname)); + if (reset_hostname) this->hostname.clear(); if (e != 0) { if (func != ResolveLoopProc) { DEBUG(net, 0, "getaddrinfo for hostname \"%s\", port %s, address family %s and socket type %s failed: %s", - this->hostname, port_name, AddressFamilyAsString(family), SocketTypeAsString(socktype), FS2OTTD(gai_strerror(e)).c_str()); + this->hostname.c_str(), port_name, AddressFamilyAsString(family), SocketTypeAsString(socktype), FS2OTTD(gai_strerror(e)).c_str()); } return INVALID_SOCKET; } @@ -452,11 +454,11 @@ void NetworkAddress::Listen(int socktype, SocketList *sockets) { assert(sockets != nullptr); - /* Setting both hostname to nullptr and port to 0 is not allowed. + /* Setting both hostname to "" and port to 0 is not allowed. * As port 0 means bind to any port, the other must mean that * we want to bind to 'all' IPs. */ if (this->address_length == 0 && this->address.ss_family == AF_UNSPEC && - StrEmpty(this->hostname) && this->GetPort() == 0) { + this->hostname.empty() && this->GetPort() == 0) { this->Resolve(AF_INET, socktype, AI_ADDRCONFIG | AI_PASSIVE, sockets, ListenLoopProc); this->Resolve(AF_INET6, socktype, AI_ADDRCONFIG | AI_PASSIVE, sockets, ListenLoopProc); } else { diff --git a/src/network/core/address.h b/src/network/core/address.h index 622566fe06..2925206448 100644 --- a/src/network/core/address.h +++ b/src/network/core/address.h @@ -28,10 +28,10 @@ typedef SmallMap SocketList; ///< Type for a mapping */ class NetworkAddress { private: - char hostname[NETWORK_HOSTNAME_LENGTH]; ///< The hostname - int address_length; ///< The length of the resolved address - sockaddr_storage address; ///< The resolved address - bool resolved; ///< Whether the address has been (tried to be) resolved + std::string hostname; ///< The hostname + int address_length; ///< The length of the resolved address + sockaddr_storage address; ///< The resolved address + bool resolved; ///< Whether the address has been (tried to be) resolved /** * Helper function to resolve something to a socket. @@ -52,7 +52,6 @@ public: address(address), resolved(address_length != 0) { - *this->hostname = '\0'; } /** @@ -64,7 +63,6 @@ public: address_length(address_length), resolved(address_length != 0) { - *this->hostname = '\0'; memset(&this->address, 0, sizeof(this->address)); memcpy(&this->address, address, address_length); } @@ -75,16 +73,15 @@ public: * @param port the port * @param family the address family */ - NetworkAddress(const char *hostname = "", uint16 port = 0, int family = AF_UNSPEC) : + NetworkAddress(std::string_view hostname = "", uint16 port = 0, int family = AF_UNSPEC) : address_length(0), resolved(false) { - /* Also handle IPv6 bracket enclosed hostnames */ - if (StrEmpty(hostname)) hostname = ""; - if (*hostname == '[') hostname++; - strecpy(this->hostname, StrEmpty(hostname) ? "" : hostname, lastof(this->hostname)); - char *tmp = strrchr(this->hostname, ']'); - if (tmp != nullptr) *tmp = '\0'; + if (!hostname.empty() && hostname.front() == '[' && hostname.back() == ']') { + hostname.remove_prefix(1); + hostname.remove_suffix(1); + } + this->hostname = hostname; memset(&this->address, 0, sizeof(this->address)); this->address.ss_family = family; diff --git a/src/network/core/game_info.cpp b/src/network/core/game_info.cpp index 815c281c31..cd6fb50017 100644 --- a/src/network/core/game_info.cpp +++ b/src/network/core/game_info.cpp @@ -113,7 +113,7 @@ bool IsNetworkCompatibleVersion(const char *other, bool extended) void CheckGameCompatibility(NetworkGameInfo &ngi, bool extended) { /* Check if we are allowed on this server based on the revision-check. */ - ngi.version_compatible = IsNetworkCompatibleVersion(ngi.server_revision, extended); + ngi.version_compatible = IsNetworkCompatibleVersion(ngi.server_revision.c_str(), extended); ngi.compatible = ngi.version_compatible; /* Check if we have all the GRFs on the client-system too. */ @@ -123,31 +123,37 @@ void CheckGameCompatibility(NetworkGameInfo &ngi, bool extended) } /** - * Fill a NetworkGameInfo structure with the latest information of the server. - * @param ngi the NetworkGameInfo struct to fill with data. + * Fill a NetworkServerGameInfo structure with the static content, or things + * that are so static they can be updated on request from a settings change. */ -void FillNetworkGameInfo(NetworkGameInfo &ngi) +void FillStaticNetworkServerGameInfo() { - /* Update some game_info */ - ngi.clients_on = _network_game_info.clients_on; - ngi.start_date = ConvertYMDToDate(_settings_game.game_creation.starting_year, 0, 1); - - ngi.use_password = !StrEmpty(_settings_client.network.server_password); - ngi.clients_max = _settings_client.network.max_clients; - ngi.companies_on = (byte)Company::GetNumItems(); - ngi.companies_max = _settings_client.network.max_companies; - ngi.spectators_on = NetworkSpectatorCount(); - ngi.spectators_max = _settings_client.network.max_spectators; - ngi.game_date = _date; - ngi.map_width = MapSizeX(); - ngi.map_height = MapSizeY(); - ngi.map_set = _settings_game.game_creation.landscape; - ngi.dedicated = _network_dedicated; - ngi.grfconfig = _grfconfig; - - strecpy(ngi.server_name, _settings_client.network.server_name, lastof(ngi.server_name)); - strecpy(ngi.server_revision, GetNetworkRevisionString(), lastof(ngi.server_revision)); - strecpy(ngi.short_server_revision, _openttd_revision, lastof(ngi.short_server_revision)); + _network_game_info.use_password = !StrEmpty(_settings_client.network.server_password); + _network_game_info.start_date = ConvertYMDToDate(_settings_game.game_creation.starting_year, 0, 1); + _network_game_info.clients_max = _settings_client.network.max_clients; + _network_game_info.companies_max = _settings_client.network.max_companies; + _network_game_info.spectators_max = _settings_client.network.max_spectators; + _network_game_info.map_width = MapSizeX(); + _network_game_info.map_height = MapSizeY(); + _network_game_info.landscape = _settings_game.game_creation.landscape; + _network_game_info.dedicated = _network_dedicated; + _network_game_info.grfconfig = _grfconfig; + + _network_game_info.server_name = _settings_client.network.server_name; + _network_game_info.server_revision = GetNetworkRevisionString(); +} + +/** + * Get the NetworkServerGameInfo structure with the latest information of the server. + * @return The current NetworkServerGameInfo. + */ +const NetworkServerGameInfo *GetCurrentNetworkServerGameInfo() +{ + /* Client_on is used as global variable to keep track on the number of clients. */ + _network_game_info.companies_on = (byte)Company::GetNumItems(); + _network_game_info.spectators_on = NetworkSpectatorCount(); + _network_game_info.game_date = _date; + return &_network_game_info; } /** @@ -181,7 +187,7 @@ static void HandleIncomingNetworkGameInfoGRFConfig(GRFConfig *config) * @param p the packet to write the data to. * @param info the NetworkGameInfo struct to serialize from. */ -void SerializeNetworkGameInfo(Packet *p, const NetworkGameInfo *info) +void SerializeNetworkGameInfo(Packet *p, const NetworkServerGameInfo *info) { p->Send_uint8 (NETWORK_GAME_INFO_VERSION); @@ -247,7 +253,7 @@ void SerializeNetworkGameInfo(Packet *p, const NetworkGameInfo *info) p->Send_string(""); // Used to be map-name. p->Send_uint16(info->map_width); p->Send_uint16(info->map_height); - p->Send_uint8 (info->map_set); + p->Send_uint8 (info->landscape); p->Send_bool (info->dedicated); } @@ -256,7 +262,7 @@ void SerializeNetworkGameInfo(Packet *p, const NetworkGameInfo *info) * @param p the packet to write the data to * @param info the NetworkGameInfo struct to serialize */ -void SerializeNetworkGameInfoExtended(Packet *p, const NetworkGameInfo *info, uint16 flags, uint16 version) +void SerializeNetworkGameInfoExtended(Packet *p, const NetworkServerGameInfo *info, uint16 flags, uint16 version) { p->Send_uint8(0); // version num @@ -275,7 +281,7 @@ void SerializeNetworkGameInfoExtended(Packet *p, const NetworkGameInfo *info, ui p->Send_string(""); // Used to be map-name. p->Send_uint32(info->map_width); p->Send_uint32(info->map_height); - p->Send_uint8 (info->map_set); + p->Send_uint8 (info->landscape); p->Send_bool (info->dedicated); { @@ -310,7 +316,7 @@ void DeserializeNetworkGameInfo(Packet *p, NetworkGameInfo *info) { static const Date MAX_DATE = ConvertYMDToDate(MAX_YEAR, 11, 31); // December is month 11 - info->game_info_version = p->Recv_uint8(); + byte game_info_version = p->Recv_uint8(); /* * Please observe the order. @@ -320,7 +326,7 @@ void DeserializeNetworkGameInfo(Packet *p, NetworkGameInfo *info) /* Update the documentation in game_info.h on changes * to the NetworkGameInfo wire-protocol! */ - switch (info->game_info_version) { + switch (game_info_version) { case 4: { GRFConfig **dst = &info->grfconfig; uint i; @@ -353,24 +359,24 @@ void DeserializeNetworkGameInfo(Packet *p, NetworkGameInfo *info) FALLTHROUGH; case 1: - p->Recv_string(info->server_name, sizeof(info->server_name)); - p->Recv_string(info->server_revision, sizeof(info->server_revision)); + info->server_name = p->Recv_string(NETWORK_NAME_LENGTH); + info->server_revision = p->Recv_string(NETWORK_REVISION_LENGTH); p->Recv_uint8 (); // Used to contain server-lang. info->use_password = p->Recv_bool (); info->clients_max = p->Recv_uint8 (); info->clients_on = p->Recv_uint8 (); info->spectators_on = p->Recv_uint8 (); - if (info->game_info_version < 3) { // 16 bits dates got scrapped and are read earlier + if (game_info_version < 3) { // 16 bits dates got scrapped and are read earlier info->game_date = p->Recv_uint16() + DAYS_TILL_ORIGINAL_BASE_YEAR; info->start_date = p->Recv_uint16() + DAYS_TILL_ORIGINAL_BASE_YEAR; } while (p->Recv_uint8() != 0) {} // Used to contain the map-name. info->map_width = p->Recv_uint16(); info->map_height = p->Recv_uint16(); - info->map_set = p->Recv_uint8 (); + info->landscape = p->Recv_uint8 (); info->dedicated = p->Recv_bool (); - if (info->map_set >= NETWORK_NUM_LANDSCAPES) info->map_set = 0; + if (info->landscape >= NETWORK_NUM_LANDSCAPES) info->landscape = 0; } } @@ -386,15 +392,13 @@ void DeserializeNetworkGameInfoExtended(Packet *p, NetworkGameInfo *info) const uint8 version = p->Recv_uint8(); if (version > 0) return; // Unknown version - info->game_info_version = 255; - info->game_date = Clamp(p->Recv_uint32(), 0, MAX_DATE); info->start_date = Clamp(p->Recv_uint32(), 0, MAX_DATE); info->companies_max = p->Recv_uint8 (); info->companies_on = p->Recv_uint8 (); info->spectators_max = p->Recv_uint8 (); - p->Recv_string(info->server_name, sizeof(info->server_name)); - p->Recv_string(info->server_revision, sizeof(info->server_revision)); + info->server_name = p->Recv_string(NETWORK_NAME_LENGTH); + info->server_revision = p->Recv_string(NETWORK_LONG_REVISION_LENGTH); p->Recv_uint8 (); // Used to contain server-lang. info->use_password = p->Recv_bool (); info->clients_max = p->Recv_uint8 (); @@ -403,7 +407,7 @@ void DeserializeNetworkGameInfoExtended(Packet *p, NetworkGameInfo *info) while (p->Recv_uint8() != 0) {} // Used to contain the map-name. info->map_width = p->Recv_uint32(); info->map_height = p->Recv_uint32(); - info->map_set = p->Recv_uint8 (); + info->landscape = p->Recv_uint8 (); info->dedicated = p->Recv_bool (); { @@ -425,7 +429,7 @@ void DeserializeNetworkGameInfoExtended(Packet *p, NetworkGameInfo *info) } } - if (info->map_set >= NETWORK_NUM_LANDSCAPES) info->map_set = 0; + if (info->landscape >= NETWORK_NUM_LANDSCAPES) info->landscape = 0; } /** diff --git a/src/network/core/game_info.h b/src/network/core/game_info.h index 988835e3c5..4e5de52169 100644 --- a/src/network/core/game_info.h +++ b/src/network/core/game_info.h @@ -57,36 +57,34 @@ */ /** - * The game information that is not generated on-the-fly and has to - * be sent to the clients. + * The game information that is sent from the server to the client. */ struct NetworkServerGameInfo { - byte clients_on; ///< Current count of clients on server + GRFConfig *grfconfig; ///< List of NewGRF files used + Date start_date; ///< When the game started + Date game_date; ///< Current date + uint32 map_width; ///< Map width + uint32 map_height; ///< Map height + std::string server_name; ///< Server name + std::string server_revision; ///< The version number the server is using (e.g.: 'r304' or 0.5.0) + bool dedicated; ///< Is this a dedicated server? + bool use_password; ///< Is this server passworded? + byte clients_on; ///< Current count of clients on server + byte clients_max; ///< Max clients allowed on server + byte companies_on; ///< How many started companies do we have + byte companies_max; ///< Max companies allowed on server + byte spectators_on; ///< How many spectators do we have? + byte spectators_max; ///< Max spectators allowed on server + byte landscape; ///< The used landscape }; /** - * The game information that is sent from the server to the clients. + * The game information that is sent from the server to the clients + * with extra information only required at the client side. */ struct NetworkGameInfo : NetworkServerGameInfo { - GRFConfig *grfconfig; ///< List of NewGRF files used - Date start_date; ///< When the game started - Date game_date; ///< Current date - uint32 map_width; ///< Map width - uint32 map_height; ///< Map height - char server_name[NETWORK_NAME_LENGTH]; ///< Server name - char short_server_revision[NETWORK_REVISION_LENGTH]; ///< The version number the server is using (e.g.: 'r304' or 0.5.0) (truncated) - char server_revision[NETWORK_LONG_REVISION_LENGTH]; ///< The version number the server is using (e.g.: 'r304' or 0.5.0) - bool dedicated; ///< Is this a dedicated server? bool version_compatible; ///< Can we connect to this server or not? (based on server_revision) bool compatible; ///< Can we connect to this server or not? (based on server_revision _and_ grf_match - bool use_password; ///< Is this server passworded? - byte game_info_version; ///< Version of the game info - byte clients_max; ///< Max clients allowed on server - byte companies_on; ///< How many started companies do we have - byte companies_max; ///< Max companies allowed on server - byte spectators_on; ///< How many spectators do we have? - byte spectators_max; ///< Max spectators allowed on server - byte map_set; ///< Graphical set }; extern NetworkServerGameInfo _network_game_info; @@ -95,14 +93,15 @@ const char *GetNetworkRevisionString(); bool IsNetworkCompatibleVersion(const char *other, bool extended = false); void CheckGameCompatibility(NetworkGameInfo &ngi, bool extended = false); -void FillNetworkGameInfo(NetworkGameInfo &ngi); +void FillStaticNetworkServerGameInfo(); +const NetworkServerGameInfo *GetCurrentNetworkServerGameInfo(); void DeserializeGRFIdentifier(Packet *p, GRFIdentifier *grf); void SerializeGRFIdentifier(Packet *p, const GRFIdentifier *grf); void DeserializeNetworkGameInfo(Packet *p, NetworkGameInfo *info); void DeserializeNetworkGameInfoExtended(Packet *p, NetworkGameInfo *info); -void SerializeNetworkGameInfo(Packet *p, const NetworkGameInfo *info); -void SerializeNetworkGameInfoExtended(Packet *p, const NetworkGameInfo *info, uint16 flags, uint16 version); +void SerializeNetworkGameInfo(Packet *p, const NetworkServerGameInfo *info); +void SerializeNetworkGameInfoExtended(Packet *p, const NetworkServerGameInfo *info, uint16 flags, uint16 version); #endif /* NETWORK_CORE_GAME_INFO_H */ diff --git a/src/network/core/packet.cpp b/src/network/core/packet.cpp index c3016f6a0f..05e9316fc0 100644 --- a/src/network/core/packet.cpp +++ b/src/network/core/packet.cpp @@ -168,12 +168,11 @@ void Packet::Send_uint64(uint64 data) * the string + '\0'. No size-byte or something. * @param data The string to send */ -void Packet::Send_string(const char *data) +void Packet::Send_string(const std::string_view data) { - assert(data != nullptr); - /* Length of the string + 1 for the '\0' termination. */ - assert(this->CanWriteToPacket(strlen(data) + 1)); - while (this->buffer.emplace_back(*data++) != '\0') {} + assert(this->CanWriteToPacket(data.size() + 1)); + this->buffer.insert(this->buffer.end(), data.begin(), data.end()); + this->buffer.emplace_back('\0'); } /** @@ -404,6 +403,35 @@ void Packet::Recv_string(char *buffer, size_t size, StringValidationSettings set str_validate(bufp, last, settings); } +/** + * Reads characters (bytes) from the packet until it finds a '\0', or reaches a + * maximum of \c length characters. + * When the '\0' has not been reached in the first \c length read characters, + * more characters are read from the packet until '\0' has been reached. However, + * these characters will not end up in the returned string. + * The length of the returned string will be at most \c length - 1 characters. + * @param length The maximum length of the string including '\0'. + * @param settings The string validation settings. + * @return The validated string. + */ +std::string Packet::Recv_string(size_t length, StringValidationSettings settings) +{ + assert(length > 1); + + /* Both loops with Recv_uint8 terminate when reading past the end of the + * packet as Recv_uint8 then closes the connection and returns 0. */ + std::string str; + char character; + while (--length > 0 && (character = this->Recv_uint8()) != '\0') str.push_back(character); + + if (length == 0) { + /* The string in the packet was longer. Read until the termination. */ + while (this->Recv_uint8() != '\0') {} + } + + return str_validate(str, settings); +} + /** * Get the amount of bytes that are still available for the Transfer functions. * @return The number of bytes that still have to be transfered. diff --git a/src/network/core/packet.h b/src/network/core/packet.h index 9d607a5e81..4e8c4bbcb0 100644 --- a/src/network/core/packet.h +++ b/src/network/core/packet.h @@ -69,7 +69,7 @@ public: void Send_uint16(uint16 data); void Send_uint32(uint32 data); void Send_uint64(uint64 data); - void Send_string(const char *data); + void Send_string(const std::string_view data); size_t Send_bytes (const byte *begin, const byte *end); void Send_binary(const char *data, const size_t size); @@ -88,6 +88,7 @@ public: uint32 Recv_uint32(); uint64 Recv_uint64(); void Recv_string(char *buffer, size_t size, StringValidationSettings settings = SVS_REPLACE_WITH_QUESTION_MARK); + std::string Recv_string(size_t length, StringValidationSettings settings = SVS_REPLACE_WITH_QUESTION_MARK); void Recv_string(std::string &buffer, StringValidationSettings settings = SVS_REPLACE_WITH_QUESTION_MARK); void Recv_binary(char *buffer, size_t size); void Recv_binary(std::string &buffer, size_t size); diff --git a/src/network/core/tcp.h b/src/network/core/tcp.h index 201b36e4ab..58a7b112ee 100644 --- a/src/network/core/tcp.h +++ b/src/network/core/tcp.h @@ -87,7 +87,7 @@ protected: NetworkAddress address; public: - TCPConnecter(const NetworkAddress &address); + TCPConnecter(const std::string &connection_string, uint16 default_port); /** Silence the warnings */ virtual ~TCPConnecter() {} diff --git a/src/network/core/tcp_connect.cpp b/src/network/core/tcp_connect.cpp index b4485cfe96..81c4d8c264 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_internal.h" #include "../../safeguards.h" @@ -21,15 +22,16 @@ static std::vector _tcp_connecters; /** * Create a new connecter for the given address - * @param address the (un)resolved address to connect to + * @param connection_string the address to connect to */ -TCPConnecter::TCPConnecter(const NetworkAddress &address) : +TCPConnecter::TCPConnecter(const std::string &connection_string, uint16 default_port) : connected(false), aborted(false), killed(false), - sock(INVALID_SOCKET), - address(address) + sock(INVALID_SOCKET) { + this->address = ParseConnectionString(connection_string, default_port); + _tcp_connecters.push_back(this); if (!StartNewThread(nullptr, "ottd:tcp", &TCPConnecter::ThreadEntry, this)) { this->Connect(); diff --git a/src/network/core/tcp_http.cpp b/src/network/core/tcp_http.cpp index 8104512f44..db7639d90f 100644 --- a/src/network/core/tcp_http.cpp +++ b/src/network/core/tcp_http.cpp @@ -203,11 +203,9 @@ int NetworkHTTPSocketHandler::HandleHeader() *url = '\0'; - NetworkAddress address = ParseConnectionString(hname, 80); - /* Restore the URL. */ *url = '/'; - new NetworkHTTPContentConnecter(address, callback, url, data, depth); + new NetworkHTTPContentConnecter(hname, callback, url, data, depth); return 0; } diff --git a/src/network/core/tcp_http.h b/src/network/core/tcp_http.h index 1a1edaf87a..cc9a3adac2 100644 --- a/src/network/core/tcp_http.h +++ b/src/network/core/tcp_http.h @@ -81,16 +81,14 @@ class NetworkHTTPContentConnecter : TCPConnecter { public: /** * Start the connecting. - * @param address the address to connect to - * @param callback the callback for HTTP retrieval - * @param url the url at the server - * @param data the data to send - * @param depth the depth (redirect recursion) of the queries + * @param connection_string The address to connect to. + * @param callback The callback for HTTP retrieval. + * @param url The url at the server. + * @param data The data to send. + * @param depth The depth (redirect recursion) of the queries. */ - NetworkHTTPContentConnecter(const NetworkAddress &address, - HTTPCallback *callback, const char *url, - const char *data = nullptr, int depth = 0) : - TCPConnecter(address), + NetworkHTTPContentConnecter(const std::string &connection_string, HTTPCallback *callback, const char *url, const char *data = nullptr, int depth = 0) : + TCPConnecter(connection_string, 80), callback(callback), url(stredup(url)), data(data), diff --git a/src/network/core/udp.cpp b/src/network/core/udp.cpp index 8d54d59d30..3c3c46c008 100644 --- a/src/network/core/udp.cpp +++ b/src/network/core/udp.cpp @@ -28,11 +28,11 @@ NetworkUDPSocketHandler::NetworkUDPSocketHandler(NetworkAddressList *bind) this->bind.push_back(addr); } } else { - /* As hostname nullptr and port 0/nullptr don't go well when + /* As an empty hostname and port 0 don't go well when * resolving it we need to add an address for each of * the address families we support. */ - this->bind.emplace_back(nullptr, 0, AF_INET); - this->bind.emplace_back(nullptr, 0, AF_INET6); + this->bind.emplace_back("", 0, AF_INET); + this->bind.emplace_back("", 0, AF_INET6); } this->fragment_token = ((uint64) InteractiveRandom()) | (((uint64) InteractiveRandom()) << 32); diff --git a/src/network/network.cpp b/src/network/network.cpp index e7b8972251..2e246d645a 100644 --- a/src/network/network.cpp +++ b/src/network/network.cpp @@ -34,6 +34,7 @@ #include "../gfx_func.h" #include "../error.h" #include "../core/checksum_func.hpp" +#include #include "../safeguards.h" @@ -479,36 +480,51 @@ static void CheckPauseOnJoin() * Converts a string to ip/port/company * Format: IP:port#company * - * connection_string will be re-terminated to separate out the hostname, port will - * be set to the port strings given by the user, inside the memory area originally - * occupied by connection_string. Similar for company, if set. + * Returns the IP part as a string view into the passed string. This view is + * valid as long the passed connection string is valid. If there is no port + * present in the connection string, the port reference will not be touched. + * When there is no company ID present in the connection string or company_id + * is nullptr, then company ID will not be touched. + * + * @param connection_string The string with the connection data. + * @param port The port reference to set. + * @param company_id The company ID to set, if available. + * @return A std::string_view into the connection string with the (IP) address part. */ -void ParseFullConnectionString(const char **company, const char **port, char *connection_string) +std::string_view ParseFullConnectionString(const std::string &connection_string, uint16 &port, CompanyID *company_id) { - bool ipv6 = (strchr(connection_string, ':') != strrchr(connection_string, ':')); - for (char *p = connection_string; *p != '\0'; p++) { - switch (*p) { - case '[': - ipv6 = true; - break; - - case ']': - ipv6 = false; - break; - - case '#': - if (company == nullptr) continue; - *company = p + 1; - *p = '\0'; - break; - - case ':': - if (ipv6) break; - *port = p + 1; - *p = '\0'; - break; + 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; + } + } } } + + size_t port_offset = ip.find_last_of(':'); + size_t ipv6_close = ip.find_last_of(']'); + if (port_offset != std::string::npos && (ipv6_close == std::string::npos || ipv6_close < port_offset)) { + std::string_view port_string = ip.substr(port_offset + 1); + ip = ip.substr(0, port_offset); + std::from_chars(port_string.data(), port_string.data() + port_string.size(), port); + } + return ip; } /** @@ -519,16 +535,11 @@ void ParseFullConnectionString(const char **company, const char **port, char *co * @param default_port The default port to set port to if not in connection_string. * @return A valid NetworkAddress of the parsed information. */ -NetworkAddress ParseConnectionString(const std::string &connection_string, int default_port) +NetworkAddress ParseConnectionString(const std::string &connection_string, uint16 default_port) { - char internal_connection_string[NETWORK_HOSTNAME_PORT_LENGTH]; - strecpy(internal_connection_string, connection_string.c_str(), lastof(internal_connection_string)); - - const char *port = nullptr; - ParseFullConnectionString(nullptr, &port, internal_connection_string); - - int rport = port != nullptr ? atoi(port) : default_port; - return NetworkAddress(internal_connection_string, rport); + uint16 port = default_port; + std::string_view ip = ParseFullConnectionString(connection_string, port); + return NetworkAddress(ip, port); } /** @@ -536,24 +547,16 @@ NetworkAddress ParseConnectionString(const std::string &connection_string, int d * NetworkAddress, where the string can be postfixed with "#company" to * indicate the requested company. * - * @param company Pointer to the company variable to set iff indicted. * @param connection_string The string to parse. * @param default_port The default port to set port to if not in connection_string. + * @param company Pointer to the company variable to set iff indicted. * @return A valid NetworkAddress of the parsed information. */ -NetworkAddress ParseGameConnectionString(CompanyID *company, const std::string &connection_string, int default_port) +static NetworkAddress ParseGameConnectionString(const std::string &connection_string, uint16 default_port, CompanyID *company) { - char internal_connection_string[NETWORK_HOSTNAME_PORT_LENGTH + 4]; // 4 extra for the "#" and company - strecpy(internal_connection_string, connection_string.c_str(), lastof(internal_connection_string)); - - const char *port_s = nullptr; - const char *company_s = nullptr; - ParseFullConnectionString(&company_s, &port_s, internal_connection_string); - - if (company_s != nullptr) *company = (CompanyID)atoi(company_s); - - int port = port_s != nullptr ? atoi(port_s) : default_port; - return NetworkAddress(internal_connection_string, port); + uint16 port = default_port; + std::string_view ip = ParseFullConnectionString(connection_string, port, company); + return NetworkAddress(ip, port); } /** @@ -637,9 +640,10 @@ static void NetworkInitialize(bool close_admins = true) class TCPQueryConnecter : TCPConnecter { private: bool request_company_info; + std::string connection_string; public: - TCPQueryConnecter(const NetworkAddress &address, bool request_company_info) : TCPConnecter(address), request_company_info(request_company_info) {} + TCPQueryConnecter(const std::string &connection_string, bool request_company_info) : TCPConnecter(connection_string, NETWORK_DEFAULT_PORT), request_company_info(request_company_info), connection_string(connection_string) {} void OnFailure() override { @@ -649,24 +653,24 @@ public: void OnConnect(SOCKET s) override { _networking = true; - new ClientNetworkGameSocketHandler(s, address); + new ClientNetworkGameSocketHandler(s, this->connection_string); MyClient::SendInformationQuery(request_company_info); } }; /** * Query a server to fetch his game-info. - * @param address the address to query. + * @param connection_string the address to query. * @param request_company_info Whether to request company info too. */ -void NetworkTCPQueryServer(NetworkAddress address, bool request_company_info) +void NetworkTCPQueryServer(const std::string &connection_string, bool request_company_info) { if (!_network_available) return; NetworkDisconnect(); NetworkInitialize(); - new TCPQueryConnecter(address, request_company_info); + new TCPQueryConnecter(connection_string, request_company_info); } /** @@ -680,20 +684,18 @@ NetworkGameList *NetworkAddServer(const std::string &connection_string) { if (connection_string.empty()) return nullptr; - NetworkAddress address = ParseConnectionString(connection_string, NETWORK_DEFAULT_PORT); - /* Ensure the item already exists in the list */ - NetworkGameList *item = NetworkGameListAddItem(address); - if (StrEmpty(item->info.server_name)) { + NetworkGameList *item = NetworkGameListAddItem(connection_string); + if (item->info.server_name.empty()) { ClearGRFConfigList(&item->info.grfconfig); - address.GetAddressAsString(item->info.server_name, lastof(item->info.server_name)); + item->info.server_name = connection_string; item->manually = true; NetworkRebuildHostList(); UpdateNetworkGameWindow(); } - NetworkTCPQueryServer(address); + NetworkTCPQueryServer(connection_string); return item; } @@ -723,14 +725,17 @@ void NetworkRebuildHostList() _network_host_list.clear(); for (NetworkGameList *item = _network_game_list; item != nullptr; item = item->next) { - if (item->manually) _network_host_list.emplace_back(NetworkAddressDumper().GetAddressAsString(&(item->address), false)); + if (item->manually) _network_host_list.emplace_back(item->connection_string); } } /** Non blocking connection create to actually connect to servers */ class TCPClientConnecter : TCPConnecter { +private: + std::string connection_string; + public: - TCPClientConnecter(const NetworkAddress &address) : TCPConnecter(address) {} + TCPClientConnecter(const std::string &connection_string) : TCPConnecter(connection_string, NETWORK_DEFAULT_PORT), connection_string(connection_string) {} void OnFailure() override { @@ -740,7 +745,7 @@ public: void OnConnect(SOCKET s) override { _networking = true; - new ClientNetworkGameSocketHandler(s, this->address); + new ClientNetworkGameSocketHandler(s, this->connection_string); IConsoleCmdExec("exec scripts/on_client.scr 0"); NetworkClient_Connected(); } @@ -766,34 +771,12 @@ public: bool NetworkClientConnectGame(const std::string &connection_string, CompanyID default_company, const char *join_server_password, const char *join_company_password) { CompanyID join_as = default_company; - NetworkAddress address = ParseGameConnectionString(&join_as, connection_string, NETWORK_DEFAULT_PORT); - - if (join_as != COMPANY_NEW_COMPANY && join_as != COMPANY_SPECTATOR) { - join_as--; - if (join_as >= MAX_COMPANIES) { - return false; - } - } - - return NetworkClientConnectGame(address, join_as, join_server_password, join_company_password); -} + std::string resolved_connection_string = ParseGameConnectionString(connection_string, NETWORK_DEFAULT_PORT, &join_as).GetAddressAsString(false); -/** - * Join a client to the server at the given address. - * See the overloaded NetworkClientConnectGame for more details. - * - * @param address The network address of the server to join to. - * @param join_as The company number to join as. - * @param join_server_password The password for the server. - * @param join_company_password The password for the company. - * @return Whether the join has started. - */ -bool NetworkClientConnectGame(NetworkAddress &address, CompanyID join_as, const char *join_server_password, const char *join_company_password) -{ if (!_network_available) return false; if (!NetworkValidateClientName()) return false; - _network_join.address = address; + _network_join.connection_string = resolved_connection_string; _network_join.company = join_as; _network_join.server_password = join_server_password; _network_join.company_password = join_company_password; @@ -822,11 +805,11 @@ void NetworkClientJoinGame() NetworkDisconnect(); NetworkInitialize(); - strecpy(_settings_client.network.last_joined, _network_join.address.GetAddressAsString(false).c_str(), lastof(_settings_client.network.last_joined)); + strecpy(_settings_client.network.last_joined, _network_join.connection_string.c_str(), lastof(_settings_client.network.last_joined)); _network_join_status = NETWORK_JOIN_STATUS_CONNECTING; ShowJoinStatusWindow(); - new TCPClientConnecter(_network_join.address); + new TCPClientConnecter(_network_join.connection_string); } static void NetworkInitGameInfo() @@ -835,6 +818,7 @@ static void NetworkInitGameInfo() strecpy(_settings_client.network.server_name, "Unnamed Server", lastof(_settings_client.network.server_name)); } + FillStaticNetworkServerGameInfo(); /* The server is a client too */ _network_game_info.clients_on = _network_dedicated ? 0 : 1; @@ -1243,7 +1227,7 @@ void NetworkStartUp() /* Generate an server id when there is none yet */ if (StrEmpty(_settings_client.network.network_id)) NetworkGenerateServerId(); - memset(&_network_game_info, 0, sizeof(_network_game_info)); + _network_game_info = {}; NetworkInitialize(); DEBUG(net, 3, "[core] network online, multiplayer available"); diff --git a/src/network/network_client.cpp b/src/network/network_client.cpp index 02dc58d36f..0a433dfc0c 100644 --- a/src/network/network_client.cpp +++ b/src/network/network_client.cpp @@ -150,9 +150,9 @@ void ClientNetworkEmergencySave() * Create a new socket for the client side of the game connection. * @param s The socket to connect with. */ -ClientNetworkGameSocketHandler::ClientNetworkGameSocketHandler (SOCKET s, NetworkAddress address) +ClientNetworkGameSocketHandler::ClientNetworkGameSocketHandler (SOCKET s, std::string connection_string) : NetworkGameSocketHandler(s), - address(address), savegame(nullptr), token(0), status(STATUS_INACTIVE) + connection_string(std::move(connection_string)), savegame(nullptr), token(0), status(STATUS_INACTIVE) { assert(ClientNetworkGameSocketHandler::my_client == nullptr); ClientNetworkGameSocketHandler::my_client = this; @@ -702,7 +702,7 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_GAME_INFO(Packe NetworkGameList *item = GetLobbyGameInfo(); if (item == nullptr) { /* This is not the lobby, so add it to the game list. */ - item = NetworkGameListAddItem(this->address); + item = NetworkGameListAddItem(this->connection_string); } /* Clear any existing GRFConfig chain. */ diff --git a/src/network/network_client.h b/src/network/network_client.h index d1898eb016..b7092e824a 100644 --- a/src/network/network_client.h +++ b/src/network/network_client.h @@ -15,7 +15,7 @@ /** Class for handling the client side of the game connection. */ class ClientNetworkGameSocketHandler : public NetworkGameSocketHandler { private: - NetworkAddress address; ///< Address we are connected to. + std::string connection_string; ///< Address we are connected to. struct PacketReader *savegame; ///< Packet reader for reading the savegame. byte token; ///< The token we need to send back to the server to prove we're the right client. @@ -86,7 +86,7 @@ protected: static NetworkRecvStatus SendMapOk(); void CheckConnection(); public: - ClientNetworkGameSocketHandler(SOCKET s, NetworkAddress address); + ClientNetworkGameSocketHandler(SOCKET s, std::string connection_string); ~ClientNetworkGameSocketHandler(); NetworkRecvStatus CloseConnection(NetworkRecvStatus status) override; @@ -132,10 +132,10 @@ void NetworkClientSetCompanyPassword(const char *password); /** Information required to join a server. */ struct NetworkJoinInfo { NetworkJoinInfo() : company(COMPANY_SPECTATOR), server_password(nullptr), company_password(nullptr) {} - NetworkAddress address; ///< The address of the server to join. - CompanyID company; ///< The company to join. - const char *server_password; ///< The password of the server to join. - const char *company_password; ///< The password of the company to join. + std::string connection_string; ///< The address of the server to join. + CompanyID company; ///< The company to join. + const char *server_password; ///< The password of the server to join. + const char *company_password; ///< The password of the company to join. }; extern NetworkJoinInfo _network_join; diff --git a/src/network/network_content.cpp b/src/network/network_content.cpp index cecaecc737..de3e8fad1c 100644 --- a/src/network/network_content.cpp +++ b/src/network/network_content.cpp @@ -367,8 +367,7 @@ void ClientNetworkContentSocketHandler::DownloadSelectedContentHTTP(const Conten this->http_response_index = -1; - NetworkAddress address(NETWORK_CONTENT_MIRROR_HOST, NETWORK_CONTENT_MIRROR_PORT); - new NetworkHTTPContentConnecter(address, this, NETWORK_CONTENT_MIRROR_URL, content_request); + new NetworkHTTPContentConnecter(NETWORK_CONTENT_MIRROR_HOST, this, NETWORK_CONTENT_MIRROR_URL, content_request); /* NetworkHTTPContentConnecter takes over freeing of content_request! */ } @@ -774,7 +773,7 @@ public: * Initiate the connecting. * @param address The address of the server. */ - NetworkContentConnecter(const NetworkAddress &address) : TCPConnecter(address) {} + NetworkContentConnecter(const std::string &connection_string) : TCPConnecter(connection_string, NETWORK_CONTENT_SERVER_PORT) {} void OnFailure() override { @@ -800,7 +799,7 @@ void ClientNetworkContentSocketHandler::Connect() { if (this->sock != INVALID_SOCKET || this->isConnecting) return; this->isConnecting = true; - new NetworkContentConnecter(NetworkAddress(NETWORK_CONTENT_SERVER_HOST, NETWORK_CONTENT_SERVER_PORT, AF_UNSPEC)); + new NetworkContentConnecter(NETWORK_CONTENT_SERVER_HOST); } /** diff --git a/src/network/network_func.h b/src/network/network_func.h index aff5adfe97..ab4960e602 100644 --- a/src/network/network_func.h +++ b/src/network/network_func.h @@ -45,7 +45,7 @@ void NetworkReboot(); void NetworkDisconnect(bool blocking = false, bool close_admins = true); void NetworkGameLoop(); void NetworkBackgroundLoop(); -void ParseFullConnectionString(const char **company, const char **port, char *connection_string); +std::string_view ParseFullConnectionString(const std::string &connection_string, uint16 &port, CompanyID *company_id = nullptr); void NetworkStartDebugLog(const std::string &connection_string); void NetworkPopulateCompanyStats(NetworkCompanyStats *stats); @@ -70,6 +70,7 @@ void NetworkServerDailyLoop(); void NetworkServerMonthlyLoop(); void NetworkServerYearlyLoop(); void NetworkServerSendConfigUpdate(); +void NetworkServerUpdateGameInfo(); void NetworkServerShowStatusToConsole(); bool NetworkServerStart(); void NetworkServerNewCompany(const Company *company, NetworkClientInfo *ci); diff --git a/src/network/network_gamelist.cpp b/src/network/network_gamelist.cpp index f0e02994be..f1ff6219ad 100644 --- a/src/network/network_gamelist.cpp +++ b/src/network/network_gamelist.cpp @@ -44,20 +44,20 @@ static void NetworkGameListHandleDelayedInsert() 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->address); + NetworkGameList *item = NetworkGameListAddItem(ins_item->connection_string); if (item != nullptr) { - if (StrEmpty(item->info.server_name)) { + if (item->info.server_name.empty()) { ClearGRFConfigList(&item->info.grfconfig); - memset(&item->info, 0, sizeof(item->info)); - strecpy(item->info.server_name, ins_item->info.server_name, lastof(item->info.server_name)); + item->info = {}; + item->info.server_name = ins_item->info.server_name; item->online = false; } item->manually |= ins_item->manually; if (item->manually) NetworkRebuildHostList(); UpdateNetworkGameWindow(); } - free(ins_item); + delete ins_item; } } @@ -67,19 +67,20 @@ static void NetworkGameListHandleDelayedInsert() * @param address the address of the to-be added item * @return a point to the newly added or already existing item */ -NetworkGameList *NetworkGameListAddItem(NetworkAddress address) +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); + prev_item = nullptr; for (item = _network_game_list; item != nullptr; item = item->next) { - if (item->address == address) return item; + if (item->connection_string == resolved_connection_string) return item; prev_item = item; } - item = CallocT(1); - item->next = nullptr; - item->address = address; + item = new NetworkGameList(resolved_connection_string); if (prev_item == nullptr) { _network_game_list = item; @@ -109,8 +110,7 @@ void NetworkGameListRemoveItem(NetworkGameList *remove) /* Remove GRFConfig information */ ClearGRFConfigList(&remove->info.grfconfig); - free(remove); - remove = nullptr; + delete remove; DEBUG(net, 4, "[gamelist] removed server from list"); NetworkRebuildHostList(); @@ -141,7 +141,7 @@ void NetworkGameListRequery() /* item gets mostly zeroed by NetworkUDPQueryServer */ uint8 retries = item->retries; - NetworkUDPQueryServer(item->address); + NetworkUDPQueryServer(item->connection_string); item->retries = (retries >= REFRESH_GAMEINFO_X_REQUERIES) ? 0 : retries; } } diff --git a/src/network/network_gamelist.h b/src/network/network_gamelist.h index ce35c01d8b..6ef9d8e41f 100644 --- a/src/network/network_gamelist.h +++ b/src/network/network_gamelist.h @@ -16,19 +16,24 @@ /** Structure with information shown in the game list (GUI) */ struct NetworkGameList { - NetworkGameInfo info; ///< The game information of this server - NetworkAddress address; ///< The connection info of the game server - bool online; ///< False if the server did not respond (default status) - bool manually; ///< True if the server was added manually - uint8 retries; ///< Number of retries (to stop requerying) - NetworkGameList *next; ///< Next pointer to make a linked game list + NetworkGameList(const std::string &connection_string, bool manually = false) : + connection_string(connection_string), manually(manually) + { + } + + NetworkGameInfo info = {}; ///< The game information of this server + std::string connection_string; ///< Address of the server + 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) + NetworkGameList *next = nullptr; ///< Next pointer to make a linked game list }; /** Game list of this client */ extern NetworkGameList *_network_game_list; void NetworkGameListAddItemDelayed(NetworkGameList *item); -NetworkGameList *NetworkGameListAddItem(NetworkAddress address); +NetworkGameList *NetworkGameListAddItem(const std::string &connection_string); void NetworkGameListRemoveItem(NetworkGameList *remove); void NetworkGameListRequery(); diff --git a/src/network/network_gui.cpp b/src/network/network_gui.cpp index af4b699200..a2a97797cb 100644 --- a/src/network/network_gui.cpp +++ b/src/network/network_gui.cpp @@ -286,8 +286,10 @@ protected: /** Sort servers by name. */ static bool NGameNameSorter(NetworkGameList * const &a, NetworkGameList * const &b) { - int r = strnatcmp(a->info.server_name, b->info.server_name, true); // Sort by name (natural sorting). - return r == 0 ? a->address.CompareTo(b->address) < 0: r < 0; + int r = strnatcmp(a->info.server_name.c_str(), b->info.server_name.c_str(), true); // Sort by name (natural sorting). + if (r == 0) r = a->connection_string.compare(b->connection_string); + + return r < 0; } /** @@ -337,7 +339,7 @@ protected: static bool NGameAllowedSorter(NetworkGameList * const &a, NetworkGameList * const &b) { /* The servers we do not know anything about (the ones that did not reply) should be at the bottom) */ - int r = StrEmpty(a->info.server_revision) - StrEmpty(b->info.server_revision); + int r = a->info.server_revision.empty() - b->info.server_revision.empty(); /* Reverse default as we are interested in version-compatible clients first */ if (r == 0) r = b->info.version_compatible - a->info.version_compatible; @@ -374,7 +376,7 @@ protected: assert((*item) != nullptr); sf.ResetState(); - sf.AddLine((*item)->info.server_name); + sf.AddLine((*item)->info.server_name.c_str()); return sf.GetState(); } @@ -648,7 +650,7 @@ public: DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, STR_NETWORK_SERVER_LIST_CLIENTS); y += FONT_HEIGHT_NORMAL; - SetDParam(0, STR_CHEAT_SWITCH_CLIMATE_TEMPERATE_LANDSCAPE + sel->info.map_set); + SetDParam(0, STR_CHEAT_SWITCH_CLIMATE_TEMPERATE_LANDSCAPE + sel->info.landscape); DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, STR_NETWORK_SERVER_LIST_LANDSCAPE); // landscape y += FONT_HEIGHT_NORMAL; @@ -661,8 +663,7 @@ public: DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, STR_NETWORK_SERVER_LIST_SERVER_VERSION); // server version y += FONT_HEIGHT_NORMAL; - std::string address = sel->address.GetAddressAsString(); - SetDParamStr(0, address.c_str()); + SetDParamStr(0, sel->connection_string.c_str()); DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, STR_NETWORK_SERVER_LIST_SERVER_ADDRESS); // server address y += FONT_HEIGHT_NORMAL; @@ -766,7 +767,7 @@ public: break; case WID_NG_REFRESH: // Refresh - if (this->server != nullptr) NetworkTCPQueryServer(this->server->address); + if (this->server != nullptr) NetworkTCPQueryServer(this->server->connection_string); break; case WID_NG_NEWGRF: // NewGRF Settings @@ -1486,22 +1487,22 @@ struct NetworkLobbyWindow : public Window { case WID_NL_JOIN: // Join company /* Button can be clicked only when it is enabled. */ - NetworkClientConnectGame(this->server->address, this->company); + NetworkClientConnectGame(this->server->connection_string, this->company); break; case WID_NL_NEW: // New company - NetworkClientConnectGame(this->server->address, COMPANY_NEW_COMPANY); + NetworkClientConnectGame(this->server->connection_string, COMPANY_NEW_COMPANY); break; case WID_NL_SPECTATE: // Spectate game - NetworkClientConnectGame(this->server->address, COMPANY_SPECTATOR); + NetworkClientConnectGame(this->server->connection_string, COMPANY_SPECTATOR); break; case WID_NL_REFRESH: // Refresh /* Clear the information so removed companies don't remain */ for (auto &company : this->company_info) company = {}; - NetworkTCPQueryServer(this->server->address, true); + NetworkTCPQueryServer(this->server->connection_string, true); break; } } @@ -1569,9 +1570,9 @@ static void ShowNetworkLobbyWindow(NetworkGameList *ngl) DeleteWindowById(WC_NETWORK_WINDOW, WN_NETWORK_WINDOW_START); DeleteWindowById(WC_NETWORK_WINDOW, WN_NETWORK_WINDOW_GAME); - strecpy(_settings_client.network.last_joined, ngl->address.GetAddressAsString(false).c_str(), lastof(_settings_client.network.last_joined)); + strecpy(_settings_client.network.last_joined, ngl->connection_string.c_str(), lastof(_settings_client.network.last_joined)); - NetworkTCPQueryServer(ngl->address, true); + NetworkTCPQueryServer(ngl->connection_string, true); new NetworkLobbyWindow(&_network_lobby_window_desc, ngl); } diff --git a/src/network/network_internal.h b/src/network/network_internal.h index c7a5eb7402..176b2c2ab0 100644 --- a/src/network/network_internal.h +++ b/src/network/network_internal.h @@ -94,7 +94,7 @@ extern uint8 _network_reconnect; extern CompanyMask _network_company_passworded; -void NetworkTCPQueryServer(NetworkAddress address, bool request_company_info = false); +void NetworkTCPQueryServer(const std::string &connection_string, bool request_company_info = false); void GetBindAddresses(NetworkAddressList *addresses, uint16 port); struct NetworkGameList *NetworkAddServer(const std::string &connection_string); @@ -126,8 +126,6 @@ StringID GetNetworkErrorMsg(NetworkErrorCode err); bool NetworkFindName(char *new_name, const char *last); const char *GenerateCompanyPasswordHash(const char *password, const char *password_server_id, uint32 password_game_seed); -bool NetworkClientConnectGame(NetworkAddress &address, CompanyID join_as, const char *join_server_password = nullptr, const char *join_company_password = nullptr); -NetworkAddress ParseConnectionString(const std::string &connection_string, int default_port); -NetworkAddress ParseGameConnectionString(CompanyID *company, const std::string &connection_string, int default_port); +NetworkAddress ParseConnectionString(const std::string &connection_string, uint16 default_port); #endif /* NETWORK_INTERNAL_H */ diff --git a/src/network/network_server.cpp b/src/network/network_server.cpp index c27bebcbc6..26702a6cee 100644 --- a/src/network/network_server.cpp +++ b/src/network/network_server.cpp @@ -360,11 +360,8 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::SendClientInfo(NetworkClientIn /** Send the client information about the server. */ NetworkRecvStatus ServerNetworkGameSocketHandler::SendGameInfo() { - NetworkGameInfo ngi; - FillNetworkGameInfo(ngi); - Packet *p = new Packet(PACKET_SERVER_GAME_INFO, SHRT_MAX); - SerializeNetworkGameInfo(p, &ngi); + SerializeNetworkGameInfo(p, GetCurrentNetworkServerGameInfo()); this->SendPacket(p); @@ -373,11 +370,8 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::SendGameInfo() NetworkRecvStatus ServerNetworkGameSocketHandler::SendGameInfoExtended(PacketGameType reply_type, uint16 flags, uint16 version) { - NetworkGameInfo ngi; - FillNetworkGameInfo(ngi); - Packet *p = new Packet(reply_type, SHRT_MAX); - SerializeNetworkGameInfoExtended(p, &ngi, flags, version); + SerializeNetworkGameInfoExtended(p, GetCurrentNetworkServerGameInfo(), flags, version); this->SendPacket(p); @@ -2166,6 +2160,12 @@ void NetworkServerSendConfigUpdate() } } +/** Update the server's NetworkServerGameInfo due to changes in settings. */ +void NetworkServerUpdateGameInfo() +{ + if (_network_server) FillStaticNetworkServerGameInfo(); +} + /** * Tell that a particular company is (not) passworded. * @param company_id The company that got/removed the password. diff --git a/src/network/network_udp.cpp b/src/network/network_udp.cpp index 23e95412a1..f80a032fef 100644 --- a/src/network/network_udp.cpp +++ b/src/network/network_udp.cpp @@ -102,35 +102,34 @@ static Packet PrepareUdpClientFindServerPacket() /** * Helper function doing the actual work for querying the server. - * @param address The address of 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(NetworkAddress &address, bool needs_mutex, bool manually) +static void DoNetworkUDPQueryServer(const std::string &connection_string, bool needs_mutex, bool manually) { /* Clear item in gamelist */ - NetworkGameList *item = CallocT(1); - address.GetAddressAsString(item->info.server_name, lastof(item->info.server_name)); - item->address = address; - item->manually = manually; + 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 = PrepareUdpClientFindServerPacket(); if (_udp_client.socket != nullptr) _udp_client.socket->SendPacket(&p, &address); } /** * Query a specific server. - * @param address The address of the server. + * @param connection_string The address of the server. * @param manually Whether the address was entered manually. */ -void NetworkUDPQueryServer(NetworkAddress address, bool manually) +void NetworkUDPQueryServer(const std::string &connection_string, bool manually) { - if (address.IsResolved() || !StartNewThread(nullptr, "ottd:udp-query", &DoNetworkUDPQueryServer, std::move(address), true, std::move(manually))) { - DoNetworkUDPQueryServer(address, true, manually); + if (!StartNewThread(nullptr, "ottd:udp-query", &DoNetworkUDPQueryServer, std::move(connection_string), true, std::move(manually))) { + DoNetworkUDPQueryServer(connection_string, true, manually); } } @@ -173,7 +172,7 @@ 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; - void Reply_CLIENT_FIND_SERVER_extended(Packet *p, NetworkAddress *client_addr, NetworkGameInfo *ngi); + void Reply_CLIENT_FIND_SERVER_extended(Packet *p, NetworkAddress *client_addr, const NetworkServerGameInfo *ngi); public: /** * Create the socket. @@ -190,16 +189,13 @@ void ServerNetworkUDPSocketHandler::Receive_CLIENT_FIND_SERVER(Packet *p, Networ return; } - NetworkGameInfo ngi; - FillNetworkGameInfo(ngi); - if (p->CanReadFromPacket(8) && p->Recv_uint32() == FIND_SERVER_EXTENDED_TOKEN) { - this->Reply_CLIENT_FIND_SERVER_extended(p, client_addr, &ngi); + this->Reply_CLIENT_FIND_SERVER_extended(p, client_addr, GetCurrentNetworkServerGameInfo()); return; } Packet packet(PACKET_UDP_SERVER_RESPONSE); - SerializeNetworkGameInfo(&packet, &ngi); + SerializeNetworkGameInfo(&packet, GetCurrentNetworkServerGameInfo()); /* Let the client know that we are here */ this->SendPacket(&packet, client_addr); @@ -207,7 +203,7 @@ void ServerNetworkUDPSocketHandler::Receive_CLIENT_FIND_SERVER(Packet *p, Networ DEBUG(net, 2, "[udp] queried from %s", client_addr->GetHostname()); } -void ServerNetworkUDPSocketHandler::Reply_CLIENT_FIND_SERVER_extended(Packet *p, NetworkAddress *client_addr, NetworkGameInfo *ngi) +void ServerNetworkUDPSocketHandler::Reply_CLIENT_FIND_SERVER_extended(Packet *p, NetworkAddress *client_addr, const NetworkServerGameInfo *ngi) { uint16 flags = p->Recv_uint16(); uint16 version = p->Recv_uint16(); @@ -396,7 +392,7 @@ void ClientNetworkUDPSocketHandler::Receive_SERVER_RESPONSE_Common(Packet *p, Ne DEBUG(net, 4, "[udp]%s server response from %s", extended ? " extended" : "", NetworkAddressDumper().GetAddressAsString(client_addr)); /* Find next item */ - item = NetworkGameListAddItem(*client_addr); + item = NetworkGameListAddItem(client_addr->GetAddressAsString(false)); /* Clear any existing GRFConfig chain. */ ClearGRFConfigList(&item->info.grfconfig); @@ -434,7 +430,8 @@ void ClientNetworkUDPSocketHandler::Receive_SERVER_RESPONSE_Common(Packet *p, Ne SerializeGRFIdentifier(&packet, &in_request[i]->ident); } - this->SendPacket(&packet, &item->address); + NetworkAddress address = NetworkAddress(ParseConnectionString(item->connection_string, NETWORK_DEFAULT_PORT)); + this->SendPacket(&packet, &address); DEBUG(net, 4, "[udp] sent newgrf data request (%u) to %s", in_request_count, NetworkAddressDumper().GetAddressAsString(client_addr)); @@ -453,7 +450,7 @@ void ClientNetworkUDPSocketHandler::Receive_SERVER_RESPONSE_Common(Packet *p, Ne } if (client_addr->GetAddress()->ss_family == AF_INET6) { - strecat(item->info.server_name, " (IPv6)", lastof(item->info.server_name)); + item->info.server_name.append(" (IPv6)"); } UpdateNetworkGameWindow(); @@ -489,7 +486,7 @@ void ClientNetworkUDPSocketHandler::Receive_MASTER_RESPONSE_LIST(Packet *p, Netw /* Somehow we reached the end of the packet */ if (this->HasClientQuit()) return; - DoNetworkUDPQueryServer(addr, false, false); + DoNetworkUDPQueryServer(addr.GetAddressAsString(false), false, false); } } } diff --git a/src/network/network_udp.h b/src/network/network_udp.h index 189657bc87..ced949cd32 100644 --- a/src/network/network_udp.h +++ b/src/network/network_udp.h @@ -15,7 +15,7 @@ void NetworkUDPInitialize(); void NetworkUDPSearchGame(); void NetworkUDPQueryMasterServer(); -void NetworkUDPQueryServer(NetworkAddress address, bool manually = false); +void NetworkUDPQueryServer(const std::string &connection_string, bool manually = false); void NetworkUDPAdvertise(); void NetworkUDPRemoveAdvertise(bool blocking); void NetworkUDPClose(); diff --git a/src/newgrf_gui.cpp b/src/newgrf_gui.cpp index 1aef047de5..46518ddd62 100644 --- a/src/newgrf_gui.cpp +++ b/src/newgrf_gui.cpp @@ -51,9 +51,9 @@ void ShowNewGRFError() if (c->error == nullptr || (c->error->severity != STR_NEWGRF_ERROR_MSG_FATAL && c->error->severity != STR_NEWGRF_ERROR_MSG_ERROR)) continue; SetDParam (0, c->error->message != STR_NULL ? c->error->message : STR_JUST_RAW_STRING); - SetDParamStr(1, c->error->custom_message.c_str()); + SetDParamStr(1, c->error->custom_message); SetDParamStr(2, c->filename); - SetDParamStr(3, c->error->data.c_str()); + SetDParamStr(3, c->error->data); for (uint i = 0; i < lengthof(c->error->param_value); i++) { SetDParam(4 + i, c->error->param_value[i]); } @@ -70,9 +70,9 @@ static void ShowNewGRFInfo(const GRFConfig *c, uint x, uint y, uint right, uint { if (c->error != nullptr) { char message[512]; - SetDParamStr(0, c->error->custom_message.c_str()); // is skipped by built-in messages + SetDParamStr(0, c->error->custom_message); // is skipped by built-in messages SetDParamStr(1, c->filename); - SetDParamStr(2, c->error->data.c_str()); + SetDParamStr(2, c->error->data); for (uint i = 0; i < lengthof(c->error->param_value); i++) { SetDParam(3 + i, c->error->param_value[i]); } @@ -752,7 +752,7 @@ struct NewGRFWindow : public Window, NewGRFScanCallback { case WID_NS_PRESET_LIST: { Dimension d = GetStringBoundingBox(STR_NUM_CUSTOM); for (const auto &i : this->grf_presets) { - SetDParamStr(0, i.c_str()); + SetDParamStr(0, i); d = maxdim(d, GetStringBoundingBox(STR_JUST_RAW_STRING)); } d.width += padding.width; @@ -785,7 +785,7 @@ struct NewGRFWindow : public Window, NewGRFScanCallback { SetDParam(0, STR_NUM_CUSTOM); } else { SetDParam(0, STR_JUST_RAW_STRING); - SetDParamStr(1, this->grf_presets[this->preset].c_str()); + SetDParamStr(1, this->grf_presets[this->preset]); } break; } diff --git a/src/openttd.cpp b/src/openttd.cpp index 426b3f3db3..d004f10972 100644 --- a/src/openttd.cpp +++ b/src/openttd.cpp @@ -555,7 +555,7 @@ void OpenBrowser(const char *url) struct AfterNewGRFScan : NewGRFScanCallback { Year startyear; ///< The start year. uint32 generation_seed; ///< Seed for the new game. - char *dedicated_host; ///< Hostname for the dedicated server. + std::string dedicated_host; ///< Hostname for the dedicated server. uint16 dedicated_port; ///< Port for the dedicated server. char *network_conn; ///< Information about the server to connect to, or nullptr. const char *join_server_password; ///< The password to join the server with. @@ -567,7 +567,7 @@ struct AfterNewGRFScan : NewGRFScanCallback { */ AfterNewGRFScan() : startyear(INVALID_YEAR), generation_seed(GENERATE_NEW_SEED), - dedicated_host(nullptr), dedicated_port(0), network_conn(nullptr), + dedicated_port(0), network_conn(nullptr), join_server_password(nullptr), join_company_password(nullptr), save_config(true) { @@ -608,7 +608,7 @@ struct AfterNewGRFScan : NewGRFScanCallback { if (startyear != INVALID_YEAR) IConsoleSetSetting("game_creation.starting_year", startyear); if (generation_seed != GENERATE_NEW_SEED) _settings_newgame.game_creation.generation_seed = generation_seed; - if (dedicated_host != nullptr) { + if (!dedicated_host.empty()) { _network_bind_list.clear(); _network_bind_list.emplace_back(dedicated_host); } @@ -720,10 +720,7 @@ int openttd_main(int argc, char *argv[]) dedicated = true; SetDebugString("net=3"); if (mgo.opt != nullptr) { - const char *port = nullptr; - ParseFullConnectionString(nullptr, &port, mgo.opt); - if (!StrEmpty(mgo.opt)) scanner->dedicated_host = mgo.opt; - if (port != nullptr) scanner->dedicated_port = atoi(port); + scanner->dedicated_host = ParseFullConnectionString(mgo.opt, scanner->dedicated_port); } break; case 'f': _dedicated_forks = true; break; @@ -1000,6 +997,28 @@ void HandleExitGameRequest() } } +/** + * Triggers everything that should be triggered when starting a game. + * @param dedicated_server Whether this is a dedicated server or not. + */ +static void OnStartGame(bool dedicated_server) +{ + /* Update the local company for a loaded game. It is either always + * a company or in the case of a dedicated server a spectator */ + if (_network_server && !dedicated_server) { + NetworkServerDoMove(CLIENT_ID_SERVER, GetDefaultLocalCompany()); + } else { + SetLocalCompany(dedicated_server ? COMPANY_SPECTATOR : GetDefaultLocalCompany()); + } + if (_ctrl_pressed && !dedicated_server) { + DoCommandP(0, PM_PAUSED_NORMAL, 1, CMD_PAUSE); + } + /* Update the static game info to set the values from the new game. */ + NetworkServerUpdateGameInfo(); + /* Execute the game-start script */ + IConsoleCmdExec("exec scripts/game_start.scr 0"); +} + static void MakeNewGameDone() { SettingsDisableElrail(_settings_game.vehicle.disable_elrails); @@ -1009,9 +1028,8 @@ static void MakeNewGameDone() /* In a dedicated server, the server does not play */ if (!VideoDriver::GetInstance()->HasGUI()) { - SetLocalCompany(COMPANY_SPECTATOR); + OnStartGame(true); if (_settings_client.gui.pause_on_newgame) DoCommandP(0, PM_PAUSED_NORMAL, 1, CMD_PAUSE); - IConsoleCmdExec("exec scripts/game_start.scr 0"); return; } @@ -1030,9 +1048,7 @@ static void MakeNewGameDone() BuildOwnerLegend(); } - IConsoleCmdExec("exec scripts/game_start.scr 0"); - - SetLocalCompany(COMPANY_FIRST); + OnStartGame(false); InitializeRailGUI(); InitializeRoadGUI(); @@ -1227,18 +1243,7 @@ void SwitchToMode(SwitchMode new_mode) /* Reset engine pool to simplify changing engine NewGRFs in scenario editor. */ EngineOverrideManager::ResetToCurrentNewGRFConfig(); } - /* Update the local company for a loaded game. It is either always - * a company or in the case of a dedicated server a spectator */ - if (_network_server && !_network_dedicated) { - NetworkServerDoMove(CLIENT_ID_SERVER, GetDefaultLocalCompany()); - } else { - SetLocalCompany(_network_dedicated ? COMPANY_SPECTATOR : GetDefaultLocalCompany()); - } - if (_ctrl_pressed && !_network_dedicated) { - DoCommandP(0, PM_PAUSED_NORMAL, 1, CMD_PAUSE); - } - /* Execute the game-start script */ - IConsoleCmdExec("exec scripts/game_start.scr 0"); + OnStartGame(_network_dedicated); /* Decrease pause counter (was increased from opening load dialog) */ DoCommandP(0, PM_PAUSED_SAVELOAD, 0, CMD_PAUSE); } diff --git a/src/settings.cpp b/src/settings.cpp index 3ec5efe75c..214e6c86e2 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -1825,6 +1825,7 @@ static bool UpdateServerPassword(int32 p1) _settings_client.network.server_password[0] = '\0'; } + NetworkServerUpdateGameInfo(); return true; } @@ -1848,6 +1849,7 @@ static bool UpdateSettingsPassword(int32 p1) static bool UpdateClientConfigValues(int32 p1) { + NetworkServerUpdateGameInfo(); if (_network_server) NetworkServerSendConfigUpdate(); return true; @@ -2071,7 +2073,7 @@ static GRFConfig *GRFLoadConfig(IniFile *ini, const char *grpname, bool is_stati SetDParam(1, STR_CONFIG_ERROR_INVALID_GRF_UNKNOWN); } - SetDParamStr(0, StrEmpty(filename) ? item->name.c_str() : filename); + SetDParamStr(0, StrEmpty(filename) ? item->name : filename); ShowErrorMessage(STR_CONFIG_ERROR, STR_CONFIG_ERROR_INVALID_GRF, WL_CRITICAL); delete c; continue; diff --git a/src/settings_gui.cpp b/src/settings_gui.cpp index d55448493f..899d7ab19f 100644 --- a/src/settings_gui.cpp +++ b/src/settings_gui.cpp @@ -127,7 +127,7 @@ struct BaseSetTextfileWindow : public TextfileWindow { { if (widget == WID_TF_CAPTION) { SetDParam(0, content_type); - SetDParamStr(1, this->baseset->name.c_str()); + SetDParamStr(1, this->baseset->name); } } }; @@ -308,10 +308,10 @@ struct GameOptionsWindow : Window { case WID_GO_LANG_DROPDOWN: SetDParamStr(0, _current_language->own_name); break; case WID_GO_GUI_ZOOM_DROPDOWN: SetDParam(0, _gui_zoom_dropdown[_gui_zoom_cfg != ZOOM_LVL_CFG_AUTO ? ZOOM_LVL_OUT_4X - _gui_zoom_cfg + 1 : 0]); break; case WID_GO_FONT_ZOOM_DROPDOWN: SetDParam(0, _font_zoom_dropdown[_font_zoom_cfg != ZOOM_LVL_CFG_AUTO ? ZOOM_LVL_OUT_4X - _font_zoom_cfg + 1 : 0]); break; - case WID_GO_BASE_GRF_DROPDOWN: SetDParamStr(0, BaseGraphics::GetUsedSet()->name.c_str()); break; + case WID_GO_BASE_GRF_DROPDOWN: SetDParamStr(0, BaseGraphics::GetUsedSet()->name); break; case WID_GO_BASE_GRF_STATUS: SetDParam(0, BaseGraphics::GetUsedSet()->GetNumInvalid()); break; - case WID_GO_BASE_SFX_DROPDOWN: SetDParamStr(0, BaseSounds::GetUsedSet()->name.c_str()); break; - case WID_GO_BASE_MUSIC_DROPDOWN: SetDParamStr(0, BaseMusic::GetUsedSet()->name.c_str()); break; + case WID_GO_BASE_SFX_DROPDOWN: SetDParamStr(0, BaseSounds::GetUsedSet()->name); break; + case WID_GO_BASE_MUSIC_DROPDOWN: SetDParamStr(0, BaseMusic::GetUsedSet()->name); break; case WID_GO_BASE_MUSIC_STATUS: SetDParam(0, BaseMusic::GetUsedSet()->GetNumInvalid()); break; case WID_GO_REFRESH_RATE_DROPDOWN: SetDParam(0, _settings_client.gui.refresh_rate); break; case WID_GO_RESOLUTION_DROPDOWN: { diff --git a/src/string_func.h b/src/string_func.h index 8fb25f0917..d2f9ee067b 100644 --- a/src/string_func.h +++ b/src/string_func.h @@ -43,7 +43,7 @@ std::string CDECL stdstr_fmt(const char *str, ...) WARN_FORMAT(1, 2); std::string stdstr_vfmt(const char *str, va_list va) WARN_FORMAT(1, 0); char *str_validate(char *str, const char *last, StringValidationSettings settings = SVS_REPLACE_WITH_QUESTION_MARK) NOACCESS(2); -std::string str_validate(const std::string &str, StringValidationSettings settings = SVS_REPLACE_WITH_QUESTION_MARK); +[[nodiscard]] std::string str_validate(const std::string &str, StringValidationSettings settings = SVS_REPLACE_WITH_QUESTION_MARK); void ValidateString(const char *str); const char *str_fix_scc_encoded(char *str, const char *last) NOACCESS(2); diff --git a/src/strings.cpp b/src/strings.cpp index b919660573..c5ff5fc60b 100644 --- a/src/strings.cpp +++ b/src/strings.cpp @@ -300,6 +300,17 @@ void SetDParamStr(uint n, const char *str) SetDParam(n, (uint64)(size_t)str); } +/** + * This function is used to "bind" the C string of a std::string to a OpenTTD dparam slot. + * The caller has to ensure that the std::string reference remains valid while the string is shown. + * @param n slot of the string + * @param str string to bind + */ +void SetDParamStr(uint n, const std::string &str) +{ + SetDParamStr(n, str.c_str()); +} + /** * Shift the string parameters in the global string parameter array by \a amount positions, making room at the beginning. * @param amount Number of positions to shift. diff --git a/src/strings_func.h b/src/strings_func.h index b783710cea..2303ad7e33 100644 --- a/src/strings_func.h +++ b/src/strings_func.h @@ -208,6 +208,7 @@ void SetDParamMaxValue(uint n, uint64 max_value, uint min_count = 0, FontSize si void SetDParamMaxDigits(uint n, uint count, FontSize size = FS_NORMAL); void SetDParamStr(uint n, const char *str); +void SetDParamStr(uint n, const std::string &str); void CopyInDParam(int offs, const uint64 *src, int num); void CopyOutDParam(uint64 *dst, int offs, int num); diff --git a/src/table/settings.ini b/src/table/settings.ini index 0a865c7189..727af75efc 100644 --- a/src/table/settings.ini +++ b/src/table/settings.ini @@ -6233,6 +6233,7 @@ type = SLE_STRB flags = SLF_NOT_IN_SAVE | SLF_NO_NETWORK_SYNC guiflags = SGF_NETWORK_ONLY def = nullptr +proc = UpdateClientConfigValues cat = SC_BASIC [SDTC_STR] @@ -6300,6 +6301,7 @@ guiflags = SGF_NETWORK_ONLY def = 25 min = 2 max = MAX_CLIENTS +proc = UpdateClientConfigValues cat = SC_BASIC [SDTC_VAR] diff --git a/src/town_gui.cpp b/src/town_gui.cpp index 65ae8e0a95..2696ba1759 100644 --- a/src/town_gui.cpp +++ b/src/town_gui.cpp @@ -442,7 +442,7 @@ public: } if (!this->town->text.empty()) { - SetDParamStr(0, this->town->text.c_str()); + SetDParamStr(0, this->town->text); DrawStringMultiLine(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y += FONT_HEIGHT_NORMAL, UINT16_MAX, STR_JUST_RAW_STRING, TC_BLACK); } } @@ -524,7 +524,7 @@ public: if (_settings_game.economy.station_noise_level) aimed_height += FONT_HEIGHT_NORMAL; if (!this->town->text.empty()) { - SetDParamStr(0, this->town->text.c_str()); + SetDParamStr(0, this->town->text); aimed_height += GetStringHeight(STR_JUST_RAW_STRING, width - WD_FRAMERECT_LEFT - WD_FRAMERECT_RIGHT); } diff --git a/src/widgets/dropdown.cpp b/src/widgets/dropdown.cpp index c73e705abf..cacc1a7efc 100644 --- a/src/widgets/dropdown.cpp +++ b/src/widgets/dropdown.cpp @@ -65,7 +65,7 @@ StringID DropDownListParamStringItem::String() const StringID DropDownListCharStringItem::String() const { - SetDParamStr(0, this->raw_string.c_str()); + SetDParamStr(0, this->raw_string); return this->string; }