From adb9fa3b36cf1031591363dcd3b6f591fd485ab0 Mon Sep 17 00:00:00 2001 From: Tyler Trahan Date: Fri, 2 Apr 2021 04:13:27 -0400 Subject: [PATCH 001/268] Feature: Press ctrl to build diagonal rivers in Scenario Editor (#8880) --- src/dock_gui.cpp | 4 ++-- src/lang/english.txt | 2 +- src/water_cmd.cpp | 22 +++++++++++++++++----- 3 files changed, 20 insertions(+), 8 deletions(-) diff --git a/src/dock_gui.cpp b/src/dock_gui.cpp index d676bf8b96..850b9d3f30 100644 --- a/src/dock_gui.cpp +++ b/src/dock_gui.cpp @@ -170,7 +170,7 @@ struct BuildDocksToolbarWindow : Window { case WID_DT_RIVER: // Build river button (in scenario editor) if (_game_mode != GM_EDITOR) return; - HandlePlacePushButton(this, WID_DT_RIVER, SPR_CURSOR_RIVER, HT_RECT); + HandlePlacePushButton(this, WID_DT_RIVER, SPR_CURSOR_RIVER, HT_RECT | HT_DIAGONAL); break; case WID_DT_BUILD_AQUEDUCT: // Build aqueduct button @@ -247,7 +247,7 @@ struct BuildDocksToolbarWindow : Window { DoCommandP(end_tile, start_tile, (_game_mode == GM_EDITOR && _ctrl_pressed) ? WATER_CLASS_SEA : WATER_CLASS_CANAL, CMD_BUILD_CANAL | CMD_MSG(STR_ERROR_CAN_T_BUILD_CANALS), CcPlaySound_CONSTRUCTION_WATER); break; case DDSP_CREATE_RIVER: - DoCommandP(end_tile, start_tile, WATER_CLASS_RIVER, CMD_BUILD_CANAL | CMD_MSG(STR_ERROR_CAN_T_PLACE_RIVERS), CcPlaySound_CONSTRUCTION_WATER); + DoCommandP(end_tile, start_tile, WATER_CLASS_RIVER | (_ctrl_pressed ? 1 << 2 : 0), CMD_BUILD_CANAL | CMD_MSG(STR_ERROR_CAN_T_PLACE_RIVERS), CcPlaySound_CONSTRUCTION_WATER); break; default: break; diff --git a/src/lang/english.txt b/src/lang/english.txt index 9f43788e54..e55fcba1a1 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -2531,7 +2531,7 @@ STR_WATERWAYS_TOOLBAR_BUILD_DOCK_TOOLTIP :{BLACK}Build sh STR_WATERWAYS_TOOLBAR_BUOY_TOOLTIP :{BLACK}Place a buoy which can be used as a waypoint. Shift toggles building/showing cost estimate STR_WATERWAYS_TOOLBAR_BUILD_AQUEDUCT_TOOLTIP :{BLACK}Build aqueduct. Shift toggles building/showing cost estimate STR_WATERWAYS_TOOLBAR_CREATE_LAKE_TOOLTIP :{BLACK}Define water area.{}Make a canal, unless Ctrl is held down at sea level, when it will flood the surroundings instead -STR_WATERWAYS_TOOLBAR_CREATE_RIVER_TOOLTIP :{BLACK}Place rivers +STR_WATERWAYS_TOOLBAR_CREATE_RIVER_TOOLTIP :{BLACK}Place rivers. Ctrl selects the area diagonally # Ship depot construction window STR_DEPOT_BUILD_SHIP_CAPTION :{WHITE}Ship Depot Orientation diff --git a/src/water_cmd.cpp b/src/water_cmd.cpp index 6a3b730657..731954c2eb 100644 --- a/src/water_cmd.cpp +++ b/src/water_cmd.cpp @@ -437,7 +437,9 @@ bool RiverModifyDesertZone(TileIndex tile, void *) * @param tile end tile of stretch-dragging * @param flags type of operation * @param p1 start tile of stretch-dragging - * @param p2 waterclass to build. sea and river can only be built in scenario editor + * @param p2 various bitstuffed data + * bits 0-1: waterclass to build. sea and river can only be built in scenario editor + * bit 2: Whether to use the Orthogonal (0) or Diagonal (1) iterator. * @param text unused * @return the cost of this operation or an error */ @@ -449,13 +451,23 @@ CommandCost CmdBuildCanal(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 /* Outside of the editor you can only build canals, not oceans */ if (wc != WATER_CLASS_CANAL && _game_mode != GM_EDITOR) return CMD_ERROR; - TileArea ta(tile, p1); - /* Outside the editor you can only drag canals, and not areas */ - if (_game_mode != GM_EDITOR && ta.w != 1 && ta.h != 1) return CMD_ERROR; + if (_game_mode != GM_EDITOR) { + TileArea ta(tile, p1); + if (ta.w != 1 && ta.h != 1) return CMD_ERROR; + } CommandCost cost(EXPENSES_CONSTRUCTION); - TILE_AREA_LOOP(tile, ta) { + + std::unique_ptr iter; + if (HasBit(p2, 2)) { + iter = std::make_unique(tile, p1); + } else { + iter = std::make_unique(tile, p1); + } + + for (; *iter != INVALID_TILE; ++(*iter)) { + TileIndex tile = *iter; CommandCost ret; Slope slope = GetTileSlope(tile); From 83ac5aa27ad5185d68e549d9be2134506009b1ae Mon Sep 17 00:00:00 2001 From: Jonathan G Rennison Date: Fri, 2 Apr 2021 09:13:53 +0100 Subject: [PATCH 002/268] Fix: Memory leak of airport tile layout in AirportChangeInfo (prop 0A) (#8928) --- src/newgrf.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/newgrf.cpp b/src/newgrf.cpp index 7ac8bc945c..46bd5a6558 100644 --- a/src/newgrf.cpp +++ b/src/newgrf.cpp @@ -3862,6 +3862,7 @@ static ChangeInfoResult AirportChangeInfo(uint airport, int numinfo, int prop, B } case 0x0A: { // Set airport layout + byte old_num_table = as->num_table; free(as->rotation); as->num_table = buf->ReadByte(); // Number of layaouts as->rotation = MallocT(as->num_table); @@ -3920,6 +3921,12 @@ static ChangeInfoResult AirportChangeInfo(uint airport, int numinfo, int prop, B tile_table[j] = CallocT(size); memcpy(tile_table[j], copy_from, sizeof(*copy_from) * size); } + /* Free old layouts in the airport spec */ + for (int j = 0; j < old_num_table; j++) { + /* remove the individual layouts */ + free(as->table[j]); + } + free(as->table); /* Install final layout construction in the airport spec */ as->table = tile_table; free(att); From e760c9fbec7ab4f0637662bd299369de6dee3db2 Mon Sep 17 00:00:00 2001 From: Didac Perez Parera Date: Fri, 2 Apr 2021 01:15:26 -0700 Subject: [PATCH 003/268] Fix: adjust object and rail station selection window padding to be consistent (#8929) consistent --- src/object_gui.cpp | 16 ++++++++-------- src/rail_gui.cpp | 14 +++++++------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/object_gui.cpp b/src/object_gui.cpp index 69857895e7..05e9b0092d 100644 --- a/src/object_gui.cpp +++ b/src/object_gui.cpp @@ -660,24 +660,24 @@ static const NWidgetPart _nested_build_object_widgets[] = { NWidget(WWT_DEFSIZEBOX, COLOUR_DARK_GREEN), EndContainer(), NWidget(WWT_PANEL, COLOUR_DARK_GREEN), - NWidget(NWID_HORIZONTAL), SetPadding(2, 0, 0, 0), - NWidget(NWID_VERTICAL), - NWidget(NWID_HORIZONTAL), SetPadding(0, 5, 2, 5), + NWidget(NWID_HORIZONTAL), SetPadding(2, 0, 0, 2), + NWidget(NWID_VERTICAL), SetPadding(0, 5, 2, 0), SetPIP(0, 2, 0), + NWidget(NWID_HORIZONTAL), NWidget(WWT_TEXT, COLOUR_DARK_GREEN), SetFill(0, 1), SetDataTip(STR_LIST_FILTER_TITLE, STR_NULL), NWidget(WWT_EDITBOX, COLOUR_GREY, WID_BO_FILTER), SetFill(1, 0), SetResize(1, 0), SetDataTip(STR_LIST_FILTER_OSKTITLE, STR_LIST_FILTER_TOOLTIP), EndContainer(), - NWidget(NWID_HORIZONTAL), SetPadding(0, 5, 2, 5), + NWidget(NWID_HORIZONTAL), NWidget(WWT_MATRIX, COLOUR_GREY, WID_BO_CLASS_LIST), SetFill(1, 0), SetMatrixDataTip(1, 0, STR_OBJECT_BUILD_CLASS_TOOLTIP), SetScrollbar(WID_BO_SCROLLBAR), NWidget(NWID_VSCROLLBAR, COLOUR_GREY, WID_BO_SCROLLBAR), EndContainer(), - NWidget(NWID_HORIZONTAL), SetPadding(0, 5, 0, 5), + NWidget(NWID_HORIZONTAL), NWidget(NWID_MATRIX, COLOUR_DARK_GREEN, WID_BO_OBJECT_MATRIX), SetPIP(0, 2, 0), NWidget(WWT_PANEL, COLOUR_GREY, WID_BO_OBJECT_SPRITE), SetDataTip(0x0, STR_OBJECT_BUILD_PREVIEW_TOOLTIP), EndContainer(), EndContainer(), EndContainer(), - NWidget(WWT_TEXT, COLOUR_DARK_GREEN, WID_BO_OBJECT_NAME), SetDataTip(STR_ORANGE_STRING, STR_NULL), SetPadding(2, 5, 2, 5), - NWidget(WWT_TEXT, COLOUR_DARK_GREEN, WID_BO_OBJECT_SIZE), SetDataTip(STR_OBJECT_BUILD_SIZE, STR_NULL), SetPadding(2, 5, 2, 5), + NWidget(WWT_TEXT, COLOUR_DARK_GREEN, WID_BO_OBJECT_NAME), SetDataTip(STR_ORANGE_STRING, STR_NULL), + NWidget(WWT_TEXT, COLOUR_DARK_GREEN, WID_BO_OBJECT_SIZE), SetDataTip(STR_OBJECT_BUILD_SIZE, STR_NULL), EndContainer(), NWidget(WWT_PANEL, COLOUR_DARK_GREEN), SetScrollbar(WID_BO_SELECT_SCROLL), NWidget(NWID_HORIZONTAL), @@ -691,7 +691,7 @@ static const NWidgetPart _nested_build_object_widgets[] = { EndContainer(), EndContainer(), NWidget(NWID_HORIZONTAL), - NWidget(WWT_EMPTY, INVALID_COLOUR, WID_BO_INFO), SetPadding(2, 5, 0, 5), SetFill(1, 0), SetResize(1, 0), + NWidget(WWT_EMPTY, INVALID_COLOUR, WID_BO_INFO), SetPadding(0, 5, 0, 1), SetFill(1, 0), SetResize(1, 0), NWidget(NWID_VERTICAL), NWidget(WWT_PANEL, COLOUR_DARK_GREEN), SetFill(0, 1), EndContainer(), NWidget(WWT_RESIZEBOX, COLOUR_DARK_GREEN), diff --git a/src/rail_gui.cpp b/src/rail_gui.cpp index 3c4adf2622..6956d0e046 100644 --- a/src/rail_gui.cpp +++ b/src/rail_gui.cpp @@ -1554,23 +1554,23 @@ static const NWidgetPart _nested_station_builder_widgets[] = { EndContainer(), EndContainer(), NWidget(WWT_PANEL, COLOUR_DARK_GREEN), - NWidget(NWID_HORIZONTAL), + NWidget(NWID_HORIZONTAL), SetPadding(2, 0, 0, 2), NWidget(NWID_VERTICAL), NWidget(NWID_SELECTION, INVALID_COLOUR, WID_BRAS_FILTER_CONTAINER), - NWidget(NWID_HORIZONTAL), SetPadding(2, 2, 0, 5), + NWidget(NWID_HORIZONTAL), SetPadding(0, 5, 2, 0), NWidget(WWT_TEXT, COLOUR_DARK_GREEN), SetFill(0, 1), SetDataTip(STR_LIST_FILTER_TITLE, STR_NULL), NWidget(WWT_EDITBOX, COLOUR_GREY, WID_BRAS_FILTER_EDITBOX), SetFill(1, 0), SetResize(1, 0), SetDataTip(STR_LIST_FILTER_OSKTITLE, STR_LIST_FILTER_TOOLTIP), EndContainer(), EndContainer(), NWidget(NWID_SELECTION, INVALID_COLOUR, WID_BRAS_SHOW_NEWST_ADDITIONS), - NWidget(NWID_HORIZONTAL), SetPIP(7, 0, 7), SetPadding(2, 0, 1, 0), + NWidget(NWID_HORIZONTAL), SetPadding(0, 5, 2, 0), NWidget(WWT_MATRIX, COLOUR_GREY, WID_BRAS_NEWST_LIST), SetMinimalSize(122, 71), SetFill(1, 0), SetMatrixDataTip(1, 0, STR_STATION_BUILD_STATION_CLASS_TOOLTIP), SetScrollbar(WID_BRAS_NEWST_SCROLL), NWidget(NWID_VSCROLLBAR, COLOUR_GREY, WID_BRAS_NEWST_SCROLL), EndContainer(), EndContainer(), - NWidget(WWT_LABEL, COLOUR_DARK_GREEN), SetMinimalSize(144, 11), SetDataTip(STR_STATION_BUILD_ORIENTATION, STR_NULL), SetPadding(1, 2, 0, 2), + NWidget(WWT_LABEL, COLOUR_DARK_GREEN), SetMinimalSize(144, 11), SetDataTip(STR_STATION_BUILD_ORIENTATION, STR_NULL), SetPadding(1, 2, 0, 0), NWidget(NWID_HORIZONTAL), NWidget(NWID_SPACER), SetMinimalSize(7, 0), SetFill(1, 0), NWidget(WWT_PANEL, COLOUR_GREY, WID_BRAS_PLATFORM_DIR_X), SetMinimalSize(66, 60), SetFill(0, 0), SetDataTip(0x0, STR_STATION_BUILD_RAILROAD_ORIENTATION_TOOLTIP), EndContainer(), @@ -1609,7 +1609,7 @@ static const NWidgetPart _nested_station_builder_widgets[] = { NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_BRAS_PLATFORM_DRAG_N_DROP), SetMinimalSize(75, 12), SetDataTip(STR_STATION_BUILD_DRAG_DROP, STR_STATION_BUILD_DRAG_DROP_TOOLTIP), NWidget(NWID_SPACER), SetMinimalSize(2, 0), SetFill(1, 0), EndContainer(), - NWidget(WWT_LABEL, COLOUR_DARK_GREEN), SetMinimalSize(144, 11), SetDataTip(STR_STATION_BUILD_COVERAGE_AREA_TITLE, STR_NULL), SetPadding(3, 2, 0, 2), + NWidget(WWT_LABEL, COLOUR_DARK_GREEN), SetMinimalSize(144, 11), SetDataTip(STR_STATION_BUILD_COVERAGE_AREA_TITLE, STR_NULL), SetPadding(3, 2, 0, 0), NWidget(NWID_HORIZONTAL), NWidget(NWID_SPACER), SetMinimalSize(2, 0), SetFill(1, 0), NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_BRAS_HIGHLIGHT_OFF), SetMinimalSize(60, 12), @@ -1623,7 +1623,7 @@ static const NWidgetPart _nested_station_builder_widgets[] = { /* We need an additional background for the matrix, as the matrix cannot handle the scrollbar due to not being an NWidgetCore. */ NWidget(WWT_PANEL, COLOUR_DARK_GREEN), SetScrollbar(WID_BRAS_MATRIX_SCROLL), NWidget(NWID_HORIZONTAL), - NWidget(NWID_MATRIX, COLOUR_DARK_GREEN, WID_BRAS_MATRIX), SetScrollbar(WID_BRAS_MATRIX_SCROLL), SetPIP(0, 2, 0), SetPadding(2, 0, 0, 0), + NWidget(NWID_MATRIX, COLOUR_DARK_GREEN, WID_BRAS_MATRIX), SetScrollbar(WID_BRAS_MATRIX_SCROLL), SetPIP(0, 2, 0), NWidget(WWT_PANEL, COLOUR_DARK_GREEN, WID_BRAS_IMAGE), SetMinimalSize(66, 60), SetFill(0, 0), SetResize(0, 0), SetDataTip(0x0, STR_STATION_BUILD_STATION_TYPE_TOOLTIP), SetScrollbar(WID_BRAS_MATRIX_SCROLL), EndContainer(), @@ -1634,7 +1634,7 @@ static const NWidgetPart _nested_station_builder_widgets[] = { EndContainer(), EndContainer(), NWidget(NWID_HORIZONTAL), - NWidget(WWT_EMPTY, INVALID_COLOUR, WID_BRAS_COVERAGE_TEXTS), SetFill(1, 1), SetResize(1, 0), + NWidget(WWT_EMPTY, INVALID_COLOUR, WID_BRAS_COVERAGE_TEXTS), SetPadding(2, 5, 0, 1), SetFill(1, 1), SetResize(1, 0), NWidget(NWID_SELECTION, INVALID_COLOUR, WID_BRAS_SHOW_NEWST_RESIZE), NWidget(NWID_VERTICAL), NWidget(WWT_PANEL, COLOUR_DARK_GREEN), SetFill(0, 1), EndContainer(), From 799eb31ff199745888ffecf76a37e0d7cce437dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Guilloux?= Date: Fri, 2 Apr 2021 10:15:45 +0200 Subject: [PATCH 004/268] Change: [CMake] Copy AI/GS compatibility files to build dir (#8906) --- CMakeLists.txt | 3 ++- bin/CMakeLists.txt | 2 ++ bin/ai/CMakeLists.txt | 40 +++++++++++++++++++++++++++++++++++ bin/game/CMakeLists.txt | 37 ++++++++++++++++++++++++++++++++ cmake/InstallAndPackage.cmake | 4 ++-- 5 files changed, 83 insertions(+), 3 deletions(-) create mode 100644 bin/CMakeLists.txt create mode 100644 bin/ai/CMakeLists.txt create mode 100644 bin/game/CMakeLists.txt diff --git a/CMakeLists.txt b/CMakeLists.txt index c5f1d11b50..17ed2df8c7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -220,6 +220,7 @@ if(MSVC) target_sources(openttd PRIVATE "${CMAKE_SOURCE_DIR}/os/windows/openttd.manifest") endif() +add_subdirectory(${CMAKE_SOURCE_DIR}/bin) add_subdirectory(${CMAKE_SOURCE_DIR}/src) add_subdirectory(${CMAKE_SOURCE_DIR}/media) @@ -240,7 +241,7 @@ if(IPO_FOUND) set_target_properties(openttd PROPERTIES INTERPROCEDURAL_OPTIMIZATION_MINSIZEREL True) set_target_properties(openttd PROPERTIES INTERPROCEDURAL_OPTIMIZATION_RELWITHDEBINFO True) endif() -set_target_properties(openttd PROPERTIES VS_DEBUGGER_WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}/bin") +set_target_properties(openttd PROPERTIES VS_DEBUGGER_WORKING_DIRECTORY "${CMAKE_BINARY_DIR}") process_compile_flags() include(LinkPackage) diff --git a/bin/CMakeLists.txt b/bin/CMakeLists.txt new file mode 100644 index 0000000000..9d75bc0eea --- /dev/null +++ b/bin/CMakeLists.txt @@ -0,0 +1,2 @@ +add_subdirectory(ai) +add_subdirectory(game) diff --git a/bin/ai/CMakeLists.txt b/bin/ai/CMakeLists.txt new file mode 100644 index 0000000000..83eb11962d --- /dev/null +++ b/bin/ai/CMakeLists.txt @@ -0,0 +1,40 @@ +set(AI_COMPAT_SOURCE_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/compat_0.7.nut + ${CMAKE_CURRENT_SOURCE_DIR}/compat_1.0.nut + ${CMAKE_CURRENT_SOURCE_DIR}/compat_1.1.nut + ${CMAKE_CURRENT_SOURCE_DIR}/compat_1.2.nut + ${CMAKE_CURRENT_SOURCE_DIR}/compat_1.3.nut + ${CMAKE_CURRENT_SOURCE_DIR}/compat_1.4.nut + ${CMAKE_CURRENT_SOURCE_DIR}/compat_1.5.nut + ${CMAKE_CURRENT_SOURCE_DIR}/compat_1.6.nut + ${CMAKE_CURRENT_SOURCE_DIR}/compat_1.7.nut + ${CMAKE_CURRENT_SOURCE_DIR}/compat_1.8.nut + ${CMAKE_CURRENT_SOURCE_DIR}/compat_1.9.nut + ${CMAKE_CURRENT_SOURCE_DIR}/compat_1.10.nut + ${CMAKE_CURRENT_SOURCE_DIR}/compat_1.11.nut + ${CMAKE_CURRENT_SOURCE_DIR}/compat_1.12.nut +) + +foreach(AI_COMPAT_SOURCE_FILE IN LISTS AI_COMPAT_SOURCE_FILES) + string(REPLACE "${CMAKE_SOURCE_DIR}/bin/" "" AI_COMPAT_SOURCE_FILE_NAME "${AI_COMPAT_SOURCE_FILE}") + string(CONCAT AI_COMPAT_BINARY_FILE "${CMAKE_BINARY_DIR}/" "${AI_COMPAT_SOURCE_FILE_NAME}") + + add_custom_command(OUTPUT ${AI_COMPAT_BINARY_FILE} + COMMAND ${CMAKE_COMMAND} -E copy + ${AI_COMPAT_SOURCE_FILE} + ${AI_COMPAT_BINARY_FILE} + MAIN_DEPENDENCY ${AI_COMPAT_SOURCE_FILE} + COMMENT "Copying ${AI_COMPAT_SOURCE_FILE_NAME}" + ) + + list(APPEND AI_COMPAT_BINARY_FILES ${AI_COMPAT_BINARY_FILE}) +endforeach() + +# Create a new target which copies all compat files +add_custom_target(ai_compat_files + DEPENDS ${AI_COMPAT_BINARY_FILES} +) + +add_dependencies(openttd + ai_compat_files +) diff --git a/bin/game/CMakeLists.txt b/bin/game/CMakeLists.txt new file mode 100644 index 0000000000..508254e1f2 --- /dev/null +++ b/bin/game/CMakeLists.txt @@ -0,0 +1,37 @@ +set(GS_COMPAT_SOURCE_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/compat_1.2.nut + ${CMAKE_CURRENT_SOURCE_DIR}/compat_1.3.nut + ${CMAKE_CURRENT_SOURCE_DIR}/compat_1.4.nut + ${CMAKE_CURRENT_SOURCE_DIR}/compat_1.5.nut + ${CMAKE_CURRENT_SOURCE_DIR}/compat_1.6.nut + ${CMAKE_CURRENT_SOURCE_DIR}/compat_1.7.nut + ${CMAKE_CURRENT_SOURCE_DIR}/compat_1.8.nut + ${CMAKE_CURRENT_SOURCE_DIR}/compat_1.9.nut + ${CMAKE_CURRENT_SOURCE_DIR}/compat_1.10.nut + ${CMAKE_CURRENT_SOURCE_DIR}/compat_1.11.nut + ${CMAKE_CURRENT_SOURCE_DIR}/compat_1.12.nut +) + +foreach(GS_COMPAT_SOURCE_FILE IN LISTS GS_COMPAT_SOURCE_FILES) + string(REPLACE "${CMAKE_SOURCE_DIR}/bin/" "" GS_COMPAT_SOURCE_FILE_NAME "${GS_COMPAT_SOURCE_FILE}") + string(CONCAT GS_COMPAT_BINARY_FILE "${CMAKE_BINARY_DIR}/" "${GS_COMPAT_SOURCE_FILE_NAME}") + + add_custom_command(OUTPUT ${GS_COMPAT_BINARY_FILE} + COMMAND ${CMAKE_COMMAND} -E copy + ${GS_COMPAT_SOURCE_FILE} + ${GS_COMPAT_BINARY_FILE} + MAIN_DEPENDENCY ${GS_COMPAT_SOURCE_FILE} + COMMENT "Copying ${GS_COMPAT_SOURCE_FILE_NAME}" + ) + + list(APPEND GS_COMPAT_BINARY_FILES ${GS_COMPAT_BINARY_FILE}) +endforeach() + +# Create a new target which copies all compat files +add_custom_target(gs_compat_files + DEPENDS ${GS_COMPAT_BINARY_FILES} +) + +add_dependencies(openttd + gs_compat_files +) diff --git a/cmake/InstallAndPackage.cmake b/cmake/InstallAndPackage.cmake index 0b1dc14933..fa36518d5b 100644 --- a/cmake/InstallAndPackage.cmake +++ b/cmake/InstallAndPackage.cmake @@ -26,8 +26,8 @@ install(TARGETS openttd install(DIRECTORY ${CMAKE_BINARY_DIR}/lang ${CMAKE_BINARY_DIR}/baseset - ${CMAKE_SOURCE_DIR}/bin/ai - ${CMAKE_SOURCE_DIR}/bin/game + ${CMAKE_BINARY_DIR}/ai + ${CMAKE_BINARY_DIR}/game ${CMAKE_SOURCE_DIR}/bin/scripts DESTINATION ${DATA_DESTINATION_DIR} COMPONENT language_files) From ec0c1595868056a9767f581df1a285ce2e37fe88 Mon Sep 17 00:00:00 2001 From: translators Date: Fri, 2 Apr 2021 17:53:17 +0000 Subject: [PATCH 005/268] Update: Translations from eints korean: 1 change by telk5093 indonesian: 25 changes by dimaspaf14 romanian: 23 changes by kneekoo --- src/lang/indonesian.txt | 25 +++++++++++++++++++++++++ src/lang/korean.txt | 2 +- src/lang/romanian.txt | 24 +++++++++++++++++++++++- 3 files changed, 49 insertions(+), 2 deletions(-) diff --git a/src/lang/indonesian.txt b/src/lang/indonesian.txt index 69f0f6abaa..2c13e4243a 100644 --- a/src/lang/indonesian.txt +++ b/src/lang/indonesian.txt @@ -946,6 +946,7 @@ STR_GAME_OPTIONS_CURRENCY_NTD :Dollar Taiwan B STR_GAME_OPTIONS_CURRENCY_CNY :Renminbi Cina (CNY) STR_GAME_OPTIONS_CURRENCY_HKD :Dollar Hong Kong (HKD) STR_GAME_OPTIONS_CURRENCY_INR :India Rupee (INR) +STR_GAME_OPTIONS_CURRENCY_MYR :Ringgit Malaysia (MYR) ############ end of currency region STR_GAME_OPTIONS_ROAD_VEHICLES_DROPDOWN_LEFT :Berkendara di lajur kiri @@ -999,6 +1000,8 @@ STR_GAME_OPTIONS_RESOLUTION :{BLACK}Resolusi STR_GAME_OPTIONS_RESOLUTION_TOOLTIP :{BLACK}Pilih resolusi layar yang diinginkan STR_GAME_OPTIONS_RESOLUTION_OTHER :lainnya +STR_GAME_OPTIONS_VIDEO_ACCELERATION :{BLACK}Akselerasi perangkat keras +STR_GAME_OPTIONS_VIDEO_ACCELERATION_RESTART :{WHITE}Pengaturan hanya akan berlaku setelah game dimulai ulang STR_GAME_OPTIONS_GUI_ZOOM_FRAME :{BLACK}Ukuran antarmuka STR_GAME_OPTIONS_GUI_ZOOM_DROPDOWN_TOOLTIP :{BLACK}Pilih ukuran elemen antarmuka yang akan digunakan @@ -1016,7 +1019,10 @@ STR_GAME_OPTIONS_FONT_ZOOM_DROPDOWN_NORMAL :Normal STR_GAME_OPTIONS_FONT_ZOOM_DROPDOWN_2X_ZOOM :Kali dua STR_GAME_OPTIONS_FONT_ZOOM_DROPDOWN_4X_ZOOM :4 kali +STR_GAME_OPTIONS_GRAPHICS :{BLACK}Grafik +STR_GAME_OPTIONS_REFRESH_RATE :{BLACK}Menampilkan kecepatan refresh +STR_GAME_OPTIONS_REFRESH_RATE_WARNING :{WHITE}Kecepatan refresh yang lebih tinggi dari 60Hz dapat memengaruhi kinerja. STR_GAME_OPTIONS_BASE_GRF :{BLACK}Set Grafik Dasar STR_GAME_OPTIONS_BASE_GRF_TOOLTIP :{BLACK}Pilih grafik dasar yang digunakan @@ -1112,6 +1118,8 @@ STR_TERRAIN_TYPE_FLAT :Datar STR_TERRAIN_TYPE_HILLY :Berbukit STR_TERRAIN_TYPE_MOUNTAINOUS :Pegunungan STR_TERRAIN_TYPE_ALPINIST :Pemanjat Gunung +STR_TERRAIN_TYPE_CUSTOM :Ketinggian Kustom +STR_TERRAIN_TYPE_CUSTOM_VALUE :Ketinggian Kustom ({NUM}) STR_CITY_APPROVAL_PERMISSIVE :Selalu boleh STR_CITY_APPROVAL_TOLERANT :Toleran @@ -1193,6 +1201,8 @@ STR_CONFIG_SETTING_DISASTERS_HELPTEXT :Mengaktifkan be STR_CONFIG_SETTING_CITY_APPROVAL :Sikap pemerintah kota terhadap restrukturasi area: {STRING} STR_CONFIG_SETTING_CITY_APPROVAL_HELPTEXT :Pilih seberapa banyak dampak kebisingan dan lingkungan oleh perusahaan terhadap peringkat kota karena pembangunan di daerah +STR_CONFIG_SETTING_MAP_HEIGHT_LIMIT :Ketinggian peta maksimum: {STRING} +STR_CONFIG_SETTING_MAP_HEIGHT_LIMIT_HELPTEXT :Tetapkan ketinggian maksimum medan peta. Dengan "(otomatis)" nilai yang baik akan diambil setelah pembuatan medan STR_CONFIG_SETTING_TOO_HIGH_MOUNTAIN :{WHITE}Kamu tidak bisa mengubah ketinggian peta maksimum di angka itu. Setidaknya ada satu gunung di peta yang lebih tinggi STR_CONFIG_SETTING_AUTOSLOPE :Ijinkan pembentukan slop dibawah bangunan, rel, dsb.: {STRING} STR_CONFIG_SETTING_AUTOSLOPE_HELPTEXT :Ijinkan pembentukan tanah dibawah bangunan dan trek tanpa merusaknya @@ -1338,6 +1348,9 @@ STR_CONFIG_SETTING_OIL_REF_EDGE_DISTANCE :Jarak maksimal STR_CONFIG_SETTING_OIL_REF_EDGE_DISTANCE_HELPTEXT :Kilang minyak hanya dibangun pada tepi peta atau pantai STR_CONFIG_SETTING_SNOWLINE_HEIGHT :Tinggi garis salju: {STRING} STR_CONFIG_SETTING_SNOWLINE_HEIGHT_HELPTEXT :Mengatur ketinggian di mana salju akan muncul. Salju juga akan mempengaruhi pengembangan industri dan persyaratan untuk pertumbuhan kota +STR_CONFIG_SETTING_DESERT_COVERAGE :Cakupan gurun: {STRING} +STR_CONFIG_SETTING_DESERT_COVERAGE_HELPTEXT :Kontrol perkiraan jumlah gurun di lanskap tropis. Gurun juga mempengaruhi generasi industri. Hanya digunakan selama pembuatan peta +STR_CONFIG_SETTING_DESERT_COVERAGE_VALUE :{NUM}% STR_CONFIG_SETTING_ROUGHNESS_OF_TERRAIN :Kekasaran daratan: {STRING} STR_CONFIG_SETTING_ROUGHNESS_OF_TERRAIN_HELPTEXT :(hanya TerraGenesis) Memilih frekuensi bukit: Bentang darat rata punya bukit yang lebih sedikit dan lebih lebar. Bentang darat bergunung punya lebih banyak bukit, dan ini mungkin akan terlihat lebih membosankan STR_CONFIG_SETTING_ROUGHNESS_OF_TERRAIN_VERY_SMOOTH :Sangat halus @@ -1643,12 +1656,15 @@ STR_CONFIG_SETTING_ZOOM_MIN :Tingkat Perbesa STR_CONFIG_SETTING_ZOOM_MIN_HELPTEXT :Perbesaran viewport maksimal. Semakin besar semakin banyak memori yang dibutuhkan STR_CONFIG_SETTING_ZOOM_MAX :Tingkat zoom out Maksimal: {STRING} STR_CONFIG_SETTING_ZOOM_MAX_HELPTEXT :Pengecilan maksimum untuk viewport. Semakin kecil semakin tidak jelas +STR_CONFIG_SETTING_SPRITE_ZOOM_MIN :Sprite resolusi tertinggi untuk digunakan: {STRING} STR_CONFIG_SETTING_ZOOM_LVL_MIN :4x STR_CONFIG_SETTING_ZOOM_LVL_IN_2X :2x STR_CONFIG_SETTING_ZOOM_LVL_NORMAL :Normal STR_CONFIG_SETTING_ZOOM_LVL_OUT_2X :2x STR_CONFIG_SETTING_ZOOM_LVL_OUT_4X :4x STR_CONFIG_SETTING_ZOOM_LVL_OUT_8X :8x +STR_CONFIG_SETTING_SPRITE_ZOOM_LVL_IN_2X :2x +STR_CONFIG_SETTING_SPRITE_ZOOM_LVL_NORMAL :1x STR_CONFIG_SETTING_TOWN_GROWTH :Kecepatan pertumbuhan kota: {STRING} STR_CONFIG_SETTING_TOWN_GROWTH_HELPTEXT :Kecepatan pertumbuhan kota STR_CONFIG_SETTING_TOWN_GROWTH_NONE :Tidak tumbuh @@ -1783,6 +1799,7 @@ STR_CONFIG_ERROR_OUT_OF_MEMORY :{WHITE}Kehabisa STR_CONFIG_ERROR_SPRITECACHE_TOO_BIG :{WHITE}Mengalokasikan {BYTES} 'spritecache' gagal. 'Spritecache' dikurangi ke {BYTES}. Ini akan kurangi kinerja OpenTTD. Untuk kurangi kebutuhan memori anda bisa coba matikan grafik 32bpp dan/atau tingkat pembesaran # Video initalization errors +STR_VIDEO_DRIVER_ERROR_NO_HARDWARE_ACCELERATION :{WHITE}... tidak ditemukan GPU yang kompatibel. Akselerasi perangkat keras dinonaktifkan # Intro window STR_INTRO_CAPTION :{WHITE}OpenTTD {REV} @@ -2314,6 +2331,8 @@ STR_MISSING_GRAPHICS_SET_MESSAGE :{BLACK}OpenTTD STR_MISSING_GRAPHICS_YES_DOWNLOAD :{BLACK}Ya, download file gambar STR_MISSING_GRAPHICS_NO_QUIT :{BLACK}Tidak, tutup OpenTTD +STR_MISSING_GRAPHICS_ERROR_TITLE :{WHITE}Gagal Mendownload +STR_MISSING_GRAPHICS_ERROR_QUIT :{BLACK}Keluar OpenTTD # Transparency settings window STR_TRANSPARENCY_CAPTION :{WHITE}Pengaturan Transparasi @@ -2864,6 +2883,9 @@ STR_MAPGEN_BY :{BLACK}* STR_MAPGEN_NUMBER_OF_TOWNS :{BLACK}Jumlah Kota: STR_MAPGEN_DATE :{BLACK}Tgl: STR_MAPGEN_NUMBER_OF_INDUSTRIES :{BLACK}Jumlah industri: +STR_MAPGEN_HEIGHTMAP_HEIGHT_DOWN :{BLACK}Kurangi satu ketinggian maksimum puncak tertinggi di peta +STR_MAPGEN_SNOW_COVERAGE_DOWN :{BLACK}Kurangi cakupan salju hingga sepuluh persen +STR_MAPGEN_DESERT_COVERAGE :{BLACK}Cakupan gurun: STR_MAPGEN_LAND_GENERATOR :{BLACK}Algoritma pulau: STR_MAPGEN_TERRAIN_TYPE :{BLACK}Jenis dataran: STR_MAPGEN_QUANTITY_OF_SEA_LAKES :{BLACK}Area perairan: @@ -2889,6 +2911,7 @@ STR_MAPGEN_HEIGHTMAP_NAME :{BLACK}Nama Pet STR_MAPGEN_HEIGHTMAP_SIZE_LABEL :{BLACK}Luas: STR_MAPGEN_HEIGHTMAP_SIZE :{ORANGE}{NUM} x {NUM} +STR_MAPGEN_SNOW_COVERAGE_QUERY_CAPT :{WHITE}Cakupan salju (dalam %) STR_MAPGEN_START_DATE_QUERY_CAPT :{WHITE}Ganti Tahun Permulaan # SE Map generation @@ -3179,6 +3202,7 @@ STR_LOCAL_AUTHORITY_ACTION_TOOLTIP_BRIBE :{YELLOW}Kolusi STR_GOALS_CAPTION :{WHITE}{COMPANY} Target STR_GOALS_SPECTATOR_CAPTION :{WHITE}Target Global STR_GOALS_SPECTATOR :Target Global +STR_GOALS_GLOBAL_BUTTON :{BLACK}Global STR_GOALS_TEXT :{ORANGE}{STRING} STR_GOALS_NONE :{ORANGE}- Tidak ada - STR_GOALS_PROGRESS :{ORANGE}{STRING} @@ -3553,6 +3577,7 @@ STR_PURCHASE_INFO_PWAGPOWER_PWAGWEIGHT :{BLACK}Daya Ger STR_PURCHASE_INFO_REFITTABLE_TO :{BLACK}Kargo dapat di ganti untuk: {GOLD}{STRING} STR_PURCHASE_INFO_ALL_TYPES :Semua jenis kargo STR_PURCHASE_INFO_NONE :Tidak Ada +STR_PURCHASE_INFO_ENGINES_ONLY :Hanya Lokomotif STR_PURCHASE_INFO_ALL_BUT :Semua tapi tidak untuk {CARGO_LIST} STR_PURCHASE_INFO_MAX_TE :{BLACK}Traksi Maks.: {GOLD}{FORCE} STR_PURCHASE_INFO_AIRCRAFT_RANGE :{BLACK}Jangkauan: {GOLD}{COMMA} kotak diff --git a/src/lang/korean.txt b/src/lang/korean.txt index fbb9847ceb..483e5a223d 100644 --- a/src/lang/korean.txt +++ b/src/lang/korean.txt @@ -3748,7 +3748,7 @@ STR_DEPOT_MASS_START_HANGAR_TOOLTIP :{BLACK}이 격 STR_DEPOT_SELL_CONFIRMATION_TEXT :{YELLOW}이 안에 있는 모든 차량을 판매하려고 합니다. 계속하시겠습니까? # Engine preview window -STR_ENGINE_PREVIEW_CAPTION :{WHITE}차량 개발자로부터의 메시지 +STR_ENGINE_PREVIEW_CAPTION :{WHITE}차량 개발자가 보낸 메시지 STR_ENGINE_PREVIEW_MESSAGE :{GOLD}저희는 이제 막 새로운 {STRING}{G 0 "을" "를"} 개발했습니다. 1년 먼저 이 차량을 사용하셔서 모두에게 공개되기 전에 잘 작동하는지 확인해주시겠습니까? STR_ENGINE_PREVIEW_RAILROAD_LOCOMOTIVE :{G=f}철도 기관차 diff --git a/src/lang/romanian.txt b/src/lang/romanian.txt index 1f7a8eb9f5..93a1d615f2 100644 --- a/src/lang/romanian.txt +++ b/src/lang/romanian.txt @@ -339,6 +339,7 @@ STR_TOOLBAR_TOOLTIP_ZOOM_THE_VIEW_IN :{BLACK}Măreşt STR_TOOLBAR_TOOLTIP_ZOOM_THE_VIEW_OUT :{BLACK}Micşorează imaginea STR_TOOLBAR_TOOLTIP_BUILD_RAILROAD_TRACK :{BLACK}Construieşte căi ferate STR_TOOLBAR_TOOLTIP_BUILD_ROADS :{BLACK}Construieşte drumuri +STR_TOOLBAR_TOOLTIP_BUILD_TRAMWAYS :{BLACK}Construiește linii de tramvai STR_TOOLBAR_TOOLTIP_BUILD_SHIP_DOCKS :{BLACK}Construieşte porturi STR_TOOLBAR_TOOLTIP_BUILD_AIRPORTS :{BLACK}Construieşte aeroporturi STR_TOOLBAR_TOOLTIP_LANDSCAPING :{BLACK}Afişează instrumentele pentru modelarea terenului, plantarea copacilor, etc. @@ -766,6 +767,7 @@ STR_SMALLMAP_TOOLTIP_ENABLE_ALL_CARGOS :{BLACK}Afișeaz STR_STATUSBAR_TOOLTIP_SHOW_LAST_NEWS :{BLACK}Re-afişează ultimul mesaj STR_STATUSBAR_COMPANY_NAME :{SILVER}- - {COMPANY} - - STR_STATUSBAR_PAUSED :{YELLOW}* * PAUZĂ * * +STR_STATUSBAR_PAUSED_LINK_GRAPH :{ORANGE}* * PAUZĂ (se actualizează graficul conexiunilor) * * STR_STATUSBAR_AUTOSAVE :{RED}SALVARE AUTOMATĂ STR_STATUSBAR_SAVING_GAME :{RED}* * SALVARE JOC * * @@ -925,6 +927,7 @@ STR_GAME_OPTIONS_CURRENCY_CUSTOM :Personalizată. STR_GAME_OPTIONS_CURRENCY_GEL :Lari Georgian (GEL) STR_GAME_OPTIONS_CURRENCY_IRR :Rial Iranian (IRR) STR_GAME_OPTIONS_CURRENCY_RUB :Ruble rusești (RUB) +STR_GAME_OPTIONS_CURRENCY_MXN :Peso Mexican (MXN) ############ end of currency region STR_GAME_OPTIONS_ROAD_VEHICLES_DROPDOWN_LEFT :Pe partea stângă @@ -987,6 +990,7 @@ STR_GAME_OPTIONS_GUI_ZOOM_DROPDOWN_2X_ZOOM :Mărime dublă STR_GAME_OPTIONS_GUI_ZOOM_DROPDOWN_4X_ZOOM :Mărime împătrită +STR_GAME_OPTIONS_FONT_ZOOM_DROPDOWN_AUTO :(auto-detecție) STR_GAME_OPTIONS_FONT_ZOOM_DROPDOWN_2X_ZOOM :Mărime dublă @@ -1108,7 +1112,7 @@ STR_CONFIG_SETTING_TYPE_COMPANY_INGAME :Setări compani STR_CONFIG_SETTING_RESTRICT_CATEGORY :{BLACK}Categorie: STR_CONFIG_SETTING_RESTRICT_TYPE :{BLACK}Tip: -STR_CONFIG_SETTING_RESTRICT_DROPDOWN_HELPTEXT :{BLACK}Arată în lista de mai jos doar setările modificate +STR_CONFIG_SETTING_RESTRICT_DROPDOWN_HELPTEXT :{BLACK}Limitează lista de mai jos doar la setările modificate STR_CONFIG_SETTING_RESTRICT_BASIC :Setări de bază (afişează numai setări importante) STR_CONFIG_SETTING_RESTRICT_ADVANCED :Setări avansate (afişează majoritatea setărilor) STR_CONFIG_SETTING_RESTRICT_ALL :Setări expert (afişează toate setările) @@ -1518,6 +1522,7 @@ STR_CONFIG_SETTING_COLOURED_NEWS_YEAR :Ştirile color STR_CONFIG_SETTING_COLOURED_NEWS_YEAR_HELPTEXT :Anul începând cu care anunțurile din ziar sunt tipărite color. Înainte de acest an, anunturile sunt monocrome (alb/negru) STR_CONFIG_SETTING_STARTING_YEAR :Anul de început al jocului: {STRING} STR_CONFIG_SETTING_ENDING_YEAR_VALUE :{NUM} +STR_CONFIG_SETTING_ECONOMY_TYPE_SMOOTH :Lin STR_CONFIG_SETTING_ALLOW_SHARES :Permite cumpărarea de acţiuni de la alte companii: {STRING} STR_CONFIG_SETTING_ALLOW_SHARES_HELPTEXT :Dacă este activată, se permite cumpărarea și vânzarea de acțiuni ale companiilor. Acțiunile devin disponibile doar când compania depășește o anumită vârstă STR_CONFIG_SETTING_FEEDER_PAYMENT_SHARE :Procentul din profitul pe secţiune care să fie plătit pentru alimentare: {STRING} @@ -1566,6 +1571,7 @@ STR_CONFIG_SETTING_TOWN_CARGOGENMODE_BITCOUNT :Liniar STR_CONFIG_SETTING_EXTRA_TREE_PLACEMENT :Poziţionarea copacilor în joc: {STRING} STR_CONFIG_SETTING_EXTRA_TREE_PLACEMENT_HELPTEXT :Controlează apariția aleatoare a copacilor în joc. Este posibil ca această opțiune să afecteze industrii care depind de creșterea copacilor, cum ar fi fabricile de cherestea +STR_CONFIG_SETTING_EXTRA_TREE_PLACEMENT_SPREAD_ALL :Cresc și se extind peste tot STR_CONFIG_SETTING_TOOLBAR_POS :Poziţia barei principale de instrumente: {STRING} STR_CONFIG_SETTING_TOOLBAR_POS_HELPTEXT :Poziţia orizontală a barei principale în partea de sus a ecranului @@ -1916,6 +1922,7 @@ STR_NETWORK_SERVER_LIST_JOIN_GAME :{BLACK}Intră STR_NETWORK_SERVER_LIST_REFRESH :{BLACK}Actualizează serverul STR_NETWORK_SERVER_LIST_REFRESH_TOOLTIP :{BLACK}Actualizează informaţiile despre server +STR_NETWORK_SERVER_LIST_SEARCH_SERVER_LAN :{BLACK}Caută în LAN STR_NETWORK_SERVER_LIST_ADD_SERVER :{BLACK}Adaugă un server STR_NETWORK_SERVER_LIST_ADD_SERVER_TOOLTIP :{BLACK}Adaugă un server la lista care va fi verificată pentru jocuri active STR_NETWORK_SERVER_LIST_START_SERVER :{BLACK}Porneşte serverul @@ -2156,6 +2163,7 @@ STR_NETWORK_MESSAGE_NAME_CHANGE :*** {STRING} ş STR_NETWORK_MESSAGE_GIVE_MONEY :*** {STRING} a dat {2:CURRENCY_LONG} către {1:STRING} STR_NETWORK_MESSAGE_SERVER_SHUTDOWN :{WHITE}Serverul a închis conexiunea STR_NETWORK_MESSAGE_SERVER_REBOOT :{WHITE}Serverul este repornit...{}Vă rugăm aşteptaţi... +STR_NETWORK_MESSAGE_KICKED :*** {STRING} a fost dat afară. Motiv: ({STRING}) # Content downloading window STR_CONTENT_TITLE :{WHITE}Descărcare resurse online @@ -2375,6 +2383,7 @@ STR_ROAD_TOOLBAR_TOOLTIP_BUILD_ROAD_TUNNEL :{BLACK}Construi STR_ROAD_TOOLBAR_TOOLTIP_BUILD_TRAMWAY_TUNNEL :{BLACK}Construieşte tunel pentru tramvaie. Shift comută între construire/afişare cost estimat STR_ROAD_TOOLBAR_TOOLTIP_TOGGLE_BUILD_REMOVE_FOR_ROAD :{BLACK}Comutator pentru construcţie/înlăturare şosele STR_ROAD_TOOLBAR_TOOLTIP_TOGGLE_BUILD_REMOVE_FOR_TRAMWAYS :{BLACK}Comută construcţie/înlăturare pentru şine de tramvai +STR_ROAD_TOOLBAR_TOOLTIP_CONVERT_TRAM :{BLACK}Convertește/Modernizează tipul tramvaiului. Shift comută construcția/afișarea costului estimat # Road depot construction window @@ -2463,6 +2472,7 @@ STR_TREES_RANDOM_TYPE :{BLACK}Arbori d STR_TREES_RANDOM_TYPE_TOOLTIP :{BLACK}Plantează arbori din diverse specii la întâmplare. Shift comută între plantare/afişare cost estimat STR_TREES_RANDOM_TREES_BUTTON :{BLACK}Arbori aleatori STR_TREES_RANDOM_TREES_TOOLTIP :{BLACK}Plantează aleator arbori pe uscat +STR_TREES_MODE_FOREST_LG_TOOLTIP :{BLACK}Plantează păduri întinse prin tragerea peste peisaj. # Land generation window (SE) STR_TERRAFORM_TOOLBAR_LAND_GENERATION_CAPTION :{WHITE}Generator suprafaţă uscat @@ -2668,8 +2678,10 @@ STR_FRAMERATE_AVERAGE :{WHITE}Medie STR_FRAMERATE_MEMORYUSE :{WHITE}Memorie STR_FRAMERATE_DATA_POINTS :{BLACK}Date bazate pe măsurători {COMMA} STR_FRAMERATE_MS_GOOD :{LTBLUE}{DECIMAL} ms +STR_FRAMERATE_FPS_GOOD :{LTBLUE}{DECIMAL} cadre/s STR_FRAMERATE_FPS_BAD :{RED}{DECIMAL} cadre/s STR_FRAMERATE_BYTES_WARN :{YELLOW}{BYTES} +STR_FRAMERATE_BYTES_BAD :{RED}{BYTES} STR_FRAMERATE_GRAPH_MILLISECONDS :{TINY_FONT}{COMMA} ms ############ Leave those lines in this order!! STR_FRAMERATE_VIDEO :{BLACK}Ieșire video: @@ -2740,6 +2752,7 @@ STR_MAPGEN_HEIGHTMAP_NAME :{BLACK}Nume har STR_MAPGEN_HEIGHTMAP_SIZE_LABEL :{BLACK}Dimensiune: STR_MAPGEN_HEIGHTMAP_SIZE :{ORANGE}{NUM} x {NUM} +STR_MAPGEN_SNOW_COVERAGE_QUERY_CAPT :{WHITE}Acoperire cu zăpadă (în %) STR_MAPGEN_START_DATE_QUERY_CAPT :{WHITE}Modifică anul de început # SE Map generation @@ -2812,7 +2825,9 @@ STR_NEWGRF_SETTINGS_VERSION :{BLACK}Versiune STR_NEWGRF_SETTINGS_MIN_VERSION :{BLACK}Vers. minimă compatibilă: {SILVER}{NUM} STR_NEWGRF_SETTINGS_MD5SUM :{BLACK}MD5sum: {SILVER}{STRING} STR_NEWGRF_SETTINGS_PALETTE :{BLACK}Paletă: {SILVER}{STRING} +STR_NEWGRF_SETTINGS_PALETTE_LEGACY :Legacy (W) STR_NEWGRF_SETTINGS_PARAMETER :{BLACK}Parametri: {SILVER}{STRING} +STR_NEWGRF_SETTINGS_PARAMETER_NONE :Nimic STR_NEWGRF_SETTINGS_NO_INFO :{BLACK}Nicio informaţie disponibilă STR_NEWGRF_SETTINGS_NOT_FOUND :{RED}Niciun fisier potrivit @@ -3012,6 +3027,8 @@ STR_LOCAL_AUTHORITY_ACTION_TOOLTIP_BRIBE :{YELLOW}Mituie # Goal window STR_GOALS_CAPTION :{WHITE}{COMPANY} Scopuri STR_GOALS_SPECTATOR_CAPTION :{WHITE}Scopuri globale +STR_GOALS_GLOBAL_BUTTON_HELPTEXT :{BLACK}Arată scopurile globale +STR_GOALS_COMPANY_BUTTON :{BLACK}Companie STR_GOALS_TEXT :{ORANGE}{STRING} STR_GOALS_NONE :{ORANGE}- Nici unul - STR_GOALS_PROGRESS :{ORANGE}{STRING} @@ -3245,6 +3262,7 @@ STR_COMPANY_INFRASTRUCTURE_VIEW_TOTAL :{WHITE}{CURRENC # Industry directory STR_INDUSTRY_DIRECTORY_CAPTION :{WHITE}Industrii STR_INDUSTRY_DIRECTORY_NONE :{ORANGE}- Nimic- +STR_INDUSTRY_DIRECTORY_ITEM_INFO :{BLACK}{CARGO_LONG}{STRING}{YELLOW} ({COMMA}% transportat){BLACK} STR_INDUSTRY_DIRECTORY_ITEM_NOPROD :{ORANGE}{INDUSTRY} STR_INDUSTRY_DIRECTORY_LIST_CAPTION :{BLACK}Numele industriilor - clic pe nume pentru focalizarea pe industrie. Ctrl+Click deschide o fereastră cu locaţia industriei STR_INDUSTRY_DIRECTORY_FILTER_ALL_TYPES :Toate tipurile de mărfuri @@ -3532,6 +3550,7 @@ STR_REPLACE_HELP_STOP_BUTTON :{BLACK}Apasă a STR_REPLACE_ENGINE_WAGON_SELECT_HELP :{BLACK}Comutã între ferestrele de înlocuire motoare si vagoane STR_REPLACE_ENGINES :Motoare STR_REPLACE_WAGONS :Vagoane +STR_REPLACE_ALL_RAILTYPE :Toate vehiculele pe șine STR_REPLACE_HELP_RAILTYPE :{BLACK}Alege un tip de cale ferată pentru care doreşti să înlocuieşti locomotivele STR_REPLACE_HELP_REPLACE_INFO_TAB :{BLACK}Arată locomotiva ceva înlocui locomotiva selectată în stânga @@ -3580,6 +3599,7 @@ STR_VEHICLE_VIEW_ROAD_VEHICLE_SHOW_DETAILS_TOOLTIP :{BLACK}Afişeaz STR_VEHICLE_VIEW_SHIP_SHOW_DETAILS_TOOLTIP :{BLACK}Afişează detaliile navei STR_VEHICLE_VIEW_AIRCRAFT_SHOW_DETAILS_TOOLTIP :{BLACK}Afişează detaliile aeronavei +STR_VEHICLE_VIEW_TRAIN_STATUS_START_STOP_TOOLTIP :{BLACK}Acțiunea trenului actual - clic pentru oprirea/pornirea trenului # Messages in the start stop button in the vehicle view @@ -3753,6 +3773,7 @@ STR_ORDER_CONDITIONAL_AGE :Vechime (ani) STR_ORDER_CONDITIONAL_REQUIRES_SERVICE :Necesită service STR_ORDER_CONDITIONAL_UNCONDITIONALLY :Întotdeauna STR_ORDER_CONDITIONAL_REMAINING_LIFETIME :Durată de viaţă rămasă +STR_ORDER_CONDITIONAL_MAX_RELIABILITY :Fiabilitate maximă STR_ORDER_CONDITIONAL_COMPARATOR_TOOLTIP :{BLACK}Cum se face compararea datelor din vehicul cu valoarea dată STR_ORDER_CONDITIONAL_COMPARATOR_EQUALS :este egal cu @@ -4047,6 +4068,7 @@ STR_GAME_SAVELOAD_ERROR_TOO_NEW_SAVEGAME :Salvarea a fost STR_GAME_SAVELOAD_ERROR_FILE_NOT_READABLE :Fişierul nu poate fi citit STR_GAME_SAVELOAD_ERROR_FILE_NOT_WRITEABLE :Fişierul nu poate fi scris STR_GAME_SAVELOAD_ERROR_DATA_INTEGRITY_CHECK_FAILED :Integritatea datelor compromisă +STR_GAME_SAVELOAD_ERROR_PATCHPACK :Salvarea este realizată cu o versiune modificată STR_GAME_SAVELOAD_NOT_AVAILABLE : STR_WARNING_LOADGAME_REMOVED_TRAMS :{WHITE}Jocul a fost salvat într-o versiune fără suport pentru tramvaie. Toate tramvaiele au fost eliminate From 5b1ea0ee387727a6cbae61b304d6871ce9318435 Mon Sep 17 00:00:00 2001 From: translators Date: Sat, 3 Apr 2021 17:52:59 +0000 Subject: [PATCH 006/268] Update: Translations from eints swedish: 1 change by DonaldDuck313 norwegian (bokmal): 1 change by Anolitt english (us): 1 change by 2TallTyler chinese (simplified): 8 changes by RichardYan314 german: 1 change by danidoedel romanian: 115 changes by kneekoo finnish: 1 change by hpiirai spanish: 2 changes by MontyMontana polish: 3 changes by yazalo --- src/lang/english_US.txt | 2 +- src/lang/finnish.txt | 2 +- src/lang/german.txt | 2 +- src/lang/norwegian_bokmal.txt | 2 +- src/lang/polish.txt | 6 +- src/lang/romanian.txt | 144 +++++++++++++++++++++++++------- src/lang/simplified_chinese.txt | 8 ++ src/lang/spanish.txt | 4 +- src/lang/swedish.txt | 2 +- 9 files changed, 133 insertions(+), 39 deletions(-) diff --git a/src/lang/english_US.txt b/src/lang/english_US.txt index 138f82e414..33c67d54e2 100644 --- a/src/lang/english_US.txt +++ b/src/lang/english_US.txt @@ -2531,7 +2531,7 @@ STR_WATERWAYS_TOOLBAR_BUILD_DOCK_TOOLTIP :{BLACK}Build sh STR_WATERWAYS_TOOLBAR_BUOY_TOOLTIP :{BLACK}Place a buoy which can be used as a waypoint. Shift toggles building/showing cost estimate STR_WATERWAYS_TOOLBAR_BUILD_AQUEDUCT_TOOLTIP :{BLACK}Build aqueduct. Shift toggles building/showing cost estimate STR_WATERWAYS_TOOLBAR_CREATE_LAKE_TOOLTIP :{BLACK}Define water area.{}Make a canal. If Ctrl is held down at sea level, it will flood the surroundings instead -STR_WATERWAYS_TOOLBAR_CREATE_RIVER_TOOLTIP :{BLACK}Place rivers +STR_WATERWAYS_TOOLBAR_CREATE_RIVER_TOOLTIP :{BLACK}Place rivers. Ctrl selects the area diagonally # Ship depot construction window STR_DEPOT_BUILD_SHIP_CAPTION :{WHITE}Ship Depot Orientation diff --git a/src/lang/finnish.txt b/src/lang/finnish.txt index f4ae079e84..dd239bce20 100644 --- a/src/lang/finnish.txt +++ b/src/lang/finnish.txt @@ -2531,7 +2531,7 @@ STR_WATERWAYS_TOOLBAR_BUILD_DOCK_TOOLTIP :{BLACK}Rakenna STR_WATERWAYS_TOOLBAR_BUOY_TOOLTIP :{BLACK}Sijoita poiju, jota voi käyttää reittipisteenä. Shift vaihtaa rakennustilan ja kustannusarvion välillä STR_WATERWAYS_TOOLBAR_BUILD_AQUEDUCT_TOOLTIP :{BLACK}Rakenna akvedukti. Shift vaihtaa rakennustilan ja kustannusarvion välillä STR_WATERWAYS_TOOLBAR_CREATE_LAKE_TOOLTIP :{BLACK}Määrittele vesialue.{}Tee kanava, paitsi jos Ctrl on painettuna merenpinnalla. Tällöin meri laajenee ympäristöön -STR_WATERWAYS_TOOLBAR_CREATE_RIVER_TOOLTIP :{BLACK}Sijoita jokia +STR_WATERWAYS_TOOLBAR_CREATE_RIVER_TOOLTIP :{BLACK}Sijoita jokia. Ctrl valitsee alueen vinottain. # Ship depot construction window STR_DEPOT_BUILD_SHIP_CAPTION :{WHITE}Telakan suunta diff --git a/src/lang/german.txt b/src/lang/german.txt index 2fbd98b795..b7df032f08 100644 --- a/src/lang/german.txt +++ b/src/lang/german.txt @@ -2532,7 +2532,7 @@ STR_WATERWAYS_TOOLBAR_BUILD_DOCK_TOOLTIP :{BLACK}Hafen ba STR_WATERWAYS_TOOLBAR_BUOY_TOOLTIP :{BLACK}Positionsboje, wird als Wegpunkt benötigt. Umschalt schaltet zwischen Bauen und Kostenvoranschlag um STR_WATERWAYS_TOOLBAR_BUILD_AQUEDUCT_TOOLTIP :{BLACK}Aquädukt bauen. Umschalt schaltet zwischen Bauen und Kostenvoranschlag um STR_WATERWAYS_TOOLBAR_CREATE_LAKE_TOOLTIP :{BLACK}Wasser erzeugen.{}Baut einen Kanal. Auf Meereshöhe wird bei gedrückter Strg-Taste ein Wasserfeld erzeugt, welches umliegendes Land flutet -STR_WATERWAYS_TOOLBAR_CREATE_RIVER_TOOLTIP :{BLACK}Flüsse platzieren +STR_WATERWAYS_TOOLBAR_CREATE_RIVER_TOOLTIP :{BLACK}Flüsse platzieren. Strg wählt das Gebiet diagonal # Ship depot construction window STR_DEPOT_BUILD_SHIP_CAPTION :{WHITE}Ausrichtung der Werft diff --git a/src/lang/norwegian_bokmal.txt b/src/lang/norwegian_bokmal.txt index 3cec1042dc..1e1e00695e 100644 --- a/src/lang/norwegian_bokmal.txt +++ b/src/lang/norwegian_bokmal.txt @@ -2535,7 +2535,7 @@ STR_WATERWAYS_TOOLBAR_BUILD_DOCK_TOOLTIP :{BLACK}Bygg hav STR_WATERWAYS_TOOLBAR_BUOY_TOOLTIP :{BLACK}Plasser en bøye, som kan brukes til å danne kontrollpunkter. Shift slår av/på kostnadsestimat STR_WATERWAYS_TOOLBAR_BUILD_AQUEDUCT_TOOLTIP :{BLACK}Bygg akvedukt. Shift slår av/på kostnadsestimat STR_WATERWAYS_TOOLBAR_CREATE_LAKE_TOOLTIP :{BLACK}Definer vannområde.{}Lag en kanal, unntatt hvis Ctrl holdes nede på havnivå, hvorpå området rundt vil fylles istedenfor -STR_WATERWAYS_TOOLBAR_CREATE_RIVER_TOOLTIP :{BLACK}Plasser elver +STR_WATERWAYS_TOOLBAR_CREATE_RIVER_TOOLTIP :{BLACK}Plasser elver. Ctrl velger området diagonalt # Ship depot construction window STR_DEPOT_BUILD_SHIP_CAPTION :{WHITE}Skipsdokkens retning diff --git a/src/lang/polish.txt b/src/lang/polish.txt index 8860ae62ab..373e06e51c 100644 --- a/src/lang/polish.txt +++ b/src/lang/polish.txt @@ -2406,10 +2406,10 @@ STR_NETWORK_SERVER_LIST_JOIN_GAME :{BLACK}Przyłą STR_NETWORK_SERVER_LIST_REFRESH :{BLACK}Odśwież serwer STR_NETWORK_SERVER_LIST_REFRESH_TOOLTIP :{BLACK}Odśwież informacje o serwerze -STR_NETWORK_SERVER_LIST_SEARCH_SERVER_INTERNET :Przeszukaj internet +STR_NETWORK_SERVER_LIST_SEARCH_SERVER_INTERNET :{BLACK}Przeszukaj internet STR_NETWORK_SERVER_LIST_SEARCH_SERVER_INTERNET_TOOLTIP :{BLACK}Znajdź w internecie serwery publiczne -STR_NETWORK_SERVER_LIST_SEARCH_SERVER_LAN :Przeszukaj LAN -STR_NETWORK_SERVER_LIST_SEARCH_SERVER_LAN_TOOLTIP :Znajdź serwery w sieci lokalnej +STR_NETWORK_SERVER_LIST_SEARCH_SERVER_LAN :{BLACK}Przeszukaj LAN +STR_NETWORK_SERVER_LIST_SEARCH_SERVER_LAN_TOOLTIP :{BLACK}Znajdź serwery w sieci lokalnej STR_NETWORK_SERVER_LIST_ADD_SERVER :{BLACK}Zapisz serwer STR_NETWORK_SERVER_LIST_ADD_SERVER_TOOLTIP :{BLACK}Zapisz serwer na liście serwerów, które będą zawsze sprawdzane w poszukiwaniu uruchomionych gier STR_NETWORK_SERVER_LIST_START_SERVER :{BLACK}Uruchom serwer diff --git a/src/lang/romanian.txt b/src/lang/romanian.txt index 93a1d615f2..4374816aee 100644 --- a/src/lang/romanian.txt +++ b/src/lang/romanian.txt @@ -164,7 +164,7 @@ STR_ABBREV_ALL :{TINY_FONT}TOT # 'Mode' of transport for cargoes STR_PASSENGERS :{COMMA} călător{P "" i} -STR_BAGS :{COMMA} sac{P "" i} +STR_BAGS :{COMMA}{NBSP}sac{P "" i} STR_TONS :{COMMA} tone STR_LITERS :{COMMA} litri STR_ITEMS :{COMMA} bucăți @@ -312,8 +312,12 @@ STR_SORT_BY_CARGO_CAPACITY :Capacitate înc STR_SORT_BY_RANGE :Raza de acțiune STR_SORT_BY_POPULATION :Populaţia STR_SORT_BY_RATING :Cotaţie +STR_SORT_BY_NUM_VEHICLES :Număr de vehicule +STR_SORT_BY_TOTAL_PROFIT_LAST_YEAR :Profit total în anul trecut +STR_SORT_BY_AVERAGE_PROFIT_LAST_YEAR :Profit mediu în anul trecut # Group by options for vehicle list +STR_GROUP_BY_SHARED_ORDERS :Comenzi comune # Tooltips for the main toolbar STR_TOOLBAR_TOOLTIP_PAUSE_GAME :{BLACK}Pauză joc @@ -331,10 +335,10 @@ STR_TOOLBAR_TOOLTIP_DISPLAY_GOALS_LIST :{BLACK}Afişeaz STR_TOOLBAR_TOOLTIP_DISPLAY_GRAPHS :{BLACK}Afişează grafice STR_TOOLBAR_TOOLTIP_DISPLAY_COMPANY_LEAGUE :{BLACK}Afişează clasamentul companiilor STR_TOOLBAR_TOOLTIP_FUND_CONSTRUCTION_OF_NEW :{BLACK}Listează sau fondează obiectivele industriale -STR_TOOLBAR_TOOLTIP_DISPLAY_LIST_OF_COMPANY_TRAINS :{BLACK}Afişează lista cu trenurile companiei. Ctrl+Click alternează deschiderea listei cu grupuri/vehicule -STR_TOOLBAR_TOOLTIP_DISPLAY_LIST_OF_COMPANY_ROAD_VEHICLES :{BLACK}Afişează lista cu autovehiculele companiei. Ctrl+Click alternează deschiderea listei cu grupuri/vehicule -STR_TOOLBAR_TOOLTIP_DISPLAY_LIST_OF_COMPANY_SHIPS :{BLACK}Afişează lista cu navele companiei. Ctrl+Click alternează deschiderea listei cu grupuri/vehicule -STR_TOOLBAR_TOOLTIP_DISPLAY_LIST_OF_COMPANY_AIRCRAFT :{BLACK}Afişează lista cu aeronavele companiei. Ctrl+Click alternează deschiderea listei cu grupuri/vehicule +STR_TOOLBAR_TOOLTIP_DISPLAY_LIST_OF_COMPANY_TRAINS :{BLACK}Afișează lista cu trenurile companiei. Ctrl+clic comută afișarea listei cu grupuri/vehicule +STR_TOOLBAR_TOOLTIP_DISPLAY_LIST_OF_COMPANY_ROAD_VEHICLES :{BLACK}Afișează lista cu autovehiculele companiei. Ctrl+clic comută afișarea listei cu grupuri/vehicule +STR_TOOLBAR_TOOLTIP_DISPLAY_LIST_OF_COMPANY_SHIPS :{BLACK}Afișează lista cu navele companiei. Ctrl+clic comută afișarea listei cu grupuri/vehicule +STR_TOOLBAR_TOOLTIP_DISPLAY_LIST_OF_COMPANY_AIRCRAFT :{BLACK}Afișează lista cu aeronavele companiei. Ctrl+clic comută afișarea listei cu grupuri/vehicule STR_TOOLBAR_TOOLTIP_ZOOM_THE_VIEW_IN :{BLACK}Măreşte imaginea STR_TOOLBAR_TOOLTIP_ZOOM_THE_VIEW_OUT :{BLACK}Micşorează imaginea STR_TOOLBAR_TOOLTIP_BUILD_RAILROAD_TRACK :{BLACK}Construieşte căi ferate @@ -360,6 +364,7 @@ STR_SCENEDIT_TOOLBAR_LANDSCAPE_GENERATION :{BLACK}Generare STR_SCENEDIT_TOOLBAR_TOWN_GENERATION :{BLACK}Generare oraş STR_SCENEDIT_TOOLBAR_INDUSTRY_GENERATION :{BLACK}Generare industrii STR_SCENEDIT_TOOLBAR_ROAD_CONSTRUCTION :{BLACK}Construcţii rutiere +STR_SCENEDIT_TOOLBAR_TRAM_CONSTRUCTION :{BLACK}Construcție tramvai STR_SCENEDIT_TOOLBAR_PLANT_TREES :{BLACK}Plantează arbori. Shift comută între plantare/afişare cost estimat STR_SCENEDIT_TOOLBAR_PLACE_SIGN :{BLACK}Plasează semn STR_SCENEDIT_TOOLBAR_PLACE_OBJECT :{BLACK}Amplasează obiect. Shift comută între amplasare/afişare cost estimat @@ -444,7 +449,7 @@ STR_RAIL_MENU_MAGLEV_CONSTRUCTION :Construcţie pe ############ range for road construction menu starts STR_ROAD_MENU_ROAD_CONSTRUCTION :Construcţii rutiere -STR_ROAD_MENU_TRAM_CONSTRUCTION :Construcţie tramvai +STR_ROAD_MENU_TRAM_CONSTRUCTION :Construcție tramvai ############ range ends here ############ range for waterways construction menu starts @@ -875,7 +880,7 @@ STR_NEWS_SERVICE_SUBSIDY_AWARDED_DOUBLE :{BIG_FONT}{BLAC STR_NEWS_SERVICE_SUBSIDY_AWARDED_TRIPLE :{BIG_FONT}{BLACK}Subvenţie acordată companiei {STRING}!{}{}Transportul de {STRING} de la {STRING} la {STRING} va aduce încasări triple timp de un an! STR_NEWS_SERVICE_SUBSIDY_AWARDED_QUADRUPLE :{BIG_FONT}{BLACK}Subvenţie acordată companiei {STRING}!{}{}Transportul de {STRING} de la {STRING} la {STRING} va aduce încasări de patru ori mai mari timp de un an! -STR_NEWS_ROAD_REBUILDING :{BIG_FONT}{BLACK}Haos pe străzile din {TOWN}!{}{}Programul finanţat de {STRING} pentru reconstrucţia străzilor aduce 6 luni de haos participanţilor la trafic! +STR_NEWS_ROAD_REBUILDING :{BIG_FONT}{BLACK}Haos pe străzile din {TOWN}!{}{}Programul finanțat de {STRING} pentru reconstrucția străzilor aduce 6 luni de haos participanților la trafic! STR_NEWS_EXCLUSIVE_RIGHTS_TITLE :{BIG_FONT}{BLACK}Monopol de transport! STR_NEWS_EXCLUSIVE_RIGHTS_DESCRIPTION :{BIG_FONT}{BLACK}Autoritatea locală a oraşului {TOWN} semnează un contract cu {STRING} pentru un an de drepturi exclusive de transport! @@ -928,6 +933,9 @@ STR_GAME_OPTIONS_CURRENCY_GEL :Lari Georgian ( STR_GAME_OPTIONS_CURRENCY_IRR :Rial Iranian (IRR) STR_GAME_OPTIONS_CURRENCY_RUB :Ruble rusești (RUB) STR_GAME_OPTIONS_CURRENCY_MXN :Peso Mexican (MXN) +STR_GAME_OPTIONS_CURRENCY_NTD :Noul Dolar Taiwanez (NTD) +STR_GAME_OPTIONS_CURRENCY_CNY :Renminbi Chinezesc (CNY) +STR_GAME_OPTIONS_CURRENCY_INR :Rupia Indiană (INR) ############ end of currency region STR_GAME_OPTIONS_ROAD_VEHICLES_DROPDOWN_LEFT :Pe partea stângă @@ -980,6 +988,7 @@ STR_GAME_OPTIONS_FULLSCREEN_TOOLTIP :{BLACK}Bifează STR_GAME_OPTIONS_RESOLUTION :{BLACK}Rezoluţia ecranului STR_GAME_OPTIONS_RESOLUTION_TOOLTIP :{BLACK}Alege rezoluţia dorită pentru joc STR_GAME_OPTIONS_RESOLUTION_OTHER :(alta/nespecificată) +STR_GAME_OPTIONS_RESOLUTION_ITEM :{NUM}x{NUM} STR_GAME_OPTIONS_GUI_ZOOM_FRAME :{BLACK}Mărime interfată @@ -989,11 +998,14 @@ STR_GAME_OPTIONS_GUI_ZOOM_DROPDOWN_NORMAL :Normală STR_GAME_OPTIONS_GUI_ZOOM_DROPDOWN_2X_ZOOM :Mărime dublă STR_GAME_OPTIONS_GUI_ZOOM_DROPDOWN_4X_ZOOM :Mărime împătrită +STR_GAME_OPTIONS_FONT_ZOOM_DROPDOWN_TOOLTIP :{BLACK}Alege dimensiunea fontului pentru interfață STR_GAME_OPTIONS_FONT_ZOOM_DROPDOWN_AUTO :(auto-detecție) +STR_GAME_OPTIONS_FONT_ZOOM_DROPDOWN_NORMAL :Normal STR_GAME_OPTIONS_FONT_ZOOM_DROPDOWN_2X_ZOOM :Mărime dublă +STR_GAME_OPTIONS_REFRESH_RATE_TOOLTIP :{BLACK}Alegeți rata de reîmprospătare dorită STR_GAME_OPTIONS_BASE_GRF :{BLACK}Set grafic de bază STR_GAME_OPTIONS_BASE_GRF_TOOLTIP :{BLACK}Selectează setul grafic de bază utilizat în joc @@ -1260,6 +1272,7 @@ STR_CONFIG_SETTING_DYNAMIC_ENGINES_EXISTING_VEHICLES :{WHITE}Schimbar STR_CONFIG_SETTING_INFRASTRUCTURE_MAINTENANCE :Mentenanță infrastructură: {STRING} STR_CONFIG_SETTING_INFRASTRUCTURE_MAINTENANCE_HELPTEXT :Cand este activă, infrastructura necesita cheltuieli cu intreținerea. Costurile cresc proporțional cu rețeaua de transport, afectând companiile mari mai mult decât companiile mici +STR_CONFIG_SETTING_COMPANY_STARTING_COLOUR_HELPTEXT :Alegeți culoarea de început pentru companie STR_CONFIG_SETTING_NEVER_EXPIRE_AIRPORTS :Aeroporturile nu expiră niciodată: {STRING} STR_CONFIG_SETTING_NEVER_EXPIRE_AIRPORTS_HELPTEXT :Activarea acestei opțiuni determina ca fiecare tip de aeroport sa fie disponibil permanent, după ce a fost introdus. @@ -1294,6 +1307,7 @@ STR_CONFIG_SETTING_POPULATION_IN_LABEL :Afişează popu STR_CONFIG_SETTING_POPULATION_IN_LABEL_HELPTEXT :Afișează populația orașelor în numele afișate pe hartă STR_CONFIG_SETTING_GRAPH_LINE_THICKNESS :Grosimea liniilor din grafice: {STRING} STR_CONFIG_SETTING_GRAPH_LINE_THICKNESS_HELPTEXT :Grosimea liniilor din grafice. O linie subțire este mai informativă, o linie mai groasă este mai ușor de văzut și are culorile mai usor de distins +STR_CONFIG_SETTING_SHOW_NEWGRF_NAME :Afișează numele NewGRF în fereastra de construcție a vehiculului: {STRING} STR_CONFIG_SETTING_LANDSCAPE :Peisaj: {STRING} STR_CONFIG_SETTING_LANDSCAPE_HELPTEXT :Peisajele definesc scenariile de bază a jocului cu cerințe diferite pentru încărcături și dezvoltare a orașelor. NewGRF și scripturile de joc permit un control mai fin @@ -1304,10 +1318,14 @@ STR_CONFIG_SETTING_LAND_GENERATOR_TERRA_GENESIS :TerraGenesis STR_CONFIG_SETTING_TERRAIN_TYPE :Tip teren: {STRING} STR_CONFIG_SETTING_TERRAIN_TYPE_HELPTEXT :(Doar TerraGenesis) Frecvența dealurilor din peisaj STR_CONFIG_SETTING_INDUSTRY_DENSITY :Densitatea industriei: {STRING} +STR_CONFIG_SETTING_INDUSTRY_DENSITY_HELPTEXT :Stabilește câte industrii ar trebui generate și ce nivel ar trebui întreținut pe durata jocului STR_CONFIG_SETTING_OIL_REF_EDGE_DISTANCE :Distanța maximă de la marginea hărții pentru rafinării: {STRING} STR_CONFIG_SETTING_OIL_REF_EDGE_DISTANCE_HELPTEXT :Rafinăriile de petrol vor fi construite doar la marginea hărţii, sau pe coastă, în cazul harţilor insulare STR_CONFIG_SETTING_SNOWLINE_HEIGHT :Grosimea stratului de zăpadă: {STRING} STR_CONFIG_SETTING_SNOWLINE_HEIGHT_HELPTEXT :Controlează înălțimea de la care zăpada apare în peisajul sub-arctic. De asemenea, zăpada afectează generarea industriilor și cerințele de creștere a orașelor. +STR_CONFIG_SETTING_DESERT_COVERAGE :Acoperire cu deșert: {STRING} +STR_CONFIG_SETTING_DESERT_COVERAGE_HELPTEXT :Controlează întinderea aproximativă de deșert din peisajul tropical. Deșert afectează și generarea industriilor. Parametrul se folosește doar la generarea hărții +STR_CONFIG_SETTING_DESERT_COVERAGE_VALUE :{NUM}% STR_CONFIG_SETTING_ROUGHNESS_OF_TERRAIN :Duritatea terenului (doar pt TerraGenesis) : {STRING} STR_CONFIG_SETTING_ROUGHNESS_OF_TERRAIN_HELPTEXT :(Doar TerraGenesis) Alegeți frecvența dealurilor: Peisajele line au dealuri mai puține și mai întinse. Peisajele dure au multe dealuri și pot arăta repetitiv STR_CONFIG_SETTING_ROUGHNESS_OF_TERRAIN_VERY_SMOOTH :Foarte fin @@ -1315,6 +1333,7 @@ STR_CONFIG_SETTING_ROUGHNESS_OF_TERRAIN_SMOOTH :Fin STR_CONFIG_SETTING_ROUGHNESS_OF_TERRAIN_ROUGH :Dur STR_CONFIG_SETTING_ROUGHNESS_OF_TERRAIN_VERY_ROUGH :Foarte dur STR_CONFIG_SETTING_VARIETY :Distribuția varietății: {STRING} +STR_CONFIG_SETTING_VARIETY_HELPTEXT :(TerraGenesis only) Specifică dacă harta conține și zone muntoase și teren plat. Deoarece aceasta face harta mai plată, alte setări ar trebui să adauge zone muntoase STR_CONFIG_SETTING_RIVER_AMOUNT_HELPTEXT :Alege câte râuri să fie generate STR_CONFIG_SETTING_TREE_PLACER :Algoritm amplasare arbori: {STRING} STR_CONFIG_SETTING_TREE_PLACER_HELPTEXT :Alegeți distribuția copacilor pe hartă: 'Original' plantează copacii dispersați uniform, 'Îmbunătățit' îi plantează grupat @@ -1341,6 +1360,7 @@ STR_CONFIG_SETTING_SMALLMAP_LAND_COLOUR_HELPTEXT :Culoarea terenu STR_CONFIG_SETTING_SMALLMAP_LAND_COLOUR_GREEN :Verde STR_CONFIG_SETTING_SMALLMAP_LAND_COLOUR_DARK_GREEN :Verde închis STR_CONFIG_SETTING_SMALLMAP_LAND_COLOUR_VIOLET :Mov +STR_CONFIG_SETTING_SCROLLMODE_HELPTEXT :Comportamentul derulării hărții STR_CONFIG_SETTING_SCROLLMODE_RMB_LOCKED :Mută harta ținând apăsat click dreapta, poziția cursorului rămânând fixă STR_CONFIG_SETTING_SCROLLMODE_LMB :Mută harta cu clic stânga STR_CONFIG_SETTING_SMOOTH_SCROLLING :Derulare uşoară ecran: {STRING} @@ -1374,6 +1394,8 @@ STR_CONFIG_SETTING_RIGHT_MOUSE_BTN_EMU_COMMAND :Comandă+Click STR_CONFIG_SETTING_RIGHT_MOUSE_BTN_EMU_CONTROL :Control+Click STR_CONFIG_SETTING_RIGHT_MOUSE_BTN_EMU_OFF :Oprit +STR_CONFIG_SETTING_RIGHT_MOUSE_WND_CLOSE :Închidere fereastră la clic-dreapta: {STRING} +STR_CONFIG_SETTING_RIGHT_MOUSE_WND_CLOSE_HELPTEXT :Închide o fereastră prin clic-dreapta înăuntrul ei. Dezactivează sfatul oferit la clic-dreapta! STR_CONFIG_SETTING_AUTOSAVE :Autosalvare: {STRING} STR_CONFIG_SETTING_AUTOSAVE_HELPTEXT :Alege intervalul de timp dintre salvările automate @@ -1414,6 +1436,8 @@ STR_CONFIG_SETTING_PERSISTENT_BUILDINGTOOLS_HELPTEXT :Menține barele STR_CONFIG_SETTING_EXPENSES_LAYOUT :Grupează cheltuielile în raportul financiar al companiei: {STRING} STR_CONFIG_SETTING_EXPENSES_LAYOUT_HELPTEXT :Definește stilul ferestrei care afișează cheltuielile companiei STR_CONFIG_SETTING_AUTO_REMOVE_SIGNALS_HELPTEXT :Elimină automat semnalele când construiești căi ferate dacă ele îți vin în cale. Nu uita că asta ar putea conduce la accidente feroviare. +STR_CONFIG_SETTING_FAST_FORWARD_SPEED_LIMIT :Limita de viteză pentru trecerea timpului: {STRING} +STR_CONFIG_SETTING_FAST_FORWARD_SPEED_LIMIT_VAL :{NUM}% din viteza normală a jocului STR_CONFIG_SETTING_SOUND_TICKER :Afișaj știri: {STRING} STR_CONFIG_SETTING_SOUND_TICKER_HELPTEXT :Redă sunet la afișarea sumarului știrilor @@ -1462,6 +1486,8 @@ STR_CONFIG_SETTING_AI_IN_MULTIPLAYER :Permite Intelig STR_CONFIG_SETTING_AI_IN_MULTIPLAYER_HELPTEXT :Permite ca jucătorii controlați de AI să participe în jocuri multiplayer STR_CONFIG_SETTING_SCRIPT_MAX_OPCODES :Număr opcodes înainte de suspendarea scripturilor: {STRING} STR_CONFIG_SETTING_SCRIPT_MAX_OPCODES_HELPTEXT :Numărul maxim de instrucțiuni pe care un script le poate executa pe parcursul unei ture +STR_CONFIG_SETTING_SCRIPT_MAX_MEMORY :Maximul de memorie utilizată per script: {STRING} +STR_CONFIG_SETTING_SCRIPT_MAX_MEMORY_HELPTEXT :Câtă memorie poate consuma un singur script înainte să fie terminat forțat. Cantitatea necesară ar putea fi mai mare pentru hărți mari. STR_CONFIG_SETTING_SCRIPT_MAX_MEMORY_VALUE :{COMMA} MiB STR_CONFIG_SETTING_SERVINT_ISPERCENT :Intervaluri de service în procente: {STRING} @@ -1522,7 +1548,11 @@ STR_CONFIG_SETTING_COLOURED_NEWS_YEAR :Ştirile color STR_CONFIG_SETTING_COLOURED_NEWS_YEAR_HELPTEXT :Anul începând cu care anunțurile din ziar sunt tipărite color. Înainte de acest an, anunturile sunt monocrome (alb/negru) STR_CONFIG_SETTING_STARTING_YEAR :Anul de început al jocului: {STRING} STR_CONFIG_SETTING_ENDING_YEAR_VALUE :{NUM} +STR_CONFIG_SETTING_ENDING_YEAR_ZERO :Niciodată +STR_CONFIG_SETTING_ECONOMY_TYPE :Tipul economiei: {STRING} +STR_CONFIG_SETTING_ECONOMY_TYPE_ORIGINAL :Original STR_CONFIG_SETTING_ECONOMY_TYPE_SMOOTH :Lin +STR_CONFIG_SETTING_ECONOMY_TYPE_FROZEN :Înghețată STR_CONFIG_SETTING_ALLOW_SHARES :Permite cumpărarea de acţiuni de la alte companii: {STRING} STR_CONFIG_SETTING_ALLOW_SHARES_HELPTEXT :Dacă este activată, se permite cumpărarea și vânzarea de acțiuni ale companiilor. Acțiunile devin disponibile doar când compania depășește o anumită vârstă STR_CONFIG_SETTING_FEEDER_PAYMENT_SHARE :Procentul din profitul pe secţiune care să fie plătit pentru alimentare: {STRING} @@ -1571,6 +1601,7 @@ STR_CONFIG_SETTING_TOWN_CARGOGENMODE_BITCOUNT :Liniar STR_CONFIG_SETTING_EXTRA_TREE_PLACEMENT :Poziţionarea copacilor în joc: {STRING} STR_CONFIG_SETTING_EXTRA_TREE_PLACEMENT_HELPTEXT :Controlează apariția aleatoare a copacilor în joc. Este posibil ca această opțiune să afecteze industrii care depind de creșterea copacilor, cum ar fi fabricile de cherestea +STR_CONFIG_SETTING_EXTRA_TREE_PLACEMENT_NO_SPREAD :Cresc dar nu se extind {RED}(strică fabrica de cherestea) STR_CONFIG_SETTING_EXTRA_TREE_PLACEMENT_SPREAD_ALL :Cresc și se extind peste tot STR_CONFIG_SETTING_TOOLBAR_POS :Poziţia barei principale de instrumente: {STRING} @@ -1586,6 +1617,7 @@ STR_CONFIG_SETTING_SOFT_LIMIT_DISABLED :dezactivat STR_CONFIG_SETTING_ZOOM_MIN :Nivelul maxim de apropiere imagine: {STRING} STR_CONFIG_SETTING_ZOOM_MIN_HELPTEXT :Nivelul maxim de apropiere a câmpului vizual. Luați aminte că nivelele înalte ridică necesarul de memorie STR_CONFIG_SETTING_ZOOM_MAX :Nivelul maxim de îndepărtare imagine: {STRING} +STR_CONFIG_SETTING_SPRITE_ZOOM_MIN :Rezoluția maximă pentru sprite-uri: {STRING} STR_CONFIG_SETTING_ZOOM_LVL_MIN :x4 STR_CONFIG_SETTING_ZOOM_LVL_IN_2X :x2 STR_CONFIG_SETTING_ZOOM_LVL_NORMAL :Normal @@ -1607,6 +1639,7 @@ STR_CONFIG_SETTING_CITY_SIZE_MULTIPLIER :Multiplicator i STR_CONFIG_SETTING_CITY_SIZE_MULTIPLIER_HELPTEXT :Dimensiunea medie a oraşelor mari relativ la oraşele normale, la începutul jocului STR_CONFIG_SETTING_LINKGRAPH_INTERVAL :Actualizează graficul de distribuţie la fiecare {STRING} zi{P 0:2 "" le} +STR_CONFIG_SETTING_LINKGRAPH_INTERVAL_HELPTEXT :Interval de timp între recalculările graficului de conexiuni. Fiecare recalculare calculează planurile unei componente ale graficului. Asta înseamnă că o valoare X pentru această setare nu va duce la actualizarea întregului grafic la fiecare X zile, ci doar o componentă va fi actualizată. Cu cât e mai mică valoarea, cu atât mai timp CPU va fi necesar pentru calcule. Cu cât e mai mare valoarea, cu atât va dura mai mult până va începe distribuția mărfii pe rute noi. STR_CONFIG_SETTING_LINKGRAPH_TIME :Acordă {STRING} zi{P 0:2 "" le} pentru recalcularea graficului de distribuţie STR_CONFIG_SETTING_DISTRIBUTION_MANUAL :manual STR_CONFIG_SETTING_DISTRIBUTION_ASYMMETRIC :asimetric @@ -1617,6 +1650,7 @@ STR_CONFIG_SETTING_DISTRIBUTION_MAIL :Modalitatea de STR_CONFIG_SETTING_DISTRIBUTION_MAIL_HELPTEXT :"simetric" înseamnă că aproximativ aceeași cantitate de poștă va fi expediată din stația A spre stația B, precum de la B la A. "asimetric" presupune expedierea de cantități arbitrare de poștă în fiecare direcție. "manual" înseamnă că repartizarea poștei nu va fi automatizată. STR_CONFIG_SETTING_DISTRIBUTION_ARMOURED :Modalitatea de distribuire pentru clasa de cargo BLINDAT: {STRING} STR_CONFIG_SETTING_DISTRIBUTION_DEFAULT :Modalitatea de distribuire pentru alte clase de cargo: {STRING} +STR_CONFIG_SETTING_DISTRIBUTION_DEFAULT_HELPTEXT :"asimetric" înseamnă că pot fi trimise cantități diferite de marfă în ambele direcții. "manual" înseamnă că nu se va face distribuție automată pentru acele mărfuri. STR_CONFIG_SETTING_LINKGRAPH_ACCURACY :Acurateţea distribuţiei: {STRING} STR_CONFIG_SETTING_DEMAND_DISTANCE :Efectul distanţei asupra cererii: {STRING} STR_CONFIG_SETTING_DEMAND_DISTANCE_HELPTEXT :Dacă setezi această valoare peste 0, distanța dintre stația origine A al mărfii și o posibilă stație B va afecta cantitatea de marfă trimisă din punctul A în B. Cu cât e mai departe B de A cu atât va fi mai mică cantitatea de marfă transportată. Cu cât mărești această valoare, cu atât mai puțină marfă se livrează spre destinațiile îndepărtate si cu atât mai multă la cele mai apropiate. @@ -1670,7 +1704,9 @@ STR_CONFIG_SETTING_ADVISORS :{ORANGE}Știri STR_CONFIG_SETTING_COMPANY :{ORANGE}Companie STR_CONFIG_SETTING_ACCOUNTING :{ORANGE}Contabilitate STR_CONFIG_SETTING_VEHICLES :{ORANGE}Vehicule +STR_CONFIG_SETTING_VEHICLES_PHYSICS :{ORANGE}Fizică STR_CONFIG_SETTING_VEHICLES_ROUTING :{ORANGE}Direcţionare +STR_CONFIG_SETTING_LIMITATIONS :{ORANGE}Limitări STR_CONFIG_SETTING_ACCIDENTS :{ORANGE}Dezastre / Accidente STR_CONFIG_SETTING_GENWORLD :{ORANGE}Generare lume STR_CONFIG_SETTING_ENVIRONMENT :{ORANGE}Mediu @@ -1716,6 +1752,7 @@ STR_CONFIG_ERROR_OUT_OF_MEMORY :{WHITE}Fără m STR_CONFIG_ERROR_SPRITECACHE_TOO_BIG :{WHITE}Nu s-au putut rezerva {BYTES} pentru cache al sprite-urilor. Mărimea cache-ului a fost redusă la {BYTES}. Performanța OpenTTD va fi redusă. Pentru a micșora cerințele jocului cu privire la memorie, poți încerca să dezactivezi modul grafic 32bpp și/sau reducerea numărului de nivele zoom # Video initalization errors +STR_VIDEO_DRIVER_ERROR_NO_HARDWARE_ACCELERATION :{WHITE}... GPU incompatibil. Accelerarea hardware este dezactivată # Intro window STR_INTRO_CAPTION :{WHITE}OpenTTD {REV} @@ -1755,6 +1792,7 @@ STR_INTRO_TOOLTIP_ONLINE_CONTENT :{BLACK}Verific STR_INTRO_TOOLTIP_SCRIPT_SETTINGS :{BLACK}Afişează setările pentru Inteligența Artificială şi pentru Scripturi Joc STR_INTRO_TOOLTIP_QUIT :{BLACK}Ieşi din 'OpenTTD' +STR_INTRO_BASESET :{BLACK}Setul grafic actual are lipsă {NUM} sprite{P "" s}. Verificați actualizările pentru setul de bază. STR_INTRO_TRANSLATION :{BLACK}Acestei traduceri îi lipse{P 0 "şte" "sc"} {NUM} text{P "" e}. Te rugăm să ajuti la îmbunătățirea OpenTTD înrolându-te ca traducător. Citește fișierul readme.txt pentru detalii. # Quit window @@ -1922,6 +1960,7 @@ STR_NETWORK_SERVER_LIST_JOIN_GAME :{BLACK}Intră STR_NETWORK_SERVER_LIST_REFRESH :{BLACK}Actualizează serverul STR_NETWORK_SERVER_LIST_REFRESH_TOOLTIP :{BLACK}Actualizează informaţiile despre server +STR_NETWORK_SERVER_LIST_SEARCH_SERVER_INTERNET_TOOLTIP :{BLACK}Caută servere publice în internet STR_NETWORK_SERVER_LIST_SEARCH_SERVER_LAN :{BLACK}Caută în LAN STR_NETWORK_SERVER_LIST_ADD_SERVER :{BLACK}Adaugă un server STR_NETWORK_SERVER_LIST_ADD_SERVER_TOOLTIP :{BLACK}Adaugă un server la lista care va fi verificată pentru jocuri active @@ -2142,10 +2181,11 @@ STR_NETWORK_ERROR_CLIENT_GUI_LOST_CONNECTION :{WHITE}În ulti STR_NETWORK_SERVER_MESSAGE :*** {1:STRING} ############ Leave those lines in this order!! STR_NETWORK_SERVER_MESSAGE_GAME_PAUSED :Joc în pauză ({STRING}) -STR_NETWORK_SERVER_MESSAGE_GAME_STILL_PAUSED_1 :Jocul este încă în pauză ({STRING}) -STR_NETWORK_SERVER_MESSAGE_GAME_STILL_PAUSED_2 :Jocul este încă în pauză ({STRING}, {STRING}) -STR_NETWORK_SERVER_MESSAGE_GAME_STILL_PAUSED_3 :Jocul este încă în pauză ({STRING}, {STRING}, {STRING}) -STR_NETWORK_SERVER_MESSAGE_GAME_STILL_PAUSED_4 :Jocul este încă în pauză ({STRING}, {STRING}, {STRING}, {STRING}) +STR_NETWORK_SERVER_MESSAGE_GAME_STILL_PAUSED_1 :Jocul încă este în pauză ({STRING}) +STR_NETWORK_SERVER_MESSAGE_GAME_STILL_PAUSED_2 :Jocul încă este în pauză ({STRING}, {STRING}) +STR_NETWORK_SERVER_MESSAGE_GAME_STILL_PAUSED_3 :Jocul încă este în pauză ({STRING}, {STRING}, {STRING}) +STR_NETWORK_SERVER_MESSAGE_GAME_STILL_PAUSED_4 :Jocul încă este în pauză ({STRING}, {STRING}, {STRING}, {STRING}) +STR_NETWORK_SERVER_MESSAGE_GAME_STILL_PAUSED_5 :Jocul încă este în pauză ({STRING}, {STRING}, {STRING}, {STRING}, {STRING}) STR_NETWORK_SERVER_MESSAGE_GAME_UNPAUSED :Jocul continuă ({STRING}) STR_NETWORK_SERVER_MESSAGE_GAME_REASON_NOT_ENOUGH_PLAYERS :număr de jucători STR_NETWORK_SERVER_MESSAGE_GAME_REASON_CONNECTING_CLIENTS :conectare clienţi @@ -2238,6 +2278,7 @@ STR_MISSING_GRAPHICS_SET_MESSAGE :{BLACK}OpenTTD STR_MISSING_GRAPHICS_YES_DOWNLOAD :{BLACK}Da, descarcă pachetele grafice STR_MISSING_GRAPHICS_NO_QUIT :{BLACK}Nu, ieși din OpenTTD +STR_MISSING_GRAPHICS_ERROR :{BLACK}Descărcarea graficii a eșuat.{}Vă rugăm descărcați manual grafica. # Transparency settings window STR_TRANSPARENCY_CAPTION :{WHITE}Optiuni transparenţă @@ -2257,6 +2298,7 @@ STR_LINKGRAPH_LEGEND_CAPTION :{BLACK}Legenda STR_LINKGRAPH_LEGEND_ALL :{BLACK}Toate STR_LINKGRAPH_LEGEND_NONE :{BLACK}Nici una STR_LINKGRAPH_LEGEND_SELECT_COMPANIES :{BLACK}Alege companiile care vor fi afișate +STR_LINKGRAPH_LEGEND_COMPANY_TOOLTIP :{BLACK}{STRING}{}{COMPANY} # Linkgraph legend window and linkgraph legend in smallmap STR_LINKGRAPH_LEGEND_UNUSED :{TINY_FONT}{BLACK}nefolosit @@ -2383,8 +2425,10 @@ STR_ROAD_TOOLBAR_TOOLTIP_BUILD_ROAD_TUNNEL :{BLACK}Construi STR_ROAD_TOOLBAR_TOOLTIP_BUILD_TRAMWAY_TUNNEL :{BLACK}Construieşte tunel pentru tramvaie. Shift comută între construire/afişare cost estimat STR_ROAD_TOOLBAR_TOOLTIP_TOGGLE_BUILD_REMOVE_FOR_ROAD :{BLACK}Comutator pentru construcţie/înlăturare şosele STR_ROAD_TOOLBAR_TOOLTIP_TOGGLE_BUILD_REMOVE_FOR_TRAMWAYS :{BLACK}Comută construcţie/înlăturare pentru şine de tramvai +STR_ROAD_TOOLBAR_TOOLTIP_CONVERT_ROAD :{BLACK}Convertește/Modernizează tipul drumului. Shift comută construcția/afișarea costului estimat STR_ROAD_TOOLBAR_TOOLTIP_CONVERT_TRAM :{BLACK}Convertește/Modernizează tipul tramvaiului. Shift comută construcția/afișarea costului estimat +STR_ROAD_NAME_TRAM :Șină de tramvai # Road depot construction window STR_BUILD_DEPOT_ROAD_ORIENTATION_CAPTION :{WHITE}Orientarea autobazei @@ -2472,6 +2516,8 @@ STR_TREES_RANDOM_TYPE :{BLACK}Arbori d STR_TREES_RANDOM_TYPE_TOOLTIP :{BLACK}Plantează arbori din diverse specii la întâmplare. Shift comută între plantare/afişare cost estimat STR_TREES_RANDOM_TREES_BUTTON :{BLACK}Arbori aleatori STR_TREES_RANDOM_TREES_TOOLTIP :{BLACK}Plantează aleator arbori pe uscat +STR_TREES_MODE_NORMAL_BUTTON :{BLACK}Normal +STR_TREES_MODE_NORMAL_TOOLTIP :{BLACK}Plantează copaci trăgându-i peste peisaj. STR_TREES_MODE_FOREST_LG_TOOLTIP :{BLACK}Plantează păduri întinse prin tragerea peste peisaj. # Land generation window (SE) @@ -2573,6 +2619,7 @@ STR_LANG_AREA_INFORMATION_RAIL_TYPE :{BLACK}Tip șin STR_LANG_AREA_INFORMATION_TRAM_TYPE :{BLACK}Tip de tramvai: {LTBLUE}{STRING} STR_LANG_AREA_INFORMATION_RAIL_SPEED_LIMIT :{BLACK}Limită viteză pe calea ferată: {LTBLUE}{VELOCITY} STR_LANG_AREA_INFORMATION_ROAD_SPEED_LIMIT :{BLACK}Viteza limită a drumului: {LTBLUE}{VELOCITY} +STR_LANG_AREA_INFORMATION_TRAM_SPEED_LIMIT :{BLACK}Limită de viteză tramvai: {LTBLUE}{VELOCITY} # Description of land area of different tiles STR_LAI_CLEAR_DESCRIPTION_ROCKS :Stânci @@ -2673,7 +2720,9 @@ STR_ABOUT_COPYRIGHT_OPENTTD :{BLACK}OpenTTD # Framerate display window STR_FRAMERATE_CAPTION :{WHITE}FPS +STR_FRAMERATE_CAPTION_SMALL :{STRING}{WHITE} ({DECIMAL}x) STR_FRAMERATE_RATE_GAMELOOP_TOOLTIP :{BLACK}Număr de evenimente de joc simulate per secundă. +STR_FRAMERATE_RATE_BLITTER_TOOLTIP :{BLACK}Numărul de cadre video randate per secundă. STR_FRAMERATE_AVERAGE :{WHITE}Medie STR_FRAMERATE_MEMORYUSE :{WHITE}Memorie STR_FRAMERATE_DATA_POINTS :{BLACK}Date bazate pe măsurători {COMMA} @@ -2683,13 +2732,19 @@ STR_FRAMERATE_FPS_BAD :{RED}{DECIMAL} STR_FRAMERATE_BYTES_WARN :{YELLOW}{BYTES} STR_FRAMERATE_BYTES_BAD :{RED}{BYTES} STR_FRAMERATE_GRAPH_MILLISECONDS :{TINY_FONT}{COMMA} ms +STR_FRAMERATE_GRAPH_SECONDS :{TINY_FONT}{COMMA} s ############ Leave those lines in this order!! +STR_FRAMERATE_GL_LINKGRAPH :{BLACK} Decalaj grafic de conexiuni: +STR_FRAMERATE_DRAWING :{BLACK}Randare grafică: +STR_FRAMERATE_DRAWING_VIEWPORTS :{BLACK} Vizoare globale: STR_FRAMERATE_VIDEO :{BLACK}Ieșire video: STR_FRAMERATE_GAMESCRIPT :{BLACK} Script joc: ############ End of leave-in-this-order ############ Leave those lines in this order!! STR_FRAMETIME_CAPTION_GAMELOOP :Buclă de joc STR_FRAMETIME_CAPTION_GL_ECONOMY :Manipularea încărcăturilor +STR_FRAMETIME_CAPTION_DRAWING_VIEWPORTS :Randarea vizorului global +STR_FRAMETIME_CAPTION_AI :IA {NUM} {STRING} ############ End of leave-in-this-order @@ -2727,6 +2782,9 @@ STR_MAPGEN_BY :{BLACK}* STR_MAPGEN_NUMBER_OF_TOWNS :{BLACK}Nr. de oraşe: STR_MAPGEN_DATE :{BLACK}Data: STR_MAPGEN_NUMBER_OF_INDUSTRIES :{BLACK}Nr. de industrii: +STR_MAPGEN_HEIGHTMAP_HEIGHT :{BLACK}Cel mai înalt vârf: +STR_MAPGEN_DESERT_COVERAGE_UP :{BLACK}Mărește întinderea deșertului cu zece procente +STR_MAPGEN_DESERT_COVERAGE_DOWN :{BLACK}Reduce întinderea deșertului cu zece procente STR_MAPGEN_LAND_GENERATOR :{BLACK}Generator de teren: STR_MAPGEN_TERRAIN_TYPE :{BLACK}Tip teren: STR_MAPGEN_QUANTITY_OF_SEA_LAKES :{BLACK}Nivelul mării: @@ -2753,6 +2811,7 @@ STR_MAPGEN_HEIGHTMAP_SIZE_LABEL :{BLACK}Dimensiu STR_MAPGEN_HEIGHTMAP_SIZE :{ORANGE}{NUM} x {NUM} STR_MAPGEN_SNOW_COVERAGE_QUERY_CAPT :{WHITE}Acoperire cu zăpadă (în %) +STR_MAPGEN_DESERT_COVERAGE_QUERY_CAPT :{WHITE}Acoperire cu deșert (în %) STR_MAPGEN_START_DATE_QUERY_CAPT :{WHITE}Modifică anul de început # SE Map generation @@ -2825,6 +2884,8 @@ STR_NEWGRF_SETTINGS_VERSION :{BLACK}Versiune STR_NEWGRF_SETTINGS_MIN_VERSION :{BLACK}Vers. minimă compatibilă: {SILVER}{NUM} STR_NEWGRF_SETTINGS_MD5SUM :{BLACK}MD5sum: {SILVER}{STRING} STR_NEWGRF_SETTINGS_PALETTE :{BLACK}Paletă: {SILVER}{STRING} +STR_NEWGRF_SETTINGS_PALETTE_DEFAULT :Implicit (D) +STR_NEWGRF_SETTINGS_PALETTE_DEFAULT_32BPP :Implicit (D) / 32 bpp STR_NEWGRF_SETTINGS_PALETTE_LEGACY :Legacy (W) STR_NEWGRF_SETTINGS_PARAMETER :{BLACK}Parametri: {SILVER}{STRING} STR_NEWGRF_SETTINGS_PARAMETER_NONE :Nimic @@ -2835,9 +2896,12 @@ STR_NEWGRF_SETTINGS_DISABLED :{RED}Dezactivat STR_NEWGRF_SETTINGS_INCOMPATIBLE :{RED}Incompatibil cu această versiune de OpenTTD # NewGRF save preset window +STR_SAVE_PRESET_CAPTION :{WHITE}Salvează presetarea +STR_SAVE_PRESET_LIST_TOOLTIP :{BLACK}Lista presetărilor disponibile; alegeți una pentru salvare cu numele de mai jos STR_SAVE_PRESET_TITLE :{BLACK}Adaugă denumire presetare STR_SAVE_PRESET_CANCEL :{BLACK}Anulează STR_SAVE_PRESET_CANCEL_TOOLTIP :{BLACK}Nu schimba setarea implicită +STR_SAVE_PRESET_SAVE :{BLACK}Salvează STR_SAVE_PRESET_SAVE_TOOLTIP :{BLACK}Salvează setarea pe numele selectat # NewGRF parameters window @@ -2958,6 +3022,7 @@ STR_SIGN_LIST_MATCH_CASE_TOOLTIP :{BLACK}Comută # Sign window STR_EDIT_SIGN_CAPTION :{WHITE}Editează textul semnului +STR_EDIT_SIGN_LOCATION_TOOLTIP :{BLACK}Centrează vizorul principal pe locația semnului. Ctrl+clic deschide un nou vizor pe locația semnului STR_EDIT_SIGN_NEXT_SIGN_TOOLTIP :{BLACK}Mergi la semnul urmator STR_EDIT_SIGN_PREVIOUS_SIGN_TOOLTIP :{BLACK}Mergi la semnul anterior @@ -3009,7 +3074,7 @@ STR_LOCAL_AUTHORITY_DO_IT_TOOLTIP :{BLACK}Activeaz STR_LOCAL_AUTHORITY_ACTION_SMALL_ADVERTISING_CAMPAIGN :Campanie publicitară mică STR_LOCAL_AUTHORITY_ACTION_MEDIUM_ADVERTISING_CAMPAIGN :Campanie publicitară medie STR_LOCAL_AUTHORITY_ACTION_LARGE_ADVERTISING_CAMPAIGN :Campanie publicitară mare -STR_LOCAL_AUTHORITY_ACTION_ROAD_RECONSTRUCTION :Finanţează reconstrucţia străzilor +STR_LOCAL_AUTHORITY_ACTION_ROAD_RECONSTRUCTION :Finanțează reconstrucția străzilor STR_LOCAL_AUTHORITY_ACTION_STATUE_OF_COMPANY :Ridică un monument dedicat preşedintelui companiei STR_LOCAL_AUTHORITY_ACTION_NEW_BUILDINGS :Finanţează construcţia de noi clădiri STR_LOCAL_AUTHORITY_ACTION_EXCLUSIVE_TRANSPORT :Cumpără drepturi exclusive de transport @@ -3018,7 +3083,7 @@ STR_LOCAL_AUTHORITY_ACTION_BRIBE :Mituieşte auto STR_LOCAL_AUTHORITY_ACTION_TOOLTIP_SMALL_ADVERTISING :{YELLOW}Iniţiază o campanie publicitară mică pentru a atrage mai mulţi călători şi mai multe mărfuri spre compania ta.{} Cost: {CURRENCY_LONG} STR_LOCAL_AUTHORITY_ACTION_TOOLTIP_MEDIUM_ADVERTISING :{YELLOW}Iniţiază o campanie publicitară medie pentru a atrage mai mulţi călători şi mai multe mărfuri spre compania ta.{} Cost: {CURRENCY_LONG} STR_LOCAL_AUTHORITY_ACTION_TOOLTIP_LARGE_ADVERTISING :{YELLOW}Iniţiază o mare campanie publicitară pentru a atrage mai mulţi călători şi mai multe mărfuri spre compania ta.{} Cost: {CURRENCY_LONG} -STR_LOCAL_AUTHORITY_ACTION_TOOLTIP_ROAD_RECONSTRUCTION :{YELLOW}Finanţează reconstrucţia străzilor locale. Acest lucru cauzează perturbări majore ale traficului rutier timp de până la 6 luni.{} Cost: {CURRENCY_LONG} +STR_LOCAL_AUTHORITY_ACTION_TOOLTIP_ROAD_RECONSTRUCTION :{YELLOW}Finanțează reconstrucția rețelei locale de drumuri.{}Aceasta cauzează perturbări majore ale traficului rutier timp de până la 6 luni.{}Cost: {CURRENCY_LONG} STR_LOCAL_AUTHORITY_ACTION_TOOLTIP_STATUE_OF_COMPANY :{YELLOW}Construieşte o statuie în cinstea companiei tale.{} Cost: {CURRENCY_LONG} STR_LOCAL_AUTHORITY_ACTION_TOOLTIP_NEW_BUILDINGS :{YELLOW}Finanţează construcţia de noi clădiri comerciale în oraş.{} Cost: {CURRENCY_LONG} STR_LOCAL_AUTHORITY_ACTION_TOOLTIP_EXCLUSIVE_TRANSPORT :{YELLOW}Cumpără drepturi exclusive de transport în acest oraş pe o perioadă de un an. Autorităţile locale vor permite doar companiei tale să transporte călători şi mărfuri.{} Cost: {CURRENCY_LONG} @@ -3026,9 +3091,10 @@ STR_LOCAL_AUTHORITY_ACTION_TOOLTIP_BRIBE :{YELLOW}Mituie # Goal window STR_GOALS_CAPTION :{WHITE}{COMPANY} Scopuri -STR_GOALS_SPECTATOR_CAPTION :{WHITE}Scopuri globale -STR_GOALS_GLOBAL_BUTTON_HELPTEXT :{BLACK}Arată scopurile globale +STR_GOALS_SPECTATOR_CAPTION :{WHITE}Obiective globale +STR_GOALS_GLOBAL_BUTTON_HELPTEXT :{BLACK}Afișează obiectivele globale STR_GOALS_COMPANY_BUTTON :{BLACK}Companie +STR_GOALS_COMPANY_BUTTON_HELPTEXT :{BLACK}Afișează obiectivele companiei STR_GOALS_TEXT :{ORANGE}{STRING} STR_GOALS_NONE :{ORANGE}- Nici unul - STR_GOALS_PROGRESS :{ORANGE}{STRING} @@ -3208,7 +3274,7 @@ STR_COMPANY_VIEW_TRAINS :{WHITE}{COMMA} STR_COMPANY_VIEW_ROAD_VEHICLES :{WHITE}{COMMA} autovehicul{P "" e} STR_COMPANY_VIEW_AIRCRAFT :{WHITE}{COMMA} aeronav{P ă e} STR_COMPANY_VIEW_SHIPS :{WHITE}{COMMA} nav{P ã e} -STR_COMPANY_VIEW_VEHICLES_NONE :{WHITE}Nici unul +STR_COMPANY_VIEW_VEHICLES_NONE :{WHITE}Niciunul STR_COMPANY_VIEW_COMPANY_VALUE :{GOLD}Valoarea companiei: {WHITE}{CURRENCY_LONG} STR_COMPANY_VIEW_SHARES_OWNED_BY :{WHITE}({COMMA}% deţinute de {COMPANY}) STR_COMPANY_VIEW_INFRASTRUCTURE :{GOLD}Infrastructură: @@ -3217,7 +3283,7 @@ STR_COMPANY_VIEW_INFRASTRUCTURE_ROAD :{WHITE}{COMMA} STR_COMPANY_VIEW_INFRASTRUCTURE_WATER :{WHITE}{COMMA} pătrățele de apă STR_COMPANY_VIEW_INFRASTRUCTURE_STATION :{WHITE}{COMMA} pătrățele stații STR_COMPANY_VIEW_INFRASTRUCTURE_AIRPORT :{WHITE}{COMMA} aeroport{P "" uri} -STR_COMPANY_VIEW_INFRASTRUCTURE_NONE :{WHITE}Nici una +STR_COMPANY_VIEW_INFRASTRUCTURE_NONE :{WHITE}Niciuna STR_COMPANY_VIEW_BUILD_HQ_BUTTON :{BLACK}Constr. sediu STR_COMPANY_VIEW_BUILD_HQ_TOOLTIP :{BLACK}Construieşte sediul companiei @@ -3227,6 +3293,8 @@ STR_COMPANY_VIEW_RELOCATE_HQ :{BLACK}Mută se STR_COMPANY_VIEW_RELOCATE_COMPANY_HEADQUARTERS :{BLACK}Mută sediul companiei (costă 1% din valoarea companiei). Shift+Click arată estimarea de cost fără a muta sediul companiei STR_COMPANY_VIEW_INFRASTRUCTURE_BUTTON :{BLACK}Detalii STR_COMPANY_VIEW_INFRASTRUCTURE_TOOLTIP :{BLACK}Vezi contabilizarea infrastructurii +STR_COMPANY_VIEW_GIVE_MONEY_BUTTON :{BLACK}Dă banii +STR_COMPANY_VIEW_GIVE_MONEY_TOOLTIP :{BLACK}Dă bani acestei companii STR_COMPANY_VIEW_NEW_FACE_BUTTON :{BLACK}Schimbă poza STR_COMPANY_VIEW_NEW_FACE_TOOLTIP :{BLACK}Alege o nouă poză a preşedintelui @@ -3264,8 +3332,13 @@ STR_INDUSTRY_DIRECTORY_CAPTION :{WHITE}Industri STR_INDUSTRY_DIRECTORY_NONE :{ORANGE}- Nimic- STR_INDUSTRY_DIRECTORY_ITEM_INFO :{BLACK}{CARGO_LONG}{STRING}{YELLOW} ({COMMA}% transportat){BLACK} STR_INDUSTRY_DIRECTORY_ITEM_NOPROD :{ORANGE}{INDUSTRY} +STR_INDUSTRY_DIRECTORY_ITEM_PROD3 :{ORANGE}{INDUSTRY} {STRING}, {STRING}, {STRING} +STR_INDUSTRY_DIRECTORY_ITEM_PRODMORE :{ORANGE}{INDUSTRY} {STRING}, {STRING}, {STRING} și încă {NUM}... STR_INDUSTRY_DIRECTORY_LIST_CAPTION :{BLACK}Numele industriilor - clic pe nume pentru focalizarea pe industrie. Ctrl+Click deschide o fereastră cu locaţia industriei +STR_INDUSTRY_DIRECTORY_ACCEPTED_CARGO_FILTER :{BLACK}Marfă acceptată: {SILVER}{STRING} +STR_INDUSTRY_DIRECTORY_PRODUCED_CARGO_FILTER :{BLACK}Marfă produsă: {SILVER}{STRING} STR_INDUSTRY_DIRECTORY_FILTER_ALL_TYPES :Toate tipurile de mărfuri +STR_INDUSTRY_DIRECTORY_FILTER_NONE :Niciunul # Industry view STR_INDUSTRY_VIEW_CAPTION :{WHITE}{INDUSTRY} @@ -3275,7 +3348,9 @@ STR_INDUSTRY_VIEW_LOCATION_TOOLTIP :{BLACK}Centreaz STR_INDUSTRY_VIEW_PRODUCTION_LEVEL :{BLACK}Nivelul producţiei: {YELLOW}{COMMA}% STR_INDUSTRY_VIEW_INDUSTRY_ANNOUNCED_CLOSURE :{YELLOW}Industria a anunţat închiderea iminentă! +STR_INDUSTRY_VIEW_REQUIRES_N_CARGO :{BLACK}Necesită: {YELLOW}{STRING}{STRING} +STR_INDUSTRY_VIEW_ACCEPT_CARGO :{YELLOW}{STRING}{BLACK}{3:STRING} STR_CONFIG_GAME_PRODUCTION :{WHITE}Schimba productia (multiplu de 8, până la 2040) STR_CONFIG_GAME_PRODUCTION_LEVEL :{WHITE}Modifică nivelul producţiei (procent, până la 800%) @@ -3327,12 +3402,12 @@ STR_GROUP_DEFAULT_AIRCRAFTS :Aeronave negrup STR_GROUP_COUNT_WITH_SUBGROUP :{TINY_FONT}{COMMA} (+{COMMA}) -STR_GROUPS_CLICK_ON_GROUP_FOR_TOOLTIP :{BLACK}Grupuri - click pe un grup pentru lista completă a vehiculelor acestuia -STR_GROUP_CREATE_TOOLTIP :{BLACK}Click pentru a creea un grup +STR_GROUPS_CLICK_ON_GROUP_FOR_TOOLTIP :{BLACK}Grupuri - apasă pe un grup pentru lista completă a vehiculelor acestuia +STR_GROUP_CREATE_TOOLTIP :{BLACK}Apasă pentru a crea un grup STR_GROUP_DELETE_TOOLTIP :{BLACK}Şterge grupul selectat STR_GROUP_RENAME_TOOLTIP :{BLACK}Redenumeşte grupul selectat STR_GROUP_LIVERY_TOOLTIP :{BLACK}Schimbă uniforma grupului selectat -STR_GROUP_REPLACE_PROTECTION_TOOLTIP :{BLACK}Click aici pentru a proteja acest grup de la înlocuirile automate globale +STR_GROUP_REPLACE_PROTECTION_TOOLTIP :{BLACK}Apasă aici pentru a proteja acest grup de înlocuirile automate globale STR_QUERY_GROUP_DELETE_CAPTION :{WHITE}Şterge Grup STR_GROUP_DELETE_QUERY_TEXT :{WHITE}Sigur dorești ștergerea grupului și a descendenților lui? @@ -3353,6 +3428,7 @@ STR_BUY_VEHICLE_TRAIN_MONORAIL_CAPTION :Noi vehicule mo STR_BUY_VEHICLE_TRAIN_MAGLEV_CAPTION :Noi vehicule pe Pernă Magnetică STR_BUY_VEHICLE_ROAD_VEHICLE_CAPTION :Autovehicule noi +STR_BUY_VEHICLE_TRAM_VEHICLE_CAPTION :Tramvaie noi ############ range for vehicle availability starts STR_BUY_VEHICLE_TRAIN_ALL_CAPTION :Vehicule pe şine @@ -3377,6 +3453,7 @@ STR_PURCHASE_INFO_AIRCRAFT_CAPACITY :{BLACK}Capacita STR_PURCHASE_INFO_PWAGPOWER_PWAGWEIGHT :{BLACK}Vagoane electrificate: {GOLD}+{POWER}{BLACK} Greutate: {GOLD}+{WEIGHT_SHORT} STR_PURCHASE_INFO_REFITTABLE_TO :{BLACK}Modificabil pentru: {GOLD}{STRING} STR_PURCHASE_INFO_ALL_TYPES :Toate tipurile de mărfuri +STR_PURCHASE_INFO_NONE :Niciunul STR_PURCHASE_INFO_ALL_BUT :Toate, cu excepţia {CARGO_LIST} STR_PURCHASE_INFO_MAX_TE :{BLACK}Efort tractor max.: {GOLD}{FORCE} STR_PURCHASE_INFO_AIRCRAFT_RANGE :{BLACK}Rază acțiune: {GOLD}{COMMA} pătrățele @@ -3391,6 +3468,7 @@ STR_BUY_VEHICLE_ROAD_VEHICLE_BUY_VEHICLE_BUTTON :{BLACK}Cumpăr STR_BUY_VEHICLE_SHIP_BUY_VEHICLE_BUTTON :{BLACK}Cumpără navă STR_BUY_VEHICLE_AIRCRAFT_BUY_VEHICLE_BUTTON :{BLACK}Cumpără aeronavă +STR_BUY_VEHICLE_TRAIN_BUY_REFIT_VEHICLE_BUTTON :{BLACK}Cumpără și adaptează vehiculul STR_BUY_VEHICLE_AIRCRAFT_BUY_REFIT_VEHICLE_BUTTON :{BLACK}Cumpără și schimbă marfa transportată de aeronavă STR_BUY_VEHICLE_TRAIN_BUY_VEHICLE_TOOLTIP :{BLACK}Cumpără vehiculul feroviar selectat. Shift+Click arată costul estimat fără să cumpere vehiculul @@ -3398,6 +3476,8 @@ STR_BUY_VEHICLE_ROAD_VEHICLE_BUY_VEHICLE_TOOLTIP :{BLACK}Cumpăr STR_BUY_VEHICLE_SHIP_BUY_VEHICLE_TOOLTIP :{BLACK}Cumpără nava selectată. Shift+Click arată costul estimativ fără a efectua achiziţia STR_BUY_VEHICLE_AIRCRAFT_BUY_VEHICLE_TOOLTIP :{BLACK}Cumpără aeronava selectată. Shift+Click arată costul estimativ fără a efectua achiziţia +STR_BUY_VEHICLE_TRAIN_BUY_REFIT_VEHICLE_TOOLTIP :{BLACK}Cumpără și repară trenul selectat. Shift+Click arată costul estimat fără cumpărare +STR_BUY_VEHICLE_AIRCRAFT_BUY_REFIT_VEHICLE_TOOLTIP :{BLACK}Cumpără și repară aeronava selectată. Shift+clic afișează costul estimat fără achiziție STR_BUY_VEHICLE_TRAIN_RENAME_BUTTON :{BLACK}Nume nou STR_BUY_VEHICLE_ROAD_VEHICLE_RENAME_BUTTON :{BLACK}Nume nou @@ -3513,6 +3593,7 @@ STR_ENGINE_PREVIEW_MONORAIL_LOCOMOTIVE :locomotivă mon STR_ENGINE_PREVIEW_MAGLEV_LOCOMOTIVE :locomotivă pernă magnetică STR_ENGINE_PREVIEW_ROAD_VEHICLE :autovehicul +STR_ENGINE_PREVIEW_TRAM_VEHICLE :tramvai STR_ENGINE_PREVIEW_AIRCRAFT :aeronavă STR_ENGINE_PREVIEW_SHIP :navă @@ -3551,8 +3632,10 @@ STR_REPLACE_ENGINE_WAGON_SELECT_HELP :{BLACK}Comutã STR_REPLACE_ENGINES :Motoare STR_REPLACE_WAGONS :Vagoane STR_REPLACE_ALL_RAILTYPE :Toate vehiculele pe șine +STR_REPLACE_ALL_ROADTYPE :Toate vehiculele rutiere STR_REPLACE_HELP_RAILTYPE :{BLACK}Alege un tip de cale ferată pentru care doreşti să înlocuieşti locomotivele +STR_REPLACE_HELP_ROADTYPE :{BLACK}Alege tipul de drum pentru care vrei să înlocuiești motoarele STR_REPLACE_HELP_REPLACE_INFO_TAB :{BLACK}Arată locomotiva ceva înlocui locomotiva selectată în stânga STR_REPLACE_RAIL_VEHICLES :Vehicule feroviare STR_REPLACE_ELRAIL_VEHICLES :Vehicule Electrificate pe Sine @@ -3568,6 +3651,7 @@ STR_REPLACE_REMOVE_WAGON_HELP :{BLACK}Fă opti # Vehicle view STR_VEHICLE_VIEW_CAPTION :{WHITE}{VEHICLE} +STR_VEHICLE_VIEW_SHIP_CENTER_TOOLTIP :{BLACK}Centrează imaginea pe locația navei. Dublu clic va urmări nava în vizorul principal. Ctrl+Clic deschide un nou vizor pe locația navei STR_VEHICLE_VIEW_TRAIN_SEND_TO_DEPOT_TOOLTIP :{BLACK}Trimite trenul într-un depou STR_VEHICLE_VIEW_ROAD_VEHICLE_SEND_TO_DEPOT_TOOLTIP :{BLACK}Trimite autovehiculul la autobază. Ctrl+clic pentru service @@ -3826,6 +3910,7 @@ STR_ORDER_REFIT_STOP_ORDER :(Modifică pent STR_ORDER_STOP_ORDER :(Stop) STR_ORDER_GO_TO_STATION :{STRING} {STATION} {STRING} +STR_ORDER_GO_TO_STATION_CAN_T_USE_STATION :{PUSH_COLOUR}{RED}(Stație inutilizabilă){POP_COLOUR} {STRING} {STATION} {STRING} STR_ORDER_IMPLICIT :(Implicit) @@ -3969,7 +4054,7 @@ STR_AI_CONFIG_GAMELIST_TOOLTIP :{BLACK}Script J STR_AI_CONFIG_AILIST_TOOLTIP :{BLACK}Modulul de IA care va fi încărcat în jocul următor STR_AI_CONFIG_HUMAN_PLAYER :Jucator uman STR_AI_CONFIG_RANDOM_AI :IA aleator -STR_AI_CONFIG_NONE :(nici unul) +STR_AI_CONFIG_NONE :(niciunul) STR_AI_CONFIG_MOVE_UP :{BLACK}În sus STR_AI_CONFIG_MOVE_UP_TOOLTIP :{BLACK}Mută IA selectată sus în listă @@ -4301,6 +4386,7 @@ STR_ERROR_CAN_T_REMOVE_ROAD_FROM :{WHITE}Nu se po STR_ERROR_CAN_T_REMOVE_TRAMWAY_FROM :{WHITE}Nu pot înlătura şina de tramvai de aici... STR_ERROR_THERE_IS_NO_ROAD :{WHITE}...nu există drum aici STR_ERROR_THERE_IS_NO_TRAMWAY :{WHITE}...nu există şină de tramvai aici +STR_ERROR_NO_SUITABLE_TRAMWAY :{WHITE}Niciun tramvai adecvat # Waterway construction errors STR_ERROR_CAN_T_BUILD_CANALS :{WHITE}Nu pot construi un canal aici... @@ -4456,15 +4542,15 @@ STR_ERROR_CAN_T_DELETE_SIGN :{WHITE}Nu pot s STR_DESKTOP_SHORTCUT_COMMENT :Un joc de simulare bazat pe Transport Tycoon Deluxe # Translatable descriptions in media/baseset/*.ob* files -STR_BASEGRAPHICS_DOS_DESCRIPTION :Setul grafic original al Transport Tycoon Deluxe pentru DOS. -STR_BASEGRAPHICS_DOS_DE_DESCRIPTION :Setul grafic original al Transport Tycoon Deluxe pentru DOS (ediţia germană). -STR_BASEGRAPHICS_WIN_DESCRIPTION :Setul grafic original al Transport Tycoon Deluxe pentru Windows. -STR_BASESOUNDS_DOS_DESCRIPTION :Setul de sunete original al Transport Tycoon Deluxe pentru DOS. -STR_BASESOUNDS_WIN_DESCRIPTION :Setul de sunete original al Transport Tycoon Deluxe pentru Windows. +STR_BASEGRAPHICS_DOS_DESCRIPTION :Grafica originală a ediției Transport Tycoon Deluxe pentru DOS. +STR_BASEGRAPHICS_DOS_DE_DESCRIPTION :Grafica originală a Transport Tycoon Deluxe pentru DOS (ediția germană). +STR_BASEGRAPHICS_WIN_DESCRIPTION :Grafica originală a ediției Transport Tycoon Deluxe pentru Windows. +STR_BASESOUNDS_DOS_DESCRIPTION :Sunetele originale ale ediției Transport Tycoon Deluxe pentru DOS. +STR_BASESOUNDS_WIN_DESCRIPTION :Sunetele originale ale Transport Tycoon Deluxe pentru Windows. STR_BASESOUNDS_NONE_DESCRIPTION :Un set de sunete fără nici un sunet inclus. -STR_BASEMUSIC_WIN_DESCRIPTION :Setul de muzică original al Transport Tycoon Deluxe pentru Windows. -STR_BASEMUSIC_DOS_DESCRIPTION :Setul de muzică original al Transport Tycoon Deluxe pentru DOS. -STR_BASEMUSIC_TTO_DESCRIPTION :Setul de muzică original al Transport (Original/World Editor) pentru DOS. +STR_BASEMUSIC_WIN_DESCRIPTION :Muzica originală a ediției Transport Tycoon Deluxe pentru Windows. +STR_BASEMUSIC_DOS_DESCRIPTION :Muzica originală a ediției Transport Tycoon Deluxe pentru DOS. +STR_BASEMUSIC_TTO_DESCRIPTION :Setul original de muzică al Transport Tycoon (Original/World Editor) pentru DOS. STR_BASEMUSIC_NONE_DESCRIPTION :Un set de muzică fără muzică inclusă. ##id 0x2000 diff --git a/src/lang/simplified_chinese.txt b/src/lang/simplified_chinese.txt index 205f8a98b9..9c46f8d189 100644 --- a/src/lang/simplified_chinese.txt +++ b/src/lang/simplified_chinese.txt @@ -1021,6 +1021,7 @@ STR_GAME_OPTIONS_FONT_ZOOM_DROPDOWN_NORMAL :正常 STR_GAME_OPTIONS_FONT_ZOOM_DROPDOWN_2X_ZOOM :两倍大小 STR_GAME_OPTIONS_FONT_ZOOM_DROPDOWN_4X_ZOOM :四倍大小 +STR_GAME_OPTIONS_GRAPHICS :{BLACK}图像 STR_GAME_OPTIONS_REFRESH_RATE :{BLACK}显示刷新率 STR_GAME_OPTIONS_REFRESH_RATE_TOOLTIP :{BLACK}选择需要的屏幕刷新率 @@ -1204,6 +1205,7 @@ STR_CONFIG_SETTING_CITY_APPROVAL :地区政府对 STR_CONFIG_SETTING_CITY_APPROVAL_HELPTEXT :设置相关参数以决定各公司造成的噪音及环境破坏时,各城镇对该公司的评价及未来区域建设的影响。 STR_CONFIG_SETTING_MAP_HEIGHT_LIMIT :地图高度限制:{STRING} +STR_CONFIG_SETTING_MAP_HEIGHT_LIMIT_VALUE :{NUM} STR_CONFIG_SETTING_TOO_HIGH_MOUNTAIN :{WHITE}您不能把最高地面高度设为这个值,因为地图上至少有一座山丘的高度比这个值还大 STR_CONFIG_SETTING_AUTOSLOPE :允许在建筑、轨道等下方改变地形(自动斜坡): {STRING} STR_CONFIG_SETTING_AUTOSLOPE_HELPTEXT :允许在建筑和轨道下方改变地形而不需要拆除他们 @@ -1468,6 +1470,9 @@ STR_CONFIG_SETTING_PERSISTENT_BUILDINGTOOLS_HELPTEXT :“打开”时 STR_CONFIG_SETTING_EXPENSES_LAYOUT :企业财政窗口中的组群支出:{STRING} STR_CONFIG_SETTING_EXPENSES_LAYOUT_HELPTEXT :“打开”时公司财务报表将分组显示 STR_CONFIG_SETTING_AUTO_REMOVE_SIGNALS :建造铁路时自动移除信号灯:{STRING} +STR_CONFIG_SETTING_FAST_FORWARD_SPEED_LIMIT :快进速度上限:{STRING} +STR_CONFIG_SETTING_FAST_FORWARD_SPEED_LIMIT_VAL :{NUM}% 正常游戏速度 +STR_CONFIG_SETTING_FAST_FORWARD_SPEED_LIMIT_ZERO :无限制(在您的计算机能允许的范围内) STR_CONFIG_SETTING_SOUND_TICKER :产业新闻: {STRING} STR_CONFIG_SETTING_SOUND_TICKER_HELPTEXT :每月初产业新闻音效 @@ -2572,6 +2577,7 @@ STR_TREES_RANDOM_TYPE :{BLACK}随机 STR_TREES_RANDOM_TYPE_TOOLTIP :{BLACK}种植随机类型的树木,按住 Shift 键可以显示所需资金 STR_TREES_RANDOM_TREES_BUTTON :{BLACK}随机树木 STR_TREES_RANDOM_TREES_TOOLTIP :{BLACK}随机地种植一些树木 +STR_TREES_MODE_FOREST_SM_BUTTON :{BLACK}树丛 STR_TREES_MODE_FOREST_LG_BUTTON :{BLACK}森林 # Land generation window (SE) @@ -3787,6 +3793,7 @@ STR_REPLACE_REMOVE_WAGON_HELP :{BLACK}当车 STR_VEHICLE_VIEW_CAPTION :{WHITE}{VEHICLE} STR_VEHICLE_VIEW_TRAIN_CENTER_TOOLTIP :{BLACK}将主视角中心移动到列车所在的位置。双击将会在主视角中跟踪列车。单击的同时按住Ctrl会在新视点中显示列车位置 +STR_VEHICLE_VIEW_ROAD_VEHICLE_CENTER_TOOLTIP :{BLACK} 将主视角中心移动到车辆所在的位置。双击将会在主视角中跟踪车辆。单击的同时按住 Ctrl 会在新视点中显示车辆位置 STR_VEHICLE_VIEW_SHIP_CENTER_TOOLTIP :{BLACK}将主视角中心移动到船只所在的位置。双击将会在主视角中跟踪船只。单击的同时按住Ctrl会在新视点中显示船只位置 STR_VEHICLE_VIEW_AIRCRAFT_CENTER_TOOLTIP :{BLACK}将主视角中心移动到飞机所在的位置。双击将会在主视角中跟踪飞机。单击的同时按住Ctrl会在新视点中显示飞机位置 @@ -4375,6 +4382,7 @@ STR_ERROR_LOAN_ALREADY_REPAYED :{WHITE}没有 STR_ERROR_CURRENCY_REQUIRED :{WHITE}需要{CURRENCY_LONG} STR_ERROR_CAN_T_REPAY_LOAN :{WHITE}不能偿还贷款…… STR_ERROR_INSUFFICIENT_FUNDS :{WHITE}不能将银行的贷款送给别人…… +STR_ERROR_CAN_T_GIVE_MONEY :{WHITE}不能给予该公司资金…… STR_ERROR_CAN_T_BUY_COMPANY :{WHITE}不能收购公司…… STR_ERROR_CAN_T_BUILD_COMPANY_HEADQUARTERS :{WHITE}不能设置公司总部 STR_ERROR_CAN_T_BUY_25_SHARE_IN_THIS :{WHITE}不能购买此公司的股份…… diff --git a/src/lang/spanish.txt b/src/lang/spanish.txt index 97f5b8320d..6b77d1f2a1 100644 --- a/src/lang/spanish.txt +++ b/src/lang/spanish.txt @@ -195,7 +195,7 @@ STR_COLOUR_DEFAULT :Por Defecto STR_UNITS_VELOCITY_IMPERIAL :{COMMA}{NBSP}mph STR_UNITS_VELOCITY_METRIC :{COMMA}{NBSP}km/h STR_UNITS_VELOCITY_SI :{COMMA}{NBSP}m/s -STR_UNITS_VELOCITY_GAMEUNITS :{DECIMAL} {NBSP}casillas/día +STR_UNITS_VELOCITY_GAMEUNITS :{DECIMAL}{NBSP}casillas/día STR_UNITS_POWER_IMPERIAL :{COMMA}{NBSP}hp STR_UNITS_POWER_METRIC :{COMMA}{NBSP}cv @@ -1730,7 +1730,7 @@ STR_CONFIG_SETTING_LOCALISATION_UNITS_VELOCITY_GAMEUNITS :Unidad del jueg STR_CONFIG_SETTING_LOCALISATION_UNITS_POWER :Unidad de potencia de vehículos: {STRING} STR_CONFIG_SETTING_LOCALISATION_UNITS_POWER_HELPTEXT :Cada vez que se muestre la potencia de un vehículo en la interfaz de usuario, se empleará la unidad seleccionada -STR_CONFIG_SETTING_LOCALISATION_UNITS_POWER_IMPERIAL :Imperial (cv) +STR_CONFIG_SETTING_LOCALISATION_UNITS_POWER_IMPERIAL :Imperial (hp) STR_CONFIG_SETTING_LOCALISATION_UNITS_POWER_METRIC :Métrico (cv) STR_CONFIG_SETTING_LOCALISATION_UNITS_POWER_SI :SI (kW) diff --git a/src/lang/swedish.txt b/src/lang/swedish.txt index aa725a47cb..2a7ef7de9a 100644 --- a/src/lang/swedish.txt +++ b/src/lang/swedish.txt @@ -2531,7 +2531,7 @@ STR_WATERWAYS_TOOLBAR_BUILD_DOCK_TOOLTIP :{BLACK}Bygg ham STR_WATERWAYS_TOOLBAR_BUOY_TOOLTIP :{BLACK}Placera en boj som kan som kan användas som riktmärke. Shift växlar mellan att bygga/visa beräknad kostnad STR_WATERWAYS_TOOLBAR_BUILD_AQUEDUCT_TOOLTIP :{BLACK}Bygg akvedukt. Shift växlar mellan att bygga/visa beräknad kostnad STR_WATERWAYS_TOOLBAR_CREATE_LAKE_TOOLTIP :{BLACK}Definiera vattenyta.{}Skapa en kanal, om inte CTRL är nedhållen vid havsnivå, då den kommer att översvämma närliggande områden istället -STR_WATERWAYS_TOOLBAR_CREATE_RIVER_TOOLTIP :{BLACK}Placera flod +STR_WATERWAYS_TOOLBAR_CREATE_RIVER_TOOLTIP :{BLACK}Placera flod. Ctrl markerar ytan diagonalt # Ship depot construction window STR_DEPOT_BUILD_SHIP_CAPTION :{WHITE}Riktning för skeppsvarv From 33c5f984f533bc101f4a92130a2b56432d5dcc08 Mon Sep 17 00:00:00 2001 From: Milek7 Date: Sun, 4 Apr 2021 08:40:56 +0200 Subject: [PATCH 007/268] Codechange: Use COINIT_MULTITHREADED in CoInitializeEx (#8938) --- src/music/dmusic.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/music/dmusic.cpp b/src/music/dmusic.cpp index b0e34070c8..c9447206af 100644 --- a/src/music/dmusic.cpp +++ b/src/music/dmusic.cpp @@ -1071,7 +1071,7 @@ static const char *LoadDefaultDLSFile(const char *user_dls) const char *MusicDriver_DMusic::Start(const StringList &parm) { /* Initialize COM */ - if (FAILED(CoInitializeEx(nullptr, COINITBASE_MULTITHREADED))) return "COM initialization failed"; + if (FAILED(CoInitializeEx(nullptr, COINIT_MULTITHREADED))) return "COM initialization failed"; /* Create the DirectMusic object */ if (FAILED(CoCreateInstance( From f481c9fc2c68ff7b83eef78d3e37982492dae981 Mon Sep 17 00:00:00 2001 From: Charles Pigott Date: Fri, 2 Apr 2021 21:08:16 +0100 Subject: [PATCH 008/268] Codechange: Replace CStrA with std::string --- src/misc/CMakeLists.txt | 1 - src/misc/array.hpp | 12 +- src/misc/dbg_helpers.cpp | 73 ++++++------ src/misc/dbg_helpers.h | 50 +++++---- src/misc/str.hpp | 149 ------------------------- src/pathfinder/yapf/yapf.hpp | 1 - src/pathfinder/yapf/yapf_base.hpp | 2 +- src/pathfinder/yapf/yapf_node.hpp | 4 +- src/pathfinder/yapf/yapf_node_rail.hpp | 10 +- src/pathfinder/yapf/yapf_rail.cpp | 4 +- src/pathfinder/yapf/yapf_type.hpp | 12 +- 11 files changed, 84 insertions(+), 234 deletions(-) delete mode 100644 src/misc/str.hpp diff --git a/src/misc/CMakeLists.txt b/src/misc/CMakeLists.txt index 9ed598004f..76780679f5 100644 --- a/src/misc/CMakeLists.txt +++ b/src/misc/CMakeLists.txt @@ -11,5 +11,4 @@ add_files( getoptdata.h hashtable.hpp lrucache.hpp - str.hpp ) diff --git a/src/misc/array.hpp b/src/misc/array.hpp index d23fb7a0d9..aea58aba42 100644 --- a/src/misc/array.hpp +++ b/src/misc/array.hpp @@ -11,7 +11,7 @@ #define ARRAY_HPP #include "fixedsizearray.hpp" -#include "str.hpp" +#include "../string_func.h" /** * Flexible array with size limit. Implemented as fixed size @@ -103,14 +103,14 @@ public: */ template void Dump(D &dmp) const { - dmp.WriteLine("capacity = %d", Tcapacity); + dmp.WriteValue("capacity", Tcapacity); uint num_items = Length(); - dmp.WriteLine("num_items = %d", num_items); - CStrA name; + dmp.WriteValue("num_items", num_items); for (uint i = 0; i < num_items; i++) { const T &item = (*this)[i]; - name.Format("item[%d]", i); - dmp.WriteStructT(name.Data(), &item); + char name[32]; + seprintf(name, lastof(name), "item[%d]", i); + dmp.WriteStructT(name, &item); } } }; diff --git a/src/misc/dbg_helpers.cpp b/src/misc/dbg_helpers.cpp index 7d38c7e65a..85d6ea1b91 100644 --- a/src/misc/dbg_helpers.cpp +++ b/src/misc/dbg_helpers.cpp @@ -10,6 +10,10 @@ #include "../stdafx.h" #include "../rail_map.h" #include "dbg_helpers.h" +#include "blob.hpp" + +#include +#include #include "../safeguards.h" @@ -20,19 +24,15 @@ static const char * const trackdir_names[] = { }; /** Return name of given Trackdir. */ -CStrA ValueStr(Trackdir td) +std::string ValueStr(Trackdir td) { - CStrA out; - out.Format("%d (%s)", td, ItemAtT(td, trackdir_names, "UNK", INVALID_TRACKDIR, "INV")); - return out.Transfer(); + return std::to_string(td) + " (" + ItemAtT(td, trackdir_names, "UNK", INVALID_TRACKDIR, "INV") + ")"; } /** Return composed name of given TrackdirBits. */ -CStrA ValueStr(TrackdirBits td_bits) +std::string ValueStr(TrackdirBits td_bits) { - CStrA out; - out.Format("%d (%s)", td_bits, ComposeNameT(td_bits, trackdir_names, "UNK", INVALID_TRACKDIR_BIT, "INV").Data()); - return out.Transfer(); + return std::to_string(td_bits) + " (" + ComposeNameT(td_bits, trackdir_names, "UNK", INVALID_TRACKDIR_BIT, "INV") + ")"; } @@ -42,11 +42,9 @@ static const char * const diagdir_names[] = { }; /** Return name of given DiagDirection. */ -CStrA ValueStr(DiagDirection dd) +std::string ValueStr(DiagDirection dd) { - CStrA out; - out.Format("%d (%s)", dd, ItemAtT(dd, diagdir_names, "UNK", INVALID_DIAGDIR, "INV")); - return out.Transfer(); + return std::to_string(dd) + " (" + ItemAtT(dd, diagdir_names, "UNK", INVALID_DIAGDIR, "INV") + ")"; } @@ -56,20 +54,19 @@ static const char * const signal_type_names[] = { }; /** Return name of given SignalType. */ -CStrA ValueStr(SignalType t) +std::string ValueStr(SignalType t) { - CStrA out; - out.Format("%d (%s)", t, ItemAtT(t, signal_type_names, "UNK")); - return out.Transfer(); + return std::to_string(t) + " (" + ItemAtT(t, signal_type_names, "UNK") + ")"; } /** Translate TileIndex into string. */ -CStrA TileStr(TileIndex tile) +std::string TileStr(TileIndex tile) { - CStrA out; - out.Format("0x%04X (%d, %d)", tile, TileX(tile), TileY(tile)); - return out.Transfer(); + std::stringstream ss; + ss << "0x" << std::setfill('0') << std::setw(4) << std::hex << tile; // 0x%04X + ss << " (" << TileX(tile) << ", " << TileY(tile) << ")"; + return ss.str(); } /** @@ -81,21 +78,21 @@ CStrA TileStr(TileIndex tile) } /** Return structured name of the current class/structure. */ -CStrA DumpTarget::GetCurrentStructName() +std::string DumpTarget::GetCurrentStructName() { - CStrA out; + std::string out; if (!m_cur_struct.empty()) { /* we are inside some named struct, return its name */ out = m_cur_struct.top(); } - return out.Transfer(); + return out; } /** * Find the given instance in our anti-recursion repository. * Return true and set name when object was found. */ -bool DumpTarget::FindKnownName(size_t type_id, const void *ptr, CStrA &name) +bool DumpTarget::FindKnownName(size_t type_id, const void *ptr, std::string &name) { KNOWN_NAMES::const_iterator it = m_known_names.find(KnownStructKey(type_id, ptr)); if (it != m_known_names.end()) { @@ -111,33 +108,29 @@ void DumpTarget::WriteIndent() { int num_spaces = 2 * m_indent; if (num_spaces > 0) { - memset(m_out.GrowSizeNC(num_spaces), ' ', num_spaces); + m_out += std::string(num_spaces, ' '); } } -/** Write a line with indent at the beginning and \ at the end. */ -void DumpTarget::WriteLine(const char *format, ...) +/** Write 'name = value' with indent and new-line. */ +void DumpTarget::WriteValue(const char *name, int value) { WriteIndent(); - va_list args; - va_start(args, format); - m_out.AddFormatL(format, args); - va_end(args); - m_out.AppendStr("\n"); + m_out += std::string(name) + " = " + std::to_string(value) + "\n"; } /** Write 'name = value' with indent and new-line. */ void DumpTarget::WriteValue(const char *name, const char *value_str) { WriteIndent(); - m_out.AddFormat("%s = %s\n", name, value_str); + m_out += std::string(name) + " = " + value_str + "\n"; } /** Write name & TileIndex to the output. */ void DumpTarget::WriteTile(const char *name, TileIndex tile) { WriteIndent(); - m_out.AddFormat("%s = %s\n", name, TileStr(tile).Data()); + m_out += std::string(name) + " = " + TileStr(tile) + "\n"; } /** @@ -146,12 +139,12 @@ void DumpTarget::WriteTile(const char *name, TileIndex tile) void DumpTarget::BeginStruct(size_t type_id, const char *name, const void *ptr) { /* make composite name */ - CStrA cur_name = GetCurrentStructName().Transfer(); - if (cur_name.Size() > 0) { + std::string cur_name = GetCurrentStructName(); + if (cur_name.size() > 0) { /* add name delimiter (we use structured names) */ - cur_name.AppendStr("."); + cur_name += "."; } - cur_name.AppendStr(name); + cur_name += name; /* put the name onto stack (as current struct name) */ m_cur_struct.push(cur_name); @@ -160,7 +153,7 @@ void DumpTarget::BeginStruct(size_t type_id, const char *name, const void *ptr) m_known_names.insert(KNOWN_NAMES::value_type(KnownStructKey(type_id, ptr), cur_name)); WriteIndent(); - m_out.AddFormat("%s = {\n", name); + m_out += std::string(name) + " = {\n"; m_indent++; } @@ -171,7 +164,7 @@ void DumpTarget::EndStruct() { m_indent--; WriteIndent(); - m_out.AddFormat("}\n"); + m_out += "}\n"; /* remove current struct name from the stack */ m_cur_struct.pop(); diff --git a/src/misc/dbg_helpers.h b/src/misc/dbg_helpers.h index 7c9b4c876f..f9d251b8a9 100644 --- a/src/misc/dbg_helpers.h +++ b/src/misc/dbg_helpers.h @@ -12,8 +12,7 @@ #include #include - -#include "str.hpp" +#include #include "../direction_type.h" #include "../signal_type.h" @@ -67,9 +66,9 @@ inline typename ArrayT::item_t ItemAtT(E idx, const T &t, typename ArrayT: * or t_unk when index is out of bounds. */ template -inline CStrA ComposeNameT(E value, T &t, const char *t_unk, E val_inv, const char *name_inv) +inline std::string ComposeNameT(E value, T &t, const char *t_unk, E val_inv, const char *name_inv) { - CStrA out; + std::string out; if (value == val_inv) { out = name_inv; } else if (value == 0) { @@ -77,18 +76,22 @@ inline CStrA ComposeNameT(E value, T &t, const char *t_unk, E val_inv, const cha } else { for (size_t i = 0; i < ArrayT::length; i++) { if ((value & (1 << i)) == 0) continue; - out.AddFormat("%s%s", (out.Size() > 0 ? "+" : ""), (const char*)t[i]); + out += (!out.empty() ? "+" : ""); + out += t[i]; value &= ~(E)(1 << i); } - if (value != 0) out.AddFormat("%s%s", (out.Size() > 0 ? "+" : ""), t_unk); + if (value != 0) { + out += (!out.empty() ? "+" : ""); + out += t_unk; + } } - return out.Transfer(); + return out; } -CStrA ValueStr(Trackdir td); -CStrA ValueStr(TrackdirBits td_bits); -CStrA ValueStr(DiagDirection dd); -CStrA ValueStr(SignalType t); +std::string ValueStr(Trackdir td); +std::string ValueStr(TrackdirBits td_bits); +std::string ValueStr(DiagDirection dd); +std::string ValueStr(SignalType t); /** Class that represents the dump-into-string target. */ struct DumpTarget { @@ -118,31 +121,31 @@ struct DumpTarget { } }; - typedef std::map KNOWN_NAMES; + typedef std::map KNOWN_NAMES; - CStrA m_out; ///< the output string - int m_indent; ///< current indent/nesting level - std::stack m_cur_struct; ///< here we will track the current structure name - KNOWN_NAMES m_known_names; ///< map of known object instances and their structured names + std::string m_out; ///< the output string + int m_indent; ///< current indent/nesting level + std::stack m_cur_struct; ///< here we will track the current structure name + KNOWN_NAMES m_known_names; ///< map of known object instances and their structured names DumpTarget() : m_indent(0) {} static size_t& LastTypeId(); - CStrA GetCurrentStructName(); - bool FindKnownName(size_t type_id, const void *ptr, CStrA &name); + std::string GetCurrentStructName(); + bool FindKnownName(size_t type_id, const void *ptr, std::string &name); void WriteIndent(); - void CDECL WriteLine(const char *format, ...) WARN_FORMAT(2, 3); + void WriteValue(const char *name, int value); void WriteValue(const char *name, const char *value_str); void WriteTile(const char *name, TileIndex t); /** Dump given enum value (as a number and as named value) */ template void WriteEnumT(const char *name, E e) { - WriteValue(name, ValueStr(e).Data()); + WriteValue(name, ValueStr(e).c_str()); } void BeginStruct(size_t type_id, const char *name, const void *ptr); @@ -155,13 +158,14 @@ struct DumpTarget { if (s == nullptr) { /* No need to dump nullptr struct. */ - WriteLine("%s = ", name); + WriteValue(name, ""); return; } - CStrA known_as; + std::string known_as; if (FindKnownName(type_id, s, known_as)) { /* We already know this one, no need to dump it. */ - WriteLine("%s = known_as.%s", name, known_as.Data()); + std::string known_as_str = std::string("known_as.") + name; + WriteValue(name, known_as_str.c_str()); } else { /* Still unknown, dump it */ BeginStruct(type_id, name, s); diff --git a/src/misc/str.hpp b/src/misc/str.hpp deleted file mode 100644 index 05a79786b8..0000000000 --- a/src/misc/str.hpp +++ /dev/null @@ -1,149 +0,0 @@ -/* - * This file is part of OpenTTD. - * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. - * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see . - */ - -/** @file str.hpp String formatting? */ - -#ifndef STR_HPP -#define STR_HPP - -#include -#include -#include "blob.hpp" -#include "../core/math_func.hpp" -#include "../string_func.h" - -/** Blob based case sensitive ANSI/UTF-8 string */ -struct CStrA : public CBlobT -{ - typedef CBlobT base; ///< base class - - /** Create an empty CStrT */ - inline CStrA() - { - } - - /** Copy constructor */ - inline CStrA(const CStrA &src) : base(src) - { - base::FixTail(); - } - - /** Take over ownership constructor */ - inline CStrA(const OnTransfer &ot) - : base(ot) - { - } - - /** Grow the actual buffer and fix the trailing zero at the end. */ - inline char *GrowSizeNC(uint count) - { - char *ret = base::GrowSizeNC(count); - base::FixTail(); - return ret; - } - - /** Append zero-ended C string. */ - inline void AppendStr(const char *str) - { - if (!StrEmpty(str)) { - base::AppendRaw(str, strlen(str)); - base::FixTail(); - } - } - - /** Append another CStrA. */ - inline void Append(const CStrA &src) - { - if (src.Length() > 0) { - base::AppendRaw(src); - base::FixTail(); - } - } - - /** Assignment from C string. */ - inline CStrA &operator=(const char *src) - { - base::Clear(); - AppendStr(src); - return *this; - } - - /** Assignment from another CStrA. */ - inline CStrA &operator=(const CStrA &src) - { - if (&src != this) { - base::Clear(); - base::AppendRaw(src.Data(), src.Size()); - base::FixTail(); - } - return *this; - } - - /** Lower-than operator (to support stl collections) */ - inline bool operator<(const CStrA &other) const - { - return strcmp(base::Data(), other.Data()) < 0; - } - - /** Add formatted string (like vsprintf) at the end of existing contents. */ - int AddFormatL(const char *format, va_list args) WARN_FORMAT(2, 0) - { - size_t addSize = std::max(strlen(format), 16); - addSize += addSize / 2; - int ret; - int err = 0; - for (;;) { - char *buf = MakeFreeSpace(addSize); - ret = vseprintf(buf, buf + base::GetReserve() - 1, format, args); - if (ret >= (int)base::GetReserve()) { - /* Greater return than given count means needed buffer size. */ - addSize = ret + 1; - continue; - } - if (ret >= 0) { - /* success */ - break; - } - err = errno; - if (err != ERANGE && err != ENOENT && err != 0) { - /* some strange failure */ - break; - } - /* small buffer (M$ implementation) */ - addSize *= 2; - } - if (ret > 0) { - GrowSizeNC(ret); - } else { - base::FixTail(); - } - return ret; - } - - /** Add formatted string (like sprintf) at the end of existing contents. */ - int CDECL WARN_FORMAT(2, 3) AddFormat(const char *format, ...) - { - va_list args; - va_start(args, format); - int ret = AddFormatL(format, args); - va_end(args); - return ret; - } - - /** Assign formatted string (like sprintf). */ - int CDECL WARN_FORMAT(2, 3) Format(const char *format, ...) - { - base::Free(); - va_list args; - va_start(args, format); - int ret = AddFormatL(format, args); - va_end(args); - return ret; - } -}; - -#endif /* STR_HPP */ diff --git a/src/pathfinder/yapf/yapf.hpp b/src/pathfinder/yapf/yapf.hpp index 867c6188ff..cac6c4c240 100644 --- a/src/pathfinder/yapf/yapf.hpp +++ b/src/pathfinder/yapf/yapf.hpp @@ -19,7 +19,6 @@ //#define inline inline #include "../../misc/blob.hpp" -#include "../../misc/str.hpp" #include "../../misc/fixedsizearray.hpp" #include "../../misc/array.hpp" #include "../../misc/hashtable.hpp" diff --git a/src/pathfinder/yapf/yapf_base.hpp b/src/pathfinder/yapf/yapf_base.hpp index 0bdd81dffe..c04fa2af92 100644 --- a/src/pathfinder/yapf/yapf_base.hpp +++ b/src/pathfinder/yapf/yapf_base.hpp @@ -317,7 +317,7 @@ public: void DumpBase(DumpTarget &dmp) const { dmp.WriteStructT("m_nodes", &m_nodes); - dmp.WriteLine("m_num_steps = %d", m_num_steps); + dmp.WriteValue("m_num_steps", m_num_steps); } /* methods that should be implemented at derived class Types::Tpf (derived from CYapfBaseT) */ diff --git a/src/pathfinder/yapf/yapf_node.hpp b/src/pathfinder/yapf/yapf_node.hpp index 689a8e9752..ce4ba3b289 100644 --- a/src/pathfinder/yapf/yapf_node.hpp +++ b/src/pathfinder/yapf/yapf_node.hpp @@ -126,8 +126,8 @@ struct CYapfNodeT { { dmp.WriteStructT("m_key", &m_key); dmp.WriteStructT("m_parent", m_parent); - dmp.WriteLine("m_cost = %d", m_cost); - dmp.WriteLine("m_estimate = %d", m_estimate); + dmp.WriteValue("m_cost", m_cost); + dmp.WriteValue("m_estimate", m_estimate); } }; diff --git a/src/pathfinder/yapf/yapf_node_rail.hpp b/src/pathfinder/yapf/yapf_node_rail.hpp index fce909ac9b..df369fcdd7 100644 --- a/src/pathfinder/yapf/yapf_node_rail.hpp +++ b/src/pathfinder/yapf/yapf_node_rail.hpp @@ -109,7 +109,7 @@ struct CYapfRailSegment dmp.WriteStructT("m_key", &m_key); dmp.WriteTile("m_last_tile", m_last_tile); dmp.WriteEnumT("m_last_td", m_last_td); - dmp.WriteLine("m_cost = %d", m_cost); + dmp.WriteValue("m_cost", m_cost); dmp.WriteTile("m_last_signal_tile", m_last_signal_tile); dmp.WriteEnumT("m_last_signal_td", m_last_signal_td); dmp.WriteEnumT("m_end_segment_reason", m_end_segment_reason); @@ -207,10 +207,10 @@ struct CYapfRailNodeT { base::Dump(dmp); dmp.WriteStructT("m_segment", m_segment); - dmp.WriteLine("m_num_signals_passed = %d", m_num_signals_passed); - dmp.WriteLine("m_targed_seen = %s", flags_u.flags_s.m_targed_seen ? "Yes" : "No"); - dmp.WriteLine("m_choice_seen = %s", flags_u.flags_s.m_choice_seen ? "Yes" : "No"); - dmp.WriteLine("m_last_signal_was_red = %s", flags_u.flags_s.m_last_signal_was_red ? "Yes" : "No"); + dmp.WriteValue("m_num_signals_passed", m_num_signals_passed); + dmp.WriteValue("m_targed_seen", flags_u.flags_s.m_targed_seen ? "Yes" : "No"); + dmp.WriteValue("m_choice_seen", flags_u.flags_s.m_choice_seen ? "Yes" : "No"); + dmp.WriteValue("m_last_signal_was_red", flags_u.flags_s.m_last_signal_was_red ? "Yes" : "No"); dmp.WriteEnumT("m_last_red_signal_type", m_last_red_signal_type); } }; diff --git a/src/pathfinder/yapf/yapf_rail.cpp b/src/pathfinder/yapf/yapf_rail.cpp index 6922c0b89d..3b6686f20d 100644 --- a/src/pathfinder/yapf/yapf_rail.cpp +++ b/src/pathfinder/yapf/yapf_rail.cpp @@ -28,8 +28,8 @@ template void DumpState(Tpf &pf1, Tpf &pf2) FILE *f2 = fopen("yapf2.txt", "wt"); assert(f1 != nullptr); assert(f2 != nullptr); - fwrite(dmp1.m_out.Data(), 1, dmp1.m_out.Size(), f1); - fwrite(dmp2.m_out.Data(), 1, dmp2.m_out.Size(), f2); + fwrite(dmp1.m_out.c_str(), 1, dmp1.m_out.size(), f1); + fwrite(dmp2.m_out.c_str(), 1, dmp2.m_out.size(), f2); fclose(f1); fclose(f2); } diff --git a/src/pathfinder/yapf/yapf_type.hpp b/src/pathfinder/yapf/yapf_type.hpp index ff63c1304a..4f301b0fb7 100644 --- a/src/pathfinder/yapf/yapf_type.hpp +++ b/src/pathfinder/yapf/yapf_type.hpp @@ -10,6 +10,9 @@ #ifndef YAPF_TYPE_HPP #define YAPF_TYPE_HPP +#include +#include + /* Enum used in PfCalcCost() to see why was the segment closed. */ enum EndSegmentReason { /* The following reasons can be saved into cached segment */ @@ -66,7 +69,7 @@ enum EndSegmentReasonBits { DECLARE_ENUM_AS_BIT_SET(EndSegmentReasonBits) -inline CStrA ValueStr(EndSegmentReasonBits bits) +inline std::string ValueStr(EndSegmentReasonBits bits) { static const char * const end_segment_reason_names[] = { "DEAD_END", "RAIL_TYPE", "INFINITE_LOOP", "SEGMENT_TOO_LONG", "CHOICE_FOLLOWS", @@ -74,9 +77,10 @@ inline CStrA ValueStr(EndSegmentReasonBits bits) "PATH_TOO_LONG", "FIRST_TWO_WAY_RED", "LOOK_AHEAD_END", "TARGET_REACHED" }; - CStrA out; - out.Format("0x%04X (%s)", bits, ComposeNameT(bits, end_segment_reason_names, "UNK", ESRB_NONE, "NONE").Data()); - return out.Transfer(); + std::stringstream ss; + ss << "0x" << std::setfill('0') << std::setw(4) << std::hex << bits; // 0x%04X + ss << " (" << ComposeNameT(bits, end_segment_reason_names, "UNK", ESRB_NONE, "NONE") << ")"; + return ss.str(); } #endif /* YAPF_TYPE_HPP */ From e8022a589d4f011cae4c3f449d91296ebcc3d947 Mon Sep 17 00:00:00 2001 From: Charles Pigott Date: Fri, 2 Apr 2021 21:16:51 +0100 Subject: [PATCH 009/268] Codechange: Replace CBlobT usage with std::vector --- src/pathfinder/yapf/yapf_costrail.hpp | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/src/pathfinder/yapf/yapf_costrail.hpp b/src/pathfinder/yapf/yapf_costrail.hpp index 2f4834633a..6bed27cba6 100644 --- a/src/pathfinder/yapf/yapf_costrail.hpp +++ b/src/pathfinder/yapf/yapf_costrail.hpp @@ -10,6 +10,8 @@ #ifndef YAPF_COSTRAIL_HPP #define YAPF_COSTRAIL_HPP +#include + #include "../../pbs.h" template @@ -52,9 +54,9 @@ protected: * @note maximum cost doesn't work with caching enabled * @todo fix maximum cost failing with caching (e.g. FS#2900) */ - int m_max_cost; - CBlobT m_sig_look_ahead_costs; - bool m_disable_cache; + int m_max_cost; + bool m_disable_cache; + std::vector m_sig_look_ahead_costs; public: bool m_stopped_on_first_two_way_signal; @@ -68,9 +70,10 @@ protected: int p0 = Yapf().PfGetSettings().rail_look_ahead_signal_p0; int p1 = Yapf().PfGetSettings().rail_look_ahead_signal_p1; int p2 = Yapf().PfGetSettings().rail_look_ahead_signal_p2; - int *pen = m_sig_look_ahead_costs.GrowSizeNC(Yapf().PfGetSettings().rail_look_ahead_max_signals); + m_sig_look_ahead_costs.clear(); + m_sig_look_ahead_costs.reserve(Yapf().PfGetSettings().rail_look_ahead_max_signals); for (uint i = 0; i < Yapf().PfGetSettings().rail_look_ahead_max_signals; i++) { - pen[i] = p0 + i * (p1 + i * p2); + m_sig_look_ahead_costs.push_back(p0 + i * (p1 + i * p2)); } } @@ -152,7 +155,7 @@ public: /** The cost for reserved tiles, including skipped ones. */ inline int ReservationCost(Node &n, TileIndex tile, Trackdir trackdir, int skipped) { - if (n.m_num_signals_passed >= m_sig_look_ahead_costs.Size() / 2) return 0; + if (n.m_num_signals_passed >= m_sig_look_ahead_costs.size() / 2) return 0; if (!IsPbsSignal(n.m_last_signal_type)) return 0; if (IsRailStationTile(tile) && IsAnyStationTileReserved(tile, trackdir, skipped)) { @@ -184,7 +187,7 @@ public: n.m_last_signal_type = sig_type; /* cache the look-ahead polynomial constant only if we didn't pass more signals than the look-ahead limit is */ - int look_ahead_cost = (n.m_num_signals_passed < m_sig_look_ahead_costs.Size()) ? m_sig_look_ahead_costs.Data()[n.m_num_signals_passed] : 0; + int look_ahead_cost = (n.m_num_signals_passed < m_sig_look_ahead_costs.size()) ? m_sig_look_ahead_costs[n.m_num_signals_passed] : 0; if (sig_state != SIGNAL_STATE_RED) { /* green signal */ n.flags_u.flags_s.m_last_signal_was_red = false; @@ -460,7 +463,7 @@ no_entry_cost: // jump here at the beginning if the node has no parent (it is th /* Apply min/max speed penalties only when inside the look-ahead radius. Otherwise * it would cause desync in MP. */ - if (n.m_num_signals_passed < m_sig_look_ahead_costs.Size()) + if (n.m_num_signals_passed < m_sig_look_ahead_costs.size()) { int min_speed = 0; int max_speed = tf->GetSpeedLimit(&min_speed); @@ -615,7 +618,7 @@ no_entry_cost: // jump here at the beginning if the node has no parent (it is th { return !m_disable_cache && (n.m_parent != nullptr) - && (n.m_parent->m_num_signals_passed >= m_sig_look_ahead_costs.Size()); + && (n.m_parent->m_num_signals_passed >= m_sig_look_ahead_costs.size()); } inline void ConnectNodeToCachedData(Node &n, CachedData &ci) From e8a94dc8bbffbf3a48c9826531e116af5ff31396 Mon Sep 17 00:00:00 2001 From: Charles Pigott Date: Fri, 2 Apr 2021 21:20:22 +0100 Subject: [PATCH 010/268] Cleanup: Delete remaining Blob code --- src/misc/CMakeLists.txt | 1 - src/misc/blob.hpp | 421 ----------------------------------- src/misc/dbg_helpers.cpp | 4 - src/pathfinder/yapf/yapf.hpp | 1 - 4 files changed, 427 deletions(-) delete mode 100644 src/misc/blob.hpp diff --git a/src/misc/CMakeLists.txt b/src/misc/CMakeLists.txt index 76780679f5..ee2ca6a41c 100644 --- a/src/misc/CMakeLists.txt +++ b/src/misc/CMakeLists.txt @@ -1,7 +1,6 @@ add_files( array.hpp binaryheap.hpp - blob.hpp countedobj.cpp countedptr.hpp dbg_helpers.cpp diff --git a/src/misc/blob.hpp b/src/misc/blob.hpp deleted file mode 100644 index 7784b6af6b..0000000000 --- a/src/misc/blob.hpp +++ /dev/null @@ -1,421 +0,0 @@ -/* - * This file is part of OpenTTD. - * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. - * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see . - */ - -/** @file blob.hpp Support for storing random binary data. */ - -#ifndef BLOB_HPP -#define BLOB_HPP - -#include "../core/alloc_func.hpp" - -/** - * Base class for simple binary blobs. - * Item is byte. - * The word 'simple' means: - * - no configurable allocator type (always made from heap) - * - no smart deallocation - deallocation must be called from the same - * module (DLL) where the blob was allocated - * - no configurable allocation policy (how big blocks should be allocated) - * - no extra ownership policy (i.e. 'copy on write') when blob is copied - * - no thread synchronization at all - * - * Internal member layout: - * 1. The only class member is pointer to the first item (see union). - * 2. Allocated block contains the blob header (see BlobHeader) followed by the raw byte data. - * Always, when it allocates memory the allocated size is: - * sizeof(BlobHeader) + - * 3. Two 'virtual' members (items and capacity) are stored in the BlobHeader at beginning - * of the allocated block. - * 4. The pointer of the union pobsize_ts behind the header (to the first data byte). - * When memory block is allocated, the sizeof(BlobHeader) it added to it. - * 5. Benefits of this layout: - * - items are accessed in the simplest possible way - just dereferencing the pointer, - * which is good for performance (assuming that data are accessed most often). - * - sizeof(blob) is the same as the size of any other pointer - * 6. Drawbacks of this layout: - * - the fact that a pointer to the allocated block is adjusted by sizeof(BlobHeader) before - * it is stored can lead to several confusions: - * - it is not a common pattern so the implementation code is bit harder to read. - * - valgrind may generate a warning that the allocated block is lost (not accessible). - */ -class ByteBlob { -protected: - /** header of the allocated memory block */ - struct BlobHeader { - size_t items; ///< actual blob size in bytes - size_t capacity; ///< maximum (allocated) size in bytes - }; - - /** type used as class member */ - union { - byte *data; ///< ptr to the first byte of data - BlobHeader *header; ///< ptr just after the BlobHeader holding items and capacity - }; - -private: - /** - * Just to silence an unsilencable GCC 4.4+ warning - * Note: This cannot be 'const' as we do a lot of 'hdrEmpty[0]->items += 0;' and 'hdrEmpty[0]->capacity += 0;' - * after const_casting. - */ - static BlobHeader hdrEmpty[]; - -public: - static const size_t tail_reserve = 4; ///< four extra bytes will be always allocated and zeroed at the end - static const size_t header_size = sizeof(BlobHeader); - - /** default constructor - initializes empty blob */ - inline ByteBlob() - { - InitEmpty(); - } - - /** copy constructor */ - inline ByteBlob(const ByteBlob &src) - { - InitEmpty(); - AppendRaw(src); - } - - /** move constructor - take ownership of blob data */ - inline ByteBlob(BlobHeader * const & src) - { - assert(src != nullptr); - header = src; - *const_cast(&src) = nullptr; - } - - /** destructor */ - inline ~ByteBlob() - { - Free(); - } - -protected: - /** all allocation should happen here */ - static inline BlobHeader *RawAlloc(size_t num_bytes) - { - return (BlobHeader*)MallocT(num_bytes); - } - - /** - * Return header pointer to the static BlobHeader with - * both items and capacity containing zero - */ - static inline BlobHeader *Zero() - { - return const_cast(&ByteBlob::hdrEmpty[1]); - } - - /** simple allocation policy - can be optimized later */ - static inline size_t AllocPolicy(size_t min_alloc) - { - if (min_alloc < (1 << 9)) { - if (min_alloc < (1 << 5)) return (1 << 5); - return (min_alloc < (1 << 7)) ? (1 << 7) : (1 << 9); - } - if (min_alloc < (1 << 15)) { - if (min_alloc < (1 << 11)) return (1 << 11); - return (min_alloc < (1 << 13)) ? (1 << 13) : (1 << 15); - } - if (min_alloc < (1 << 20)) { - if (min_alloc < (1 << 17)) return (1 << 17); - return (min_alloc < (1 << 19)) ? (1 << 19) : (1 << 20); - } - min_alloc = (min_alloc | ((1 << 20) - 1)) + 1; - return min_alloc; - } - - /** all deallocations should happen here */ - static inline void RawFree(BlobHeader *p) - { - /* Just to silence an unsilencable GCC 4.4+ warning. */ - assert(p != ByteBlob::hdrEmpty); - - /* In case GCC warns about the following, see GCC's PR38509 why it is bogus. */ - free(p); - } - - /** initialize the empty blob */ - inline void InitEmpty() - { - header = Zero(); - } - - /** initialize blob by attaching it to the given header followed by data */ - inline void Init(BlobHeader *src) - { - header = &src[1]; - } - - /** blob header accessor - use it rather than using the pointer arithmetic directly - non-const version */ - inline BlobHeader& Hdr() - { - return *(header - 1); - } - - /** blob header accessor - use it rather than using the pointer arithmetic directly - const version */ - inline const BlobHeader& Hdr() const - { - return *(header - 1); - } - - /** return reference to the actual blob size - used when the size needs to be modified */ - inline size_t& LengthRef() - { - return Hdr().items; - } - -public: - /** return true if blob doesn't contain valid data */ - inline bool IsEmpty() const - { - return Length() == 0; - } - - /** return the number of valid data bytes in the blob */ - inline size_t Length() const - { - return Hdr().items; - } - - /** return the current blob capacity in bytes */ - inline size_t Capacity() const - { - return Hdr().capacity; - } - - /** return pointer to the first byte of data - non-const version */ - inline byte *Begin() - { - return data; - } - - /** return pointer to the first byte of data - const version */ - inline const byte *Begin() const - { - return data; - } - - /** invalidate blob's data - doesn't free buffer */ - inline void Clear() - { - LengthRef() = 0; - } - - /** free the blob's memory */ - inline void Free() - { - if (Capacity() > 0) { - RawFree(&Hdr()); - InitEmpty(); - } - } - - /** append new bytes at the end of existing data bytes - reallocates if necessary */ - inline void AppendRaw(const void *p, size_t num_bytes) - { - assert(p != nullptr); - if (num_bytes > 0) { - memcpy(Append(num_bytes), p, num_bytes); - } - } - - /** append bytes from given source blob to the end of existing data bytes - reallocates if necessary */ - inline void AppendRaw(const ByteBlob& src) - { - if (!src.IsEmpty()) { - memcpy(Append(src.Length()), src.Begin(), src.Length()); - } - } - - /** - * Reallocate if there is no free space for num_bytes bytes. - * @return pointer to the new data to be added - */ - inline byte *Prepare(size_t num_bytes) - { - size_t new_size = Length() + num_bytes; - if (new_size > Capacity()) SmartAlloc(new_size); - return data + Length(); - } - - /** - * Increase Length() by num_bytes. - * @return pointer to the new data added - */ - inline byte *Append(size_t num_bytes) - { - byte *pNewData = Prepare(num_bytes); - LengthRef() += num_bytes; - return pNewData; - } - - /** reallocate blob data if needed */ - void SmartAlloc(size_t new_size) - { - if (Capacity() >= new_size) return; - /* calculate minimum block size we need to allocate - * and ask allocation policy for some reasonable block size */ - assert(new_size < SIZE_MAX - header_size - tail_reserve); - new_size = AllocPolicy(header_size + new_size + tail_reserve); - - /* allocate new block and setup header */ - BlobHeader *tmp = RawAlloc(new_size); - tmp->items = Length(); - tmp->capacity = new_size - (header_size + tail_reserve); - - /* copy existing data */ - if (tmp->items != 0) { - memcpy(tmp + 1, data, tmp->items); - } - - /* replace our block with new one */ - if (Capacity() > 0) { - RawFree(&Hdr()); - } - Init(tmp); - } - - /** fixing the four bytes at the end of blob data - useful when blob is used to hold string */ - inline void FixTail() const - { - if (Capacity() > 0) { - byte *p = &data[Length()]; - for (uint i = 0; i < tail_reserve; i++) { - p[i] = 0; - } - } - } -}; - -/** - * Blob - simple dynamic T array. T (template argument) is a placeholder for any type. - * T can be any integral type, pointer, or structure. Using Blob instead of just plain C array - * simplifies the resource management in several ways: - * 1. When adding new item(s) it automatically grows capacity if needed. - * 2. When variable of type Blob comes out of scope it automatically frees the data buffer. - * 3. Takes care about the actual data size (number of used items). - * 4. Dynamically constructs only used items (as opposite of static array which constructs all items) - */ -template -class CBlobT : public ByteBlob { - /* make template arguments public: */ -public: - typedef ByteBlob base; - - static const size_t type_size = sizeof(T); - - struct OnTransfer { - typename base::BlobHeader *header; - - OnTransfer(const OnTransfer& src) : header(src.header) - { - assert(src.header != nullptr); - *const_cast(&src.header) = nullptr; - } - - OnTransfer(CBlobT& src) : header(src.header) - { - src.InitEmpty(); - } - - ~OnTransfer() - { - assert(header == nullptr); - } - }; - - /** Default constructor - makes new Blob ready to accept any data */ - inline CBlobT() - : base() - {} - - /** Take ownership constructor */ - inline CBlobT(const OnTransfer& ot) - : base(ot.header) - {} - - /** Destructor - ensures that allocated memory (if any) is freed */ - inline ~CBlobT() - { - Free(); - } - - /** Check the validity of item index (only in debug mode) */ - inline void CheckIdx(size_t index) const - { - assert(index < Size()); - } - - /** Return pointer to the first data item - non-const version */ - inline T *Data() - { - return (T*)base::Begin(); - } - - /** Return pointer to the first data item - const version */ - inline const T *Data() const - { - return (const T*)base::Begin(); - } - - /** Return pointer to the index-th data item - non-const version */ - inline T *Data(size_t index) - { - CheckIdx(index); - return (Data() + index); - } - - /** Return pointer to the index-th data item - const version */ - inline const T *Data(size_t index) const - { - CheckIdx(index); - return (Data() + index); - } - - /** Return number of items in the Blob */ - inline size_t Size() const - { - return (base::Length() / type_size); - } - - /** Return total number of items that can fit in the Blob without buffer reallocation */ - inline size_t MaxSize() const - { - return (base::Capacity() / type_size); - } - - /** Return number of additional items that can fit in the Blob without buffer reallocation */ - inline size_t GetReserve() const - { - return ((base::Capacity() - base::Length()) / type_size); - } - - /** Grow number of data items in Blob by given number - doesn't construct items */ - inline T *GrowSizeNC(size_t num_items) - { - return (T*)base::Append(num_items * type_size); - } - - /** - * Ensures that given number of items can be added to the end of Blob. Returns pointer to the - * first free (unused) item - */ - inline T *MakeFreeSpace(size_t num_items) - { - return (T*)base::Prepare(num_items * type_size); - } - - inline OnTransfer Transfer() - { - return OnTransfer(*this); - } -}; - - -#endif /* BLOB_HPP */ diff --git a/src/misc/dbg_helpers.cpp b/src/misc/dbg_helpers.cpp index 85d6ea1b91..b197e9a8af 100644 --- a/src/misc/dbg_helpers.cpp +++ b/src/misc/dbg_helpers.cpp @@ -10,7 +10,6 @@ #include "../stdafx.h" #include "../rail_map.h" #include "dbg_helpers.h" -#include "blob.hpp" #include #include @@ -169,6 +168,3 @@ void DumpTarget::EndStruct() /* remove current struct name from the stack */ m_cur_struct.pop(); } - -/** Just to silence an unsilencable GCC 4.4+ warning */ -/* static */ ByteBlob::BlobHeader ByteBlob::hdrEmpty[] = {{0, 0}, {0, 0}}; diff --git a/src/pathfinder/yapf/yapf.hpp b/src/pathfinder/yapf/yapf.hpp index cac6c4c240..134c0dcf98 100644 --- a/src/pathfinder/yapf/yapf.hpp +++ b/src/pathfinder/yapf/yapf.hpp @@ -18,7 +18,6 @@ //#undef FORCEINLINE //#define inline inline -#include "../../misc/blob.hpp" #include "../../misc/fixedsizearray.hpp" #include "../../misc/array.hpp" #include "../../misc/hashtable.hpp" From 295d5429114bd11280f910a1f5e9fd1367886c1a Mon Sep 17 00:00:00 2001 From: translators Date: Sun, 4 Apr 2021 17:52:55 +0000 Subject: [PATCH 011/268] Update: Translations from eints spanish (mexican): 1 change by absay vietnamese: 1 change by KhoiCanDev estonian: 1 change by siimsoni romanian: 3 changes by kneekoo catalan: 1 change by perezdidac portuguese: 1 change by azulcosta --- src/lang/catalan.txt | 2 +- src/lang/estonian.txt | 2 +- src/lang/portuguese.txt | 2 +- src/lang/romanian.txt | 4 +++- src/lang/spanish_MX.txt | 2 +- src/lang/vietnamese.txt | 2 +- 6 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/lang/catalan.txt b/src/lang/catalan.txt index 91ac2bbdd5..bd9166e6ea 100644 --- a/src/lang/catalan.txt +++ b/src/lang/catalan.txt @@ -2532,7 +2532,7 @@ STR_WATERWAYS_TOOLBAR_BUILD_DOCK_TOOLTIP :{BLACK}Construe STR_WATERWAYS_TOOLBAR_BUOY_TOOLTIP :{BLACK}Situa una boia que pot ser útil per fer punts de control addicionals. Shift commuta construeix/mostra el cost estimat STR_WATERWAYS_TOOLBAR_BUILD_AQUEDUCT_TOOLTIP :{BLACK} Construeix aqüeducte. Shift commuta construeix/mostra el cost estimat STR_WATERWAYS_TOOLBAR_CREATE_LAKE_TOOLTIP :{BLACK}Defineix caselles com a canals d'aigua.{}Amb Ctrl+Clic a nivell de mar, es defineix una casella de mar i s'inundaran els seus voltants. -STR_WATERWAYS_TOOLBAR_CREATE_RIVER_TOOLTIP :{BLACK}Crea rius i caselles d'aigua. +STR_WATERWAYS_TOOLBAR_CREATE_RIVER_TOOLTIP :{BLACK}Crea rius i caselles d'aigua. Ctrl selecciona l'àrea diagonalment. # Ship depot construction window STR_DEPOT_BUILD_SHIP_CAPTION :{WHITE}Drassanes diff --git a/src/lang/estonian.txt b/src/lang/estonian.txt index 8e5bad9bcf..da66065e5a 100644 --- a/src/lang/estonian.txt +++ b/src/lang/estonian.txt @@ -2589,7 +2589,7 @@ STR_WATERWAYS_TOOLBAR_BUILD_DOCK_TOOLTIP :{BLACK}Ehita la STR_WATERWAYS_TOOLBAR_BUOY_TOOLTIP :{BLACK}Paigalda poi, mis on kasutatav teemärgisena. Shift valib ehitamise/hinna kuvamise režiimi STR_WATERWAYS_TOOLBAR_BUILD_AQUEDUCT_TOOLTIP :{BLACK}Ehita veesild. Shift valib ehitamise/hinna kuvamise režiimi STR_WATERWAYS_TOOLBAR_CREATE_LAKE_TOOLTIP :{BLACK}Määratle veealad.{}Ehita kanal. Veekõrgusel Ctrl-klahvi all hoidmine ujutab ümbruskonna üle -STR_WATERWAYS_TOOLBAR_CREATE_RIVER_TOOLTIP :{BLACK}Jõgede paigutamine +STR_WATERWAYS_TOOLBAR_CREATE_RIVER_TOOLTIP :{BLACK}Jõgede paigutamine. Ctrl valib ala põiki # Ship depot construction window STR_DEPOT_BUILD_SHIP_CAPTION :{WHITE}Laevaremonditehase suund diff --git a/src/lang/portuguese.txt b/src/lang/portuguese.txt index 6c2bb390cf..203cd9327d 100644 --- a/src/lang/portuguese.txt +++ b/src/lang/portuguese.txt @@ -2532,7 +2532,7 @@ STR_WATERWAYS_TOOLBAR_BUILD_DOCK_TOOLTIP :{BLACK}Construi STR_WATERWAYS_TOOLBAR_BUOY_TOOLTIP :{BLACK}Esta bóia de posição pode ser usada para marcar pontos de rota adicionais. Shift alterna construção/mostra de custos estimados STR_WATERWAYS_TOOLBAR_BUILD_AQUEDUCT_TOOLTIP :{BLACK}Construir aqueduto. Shift alterna contruir/mostrar custo estimado STR_WATERWAYS_TOOLBAR_CREATE_LAKE_TOOLTIP :{BLACK}Definir área de água.{}Construir um canal, a não ser que a tecla Ctrl esteja pressionada a nível do mar, nesse caso inundará as zonas circundantes -STR_WATERWAYS_TOOLBAR_CREATE_RIVER_TOOLTIP :{BLACK}Colocar rios +STR_WATERWAYS_TOOLBAR_CREATE_RIVER_TOOLTIP :{BLACK}Colocar rios. Tecla "Ctrl" seleciona a área diagonalmente # Ship depot construction window STR_DEPOT_BUILD_SHIP_CAPTION :{WHITE}Orientação do Depósito diff --git a/src/lang/romanian.txt b/src/lang/romanian.txt index 4374816aee..889268e22e 100644 --- a/src/lang/romanian.txt +++ b/src/lang/romanian.txt @@ -2268,7 +2268,7 @@ STR_CONTENT_DOWNLOAD_PROGRESS_SIZE :{WHITE}{BYTES} # Content downloading error messages STR_CONTENT_ERROR_COULD_NOT_CONNECT :{WHITE}Conectare la server eşuată... -STR_CONTENT_ERROR_COULD_NOT_DOWNLOAD :{WHITE}Descărcare eşuată +STR_CONTENT_ERROR_COULD_NOT_DOWNLOAD :{WHITE}Descărcare eșuată STR_CONTENT_ERROR_COULD_NOT_DOWNLOAD_CONNECTION_LOST :{WHITE}... conexiune întreruptă STR_CONTENT_ERROR_COULD_NOT_DOWNLOAD_FILE_NOT_WRITABLE :{WHITE}... fişierul nu poate fi scris STR_CONTENT_ERROR_COULD_NOT_EXTRACT :{WHITE}Fişierul descărcat nu a putut fi decompresat @@ -2278,6 +2278,7 @@ STR_MISSING_GRAPHICS_SET_MESSAGE :{BLACK}OpenTTD STR_MISSING_GRAPHICS_YES_DOWNLOAD :{BLACK}Da, descarcă pachetele grafice STR_MISSING_GRAPHICS_NO_QUIT :{BLACK}Nu, ieși din OpenTTD +STR_MISSING_GRAPHICS_ERROR_TITLE :{WHITE}Descărcarea a eșuat STR_MISSING_GRAPHICS_ERROR :{BLACK}Descărcarea graficii a eșuat.{}Vă rugăm descărcați manual grafica. # Transparency settings window @@ -2744,6 +2745,7 @@ STR_FRAMERATE_GAMESCRIPT :{BLACK} Script STR_FRAMETIME_CAPTION_GAMELOOP :Buclă de joc STR_FRAMETIME_CAPTION_GL_ECONOMY :Manipularea încărcăturilor STR_FRAMETIME_CAPTION_DRAWING_VIEWPORTS :Randarea vizorului global +STR_FRAMETIME_CAPTION_SOUND :Mixaj de sunet STR_FRAMETIME_CAPTION_AI :IA {NUM} {STRING} ############ End of leave-in-this-order diff --git a/src/lang/spanish_MX.txt b/src/lang/spanish_MX.txt index d47e13c86e..b2be26e6be 100644 --- a/src/lang/spanish_MX.txt +++ b/src/lang/spanish_MX.txt @@ -2532,7 +2532,7 @@ STR_WATERWAYS_TOOLBAR_BUILD_DOCK_TOOLTIP :{BLACK}Construi STR_WATERWAYS_TOOLBAR_BUOY_TOOLTIP :{BLACK}Colocar boya para utilizar como punto de ruta marítimo. Mayús muestra una estimación del precio STR_WATERWAYS_TOOLBAR_BUILD_AQUEDUCT_TOOLTIP :{BLACK}Construir acueducto. Mayús muestra una estimación del precio STR_WATERWAYS_TOOLBAR_CREATE_LAKE_TOOLTIP :{BLACK}Definir cuerpo de agua.{}Crea un canal, a menos que se pulse Ctrl en un área al nivel del mar, en cuyo caso se inundarán los alrededores -STR_WATERWAYS_TOOLBAR_CREATE_RIVER_TOOLTIP :{BLACK}Colocar ríos +STR_WATERWAYS_TOOLBAR_CREATE_RIVER_TOOLTIP :{BLACK}Colocar ríos. Ctrl para seleccionar un área en diagonal # Ship depot construction window STR_DEPOT_BUILD_SHIP_CAPTION :{WHITE}Orientación del astillero diff --git a/src/lang/vietnamese.txt b/src/lang/vietnamese.txt index 51b291ccab..6fd16d0cb7 100644 --- a/src/lang/vietnamese.txt +++ b/src/lang/vietnamese.txt @@ -2531,7 +2531,7 @@ STR_WATERWAYS_TOOLBAR_BUILD_DOCK_TOOLTIP :{BLACK}Xây c STR_WATERWAYS_TOOLBAR_BUOY_TOOLTIP :{BLACK}Đặt một phao hàng hải như là điểm mốc trên đường đi. Shift+Click để xem chi phí dự tính STR_WATERWAYS_TOOLBAR_BUILD_AQUEDUCT_TOOLTIP :{BLACK}Xây cống. Shift+Click để xem chi phí dự tính STR_WATERWAYS_TOOLBAR_CREATE_LAKE_TOOLTIP :{BLACK}Đặt tên vùng biển.{}Tạo một kênh đào, nếu ấn Ctrl thì giữ xuống tới mực nước biển, không thì sẽ bị ngập nước -STR_WATERWAYS_TOOLBAR_CREATE_RIVER_TOOLTIP :{BLACK}Đặt sông. +STR_WATERWAYS_TOOLBAR_CREATE_RIVER_TOOLTIP :{BLACK}Đặt sông. Giữ Ctrl để chọn đường chéo # Ship depot construction window STR_DEPOT_BUILD_SHIP_CAPTION :{WHITE}Hướng Xưởng Tàu From 1cd3a3b070a9e17acdbe9a875a305643bc834e6a Mon Sep 17 00:00:00 2001 From: Michael Lutz Date: Mon, 5 Apr 2021 12:18:45 +0200 Subject: [PATCH 012/268] Fix #8935: [OSX] Crash when clicking 'Save' due to wrongly-threaded OS call. (#8944) --- src/video/cocoa/cocoa_v.mm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/video/cocoa/cocoa_v.mm b/src/video/cocoa/cocoa_v.mm index 9fb74cd041..c16d196906 100644 --- a/src/video/cocoa/cocoa_v.mm +++ b/src/video/cocoa/cocoa_v.mm @@ -224,7 +224,7 @@ bool VideoDriver_Cocoa::AfterBlitterChange() */ void VideoDriver_Cocoa::EditBoxLostFocus() { - [ [ this->cocoaview inputContext ] discardMarkedText ]; + [ [ this->cocoaview inputContext ] performSelectorOnMainThread:@selector(discardMarkedText) withObject:nil waitUntilDone:[ NSThread isMainThread ] ]; /* Clear any marked string from the current edit box. */ HandleTextInput(nullptr, true); } From 130a052ed5a364c1b9c4659b44fe2620bdd569e4 Mon Sep 17 00:00:00 2001 From: PeterN Date: Mon, 5 Apr 2021 11:18:59 +0100 Subject: [PATCH 013/268] Fix: Apply master effect volume during mixing instead of sound start. (#8945) This makes the volume control work as most players would expect, affecting existing playing sounds as well as new sounds. --- src/mixer.cpp | 19 +++++++++++-------- src/sound.cpp | 4 ++-- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/src/mixer.cpp b/src/mixer.cpp index 90e3951cb0..0a41bb7c71 100644 --- a/src/mixer.cpp +++ b/src/mixer.cpp @@ -11,6 +11,7 @@ #include #include "core/math_func.hpp" #include "framerate_type.h" +#include "settings_type.h" #include "safeguards.h" #include "mixer.h" @@ -60,7 +61,7 @@ static int RateConversion(T *b, int frac_pos) return ((b[0] * ((1 << 16) - frac_pos)) + (b[1] * frac_pos)) >> 16; } -static void mix_int16(MixerChannel *sc, int16 *buffer, uint samples) +static void mix_int16(MixerChannel *sc, int16 *buffer, uint samples, uint8 effect_vol) { if (samples > sc->samples_left) samples = sc->samples_left; sc->samples_left -= samples; @@ -69,8 +70,8 @@ static void mix_int16(MixerChannel *sc, int16 *buffer, uint samples) const int16 *b = (const int16 *)sc->memory + sc->pos; uint32 frac_pos = sc->frac_pos; uint32 frac_speed = sc->frac_speed; - int volume_left = sc->volume_left; - int volume_right = sc->volume_right; + int volume_left = sc->volume_left * effect_vol / 255; + int volume_right = sc->volume_right * effect_vol / 255; if (frac_speed == 0x10000) { /* Special case when frac_speed is 0x10000 */ @@ -96,7 +97,7 @@ static void mix_int16(MixerChannel *sc, int16 *buffer, uint samples) sc->pos = b - (const int16 *)sc->memory; } -static void mix_int8_to_int16(MixerChannel *sc, int16 *buffer, uint samples) +static void mix_int8_to_int16(MixerChannel *sc, int16 *buffer, uint samples, uint8 effect_vol) { if (samples > sc->samples_left) samples = sc->samples_left; sc->samples_left -= samples; @@ -105,8 +106,8 @@ static void mix_int8_to_int16(MixerChannel *sc, int16 *buffer, uint samples) const int8 *b = sc->memory + sc->pos; uint32 frac_pos = sc->frac_pos; uint32 frac_speed = sc->frac_speed; - int volume_left = sc->volume_left; - int volume_right = sc->volume_right; + int volume_left = sc->volume_left * effect_vol / 255; + int volume_right = sc->volume_right * effect_vol / 255; if (frac_speed == 0x10000) { /* Special case when frac_speed is 0x10000 */ @@ -154,13 +155,15 @@ void MxMixSamples(void *buffer, uint samples) /* Fetch music if a sampled stream is available */ if (_music_stream) _music_stream((int16*)buffer, samples); + uint8 effect_vol = _settings_client.music.effect_vol; + /* Mix each channel */ for (mc = _channels; mc != endof(_channels); mc++) { if (mc->active) { if (mc->is16bit) { - mix_int16(mc, (int16*)buffer, samples); + mix_int16(mc, (int16*)buffer, samples, effect_vol); } else { - mix_int8_to_int16(mc, (int16*)buffer, samples); + mix_int8_to_int16(mc, (int16*)buffer, samples, effect_vol); } if (mc->samples_left == 0) MxCloseChannel(mc); } diff --git a/src/sound.cpp b/src/sound.cpp index e6c1cb07c8..7d70fa760d 100644 --- a/src/sound.cpp +++ b/src/sound.cpp @@ -252,7 +252,7 @@ static void SndPlayScreenCoordFx(SoundID sound, int left, int right, int top, in StartSound( sound, panning, - (_settings_client.music.effect_vol * _vol_factor_by_zoom[vp->zoom - ZOOM_LVL_BEGIN]) / 256 + _vol_factor_by_zoom[vp->zoom - ZOOM_LVL_BEGIN] ); return; } @@ -281,7 +281,7 @@ void SndPlayVehicleFx(SoundID sound, const Vehicle *v) void SndPlayFx(SoundID sound) { - StartSound(sound, 0.5, _settings_client.music.effect_vol); + StartSound(sound, 0.5, UINT8_MAX); } INSTANTIATE_BASE_MEDIA_METHODS(BaseMedia, SoundsSet) From 43c465e8f4aa2585a8454ae656513e4c8555aa75 Mon Sep 17 00:00:00 2001 From: Didac Perez Parera Date: Mon, 5 Apr 2021 13:24:03 -0700 Subject: [PATCH 014/268] Change: Disable NewGRF window apply button if no change was made (#8934) --- src/newgrf_gui.cpp | 52 ++++++++++++++++++++++++++++++++++------------ src/window_type.h | 8 ++++--- 2 files changed, 44 insertions(+), 16 deletions(-) diff --git a/src/newgrf_gui.cpp b/src/newgrf_gui.cpp index 9ed21e5c57..ad37b35425 100644 --- a/src/newgrf_gui.cpp +++ b/src/newgrf_gui.cpp @@ -612,6 +612,7 @@ struct NewGRFWindow : public Window, NewGRFScanCallback { bool execute; ///< On pressing 'apply changes' are grf changes applied immediately, or only list is updated. int preset; ///< Selected preset or \c -1 if none selected. int active_over; ///< Active GRF item over which another one is dragged, \c -1 if none. + bool modified; ///< The list of active NewGRFs has been modified since the last time they got saved. Scrollbar *vscroll; Scrollbar *vscroll2; @@ -654,7 +655,7 @@ struct NewGRFWindow : public Window, NewGRFScanCallback { this->avails.SetFilterFuncs(this->filter_funcs); this->avails.ForceRebuild(); - this->OnInvalidateData(GOID_NEWGRF_LIST_EDITED); + this->OnInvalidateData(GOID_NEWGRF_CURRENT_LOADED); } ~NewGRFWindow() @@ -663,7 +664,7 @@ struct NewGRFWindow : public Window, NewGRFScanCallback { DeleteWindowByClass(WC_TEXTFILE); DeleteWindowByClass(WC_SAVE_PRESET); - if (this->editable && !this->execute && !_exit_game) { + if (this->editable && this->modified && !this->execute && !_exit_game) { CopyGRFConfigList(this->orig_list, this->actives, true); ResetGRFConfig(false); ReloadNewGRFData(); @@ -970,7 +971,7 @@ struct NewGRFWindow : public Window, NewGRFScanCallback { } this->vscroll->ScrollTowards(pos); this->preset = -1; - this->InvalidateData(); + this->InvalidateData(GOID_NEWGRF_LIST_EDITED); break; } @@ -989,7 +990,7 @@ struct NewGRFWindow : public Window, NewGRFScanCallback { } this->vscroll->ScrollTowards(pos); this->preset = -1; - this->InvalidateData(); + this->InvalidateData(GOID_NEWGRF_LIST_EDITED); break; } @@ -1095,6 +1096,7 @@ struct NewGRFWindow : public Window, NewGRFScanCallback { CopyGRFConfigList(this->orig_list, this->actives, true); ResetGRFConfig(false); ReloadNewGRFData(); + this->InvalidateData(GOID_NEWGRF_CHANGES_APPLIED); } this->DeleteChildWindows(WC_QUERY_STRING); // Remove the parameter query window break; @@ -1104,6 +1106,7 @@ struct NewGRFWindow : public Window, NewGRFScanCallback { if (this->active_sel == nullptr || !this->show_params || this->active_sel->num_valid_params == 0) break; OpenGRFParameterWindow(this->active_sel, this->editable); + this->InvalidateData(GOID_NEWGRF_CHANGES_MADE); break; } @@ -1111,6 +1114,7 @@ struct NewGRFWindow : public Window, NewGRFScanCallback { if (this->active_sel != nullptr && this->editable) { this->active_sel->palette ^= GRFP_USE_MASK; this->SetDirty(); + this->InvalidateData(GOID_NEWGRF_CHANGES_MADE); } break; @@ -1157,7 +1161,7 @@ struct NewGRFWindow : public Window, NewGRFScanCallback { DeleteWindowByClass(WC_GRF_PARAMETERS); DeleteWindowByClass(WC_TEXTFILE); this->active_sel = nullptr; - this->InvalidateData(GOID_NEWGRF_PRESET_LOADED); + this->InvalidateData(GOID_NEWGRF_CHANGES_MADE); } void OnQueryTextFinished(char *str) override @@ -1178,6 +1182,20 @@ struct NewGRFWindow : public Window, NewGRFScanCallback { this->InvalidateData(); } + /** + * Updates the scroll bars for the active and inactive NewGRF lists. + */ + void UpdateScrollBars() + { + /* Update scrollbars */ + int i = 0; + for (const GRFConfig *c = this->actives; c != nullptr; c = c->next, i++) {} + + this->vscroll->SetCount(i + 1); // Reserve empty space for drag and drop handling. + + if (this->avail_pos >= 0) this->vscroll2->ScrollTowards(this->avail_pos); + } + /** * Some data on this window has become invalid. * @param data Information about the changed data. @see GameOptionsInvalidationData @@ -1212,27 +1230,34 @@ struct NewGRFWindow : public Window, NewGRFScanCallback { this->avails.ForceRebuild(); FALLTHROUGH; + case GOID_NEWGRF_CURRENT_LOADED: + this->modified = false; + UpdateScrollBars(); + break; + case GOID_NEWGRF_LIST_EDITED: this->preset = -1; FALLTHROUGH; - case GOID_NEWGRF_PRESET_LOADED: { - /* Update scrollbars */ - int i = 0; - for (const GRFConfig *c = this->actives; c != nullptr; c = c->next, i++) {} + case GOID_NEWGRF_CHANGES_MADE: + UpdateScrollBars(); - this->vscroll->SetCount(i + 1); // Reserve empty space for drag and drop handling. + /* Changes have been made to the list of active NewGRFs */ + this->modified = true; - if (this->avail_pos >= 0) this->vscroll2->ScrollTowards(this->avail_pos); break; - } + + case GOID_NEWGRF_CHANGES_APPLIED: + /* No changes have been made to the list of active NewGRFs since the last time the changes got applied */ + this->modified = false; + break; } this->BuildAvailables(); + this->SetWidgetDisabledState(WID_NS_APPLY_CHANGES, !this->editable || !this->modified); this->SetWidgetsDisabledState(!this->editable, WID_NS_PRESET_LIST, - WID_NS_APPLY_CHANGES, WID_NS_TOGGLE_PALETTE, WIDGET_LIST_END ); @@ -1973,6 +1998,7 @@ static void NewGRFConfirmationCallback(Window *w, bool confirmed) for (c = nw->actives; c != nullptr && i > 0; c = c->next, i--) {} nw->active_sel = c; nw->avails.ForceRebuild(); + nw->modified = false; w->InvalidateData(); diff --git a/src/window_type.h b/src/window_type.h index 86dcc6fb33..e4b08e6e5d 100644 --- a/src/window_type.h +++ b/src/window_type.h @@ -703,9 +703,11 @@ enum WindowClass { /** Data value for #Window::OnInvalidateData() of windows with class #WC_GAME_OPTIONS. */ enum GameOptionsInvalidationData { GOID_DEFAULT = 0, - GOID_NEWGRF_RESCANNED, ///< NewGRFs were just rescanned. - GOID_NEWGRF_LIST_EDITED, ///< List of active NewGRFs is being edited. - GOID_NEWGRF_PRESET_LOADED, ///< A NewGRF preset was picked. + GOID_NEWGRF_RESCANNED, ///< NewGRFs were just rescanned. + GOID_NEWGRF_CURRENT_LOADED, ///< The current list of active NewGRF has been loaded. + GOID_NEWGRF_LIST_EDITED, ///< List of active NewGRFs is being edited. + GOID_NEWGRF_CHANGES_MADE, ///< Changes have been made to a given NewGRF either through the palette or its parameters. + GOID_NEWGRF_CHANGES_APPLIED, ///< The active NewGRF list changes have been applied. }; struct Window; From ca783d447a831088e21c13919e4865353e98d54d Mon Sep 17 00:00:00 2001 From: Didac Perez Parera Date: Tue, 6 Apr 2021 03:47:44 -0700 Subject: [PATCH 015/268] Feature: Button to reset game settings to their default values (#8958) --- src/lang/english.txt | 3 ++ src/settings_gui.cpp | 52 ++++++++++++++++++++++++++++++++++- src/widgets/settings_widget.h | 1 + 3 files changed, 55 insertions(+), 1 deletion(-) diff --git a/src/lang/english.txt b/src/lang/english.txt index e55fcba1a1..0f4b4a99d4 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -1138,6 +1138,7 @@ STR_CONFIG_SETTING_TREE_CAPTION :{WHITE}Settings STR_CONFIG_SETTING_FILTER_TITLE :{BLACK}Filter string: STR_CONFIG_SETTING_EXPAND_ALL :{BLACK}Expand all STR_CONFIG_SETTING_COLLAPSE_ALL :{BLACK}Collapse all +STR_CONFIG_SETTING_RESET_ALL :{BLACK}Reset all values STR_CONFIG_SETTING_NO_EXPLANATION_AVAILABLE_HELPTEXT :(no explanation available) STR_CONFIG_SETTING_DEFAULT_VALUE :{LTBLUE}Default value: {ORANGE}{STRING1} STR_CONFIG_SETTING_TYPE :{LTBLUE}Setting type: {ORANGE}{STRING} @@ -1146,6 +1147,8 @@ STR_CONFIG_SETTING_TYPE_GAME_MENU :Game setting (s STR_CONFIG_SETTING_TYPE_GAME_INGAME :Game setting (stored in save; affects only current game) STR_CONFIG_SETTING_TYPE_COMPANY_MENU :Company setting (stored in saves; affects only new games) STR_CONFIG_SETTING_TYPE_COMPANY_INGAME :Company setting (stored in save; affects only current company) +STR_CONFIG_SETTING_RESET_ALL_CONFIRMATION_DIALOG_CAPTION :{WHITE}Caution! +STR_CONFIG_SETTING_RESET_ALL_CONFIRMATION_DIALOG_TEXT :{WHITE}This action will reset all game settings to their default values.{}Are you sure you want to proceed? STR_CONFIG_SETTING_RESTRICT_CATEGORY :{BLACK}Category: STR_CONFIG_SETTING_RESTRICT_TYPE :{BLACK}Type: diff --git a/src/settings_gui.cpp b/src/settings_gui.cpp index b754508777..68587c46a7 100644 --- a/src/settings_gui.cpp +++ b/src/settings_gui.cpp @@ -733,6 +733,7 @@ struct BaseSettingEntry { virtual void Init(byte level = 0); virtual void FoldAll() {} virtual void UnFoldAll() {} + virtual void ResetAll() = 0; /** * Set whether this is the last visible entry of the parent node. @@ -769,6 +770,7 @@ struct SettingEntry : BaseSettingEntry { SettingEntry(const char *name); virtual void Init(byte level = 0); + virtual void ResetAll(); virtual uint Length() const; virtual uint GetMaxHelpHeight(int maxw); virtual bool UpdateFilterState(SettingFilter &filter, bool force_visible); @@ -806,6 +808,7 @@ struct SettingsContainer { } void Init(byte level = 0); + void ResetAll(); void FoldAll(); void UnFoldAll(); @@ -828,6 +831,7 @@ struct SettingsPage : BaseSettingEntry, SettingsContainer { SettingsPage(StringID title); virtual void Init(byte level = 0); + virtual void ResetAll(); virtual void FoldAll(); virtual void UnFoldAll(); @@ -970,6 +974,13 @@ void SettingEntry::Init(byte level) assert(this->setting != nullptr); } +/* Sets the given setting entry to its default value */ +void SettingEntry::ResetAll() +{ + int32 default_value = ReadValue(&this->setting->desc.def, this->setting->save.conv); + SetSettingValue(this->index, default_value); +} + /** * Set the button-depressed flags (#SEF_LEFT_DEPRESSED and #SEF_RIGHT_DEPRESSED) to a specified value * @param new_val New value for the button flags @@ -1081,7 +1092,6 @@ bool SettingEntry::UpdateFilterState(SettingFilter &filter, bool force_visible) return visible; } - static const void *ResolveVariableAddress(const GameSettings *settings_ptr, const SettingDesc *sd) { if ((sd->desc.flags & SGF_PER_COMPANY) != 0) { @@ -1172,6 +1182,14 @@ void SettingsContainer::Init(byte level) } } +/** Resets all settings to their default values */ +void SettingsContainer::ResetAll() +{ + for (auto settings_entry : this->entries) { + settings_entry->ResetAll(); + } +} + /** Recursively close all folds of sub-pages */ void SettingsContainer::FoldAll() { @@ -1323,6 +1341,14 @@ void SettingsPage::Init(byte level) SettingsContainer::Init(level + 1); } +/** Resets all settings to their default values */ +void SettingsPage::ResetAll() +{ + for (auto settings_entry : this->entries) { + settings_entry->ResetAll(); + } +} + /** Recursively close all (filtered) folds of sub-pages */ void SettingsPage::FoldAll() { @@ -1794,6 +1820,20 @@ enum WarnHiddenResult { WHR_CATEGORY_TYPE, ///< Both category and type settings filtered matches away. }; +/** + * Callback function for the reset all settings button + * @param w Window which is calling this callback + * @param confirmed boolean value, true when yes was clicked, false otherwise + */ +static void ResetAllSettingsConfirmationCallback(Window *w, bool confirmed) +{ + if (confirmed) { + GetSettingsTree().ResetAll(); + GetSettingsTree().FoldAll(); + w->InvalidateData(); + } +} + /** Window to edit settings of the game. */ struct GameSettingsWindow : Window { static const int SETTINGTREE_LEFT_OFFSET = 5; ///< Position of left edge of setting values @@ -2031,6 +2071,15 @@ struct GameSettingsWindow : Window { this->InvalidateData(); break; + case WID_GS_RESET_ALL: + ShowQuery( + STR_CONFIG_SETTING_RESET_ALL_CONFIRMATION_DIALOG_CAPTION, + STR_CONFIG_SETTING_RESET_ALL_CONFIRMATION_DIALOG_TEXT, + this, + ResetAllSettingsConfirmationCallback + ); + break; + case WID_GS_RESTRICT_DROPDOWN: { DropDownList list = this->BuildDropDownList(widget); if (!list.empty()) { @@ -2387,6 +2436,7 @@ static const NWidgetPart _nested_settings_selection_widgets[] = { NWidget(NWID_HORIZONTAL), NWidget(WWT_PUSHTXTBTN, COLOUR_MAUVE, WID_GS_EXPAND_ALL), SetDataTip(STR_CONFIG_SETTING_EXPAND_ALL, STR_NULL), NWidget(WWT_PUSHTXTBTN, COLOUR_MAUVE, WID_GS_COLLAPSE_ALL), SetDataTip(STR_CONFIG_SETTING_COLLAPSE_ALL, STR_NULL), + NWidget(WWT_PUSHTXTBTN, COLOUR_MAUVE, WID_GS_RESET_ALL), SetDataTip(STR_CONFIG_SETTING_RESET_ALL, STR_NULL), NWidget(WWT_PANEL, COLOUR_MAUVE), SetFill(1, 0), SetResize(1, 0), EndContainer(), NWidget(WWT_RESIZEBOX, COLOUR_MAUVE), diff --git a/src/widgets/settings_widget.h b/src/widgets/settings_widget.h index 7f8b0e1117..6cb2d7e949 100644 --- a/src/widgets/settings_widget.h +++ b/src/widgets/settings_widget.h @@ -44,6 +44,7 @@ enum GameSettingsWidgets { WID_GS_HELP_TEXT, ///< Information area to display help text of the selected option. WID_GS_EXPAND_ALL, ///< Expand all button. WID_GS_COLLAPSE_ALL, ///< Collapse all button. + WID_GS_RESET_ALL, ///< Reset all button. WID_GS_RESTRICT_CATEGORY, ///< Label upfront to the category drop-down box to restrict the list of settings to show WID_GS_RESTRICT_TYPE, ///< Label upfront to the type drop-down box to restrict the list of settings to show WID_GS_RESTRICT_DROPDOWN, ///< The drop down box to restrict the list of settings From 39b4a8e67e5804265340a9c2ffd85afe8c7114eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Guilloux?= Date: Tue, 6 Apr 2021 13:54:27 +0200 Subject: [PATCH 016/268] Change: [CMake] Improve 'In-source build' error message (#8955) --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 17ed2df8c7..63a0ce2fe8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,7 +7,7 @@ endif() project(${BINARY_NAME}) if(CMAKE_SOURCE_DIR STREQUAL CMAKE_BINARY_DIR) - message(FATAL_ERROR "In-source builds not allowed. Please run \"cmake ..\" from the bin directory") + message(FATAL_ERROR "In-source builds not allowed. Please run \"cmake ..\" from the build directory. You may need to delete \"${CMAKE_SOURCE_DIR}/CMakeCache.txt\" first.") endif() # Debug mode by default. From 2f5035d6dc6a1daf4884d85288349bfdd726f210 Mon Sep 17 00:00:00 2001 From: translators Date: Tue, 6 Apr 2021 17:53:34 +0000 Subject: [PATCH 017/268] Update: Translations from eints korean: 1 change by telk5093 --- src/lang/korean.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lang/korean.txt b/src/lang/korean.txt index 483e5a223d..798f2fbb52 100644 --- a/src/lang/korean.txt +++ b/src/lang/korean.txt @@ -2532,7 +2532,7 @@ STR_WATERWAYS_TOOLBAR_BUILD_DOCK_TOOLTIP :{BLACK}항구 STR_WATERWAYS_TOOLBAR_BUOY_TOOLTIP :{BLACK}경유지로 사용할 수 있는 부표를 만듭니다. SHIFT 키를 누른 채로 사용하면 예상 비용을 볼 수 있습니다 STR_WATERWAYS_TOOLBAR_BUILD_AQUEDUCT_TOOLTIP :{BLACK}수도교를 만듭니다. SHIFT 키를 누른 채로 사용하면 예상 비용을 볼 수 있습니다 STR_WATERWAYS_TOOLBAR_CREATE_LAKE_TOOLTIP :{BLACK}벽으로 물을 막고 있는 운하를 만듭니다.{}해수면 높이에서 CTRL 키를 누른 채로 사용하면 벽 없이 물을 만들어 바다나 호수를 만들 수 있습니다 -STR_WATERWAYS_TOOLBAR_CREATE_RIVER_TOOLTIP :{BLACK}강을 만듭니다 +STR_WATERWAYS_TOOLBAR_CREATE_RIVER_TOOLTIP :{BLACK}강을 만듭니다. CTRL 키를 누르면 대각선 방향의 영역을 선택할 수 있습니다. # Ship depot construction window STR_DEPOT_BUILD_SHIP_CAPTION :{WHITE}정박소 방향 선택 From f0a24e98f55def996b0913690736f4c708fc4c55 Mon Sep 17 00:00:00 2001 From: Bernard Teo Date: Wed, 7 Apr 2021 02:30:35 +0800 Subject: [PATCH 018/268] Fix #8922: Show vehicle window for single vehicle in shared order grouping (#8926) --- src/group_gui.cpp | 6 +++++- src/vehicle_gui.cpp | 10 ++++++++-- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/group_gui.cpp b/src/group_gui.cpp index e711201a68..0573837688 100644 --- a/src/group_gui.cpp +++ b/src/group_gui.cpp @@ -892,7 +892,11 @@ public: /* We do not support VehicleClicked() here since the contextual action may only make sense for individual vehicles */ if (vindex == v->index) { - ShowVehicleListWindow(v); + if (vehgroup.NumVehicles() == 1) { + ShowVehicleViewWindow(v); + } else { + ShowVehicleListWindow(v); + } } break; } diff --git a/src/vehicle_gui.cpp b/src/vehicle_gui.cpp index 5960f0be94..aa28938248 100644 --- a/src/vehicle_gui.cpp +++ b/src/vehicle_gui.cpp @@ -1830,12 +1830,18 @@ public: break; } - case GB_SHARED_ORDERS: + case GB_SHARED_ORDERS: { assert(vehgroup.NumVehicles() > 0); + const Vehicle *v = vehgroup.vehicles_begin[0]; /* We do not support VehicleClicked() here since the contextual action may only make sense for individual vehicles */ - ShowVehicleListWindow(vehgroup.vehicles_begin[0]); + if (vehgroup.NumVehicles() == 1) { + ShowVehicleViewWindow(v); + } else { + ShowVehicleListWindow(v); + } break; + } default: NOT_REACHED(); } From 42fbdda9ab12a06a5fbe4817cb93c227b828e042 Mon Sep 17 00:00:00 2001 From: Peter Nelson Date: Sun, 4 Apr 2021 15:58:06 +0100 Subject: [PATCH 019/268] Change: Apply power-of-3 scaling to master effect volume to improve perceived loudness change. --- src/mixer.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/mixer.cpp b/src/mixer.cpp index 0a41bb7c71..0c9d9b1517 100644 --- a/src/mixer.cpp +++ b/src/mixer.cpp @@ -155,7 +155,13 @@ void MxMixSamples(void *buffer, uint samples) /* Fetch music if a sampled stream is available */ if (_music_stream) _music_stream((int16*)buffer, samples); - uint8 effect_vol = _settings_client.music.effect_vol; + /* Apply simple x^3 scaling to master effect volume. This increases the + * perceived difference in loudness to better match expectations. effect_vol + * is expected to be in the range 0-127 hence the division by 127 * 127 to + * get back into range. */ + uint8 effect_vol = (_settings_client.music.effect_vol * + _settings_client.music.effect_vol * + _settings_client.music.effect_vol) / (127 * 127); /* Mix each channel */ for (mc = _channels; mc != endof(_channels); mc++) { From 920bf703cda28e2c864531f5bc5468cff1f0ce73 Mon Sep 17 00:00:00 2001 From: Peter Nelson Date: Sun, 4 Apr 2021 19:13:08 +0100 Subject: [PATCH 020/268] Fix: Allow full sample range for 16-bit audio. Clamping each sample value to half the available range could cause unnecessary premature clipping with lots of sounds playing. This change does not affect the actual volume level. --- src/mixer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mixer.cpp b/src/mixer.cpp index 0c9d9b1517..d892a573c6 100644 --- a/src/mixer.cpp +++ b/src/mixer.cpp @@ -46,7 +46,7 @@ static MxStreamCallback _music_stream = nullptr; * stops overflowing when too many sounds are played at the same time, which * causes an even worse sound quality. */ -static const int MAX_VOLUME = 128 * 128; +static const int MAX_VOLUME = 32767; /** * Perform the rate conversion between the input and output. From 825867f2c50ce508fac442e6113da9cebbfccf75 Mon Sep 17 00:00:00 2001 From: Jonathan G Rennison Date: Tue, 6 Apr 2021 19:31:52 +0100 Subject: [PATCH 021/268] Fix: [Network] State conditions for sending client info/quit packets (#8959) Use status >= STATUS_AUTHORIZED as the state criteria for all cases where updates about other clients are sent. This avoids the case where a client is informed that another client has joined but not informed when it later quits, resulting in stale entries in the client list window. --- src/network/network_server.cpp | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/network/network_server.cpp b/src/network/network_server.cpp index 9e4d0d88f9..86885d5980 100644 --- a/src/network/network_server.cpp +++ b/src/network/network_server.cpp @@ -456,7 +456,7 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::SendError(NetworkErrorCode err } for (NetworkClientSocket *new_cs : NetworkClientSocket::Iterate()) { - if (new_cs->status > STATUS_AUTHORIZED && new_cs != this) { + if (new_cs->status >= STATUS_AUTHORIZED && new_cs != this) { /* Some errors we filter to a more general error. Clients don't have to know the real * reason a joining failed. */ if (error == NETWORK_ERROR_NOT_AUTHORIZED || error == NETWORK_ERROR_NOT_EXPECTED || error == NETWORK_ERROR_WRONG_REVISION) { @@ -549,7 +549,7 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::SendWelcome() /* Transmit info about all the active clients */ for (NetworkClientSocket *new_cs : NetworkClientSocket::Iterate()) { - if (new_cs != this && new_cs->status > STATUS_AUTHORIZED) { + if (new_cs != this && new_cs->status >= STATUS_AUTHORIZED) { this->SendClientInfo(new_cs->GetInfo()); } } @@ -1068,7 +1068,7 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::Receive_CLIENT_MAP_OK(Packet * this->last_frame_server = _frame_counter; for (NetworkClientSocket *new_cs : NetworkClientSocket::Iterate()) { - if (new_cs->status > STATUS_AUTHORIZED) { + if (new_cs->status >= STATUS_AUTHORIZED) { new_cs->SendClientInfo(this->GetInfo()); new_cs->SendJoin(this->client_id); } @@ -1178,7 +1178,7 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::Receive_CLIENT_ERROR(Packet *p NetworkTextMessage(NETWORK_ACTION_LEAVE, CC_DEFAULT, false, client_name, nullptr, strid); for (NetworkClientSocket *new_cs : NetworkClientSocket::Iterate()) { - if (new_cs->status > STATUS_AUTHORIZED) { + if (new_cs->status >= STATUS_AUTHORIZED) { new_cs->SendErrorQuit(this->client_id, errorno); } } @@ -1204,7 +1204,7 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::Receive_CLIENT_QUIT(Packet *p) NetworkTextMessage(NETWORK_ACTION_LEAVE, CC_DEFAULT, false, client_name, nullptr, STR_NETWORK_MESSAGE_CLIENT_LEAVING); for (NetworkClientSocket *new_cs : NetworkClientSocket::Iterate()) { - if (new_cs->status > STATUS_AUTHORIZED && new_cs != this) { + if (new_cs->status >= STATUS_AUTHORIZED && new_cs != this) { new_cs->SendQuit(this->client_id); } } @@ -1607,7 +1607,9 @@ void NetworkUpdateClientInfo(ClientID client_id) DEBUG(desync, 1, "client: %08x; %02x; %02x; %04x", _date, _date_fract, (int)ci->client_playas, client_id); for (NetworkClientSocket *cs : NetworkClientSocket::Iterate()) { - cs->SendClientInfo(ci); + if (cs->status >= ServerNetworkGameSocketHandler::STATUS_AUTHORIZED) { + cs->SendClientInfo(ci); + } } NetworkAdminClientUpdate(ci); From e0561dbded57f195e7842cf69764e3ee2c3a71da Mon Sep 17 00:00:00 2001 From: Niels Martin Hansen Date: Sun, 21 Feb 2021 17:03:19 +0100 Subject: [PATCH 022/268] Fix #8713: Change OTTD2FS and FS2OTTD to return string objects instead of static buffers --- src/debug.cpp | 2 +- src/fileio.cpp | 14 ++++---- src/fileio_func.h | 2 +- src/fios.cpp | 4 +-- src/ini.cpp | 6 ++-- src/music/cocoa_m.cpp | 4 +-- src/music/dmusic.cpp | 4 +-- src/network/core/address.cpp | 2 +- src/os/os2/os2.cpp | 3 -- src/os/unix/unix.cpp | 21 +++++------- src/os/windows/crashlog_win.cpp | 37 +++++++++++--------- src/os/windows/font_win32.cpp | 11 +++--- src/os/windows/font_win32.h | 3 +- src/os/windows/win32.cpp | 60 ++++++++++++++++++--------------- src/stdafx.h | 26 +++++++------- src/strings.cpp | 6 ++-- src/video/win32_v.cpp | 4 +-- 17 files changed, 109 insertions(+), 100 deletions(-) diff --git a/src/debug.cpp b/src/debug.cpp index 034af755fd..eb8dc5d4fa 100644 --- a/src/debug.cpp +++ b/src/debug.cpp @@ -134,7 +134,7 @@ static void debug_print(const char *dbg, const char *buf) #if defined(_WIN32) wchar_t system_buf[512]; convert_to_fs(buffer, system_buf, lengthof(system_buf), true); - _fputts(system_buf, stderr); + fputws(system_buf, stderr); #else fputs(buffer, stderr); #endif diff --git a/src/fileio.cpp b/src/fileio.cpp index 6b33f8aa24..f2a2b14f3a 100644 --- a/src/fileio.cpp +++ b/src/fileio.cpp @@ -279,7 +279,7 @@ bool FioCheckFileExists(const std::string &filename, Subdirectory subdir) */ bool FileExists(const std::string &filename) { - return access(OTTD2FS(filename.c_str()), 0) == 0; + return access(OTTD2FS(filename).c_str(), 0) == 0; } /** @@ -358,7 +358,7 @@ static FILE *FioFOpenFileSp(const std::string &filename, const char *mode, Searc } #if defined(_WIN32) - if (mode[0] == 'r' && GetFileAttributes(OTTD2FS(buf.c_str())) == INVALID_FILE_ATTRIBUTES) return nullptr; + if (mode[0] == 'r' && GetFileAttributes(OTTD2FS(buf).c_str()) == INVALID_FILE_ATTRIBUTES) return nullptr; #endif f = fopen(buf.c_str(), mode); @@ -506,11 +506,11 @@ void FioCreateDirectory(const std::string &name) /* Ignore directory creation errors; they'll surface later on, and most * of the time they are 'directory already exists' errors anyhow. */ #if defined(_WIN32) - CreateDirectory(OTTD2FS(name.c_str()), nullptr); + CreateDirectory(OTTD2FS(name).c_str(), nullptr); #elif defined(OS2) && !defined(__INNOTEK_LIBC__) - mkdir(OTTD2FS(name.c_str())); + mkdir(OTTD2FS(name).c_str()); #else - mkdir(OTTD2FS(name.c_str()), 0755); + mkdir(OTTD2FS(name).c_str(), 0755); #endif } @@ -1315,7 +1315,7 @@ static uint ScanPath(FileScanner *fs, const char *extension, const char *path, s if (path == nullptr || (dir = ttd_opendir(path)) == nullptr) return 0; while ((dirent = readdir(dir)) != nullptr) { - const char *d_name = FS2OTTD(dirent->d_name); + std::string d_name = FS2OTTD(dirent->d_name); if (!FiosIsValidFile(path, dirent, &sb)) continue; @@ -1325,7 +1325,7 @@ static uint ScanPath(FileScanner *fs, const char *extension, const char *path, s if (S_ISDIR(sb.st_mode)) { /* Directory */ if (!recursive) continue; - if (strcmp(d_name, ".") == 0 || strcmp(d_name, "..") == 0) continue; + if (d_name == "." || d_name == "..") continue; AppendPathSeparator(filename); num += ScanPath(fs, extension, filename.c_str(), basepath_length, recursive); } else if (S_ISREG(sb.st_mode)) { diff --git a/src/fileio_func.h b/src/fileio_func.h index 6dbaea8970..1428356506 100644 --- a/src/fileio_func.h +++ b/src/fileio_func.h @@ -132,7 +132,7 @@ int closedir(DIR *d); */ static inline DIR *ttd_opendir(const char *path) { - return opendir(OTTD2FS(path)); + return opendir(OTTD2FS(path).c_str()); } diff --git a/src/fios.cpp b/src/fios.cpp index b48bc21089..b68da08f80 100644 --- a/src/fios.cpp +++ b/src/fios.cpp @@ -309,7 +309,7 @@ bool FiosFileScanner::AddFile(const std::string &filename, size_t basepath_lengt FiosItem *fios = file_list.Append(); #ifdef _WIN32 // Retrieve the file modified date using GetFileTime rather than stat to work around an obscure MSVC bug that affects Windows XP - HANDLE fh = CreateFile(OTTD2FS(filename.c_str()), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, OPEN_EXISTING, 0, nullptr); + HANDLE fh = CreateFile(OTTD2FS(filename).c_str(), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, OPEN_EXISTING, 0, nullptr); if (fh != INVALID_HANDLE_VALUE) { FILETIME ft; @@ -384,7 +384,7 @@ static void FiosGetFileList(SaveLoadOperation fop, fios_getlist_callback_proc *c /* Show subdirectories */ if ((dir = ttd_opendir(_fios_path->c_str())) != nullptr) { while ((dirent = readdir(dir)) != nullptr) { - strecpy(d_name, FS2OTTD(dirent->d_name), lastof(d_name)); + strecpy(d_name, FS2OTTD(dirent->d_name).c_str(), lastof(d_name)); /* found file must be directory, but not '.' or '..' */ if (FiosIsValidFile(_fios_path->c_str(), dirent, &sb) && S_ISDIR(sb.st_mode) && diff --git a/src/ini.cpp b/src/ini.cpp index 4988c778ee..a5045b7151 100644 --- a/src/ini.cpp +++ b/src/ini.cpp @@ -53,7 +53,7 @@ bool IniFile::SaveToDisk(const std::string &filename) std::string file_new{ filename }; file_new.append(".new"); - std::ofstream os(OTTD2FS(file_new.c_str())); + std::ofstream os(OTTD2FS(file_new)); if (os.fail()) return false; for (const IniGroup *group = this->group; group != nullptr; group = group->next) { @@ -94,8 +94,8 @@ bool IniFile::SaveToDisk(const std::string &filename) #if defined(_WIN32) /* Allocate space for one more \0 character. */ wchar_t tfilename[MAX_PATH + 1], tfile_new[MAX_PATH + 1]; - wcsncpy(tfilename, OTTD2FS(filename.c_str()), MAX_PATH); - wcsncpy(tfile_new, OTTD2FS(file_new.c_str()), MAX_PATH); + wcsncpy(tfilename, OTTD2FS(filename).c_str(), MAX_PATH); + wcsncpy(tfile_new, OTTD2FS(file_new).c_str(), MAX_PATH); /* SHFileOperation wants a double '\0' terminated string. */ tfilename[MAX_PATH - 1] = '\0'; tfile_new[MAX_PATH - 1] = '\0'; diff --git a/src/music/cocoa_m.cpp b/src/music/cocoa_m.cpp index a989cfe769..279e0b38ae 100644 --- a/src/music/cocoa_m.cpp +++ b/src/music/cocoa_m.cpp @@ -134,8 +134,8 @@ void MusicDriver_Cocoa::PlaySong(const MusicSongInfo &song) return; } - const char *os_file = OTTD2FS(filename.c_str()); - CFAutoRelease url(CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault, (const UInt8*)os_file, strlen(os_file), false)); + std::string os_file = OTTD2FS(filename); + CFAutoRelease url(CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault, (const UInt8*)os_file.c_str(), os_file.length(), false)); if (MusicSequenceFileLoad(_sequence, url.get(), kMusicSequenceFile_AnyType, 0) != noErr) { DEBUG(driver, 0, "cocoa_m: Failed to load MIDI file"); diff --git a/src/music/dmusic.cpp b/src/music/dmusic.cpp index c9447206af..619a20d8bf 100644 --- a/src/music/dmusic.cpp +++ b/src/music/dmusic.cpp @@ -430,7 +430,7 @@ bool DLSFile::ReadDLSWaveList(FILE *f, DWORD list_length) bool DLSFile::LoadFile(const wchar_t *file) { - DEBUG(driver, 2, "DMusic: Try to load DLS file %s", FS2OTTD(file)); + DEBUG(driver, 2, "DMusic: Try to load DLS file %s", FS2OTTD(file).c_str()); FILE *f = _wfopen(file, L"rb"); if (f == nullptr) return false; @@ -881,7 +881,7 @@ static const char *LoadDefaultDLSFile(const char *user_dls) if (!dls_file.LoadFile(path)) return "Can't load GM DLS collection"; } } else { - if (!dls_file.LoadFile(OTTD2FS(user_dls))) return "Can't load GM DLS collection"; + if (!dls_file.LoadFile(OTTD2FS(user_dls).c_str())) return "Can't load GM DLS collection"; } /* Get download port and allocate download IDs. */ diff --git a/src/network/core/address.cpp b/src/network/core/address.cpp index 2e00b5b193..a45bd49142 100644 --- a/src/network/core/address.cpp +++ b/src/network/core/address.cpp @@ -246,7 +246,7 @@ SOCKET NetworkAddress::Resolve(int family, int socktype, int flags, SocketList * 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))); + this->hostname, port_name, AddressFamilyAsString(family), SocketTypeAsString(socktype), FS2OTTD(gai_strerror(e)).c_str()); } return INVALID_SOCKET; } diff --git a/src/os/os2/os2.cpp b/src/os/os2/os2.cpp index f1128b0c61..009a3e0ede 100644 --- a/src/os/os2/os2.cpp +++ b/src/os/os2/os2.cpp @@ -203,9 +203,6 @@ bool GetClipboardContents(char *buffer, const char *last) } -const char *FS2OTTD(const char *name) {return name;} -const char *OTTD2FS(const char *name) {return name;} - void OSOpenBrowser(const char *url) { // stub only diff --git a/src/os/unix/unix.cpp b/src/os/unix/unix.cpp index 9ef2bc386d..57edf1d38e 100644 --- a/src/os/unix/unix.cpp +++ b/src/os/unix/unix.cpp @@ -146,9 +146,8 @@ static const char *GetLocalCode() * Convert between locales, which from and which to is set in the calling * functions OTTD2FS() and FS2OTTD(). */ -static const char *convert_tofrom_fs(iconv_t convd, const char *name) +static const char *convert_tofrom_fs(iconv_t convd, const char *name, char *outbuf, size_t outlen) { - static char buf[1024]; /* There are different implementations of iconv. The older ones, * e.g. SUSv2, pass a const pointer, whereas the newer ones, e.g. * IEEE 1003.1 (2004), pass a non-const pointer. */ @@ -158,9 +157,8 @@ static const char *convert_tofrom_fs(iconv_t convd, const char *name) const char *inbuf = name; #endif - char *outbuf = buf; - size_t outlen = sizeof(buf) - 1; size_t inlen = strlen(name); + char *buf = outbuf; strecpy(outbuf, name, outbuf + outlen); @@ -179,9 +177,10 @@ static const char *convert_tofrom_fs(iconv_t convd, const char *name) * @param name pointer to a valid string that will be converted * @return pointer to a new stringbuffer that contains the converted string */ -const char *OTTD2FS(const char *name) +std::string OTTD2FS(const std::string &name) { static iconv_t convd = (iconv_t)(-1); + char buf[1024] = {}; if (convd == (iconv_t)(-1)) { const char *env = GetLocalCode(); @@ -192,17 +191,18 @@ const char *OTTD2FS(const char *name) } } - return convert_tofrom_fs(convd, name); + return convert_tofrom_fs(convd, name.c_str(), buf, lengthof(buf)); } /** * Convert to OpenTTD's encoding from that of the local environment - * @param name pointer to a valid string that will be converted + * @param name valid string that will be converted * @return pointer to a new stringbuffer that contains the converted string */ -const char *FS2OTTD(const char *name) +std::string FS2OTTD(const std::string &name) { static iconv_t convd = (iconv_t)(-1); + char buf[1024] = {}; if (convd == (iconv_t)(-1)) { const char *env = GetLocalCode(); @@ -213,12 +213,9 @@ const char *FS2OTTD(const char *name) } } - return convert_tofrom_fs(convd, name); + return convert_tofrom_fs(convd, name.c_str(), buf, lengthof(buf)); } -#else -const char *FS2OTTD(const char *name) {return name;} -const char *OTTD2FS(const char *name) {return name;} #endif /* WITH_ICONV */ void ShowInfo(const char *str) diff --git a/src/os/windows/crashlog_win.cpp b/src/os/windows/crashlog_win.cpp index 76a05eaa9e..9768bcc82e 100644 --- a/src/os/windows/crashlog_win.cpp +++ b/src/os/windows/crashlog_win.cpp @@ -189,7 +189,7 @@ static char *PrintModuleInfo(char *output, const char *last, HMODULE mod) GetModuleFileName(mod, buffer, MAX_PATH); GetFileInfo(&dfi, buffer); output += seprintf(output, last, " %-20s handle: %p size: %d crc: %.8X date: %d-%.2d-%.2d %.2d:%.2d:%.2d\n", - FS2OTTD(buffer), + FS2OTTD(buffer).c_str(), mod, dfi.size, dfi.crc32, @@ -501,7 +501,7 @@ char *CrashLogWindows::AppendDecodedStacktrace(char *buffer, const char *last) c MiniDumpWriteDump_t funcMiniDumpWriteDump = (MiniDumpWriteDump_t)GetProcAddress(dbghelp, "MiniDumpWriteDump"); if (funcMiniDumpWriteDump != nullptr) { seprintf(filename, filename_last, "%scrash.dmp", _personal_dir.c_str()); - HANDLE file = CreateFile(OTTD2FS(filename), GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS, 0, 0); + HANDLE file = CreateFile(OTTD2FS(filename).c_str(), GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS, 0, 0); HANDLE proc = GetCurrentProcess(); DWORD procid = GetCurrentProcessId(); MINIDUMP_EXCEPTION_INFORMATION mdei; @@ -689,7 +689,8 @@ static INT_PTR CALLBACK CrashDialogFunc(HWND wnd, UINT msg, WPARAM wParam, LPARA switch (msg) { case WM_INITDIALOG: { /* We need to put the crash-log in a separate buffer because the default - * buffer in OTTD2FS is not large enough (512 chars) */ + * buffer in MB_TO_WIDE is not large enough (512 chars) */ + wchar_t filenamebuf[MAX_PATH * 2]; wchar_t crash_msgW[lengthof(CrashLogWindows::current->crashlog)]; /* Convert unix -> dos newlines because the edit box only supports that properly :( */ const char *unix_nl = CrashLogWindows::current->crashlog; @@ -704,19 +705,23 @@ static INT_PTR CALLBACK CrashDialogFunc(HWND wnd, UINT msg, WPARAM wParam, LPARA /* Add path to crash.log and crash.dmp (if any) to the crash window text */ size_t len = wcslen(_crash_desc) + 2; - len += wcslen(OTTD2FS(CrashLogWindows::current->crashlog_filename)) + 2; - len += wcslen(OTTD2FS(CrashLogWindows::current->crashdump_filename)) + 2; - len += wcslen(OTTD2FS(CrashLogWindows::current->screenshot_filename)) + 1; + len += wcslen(convert_to_fs(CrashLogWindows::current->crashlog_filename, filenamebuf, lengthof(filenamebuf), false)) + 2; + len += wcslen(convert_to_fs(CrashLogWindows::current->crashdump_filename, filenamebuf, lengthof(filenamebuf), false)) + 2; + len += wcslen(convert_to_fs(CrashLogWindows::current->screenshot_filename, filenamebuf, lengthof(filenamebuf), false)) + 1; wchar_t *text = AllocaM(wchar_t, len); - _snwprintf(text, len, _crash_desc, OTTD2FS(CrashLogWindows::current->crashlog_filename)); - if (OTTD2FS(CrashLogWindows::current->crashdump_filename)[0] != L'\0') { - wcscat(text, L"\n"); - wcscat(text, OTTD2FS(CrashLogWindows::current->crashdump_filename)); + int printed = _snwprintf(text, len, _crash_desc, convert_to_fs(CrashLogWindows::current->crashlog_filename, filenamebuf, lengthof(filenamebuf), false)); + if (printed < 0 || (size_t)printed > len) { + MessageBox(wnd, L"Catastrophic failure trying to display crash message. Could not perform text formatting.", L"OpenTTD", MB_ICONERROR); + return FALSE; } - if (OTTD2FS(CrashLogWindows::current->screenshot_filename)[0] != L'\0') { + if (convert_to_fs(CrashLogWindows::current->crashdump_filename, filenamebuf, lengthof(filenamebuf), false)[0] != L'\0') { wcscat(text, L"\n"); - wcscat(text, OTTD2FS(CrashLogWindows::current->screenshot_filename)); + wcscat(text, filenamebuf); + } + if (convert_to_fs(CrashLogWindows::current->screenshot_filename, filenamebuf, lengthof(filenamebuf), false)[0] != L'\0') { + wcscat(text, L"\n"); + wcscat(text, filenamebuf); } SetDlgItemText(wnd, 10, text); @@ -730,18 +735,20 @@ static INT_PTR CALLBACK CrashDialogFunc(HWND wnd, UINT msg, WPARAM wParam, LPARA CrashLog::AfterCrashLogCleanup(); ExitProcess(2); case 13: // Emergency save + wchar_t filenamebuf[MAX_PATH * 2]; char filename[MAX_PATH]; if (CrashLogWindows::current->WriteSavegame(filename, lastof(filename))) { - size_t len = wcslen(_save_succeeded) + wcslen(OTTD2FS(filename)) + 1; + convert_to_fs(filename, filenamebuf, lengthof(filenamebuf), false); + size_t len = lengthof(_save_succeeded) + wcslen(filenamebuf) + 1; wchar_t *text = AllocaM(wchar_t, len); - _snwprintf(text, len, _save_succeeded, OTTD2FS(filename)); + _snwprintf(text, len, _save_succeeded, filenamebuf); MessageBox(wnd, text, L"Save successful", MB_ICONINFORMATION); } else { MessageBox(wnd, L"Save failed", L"Save failed", MB_ICONINFORMATION); } break; case 15: // Expand window to show crash-message - _expanded ^= 1; + _expanded = !_expanded; SetWndSize(wnd, _expanded); break; } diff --git a/src/os/windows/font_win32.cpp b/src/os/windows/font_win32.cpp index 9a9dba2f2e..1d923ddb21 100644 --- a/src/os/windows/font_win32.cpp +++ b/src/os/windows/font_win32.cpp @@ -83,7 +83,7 @@ FT_Error GetFontByFaceName(const char *font_name, FT_Face *face) } /* Convert font name to file system encoding. */ - wchar_t *font_namep = wcsdup(OTTD2FS(font_name)); + wchar_t *font_namep = wcsdup(OTTD2FS(font_name).c_str()); for (index = 0;; index++) { wchar_t *s; @@ -377,6 +377,7 @@ Win32FontCache::Win32FontCache(FontSize fs, const LOGFONT &logfont, int pixels) { this->dc = CreateCompatibleDC(nullptr); this->SetFontSize(fs, pixels); + this->fontname = FS2OTTD(this->logfont.lfFaceName); } Win32FontCache::~Win32FontCache() @@ -438,7 +439,7 @@ void Win32FontCache::SetFontSize(FontSize fs, int pixels) this->glyph_size.cx = otm->otmTextMetrics.tmMaxCharWidth; this->glyph_size.cy = otm->otmTextMetrics.tmHeight; - DEBUG(freetype, 2, "Loaded font '%s' with size %d", FS2OTTD((LPTSTR)((BYTE *)otm + (ptrdiff_t)otm->otmpFullName)), pixels); + DEBUG(freetype, 2, "Loaded font '%s' with size %d", FS2OTTD((LPWSTR)((BYTE *)otm + (ptrdiff_t)otm->otmpFullName)).c_str(), pixels); } /** @@ -541,10 +542,10 @@ void Win32FontCache::ClearFontCache() /* Convert characters outside of the BMP into surrogate pairs. */ WCHAR chars[2]; if (key >= 0x010000U) { - chars[0] = (WCHAR)(((key - 0x010000U) >> 10) + 0xD800); - chars[1] = (WCHAR)(((key - 0x010000U) & 0x3FF) + 0xDC00); + chars[0] = (wchar_t)(((key - 0x010000U) >> 10) + 0xD800); + chars[1] = (wchar_t)(((key - 0x010000U) & 0x3FF) + 0xDC00); } else { - chars[0] = (WCHAR)(key & 0xFFFF); + chars[0] = (wchar_t)(key & 0xFFFF); } WORD glyphs[2] = { 0, 0 }; diff --git a/src/os/windows/font_win32.h b/src/os/windows/font_win32.h index ba413fae5e..6ab304c89c 100644 --- a/src/os/windows/font_win32.h +++ b/src/os/windows/font_win32.h @@ -21,6 +21,7 @@ private: HDC dc = nullptr; ///< Cached GDI device context. HGDIOBJ old_font; ///< Old font selected into the GDI context. SIZE glyph_size; ///< Maximum size of regular glyphs. + std::string fontname; ///< Cached copy of this->logfont.lfFaceName void SetFontSize(FontSize fs, int pixels); @@ -33,7 +34,7 @@ public: ~Win32FontCache(); void ClearFontCache() override; GlyphID MapCharToGlyph(WChar key) override; - const char *GetFontName() override { return FS2OTTD(this->logfont.lfFaceName); } + const char *GetFontName() override { return this->fontname.c_str(); } const void *GetOSHandle() override { return &this->logfont; } }; diff --git a/src/os/windows/win32.cpp b/src/os/windows/win32.cpp index cfe7d42d95..568876ab1e 100644 --- a/src/os/windows/win32.cpp +++ b/src/os/windows/win32.cpp @@ -57,7 +57,7 @@ bool LoadLibraryList(Function proc[], const char *dll) { while (*dll != '\0') { HMODULE lib; - lib = LoadLibrary(OTTD2FS(dll)); + lib = LoadLibrary(OTTD2FS(dll).c_str()); if (lib == nullptr) return false; for (;;) { @@ -77,12 +77,12 @@ bool LoadLibraryList(Function proc[], const char *dll) void ShowOSErrorBox(const char *buf, bool system) { MyShowCursor(true); - MessageBox(GetActiveWindow(), OTTD2FS(buf), L"Error!", MB_ICONSTOP | MB_TASKMODAL); + MessageBox(GetActiveWindow(), OTTD2FS(buf).c_str(), L"Error!", MB_ICONSTOP | MB_TASKMODAL); } void OSOpenBrowser(const char *url) { - ShellExecute(GetActiveWindow(), L"open", OTTD2FS(url), nullptr, nullptr, SW_SHOWNORMAL); + ShellExecute(GetActiveWindow(), L"open", OTTD2FS(url).c_str(), nullptr, nullptr, SW_SHOWNORMAL); } /* Code below for windows version of opendir/readdir/closedir copied and @@ -141,14 +141,14 @@ DIR *opendir(const wchar_t *path) if ((fa != INVALID_FILE_ATTRIBUTES) && (fa & FILE_ATTRIBUTE_DIRECTORY)) { d = dir_calloc(); if (d != nullptr) { - wchar_t search_path[MAX_PATH]; + std::wstring search_path = path; bool slash = path[wcslen(path) - 1] == '\\'; /* build search path for FindFirstFile, try not to append additional slashes * as it throws Win9x off its groove for root directories */ - _snwprintf(search_path, lengthof(search_path), L"%s%s*", path, slash ? L"" : L"\\"); - *lastof(search_path) = '\0'; - d->hFind = FindFirstFile(search_path, &d->fd); + if (!slash) search_path += L"\\"; + search_path += L"*"; + d->hFind = FindFirstFile(search_path.c_str(), &d->fd); if (d->hFind != INVALID_HANDLE_VALUE || GetLastError() == ERROR_NO_MORE_FILES) { // the directory is empty @@ -246,7 +246,7 @@ bool FiosGetDiskFreeSpace(const char *path, uint64 *tot) UINT sem = SetErrorMode(SEM_FAILCRITICALERRORS); // disable 'no-disk' message box ULARGE_INTEGER bytes_free; - bool retval = GetDiskFreeSpaceEx(OTTD2FS(path), &bytes_free, nullptr, nullptr); + bool retval = GetDiskFreeSpaceEx(OTTD2FS(path).c_str(), &bytes_free, nullptr, nullptr); if (retval) *tot = bytes_free.QuadPart; SetErrorMode(sem); // reset previous setting @@ -415,7 +415,7 @@ int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLi /* Convert the command line to UTF-8. We need a dedicated buffer * for this because argv[] points into this buffer and this needs to * be available between subsequent calls to FS2OTTD(). */ - char *cmdline = stredup(FS2OTTD(GetCommandLine())); + char *cmdline = stredup(FS2OTTD(GetCommandLine()).c_str()); #if defined(_DEBUG) CreateConsole(); @@ -553,34 +553,40 @@ bool GetClipboardContents(char *buffer, const char *last) /** - * Convert to OpenTTD's encoding from wide characters. + * Convert to OpenTTD's encoding from a wide string. * OpenTTD internal encoding is UTF8. - * The returned value's contents can only be guaranteed until the next call to - * this function. So if the value is needed for anything else, use convert_from_fs - * @param name pointer to a valid string that will be converted (local, or wide) - * @return pointer to the converted string; if failed string is of zero-length + * @param name valid string that will be converted (local, or wide) + * @return converted string; if failed string is of zero-length * @see the current code-page comes from video\win32_v.cpp, event-notification * WM_INPUTLANGCHANGE */ -const char *FS2OTTD(const wchar_t *name) +std::string FS2OTTD(const std::wstring &name) { - static char utf8_buf[512]; - return convert_from_fs(name, utf8_buf, lengthof(utf8_buf)); + int name_len = (name.length() >= INT_MAX) ? INT_MAX : (int)name.length(); + int len = WideCharToMultiByte(CP_UTF8, 0, name.c_str(), name_len, nullptr, 0, nullptr, nullptr); + if (len <= 0) return std::string(); + char *utf8_buf = AllocaM(char, len + 1); + utf8_buf[len] = '\0'; + WideCharToMultiByte(CP_UTF8, 0, name.c_str(), name_len, utf8_buf, len, nullptr, nullptr); + return std::string(utf8_buf, static_cast(len)); } /** - * Convert from OpenTTD's encoding to wide characters. + * Convert from OpenTTD's encoding to a wide string. * OpenTTD internal encoding is UTF8. - * The returned value's contents can only be guaranteed until the next call to - * this function. So if the value is needed for anything else, use convert_from_fs - * @param name pointer to a valid string that will be converted (UTF8) + * @param name valid string that will be converted (UTF8) * @param console_cp convert to the console encoding instead of the normal system encoding. - * @return pointer to the converted string; if failed string is of zero-length + * @return converted string; if failed string is of zero-length */ -const wchar_t *OTTD2FS(const char *name, bool console_cp) +std::wstring OTTD2FS(const std::string &name, bool console_cp) { - static wchar_t system_buf[512]; - return convert_to_fs(name, system_buf, lengthof(system_buf), console_cp); + int name_len = (name.length() >= INT_MAX) ? INT_MAX : (int)name.length(); + int len = MultiByteToWideChar(CP_UTF8, 0, name.c_str(), name_len, nullptr, 0); + if (len <= 0) return std::wstring(); + wchar_t *system_buf = AllocaM(wchar_t, len + 1); + system_buf[len] = L'\0'; + MultiByteToWideChar(CP_UTF8, 0, name.c_str(), name_len, system_buf, len); + return std::wstring(system_buf, static_cast(len)); } @@ -594,10 +600,8 @@ const wchar_t *OTTD2FS(const char *name, bool console_cp) */ char *convert_from_fs(const wchar_t *name, char *utf8_buf, size_t buflen) { - const wchar_t *wide_buf = name; - /* Convert UTF-16 string to UTF-8. */ - int len = WideCharToMultiByte(CP_UTF8, 0, wide_buf, -1, utf8_buf, (int)buflen, nullptr, nullptr); + int len = WideCharToMultiByte(CP_UTF8, 0, name, -1, utf8_buf, (int)buflen, nullptr, nullptr); if (len == 0) utf8_buf[0] = '\0'; return utf8_buf; diff --git a/src/stdafx.h b/src/stdafx.h index 42a6106e8a..06860f1045 100644 --- a/src/stdafx.h +++ b/src/stdafx.h @@ -93,6 +93,7 @@ #include #include #include +#include #ifndef SIZE_MAX # define SIZE_MAX ((size_t)-1) @@ -252,25 +253,26 @@ #endif /* defined(_MSC_VER) */ -/* NOTE: the string returned by these functions is only valid until the next - * call to the same function and is not thread- or reentrancy-safe */ #if !defined(STRGEN) && !defined(SETTINGSGEN) # if defined(_WIN32) char *getcwd(char *buf, size_t size); -# include # include +# include - namespace std { using ::_wfopen; } -# define fopen(file, mode) _wfopen(OTTD2FS(file), _T(mode)) -# define unlink(file) _wunlink(OTTD2FS(file)) +# define fopen(file, mode) _wfopen(OTTD2FS(file).c_str(), _T(mode)) +# define unlink(file) _wunlink(OTTD2FS(file).c_str()) - const char *FS2OTTD(const wchar_t *name); - const wchar_t *OTTD2FS(const char *name, bool console_cp = false); + std::string FS2OTTD(const std::wstring &name); + std::wstring OTTD2FS(const std::string &name, bool console_cp = false); +# elif defined(WITH_ICONV) +# define fopen(file, mode) fopen(OTTD2FS(file).c_str(), mode) + std::string FS2OTTD(const std::string &name); + std::string OTTD2FS(const std::string &name); # else -# define fopen(file, mode) fopen(OTTD2FS(file), mode) - const char *FS2OTTD(const char *name); - const char *OTTD2FS(const char *name); -# endif /* _WIN32 */ + // no override of fopen() since no transformation is required of the filename + template std::string FS2OTTD(T name) { return name; } + template std::string OTTD2FS(T name) { return name; } +# endif /* _WIN32 or WITH_ICONV */ #endif /* STRGEN || SETTINGSGEN */ #if defined(_WIN32) || defined(__OS2__) && !defined(__INNOTEK_LIBC__) diff --git a/src/strings.cpp b/src/strings.cpp index 08e826141c..7cbdc3cb09 100644 --- a/src/strings.cpp +++ b/src/strings.cpp @@ -1910,14 +1910,14 @@ static void GetLanguageList(const char *path) if (dir != nullptr) { struct dirent *dirent; while ((dirent = readdir(dir)) != nullptr) { - const char *d_name = FS2OTTD(dirent->d_name); - const char *extension = strrchr(d_name, '.'); + std::string d_name = FS2OTTD(dirent->d_name); + const char *extension = strrchr(d_name.c_str(), '.'); /* Not a language file */ if (extension == nullptr || strcmp(extension, ".lng") != 0) continue; LanguageMetadata lmd; - seprintf(lmd.file, lastof(lmd.file), "%s%s", path, d_name); + seprintf(lmd.file, lastof(lmd.file), "%s%s", path, d_name.c_str()); /* Check whether the file is of the correct version */ if (!GetLanguageFileHeader(lmd.file, &lmd)) { diff --git a/src/video/win32_v.cpp b/src/video/win32_v.cpp index 1d9cd3e2bf..1b3476dab9 100644 --- a/src/video/win32_v.cpp +++ b/src/video/win32_v.cpp @@ -214,7 +214,7 @@ bool VideoDriver_Win32Base::MakeWindow(bool full_screen, bool resize) char window_title[64]; seprintf(window_title, lastof(window_title), "OpenTTD %s", _openttd_revision); - this->main_wnd = CreateWindow(L"OTTD", OTTD2FS(window_title), style, x, y, w, h, 0, 0, GetModuleHandle(nullptr), this); + this->main_wnd = CreateWindow(L"OTTD", OTTD2FS(window_title).c_str(), style, x, y, w, h, 0, 0, GetModuleHandle(nullptr), this); if (this->main_wnd == nullptr) usererror("CreateWindow failed"); ShowWindow(this->main_wnd, showstyle); } @@ -331,7 +331,7 @@ static LRESULT HandleIMEComposition(HWND hwnd, WPARAM wParam, LPARAM lParam) /* Transmit text to windowing system. */ if (len > 0) { HandleTextInput(nullptr, true); // Clear marked string. - HandleTextInput(FS2OTTD(str)); + HandleTextInput(FS2OTTD(str).c_str()); } SetCompositionPos(hwnd); From 746f1ca11a18af97e4fed06e5ac1af622bf21d35 Mon Sep 17 00:00:00 2001 From: Niels Martin Hansen Date: Fri, 2 Apr 2021 18:35:00 +0200 Subject: [PATCH 023/268] Codechange: Remove the now meaningless console_cp parameter from OTTD2FS --- src/debug.cpp | 2 +- src/os/windows/crashlog_win.cpp | 14 +++++++------- src/os/windows/font_win32.cpp | 6 +++--- src/os/windows/win32.cpp | 4 ++-- src/os/windows/win32.h | 2 +- src/stdafx.h | 2 +- 6 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/debug.cpp b/src/debug.cpp index eb8dc5d4fa..25a0f1a82f 100644 --- a/src/debug.cpp +++ b/src/debug.cpp @@ -133,7 +133,7 @@ static void debug_print(const char *dbg, const char *buf) seprintf(buffer, lastof(buffer), "%sdbg: [%s] %s\n", GetLogPrefix(), dbg, buf); #if defined(_WIN32) wchar_t system_buf[512]; - convert_to_fs(buffer, system_buf, lengthof(system_buf), true); + convert_to_fs(buffer, system_buf, lengthof(system_buf)); fputws(system_buf, stderr); #else fputs(buffer, stderr); diff --git a/src/os/windows/crashlog_win.cpp b/src/os/windows/crashlog_win.cpp index 9768bcc82e..a7ff8e4110 100644 --- a/src/os/windows/crashlog_win.cpp +++ b/src/os/windows/crashlog_win.cpp @@ -705,21 +705,21 @@ static INT_PTR CALLBACK CrashDialogFunc(HWND wnd, UINT msg, WPARAM wParam, LPARA /* Add path to crash.log and crash.dmp (if any) to the crash window text */ size_t len = wcslen(_crash_desc) + 2; - len += wcslen(convert_to_fs(CrashLogWindows::current->crashlog_filename, filenamebuf, lengthof(filenamebuf), false)) + 2; - len += wcslen(convert_to_fs(CrashLogWindows::current->crashdump_filename, filenamebuf, lengthof(filenamebuf), false)) + 2; - len += wcslen(convert_to_fs(CrashLogWindows::current->screenshot_filename, filenamebuf, lengthof(filenamebuf), false)) + 1; + len += wcslen(convert_to_fs(CrashLogWindows::current->crashlog_filename, filenamebuf, lengthof(filenamebuf))) + 2; + len += wcslen(convert_to_fs(CrashLogWindows::current->crashdump_filename, filenamebuf, lengthof(filenamebuf))) + 2; + len += wcslen(convert_to_fs(CrashLogWindows::current->screenshot_filename, filenamebuf, lengthof(filenamebuf))) + 1; wchar_t *text = AllocaM(wchar_t, len); - int printed = _snwprintf(text, len, _crash_desc, convert_to_fs(CrashLogWindows::current->crashlog_filename, filenamebuf, lengthof(filenamebuf), false)); + int printed = _snwprintf(text, len, _crash_desc, convert_to_fs(CrashLogWindows::current->crashlog_filename, filenamebuf, lengthof(filenamebuf))); if (printed < 0 || (size_t)printed > len) { MessageBox(wnd, L"Catastrophic failure trying to display crash message. Could not perform text formatting.", L"OpenTTD", MB_ICONERROR); return FALSE; } - if (convert_to_fs(CrashLogWindows::current->crashdump_filename, filenamebuf, lengthof(filenamebuf), false)[0] != L'\0') { + if (convert_to_fs(CrashLogWindows::current->crashdump_filename, filenamebuf, lengthof(filenamebuf))[0] != L'\0') { wcscat(text, L"\n"); wcscat(text, filenamebuf); } - if (convert_to_fs(CrashLogWindows::current->screenshot_filename, filenamebuf, lengthof(filenamebuf), false)[0] != L'\0') { + if (convert_to_fs(CrashLogWindows::current->screenshot_filename, filenamebuf, lengthof(filenamebuf))[0] != L'\0') { wcscat(text, L"\n"); wcscat(text, filenamebuf); } @@ -738,7 +738,7 @@ static INT_PTR CALLBACK CrashDialogFunc(HWND wnd, UINT msg, WPARAM wParam, LPARA wchar_t filenamebuf[MAX_PATH * 2]; char filename[MAX_PATH]; if (CrashLogWindows::current->WriteSavegame(filename, lastof(filename))) { - convert_to_fs(filename, filenamebuf, lengthof(filenamebuf), false); + convert_to_fs(filename, filenamebuf, lengthof(filenamebuf)); size_t len = lengthof(_save_succeeded) + wcslen(filenamebuf) + 1; wchar_t *text = AllocaM(wchar_t, len); _snwprintf(text, len, _save_succeeded, filenamebuf); diff --git a/src/os/windows/font_win32.cpp b/src/os/windows/font_win32.cpp index 1d923ddb21..59d5e0ad85 100644 --- a/src/os/windows/font_win32.cpp +++ b/src/os/windows/font_win32.cpp @@ -606,12 +606,12 @@ void LoadWin32Font(FontSize fs) /* See if this is an absolute path. */ if (FileExists(settings->font)) { - convert_to_fs(settings->font, fontPath, lengthof(fontPath), false); + convert_to_fs(settings->font, fontPath, lengthof(fontPath)); } else { /* Scan the search-paths to see if it can be found. */ std::string full_font = FioFindFullPath(BASE_DIR, settings->font); if (!full_font.empty()) { - convert_to_fs(full_font.c_str(), fontPath, lengthof(fontPath), false); + convert_to_fs(full_font.c_str(), fontPath, lengthof(fontPath)); } } @@ -649,7 +649,7 @@ void LoadWin32Font(FontSize fs) if (logfont.lfFaceName[0] == 0) { logfont.lfWeight = strcasestr(settings->font, " bold") != nullptr ? FW_BOLD : FW_NORMAL; // Poor man's way to allow selecting bold fonts. - convert_to_fs(settings->font, logfont.lfFaceName, lengthof(logfont.lfFaceName), false); + convert_to_fs(settings->font, logfont.lfFaceName, lengthof(logfont.lfFaceName)); } HFONT font = CreateFontIndirect(&logfont); diff --git a/src/os/windows/win32.cpp b/src/os/windows/win32.cpp index 568876ab1e..3f72d1e723 100644 --- a/src/os/windows/win32.cpp +++ b/src/os/windows/win32.cpp @@ -578,7 +578,7 @@ std::string FS2OTTD(const std::wstring &name) * @param console_cp convert to the console encoding instead of the normal system encoding. * @return converted string; if failed string is of zero-length */ -std::wstring OTTD2FS(const std::string &name, bool console_cp) +std::wstring OTTD2FS(const std::string &name) { int name_len = (name.length() >= INT_MAX) ? INT_MAX : (int)name.length(); int len = MultiByteToWideChar(CP_UTF8, 0, name.c_str(), name_len, nullptr, 0); @@ -618,7 +618,7 @@ char *convert_from_fs(const wchar_t *name, char *utf8_buf, size_t buflen) * @param console_cp convert to the console encoding instead of the normal system encoding. * @return pointer to system_buf. If conversion fails the string is of zero-length */ -wchar_t *convert_to_fs(const char *name, wchar_t *system_buf, size_t buflen, bool console_cp) +wchar_t *convert_to_fs(const char *name, wchar_t *system_buf, size_t buflen) { int len = MultiByteToWideChar(CP_UTF8, 0, name, -1, system_buf, (int)buflen); if (len == 0) system_buf[0] = '\0'; diff --git a/src/os/windows/win32.h b/src/os/windows/win32.h index 60cbed6e97..095181c46c 100644 --- a/src/os/windows/win32.h +++ b/src/os/windows/win32.h @@ -17,7 +17,7 @@ typedef void (*Function)(int); bool LoadLibraryList(Function proc[], const char *dll); char *convert_from_fs(const wchar_t *name, char *utf8_buf, size_t buflen); -wchar_t *convert_to_fs(const char *name, wchar_t *utf16_buf, size_t buflen, bool console_cp = false); +wchar_t *convert_to_fs(const char *name, wchar_t *utf16_buf, size_t buflen); #if defined(__MINGW32__) && !defined(__MINGW64__) #define SHGFP_TYPE_CURRENT 0 diff --git a/src/stdafx.h b/src/stdafx.h index 06860f1045..f13104a5b2 100644 --- a/src/stdafx.h +++ b/src/stdafx.h @@ -263,7 +263,7 @@ # define unlink(file) _wunlink(OTTD2FS(file).c_str()) std::string FS2OTTD(const std::wstring &name); - std::wstring OTTD2FS(const std::string &name, bool console_cp = false); + std::wstring OTTD2FS(const std::string &name); # elif defined(WITH_ICONV) # define fopen(file, mode) fopen(OTTD2FS(file).c_str(), mode) std::string FS2OTTD(const std::string &name); From 9725bd8dd057b499e6e79990be4e23ed31517c3c Mon Sep 17 00:00:00 2001 From: Ghislain Antony Vaillant Date: Mon, 5 Apr 2021 17:26:41 +0200 Subject: [PATCH 024/268] Fix: Use FluidSynth default soundfont --- src/music/fluidsynth.cpp | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/src/music/fluidsynth.cpp b/src/music/fluidsynth.cpp index 61686a441e..acbc4a47ea 100644 --- a/src/music/fluidsynth.cpp +++ b/src/music/fluidsynth.cpp @@ -77,12 +77,22 @@ const char *MusicDriver_FluidSynth::Start(const StringList ¶m) /* Load a SoundFont and reset presets (so that new instruments * get used from the SoundFont) */ if (!sfont_name) { - int i; sfont_id = FLUID_FAILED; - for (i = 0; default_sf[i]; i++) { - if (!fluid_is_soundfont(default_sf[i])) continue; - sfont_id = fluid_synth_sfload(_midi.synth, default_sf[i], 1); - if (sfont_id != FLUID_FAILED) break; + + /* Try loading the default soundfont registered with FluidSynth. */ + char *default_soundfont; + fluid_settings_dupstr(_midi.settings, "synth.default-soundfont", &default_soundfont); + if (fluid_is_soundfont(default_soundfont)) { + sfont_id = fluid_synth_sfload(_midi.synth, default_soundfont, 1); + } + + /* If no default soundfont found, try our own list. */ + if (sfont_id == FLUID_FAILED) { + for (int i = 0; default_sf[i]; i++) { + if (!fluid_is_soundfont(default_sf[i])) continue; + sfont_id = fluid_synth_sfload(_midi.synth, default_sf[i], 1); + if (sfont_id != FLUID_FAILED) break; + } } if (sfont_id == FLUID_FAILED) return "Could not open any sound font"; } else { From e7cf589868480022599f6935997ef45963fc3f5a Mon Sep 17 00:00:00 2001 From: Ghislain Antony Vaillant Date: Sun, 4 Apr 2021 21:07:10 +0200 Subject: [PATCH 025/268] Fix: Honor default soundfont for FluidSynth Debian now provides a default soundfont for FluidSynth via its alternatives system. In short, FluidSynth is configured to look for `/usr/share/sounds/sf3/default-GM.sf3` as its default soundfont, and each soundfront package (FluidR3, OPL-3, MuseScore...) may provide or override this symlink. By default, FluidSynth is installed on Debian with the `TimGM6mb` soundfont by default due to its limited size. See https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=929185 for further details. --- src/music/fluidsynth.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/music/fluidsynth.cpp b/src/music/fluidsynth.cpp index acbc4a47ea..000432e1da 100644 --- a/src/music/fluidsynth.cpp +++ b/src/music/fluidsynth.cpp @@ -29,7 +29,15 @@ static FMusicDriver_FluidSynth iFMusicDriver_FluidSynth; /** List of sound fonts to try by default. */ static const char *default_sf[] = { - /* Debian/Ubuntu/OpenSUSE preferred */ + /* FluidSynth preferred */ + /* See: https://www.fluidsynth.org/api/settings_synth.html#settings_synth_default-soundfont */ + "/usr/share/soundfonts/default.sf2", + + /* Debian/Ubuntu preferred */ + /* See: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=929185 */ + "/usr/share/sounds/sf3/default-GM.sf3", + + /* OpenSUSE preferred */ "/usr/share/sounds/sf2/FluidR3_GM.sf2", /* RedHat/Fedora/Arch preferred */ From f306d65ea3985de4701d4a552b653757d0cb4858 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Guilloux?= Date: Wed, 7 Apr 2021 12:43:17 +0200 Subject: [PATCH 026/268] Fix: [CMake] Use the right run-time library depending on vcpkg triplet (#8964) --- cmake/CompileFlags.cmake | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/cmake/CompileFlags.cmake b/cmake/CompileFlags.cmake index f4047b8396..86d336b595 100644 --- a/cmake/CompileFlags.cmake +++ b/cmake/CompileFlags.cmake @@ -4,20 +4,22 @@ # macro(compile_flags) if(MSVC) - # Switch to MT (static) instead of MD (dynamic) binary + if(VCPKG_TARGET_TRIPLET MATCHES "-static" AND NOT VCPKG_TARGET_TRIPLET MATCHES "-md") + # Switch to MT (static) instead of MD (dynamic) binary - # For MSVC two generators are available - # - a command line generator (Ninja) using CMAKE_BUILD_TYPE to specify the - # configuration of the build tree - # - an IDE generator (Visual Studio) using CMAKE_CONFIGURATION_TYPES to - # specify all configurations that will be available in the generated solution - list(APPEND MSVC_CONFIGS "${CMAKE_BUILD_TYPE}" "${CMAKE_CONFIGURATION_TYPES}") + # For MSVC two generators are available + # - a command line generator (Ninja) using CMAKE_BUILD_TYPE to specify the + # configuration of the build tree + # - an IDE generator (Visual Studio) using CMAKE_CONFIGURATION_TYPES to + # specify all configurations that will be available in the generated solution + list(APPEND MSVC_CONFIGS "${CMAKE_BUILD_TYPE}" "${CMAKE_CONFIGURATION_TYPES}") - # Set usage of static runtime for all configurations - foreach(MSVC_CONFIG ${MSVC_CONFIGS}) - string(TOUPPER "CMAKE_CXX_FLAGS_${MSVC_CONFIG}" MSVC_FLAGS) - string(REPLACE "/MD" "/MT" ${MSVC_FLAGS} "${${MSVC_FLAGS}}") - endforeach() + # Set usage of static runtime for all configurations + foreach(MSVC_CONFIG ${MSVC_CONFIGS}) + string(TOUPPER "CMAKE_CXX_FLAGS_${MSVC_CONFIG}" MSVC_FLAGS) + string(REPLACE "/MD" "/MT" ${MSVC_FLAGS} "${${MSVC_FLAGS}}") + endforeach() + endif() # "If /Zc:rvalueCast is specified, the compiler follows section 5.4 of the # C++11 standard". We need C++11 for the way we use threads. From 152940358469f060d0af68ecefb88293ac604aa0 Mon Sep 17 00:00:00 2001 From: translators Date: Wed, 7 Apr 2021 17:53:18 +0000 Subject: [PATCH 027/268] Update: Translations from eints norwegian (bokmal): 3 changes by Anolitt spanish (mexican): 3 changes by absay vietnamese: 3 changes by KhoiCanDev german: 3 changes by danidoedel finnish: 3 changes by hpiirai catalan: 3 changes by J0anJosep spanish: 3 changes by JohnBoyFan --- src/lang/catalan.txt | 3 +++ src/lang/finnish.txt | 3 +++ src/lang/german.txt | 3 +++ src/lang/norwegian_bokmal.txt | 3 +++ src/lang/spanish.txt | 3 +++ src/lang/spanish_MX.txt | 3 +++ src/lang/vietnamese.txt | 3 +++ 7 files changed, 21 insertions(+) diff --git a/src/lang/catalan.txt b/src/lang/catalan.txt index bd9166e6ea..f4d8021e19 100644 --- a/src/lang/catalan.txt +++ b/src/lang/catalan.txt @@ -1139,6 +1139,7 @@ STR_CONFIG_SETTING_TREE_CAPTION :{WHITE}Configur STR_CONFIG_SETTING_FILTER_TITLE :{G=Femenin}{BLACK}Cadena de filtrat: STR_CONFIG_SETTING_EXPAND_ALL :{BLACK}Desplega-ho tot STR_CONFIG_SETTING_COLLAPSE_ALL :{BLACK}Plega-ho tot +STR_CONFIG_SETTING_RESET_ALL :{BLACK}Restableix tots els valors STR_CONFIG_SETTING_NO_EXPLANATION_AVAILABLE_HELPTEXT :(cap explicació disponible) STR_CONFIG_SETTING_DEFAULT_VALUE :{LTBLUE}Valor per defecte: {ORANGE}{STRING} STR_CONFIG_SETTING_TYPE :{LTBLUE}Tipus de paràmetre: {ORANGE}{STRING} @@ -1147,6 +1148,8 @@ STR_CONFIG_SETTING_TYPE_GAME_MENU :Paràmetre de l STR_CONFIG_SETTING_TYPE_GAME_INGAME :Paràmetre de la partida (emmagatzemat a la partida actual; només afecta la partida actual) STR_CONFIG_SETTING_TYPE_COMPANY_MENU :Paràmetre de la companyia (emmagatzemat a les partides; només afectarà les partides noves) STR_CONFIG_SETTING_TYPE_COMPANY_INGAME :Paràmetre de la companyia (emmagatzemat a la partida actual; només afecta la companyia actual) +STR_CONFIG_SETTING_RESET_ALL_CONFIRMATION_DIALOG_CAPTION :{WHITE}Avís! +STR_CONFIG_SETTING_RESET_ALL_CONFIRMATION_DIALOG_TEXT :{WHITE}Aquesta acció restablirà la configuració de la partida als seus valors per defecte.{}Esteu segur que voleu fer-ho? STR_CONFIG_SETTING_RESTRICT_CATEGORY :{BLACK}Categoria: STR_CONFIG_SETTING_RESTRICT_TYPE :{BLACK}Tipus: diff --git a/src/lang/finnish.txt b/src/lang/finnish.txt index dd239bce20..e457ea9fe8 100644 --- a/src/lang/finnish.txt +++ b/src/lang/finnish.txt @@ -1138,6 +1138,7 @@ STR_CONFIG_SETTING_TREE_CAPTION :{WHITE}Asetukse STR_CONFIG_SETTING_FILTER_TITLE :{BLACK}Suodatinteksti: STR_CONFIG_SETTING_EXPAND_ALL :{BLACK}Avaa kaikki STR_CONFIG_SETTING_COLLAPSE_ALL :{BLACK}Sulje kaikki +STR_CONFIG_SETTING_RESET_ALL :{BLACK}Palauta oletukset STR_CONFIG_SETTING_NO_EXPLANATION_AVAILABLE_HELPTEXT :(selitystä ei saatavilla) STR_CONFIG_SETTING_DEFAULT_VALUE :{LTBLUE}Oletusarvo: {ORANGE}{STRING} STR_CONFIG_SETTING_TYPE :{LTBLUE}Asetuksen tyyppi: {ORANGE}{STRING} @@ -1146,6 +1147,8 @@ STR_CONFIG_SETTING_TYPE_GAME_MENU :Pelin asetus (t STR_CONFIG_SETTING_TYPE_GAME_INGAME :Pelin asetus (tallennetaan tallenteeseen; vaikuttaa vain nykyiseen peliin) STR_CONFIG_SETTING_TYPE_COMPANY_MENU :Yhtiön asetus (tallennetaan tallenteisiin; vaikuttaa vain uusiin peleihin) STR_CONFIG_SETTING_TYPE_COMPANY_INGAME :Yhtiön asetus (tallennetaan tallennukseen; vaikuttaa vain nykyiseen yhtiöön) +STR_CONFIG_SETTING_RESET_ALL_CONFIRMATION_DIALOG_CAPTION :{WHITE}Varoitus! +STR_CONFIG_SETTING_RESET_ALL_CONFIRMATION_DIALOG_TEXT :{WHITE}Tämä toiminto palauttaa pelin kaikki asetukset oletusarvoihinsa.{}Haluatko varmasti jatkaa? STR_CONFIG_SETTING_RESTRICT_CATEGORY :{BLACK}Kategoria: STR_CONFIG_SETTING_RESTRICT_TYPE :{BLACK}Tyyppi: diff --git a/src/lang/german.txt b/src/lang/german.txt index b7df032f08..ea971e99ee 100644 --- a/src/lang/german.txt +++ b/src/lang/german.txt @@ -1139,6 +1139,7 @@ STR_CONFIG_SETTING_TREE_CAPTION :{WHITE}Einstell STR_CONFIG_SETTING_FILTER_TITLE :{BLACK}Suchtext: STR_CONFIG_SETTING_EXPAND_ALL :{BLACK}Alles ausklappen STR_CONFIG_SETTING_COLLAPSE_ALL :{BLACK}Alles einklappen +STR_CONFIG_SETTING_RESET_ALL :{BLACK}Alle Werte zurücksetzen STR_CONFIG_SETTING_NO_EXPLANATION_AVAILABLE_HELPTEXT :(keine Erklärung verfügbar) STR_CONFIG_SETTING_DEFAULT_VALUE :{LTBLUE}Standardwert: {ORANGE}{STRING} STR_CONFIG_SETTING_TYPE :{LTBLUE}Art der Einstellung: {ORANGE}{STRING} @@ -1147,6 +1148,8 @@ STR_CONFIG_SETTING_TYPE_GAME_MENU :Karten-Einstell STR_CONFIG_SETTING_TYPE_GAME_INGAME :Karten-Einstellung (im Spielstand gespeichert; beeinflusst nur aktuelles Spiel) STR_CONFIG_SETTING_TYPE_COMPANY_MENU :Firmen-Einstellung (in Spielständen gespeichert; beeinflusst nur neue Spiele) STR_CONFIG_SETTING_TYPE_COMPANY_INGAME :Firmen-Einstellung (im Spielstand gespeichert; beeinflusst nur aktuelle Firma) +STR_CONFIG_SETTING_RESET_ALL_CONFIRMATION_DIALOG_CAPTION :{WHITE}Achtung! +STR_CONFIG_SETTING_RESET_ALL_CONFIRMATION_DIALOG_TEXT :{WHITE}Diese Aktion setzt alle Spieleinstellungen auf ihre Standardwerte zurück.{}Sind Sie sicher, dass Sie fortfahren möchten? STR_CONFIG_SETTING_RESTRICT_CATEGORY :{BLACK}Kategorie: STR_CONFIG_SETTING_RESTRICT_TYPE :{BLACK}Art: diff --git a/src/lang/norwegian_bokmal.txt b/src/lang/norwegian_bokmal.txt index 1e1e00695e..93333cc2e5 100644 --- a/src/lang/norwegian_bokmal.txt +++ b/src/lang/norwegian_bokmal.txt @@ -1140,6 +1140,7 @@ STR_CONFIG_SETTING_TREE_CAPTION :{WHITE}Innstill STR_CONFIG_SETTING_FILTER_TITLE :{BLACK}Filtrer streng: STR_CONFIG_SETTING_EXPAND_ALL :{BLACK}Vis alle STR_CONFIG_SETTING_COLLAPSE_ALL :{BLACK}Skjul alle +STR_CONFIG_SETTING_RESET_ALL :{BLACK}Tilbakestill alle verdier STR_CONFIG_SETTING_NO_EXPLANATION_AVAILABLE_HELPTEXT :(ingen forklaring tilgjengelig) STR_CONFIG_SETTING_DEFAULT_VALUE :{LTBLUE}Standard verdi: {ORANGE}{STRING} STR_CONFIG_SETTING_TYPE :{LTBLUE}Instillings type: {ORANGE}{STRING} @@ -1148,6 +1149,8 @@ STR_CONFIG_SETTING_TYPE_GAME_MENU :Selskapet innst STR_CONFIG_SETTING_TYPE_GAME_INGAME :Selskapet innstilling (lagret i lagringsfilen, påvirker bare gjeldende spill) STR_CONFIG_SETTING_TYPE_COMPANY_MENU :Selskapet innstilling (lagret i lagringsfilen, påvirker bare nye spill) STR_CONFIG_SETTING_TYPE_COMPANY_INGAME :Selskapet innstilling (lagret i lagringsfilen, påvirker bare gjeldende selskap) +STR_CONFIG_SETTING_RESET_ALL_CONFIRMATION_DIALOG_CAPTION :{WHITE}Advarsel! +STR_CONFIG_SETTING_RESET_ALL_CONFIRMATION_DIALOG_TEXT :{WHITE}Denne handlingen vil tilbakestille alle spillinnstillingene til standard verdier.{}Er du sikker på at du vil fortsette? STR_CONFIG_SETTING_RESTRICT_CATEGORY :{BLACK}Kategori: STR_CONFIG_SETTING_RESTRICT_TYPE :{BLACK}Type: diff --git a/src/lang/spanish.txt b/src/lang/spanish.txt index 6b77d1f2a1..e4c02a2ef0 100644 --- a/src/lang/spanish.txt +++ b/src/lang/spanish.txt @@ -1139,6 +1139,7 @@ STR_CONFIG_SETTING_TREE_CAPTION :{WHITE}Configur STR_CONFIG_SETTING_FILTER_TITLE :{BLACK}Patrón de filtrado: STR_CONFIG_SETTING_EXPAND_ALL :{BLACK}Expandir todos STR_CONFIG_SETTING_COLLAPSE_ALL :{BLACK}Colapsar todos +STR_CONFIG_SETTING_RESET_ALL :{BLACK}Restablecer valores STR_CONFIG_SETTING_NO_EXPLANATION_AVAILABLE_HELPTEXT :(no hay explicación disponible) STR_CONFIG_SETTING_DEFAULT_VALUE :{LTBLUE}Valor por defecto: {ORANGE}{STRING} STR_CONFIG_SETTING_TYPE :{LTBLUE}Tipo de opción: {ORANGE}{STRING} @@ -1147,6 +1148,8 @@ STR_CONFIG_SETTING_TYPE_GAME_MENU :Opción de part STR_CONFIG_SETTING_TYPE_GAME_INGAME :Opción de partida (almacenada en la partida guardada, afecta solamente a la partida actual) STR_CONFIG_SETTING_TYPE_COMPANY_MENU :Opción de empresa (se almacena en las partidas guardadas, afecta solamente a las nuevas partidas) STR_CONFIG_SETTING_TYPE_COMPANY_INGAME :Opción de empresa (almacenada en la partida guardada, afecta solamente a la empresa actual) +STR_CONFIG_SETTING_RESET_ALL_CONFIRMATION_DIALOG_CAPTION :¡Advertencia! +STR_CONFIG_SETTING_RESET_ALL_CONFIRMATION_DIALOG_TEXT :{WHITE}Esta acción restablecerá todos los ajustes del juego a sus valores por defecto.{}¿Deseas proceder? STR_CONFIG_SETTING_RESTRICT_CATEGORY :{BLACK}Categoría: STR_CONFIG_SETTING_RESTRICT_TYPE :{BLACK}Tipo: diff --git a/src/lang/spanish_MX.txt b/src/lang/spanish_MX.txt index b2be26e6be..b3cada6dbd 100644 --- a/src/lang/spanish_MX.txt +++ b/src/lang/spanish_MX.txt @@ -1139,6 +1139,7 @@ STR_CONFIG_SETTING_TREE_CAPTION :{WHITE}Configur STR_CONFIG_SETTING_FILTER_TITLE :{BLACK}Filtrar texto: STR_CONFIG_SETTING_EXPAND_ALL :{BLACK}Desplegar todo STR_CONFIG_SETTING_COLLAPSE_ALL :{BLACK}Plegar todo +STR_CONFIG_SETTING_RESET_ALL :{BLACK}Restablecer todos los ajustes STR_CONFIG_SETTING_NO_EXPLANATION_AVAILABLE_HELPTEXT :(sin explicación disponible) STR_CONFIG_SETTING_DEFAULT_VALUE :{LTBLUE}Valor por defecto: {ORANGE}{STRING} STR_CONFIG_SETTING_TYPE :{LTBLUE}Tipo de opción: {ORANGE}{STRING} @@ -1147,6 +1148,8 @@ STR_CONFIG_SETTING_TYPE_GAME_MENU :Opción de part STR_CONFIG_SETTING_TYPE_GAME_INGAME :Opción de partida (almacenada en la partida guardada, afecta solamente la partida actual) STR_CONFIG_SETTING_TYPE_COMPANY_MENU :Opción de empresa (se almacena en las partidas guardadas, afecta solamente las partidas nuevas) STR_CONFIG_SETTING_TYPE_COMPANY_INGAME :Opción de empresa (almacenada en la partida guardada, afecta solamente la empresa actual) +STR_CONFIG_SETTING_RESET_ALL_CONFIRMATION_DIALOG_CAPTION :{WHITE}¡Advertencia! +STR_CONFIG_SETTING_RESET_ALL_CONFIRMATION_DIALOG_TEXT :{WHITE}Esta acción restablecerá todos los ajustes del juego a sus valores originales.{}¿Deseas proceder? STR_CONFIG_SETTING_RESTRICT_CATEGORY :{BLACK}Categoría: STR_CONFIG_SETTING_RESTRICT_TYPE :{BLACK}Tipo: diff --git a/src/lang/vietnamese.txt b/src/lang/vietnamese.txt index 6fd16d0cb7..4b51cb67a9 100644 --- a/src/lang/vietnamese.txt +++ b/src/lang/vietnamese.txt @@ -1138,6 +1138,7 @@ STR_CONFIG_SETTING_TREE_CAPTION :{WHITE}Thiết STR_CONFIG_SETTING_FILTER_TITLE :{BLACK}Lọc chuỗi: STR_CONFIG_SETTING_EXPAND_ALL :{BLACK}Mở xuống tất cả STR_CONFIG_SETTING_COLLAPSE_ALL :{BLACK}Đóng lại tất cả +STR_CONFIG_SETTING_RESET_ALL :{BLACK}Thiết lập lại tất cả STR_CONFIG_SETTING_NO_EXPLANATION_AVAILABLE_HELPTEXT :(hiện không có giải thích nào) STR_CONFIG_SETTING_DEFAULT_VALUE :{LTBLUE}Giá trị mặc định: {ORANGE}{STRING} STR_CONFIG_SETTING_TYPE :{LTBLUE}Kiểu thiết lập: {ORANGE}{STRING} @@ -1146,6 +1147,8 @@ STR_CONFIG_SETTING_TYPE_GAME_MENU :Thiết lập t STR_CONFIG_SETTING_TYPE_GAME_INGAME :Thiết lập trò chơi (chứa trong save; chỉ tác động tới ván chơi hiện tại) STR_CONFIG_SETTING_TYPE_COMPANY_MENU :Thiết lập công ty (chứa trong save; chỉ tác động tới các ván chơi mới) STR_CONFIG_SETTING_TYPE_COMPANY_INGAME :Thiết lập công ty (chứa trong save; chỉ tác động tới công ty hiện tại) +STR_CONFIG_SETTING_RESET_ALL_CONFIRMATION_DIALOG_CAPTION :{WHITE}Chú Ý! +STR_CONFIG_SETTING_RESET_ALL_CONFIRMATION_DIALOG_TEXT :{WHITE}Hành động này sẽ thiết lập lại tất cả các thông số về ban đầu.{}Bạn có muốn tiến hành? STR_CONFIG_SETTING_RESTRICT_CATEGORY :{BLACK}Phân loại: STR_CONFIG_SETTING_RESTRICT_TYPE :{BLACK}Kiểu: From ae94b2d29695d46813e93d26e05abb436f2bf396 Mon Sep 17 00:00:00 2001 From: Jonathan G Rennison Date: Mon, 5 Apr 2021 12:13:56 +0100 Subject: [PATCH 028/268] Fix: Viewport drag tooltip flickering when dragging outside window --- src/misc_gui.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/misc_gui.cpp b/src/misc_gui.cpp index 9d0259b3be..65f98b3653 100644 --- a/src/misc_gui.cpp +++ b/src/misc_gui.cpp @@ -782,7 +782,7 @@ void GuiShowTooltips(Window *parent, StringID str, uint paramcount, const uint64 { DeleteWindowById(WC_TOOLTIPS, 0); - if (str == STR_NULL) return; + if (str == STR_NULL || !_cursor.in_window) return; new TooltipsWindow(parent, str, paramcount, params, close_tooltip); } From 3ede756d23b09f52a8099736d9a78abc966448b8 Mon Sep 17 00:00:00 2001 From: Jonathan G Rennison Date: Mon, 5 Apr 2021 12:30:58 +0100 Subject: [PATCH 029/268] Fix: Viewport drag tooltips not being removed when dragging over other windows --- src/misc_gui.cpp | 6 ++++++ src/viewport.cpp | 2 +- src/window_gui.h | 1 + 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/misc_gui.cpp b/src/misc_gui.cpp index 65f98b3653..2f538f2af9 100644 --- a/src/misc_gui.cpp +++ b/src/misc_gui.cpp @@ -766,6 +766,12 @@ struct TooltipsWindow : public Window case TCC_RIGHT_CLICK: if (!_right_button_down) delete this; break; case TCC_HOVER: if (!_mouse_hovering) delete this; break; case TCC_NONE: break; + + case TCC_EXIT_VIEWPORT: { + Window *w = FindWindowFromPt(_cursor.pos.x, _cursor.pos.y); + if (w == nullptr || IsPtInWindowViewport(w, _cursor.pos.x, _cursor.pos.y) == nullptr) delete this; + break; + } } } }; diff --git a/src/viewport.cpp b/src/viewport.cpp index 2a3f9b32de..aae659d53d 100644 --- a/src/viewport.cpp +++ b/src/viewport.cpp @@ -2637,7 +2637,7 @@ void UpdateTileSelection() * @param params (optional) up to 5 pieces of additional information that may be added to a tooltip * @param close_cond Condition for closing this tooltip. */ -static inline void ShowMeasurementTooltips(StringID str, uint paramcount, const uint64 params[], TooltipCloseCondition close_cond = TCC_NONE) +static inline void ShowMeasurementTooltips(StringID str, uint paramcount, const uint64 params[], TooltipCloseCondition close_cond = TCC_EXIT_VIEWPORT) { if (!_settings_client.gui.measure_tooltip) return; GuiShowTooltips(_thd.GetCallbackWnd(), str, paramcount, params, close_cond); diff --git a/src/window_gui.h b/src/window_gui.h index c792a6b28c..aca7f4486c 100644 --- a/src/window_gui.h +++ b/src/window_gui.h @@ -268,6 +268,7 @@ enum TooltipCloseCondition { TCC_RIGHT_CLICK, TCC_HOVER, TCC_NONE, + TCC_EXIT_VIEWPORT, }; /** From 2b86d42ddc9bfd6aef2856ec953cb03eefe6cd12 Mon Sep 17 00:00:00 2001 From: translators Date: Thu, 8 Apr 2021 17:53:09 +0000 Subject: [PATCH 030/268] Update: Translations from eints english (us): 3 changes by 2TallTyler korean: 3 changes by telk5093 russian: 32 changes by Ln-Wolf spanish: 1 change by JohnBoyFan french: 4 changes by arikover portuguese: 10 changes by azulcosta --- src/lang/english_US.txt | 3 +++ src/lang/french.txt | 5 ++++- src/lang/korean.txt | 3 +++ src/lang/portuguese.txt | 17 ++++++++++------- src/lang/russian.txt | 35 ++++++++++++++++++++++++++++++++--- src/lang/spanish.txt | 2 +- 6 files changed, 53 insertions(+), 12 deletions(-) diff --git a/src/lang/english_US.txt b/src/lang/english_US.txt index 33c67d54e2..4a3eb6abb8 100644 --- a/src/lang/english_US.txt +++ b/src/lang/english_US.txt @@ -1138,6 +1138,7 @@ STR_CONFIG_SETTING_TREE_CAPTION :{WHITE}Settings STR_CONFIG_SETTING_FILTER_TITLE :{BLACK}Filter string: STR_CONFIG_SETTING_EXPAND_ALL :{BLACK}Expand all STR_CONFIG_SETTING_COLLAPSE_ALL :{BLACK}Collapse all +STR_CONFIG_SETTING_RESET_ALL :{BLACK}Reset all values STR_CONFIG_SETTING_NO_EXPLANATION_AVAILABLE_HELPTEXT :(no explanation available) STR_CONFIG_SETTING_DEFAULT_VALUE :{LTBLUE}Default value: {ORANGE}{STRING} STR_CONFIG_SETTING_TYPE :{LTBLUE}Setting type: {ORANGE}{STRING} @@ -1146,6 +1147,8 @@ STR_CONFIG_SETTING_TYPE_GAME_MENU :Game setting (s STR_CONFIG_SETTING_TYPE_GAME_INGAME :Game setting (stored in save; affects only current game) STR_CONFIG_SETTING_TYPE_COMPANY_MENU :Company setting (stored in saves; affects only new games) STR_CONFIG_SETTING_TYPE_COMPANY_INGAME :Company setting (stored in save; affects only current company) +STR_CONFIG_SETTING_RESET_ALL_CONFIRMATION_DIALOG_CAPTION :{WHITE}Caution! +STR_CONFIG_SETTING_RESET_ALL_CONFIRMATION_DIALOG_TEXT :{WHITE}This action will reset all game settings to their default values.{}Are you sure you want to proceed? STR_CONFIG_SETTING_RESTRICT_CATEGORY :{BLACK}Category: STR_CONFIG_SETTING_RESTRICT_TYPE :{BLACK}Type: diff --git a/src/lang/french.txt b/src/lang/french.txt index 20e1104d81..09a7f38e55 100644 --- a/src/lang/french.txt +++ b/src/lang/french.txt @@ -1139,6 +1139,7 @@ STR_CONFIG_SETTING_TREE_CAPTION :{WHITE}Paramèt STR_CONFIG_SETTING_FILTER_TITLE :{BLACK}Filtre{NBSP}: STR_CONFIG_SETTING_EXPAND_ALL :{BLACK}Tout développer STR_CONFIG_SETTING_COLLAPSE_ALL :{BLACK}Tout réduire +STR_CONFIG_SETTING_RESET_ALL :{BLACK}Réinitialiser tous les réglages STR_CONFIG_SETTING_NO_EXPLANATION_AVAILABLE_HELPTEXT :(pas d'explication disponible) STR_CONFIG_SETTING_DEFAULT_VALUE :{LTBLUE}Valeur par défaut{NBSP}: {ORANGE}{STRING} STR_CONFIG_SETTING_TYPE :{LTBLUE}Type de paramètre{NBSP}: {ORANGE}{STRING} @@ -1147,6 +1148,8 @@ STR_CONFIG_SETTING_TYPE_GAME_MENU :Paramètre de j STR_CONFIG_SETTING_TYPE_GAME_INGAME :Paramètre de jeu (enregistré dans la sauvegarde{NBSP}; affecte uniquement la partie actuelle) STR_CONFIG_SETTING_TYPE_COMPANY_MENU :Paramètre de compagnie (enregistré dans les sauvegardes{NBSP}; affecte uniquement les nouvelles parties) STR_CONFIG_SETTING_TYPE_COMPANY_INGAME :Paramètre de compagnie (enregistré dans la sauvegarde{NBSP}; affecte uniquement la compagnie actuelle) +STR_CONFIG_SETTING_RESET_ALL_CONFIRMATION_DIALOG_CAPTION :{WHITE}Attention{NBSP}! +STR_CONFIG_SETTING_RESET_ALL_CONFIRMATION_DIALOG_TEXT :{WHITE}Par cette action, toues les réglages seront réinitialisés aux valeurs par défaut.{}Êtes-vous sûr de vouloir continuer{NBSP}? STR_CONFIG_SETTING_RESTRICT_CATEGORY :{BLACK}Catégorie{NBSP}: STR_CONFIG_SETTING_RESTRICT_TYPE :{BLACK}Type{NBSP}: @@ -2532,7 +2535,7 @@ STR_WATERWAYS_TOOLBAR_BUILD_DOCK_TOOLTIP :{BLACK}Construi STR_WATERWAYS_TOOLBAR_BUOY_TOOLTIP :{BLACK}Placer une bouée pouvant servir de guide aux navires.{}Shift pour afficher seulement le coût estimé. STR_WATERWAYS_TOOLBAR_BUILD_AQUEDUCT_TOOLTIP :{BLACK}Construire un aqueduc.{}Shift pour afficher seulement le coût estimé. STR_WATERWAYS_TOOLBAR_CREATE_LAKE_TOOLTIP :{BLACK}Définir une zone d'eau.{}Construire un canal, sauf si Ctrl est enfoncé au niveau de la mer{NBSP}: dans ce cas, le voisinage sera inondé. -STR_WATERWAYS_TOOLBAR_CREATE_RIVER_TOOLTIP :{BLACK}Placer des rivières +STR_WATERWAYS_TOOLBAR_CREATE_RIVER_TOOLTIP :{BLACK}Placer des rivières. Presser Ctrl pour sélectionner en diagonale # Ship depot construction window STR_DEPOT_BUILD_SHIP_CAPTION :{WHITE}Orientation du dépôt diff --git a/src/lang/korean.txt b/src/lang/korean.txt index 798f2fbb52..47ccfc9028 100644 --- a/src/lang/korean.txt +++ b/src/lang/korean.txt @@ -1139,6 +1139,7 @@ STR_CONFIG_SETTING_TREE_CAPTION :{WHITE}설정 STR_CONFIG_SETTING_FILTER_TITLE :{BLACK}검색할 문자열: STR_CONFIG_SETTING_EXPAND_ALL :{BLACK}모두 펼치기 STR_CONFIG_SETTING_COLLAPSE_ALL :{BLACK}모두 접기 +STR_CONFIG_SETTING_RESET_ALL :{BLACK}모든 설정 초기화 STR_CONFIG_SETTING_NO_EXPLANATION_AVAILABLE_HELPTEXT :(설명이 존재하지 않습니다) STR_CONFIG_SETTING_DEFAULT_VALUE :{LTBLUE}기본값: {ORANGE}{STRING} STR_CONFIG_SETTING_TYPE :{LTBLUE}설정 종류: {ORANGE}{STRING} @@ -1147,6 +1148,8 @@ STR_CONFIG_SETTING_TYPE_GAME_MENU :게임 설정 ( STR_CONFIG_SETTING_TYPE_GAME_INGAME :게임 설정 (게임 저장 파일에 저장됨; 현재 게임에만 적용됨) STR_CONFIG_SETTING_TYPE_COMPANY_MENU :회사 설정 (게임 저장 파일에 저장됨; 새 게임에만 적용됨) STR_CONFIG_SETTING_TYPE_COMPANY_INGAME :회사 설정 (게임 저장 파일에 저장됨; 현재 회사에만 적용됨) +STR_CONFIG_SETTING_RESET_ALL_CONFIRMATION_DIALOG_CAPTION :{WHITE}경고! +STR_CONFIG_SETTING_RESET_ALL_CONFIRMATION_DIALOG_TEXT :{WHITE}게임의 모든 설정을 기본값으로 되돌립니다.{}정말 모든 설정을 초기화하시겠습니까? STR_CONFIG_SETTING_RESTRICT_CATEGORY :{BLACK}분류: STR_CONFIG_SETTING_RESTRICT_TYPE :{BLACK}종류: diff --git a/src/lang/portuguese.txt b/src/lang/portuguese.txt index 203cd9327d..f98ef16211 100644 --- a/src/lang/portuguese.txt +++ b/src/lang/portuguese.txt @@ -1139,6 +1139,7 @@ STR_CONFIG_SETTING_TREE_CAPTION :{WHITE}Definiç STR_CONFIG_SETTING_FILTER_TITLE :{BLACK}Filtrar frase: STR_CONFIG_SETTING_EXPAND_ALL :{BLACK}Expandir todas STR_CONFIG_SETTING_COLLAPSE_ALL :{BLACK}Colapsar todas +STR_CONFIG_SETTING_RESET_ALL :{BLACK}Repor todos os valores STR_CONFIG_SETTING_NO_EXPLANATION_AVAILABLE_HELPTEXT :(sem explicação disponível) STR_CONFIG_SETTING_DEFAULT_VALUE :{LTBLUE}Valor por omissão: {ORANGE}{STRING} STR_CONFIG_SETTING_TYPE :{LTBLUE}Tipo de configuração: {ORANGE}{STRING} @@ -1147,6 +1148,8 @@ STR_CONFIG_SETTING_TYPE_GAME_MENU :Configuração STR_CONFIG_SETTING_TYPE_GAME_INGAME :Configuração de jogo (guardado; afeta apenas o jogo atual) STR_CONFIG_SETTING_TYPE_COMPANY_MENU :Configurações de empresa (guardado; afeta apenas novos jogos) STR_CONFIG_SETTING_TYPE_COMPANY_INGAME :Configurações de empresa (guardado; afeta apenas a empresa atual) +STR_CONFIG_SETTING_RESET_ALL_CONFIRMATION_DIALOG_CAPTION :{WHITE}Cuidado! +STR_CONFIG_SETTING_RESET_ALL_CONFIRMATION_DIALOG_TEXT :{WHITE}Esta ação vai repor todas as configurações do jogo para os seus valores padrão.{}Tem a certeza que deseja continuar? STR_CONFIG_SETTING_RESTRICT_CATEGORY :{BLACK}Categoria: STR_CONFIG_SETTING_RESTRICT_TYPE :{BLACK}Tipo: @@ -1904,11 +1907,11 @@ STR_LIVERY_SECONDARY_TOOLTIP :{BLACK}Seleccio STR_LIVERY_PANEL_TOOLTIP :{BLACK}Seleccione o esquema de cores a alterar, ou esquemas múltiplos utilizando a tecla Ctrl com o botão esquerdo do rato. Marque a caixa para comutar a utilização do esquema de cores STR_LIVERY_DEFAULT :Estampagem Padrão -STR_LIVERY_STEAM :Motor a Vapor -STR_LIVERY_DIESEL :Motor Diesel -STR_LIVERY_ELECTRIC :Motor Eléctrico -STR_LIVERY_MONORAIL :Motor Monocarril -STR_LIVERY_MAGLEV :Motor Maglev (Levitação Magnética) +STR_LIVERY_STEAM :Locomotivas a Vapor +STR_LIVERY_DIESEL :Locomotivas Diesel +STR_LIVERY_ELECTRIC :Locomotivas Eléctricas +STR_LIVERY_MONORAIL :Motoras Monocarril +STR_LIVERY_MAGLEV :Motoras Maglev (Levitação Magnética) STR_LIVERY_DMU :DMU STR_LIVERY_EMU :EMU STR_LIVERY_PASSENGER_WAGON_STEAM :Carruagem de Passageiros (Vapor) @@ -1917,8 +1920,8 @@ STR_LIVERY_PASSENGER_WAGON_ELECTRIC :Carruagem de Pa STR_LIVERY_PASSENGER_WAGON_MONORAIL :Carruagem de Passageiros (Monocarril) STR_LIVERY_PASSENGER_WAGON_MAGLEV :Carruagem de Passageiros (Maglev) STR_LIVERY_FREIGHT_WAGON :Vagão de Carga -STR_LIVERY_BUS :Autocarro -STR_LIVERY_TRUCK :Veículo de Mercadorias +STR_LIVERY_BUS :Autocarros +STR_LIVERY_TRUCK :Camiões STR_LIVERY_PASSENGER_SHIP :Navio de passageiros STR_LIVERY_FREIGHT_SHIP :Navio cargueiro STR_LIVERY_HELICOPTER :Helicóptero diff --git a/src/lang/russian.txt b/src/lang/russian.txt index 5d5f88da6a..1a67358636 100644 --- a/src/lang/russian.txt +++ b/src/lang/russian.txt @@ -1263,7 +1263,7 @@ STR_DISASTER_NONE :нет STR_DISASTER_REDUCED :сниженная STR_DISASTER_NORMAL :обычная -STR_SUBSIDY_X1_5 :x1.5 +STR_SUBSIDY_X1_5 :x1,5 STR_SUBSIDY_X2 :x2 STR_SUBSIDY_X3 :x3 STR_SUBSIDY_X4 :x4 @@ -1273,6 +1273,8 @@ STR_TERRAIN_TYPE_FLAT :Равнинн STR_TERRAIN_TYPE_HILLY :Холмистый STR_TERRAIN_TYPE_MOUNTAINOUS :Преимущественно горный STR_TERRAIN_TYPE_ALPINIST :Исключительно горный +STR_TERRAIN_TYPE_CUSTOM :Установить высоту вручную +STR_TERRAIN_TYPE_CUSTOM_VALUE :Установленная высота ({NUM}) STR_CITY_APPROVAL_PERMISSIVE :дозволяющее STR_CITY_APPROVAL_TOLERANT :терпимое @@ -1285,6 +1287,7 @@ STR_CONFIG_SETTING_TREE_CAPTION :{WHITE}Расш STR_CONFIG_SETTING_FILTER_TITLE :{BLACK}Фильтр: STR_CONFIG_SETTING_EXPAND_ALL :{BLACK}Развернуть всё STR_CONFIG_SETTING_COLLAPSE_ALL :{BLACK}Свернуть всё +STR_CONFIG_SETTING_RESET_ALL :{BLACK}Сбросить все значения STR_CONFIG_SETTING_NO_EXPLANATION_AVAILABLE_HELPTEXT :(нет описания) STR_CONFIG_SETTING_DEFAULT_VALUE :{LTBLUE}Значение по умолчанию: {ORANGE}{STRING} STR_CONFIG_SETTING_TYPE :{LTBLUE}Тип настроек: {ORANGE}{STRING} @@ -1293,6 +1296,8 @@ STR_CONFIG_SETTING_TYPE_GAME_MENU :Настрой STR_CONFIG_SETTING_TYPE_GAME_INGAME :Настройки игры (содержатся в файлах сохранений; действительны только для текущей игры) STR_CONFIG_SETTING_TYPE_COMPANY_MENU :Настройки компании (содержатся в файлах сохранений; действительны только для новых игр) STR_CONFIG_SETTING_TYPE_COMPANY_INGAME :Настройки компании (содержатся в файлах сохранений; действительны только для текущей игры) +STR_CONFIG_SETTING_RESET_ALL_CONFIRMATION_DIALOG_CAPTION :{WHITE}Внимание! +STR_CONFIG_SETTING_RESET_ALL_CONFIRMATION_DIALOG_TEXT :{WHITE}Вы уверены, что хотите сбросить все настройки игры к значениям по умолчанию? STR_CONFIG_SETTING_RESTRICT_CATEGORY :{BLACK}Категория: STR_CONFIG_SETTING_RESTRICT_TYPE :{BLACK}Тип: @@ -1356,6 +1361,10 @@ STR_CONFIG_SETTING_DISASTERS_HELPTEXT :Включен STR_CONFIG_SETTING_CITY_APPROVAL :Отношение городского совета к реструктуризации окрестностей: {STRING} STR_CONFIG_SETTING_CITY_APPROVAL_HELPTEXT :Выберите уровень влияния шума и загрязнения окружающей среды компаниями на их рейтинг в городах и возможность дальнейшего строительства около городов +STR_CONFIG_SETTING_MAP_HEIGHT_LIMIT :Максимальная высота объектов на карте: {STRING} +STR_CONFIG_SETTING_MAP_HEIGHT_LIMIT_HELPTEXT :Установить максимальное значение высоты гор на карте. «(Автовыбор)» подберёт приемлемое значение после создания ландшафта. +STR_CONFIG_SETTING_MAP_HEIGHT_LIMIT_VALUE :{NUM} +STR_CONFIG_SETTING_MAP_HEIGHT_LIMIT_AUTO :(автовыбор) STR_CONFIG_SETTING_TOO_HIGH_MOUNTAIN :{WHITE}Невозможно изменить максимальную высоту. На карте есть горы выше этого значения. STR_CONFIG_SETTING_AUTOSLOPE :Разрешить изм. ландшафта под зданиями, дорогами и т. д. (автоспуски): {STRING} STR_CONFIG_SETTING_AUTOSLOPE_HELPTEXT :Разрешить изменение ландшафта под домами и дорогами, не требуя их сноса @@ -1500,8 +1509,13 @@ STR_CONFIG_SETTING_INDUSTRY_DENSITY_HELPTEXT :Выберит STR_CONFIG_SETTING_OIL_REF_EDGE_DISTANCE :Макс. расстояние от края карты до предприятий нефтяной индустрии: {STRING} STR_CONFIG_SETTING_OIL_REF_EDGE_DISTANCE_HELPTEXT :Эта настройка ограничивает расстояние от края карты до нефтяных платформ и нефтеперерабатывающих заводов. Таким образом, на краях карты, оканчивающихся водой, они будут строиться у берега. На картах размером более 256 это значение будет соответственно увеличено. STR_CONFIG_SETTING_SNOWLINE_HEIGHT :Высота снеговой линии: {STRING} -STR_CONFIG_SETTING_SNOWLINE_HEIGHT_HELPTEXT :Укажите, на какой высоте в субарктическом климате устанавливается снеговой покров. Наличие снега влияет на расстановку предприятий и на условия роста городов. +STR_CONFIG_SETTING_SNOWLINE_HEIGHT_HELPTEXT :Это значение определяет, на какой высоте в субарктическом климате устанавливается снеговой покров. Наличие снега влияет на расстановку предприятий и на условия роста городов.{}Значение может быть установлено только в редакторе сценариев; иначе оно рассчитывается в зависимости от значения «снежного покрытия». +STR_CONFIG_SETTING_SNOW_COVERAGE :Снежное покрытие: {STRING} +STR_CONFIG_SETTING_SNOW_COVERAGE_HELPTEXT :Это значение определяет примерную относительную площадь суши, покрытой снегом в субарктическом климате.Наличие снега влияет на расстановку предприятий и на условия роста городов.{}Используется только при создании карты. Земля чуть выше уровня моря никогда не покрывается снегом. +STR_CONFIG_SETTING_SNOW_COVERAGE_VALUE :{NUM}% +STR_CONFIG_SETTING_DESERT_COVERAGE :Песчаное покрытие: {STRING} STR_CONFIG_SETTING_DESERT_COVERAGE_HELPTEXT :Укажите приблизительное количество пустыни на тропическом ландшафте. Пустыня также влияет на промышленное производство. Используется только во время создания карты +STR_CONFIG_SETTING_DESERT_COVERAGE_VALUE :{NUM}% STR_CONFIG_SETTING_ROUGHNESS_OF_TERRAIN :Грубость ландшафта: {STRING} STR_CONFIG_SETTING_ROUGHNESS_OF_TERRAIN_HELPTEXT :(Только для TerraGenesis){}Выберите количество гор и холмов на карте. На гладком ландшафте холмов немного и они более пологие. На грубом - много гор, и ландшафт может показаться слишком однообразным. STR_CONFIG_SETTING_ROUGHNESS_OF_TERRAIN_VERY_SMOOTH :Очень гладкий @@ -2691,7 +2705,7 @@ STR_WATERWAYS_TOOLBAR_BUILD_DOCK_TOOLTIP :{BLACK}Стро STR_WATERWAYS_TOOLBAR_BUOY_TOOLTIP :{BLACK}Буи помогают в навигации на больших расстояниях; используйте их как маршрутные точки. При нажатом Shift - оценка стоимости строительства. STR_WATERWAYS_TOOLBAR_BUILD_AQUEDUCT_TOOLTIP :{BLACK}Строительство акведуков. При нажатом Shift - оценка стоимости строительства. STR_WATERWAYS_TOOLBAR_CREATE_LAKE_TOOLTIP :{BLACK}Создать канал.{}При зажатом Ctrl клетка на уровне моря наполняется водой. -STR_WATERWAYS_TOOLBAR_CREATE_RIVER_TOOLTIP :{BLACK}Разместить реки. +STR_WATERWAYS_TOOLBAR_CREATE_RIVER_TOOLTIP :{BLACK}Создание рек на карте. При нажатом Ctrl они создаются по диагонали. # Ship depot construction window STR_DEPOT_BUILD_SHIP_CAPTION :{WHITE}Направление дока @@ -3066,6 +3080,17 @@ STR_MAPGEN_BY :{BLACK}× STR_MAPGEN_NUMBER_OF_TOWNS :{BLACK}Количество городов: STR_MAPGEN_DATE :{BLACK}Дата: STR_MAPGEN_NUMBER_OF_INDUSTRIES :{BLACK}Кол-во предпр.: +STR_MAPGEN_HEIGHTMAP_HEIGHT :{BLACK}Высочайшая вершина: +STR_MAPGEN_HEIGHTMAP_HEIGHT_UP :{BLACK}Увеличить максимальную высоту гор на карте на 1 +STR_MAPGEN_HEIGHTMAP_HEIGHT_DOWN :{BLACK}Уменьшить максимальную высоту гор на карте на 1 +STR_MAPGEN_SNOW_COVERAGE :{BLACK}Снежное покрытие: +STR_MAPGEN_SNOW_COVERAGE_UP :{BLACK}Увеличить площадь снежного покрытия на 10% +STR_MAPGEN_SNOW_COVERAGE_DOWN :{BLACK}Уменьшить площадь снежного покрытия на 10% +STR_MAPGEN_SNOW_COVERAGE_TEXT :{BLACK}{NUM}% +STR_MAPGEN_DESERT_COVERAGE :{BLACK}Песчаное покрытие: +STR_MAPGEN_DESERT_COVERAGE_UP :{BLACK}Увеличить площадь песчаного покрытия на 10% +STR_MAPGEN_DESERT_COVERAGE_DOWN :{BLACK}Уменьшить площадь песчаного покрытия на 10% +STR_MAPGEN_DESERT_COVERAGE_TEXT :{BLACK}{NUM}% STR_MAPGEN_LAND_GENERATOR :{BLACK}Генератор ландшафта: STR_MAPGEN_TERRAIN_TYPE :{BLACK}Тип ландшафта: STR_MAPGEN_QUANTITY_OF_SEA_LAKES :{BLACK}Количество морей и озёр: @@ -3091,7 +3116,10 @@ STR_MAPGEN_HEIGHTMAP_NAME :{BLACK}Назв STR_MAPGEN_HEIGHTMAP_SIZE_LABEL :{BLACK}Размер: STR_MAPGEN_HEIGHTMAP_SIZE :{ORANGE}{NUM} × {NUM} +STR_MAPGEN_TERRAIN_TYPE_QUERY_CAPT :{WHITE}Максимальная высота STR_MAPGEN_HEIGHTMAP_HEIGHT_QUERY_CAPT :{WHITE}Cамая высокая вершина +STR_MAPGEN_SNOW_COVERAGE_QUERY_CAPT :{WHITE}Снежное покрытие (в %) +STR_MAPGEN_DESERT_COVERAGE_QUERY_CAPT :{WHITE}Песчаное покрытие (в %) STR_MAPGEN_START_DATE_QUERY_CAPT :{WHITE}Изменить год начала игры # SE Map generation @@ -4520,6 +4548,7 @@ STR_WARNING_FALLBACK_SOUNDSET :{WHITE}Теку STR_WARNING_SCREENSHOT_SIZE_CAPTION :{WHITE}Огромный снимок экрана STR_WARNING_SCREENSHOT_SIZE_MESSAGE :{YELLOW}Снимок экрана будет иметь размер {COMMA}{NBSP}х{NBSP}{COMMA}{NBSP}пиксел{P ь я ей}. Его создание займёт некоторое время. Продолжить? +STR_MESSAGE_HEIGHTMAP_SUCCESSFULLY :{WHITE}Карта высот успешно сохранена под именем «{STRING}». Максимальная высота - {NUM}. STR_MESSAGE_SCREENSHOT_SUCCESSFULLY :{WHITE}Снимок экрана сохранён под именем «{STRING}» STR_ERROR_SCREENSHOT_FAILED :{WHITE}Не удалось сделать снимок экрана diff --git a/src/lang/spanish.txt b/src/lang/spanish.txt index e4c02a2ef0..b7dc758c3c 100644 --- a/src/lang/spanish.txt +++ b/src/lang/spanish.txt @@ -1148,7 +1148,7 @@ STR_CONFIG_SETTING_TYPE_GAME_MENU :Opción de part STR_CONFIG_SETTING_TYPE_GAME_INGAME :Opción de partida (almacenada en la partida guardada, afecta solamente a la partida actual) STR_CONFIG_SETTING_TYPE_COMPANY_MENU :Opción de empresa (se almacena en las partidas guardadas, afecta solamente a las nuevas partidas) STR_CONFIG_SETTING_TYPE_COMPANY_INGAME :Opción de empresa (almacenada en la partida guardada, afecta solamente a la empresa actual) -STR_CONFIG_SETTING_RESET_ALL_CONFIRMATION_DIALOG_CAPTION :¡Advertencia! +STR_CONFIG_SETTING_RESET_ALL_CONFIRMATION_DIALOG_CAPTION :{WHITE}¡Advertencia! STR_CONFIG_SETTING_RESET_ALL_CONFIRMATION_DIALOG_TEXT :{WHITE}Esta acción restablecerá todos los ajustes del juego a sus valores por defecto.{}¿Deseas proceder? STR_CONFIG_SETTING_RESTRICT_CATEGORY :{BLACK}Categoría: From 5233fd275146ced3a0338d1740cef024be4063f5 Mon Sep 17 00:00:00 2001 From: Wim Leflere Date: Fri, 9 Apr 2021 11:22:03 +0200 Subject: [PATCH 031/268] Change: [win32] Use user UI language for initial language selection (#8973) (#8974) --- src/os/windows/win32.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/os/windows/win32.cpp b/src/os/windows/win32.cpp index 3f72d1e723..073f70b98c 100644 --- a/src/os/windows/win32.cpp +++ b/src/os/windows/win32.cpp @@ -629,9 +629,12 @@ wchar_t *convert_to_fs(const char *name, wchar_t *system_buf, size_t buflen) /** Determine the current user's locale. */ const char *GetCurrentLocale(const char *) { + const LANGID userUiLang = GetUserDefaultUILanguage(); + const LCID userUiLocale = MAKELCID(userUiLang, SORT_DEFAULT); + char lang[9], country[9]; - if (GetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_SISO639LANGNAME, lang, lengthof(lang)) == 0 || - GetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_SISO3166CTRYNAME, country, lengthof(country)) == 0) { + if (GetLocaleInfoA(userUiLocale, LOCALE_SISO639LANGNAME, lang, lengthof(lang)) == 0 || + GetLocaleInfoA(userUiLocale, LOCALE_SISO3166CTRYNAME, country, lengthof(country)) == 0) { /* Unable to retrieve the locale. */ return nullptr; } From 49d370185bf2a5fe8c34a4087ee8e9763c2821c9 Mon Sep 17 00:00:00 2001 From: Niels Martin Hansen Date: Sun, 4 Apr 2021 09:51:48 +0200 Subject: [PATCH 032/268] Change: Default music and sound effects volume to be lower --- src/table/settings.ini | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/table/settings.ini b/src/table/settings.ini index c4a6aeafc9..efbb3e0c78 100644 --- a/src/table/settings.ini +++ b/src/table/settings.ini @@ -3522,7 +3522,7 @@ cat = SC_BASIC var = music.music_vol type = SLE_UINT8 flags = SLF_NOT_IN_SAVE | SLF_NO_NETWORK_SYNC -def = 127 +def = 50 min = 0 max = 127 interval = 1 @@ -3532,7 +3532,7 @@ cat = SC_BASIC var = music.effect_vol type = SLE_UINT8 flags = SLF_NOT_IN_SAVE | SLF_NO_NETWORK_SYNC -def = 127 +def = 100 min = 0 max = 127 interval = 1 From 64c9af09914472c6a9eeb495cd790f7c325b194c Mon Sep 17 00:00:00 2001 From: Niels Martin Hansen Date: Sun, 4 Apr 2021 10:34:37 +0200 Subject: [PATCH 033/268] Add: NWidgetBase::GetCurrentRect function --- src/widget_type.h | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/widget_type.h b/src/widget_type.h index c5083c6c5a..1692ef2266 100644 --- a/src/widget_type.h +++ b/src/widget_type.h @@ -158,6 +158,16 @@ public: virtual void Draw(const Window *w) = 0; virtual void SetDirty(const Window *w) const; + Rect GetCurrentRect() const + { + Rect r; + r.left = this->pos_x; + r.top = this->pos_y; + r.right = this->pos_x + this->current_x; + r.bottom = this->pos_y + this->current_y; + return r; + } + WidgetType type; ///< Type of the widget / nested widget. uint fill_x; ///< Horizontal fill stepsize (from initial size, \c 0 means not resizable). uint fill_y; ///< Vertical fill stepsize (from initial size, \c 0 means not resizable). From 88c92f7b7c51295f5a9cc8d1b837fbca03be37fb Mon Sep 17 00:00:00 2001 From: Niels Martin Hansen Date: Sun, 4 Apr 2021 10:04:06 +0200 Subject: [PATCH 034/268] Codechange: Move volume control slider logic to separate functions --- src/music_gui.cpp | 44 ++++++------------------ src/settings_gui.cpp | 1 + src/widgets/CMakeLists.txt | 2 ++ src/widgets/slider.cpp | 70 ++++++++++++++++++++++++++++++++++++++ src/widgets/slider_func.h | 21 ++++++++++++ 5 files changed, 104 insertions(+), 34 deletions(-) create mode 100644 src/widgets/slider.cpp create mode 100644 src/widgets/slider_func.h diff --git a/src/music_gui.cpp b/src/music_gui.cpp index 7e2aaac1d6..469066706e 100644 --- a/src/music_gui.cpp +++ b/src/music_gui.cpp @@ -26,6 +26,7 @@ #include "settings_gui.h" #include "widgets/dropdown_func.h" #include "widgets/dropdown_type.h" +#include "widgets/slider_func.h" #include "widgets/music_widget.h" @@ -643,8 +644,6 @@ static void ShowMusicTrackSelection() } struct MusicWindow : public Window { - static const int slider_width = 3; - MusicWindow(WindowDesc *desc, WindowNumber number) : Window(desc) { this->InitNested(number); @@ -740,27 +739,13 @@ struct MusicWindow : public Window { break; } - case WID_M_MUSIC_VOL: case WID_M_EFFECT_VOL: { - /* Draw a wedge indicating low to high volume level. */ - const int ha = (r.bottom - r.top) / 5; - int wx1 = r.left, wx2 = r.right; - if (_current_text_dir == TD_RTL) std::swap(wx1, wx2); - const uint shadow = _colour_gradient[COLOUR_GREY][3]; - const uint fill = _colour_gradient[COLOUR_GREY][6]; - const uint light = _colour_gradient[COLOUR_GREY][7]; - const std::vector wedge{ Point{wx1, r.bottom - ha}, Point{wx2, r.top + ha}, Point{wx2, r.bottom - ha} }; - GfxFillPolygon(wedge, fill); - GfxDrawLine(wedge[0].x, wedge[0].y, wedge[2].x, wedge[2].y, light); - GfxDrawLine(wedge[1].x, wedge[1].y, wedge[2].x, wedge[2].y, _current_text_dir == TD_RTL ? shadow : light); - GfxDrawLine(wedge[0].x, wedge[0].y, wedge[1].x, wedge[1].y, shadow); - /* Draw a slider handle indicating current volume level. */ - const int sw = ScaleGUITrad(slider_width); - byte volume = (widget == WID_M_MUSIC_VOL) ? _settings_client.music.music_vol : _settings_client.music.effect_vol; - if (_current_text_dir == TD_RTL) volume = 127 - volume; - const int x = r.left + (volume * (r.right - r.left - sw) / 127); - DrawFrameRect(x, r.top, x + sw, r.bottom, COLOUR_GREY, FR_NONE); + case WID_M_MUSIC_VOL: + DrawVolumeSliderWidget(r, _settings_client.music.music_vol); + break; + + case WID_M_EFFECT_VOL: + DrawVolumeSliderWidget(r, _settings_client.music.effect_vol); break; - } } } @@ -801,18 +786,9 @@ struct MusicWindow : public Window { break; case WID_M_MUSIC_VOL: case WID_M_EFFECT_VOL: { // volume sliders - int x = pt.x - this->GetWidget(widget)->pos_x; - - byte *vol = (widget == WID_M_MUSIC_VOL) ? &_settings_client.music.music_vol : &_settings_client.music.effect_vol; - - byte new_vol = Clamp(x * 127 / (int)this->GetWidget(widget)->current_x, 0, 127); - if (_current_text_dir == TD_RTL) new_vol = 127 - new_vol; - /* Clamp to make sure min and max are properly settable */ - if (new_vol > 124) new_vol = 127; - if (new_vol < 3) new_vol = 0; - if (new_vol != *vol) { - *vol = new_vol; - if (widget == WID_M_MUSIC_VOL) MusicDriver::GetInstance()->SetVolume(new_vol); + byte &vol = (widget == WID_M_MUSIC_VOL) ? _settings_client.music.music_vol : _settings_client.music.effect_vol; + if (ClickVolumeSliderWidget(this->GetWidget(widget)->GetCurrentRect(), pt, vol)) { + if (widget == WID_M_MUSIC_VOL) MusicDriver::GetInstance()->SetVolume(vol); this->SetDirty(); } diff --git a/src/settings_gui.cpp b/src/settings_gui.cpp index 68587c46a7..513a3ef8f3 100644 --- a/src/settings_gui.cpp +++ b/src/settings_gui.cpp @@ -21,6 +21,7 @@ #include "string_func.h" #include "widgets/dropdown_type.h" #include "widgets/dropdown_func.h" +#include "widgets/slider_func.h" #include "highscore.h" #include "base_media_base.h" #include "company_base.h" diff --git a/src/widgets/CMakeLists.txt b/src/widgets/CMakeLists.txt index 18ecd529e8..5586870a3a 100644 --- a/src/widgets/CMakeLists.txt +++ b/src/widgets/CMakeLists.txt @@ -44,6 +44,8 @@ add_files( screenshot_widget.h settings_widget.h sign_widget.h + slider.cpp + slider_func.h smallmap_widget.h station_widget.h statusbar_widget.h diff --git a/src/widgets/slider.cpp b/src/widgets/slider.cpp new file mode 100644 index 0000000000..6d6d732884 --- /dev/null +++ b/src/widgets/slider.cpp @@ -0,0 +1,70 @@ +/* + * This file is part of OpenTTD. + * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. + * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see . + */ + +/** @file slider.cpp Implementation of the horizontal slider widget. */ + +#include "../stdafx.h" +#include "../window_gui.h" +#include "../window_func.h" +#include "../strings_func.h" +#include "../zoom_func.h" +#include "slider_func.h" + +#include "../safeguards.h" + + +/** + * Draw a volume slider widget with know at given value + * @param r Rectangle to draw the widget in + * @param value Value to put the slider at + */ +void DrawVolumeSliderWidget(Rect r, byte value) +{ + static const int slider_width = 3; + + /* Draw a wedge indicating low to high volume level. */ + const int ha = (r.bottom - r.top) / 5; + int wx1 = r.left, wx2 = r.right; + if (_current_text_dir == TD_RTL) std::swap(wx1, wx2); + const uint shadow = _colour_gradient[COLOUR_GREY][3]; + const uint fill = _colour_gradient[COLOUR_GREY][6]; + const uint light = _colour_gradient[COLOUR_GREY][7]; + const std::vector wedge{ Point{wx1, r.bottom - ha}, Point{wx2, r.top + ha}, Point{wx2, r.bottom - ha} }; + GfxFillPolygon(wedge, fill); + GfxDrawLine(wedge[0].x, wedge[0].y, wedge[2].x, wedge[2].y, light); + GfxDrawLine(wedge[1].x, wedge[1].y, wedge[2].x, wedge[2].y, _current_text_dir == TD_RTL ? shadow : light); + GfxDrawLine(wedge[0].x, wedge[0].y, wedge[1].x, wedge[1].y, shadow); + + /* Draw a slider handle indicating current volume level. */ + const int sw = ScaleGUITrad(slider_width); + if (_current_text_dir == TD_RTL) value = 127 - value; + const int x = r.left + (value * (r.right - r.left - sw) / 127); + DrawFrameRect(x, r.top, x + sw, r.bottom, COLOUR_GREY, FR_NONE); +} + +/** + * Handle click on a volume slider widget to change the value + * @param r Rectangle of the widget + * @param pt Clicked point + * @param value[in,out] Volume value to modify + * @return True if the volume setting was modified + */ +bool ClickVolumeSliderWidget(Rect r, Point pt, byte &value) +{ + byte new_vol = Clamp((pt.x - r.left) * 127 / (r.right - r.left), 0, 127); + if (_current_text_dir == TD_RTL) new_vol = 127 - new_vol; + + /* Clamp to make sure min and max are properly settable */ + if (new_vol > 124) new_vol = 127; + if (new_vol < 3) new_vol = 0; + if (new_vol != value) { + value = new_vol; + return true; + } + + return false; +} diff --git a/src/widgets/slider_func.h b/src/widgets/slider_func.h new file mode 100644 index 0000000000..1aa1fa10c6 --- /dev/null +++ b/src/widgets/slider_func.h @@ -0,0 +1,21 @@ +/* + * This file is part of OpenTTD. + * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. + * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see . + */ + +/** @file slider_type.h Types related to the horizontal slider widget. */ + +#ifndef WIDGETS_SLIDER_TYPE_H +#define WIDGETS_SLIDER_TYPE_H + +#include "../window_type.h" +#include "../gfx_func.h" + + +void DrawVolumeSliderWidget(Rect r, byte value); +bool ClickVolumeSliderWidget(Rect r, Point pt, byte &value); + + +#endif /* WIDGETS_SLIDER_TYPE_H */ From 785e42a6f9d5d36b2bb98d2b941839ffc6de1fc5 Mon Sep 17 00:00:00 2001 From: Niels Martin Hansen Date: Sun, 4 Apr 2021 11:22:13 +0200 Subject: [PATCH 035/268] Feature: Volume sliders in Game Options window --- src/music_gui.cpp | 1 + src/settings_gui.cpp | 40 ++++++++++++++++++++++++++++++++--- src/widgets/settings_widget.h | 2 ++ 3 files changed, 40 insertions(+), 3 deletions(-) diff --git a/src/music_gui.cpp b/src/music_gui.cpp index 469066706e..28cb6ce4e1 100644 --- a/src/music_gui.cpp +++ b/src/music_gui.cpp @@ -790,6 +790,7 @@ struct MusicWindow : public Window { if (ClickVolumeSliderWidget(this->GetWidget(widget)->GetCurrentRect(), pt, vol)) { if (widget == WID_M_MUSIC_VOL) MusicDriver::GetInstance()->SetVolume(vol); this->SetDirty(); + SetWindowClassesDirty(WC_GAME_OPTIONS); } if (click_count > 0) this->mouse_capture_widget = widget; diff --git a/src/settings_gui.cpp b/src/settings_gui.cpp index 513a3ef8f3..6788d1ae39 100644 --- a/src/settings_gui.cpp +++ b/src/settings_gui.cpp @@ -37,6 +37,7 @@ #include "fontcache.h" #include "zoom_func.h" #include "video/video_driver.hpp" +#include "music/music_driver.hpp" #include #include @@ -336,6 +337,14 @@ struct GameOptionsWindow : Window { SetDParamStr(0, BaseMusic::GetUsedSet()->GetDescription(GetCurrentLanguageIsoCode())); DrawStringMultiLine(r.left, r.right, r.top, UINT16_MAX, STR_BLACK_RAW_STRING); break; + + case WID_GO_BASE_SFX_VOLUME: + DrawVolumeSliderWidget(r, _settings_client.music.effect_vol); + break; + + case WID_GO_BASE_MUSIC_VOLUME: + DrawVolumeSliderWidget(r, _settings_client.music.music_vol); + break; } } @@ -388,6 +397,16 @@ struct GameOptionsWindow : Window { } break; + case WID_GO_BASE_SFX_VOLUME: + case WID_GO_BASE_MUSIC_VOLUME: + size->width = ScaleGUITrad(67); + size->height = ScaleGUITrad(12); + resize->width = 0; + resize->height = 0; + fill->width = 0; + fill->height = 0; + break; + default: { int selected; DropDownList list = this->BuildDropDownList(widget, &selected); @@ -442,6 +461,19 @@ struct GameOptionsWindow : Window { this->SetDirty(); break; + case WID_GO_BASE_SFX_VOLUME: + case WID_GO_BASE_MUSIC_VOLUME: { + byte &vol = (widget == WID_GO_BASE_MUSIC_VOLUME) ? _settings_client.music.music_vol : _settings_client.music.effect_vol; + if (ClickVolumeSliderWidget(this->GetWidget(widget)->GetCurrentRect(), pt, vol)) { + if (widget == WID_GO_BASE_MUSIC_VOLUME) MusicDriver::GetInstance()->SetVolume(vol); + this->SetDirty(); + SetWindowClassesDirty(WC_MUSIC_WINDOW); + } + + if (click_count > 0) this->mouse_capture_widget = widget; + break; + } + default: { int selected; DropDownList list = this->BuildDropDownList(widget, &selected); @@ -646,9 +678,10 @@ static const NWidgetPart _nested_game_options_widgets[] = { EndContainer(), NWidget(WWT_FRAME, COLOUR_GREY), SetDataTip(STR_GAME_OPTIONS_BASE_SFX, STR_NULL), SetPadding(0, 10, 0, 10), - NWidget(NWID_HORIZONTAL), SetPIP(0, 30, 0), + NWidget(NWID_HORIZONTAL), SetPIP(0, 30, 7), NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_GO_BASE_SFX_DROPDOWN), SetMinimalSize(150, 12), SetDataTip(STR_BLACK_RAW_STRING, STR_GAME_OPTIONS_BASE_SFX_TOOLTIP), - NWidget(NWID_SPACER), SetFill(1, 0), + NWidget(NWID_SPACER), SetMinimalSize(150, 12), SetFill(1, 0), + NWidget(WWT_EMPTY, COLOUR_GREY, WID_GO_BASE_SFX_VOLUME), SetMinimalSize(67, 12), SetMinimalTextLines(1, 0), SetDataTip(0x0, STR_MUSIC_TOOLTIP_DRAG_SLIDERS_TO_SET_MUSIC), EndContainer(), NWidget(WWT_TEXT, COLOUR_GREY, WID_GO_BASE_SFX_DESCRIPTION), SetMinimalSize(330, 0), SetDataTip(STR_EMPTY, STR_GAME_OPTIONS_BASE_SFX_DESCRIPTION_TOOLTIP), SetFill(1, 0), SetPadding(6, 0, 6, 0), NWidget(NWID_HORIZONTAL, NC_EQUALSIZE), SetPIP(7, 0, 7), @@ -659,9 +692,10 @@ static const NWidgetPart _nested_game_options_widgets[] = { EndContainer(), NWidget(WWT_FRAME, COLOUR_GREY), SetDataTip(STR_GAME_OPTIONS_BASE_MUSIC, STR_NULL), SetPadding(0, 10, 0, 10), - NWidget(NWID_HORIZONTAL), SetPIP(0, 30, 0), + NWidget(NWID_HORIZONTAL), SetPIP(0, 30, 7), NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_GO_BASE_MUSIC_DROPDOWN), SetMinimalSize(150, 12), SetDataTip(STR_BLACK_RAW_STRING, STR_GAME_OPTIONS_BASE_MUSIC_TOOLTIP), NWidget(WWT_TEXT, COLOUR_GREY, WID_GO_BASE_MUSIC_STATUS), SetMinimalSize(150, 12), SetDataTip(STR_EMPTY, STR_NULL), SetFill(1, 0), + NWidget(WWT_EMPTY, COLOUR_GREY, WID_GO_BASE_MUSIC_VOLUME), SetMinimalSize(67, 12), SetMinimalTextLines(1, 0), SetDataTip(0x0, STR_MUSIC_TOOLTIP_DRAG_SLIDERS_TO_SET_MUSIC), EndContainer(), NWidget(WWT_TEXT, COLOUR_GREY, WID_GO_BASE_MUSIC_DESCRIPTION), SetMinimalSize(330, 0), SetDataTip(STR_EMPTY, STR_GAME_OPTIONS_BASE_MUSIC_DESCRIPTION_TOOLTIP), SetFill(1, 0), SetPadding(6, 0, 6, 0), NWidget(NWID_HORIZONTAL, NC_EQUALSIZE), SetPIP(7, 0, 7), diff --git a/src/widgets/settings_widget.h b/src/widgets/settings_widget.h index 6cb2d7e949..ae9c22c1a8 100644 --- a/src/widgets/settings_widget.h +++ b/src/widgets/settings_widget.h @@ -25,9 +25,11 @@ enum GameOptionsWidgets { WID_GO_BASE_GRF_TEXTFILE, ///< Open base GRF readme, changelog (+1) or license (+2). WID_GO_BASE_GRF_DESCRIPTION = WID_GO_BASE_GRF_TEXTFILE + TFT_END, ///< Description of selected base GRF. WID_GO_BASE_SFX_DROPDOWN, ///< Use to select a base SFX. + WID_GO_BASE_SFX_VOLUME, ///< Change sound effects volume. WID_GO_BASE_SFX_TEXTFILE, ///< Open base SFX readme, changelog (+1) or license (+2). WID_GO_BASE_SFX_DESCRIPTION = WID_GO_BASE_SFX_TEXTFILE + TFT_END, ///< Description of selected base SFX. WID_GO_BASE_MUSIC_DROPDOWN, ///< Use to select a base music set. + WID_GO_BASE_MUSIC_VOLUME, ///< Change music volume. WID_GO_BASE_MUSIC_STATUS, ///< Info about corrupted files etc. WID_GO_BASE_MUSIC_TEXTFILE, ///< Open base music readme, changelog (+1) or license (+2). WID_GO_BASE_MUSIC_DESCRIPTION = WID_GO_BASE_MUSIC_TEXTFILE + TFT_END, ///< Description of selected base music set. From 96d33ab46a9244f8302a33e827c1b4ea1f3909e5 Mon Sep 17 00:00:00 2001 From: Michael Lutz Date: Thu, 8 Apr 2021 23:16:43 +0200 Subject: [PATCH 036/268] Fix #8930: [Win32] Don't handle printable keys on keydown if an edit box is in focus. Handle printable input only when the matching WM_CHAR message is incoming. Without an edit box, do the handling in keydown as usual to support hotkeys. --- src/video/win32_v.cpp | 37 +++++++++++++++++-------------------- 1 file changed, 17 insertions(+), 20 deletions(-) diff --git a/src/video/win32_v.cpp b/src/video/win32_v.cpp index 1b3476dab9..b4d3946ea0 100644 --- a/src/video/win32_v.cpp +++ b/src/video/win32_v.cpp @@ -552,14 +552,6 @@ LRESULT CALLBACK WndProcGdi(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) uint scancode = GB(lParam, 16, 8); keycode = scancode == 41 ? (uint)WKC_BACKQUOTE : MapWindowsKey(wParam); - /* Silently drop all messages handled by WM_CHAR. */ - MSG msg; - if (PeekMessage(&msg, nullptr, 0, 0, PM_NOREMOVE)) { - if ((msg.message == WM_CHAR || msg.message == WM_DEADCHAR) && GB(lParam, 16, 8) == GB(msg.lParam, 16, 8)) { - return 0; - } - } - uint charcode = MapVirtualKey(wParam, MAPVK_VK_TO_CHAR); /* No character translation? */ @@ -568,21 +560,26 @@ LRESULT CALLBACK WndProcGdi(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) return 0; } - /* Is the console key a dead key? If yes, ignore the first key down event. */ - if (HasBit(charcode, 31) && !console) { - if (scancode == 41) { - console = true; - return 0; + /* If an edit box is in focus, wait for the corresponding WM_CHAR message. */ + if (!EditBoxInGlobalFocus()) { + /* Is the console key a dead key? If yes, ignore the first key down event. */ + if (HasBit(charcode, 31) && !console) { + if (scancode == 41) { + console = true; + return 0; + } } + console = false; + + /* IMEs and other input methods sometimes send a WM_CHAR without a WM_KEYDOWN, + * clear the keycode so a previous WM_KEYDOWN doesn't become 'stuck'. */ + uint cur_keycode = keycode; + keycode = 0; + + return HandleCharMsg(cur_keycode, LOWORD(charcode)); } - console = false; - /* IMEs and other input methods sometimes send a WM_CHAR without a WM_KEYDOWN, - * clear the keycode so a previous WM_KEYDOWN doesn't become 'stuck'. */ - uint cur_keycode = keycode; - keycode = 0; - - return HandleCharMsg(cur_keycode, LOWORD(charcode)); + return 0; } case WM_SYSKEYDOWN: // user presses F10 or Alt, both activating the title-menu From e98aed8b485caa63145ae1d8f48b177da3b54006 Mon Sep 17 00:00:00 2001 From: translators Date: Fri, 9 Apr 2021 17:46:40 +0000 Subject: [PATCH 037/268] Update: Translations from eints swedish: 3 changes by DonaldDuck313 korean: 2 changes by telk5093 portuguese (brazilian): 4 changes by brunodelara-cloudcrm --- src/lang/brazilian_portuguese.txt | 5 ++++- src/lang/korean.txt | 4 ++-- src/lang/swedish.txt | 3 +++ 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/lang/brazilian_portuguese.txt b/src/lang/brazilian_portuguese.txt index e13a883070..09fd14b100 100644 --- a/src/lang/brazilian_portuguese.txt +++ b/src/lang/brazilian_portuguese.txt @@ -1139,6 +1139,7 @@ STR_CONFIG_SETTING_TREE_CAPTION :{WHITE}Configur STR_CONFIG_SETTING_FILTER_TITLE :{BLACK}Filtro: STR_CONFIG_SETTING_EXPAND_ALL :{BLACK}Maximizar tudo STR_CONFIG_SETTING_COLLAPSE_ALL :{BLACK}Minimizar tudo +STR_CONFIG_SETTING_RESET_ALL :{BLACK}Redefinir todos os parâmetros STR_CONFIG_SETTING_NO_EXPLANATION_AVAILABLE_HELPTEXT :(não há explicação disponível) STR_CONFIG_SETTING_DEFAULT_VALUE :{LTBLUE}Valor padrão: {ORANGE}{STRING} STR_CONFIG_SETTING_TYPE :{LTBLUE}Tipo de config.: {ORANGE}{STRING} @@ -1147,6 +1148,8 @@ STR_CONFIG_SETTING_TYPE_GAME_MENU :Config. do jogo STR_CONFIG_SETTING_TYPE_GAME_INGAME :Config. do jogo (guardado no savegame; afeta apenas jogo atual) STR_CONFIG_SETTING_TYPE_COMPANY_MENU :Config. da companhia (guardado no savegame; afeta apenas novos jogos) STR_CONFIG_SETTING_TYPE_COMPANY_INGAME :Config. da companhia (guardado no savegame; afeta apenas a comp. atual) +STR_CONFIG_SETTING_RESET_ALL_CONFIRMATION_DIALOG_CAPTION :{WHITE}Cuidado! +STR_CONFIG_SETTING_RESET_ALL_CONFIRMATION_DIALOG_TEXT :{WHITE}Essa ação irá restaurar todas as configurações para os valores padrão.{}Tem certeza que deseja continuar? STR_CONFIG_SETTING_RESTRICT_CATEGORY :{BLACK}Categoria: STR_CONFIG_SETTING_RESTRICT_TYPE :{BLACK}Tipo: @@ -2532,7 +2535,7 @@ STR_WATERWAYS_TOOLBAR_BUILD_DOCK_TOOLTIP :{BLACK}Construi STR_WATERWAYS_TOOLBAR_BUOY_TOOLTIP :{BLACK}Posicione a bóia, que pode ser usada como ponto de rota. Shift altera construção/preço estimado STR_WATERWAYS_TOOLBAR_BUILD_AQUEDUCT_TOOLTIP :{BLACK}Construir aqueduto. Shift altera construção/preço estimado STR_WATERWAYS_TOOLBAR_CREATE_LAKE_TOOLTIP :{BLACK}Define área com água.{}Faz um canal, a menos se CTRL for pressionado ao nível do mar, neste caso inundará ao redor -STR_WATERWAYS_TOOLBAR_CREATE_RIVER_TOOLTIP :{BLACK}Criar rios +STR_WATERWAYS_TOOLBAR_CREATE_RIVER_TOOLTIP :{BLACK}Criar rios. Ctrl seleciona a área na diagonal # Ship depot construction window STR_DEPOT_BUILD_SHIP_CAPTION :{WHITE}Orientação do Depósito Naval diff --git a/src/lang/korean.txt b/src/lang/korean.txt index 47ccfc9028..31149fa9c7 100644 --- a/src/lang/korean.txt +++ b/src/lang/korean.txt @@ -3093,7 +3093,7 @@ STR_NEWGRF_ERROR_LOAD_BEFORE :{1:STRING}{G 1 STR_NEWGRF_ERROR_LOAD_AFTER :{1:STRING}{G 1 "은" "는"} 반드시 {STRING} 뒤에 불러와야 합니다 STR_NEWGRF_ERROR_OTTD_VERSION_NUMBER :{1:STRING}{G 1 "은" "는"} OpenTTD {STRING} 버전이나 그 이상이 필요합니다 STR_NEWGRF_ERROR_AFTER_TRANSLATED_FILE :GRF 파일이 번역을 위해 만들어졌습니다 -STR_NEWGRF_ERROR_TOO_MANY_NEWGRFS_LOADED :NewGRF이 너무 많습니다 +STR_NEWGRF_ERROR_TOO_MANY_NEWGRFS_LOADED :NewGRF가 너무 많습니다 STR_NEWGRF_ERROR_STATIC_GRF_CAUSES_DESYNC :{2:STRING}{G 2 "을" "를"} 포함한 정적 NewGRF {1:STRING}{G 1 "을" "를"} 불러오는 것은 비동기화를 일으킬 수 있습니다 STR_NEWGRF_ERROR_UNEXPECTED_SPRITE :예기치 않은 스프라이트 (스프라이트 {3:NUM}) STR_NEWGRF_ERROR_UNKNOWN_PROPERTY :알려지지 않은 액션 0 속성 {4:HEX} (스프라이트 {3:NUM}) @@ -3127,7 +3127,7 @@ STR_NEWGRF_LIST_COMPATIBLE :{YELLOW}호환 STR_NEWGRF_LIST_MISSING :{RED}파일 없음 # NewGRF 'it's broken' warnings -STR_NEWGRF_BROKEN :{WHITE}'{0:STRING}' NewGRF이 적용되는 과정에서 비동기화나 충돌이 일어날 수 있습니다 +STR_NEWGRF_BROKEN :{WHITE}'{0:STRING}' NewGRF가 적용되는 과정에서 비동기화나 충돌이 일어날 수 있습니다 STR_NEWGRF_BROKEN_POWERED_WAGON :{WHITE}차고지 안에 있지 않은 '{1:ENGINE}'에 대한 동력 차량 상태가 바뀌었습니다 STR_NEWGRF_BROKEN_VEHICLE_LENGTH :{WHITE}'{1:ENGINE}'{G 1 "이" "가"} 차고지 안에 있지 않으면 차량 길이가 바뀝니다 STR_NEWGRF_BROKEN_CAPACITY :{WHITE}차량이 기지 안에 있지 않거나 개조가 불가능한 상태에서 '{1:ENGINE}'의 수송량이 변경되었습니다 diff --git a/src/lang/swedish.txt b/src/lang/swedish.txt index 2a7ef7de9a..4e2c8487de 100644 --- a/src/lang/swedish.txt +++ b/src/lang/swedish.txt @@ -1138,6 +1138,7 @@ STR_CONFIG_SETTING_TREE_CAPTION :{WHITE}Inställ STR_CONFIG_SETTING_FILTER_TITLE :{BLACK}Sökfilter: STR_CONFIG_SETTING_EXPAND_ALL :{BLACK}Utöka alla STR_CONFIG_SETTING_COLLAPSE_ALL :{BLACK}Stäng alla +STR_CONFIG_SETTING_RESET_ALL :{BLACK}Återställ alla värden STR_CONFIG_SETTING_NO_EXPLANATION_AVAILABLE_HELPTEXT :(ingen förklaring tillgänglig) STR_CONFIG_SETTING_DEFAULT_VALUE :{LTBLUE}Standardvärde: {ORANGE}{STRING} STR_CONFIG_SETTING_TYPE :{LTBLUE}Typ av inställning: {ORANGE}{STRING} @@ -1146,6 +1147,8 @@ STR_CONFIG_SETTING_TYPE_GAME_MENU :Spelinställnin STR_CONFIG_SETTING_TYPE_GAME_INGAME :Spelinställning (sparad i spel-fil och påverkar enbart nuvarande spel) STR_CONFIG_SETTING_TYPE_COMPANY_MENU :Företagsinställning (sparad i spel-fil och påverkar enbart nya spel) STR_CONFIG_SETTING_TYPE_COMPANY_INGAME :Företagsinställning (sparad i spel-fil och påverkar enbart nuvarande företag) +STR_CONFIG_SETTING_RESET_ALL_CONFIRMATION_DIALOG_CAPTION :{WHITE}Varning! +STR_CONFIG_SETTING_RESET_ALL_CONFIRMATION_DIALOG_TEXT :{WHITE}Detta kommer att återställa alla spelinställningar till deras standardvärden.{}Är du säker på att du vill fortsätta? STR_CONFIG_SETTING_RESTRICT_CATEGORY :{BLACK}Kategori: STR_CONFIG_SETTING_RESTRICT_TYPE :{BLACK}Typ: From ff6924f1222ade72feab10a9e9137e50fad9ea89 Mon Sep 17 00:00:00 2001 From: Patric Stout Date: Sat, 10 Apr 2021 11:14:55 +0200 Subject: [PATCH 038/268] Fix 70bc55cfd6e: snow line height was set while calculating desert line (#8989) Seems I liked copy/pasting just a tiny bit too much. --- src/landscape.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/landscape.cpp b/src/landscape.cpp index 9c524bf6c1..6ea3af75b6 100644 --- a/src/landscape.cpp +++ b/src/landscape.cpp @@ -1400,7 +1400,7 @@ static void CalculateSnowLine() static uint8 CalculateDesertLine() { /* CalculateCoverageLine() runs from top to bottom, so we need to invert the coverage. */ - return _settings_game.game_creation.snow_line_height = CalculateCoverageLine(100 - _settings_game.game_creation.desert_coverage, 4); + return CalculateCoverageLine(100 - _settings_game.game_creation.desert_coverage, 4); } void GenerateLandscape(byte mode) From c64b0946e882ca6ff199834853696e3e0c239b04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Guilloux?= Date: Sat, 10 Apr 2021 11:15:47 +0200 Subject: [PATCH 039/268] Fix e0561dbde: [MinGW] use ofstring(wchar_t*) as ofstring(wstring) doesn't exist (#8985) --- src/ini.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ini.cpp b/src/ini.cpp index a5045b7151..017f5f9030 100644 --- a/src/ini.cpp +++ b/src/ini.cpp @@ -53,7 +53,7 @@ bool IniFile::SaveToDisk(const std::string &filename) std::string file_new{ filename }; file_new.append(".new"); - std::ofstream os(OTTD2FS(file_new)); + std::ofstream os(OTTD2FS(file_new).c_str()); if (os.fail()) return false; for (const IniGroup *group = this->group; group != nullptr; group = group->next) { From 0cb99c55233e8e29ba33ae49002f9453d55d600c Mon Sep 17 00:00:00 2001 From: Didac Perez Parera Date: Sat, 10 Apr 2021 02:19:14 -0700 Subject: [PATCH 040/268] Codechange: nullptr deletion in DeleteWindowById (#8941) --- src/window.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/window.cpp b/src/window.cpp index e164d0d4a6..2216fb2418 100644 --- a/src/window.cpp +++ b/src/window.cpp @@ -1165,8 +1165,7 @@ Window *FindWindowByClass(WindowClass cls) void DeleteWindowById(WindowClass cls, WindowNumber number, bool force) { Window *w = FindWindowById(cls, number); - if (force || w == nullptr || - (w->flags & WF_STICKY) == 0) { + if (w != nullptr && (force || (w->flags & WF_STICKY) == 0)) { delete w; } } From c800dcaff8b72a1e9997f39b2fd701fa6378799a Mon Sep 17 00:00:00 2001 From: Niels Martin Hansen Date: Sat, 10 Apr 2021 11:17:53 +0200 Subject: [PATCH 041/268] Fix: [Win32] Font glyphs of certain widths broke Font glyphs between 33 and 39 pixels wide, in the Win32 font system, used wrong alignment and caused glyphs to appear broken. When in the 33 to 39 pixel range, glyphs without AA were rounded down to 32 pixel pitch, instead of up to 64 pixel pitch. --- src/os/windows/font_win32.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/os/windows/font_win32.cpp b/src/os/windows/font_win32.cpp index 59d5e0ad85..55d4193e89 100644 --- a/src/os/windows/font_win32.cpp +++ b/src/os/windows/font_win32.cpp @@ -498,7 +498,7 @@ void Win32FontCache::ClearFontCache() * For anti-aliased rendering, GDI uses the strange value range of 0 to 64, * inclusively. To map this to 0 to 255, we shift left by two and then * subtract one. */ - uint pitch = Align(aa ? gm.gmBlackBoxX : std::max(gm.gmBlackBoxX / 8u, 1u), 4); + uint pitch = Align(aa ? gm.gmBlackBoxX : std::max((gm.gmBlackBoxX + 7u) / 8u, 1u), 4); /* Draw shadow for medium size. */ if (this->fs == FS_NORMAL && !aa) { From 8a944c88c9b10642641f2f44ae2937cf36b481a4 Mon Sep 17 00:00:00 2001 From: Patric Stout Date: Sat, 10 Apr 2021 11:47:17 +0200 Subject: [PATCH 042/268] Change: [DorpsGek] also announce Discussion creation/comments from GitHub (#8991) --- .dorpsgek.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.dorpsgek.yml b/.dorpsgek.yml index 363d8b0b42..7ce84af72f 100644 --- a/.dorpsgek.yml +++ b/.dorpsgek.yml @@ -10,6 +10,7 @@ notifications: only-by: - DorpsGek commit-comment: + discussion: pull-request: issue: tag-created: From 47c04033200c58671f31b1df0ad474f37bfb1864 Mon Sep 17 00:00:00 2001 From: Didac Perez Parera Date: Sat, 10 Apr 2021 03:55:49 -0700 Subject: [PATCH 043/268] Change: do not disable NewGRF window apply button if dev tools are enabled (#8975) enabled --- src/newgrf_gui.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/newgrf_gui.cpp b/src/newgrf_gui.cpp index ad37b35425..50e402ffb9 100644 --- a/src/newgrf_gui.cpp +++ b/src/newgrf_gui.cpp @@ -1255,7 +1255,7 @@ struct NewGRFWindow : public Window, NewGRFScanCallback { this->BuildAvailables(); - this->SetWidgetDisabledState(WID_NS_APPLY_CHANGES, !this->editable || !this->modified); + this->SetWidgetDisabledState(WID_NS_APPLY_CHANGES, !((this->editable && this->modified) || _settings_client.gui.newgrf_developer_tools)); this->SetWidgetsDisabledState(!this->editable, WID_NS_PRESET_LIST, WID_NS_TOGGLE_PALETTE, From c50626319765e85efeb2e327cb401c807d5a26f3 Mon Sep 17 00:00:00 2001 From: Charles Pigott Date: Sat, 10 Apr 2021 13:44:17 +0100 Subject: [PATCH 044/268] Fix #8956: Industry disaster news messages showed the wrong location (#8992) --- src/disaster_vehicle.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/disaster_vehicle.cpp b/src/disaster_vehicle.cpp index c5d76877cc..cc245b38fe 100644 --- a/src/disaster_vehicle.cpp +++ b/src/disaster_vehicle.cpp @@ -456,7 +456,7 @@ static bool DisasterTick_Aircraft(DisasterVehicle *v, uint16 image_override, boo DestructIndustry(i); SetDParam(0, i->town->index); - AddTileNewsItem(news_message, NT_ACCIDENT, v->dest_tile); + AddIndustryNewsItem(news_message, NT_ACCIDENT, i->index); if (_settings_client.sound.disaster) SndPlayTileFx(SND_12_EXPLOSION, i->location.tile); } } else if (v->current_order.GetDestination() == 0) { From 5a14bf3a6ccf08c26b63b4f68d1cdae66f732770 Mon Sep 17 00:00:00 2001 From: translators Date: Sat, 10 Apr 2021 15:24:45 +0000 Subject: [PATCH 045/268] Update: Translations from eints korean: 1 change by telk5093 spanish: 1 change by MontyMontana --- src/lang/korean.txt | 2 +- src/lang/spanish.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lang/korean.txt b/src/lang/korean.txt index 31149fa9c7..c5e44fa35a 100644 --- a/src/lang/korean.txt +++ b/src/lang/korean.txt @@ -954,7 +954,7 @@ STR_GAME_OPTIONS_CURRENCY_MYR :말레이시아 STR_GAME_OPTIONS_ROAD_VEHICLES_DROPDOWN_LEFT :좌측통행 STR_GAME_OPTIONS_ROAD_VEHICLES_DROPDOWN_RIGHT :우측통행 -STR_GAME_OPTIONS_TOWN_NAMES_FRAME :{BLACK}도시 이름 +STR_GAME_OPTIONS_TOWN_NAMES_FRAME :{BLACK}도시 이름: STR_GAME_OPTIONS_TOWN_NAMES_DROPDOWN_TOOLTIP :{BLACK}도시 이름 스타일을 선택하세요 ############ start of townname region diff --git a/src/lang/spanish.txt b/src/lang/spanish.txt index b7dc758c3c..855546f875 100644 --- a/src/lang/spanish.txt +++ b/src/lang/spanish.txt @@ -119,7 +119,7 @@ STR_QUANTITY_SUGAR :{WEIGHT_LONG} d STR_QUANTITY_TOYS :{COMMA}{NBSP}juguete{P "" s} STR_QUANTITY_SWEETS :{COMMA}{NBSP}bolsa{P "" s} de caramelos STR_QUANTITY_COLA :{VOLUME_LONG} de cola -STR_QUANTITY_CANDYFLOSS :{WEIGHT_LONG} de algodón dulce +STR_QUANTITY_CANDYFLOSS :{WEIGHT_LONG} de algodón de azúcar STR_QUANTITY_BUBBLES :{COMMA} burbuja{P "" s} STR_QUANTITY_TOFFEE :{WEIGHT_LONG} de tofe{P "" s} STR_QUANTITY_BATTERIES :{COMMA} pila{P "" s} From d4c3d01d07980708e86c3532422b4bf437aabb64 Mon Sep 17 00:00:00 2001 From: Nicolae Crefelean Date: Sat, 10 Apr 2021 17:33:27 +0200 Subject: [PATCH 046/268] Add: new plural form for Romanian translation (#8936) --- src/lang/romanian.txt | 2 +- src/strings.cpp | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/lang/romanian.txt b/src/lang/romanian.txt index 889268e22e..42ec4d24b3 100644 --- a/src/lang/romanian.txt +++ b/src/lang/romanian.txt @@ -1,7 +1,7 @@ ##name Romanian ##ownname Românӑ ##isocode ro_RO -##plural 0 +##plural 14 ##textdir ltr ##digitsep . ##digitsepcur . diff --git a/src/strings.cpp b/src/strings.cpp index 7cbdc3cb09..1f288d308e 100644 --- a/src/strings.cpp +++ b/src/strings.cpp @@ -628,6 +628,12 @@ static int DeterminePluralForm(int64 count, int plural_form) * Scottish Gaelic */ case 13: return ((n == 1 || n == 11) ? 0 : (n == 2 || n == 12) ? 1 : ((n > 2 && n < 11) || (n > 12 && n < 20)) ? 2 : 3); + + /* Three forms: special cases for 1, 0 and numbers ending in 01 to 19. + * Used in: + * Romanian */ + case 14: + return n == 1 ? 0 : (n == 0 || (n % 100 > 0 && n % 100 < 20)) ? 1 : 2; } } From 0e01a7a43122a17d8537c455e6a068f75f61b809 Mon Sep 17 00:00:00 2001 From: translators Date: Sat, 10 Apr 2021 15:48:34 +0000 Subject: [PATCH 047/268] Update: Translations from eints --- src/lang/romanian.txt | 55 ------------------------------------------- 1 file changed, 55 deletions(-) diff --git a/src/lang/romanian.txt b/src/lang/romanian.txt index 42ec4d24b3..88113eac2d 100644 --- a/src/lang/romanian.txt +++ b/src/lang/romanian.txt @@ -93,37 +93,25 @@ STR_CARGO_SINGULAR_FIZZY_DRINK :Suc acidulat # Quantity of cargo STR_QUANTITY_NOTHING : -STR_QUANTITY_PASSENGERS :{COMMA} călător{P "" i} STR_QUANTITY_COAL :{WEIGHT_LONG} de cărbune -STR_QUANTITY_MAIL :{COMMA} sac{P "" i} cu colete poștale STR_QUANTITY_OIL :{VOLUME_LONG} de petrol -STR_QUANTITY_LIVESTOCK :{COMMA} animal{P "" e} -STR_QUANTITY_GOODS :{COMMA} pachet{P "" e} de bunuri STR_QUANTITY_GRAIN :{WEIGHT_LONG} de cereale STR_QUANTITY_WOOD :{WEIGHT_LONG} de lemne STR_QUANTITY_IRON_ORE :{WEIGHT_LONG} de minereu de fier STR_QUANTITY_STEEL :{WEIGHT_LONG} de oțel -STR_QUANTITY_VALUABLES :{COMMA} cuti{P e i} de valori STR_QUANTITY_COPPER_ORE :{WEIGHT_LONG} de minereu de cupru STR_QUANTITY_MAIZE :{WEIGHT_LONG} de porumb STR_QUANTITY_FRUIT :{WEIGHT_LONG} de fructe -STR_QUANTITY_DIAMONDS :{COMMA} sac{P "" i} cu diamante STR_QUANTITY_FOOD :{WEIGHT_LONG} de alimente STR_QUANTITY_PAPER :{WEIGHT_LONG} de hârtie -STR_QUANTITY_GOLD :{COMMA} sac{P "" i} cu aur STR_QUANTITY_WATER :{VOLUME_LONG} de apă STR_QUANTITY_WHEAT :{WEIGHT_LONG} de grâu STR_QUANTITY_RUBBER :{VOLUME_LONG} de cauciuc STR_QUANTITY_SUGAR :{WEIGHT_LONG} de zahăr -STR_QUANTITY_TOYS :{COMMA} sac{P "" i} cu jucării -STR_QUANTITY_SWEETS :{COMMA} sac{P "" i} cu bomboane STR_QUANTITY_COLA :{VOLUME_LONG} de cola STR_QUANTITY_CANDYFLOSS :{WEIGHT_LONG} de vată de zahăr -STR_QUANTITY_BUBBLES :{COMMA} balonaș{P "" e} STR_QUANTITY_TOFFEE :{WEIGHT_LONG} de caramel -STR_QUANTITY_BATTERIES :{COMMA} bateri{P e i} STR_QUANTITY_PLASTIC :{VOLUME_LONG} de plastic -STR_QUANTITY_FIZZY_DRINKS :{COMMA} bido{P n ane} cu suc STR_QUANTITY_N_A :N/A # Two letter abbreviation of cargo name @@ -163,12 +151,9 @@ STR_ABBREV_NONE :{TINY_FONT}NU STR_ABBREV_ALL :{TINY_FONT}TOT # 'Mode' of transport for cargoes -STR_PASSENGERS :{COMMA} călător{P "" i} -STR_BAGS :{COMMA}{NBSP}sac{P "" i} STR_TONS :{COMMA} tone STR_LITERS :{COMMA} litri STR_ITEMS :{COMMA} bucăți -STR_CRATES :{COMMA} pachet{P "" e} # Colours, do not shuffle STR_COLOUR_DARK_BLUE :Albastru închis @@ -203,16 +188,12 @@ STR_UNITS_WEIGHT_SHORT_IMPERIAL :{COMMA}t STR_UNITS_WEIGHT_SHORT_METRIC :{COMMA}t STR_UNITS_WEIGHT_SHORT_SI :{COMMA}kg -STR_UNITS_WEIGHT_LONG_IMPERIAL :{COMMA} ton{P ă e} -STR_UNITS_WEIGHT_LONG_METRIC :{COMMA} ton{P ă e} STR_UNITS_WEIGHT_LONG_SI :{COMMA} kg STR_UNITS_VOLUME_SHORT_IMPERIAL :{COMMA}gal STR_UNITS_VOLUME_SHORT_METRIC :{COMMA}l STR_UNITS_VOLUME_SHORT_SI :{COMMA}m³ -STR_UNITS_VOLUME_LONG_IMPERIAL :{COMMA} galo{P n ane} -STR_UNITS_VOLUME_LONG_METRIC :{COMMA} litr{P u i} STR_UNITS_VOLUME_LONG_SI :{COMMA} m³ STR_UNITS_FORCE_IMPERIAL :{COMMA} lbf @@ -1009,7 +990,6 @@ STR_GAME_OPTIONS_REFRESH_RATE_TOOLTIP :{BLACK}Alegeți STR_GAME_OPTIONS_BASE_GRF :{BLACK}Set grafic de bază STR_GAME_OPTIONS_BASE_GRF_TOOLTIP :{BLACK}Selectează setul grafic de bază utilizat în joc -STR_GAME_OPTIONS_BASE_GRF_STATUS :{RED}{NUM} fişier{P "" "e"} lipsă/corupt{P "" e} STR_GAME_OPTIONS_BASE_GRF_DESCRIPTION_TOOLTIP :{BLACK}Informaţii adiţionale despre setul grafic de bază STR_GAME_OPTIONS_BASE_SFX :{BLACK}Set sunete de bază @@ -1018,7 +998,6 @@ STR_GAME_OPTIONS_BASE_SFX_DESCRIPTION_TOOLTIP :{BLACK}Informa STR_GAME_OPTIONS_BASE_MUSIC :{BLACK}Setul de muzică de bază STR_GAME_OPTIONS_BASE_MUSIC_TOOLTIP :{BLACK}Selectaţi setul de muzică de bază -STR_GAME_OPTIONS_BASE_MUSIC_STATUS :{RED}{NUM} fişier{P "" e} corupt{P "" e} STR_GAME_OPTIONS_BASE_MUSIC_DESCRIPTION_TOOLTIP :{BLACK}Informaţii adiţionale despre setul de muzică de bază STR_ERROR_RESOLUTION_LIST_FAILED :{WHITE}Nu s-a putut obține lista de rezoluții suportate @@ -1191,7 +1170,6 @@ STR_CONFIG_SETTING_EXTRADYNAMITE :Permite demolar STR_CONFIG_SETTING_EXTRADYNAMITE_HELPTEXT :Facilitează eliminarea de clădiri şi infrastructură deţinute de oraş STR_CONFIG_SETTING_TRAIN_LENGTH :Lungimea maximă a trenurilor: {STRING} STR_CONFIG_SETTING_TRAIN_LENGTH_HELPTEXT :Configurează lungimea maximă a trenurilor -STR_CONFIG_SETTING_TILE_LENGTH :{COMMA} pătrăţel{P 0 "" e} STR_CONFIG_SETTING_SMOKE_AMOUNT :Cantitatea de fum/ scântei ale vehiculului: {STRING} STR_CONFIG_SETTING_SMOKE_AMOUNT_HELPTEXT :Configurează cât de mult fum sau cât de multe scântei sunt emise de vehicule STR_CONFIG_SETTING_TRAIN_ACCELERATION_MODEL :Modelul de acceleraţie al trenurilor: {STRING} @@ -1292,16 +1270,12 @@ STR_CONFIG_SETTING_AUTORENEW_VEHICLE :Înnoire automa STR_CONFIG_SETTING_AUTORENEW_VEHICLE_HELPTEXT :După activare, orice vehicul care este învechit va fi reînnoit automat când condițiile de înlocuire automată sunt îndeplinite STR_CONFIG_SETTING_AUTORENEW_MONTHS :Autoreînnoire când vehiculul {STRING} vârsta maximă STR_CONFIG_SETTING_AUTORENEW_MONTHS_HELPTEXT :Vârsta aproximativă când un vehicul ar trebui autoreînnoit -STR_CONFIG_SETTING_AUTORENEW_MONTHS_VALUE_BEFORE :mai are {COMMA} lun{P 0 ă i} până la -STR_CONFIG_SETTING_AUTORENEW_MONTHS_VALUE_AFTER :a depășit cu {COMMA} lun{P 0 ă i} STR_CONFIG_SETTING_AUTORENEW_MONEY :Fonduri minime pentru înnoire automată: {STRING} STR_CONFIG_SETTING_AUTORENEW_MONEY_HELPTEXT :Suma minimă care trebuie să rămână disponibilă atunci când se face autoreînnoirea STR_CONFIG_SETTING_ERRMSG_DURATION :Durata de afișare a mesajelor de eroare: {STRING} STR_CONFIG_SETTING_ERRMSG_DURATION_HELPTEXT :Durata afișării mesajelor de eroare în fereastra roșie. Unele mesaje de eroare (cele critice) nu sunt închise automat după trecerea acestei perioade, și trebuie închise manual. -STR_CONFIG_SETTING_ERRMSG_DURATION_VALUE :{COMMA} secund{P 0 ă e} STR_CONFIG_SETTING_HOVER_DELAY :Afișează texte informative: {STRING} STR_CONFIG_SETTING_HOVER_DELAY_HELPTEXT :Durata dinaintea afișării sfaturilor când se ține mausul pe un element al interfeței. Alternativ, afișarea sfaturilor poate fi setată pentru clic-dreapta -STR_CONFIG_SETTING_HOVER_DELAY_VALUE :Plutește {COMMA} milisecund{P 0 ă e} STR_CONFIG_SETTING_HOVER_DELAY_DISABLED :Click dreapta STR_CONFIG_SETTING_POPULATION_IN_LABEL :Afişează populaţia unui oras lângă nume: {STRING} STR_CONFIG_SETTING_POPULATION_IN_LABEL_HELPTEXT :Afișează populația orașelor în numele afișate pe hartă @@ -1494,7 +1468,6 @@ STR_CONFIG_SETTING_SERVINT_ISPERCENT :Intervaluri de STR_CONFIG_SETTING_SERVINT_ISPERCENT_HELPTEXT :Alege dacă întreținerea vehiculelor este activată de trecerea unei anumite perioade de timp, sau scăzând un anumit procent din gradul de rezistență al vehiculului STR_CONFIG_SETTING_SERVINT_TRAINS :Intervalul de întreținere implicit al trenurilor: {STRING} STR_CONFIG_SETTING_SERVINT_TRAINS_HELPTEXT :Alege perioada de întreținere implicită pentru noi vehicule feroviare, dacă nu există un interval de întreținere stabilit pentru vehicul -STR_CONFIG_SETTING_SERVINT_VALUE :{COMMA} zi{P 0 "" le}/% STR_CONFIG_SETTING_SERVINT_DISABLED :Dezactivat STR_CONFIG_SETTING_SERVINT_ROAD_VEHICLES :Intervalul de întreținere implicit al vehiculelor rutiere: {STRING} STR_CONFIG_SETTING_SERVINT_ROAD_VEHICLES_HELPTEXT :Alege perioada de întreținere implicită pentru noi vehicule rutiere, dacă nu există un interval de întreținere stabilit pentru vehicul @@ -1559,7 +1532,6 @@ STR_CONFIG_SETTING_FEEDER_PAYMENT_SHARE :Procentul din p STR_CONFIG_SETTING_FEEDER_PAYMENT_SHARE_HELPTEXT :Procentul din câştig care este oferit legăturilor intermediare pentru alimentare, oferind mai mult control asupra încasărilor STR_CONFIG_SETTING_DRAG_SIGNALS_DENSITY :Când se trage cu mouse-ul, plasează semnale la fiecare: {STRING} STR_CONFIG_SETTING_DRAG_SIGNALS_DENSITY_HELPTEXT :Configurează distanţa la care se vor construi semnale pe şină până la următorul obstacol (semnal, intersecţie), dacâ se trage cu mouse-ul -STR_CONFIG_SETTING_DRAG_SIGNALS_DENSITY_VALUE :{COMMA} pătrăţel{P 0 "" e} STR_CONFIG_SETTING_DRAG_SIGNALS_FIXED_DISTANCE :La plasarea mai multor semale, păstrează distanţa fixă între acestea: {STRING} STR_CONFIG_SETTING_SEMAPHORE_BUILD_BEFORE_DATE :Construieşte automat semafoare înainte de: {STRING} STR_CONFIG_SETTING_SEMAPHORE_BUILD_BEFORE_DATE_HELPTEXT :Alege anul din care se vor folosi semnale electrice pe calea feroviară. Înainte de acest an, se vor folosi semnale non-electrice care au aceeasi funcționalitate dar arată diferit @@ -1609,7 +1581,6 @@ STR_CONFIG_SETTING_TOOLBAR_POS_HELPTEXT :Poziţia orizon STR_CONFIG_SETTING_STATUSBAR_POS :Poziţia barei de stare: {STRING} STR_CONFIG_SETTING_STATUSBAR_POS_HELPTEXT :Poziţia orizontală a barei principale în partea de jos a ecranului STR_CONFIG_SETTING_SNAP_RADIUS :Raza "magnetică" a ferestrelor: {STRING} -STR_CONFIG_SETTING_SNAP_RADIUS_VALUE :{COMMA} pixel{P 0 "" i} STR_CONFIG_SETTING_SNAP_RADIUS_DISABLED :Dezactivat STR_CONFIG_SETTING_SOFT_LIMIT :Numărul maxim de ferestre nefixate: {STRING} STR_CONFIG_SETTING_SOFT_LIMIT_VALUE :{COMMA} @@ -1638,9 +1609,7 @@ STR_CONFIG_SETTING_LARGER_TOWNS_DISABLED :deloc STR_CONFIG_SETTING_CITY_SIZE_MULTIPLIER :Multiplicator iniţial dimensiune oraş: {STRING} STR_CONFIG_SETTING_CITY_SIZE_MULTIPLIER_HELPTEXT :Dimensiunea medie a oraşelor mari relativ la oraşele normale, la începutul jocului -STR_CONFIG_SETTING_LINKGRAPH_INTERVAL :Actualizează graficul de distribuţie la fiecare {STRING} zi{P 0:2 "" le} STR_CONFIG_SETTING_LINKGRAPH_INTERVAL_HELPTEXT :Interval de timp între recalculările graficului de conexiuni. Fiecare recalculare calculează planurile unei componente ale graficului. Asta înseamnă că o valoare X pentru această setare nu va duce la actualizarea întregului grafic la fiecare X zile, ci doar o componentă va fi actualizată. Cu cât e mai mică valoarea, cu atât mai timp CPU va fi necesar pentru calcule. Cu cât e mai mare valoarea, cu atât va dura mai mult până va începe distribuția mărfii pe rute noi. -STR_CONFIG_SETTING_LINKGRAPH_TIME :Acordă {STRING} zi{P 0:2 "" le} pentru recalcularea graficului de distribuţie STR_CONFIG_SETTING_DISTRIBUTION_MANUAL :manual STR_CONFIG_SETTING_DISTRIBUTION_ASYMMETRIC :asimetric STR_CONFIG_SETTING_DISTRIBUTION_SYMMETRIC :simetric @@ -1792,8 +1761,6 @@ STR_INTRO_TOOLTIP_ONLINE_CONTENT :{BLACK}Verific STR_INTRO_TOOLTIP_SCRIPT_SETTINGS :{BLACK}Afişează setările pentru Inteligența Artificială şi pentru Scripturi Joc STR_INTRO_TOOLTIP_QUIT :{BLACK}Ieşi din 'OpenTTD' -STR_INTRO_BASESET :{BLACK}Setul grafic actual are lipsă {NUM} sprite{P "" s}. Verificați actualizările pentru setul de bază. -STR_INTRO_TRANSLATION :{BLACK}Acestei traduceri îi lipse{P 0 "şte" "sc"} {NUM} text{P "" e}. Te rugăm să ajuti la îmbunătățirea OpenTTD înrolându-te ca traducător. Citește fișierul readme.txt pentru detalii. # Quit window STR_QUIT_CAPTION :{WHITE}Ieşire din joc @@ -1982,13 +1949,10 @@ STR_NETWORK_START_SERVER_ADVERTISED_LABEL :{BLACK}Publicat STR_NETWORK_START_SERVER_ADVERTISED_TOOLTIP :{BLACK}Alege între un joc publicat (prin Internet) și unul privat (reț) game STR_NETWORK_START_SERVER_UNADVERTISED :Nu STR_NETWORK_START_SERVER_ADVERTISED :Da -STR_NETWORK_START_SERVER_CLIENTS_SELECT :{BLACK}{NUM} clien{P t ţi} STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS :{BLACK}Număr maxim de clienţi: STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS_TOOLTIP :{BLACK}Alege un număr maxim de clienţi. Nu trebuie ocupate toate locurile. -STR_NETWORK_START_SERVER_COMPANIES_SELECT :{BLACK}{NUM} compan{P ie ii} STR_NETWORK_START_SERVER_NUMBER_OF_COMPANIES :{BLACK}Companii maxim: STR_NETWORK_START_SERVER_NUMBER_OF_COMPANIES_TOOLTIP :{BLACK}Limitează serverul la un anumit număr de companii -STR_NETWORK_START_SERVER_SPECTATORS_SELECT :{BLACK}{NUM} spectator{P "" i} STR_NETWORK_START_SERVER_NUMBER_OF_SPECTATORS :{BLACK}Spectatori maxim: STR_NETWORK_START_SERVER_NUMBER_OF_SPECTATORS_TOOLTIP :{BLACK}Limitează serverul la un anumit număr de spectatori STR_NETWORK_START_SERVER_LANGUAGE_SPOKEN :{BLACK}Limba vorbită: @@ -2075,7 +2039,6 @@ STR_NETWORK_CONNECTING_6 :{BLACK}(6/6) Î STR_NETWORK_CONNECTING_SPECIAL_1 :{BLACK}Preluare informaţii joc... STR_NETWORK_CONNECTING_SPECIAL_2 :{BLACK}Preluare informaţii companie... ############ End of leave-in-this-order -STR_NETWORK_CONNECTING_WAITING :{BLACK}{NUM} clien{P t ţi} înaintea noastră STR_NETWORK_CONNECTING_DOWNLOADING_1 :{BLACK}{BYTES} descărcat până acum STR_NETWORK_CONNECTING_DOWNLOADING_2 :{BLACK}{BYTES} / {BYTES} descărcaţi până acum @@ -3014,7 +2977,6 @@ STR_INVALID_VEHICLE : Date: Sat, 10 Apr 2021 18:09:04 +0200 Subject: [PATCH 048/268] Fix d4c3d01d: add plural form 14 to strgen. (#8999) --- src/table/strgen_tables.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/table/strgen_tables.h b/src/table/strgen_tables.h index 1c565089ad..ecdafb3ee7 100644 --- a/src/table/strgen_tables.h +++ b/src/table/strgen_tables.h @@ -176,6 +176,7 @@ static const PluralForm _plural_forms[] = { { 2, "Two forms: cases for numbers ending with a consonant, and with a vowel.", "\"yeong,il,sam,yuk,chil,pal\" \"i,sa,o,gu\"" }, { 4, "Four forms: special cases for 1, 0 and numbers ending in 02 to 10, and numbers ending in 11 to 19.", "\"1\" \"0,2..10,102..110,202..210,...\" \"11..19,111..119,211..219,...\" \"other\"" }, { 4, "Four forms: special cases for 1 and 11, 2 and 12, 3..10 and 13..19.", "\"1,11\" \"2,12\" \"3..10,13..19\" \"other\"" }, + { 3, "Three forms: special cases for 1, 0 and numbers ending in 01 to 19.", "\"1\" \"0,2..19,101..119,201..219,...\" \"other\"" }, }; /* Flags: From fbd0a2e65a5563e8f8059aae1adb0f375f2d3348 Mon Sep 17 00:00:00 2001 From: Jonathan G Rennison Date: Mon, 5 Apr 2021 21:32:48 +0100 Subject: [PATCH 049/268] Fix: Thread unsafe use of sprite cache in OpenGLBackend::DrawMouseCursor See also: #8870 See also: #8977 --- src/video/opengl.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/video/opengl.cpp b/src/video/opengl.cpp index e74ab7b5bc..c346fd1528 100644 --- a/src/video/opengl.cpp +++ b/src/video/opengl.cpp @@ -1060,9 +1060,9 @@ void OpenGLBackend::DrawMouseCursor() /* Sprites are cached by PopulateCursorCache(). */ if (this->cursor_cache.Contains(sprite)) { - const Sprite *spr = GetSprite(sprite, ST_NORMAL); + Sprite *spr = this->cursor_cache.Get(sprite); - this->RenderOglSprite((OpenGLSprite *)this->cursor_cache.Get(sprite)->data, _cursor.sprite_seq[i].pal, + this->RenderOglSprite((OpenGLSprite *)spr->data, _cursor.sprite_seq[i].pal, _cursor.pos.x + _cursor.sprite_pos[i].x + UnScaleByZoom(spr->x_offs, ZOOM_LVL_GUI), _cursor.pos.y + _cursor.sprite_pos[i].y + UnScaleByZoom(spr->y_offs, ZOOM_LVL_GUI), ZOOM_LVL_GUI); From 39b7ef31f8754a7e9d535d3b061a2e167ccd1338 Mon Sep 17 00:00:00 2001 From: Jonathan G Rennison Date: Mon, 5 Apr 2021 23:22:55 +0100 Subject: [PATCH 050/268] Fix: Data races on cursor state in OpenGL backends --- src/video/cocoa/cocoa_ogl.mm | 2 +- src/video/opengl.cpp | 21 ++++++++++++++++----- src/video/opengl.h | 6 ++++++ src/video/sdl2_opengl_v.cpp | 2 +- src/video/win32_v.cpp | 2 +- 5 files changed, 25 insertions(+), 8 deletions(-) diff --git a/src/video/cocoa/cocoa_ogl.mm b/src/video/cocoa/cocoa_ogl.mm index 8d02428e0c..f8c2e97e0d 100644 --- a/src/video/cocoa/cocoa_ogl.mm +++ b/src/video/cocoa/cocoa_ogl.mm @@ -134,7 +134,7 @@ static bool _allowSoftware; CGLSetCurrentContext(ctx); OpenGLBackend::Get()->Paint(); - if (_cursor.in_window) OpenGLBackend::Get()->DrawMouseCursor(); + OpenGLBackend::Get()->DrawMouseCursor(); [ super drawInCGLContext:ctx pixelFormat:pf forLayerTime:t displayTime:ts ]; } diff --git a/src/video/opengl.cpp b/src/video/opengl.cpp index c346fd1528..c1c1afaaec 100644 --- a/src/video/opengl.cpp +++ b/src/video/opengl.cpp @@ -1053,18 +1053,20 @@ void OpenGLBackend::Paint() */ void OpenGLBackend::DrawMouseCursor() { + if (!this->cursor_in_window) return; + /* Draw cursor on screen */ _cur_dpi = &_screen; - for (uint i = 0; i < _cursor.sprite_count; ++i) { - SpriteID sprite = _cursor.sprite_seq[i].sprite; + for (uint i = 0; i < this->cursor_sprite_count; ++i) { + SpriteID sprite = this->cursor_sprite_seq[i].sprite; /* Sprites are cached by PopulateCursorCache(). */ if (this->cursor_cache.Contains(sprite)) { Sprite *spr = this->cursor_cache.Get(sprite); - this->RenderOglSprite((OpenGLSprite *)spr->data, _cursor.sprite_seq[i].pal, - _cursor.pos.x + _cursor.sprite_pos[i].x + UnScaleByZoom(spr->x_offs, ZOOM_LVL_GUI), - _cursor.pos.y + _cursor.sprite_pos[i].y + UnScaleByZoom(spr->y_offs, ZOOM_LVL_GUI), + this->RenderOglSprite((OpenGLSprite *)spr->data, this->cursor_sprite_seq[i].pal, + this->cursor_pos.x + this->cursor_sprite_pos[i].x + UnScaleByZoom(spr->x_offs, ZOOM_LVL_GUI), + this->cursor_pos.y + this->cursor_sprite_pos[i].y + UnScaleByZoom(spr->y_offs, ZOOM_LVL_GUI), ZOOM_LVL_GUI); } } @@ -1072,6 +1074,9 @@ void OpenGLBackend::DrawMouseCursor() void OpenGLBackend::PopulateCursorCache() { + static_assert(lengthof(_cursor.sprite_seq) == lengthof(this->cursor_sprite_seq)); + static_assert(lengthof(_cursor.sprite_pos) == lengthof(this->cursor_sprite_pos)); + if (this->clear_cursor_cache) { /* We have a pending cursor cache clear to do first. */ this->clear_cursor_cache = false; @@ -1085,7 +1090,13 @@ void OpenGLBackend::PopulateCursorCache() } } + this->cursor_pos = _cursor.pos; + this->cursor_sprite_count = _cursor.sprite_count; + this->cursor_in_window = _cursor.in_window; + for (uint i = 0; i < _cursor.sprite_count; ++i) { + this->cursor_sprite_seq[i] = _cursor.sprite_seq[i]; + this->cursor_sprite_pos[i] = _cursor.sprite_pos[i]; SpriteID sprite = _cursor.sprite_seq[i].sprite; if (!this->cursor_cache.Contains(sprite)) { diff --git a/src/video/opengl.h b/src/video/opengl.h index c17a8536d9..7e42b20bed 100644 --- a/src/video/opengl.h +++ b/src/video/opengl.h @@ -65,6 +65,12 @@ private: PaletteID last_sprite_pal = (PaletteID)-1; ///< Last uploaded remap palette. bool clear_cursor_cache = false; ///< A clear of the cursor cache is pending. + Point cursor_pos; ///< Cursor position + bool cursor_in_window; ///< Cursor inside this window + PalSpriteID cursor_sprite_seq[16]; ///< Current image of cursor + Point cursor_sprite_pos[16]; ///< Relative position of individual cursor sprites + uint cursor_sprite_count; ///< Number of cursor sprites to draw + OpenGLBackend(); ~OpenGLBackend(); diff --git a/src/video/sdl2_opengl_v.cpp b/src/video/sdl2_opengl_v.cpp index 86dc104dd2..5df2837513 100644 --- a/src/video/sdl2_opengl_v.cpp +++ b/src/video/sdl2_opengl_v.cpp @@ -174,7 +174,7 @@ void VideoDriver_SDL_OpenGL::Paint() } OpenGLBackend::Get()->Paint(); - if (_cursor.in_window) OpenGLBackend::Get()->DrawMouseCursor(); + OpenGLBackend::Get()->DrawMouseCursor(); SDL_GL_SwapWindow(this->sdl_window); } diff --git a/src/video/win32_v.cpp b/src/video/win32_v.cpp index b4d3946ea0..426d74c0aa 100644 --- a/src/video/win32_v.cpp +++ b/src/video/win32_v.cpp @@ -1454,7 +1454,7 @@ void VideoDriver_Win32OpenGL::Paint() } OpenGLBackend::Get()->Paint(); - if (_cursor.in_window) OpenGLBackend::Get()->DrawMouseCursor(); + OpenGLBackend::Get()->DrawMouseCursor(); SwapBuffers(this->dc); } From 59b6e46bcee466532e7dae3a272079ecdafa0ae1 Mon Sep 17 00:00:00 2001 From: Peter Nelson Date: Sat, 10 Apr 2021 12:40:56 +0100 Subject: [PATCH 051/268] Fix: Adjust scrolling interval of credits to account for text line height --- src/misc_gui.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/misc_gui.cpp b/src/misc_gui.cpp index 2f538f2af9..8ca94b2c3e 100644 --- a/src/misc_gui.cpp +++ b/src/misc_gui.cpp @@ -497,7 +497,7 @@ struct AboutWindow : public Window { int line_height; ///< The height of a single line static const int num_visible_lines = 19; ///< The number of lines visible simultaneously - static const uint TIMER_INTERVAL = 150; ///< Scrolling interval in ms + static const uint TIMER_INTERVAL = 2100; ///< Scrolling interval, scaled by line text line height. This value chosen to maintain parity: 2100 / FONT_HEIGHT_NORMAL = 150ms GUITimer timer; AboutWindow() : Window(&_about_desc) @@ -505,7 +505,6 @@ struct AboutWindow : public Window { this->InitNested(WN_GAME_OPTIONS_ABOUT); this->text_position = this->GetWidget(WID_A_SCROLLING_TEXT)->pos_y + this->GetWidget(WID_A_SCROLLING_TEXT)->current_y; - this->timer.SetInterval(TIMER_INTERVAL); } void SetStringParameters(int widget) const override @@ -528,6 +527,10 @@ struct AboutWindow : public Window { d.width = std::max(d.width, GetStringBoundingBox(_credits[i]).width); } *size = maxdim(*size, d); + + /* Set scroll interval based on required speed. To keep scrolling smooth, + * the interval is adjusted rather than the distance moved. */ + this->timer.SetInterval(TIMER_INTERVAL / FONT_HEIGHT_NORMAL); } void DrawWidget(const Rect &r, int widget) const override From f9460c0c8b4f9f994aadc396579590d26fbac32b Mon Sep 17 00:00:00 2001 From: Peter Nelson Date: Sat, 10 Apr 2021 18:29:21 +0100 Subject: [PATCH 052/268] Fix #8981: Don't attempt to re-reserve path if already entering/entered depot. --- src/rail_cmd.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rail_cmd.cpp b/src/rail_cmd.cpp index a5e985c022..888b98e943 100644 --- a/src/rail_cmd.cpp +++ b/src/rail_cmd.cpp @@ -1197,7 +1197,7 @@ CommandCost CmdBuildSingleSignal(TileIndex tile, DoCommandFlag flags, uint32 p1, MarkTileDirtyByTile(tile); AddTrackToSignalBuffer(tile, track, _current_company); YapfNotifyTrackLayoutChange(tile, track); - if (v != nullptr) { + if (v != nullptr && v->track != TRACK_BIT_DEPOT) { /* Extend the train's path if it's not stopped or loading, or not at a safe position. */ if (!(((v->vehstatus & VS_STOPPED) && v->cur_speed == 0) || v->current_order.IsType(OT_LOADING)) || !IsSafeWaitingPosition(v, v->tile, v->GetVehicleTrackdir(), true, _settings_game.pf.forbid_90_deg)) { From fdc8230dfa05ffe352f34a302efa40b27f338e5f Mon Sep 17 00:00:00 2001 From: TELK Date: Sun, 11 Apr 2021 19:48:51 +0900 Subject: [PATCH 053/268] Cleanup: Fix comment for only one form (#9012) --- src/strings.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/strings.cpp b/src/strings.cpp index 1f288d308e..33724158c7 100644 --- a/src/strings.cpp +++ b/src/strings.cpp @@ -534,7 +534,7 @@ static int DeterminePluralForm(int64 count, int plural_form) /* Only one form. * Used in: - * Hungarian, Japanese, Korean, Turkish */ + * Hungarian, Japanese, Turkish */ case 1: return 0; From df045b92ea45ef757dfcfded4b72028ee18ebf0d Mon Sep 17 00:00:00 2001 From: rubidium42 Date: Sun, 11 Apr 2021 13:52:55 +0200 Subject: [PATCH 054/268] Fix #9008: Validate starting year given on the command line. (#9014) An invalid starting year causes all sorts of weird behaviour and crashes in map generation. Now just set the appropriate setting via IConsoleSetSetting so the validation and, if needed, clamping is performed on the starting year value. --- src/openttd.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/openttd.cpp b/src/openttd.cpp index 235d36f01b..250aa0db38 100644 --- a/src/openttd.cpp +++ b/src/openttd.cpp @@ -455,7 +455,7 @@ struct AfterNewGRFScan : NewGRFScanCallback { /* restore saved music volume */ MusicDriver::GetInstance()->SetVolume(_settings_client.music.music_vol); - if (startyear != INVALID_YEAR) _settings_newgame.game_creation.starting_year = startyear; + 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) { From 56f982fa7f6da6134cff0d81040ebdf39b72ea2d Mon Sep 17 00:00:00 2001 From: PeterN Date: Sun, 11 Apr 2021 13:07:08 +0100 Subject: [PATCH 055/268] Fix #9015: Don't set free space value if not requested. (#9016) --- src/os/windows/win32.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/os/windows/win32.cpp b/src/os/windows/win32.cpp index 073f70b98c..ddcfd4866c 100644 --- a/src/os/windows/win32.cpp +++ b/src/os/windows/win32.cpp @@ -247,7 +247,7 @@ bool FiosGetDiskFreeSpace(const char *path, uint64 *tot) ULARGE_INTEGER bytes_free; bool retval = GetDiskFreeSpaceEx(OTTD2FS(path).c_str(), &bytes_free, nullptr, nullptr); - if (retval) *tot = bytes_free.QuadPart; + if (retval && tot != nullptr) *tot = bytes_free.QuadPart; SetErrorMode(sem); // reset previous setting return retval; From f0f20730067ef79dd87fd24b5a670ef3f38c0b88 Mon Sep 17 00:00:00 2001 From: Patric Stout Date: Sat, 10 Apr 2021 14:53:26 +0200 Subject: [PATCH 056/268] Feature: allow a toggle to enable/disable vsync Vsync should be off by default, as for most players it will be better to play without vsync. Exception exist, mainly people who play in fullscreen mode. --- src/lang/english.txt | 3 +++ src/settings_gui.cpp | 31 +++++++++++++++++++++++++++++-- src/table/misc_settings.ini | 6 ++++++ src/video/sdl2_opengl_v.cpp | 9 +++++++-- src/video/sdl2_opengl_v.h | 2 ++ src/video/video_driver.cpp | 1 + src/video/video_driver.hpp | 7 +++++++ src/video/win32_v.cpp | 17 ++++++++++------- src/video/win32_v.h | 3 ++- src/widgets/settings_widget.h | 1 + 10 files changed, 68 insertions(+), 12 deletions(-) diff --git a/src/lang/english.txt b/src/lang/english.txt index 0f4b4a99d4..9bccd86df2 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -1006,6 +1006,9 @@ STR_GAME_OPTIONS_VIDEO_ACCELERATION :{BLACK}Hardware STR_GAME_OPTIONS_VIDEO_ACCELERATION_TOOLTIP :{BLACK}Check this box to allow OpenTTD to try to use hardware acceleration. A changed setting will only be applied upon game restart STR_GAME_OPTIONS_VIDEO_ACCELERATION_RESTART :{WHITE}The setting will only take effect after a game restart +STR_GAME_OPTIONS_VIDEO_VSYNC :{BLACK}VSync +STR_GAME_OPTIONS_VIDEO_VSYNC_TOOLTIP :{BLACK}Check this box to v-sync the screen. A changed setting will only be applied upon game restart. Only works with hardware acceleration enabled + STR_GAME_OPTIONS_GUI_ZOOM_FRAME :{BLACK}Interface size STR_GAME_OPTIONS_GUI_ZOOM_DROPDOWN_TOOLTIP :{BLACK}Select the interface element size to use diff --git a/src/settings_gui.cpp b/src/settings_gui.cpp index 6788d1ae39..afc8f080ef 100644 --- a/src/settings_gui.cpp +++ b/src/settings_gui.cpp @@ -458,6 +458,19 @@ struct GameOptionsWindow : Window { _video_hw_accel = !_video_hw_accel; ShowErrorMessage(STR_GAME_OPTIONS_VIDEO_ACCELERATION_RESTART, INVALID_STRING_ID, WL_INFO); this->SetWidgetLoweredState(WID_GO_VIDEO_ACCEL_BUTTON, _video_hw_accel); +#ifndef __APPLE__ + this->SetWidgetDisabledState(WID_GO_VIDEO_VSYNC_BUTTON, !_video_hw_accel); +#endif + this->SetDirty(); + break; + + case WID_GO_VIDEO_VSYNC_BUTTON: + if (!_video_hw_accel) break; + + _video_vsync = !_video_vsync; + VideoDriver::GetInstance()->ToggleVsync(_video_vsync); + + this->SetWidgetLoweredState(WID_GO_VIDEO_VSYNC_BUTTON, _video_vsync); this->SetDirty(); break; @@ -598,6 +611,11 @@ struct GameOptionsWindow : Window { this->SetWidgetLoweredState(WID_GO_FULLSCREEN_BUTTON, _fullscreen); this->SetWidgetLoweredState(WID_GO_VIDEO_ACCEL_BUTTON, _video_hw_accel); +#ifndef __APPLE__ + this->SetWidgetLoweredState(WID_GO_VIDEO_VSYNC_BUTTON, _video_vsync); + this->SetWidgetDisabledState(WID_GO_VIDEO_VSYNC_BUTTON, !_video_hw_accel); +#endif + bool missing_files = BaseGraphics::GetUsedSet()->GetNumMissing() == 0; this->GetWidget(WID_GO_BASE_GRF_STATUS)->SetDataTip(missing_files ? STR_EMPTY : STR_GAME_OPTIONS_BASE_GRF_STATUS, STR_NULL); @@ -647,7 +665,10 @@ static const NWidgetPart _nested_game_options_widgets[] = { NWidget(WWT_TEXT, COLOUR_GREY), SetMinimalSize(0, 12), SetFill(1, 0), SetDataTip(STR_GAME_OPTIONS_RESOLUTION, STR_NULL), SetPadding(0, 0, 2, 0), NWidget(WWT_TEXT, COLOUR_GREY), SetMinimalSize(0, 12), SetFill(1, 0), SetDataTip(STR_GAME_OPTIONS_REFRESH_RATE, STR_NULL), SetPadding(0, 0, 2, 0), NWidget(WWT_TEXT, COLOUR_GREY), SetMinimalSize(0, 12), SetFill(1, 0), SetDataTip(STR_GAME_OPTIONS_FULLSCREEN, STR_NULL), SetPadding(0, 0, 2, 0), - NWidget(WWT_TEXT, COLOUR_GREY), SetMinimalSize(0, 12), SetFill(1, 0), SetDataTip(STR_GAME_OPTIONS_VIDEO_ACCELERATION, STR_NULL), + NWidget(WWT_TEXT, COLOUR_GREY), SetMinimalSize(0, 12), SetFill(1, 0), SetDataTip(STR_GAME_OPTIONS_VIDEO_ACCELERATION, STR_NULL), SetPadding(0, 0, 2, 0), +#ifndef __APPLE__ + NWidget(WWT_TEXT, COLOUR_GREY), SetMinimalSize(0, 12), SetFill(1, 0), SetDataTip(STR_GAME_OPTIONS_VIDEO_VSYNC, STR_NULL), +#endif EndContainer(), NWidget(NWID_VERTICAL), NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_GO_RESOLUTION_DROPDOWN), SetMinimalSize(100, 12), SetDataTip(STR_BLACK_STRING, STR_GAME_OPTIONS_RESOLUTION_TOOLTIP), SetFill(1, 0), SetPadding(0, 0, 2, 0), @@ -656,10 +677,16 @@ static const NWidgetPart _nested_game_options_widgets[] = { NWidget(NWID_SPACER), SetMinimalSize(1, 0), SetFill(1, 0), NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_GO_FULLSCREEN_BUTTON), SetMinimalSize(21, 9), SetDataTip(STR_EMPTY, STR_GAME_OPTIONS_FULLSCREEN_TOOLTIP), EndContainer(), - NWidget(NWID_HORIZONTAL), + NWidget(NWID_HORIZONTAL), SetPadding(0, 0, 2, 0), NWidget(NWID_SPACER), SetMinimalSize(1, 0), SetFill(1, 0), NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_GO_VIDEO_ACCEL_BUTTON), SetMinimalSize(21, 9), SetDataTip(STR_EMPTY, STR_GAME_OPTIONS_VIDEO_ACCELERATION_TOOLTIP), EndContainer(), +#ifndef __APPLE__ + NWidget(NWID_HORIZONTAL), + NWidget(NWID_SPACER), SetMinimalSize(1, 0), SetFill(1, 0), + NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_GO_VIDEO_VSYNC_BUTTON), SetMinimalSize(21, 9), SetDataTip(STR_EMPTY, STR_GAME_OPTIONS_VIDEO_VSYNC_TOOLTIP), + EndContainer(), +#endif EndContainer(), EndContainer(), EndContainer(), diff --git a/src/table/misc_settings.ini b/src/table/misc_settings.ini index 9286d1c959..49a4caa14c 100644 --- a/src/table/misc_settings.ini +++ b/src/table/misc_settings.ini @@ -77,6 +77,12 @@ var = _video_hw_accel def = true cat = SC_BASIC +[SDTG_BOOL] +name = ""video_vsync"" +var = _video_vsync +def = false +cat = SC_BASIC + [SDTG_OMANY] name = ""support8bpp"" type = SLE_UINT8 diff --git a/src/video/sdl2_opengl_v.cpp b/src/video/sdl2_opengl_v.cpp index 5df2837513..08718a01e3 100644 --- a/src/video/sdl2_opengl_v.cpp +++ b/src/video/sdl2_opengl_v.cpp @@ -70,8 +70,6 @@ const char *VideoDriver_SDL_OpenGL::Start(const StringList ¶m) SDL_GetWindowSize(this->sdl_window, &w, &h); this->ClientSizeChanged(w, h, true); - SDL_GL_SetSwapInterval(GetDriverParamBool(param, "vsync") ? 1 : 0); - return nullptr; } @@ -91,6 +89,11 @@ void VideoDriver_SDL_OpenGL::DestroyContext() } } +void VideoDriver_SDL_OpenGL::ToggleVsync(bool vsync) +{ + SDL_GL_SetSwapInterval(vsync); +} + const char *VideoDriver_SDL_OpenGL::AllocateContext() { SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8); @@ -107,6 +110,8 @@ const char *VideoDriver_SDL_OpenGL::AllocateContext() this->gl_context = SDL_GL_CreateContext(this->sdl_window); if (this->gl_context == nullptr) return "SDL2: Can't active GL context"; + ToggleVsync(_video_vsync); + return OpenGLBackend::Create(&GetOGLProcAddressCallback); } diff --git a/src/video/sdl2_opengl_v.h b/src/video/sdl2_opengl_v.h index f749b1f459..c7e647ca88 100644 --- a/src/video/sdl2_opengl_v.h +++ b/src/video/sdl2_opengl_v.h @@ -29,6 +29,8 @@ public: bool HasAnimBuffer() override { return true; } uint8 *GetAnimBuffer() override { return this->anim_buffer; } + void ToggleVsync(bool vsync) override; + const char *GetName() const override { return "sdl-opengl"; } protected: diff --git a/src/video/video_driver.cpp b/src/video/video_driver.cpp index bee67e1ea0..eaff0b7414 100644 --- a/src/video/video_driver.cpp +++ b/src/video/video_driver.cpp @@ -21,6 +21,7 @@ #include "video_driver.hpp" bool _video_hw_accel; ///< Whether to consider hardware accelerated video drivers. +bool _video_vsync; ///< Whether we should use vsync (only if _video_hw_accel is enabled). void VideoDriver::GameLoop() { diff --git a/src/video/video_driver.hpp b/src/video/video_driver.hpp index 7a859565a6..f143b61a80 100644 --- a/src/video/video_driver.hpp +++ b/src/video/video_driver.hpp @@ -28,6 +28,7 @@ extern std::vector _resolutions; extern Dimension _cur_resolution; extern bool _rightclick_emulate; extern bool _video_hw_accel; +extern bool _video_vsync; /** The base of all video drivers. */ class VideoDriver : public Driver { @@ -66,6 +67,12 @@ public: */ virtual bool ToggleFullscreen(bool fullscreen) = 0; + /** + * Change the vsync setting. + * @param vsync The new setting. + */ + virtual void ToggleVsync(bool vsync) {} + /** * Callback invoked after the blitter was changed. * @return True if no error. diff --git a/src/video/win32_v.cpp b/src/video/win32_v.cpp index 426d74c0aa..4674784618 100644 --- a/src/video/win32_v.cpp +++ b/src/video/win32_v.cpp @@ -1290,7 +1290,6 @@ const char *VideoDriver_Win32OpenGL::Start(const StringList ¶m) if (BlitterFactory::GetCurrentBlitter()->GetScreenDepth() == 0) return "Only real blitters supported"; Dimension old_res = _cur_resolution; // Save current screen resolution in case of errors, as MakeWindow invalidates it. - this->vsync = GetDriverParamBool(param, "vsync"); LoadWGLExtensions(); @@ -1335,6 +1334,15 @@ void VideoDriver_Win32OpenGL::DestroyContext() } } +void VideoDriver_Win32OpenGL::ToggleVsync(bool vsync) +{ + if (_wglSwapIntervalEXT != nullptr) { + _wglSwapIntervalEXT(vsync); + } else if (vsync) { + DEBUG(driver, 0, "OpenGL: Vsync requested, but not supported by driver"); + } +} + const char *VideoDriver_Win32OpenGL::AllocateContext() { this->dc = GetDC(this->main_wnd); @@ -1363,12 +1371,7 @@ const char *VideoDriver_Win32OpenGL::AllocateContext() } if (!wglMakeCurrent(this->dc, rc)) return "Can't active GL context"; - /* Enable/disable Vsync if supported. */ - if (_wglSwapIntervalEXT != nullptr) { - _wglSwapIntervalEXT(this->vsync ? 1 : 0); - } else if (vsync) { - DEBUG(driver, 0, "OpenGL: Vsync requested, but not supported by driver"); - } + this->ToggleVsync(_video_vsync); this->gl_rc = rc; return OpenGLBackend::Create(&GetOGLProcAddressCallback); diff --git a/src/video/win32_v.h b/src/video/win32_v.h index f6ca291f10..8c63aeedf4 100644 --- a/src/video/win32_v.h +++ b/src/video/win32_v.h @@ -138,12 +138,13 @@ public: bool HasAnimBuffer() override { return true; } uint8 *GetAnimBuffer() override { return this->anim_buffer; } + void ToggleVsync(bool vsync) override; + const char *GetName() const override { return "win32-opengl"; } protected: HDC dc; ///< Window device context. HGLRC gl_rc; ///< OpenGL context. - bool vsync; ///< Enable VSync? uint8 *anim_buffer; ///< Animation buffer from OpenGL back-end. uint8 GetFullscreenBpp() override { return 32; } // OpenGL is always 32 bpp. diff --git a/src/widgets/settings_widget.h b/src/widgets/settings_widget.h index ae9c22c1a8..c897927341 100644 --- a/src/widgets/settings_widget.h +++ b/src/widgets/settings_widget.h @@ -35,6 +35,7 @@ enum GameOptionsWidgets { WID_GO_BASE_MUSIC_DESCRIPTION = WID_GO_BASE_MUSIC_TEXTFILE + TFT_END, ///< Description of selected base music set. WID_GO_FONT_ZOOM_DROPDOWN, ///< Dropdown for the font zoom level. WID_GO_VIDEO_ACCEL_BUTTON, ///< Toggle for video acceleration. + WID_GO_VIDEO_VSYNC_BUTTON, ///< Toggle for video vsync. WID_GO_REFRESH_RATE_DROPDOWN, ///< Dropdown for all available refresh rates. }; From d50b934bb416a96cfdc517d703ef0795d4d0424b Mon Sep 17 00:00:00 2001 From: Patric Stout Date: Sat, 10 Apr 2021 15:21:11 +0200 Subject: [PATCH 057/268] Change: reworked how the Game Option display options are drawn "Hardware acceleration" was not aligned with its checkbox. So instead of drawing the labels left and the options right, now draw settings one by one with a spacer between label and option to get the right spacing. Also, use SetPIP instead of repeating a SetPadding for all but last element. --- src/settings_gui.cpp | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/src/settings_gui.cpp b/src/settings_gui.cpp index afc8f080ef..0705c3fcc7 100644 --- a/src/settings_gui.cpp +++ b/src/settings_gui.cpp @@ -661,28 +661,30 @@ static const NWidgetPart _nested_game_options_widgets[] = { NWidget(WWT_FRAME, COLOUR_GREY), SetDataTip(STR_GAME_OPTIONS_GRAPHICS, STR_NULL), SetPadding(0, 10, 0, 10), NWidget(NWID_HORIZONTAL), - NWidget(NWID_VERTICAL), - NWidget(WWT_TEXT, COLOUR_GREY), SetMinimalSize(0, 12), SetFill(1, 0), SetDataTip(STR_GAME_OPTIONS_RESOLUTION, STR_NULL), SetPadding(0, 0, 2, 0), - NWidget(WWT_TEXT, COLOUR_GREY), SetMinimalSize(0, 12), SetFill(1, 0), SetDataTip(STR_GAME_OPTIONS_REFRESH_RATE, STR_NULL), SetPadding(0, 0, 2, 0), - NWidget(WWT_TEXT, COLOUR_GREY), SetMinimalSize(0, 12), SetFill(1, 0), SetDataTip(STR_GAME_OPTIONS_FULLSCREEN, STR_NULL), SetPadding(0, 0, 2, 0), - NWidget(WWT_TEXT, COLOUR_GREY), SetMinimalSize(0, 12), SetFill(1, 0), SetDataTip(STR_GAME_OPTIONS_VIDEO_ACCELERATION, STR_NULL), SetPadding(0, 0, 2, 0), -#ifndef __APPLE__ - NWidget(WWT_TEXT, COLOUR_GREY), SetMinimalSize(0, 12), SetFill(1, 0), SetDataTip(STR_GAME_OPTIONS_VIDEO_VSYNC, STR_NULL), -#endif - EndContainer(), - NWidget(NWID_VERTICAL), - NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_GO_RESOLUTION_DROPDOWN), SetMinimalSize(100, 12), SetDataTip(STR_BLACK_STRING, STR_GAME_OPTIONS_RESOLUTION_TOOLTIP), SetFill(1, 0), SetPadding(0, 0, 2, 0), - NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_GO_REFRESH_RATE_DROPDOWN), SetMinimalSize(100, 12), SetDataTip(STR_GAME_OPTIONS_REFRESH_RATE_ITEM, STR_GAME_OPTIONS_REFRESH_RATE_TOOLTIP), SetFill(1, 0), SetPadding(0, 0, 2, 0), - NWidget(NWID_HORIZONTAL), SetPadding(0, 0, 2, 0), + NWidget(NWID_VERTICAL), SetPIP(0, 2, 0), + NWidget(NWID_HORIZONTAL), + NWidget(WWT_TEXT, COLOUR_GREY), SetMinimalSize(0, 12),SetDataTip(STR_GAME_OPTIONS_RESOLUTION, STR_NULL), + NWidget(NWID_SPACER), SetMinimalSize(1, 0), SetFill(1, 0), + NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_GO_RESOLUTION_DROPDOWN), SetMinimalSize(200, 12), SetDataTip(STR_BLACK_STRING, STR_GAME_OPTIONS_RESOLUTION_TOOLTIP), + EndContainer(), + NWidget(NWID_HORIZONTAL), + NWidget(WWT_TEXT, COLOUR_GREY), SetMinimalSize(0, 12), SetDataTip(STR_GAME_OPTIONS_REFRESH_RATE, STR_NULL), + NWidget(NWID_SPACER), SetMinimalSize(1, 0), SetFill(1, 0), + NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_GO_REFRESH_RATE_DROPDOWN), SetMinimalSize(200, 12), SetDataTip(STR_GAME_OPTIONS_REFRESH_RATE_ITEM, STR_GAME_OPTIONS_REFRESH_RATE_TOOLTIP), + EndContainer(), + NWidget(NWID_HORIZONTAL), + NWidget(WWT_TEXT, COLOUR_GREY), SetMinimalSize(0, 12), SetDataTip(STR_GAME_OPTIONS_FULLSCREEN, STR_NULL), NWidget(NWID_SPACER), SetMinimalSize(1, 0), SetFill(1, 0), NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_GO_FULLSCREEN_BUTTON), SetMinimalSize(21, 9), SetDataTip(STR_EMPTY, STR_GAME_OPTIONS_FULLSCREEN_TOOLTIP), EndContainer(), - NWidget(NWID_HORIZONTAL), SetPadding(0, 0, 2, 0), + NWidget(NWID_HORIZONTAL), + NWidget(WWT_TEXT, COLOUR_GREY), SetMinimalSize(0, 12), SetDataTip(STR_GAME_OPTIONS_VIDEO_ACCELERATION, STR_NULL), NWidget(NWID_SPACER), SetMinimalSize(1, 0), SetFill(1, 0), NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_GO_VIDEO_ACCEL_BUTTON), SetMinimalSize(21, 9), SetDataTip(STR_EMPTY, STR_GAME_OPTIONS_VIDEO_ACCELERATION_TOOLTIP), EndContainer(), #ifndef __APPLE__ NWidget(NWID_HORIZONTAL), + NWidget(WWT_TEXT, COLOUR_GREY), SetMinimalSize(0, 12), SetDataTip(STR_GAME_OPTIONS_VIDEO_VSYNC, STR_NULL), NWidget(NWID_SPACER), SetMinimalSize(1, 0), SetFill(1, 0), NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_GO_VIDEO_VSYNC_BUTTON), SetMinimalSize(21, 9), SetDataTip(STR_EMPTY, STR_GAME_OPTIONS_VIDEO_VSYNC_TOOLTIP), EndContainer(), From 5644c00482ce2ca43e0f46f3ed40993599bb5707 Mon Sep 17 00:00:00 2001 From: Michael Lutz Date: Sun, 11 Apr 2021 14:28:29 +0200 Subject: [PATCH 058/268] Fix: Check for a validly mapped OpenGL screen buffer during driver init. (#9007) --- src/video/sdl2_opengl_v.cpp | 5 +++++ src/video/win32_v.cpp | 6 ++++++ 2 files changed, 11 insertions(+) diff --git a/src/video/sdl2_opengl_v.cpp b/src/video/sdl2_opengl_v.cpp index 08718a01e3..202593e64a 100644 --- a/src/video/sdl2_opengl_v.cpp +++ b/src/video/sdl2_opengl_v.cpp @@ -69,6 +69,11 @@ const char *VideoDriver_SDL_OpenGL::Start(const StringList ¶m) int w, h; SDL_GetWindowSize(this->sdl_window, &w, &h); this->ClientSizeChanged(w, h, true); + /* We should have a valid screen buffer now. If not, something went wrong and we should abort. */ + if (_screen.dst_ptr == nullptr) { + this->Stop(); + return "Can't get pointer to screen buffer"; + } return nullptr; } diff --git a/src/video/win32_v.cpp b/src/video/win32_v.cpp index 4674784618..08ab27c539 100644 --- a/src/video/win32_v.cpp +++ b/src/video/win32_v.cpp @@ -1305,6 +1305,12 @@ const char *VideoDriver_Win32OpenGL::Start(const StringList ¶m) } this->ClientSizeChanged(this->width, this->height, true); + /* We should have a valid screen buffer now. If not, something went wrong and we should abort. */ + if (_screen.dst_ptr == nullptr) { + this->Stop(); + _cur_resolution = old_res; + return "Can't get pointer to screen buffer"; + } MarkWholeScreenDirty(); From 31c5b8fe0f9c070143625478f7120e0c9c2332fd Mon Sep 17 00:00:00 2001 From: PeterN Date: Sun, 11 Apr 2021 13:30:13 +0100 Subject: [PATCH 059/268] Fix: Invalidate cached vehicle colourmaps when changing liveries setting. (#9006) --- src/settings.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/settings.cpp b/src/settings.cpp index 3448ce4b3e..fdb26368cf 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -63,6 +63,7 @@ #include "roadveh.h" #include "fios.h" #include "strings_func.h" +#include "vehicle_func.h" #include "void_map.h" #include "station_base.h" @@ -1153,6 +1154,7 @@ static bool InvalidateNewGRFChangeWindows(int32 p1) static bool InvalidateCompanyLiveryWindow(int32 p1) { InvalidateWindowClassesData(WC_COMPANY_COLOUR, -1); + ResetVehicleColourMap(); return RedrawScreen(p1); } From 9aebfca083bd36ac1af8d187a6728521aee60386 Mon Sep 17 00:00:00 2001 From: Wim Leflere Date: Sun, 11 Apr 2021 15:15:37 +0200 Subject: [PATCH 060/268] Fix: clang-cl build (#9018) Remove macro redefinitions Add final and fallthrough attributes for clang-cl --- src/stdafx.h | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/stdafx.h b/src/stdafx.h index f13104a5b2..6a33223455 100644 --- a/src/stdafx.h +++ b/src/stdafx.h @@ -113,7 +113,7 @@ #endif /* Stuff for GCC */ -#if defined(__GNUC__) || defined(__clang__) +#if defined(__GNUC__) || (defined(__clang__) && !defined(_MSC_VER)) # define NORETURN __attribute__ ((noreturn)) # define CDECL # define __int64 long long @@ -196,14 +196,10 @@ # define CDECL _cdecl # define WARN_FORMAT(string, args) -# ifndef __clang__ -# define FINAL sealed -# else -# define FINAL -# endif +# define FINAL final /* fallthrough attribute, VS 2017 */ -# if (_MSC_VER >= 1910) +# if (_MSC_VER >= 1910) || defined(__clang__) # define FALLTHROUGH [[fallthrough]] # else # define FALLTHROUGH From e5b960eaeb8b0ed565347fd87538c236b0e49bf2 Mon Sep 17 00:00:00 2001 From: translators Date: Sun, 11 Apr 2021 17:50:00 +0000 Subject: [PATCH 061/268] Update: Translations from eints korean: 23 changes by telk5093 romanian: 1 change by kneekoo russian: 1 change by Ln-Wolf french: 1 change by glx22 --- src/lang/afrikaans.txt | 1 + src/lang/arabic_egypt.txt | 1 + src/lang/basque.txt | 1 + src/lang/belarusian.txt | 1 + src/lang/brazilian_portuguese.txt | 1 + src/lang/bulgarian.txt | 1 + src/lang/catalan.txt | 1 + src/lang/croatian.txt | 1 + src/lang/czech.txt | 1 + src/lang/danish.txt | 1 + src/lang/dutch.txt | 1 + src/lang/english_AU.txt | 1 + src/lang/english_US.txt | 1 + src/lang/esperanto.txt | 1 + src/lang/estonian.txt | 1 + src/lang/faroese.txt | 1 + src/lang/finnish.txt | 1 + src/lang/french.txt | 3 +- src/lang/gaelic.txt | 1 + src/lang/galician.txt | 1 + src/lang/german.txt | 1 + src/lang/greek.txt | 1 + src/lang/hebrew.txt | 1 + src/lang/hungarian.txt | 1 + src/lang/icelandic.txt | 1 + src/lang/indonesian.txt | 1 + src/lang/irish.txt | 1 + src/lang/italian.txt | 1 + src/lang/japanese.txt | 1 + src/lang/korean.txt | 47 +++++++++++++++--------------- src/lang/latin.txt | 1 + src/lang/latvian.txt | 1 + src/lang/lithuanian.txt | 1 + src/lang/luxembourgish.txt | 1 + src/lang/malay.txt | 1 + src/lang/norwegian_bokmal.txt | 1 + src/lang/norwegian_nynorsk.txt | 1 + src/lang/polish.txt | 1 + src/lang/portuguese.txt | 1 + src/lang/romanian.txt | 2 ++ src/lang/russian.txt | 3 +- src/lang/serbian.txt | 1 + src/lang/simplified_chinese.txt | 1 + src/lang/slovak.txt | 1 + src/lang/slovenian.txt | 1 + src/lang/spanish.txt | 1 + src/lang/spanish_MX.txt | 1 + src/lang/swedish.txt | 1 + src/lang/tamil.txt | 1 + src/lang/thai.txt | 1 + src/lang/traditional_chinese.txt | 1 + src/lang/turkish.txt | 1 + src/lang/ukrainian.txt | 1 + src/lang/unfinished/chuvash.txt | 1 + src/lang/unfinished/frisian.txt | 1 + src/lang/unfinished/ido.txt | 1 + src/lang/unfinished/macedonian.txt | 1 + src/lang/unfinished/maltese.txt | 1 + src/lang/unfinished/marathi.txt | 1 + src/lang/unfinished/persian.txt | 1 + src/lang/unfinished/urdu.txt | 1 + src/lang/vietnamese.txt | 1 + src/lang/welsh.txt | 1 + 63 files changed, 89 insertions(+), 25 deletions(-) diff --git a/src/lang/afrikaans.txt b/src/lang/afrikaans.txt index b5c9a79bf1..a598df6299 100644 --- a/src/lang/afrikaans.txt +++ b/src/lang/afrikaans.txt @@ -990,6 +990,7 @@ STR_GAME_OPTIONS_RESOLUTION_TOOLTIP :{BLACK}Kies die STR_GAME_OPTIONS_RESOLUTION_OTHER :ander + STR_GAME_OPTIONS_GUI_ZOOM_FRAME :{BLACK} Koppelvlak groote STR_GAME_OPTIONS_GUI_ZOOM_DROPDOWN_TOOLTIP :{BLACK} Kies die koppelvlak element groote om te gebruik diff --git a/src/lang/arabic_egypt.txt b/src/lang/arabic_egypt.txt index ba35fa9067..bb89534dad 100644 --- a/src/lang/arabic_egypt.txt +++ b/src/lang/arabic_egypt.txt @@ -965,6 +965,7 @@ STR_GAME_OPTIONS_VIDEO_ACCELERATION :{BLACK}تسري STR_GAME_OPTIONS_VIDEO_ACCELERATION_TOOLTIP :{BLACK}حدد هذا المربع للسماح لـ OpenTTD بمحاولة استخدام تسريع الأجهزة. سيتم تطبيق الإعداد الذي تم تغييره فقط عند إعادة تشغيل اللعبة STR_GAME_OPTIONS_VIDEO_ACCELERATION_RESTART :{WHITE}لن يعمل الإعداد إلا بعد إعادة تشغيل اللعبة + STR_GAME_OPTIONS_GUI_ZOOM_FRAME :{BLACK}حجم اللوحة STR_GAME_OPTIONS_GUI_ZOOM_DROPDOWN_TOOLTIP :{BLACK}حدد العنصر المطلوب diff --git a/src/lang/basque.txt b/src/lang/basque.txt index df73bdc173..b9f89ac069 100644 --- a/src/lang/basque.txt +++ b/src/lang/basque.txt @@ -961,6 +961,7 @@ STR_GAME_OPTIONS_RESOLUTION_TOOLTIP :{BLACK}Pantaila STR_GAME_OPTIONS_RESOLUTION_OTHER :besteak + STR_GAME_OPTIONS_GUI_ZOOM_FRAME :{BLACK}Interfaze tamaina STR_GAME_OPTIONS_GUI_ZOOM_DROPDOWN_NORMAL :Normala diff --git a/src/lang/belarusian.txt b/src/lang/belarusian.txt index a3807fc76f..9a9c84ba6e 100644 --- a/src/lang/belarusian.txt +++ b/src/lang/belarusian.txt @@ -1299,6 +1299,7 @@ STR_GAME_OPTIONS_RESOLUTION_TOOLTIP :{BLACK}Выба STR_GAME_OPTIONS_RESOLUTION_OTHER :Iншае + STR_GAME_OPTIONS_GUI_ZOOM_FRAME :{BLACK}Памер элементаў інтэрфейсу STR_GAME_OPTIONS_GUI_ZOOM_DROPDOWN_TOOLTIP :{BLACK}Выберыце памер элементаў інтэрфейсу diff --git a/src/lang/brazilian_portuguese.txt b/src/lang/brazilian_portuguese.txt index 09fd14b100..3d902f7234 100644 --- a/src/lang/brazilian_portuguese.txt +++ b/src/lang/brazilian_portuguese.txt @@ -1007,6 +1007,7 @@ STR_GAME_OPTIONS_VIDEO_ACCELERATION :{BLACK}Acelera STR_GAME_OPTIONS_VIDEO_ACCELERATION_TOOLTIP :{BLACK}Marque esta caixa para permitir que o OpenTTD tente usar a aceleração de hardware. Qualquer mudança nesta configuração só será aplicada após reiniciar o jogo. STR_GAME_OPTIONS_VIDEO_ACCELERATION_RESTART :{WHITE}A configuração só terá efeito após reiniciar o jogo + STR_GAME_OPTIONS_GUI_ZOOM_FRAME :{BLACK}Tamanho da interface STR_GAME_OPTIONS_GUI_ZOOM_DROPDOWN_TOOLTIP :{BLACK}Selecione o tamanho de elemento de interface a ser usado diff --git a/src/lang/bulgarian.txt b/src/lang/bulgarian.txt index 3b174449e2..3f40d2c0af 100644 --- a/src/lang/bulgarian.txt +++ b/src/lang/bulgarian.txt @@ -969,6 +969,7 @@ STR_GAME_OPTIONS_RESOLUTION_TOOLTIP :{BLACK}Избо STR_GAME_OPTIONS_RESOLUTION_OTHER :друго + STR_GAME_OPTIONS_GUI_ZOOM_FRAME :{BLACK}Интерфейс размер STR_GAME_OPTIONS_GUI_ZOOM_DROPDOWN_TOOLTIP :{BLACK}Изберете размера на интерфейс елемент за използване diff --git a/src/lang/catalan.txt b/src/lang/catalan.txt index f4d8021e19..65f17997bd 100644 --- a/src/lang/catalan.txt +++ b/src/lang/catalan.txt @@ -1007,6 +1007,7 @@ STR_GAME_OPTIONS_VIDEO_ACCELERATION :{BLACK}Accelera STR_GAME_OPTIONS_VIDEO_ACCELERATION_TOOLTIP :{BLACK}Seleccioneu aquesta opció per permetre que l'OpenTTD provi d'usar acceleració per maquinari. Si es canvia l'opció, s'aplicarà quan es reiniciï el programa. STR_GAME_OPTIONS_VIDEO_ACCELERATION_RESTART :{WHITE}La configuració tindrà efecte quan es reiniciï el programa. + STR_GAME_OPTIONS_GUI_ZOOM_FRAME :{BLACK}Mida de la interfície STR_GAME_OPTIONS_GUI_ZOOM_DROPDOWN_TOOLTIP :{BLACK}Escull la mida dels elements de la interfície diff --git a/src/lang/croatian.txt b/src/lang/croatian.txt index 6be4993e88..f534c46cdc 100644 --- a/src/lang/croatian.txt +++ b/src/lang/croatian.txt @@ -1086,6 +1086,7 @@ STR_GAME_OPTIONS_RESOLUTION_TOOLTIP :{BLACK}Odaberi STR_GAME_OPTIONS_RESOLUTION_OTHER :ostalo + STR_GAME_OPTIONS_GUI_ZOOM_FRAME :{BLACK}Veličina sučelja STR_GAME_OPTIONS_GUI_ZOOM_DROPDOWN_TOOLTIP :{BLACK}Odaberite koju ćete veličinu elementa sučelja koristiti diff --git a/src/lang/czech.txt b/src/lang/czech.txt index 2b0c8b1e9b..eefb64a7ea 100644 --- a/src/lang/czech.txt +++ b/src/lang/czech.txt @@ -1093,6 +1093,7 @@ STR_GAME_OPTIONS_VIDEO_ACCELERATION :{BLACK}Hardwaro STR_GAME_OPTIONS_VIDEO_ACCELERATION_TOOLTIP :{BLACK}Zaškrtni, pokud chceš OpenTTD povolit použití hardwarové akcelerace. Změněné nastavení bude aplikováno po restartu hry STR_GAME_OPTIONS_VIDEO_ACCELERATION_RESTART :{WHITE}Nastavení vstoupí v platnost pouze po restartu hry + STR_GAME_OPTIONS_GUI_ZOOM_FRAME :{BLACK}Velikost rozhraní STR_GAME_OPTIONS_GUI_ZOOM_DROPDOWN_TOOLTIP :{BLACK}Zvolit velikost prvků uživatelského rozhraní diff --git a/src/lang/danish.txt b/src/lang/danish.txt index 86d48f86ad..7f0e804fec 100644 --- a/src/lang/danish.txt +++ b/src/lang/danish.txt @@ -991,6 +991,7 @@ STR_GAME_OPTIONS_RESOLUTION_OTHER :andet STR_GAME_OPTIONS_VIDEO_ACCELERATION :{BLACK}Hardware-acceleration + STR_GAME_OPTIONS_GUI_ZOOM_FRAME :{BLACK}grænseflade størrelse STR_GAME_OPTIONS_GUI_ZOOM_DROPDOWN_TOOLTIP :{BLACK}Vælg den grænseflade størrelse du ønsker at benytte diff --git a/src/lang/dutch.txt b/src/lang/dutch.txt index 8a5b74bfb5..799ce679b1 100644 --- a/src/lang/dutch.txt +++ b/src/lang/dutch.txt @@ -1006,6 +1006,7 @@ STR_GAME_OPTIONS_VIDEO_ACCELERATION :{BLACK}Hardware STR_GAME_OPTIONS_VIDEO_ACCELERATION_TOOLTIP :{BLACK}Selecteer dit vakje om OpenTTD hardwareversnelling te laten gebruiken. De gewijzigde instelling wordt pas van kracht nadat het spel opnieuw is gestart. STR_GAME_OPTIONS_VIDEO_ACCELERATION_RESTART :{WHITE}De instelling wordt pas van kracht als het spel opnieuw is gestart + STR_GAME_OPTIONS_GUI_ZOOM_FRAME :{BLACK}Menupuntgrootte STR_GAME_OPTIONS_GUI_ZOOM_DROPDOWN_TOOLTIP :{BLACK}Kiest de grootte van bedieningselementen diff --git a/src/lang/english_AU.txt b/src/lang/english_AU.txt index cfb2f69b47..159af46cd3 100644 --- a/src/lang/english_AU.txt +++ b/src/lang/english_AU.txt @@ -966,6 +966,7 @@ STR_GAME_OPTIONS_RESOLUTION_OTHER :other + STR_GAME_OPTIONS_BASE_GRF :{BLACK}Base graphics set STR_GAME_OPTIONS_BASE_GRF_TOOLTIP :{BLACK}Select the base graphics set to use STR_GAME_OPTIONS_BASE_GRF_STATUS :{RED}{NUM} missing/corrupted file{P "" s} diff --git a/src/lang/english_US.txt b/src/lang/english_US.txt index 4a3eb6abb8..c9763aeeb0 100644 --- a/src/lang/english_US.txt +++ b/src/lang/english_US.txt @@ -1006,6 +1006,7 @@ STR_GAME_OPTIONS_VIDEO_ACCELERATION :{BLACK}Hardware STR_GAME_OPTIONS_VIDEO_ACCELERATION_TOOLTIP :{BLACK}Check this box to allow OpenTTD to try to use hardware acceleration. A changed setting will only be applied upon game restart STR_GAME_OPTIONS_VIDEO_ACCELERATION_RESTART :{WHITE}The setting will only take effect after a game restart + STR_GAME_OPTIONS_GUI_ZOOM_FRAME :{BLACK}Interface size STR_GAME_OPTIONS_GUI_ZOOM_DROPDOWN_TOOLTIP :{BLACK}Select the interface element size to use diff --git a/src/lang/esperanto.txt b/src/lang/esperanto.txt index 7df07fdd3d..c8893d3ab4 100644 --- a/src/lang/esperanto.txt +++ b/src/lang/esperanto.txt @@ -953,6 +953,7 @@ STR_GAME_OPTIONS_RESOLUTION_TOOLTIP :{BLACK}Elektu u STR_GAME_OPTIONS_RESOLUTION_OTHER :alia + STR_GAME_OPTIONS_GUI_ZOOM_FRAME :{BLACK}Interfacgrandeco STR_GAME_OPTIONS_GUI_ZOOM_DROPDOWN_NORMAL :Normala diff --git a/src/lang/estonian.txt b/src/lang/estonian.txt index da66065e5a..4fc6af2b96 100644 --- a/src/lang/estonian.txt +++ b/src/lang/estonian.txt @@ -1063,6 +1063,7 @@ 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_GUI_ZOOM_FRAME :{BLACK}Liidese suurus STR_GAME_OPTIONS_GUI_ZOOM_DROPDOWN_TOOLTIP :{BLACK}Vali kasutatav liideseelementide suurus diff --git a/src/lang/faroese.txt b/src/lang/faroese.txt index fb9edb11ed..e6519df5f2 100644 --- a/src/lang/faroese.txt +++ b/src/lang/faroese.txt @@ -947,6 +947,7 @@ STR_GAME_OPTIONS_RESOLUTION_OTHER :annað + STR_GAME_OPTIONS_BASE_GRF :{BLACK}Base grafikk sett STR_GAME_OPTIONS_BASE_GRF_TOOLTIP :{BLACK}Vel ta base grafikk setti tú vil brúka STR_GAME_OPTIONS_BASE_GRF_STATUS :{RED}{NUM} vantandi/oyðiløgd fíl{P a ir} diff --git a/src/lang/finnish.txt b/src/lang/finnish.txt index e457ea9fe8..00173ff33e 100644 --- a/src/lang/finnish.txt +++ b/src/lang/finnish.txt @@ -1006,6 +1006,7 @@ STR_GAME_OPTIONS_VIDEO_ACCELERATION :{BLACK}Laitteis STR_GAME_OPTIONS_VIDEO_ACCELERATION_TOOLTIP :{BLACK}Valitse tämä, jos haluat, että OpenTTD yrittää käyttää laitteistokiihdytystä. Muutettu asetus tulee voimaan vasta pelin uudelleenkäynnistyksen jälkeen. STR_GAME_OPTIONS_VIDEO_ACCELERATION_RESTART :{WHITE}Asetus tulee voimaan vasta pelin uudelleenkäynnistyksen jälkeen + STR_GAME_OPTIONS_GUI_ZOOM_FRAME :{BLACK}Käyttöliittymän koko STR_GAME_OPTIONS_GUI_ZOOM_DROPDOWN_TOOLTIP :{BLACK}Valitse käyttöliittymäelementtien koko diff --git a/src/lang/french.txt b/src/lang/french.txt index 09a7f38e55..80ddd9833d 100644 --- a/src/lang/french.txt +++ b/src/lang/french.txt @@ -1007,6 +1007,7 @@ STR_GAME_OPTIONS_VIDEO_ACCELERATION :{BLACK} Accél STR_GAME_OPTIONS_VIDEO_ACCELERATION_TOOLTIP :{BLACK}Cochez cette case permet à OpenTTD d'utiliser l'accélération matérielle, si possible. Un paramètre modifié ne sera pris en compte qu'au redémarrage du jeu STR_GAME_OPTIONS_VIDEO_ACCELERATION_RESTART :{WHITE}Les paramètres ne prendront effet qu'après le redémarrage du jeu + STR_GAME_OPTIONS_GUI_ZOOM_FRAME :{BLACK}Taille d'interface STR_GAME_OPTIONS_GUI_ZOOM_DROPDOWN_TOOLTIP :{BLACK}Choisir la taille d'élément d'interface à utiliser @@ -1023,7 +1024,7 @@ STR_GAME_OPTIONS_FONT_ZOOM_DROPDOWN_NORMAL :Normal STR_GAME_OPTIONS_FONT_ZOOM_DROPDOWN_2X_ZOOM :Taille double STR_GAME_OPTIONS_FONT_ZOOM_DROPDOWN_4X_ZOOM :Taille quadruple -STR_GAME_OPTIONS_GRAPHICS :Graphiques {BLACK} +STR_GAME_OPTIONS_GRAPHICS :{BLACK} Graphiques STR_GAME_OPTIONS_REFRESH_RATE :{BLACK} Taux de rafraîchissement de l'affichage STR_GAME_OPTIONS_REFRESH_RATE_TOOLTIP :{BLACK} Sélectionnez la fréquence de rafraîchissement à utiliser diff --git a/src/lang/gaelic.txt b/src/lang/gaelic.txt index 6755dd6909..7e15bd033d 100644 --- a/src/lang/gaelic.txt +++ b/src/lang/gaelic.txt @@ -1174,6 +1174,7 @@ STR_GAME_OPTIONS_RESOLUTION_TOOLTIP :{BLACK}Tagh dù STR_GAME_OPTIONS_RESOLUTION_OTHER :Gnàthaichte + STR_GAME_OPTIONS_GUI_ZOOM_FRAME :{BLACK}Meud na h-eadar-aghaidh STR_GAME_OPTIONS_GUI_ZOOM_DROPDOWN_TOOLTIP :{BLACK}Tagh am meud airson rud san eadar-aghaidh diff --git a/src/lang/galician.txt b/src/lang/galician.txt index 6b10b58184..d993322b42 100644 --- a/src/lang/galician.txt +++ b/src/lang/galician.txt @@ -989,6 +989,7 @@ STR_GAME_OPTIONS_RESOLUTION_TOOLTIP :{BLACK}Seleccio STR_GAME_OPTIONS_RESOLUTION_OTHER :outra + STR_GAME_OPTIONS_GUI_ZOOM_FRAME :{BLACK}Tamaño da interface STR_GAME_OPTIONS_GUI_ZOOM_DROPDOWN_TOOLTIP :{BLACK}Seleciona o tamaño de elementos da interface a usar diff --git a/src/lang/german.txt b/src/lang/german.txt index ea971e99ee..ea70ed2f2b 100644 --- a/src/lang/german.txt +++ b/src/lang/german.txt @@ -1007,6 +1007,7 @@ STR_GAME_OPTIONS_VIDEO_ACCELERATION :{BLACK}Hardware STR_GAME_OPTIONS_VIDEO_ACCELERATION_TOOLTIP :{BLACK}Dieses Kästchen ankreuzen, um OpenTTD zu erlauben, die Hardwarebeschleunigung zu verwenden. Eine geänderte Einstellung wird nur beim Spielneustart wirksam STR_GAME_OPTIONS_VIDEO_ACCELERATION_RESTART :{WHITE}Die Einstellung tritt nur nach einem Neustart des Spiels in Kraft + STR_GAME_OPTIONS_GUI_ZOOM_FRAME :{BLACK}Größe der Bedienelemente STR_GAME_OPTIONS_GUI_ZOOM_DROPDOWN_TOOLTIP :{BLACK}Wähle die Größe der Bedienelemente diff --git a/src/lang/greek.txt b/src/lang/greek.txt index 9be35fa43a..6593038b4f 100644 --- a/src/lang/greek.txt +++ b/src/lang/greek.txt @@ -1101,6 +1101,7 @@ STR_GAME_OPTIONS_RESOLUTION_TOOLTIP :{BLACK}Διαλ STR_GAME_OPTIONS_RESOLUTION_OTHER :άλλη + STR_GAME_OPTIONS_GUI_ZOOM_FRAME :{BLACK}Μέγεθος διεπαφής STR_GAME_OPTIONS_GUI_ZOOM_DROPDOWN_TOOLTIP :{BLACK}Επιλέξτε το μέγεθος στοιχείου διεπαφής diff --git a/src/lang/hebrew.txt b/src/lang/hebrew.txt index 9e0a2be7e5..ba5d417ab0 100644 --- a/src/lang/hebrew.txt +++ b/src/lang/hebrew.txt @@ -988,6 +988,7 @@ STR_GAME_OPTIONS_RESOLUTION_TOOLTIP :{BLACK}בחר STR_GAME_OPTIONS_RESOLUTION_OTHER :אחר + STR_GAME_OPTIONS_GUI_ZOOM_FRAME :{BLACK}גודל ממשק STR_GAME_OPTIONS_GUI_ZOOM_DROPDOWN_TOOLTIP :{BLACK}בחר את ממשק גודל העצם לשימוש diff --git a/src/lang/hungarian.txt b/src/lang/hungarian.txt index b7064f1a4f..f9c689f7f8 100644 --- a/src/lang/hungarian.txt +++ b/src/lang/hungarian.txt @@ -1070,6 +1070,7 @@ STR_GAME_OPTIONS_VIDEO_ACCELERATION :{BLACK}Hardvere STR_GAME_OPTIONS_VIDEO_ACCELERATION_TOOLTIP :{BLACK}Bekapcsolásával az OpenTTD hardveres gyorsítást próbál alkalmazni. A beállítás csak a játék újraindítása után lép érvénybe. STR_GAME_OPTIONS_VIDEO_ACCELERATION_RESTART :{WHITE}Ez a beállítás csak a játék újraindítása után lép érvénybe + STR_GAME_OPTIONS_GUI_ZOOM_FRAME :{BLACK}Felület mérete STR_GAME_OPTIONS_GUI_ZOOM_DROPDOWN_TOOLTIP :{BLACK}Használni kívánt felületméret kiválasztása diff --git a/src/lang/icelandic.txt b/src/lang/icelandic.txt index 9aae3f3510..778c072250 100644 --- a/src/lang/icelandic.txt +++ b/src/lang/icelandic.txt @@ -946,6 +946,7 @@ STR_GAME_OPTIONS_RESOLUTION_OTHER :annað + STR_GAME_OPTIONS_BASE_GRF :{BLACK}Grunngrafík STR_GAME_OPTIONS_BASE_GRF_TOOLTIP :{BLACK}Nota grunngrafíkina STR_GAME_OPTIONS_BASE_GRF_STATUS :{RED}{NUM} týnd{P "" ar} eða ónýt{P "" ar} skrá{P "" r} diff --git a/src/lang/indonesian.txt b/src/lang/indonesian.txt index 2c13e4243a..20ee7ef74d 100644 --- a/src/lang/indonesian.txt +++ b/src/lang/indonesian.txt @@ -1003,6 +1003,7 @@ STR_GAME_OPTIONS_RESOLUTION_OTHER :lainnya STR_GAME_OPTIONS_VIDEO_ACCELERATION :{BLACK}Akselerasi perangkat keras STR_GAME_OPTIONS_VIDEO_ACCELERATION_RESTART :{WHITE}Pengaturan hanya akan berlaku setelah game dimulai ulang + STR_GAME_OPTIONS_GUI_ZOOM_FRAME :{BLACK}Ukuran antarmuka STR_GAME_OPTIONS_GUI_ZOOM_DROPDOWN_TOOLTIP :{BLACK}Pilih ukuran elemen antarmuka yang akan digunakan diff --git a/src/lang/irish.txt b/src/lang/irish.txt index 6d0a52242b..a6b192cf7f 100644 --- a/src/lang/irish.txt +++ b/src/lang/irish.txt @@ -969,6 +969,7 @@ STR_GAME_OPTIONS_RESOLUTION_TOOLTIP :{BLACK}Roghnaig STR_GAME_OPTIONS_RESOLUTION_OTHER :eile + STR_GAME_OPTIONS_GUI_ZOOM_FRAME :{BLACK}Méid an chomhéadain STR_GAME_OPTIONS_GUI_ZOOM_DROPDOWN_TOOLTIP :{BLACK}Roghnaigh méid na heiliminte comhéadain a úsáidfear diff --git a/src/lang/italian.txt b/src/lang/italian.txt index 4de8c45e8a..2627a7b6ce 100644 --- a/src/lang/italian.txt +++ b/src/lang/italian.txt @@ -1008,6 +1008,7 @@ STR_GAME_OPTIONS_VIDEO_ACCELERATION :{BLACK}Accelera STR_GAME_OPTIONS_VIDEO_ACCELERATION_TOOLTIP :{BLACK}Seleziona questa opzione per consentire a OpenTTD di utilizzare l'accelerazione hardware. Eventuali cambiamenti avranno effetto solo dopo un riavvio del gioco STR_GAME_OPTIONS_VIDEO_ACCELERATION_RESTART :{WHITE}Le nuove impostazioni avranno effetto solo dopo un riavvio del gioco + STR_GAME_OPTIONS_GUI_ZOOM_FRAME :{BLACK}Dimensione interfaccia STR_GAME_OPTIONS_GUI_ZOOM_DROPDOWN_TOOLTIP :{BLACK}Seleziona la dimensione deglie elementi dell'interfaccia grafica diff --git a/src/lang/japanese.txt b/src/lang/japanese.txt index 48725eefb7..228b16dffb 100644 --- a/src/lang/japanese.txt +++ b/src/lang/japanese.txt @@ -982,6 +982,7 @@ STR_GAME_OPTIONS_RESOLUTION_ITEM :{NUM}x{NUM} STR_GAME_OPTIONS_VIDEO_ACCELERATION :{BLACK}ハードウェアアクセラレーション + STR_GAME_OPTIONS_GUI_ZOOM_FRAME :{BLACK}インターフェイスのサイズ STR_GAME_OPTIONS_GUI_ZOOM_DROPDOWN_TOOLTIP :{BLACK}インターフェイス上の単位サイズを指定します diff --git a/src/lang/korean.txt b/src/lang/korean.txt index c5e44fa35a..a0190dd42f 100644 --- a/src/lang/korean.txt +++ b/src/lang/korean.txt @@ -1007,6 +1007,7 @@ STR_GAME_OPTIONS_VIDEO_ACCELERATION :{BLACK}하드 STR_GAME_OPTIONS_VIDEO_ACCELERATION_TOOLTIP :{BLACK}OpenTTD가 하드웨어 가속을 사용하게 하려면 체크하세요. 변경된 설정은 게임을 재시작한 뒤에 적용됩니다. STR_GAME_OPTIONS_VIDEO_ACCELERATION_RESTART :{WHITE}이 설정은 게임을 재시작한 뒤에 적용될 것입니다 + STR_GAME_OPTIONS_GUI_ZOOM_FRAME :{BLACK}인터페이스 크기 STR_GAME_OPTIONS_GUI_ZOOM_DROPDOWN_TOOLTIP :{BLACK}인터페이스의 크기를 선택합니다. @@ -1537,20 +1538,20 @@ STR_CONFIG_SETTING_SCRIPT_MAX_MEMORY :스크립트당 STR_CONFIG_SETTING_SCRIPT_MAX_MEMORY_HELPTEXT :스크립트 하나가 강제 종료되기 전까지 사용할 수 있는 메모리의 양입니다. 크기가 큰 맵에서는 값을 크게 설정해야할 수도 있습니다. STR_CONFIG_SETTING_SCRIPT_MAX_MEMORY_VALUE :{COMMA} MiB -STR_CONFIG_SETTING_SERVINT_ISPERCENT :신뢰도에 따른 정비 설정: {STRING} +STR_CONFIG_SETTING_SERVINT_ISPERCENT :신뢰도에 따른 점검 설정: {STRING} STR_CONFIG_SETTING_SERVINT_ISPERCENT_HELPTEXT :차량 점검 방식을 "마지막 점검 이후 지난 시간 (또는) 최대 신뢰도에 대한 차량 신뢰도의 일정 퍼센트 하락 여부" 중에 하나로 선택합니다. STR_CONFIG_SETTING_SERVINT_TRAINS :열차에 대한 기본 점검 기준: {STRING} STR_CONFIG_SETTING_SERVINT_TRAINS_HELPTEXT :열차에 따로 점검 기간이 설정되어있지 않은 경우에 사용할 기본 점검 기간을 설정합니다. STR_CONFIG_SETTING_SERVINT_VALUE :{COMMA}{NBSP}일/% STR_CONFIG_SETTING_SERVINT_DISABLED :사용 안 함 STR_CONFIG_SETTING_SERVINT_ROAD_VEHICLES :차량에 대한 기본 점검 기준: {STRING} -STR_CONFIG_SETTING_SERVINT_ROAD_VEHICLES_HELPTEXT :차량 정비 설정을 하지 않은 경우, 기본값으로 사용할 정비 주기를 설정합니다. +STR_CONFIG_SETTING_SERVINT_ROAD_VEHICLES_HELPTEXT :차량 점검 설정을 하지 않은 경우, 기본값으로 사용할 점검 주기를 설정합니다 STR_CONFIG_SETTING_SERVINT_AIRCRAFT :항공기에 대한 기본 점검 기준: {STRING} STR_CONFIG_SETTING_SERVINT_AIRCRAFT_HELPTEXT :항공기에 따로 점검 기간이 설정되어있지 않은 경우에 사용할 기본 점검 기간을 설정합니다. STR_CONFIG_SETTING_SERVINT_SHIPS :선박에 대한 기본 점검 기준: {STRING} STR_CONFIG_SETTING_SERVINT_SHIPS_HELPTEXT :선박에 따로 점검 기간이 설정되어있지 않은 경우에 사용할 기본 점검 기간을 설정합니다. -STR_CONFIG_SETTING_NOSERVICE :차량 고장 설정이 비활성화된 경우 정비하지 않음: {STRING} -STR_CONFIG_SETTING_NOSERVICE_HELPTEXT :이 설정을 켜면, 차량이 고장나지 않도록 설정되어 있는 경우 차량이 정비를 하러 가지 않습니다. +STR_CONFIG_SETTING_NOSERVICE :차량 고장 설정을 껐으면 점검을 하지 않음: {STRING} +STR_CONFIG_SETTING_NOSERVICE_HELPTEXT :이 설정을 켜면, 차량이 고장나지 않도록 설정되어 있는 경우 차량이 자동으로 점검을 하러 가지 않습니다 STR_CONFIG_SETTING_WAGONSPEEDLIMITS :화물차 속력 제한 적용: {STRING} STR_CONFIG_SETTING_WAGONSPEEDLIMITS_HELPTEXT :이 설정을 켜면, 화물차의 속력 제한값에 따라 열차의 최대 속력을 제한합니다. STR_CONFIG_SETTING_DISABLE_ELRAILS :전기 철도를 사용하지 않음: {STRING} @@ -2409,7 +2410,7 @@ STR_RAIL_TOOLBAR_MAGLEV_CONSTRUCTION_CAPTION :자기부상열 STR_RAIL_TOOLBAR_TOOLTIP_BUILD_RAILROAD_TRACK :{BLACK}선로를 건설합니다. CTRL 키를 누르면 건설모드/철거모드로 전환합니다. SHIFT 키를 누른 채로 사용하면 예상 비용을 볼 수 있습니다 STR_RAIL_TOOLBAR_TOOLTIP_BUILD_AUTORAIL :{BLACK}자동건설 모드로 선로를 건설합니다. CTRL 키를 누르면 건설/철거모드를 바꿀 수 있습니다. SHIFT 키를 누른 채로 사용하면 예상 비용을 볼 수 있습니다 -STR_RAIL_TOOLBAR_TOOLTIP_BUILD_TRAIN_DEPOT_FOR_BUILDING :{BLACK}차량기지를 건설합니다. 차량을 구입하거나 정비를 할 수 있습니다. SHIFT 키를 누른 채로 사용하면 예상 비용을 볼 수 있습니다 +STR_RAIL_TOOLBAR_TOOLTIP_BUILD_TRAIN_DEPOT_FOR_BUILDING :{BLACK}차량기지를 건설합니다. 차량을 구입하거나 점검을 할 수 있습니다. SHIFT 키를 누른 채로 사용하면 예상 비용을 볼 수 있습니다 STR_RAIL_TOOLBAR_TOOLTIP_CONVERT_RAIL_TO_WAYPOINT :{BLACK}선로에 경유지를 설치합니다. CTRL 키를 사용하면 같은 이름의 경유지를 서로 떨어진 곳에 지을 수 있습니다. SHIFT 키를 누른 채로 사용하면 예상 비용을 볼 수 있습니다 STR_RAIL_TOOLBAR_TOOLTIP_BUILD_RAILROAD_STATION :{BLACK}철도역을 짓습니다. CTRL 키를 사용하면 같은 이름의 역을 서로 떨어진 곳에 지을 수 있습니다. SHIFT 키를 누른 채로 사용하면 예상 비용을 볼 수 있습니다 STR_RAIL_TOOLBAR_TOOLTIP_BUILD_RAILROAD_SIGNALS :{BLACK}신호기를 설치합니다. CTRL 키를 누르면 구식/전자식으로 전환합니다.{}선로를 따라 드래그해서 설치할 수 있습니다. CTRL 키를 누른 채로 드래그하면 다음 분기점이나 다음 신호기까지 신호기를 설치합니다.{}CTRL 키를 누른 채 클릭하면 신호기 선택 창을 전환합니다. SHIFT 키를 누른 채로 사용하면 예상 비용을 볼 수 있습니다 @@ -2490,8 +2491,8 @@ STR_ROAD_TOOLBAR_TOOLTIP_BUILD_ROAD_SECTION :{BLACK}도로 STR_ROAD_TOOLBAR_TOOLTIP_BUILD_TRAMWAY_SECTION :{BLACK}전차 선로를 짓습니다. CTRL 키를 누르고 있으면 건설/제거 모드를 바꿀 수 있습니다. SHIFT 키를 누른 채로 사용하면 예상 비용을 볼 수 있습니다 STR_ROAD_TOOLBAR_TOOLTIP_BUILD_AUTOROAD :{BLACK}자동건설 모드로 도로를 짓습니다. CTRL 키를 누르고 있으면 건설/제거 모드를 바꿀 수 있습니다. SHIFT 키를 누른 채로 사용하면 예상 비용을 볼 수 있습니다 STR_ROAD_TOOLBAR_TOOLTIP_BUILD_AUTOTRAM :{BLACK}자동건설 모드로 전차 선로를 짓습니다. CTRL 키를 누르고 있으면 건설/제거 모드를 바꿀 수 있습니다. SHIFT 키를 누른 채로 사용하면 예상 비용을 볼 수 있습니다 -STR_ROAD_TOOLBAR_TOOLTIP_BUILD_ROAD_VEHICLE_DEPOT :{BLACK}차고지를 건설합니다. 차량을 구입하거나 정비를 할 수 있습니다. SHIFT 키를 누른 채로 사용하면 예상 비용을 볼 수 있습니다 -STR_ROAD_TOOLBAR_TOOLTIP_BUILD_TRAM_VEHICLE_DEPOT :{BLACK}전차 차고지를 건설합니다. 차량을 구입하거나 정비를 할 수 있습니다. SHIFT 키를 누른 채로 사용하면 예상 비용을 볼 수 있습니다 +STR_ROAD_TOOLBAR_TOOLTIP_BUILD_ROAD_VEHICLE_DEPOT :{BLACK}차고지를 건설합니다. 차량을 구입하거나 점검을 할 수 있습니다. SHIFT 키를 누른 채로 사용하면 예상 비용을 볼 수 있습니다 +STR_ROAD_TOOLBAR_TOOLTIP_BUILD_TRAM_VEHICLE_DEPOT :{BLACK}전차 차고지를 건설합니다. 차량을 구입하거나 점검을 할 수 있습니다. SHIFT 키를 누른 채로 사용하면 예상 비용을 볼 수 있습니다 STR_ROAD_TOOLBAR_TOOLTIP_BUILD_BUS_STATION :{BLACK}버스 정류장을 짓습니다. CTRL 키를 사용하면 근처 정류장과 연결할 수 있습니다. SHIFT 키를 누른 채로 사용하면 예상 비용을 볼 수 있습니다 STR_ROAD_TOOLBAR_TOOLTIP_BUILD_PASSENGER_TRAM_STATION :{BLACK}여객 전차역을 짓습니다. CTRL 키를 사용하면 근처 역과 연결할 수 있습니다. SHIFT 키를 누른 채로 사용하면 예상 비용을 볼 수 있습니다 STR_ROAD_TOOLBAR_TOOLTIP_BUILD_TRUCK_LOADING_BAY :{BLACK}트럭 적하장을 짓습니다. CTRL 키를 사용하면 근처 적하장과 연결할 수 있습니다. SHIFT 키를 누른 채로 사용하면 예상 비용을 볼 수 있습니다 @@ -3532,7 +3533,7 @@ STR_VEHICLE_LIST_AVAILABLE_ENGINES_TOOLTIP :{BLACK}이 차 STR_VEHICLE_LIST_MANAGE_LIST :{BLACK}관리 STR_VEHICLE_LIST_MANAGE_LIST_TOOLTIP :{BLACK}이 목록에 있는 모든 열차에 지시를 내려 관리합니다 STR_VEHICLE_LIST_REPLACE_VEHICLES :차량 교체 -STR_VEHICLE_LIST_SEND_FOR_SERVICING :정비하러 보내기 +STR_VEHICLE_LIST_SEND_FOR_SERVICING :점검하러 보내기 STR_VEHICLE_LIST_SEND_TRAIN_TO_DEPOT :차량기지로 보내기 STR_VEHICLE_LIST_SEND_ROAD_VEHICLE_TO_DEPOT :차고지로 보내기 @@ -3826,10 +3827,10 @@ STR_VEHICLE_VIEW_ROAD_VEHICLE_CENTER_TOOLTIP :{BLACK}이 차 STR_VEHICLE_VIEW_SHIP_CENTER_TOOLTIP :{BLACK}이 선박의 위치로 화면을 이동합니다. 더블 클릭하면 이 선박을 따라 화면이 움직입니다. CTRL+클릭하면 이 선박 위치를 기준으로 외부 화면을 엽니다 STR_VEHICLE_VIEW_AIRCRAFT_CENTER_TOOLTIP :{BLACK}이 항공기의 위치로 화면을 이동합니다. 더블 클릭하면 이 항공기를 따라 화면이 움직입니다. CTRL+클릭하면 이 항공기 위치를 기준으로 외부 화면을 엽니다 -STR_VEHICLE_VIEW_TRAIN_SEND_TO_DEPOT_TOOLTIP :{BLACK}열차를 차량기지로 보냅니다. CTRL+클릭하면 정비를 하러 차량기지에 들르기만 합니다 -STR_VEHICLE_VIEW_ROAD_VEHICLE_SEND_TO_DEPOT_TOOLTIP :{BLACK}차량을 차고지로 보냅니다. CTRL+클릭하면 정비를 하러 차고지에 들르기만 합니다 -STR_VEHICLE_VIEW_SHIP_SEND_TO_DEPOT_TOOLTIP :{BLACK}선박을 정박소로 보냅니다. CTRL+클릭하면 정비를 하러 정박소에 들르기만 합니다 -STR_VEHICLE_VIEW_AIRCRAFT_SEND_TO_DEPOT_TOOLTIP :{BLACK}항공기를 격납고로 보냅니다. CTRL+클릭하면 정비를 하러 격납고에 들르기만 합니다 +STR_VEHICLE_VIEW_TRAIN_SEND_TO_DEPOT_TOOLTIP :{BLACK}열차를 차량기지로 보냅니다. CTRL+클릭하면 점검을 하러 차량기지에 들르기만 합니다 +STR_VEHICLE_VIEW_ROAD_VEHICLE_SEND_TO_DEPOT_TOOLTIP :{BLACK}차량을 차고지로 보냅니다. CTRL+클릭하면 점검을 하러 차고지에 들르기만 합니다 +STR_VEHICLE_VIEW_SHIP_SEND_TO_DEPOT_TOOLTIP :{BLACK}선박을 정박소로 보냅니다. CTRL+클릭하면 점검을 하러 정박소에 들르기만 합니다 +STR_VEHICLE_VIEW_AIRCRAFT_SEND_TO_DEPOT_TOOLTIP :{BLACK}항공기를 격납고로 보냅니다. CTRL+클릭하면 점검을 하러 격납고에 들르기만 합니다 STR_VEHICLE_VIEW_CLONE_TRAIN_INFO :{BLACK}객차/화차를 포함한 열차 전체를 복제합니다. CTRL+클릭하면 경로도 함께 공유됩니다. SHIFT+클릭하면 예상 비용을 볼 수 있습니다 STR_VEHICLE_VIEW_CLONE_ROAD_VEHICLE_INFO :{BLACK}차량을 복제합니다. CTRL+클릭하면 경로도 함께 공유됩니다. SHIFT+클릭하면 예상 비용을 볼 수 있습니다 @@ -3917,12 +3918,12 @@ STR_VEHICLE_INFO_CAPACITY_CAPACITY :{BLACK}수송 STR_VEHICLE_INFO_FEEDER_CARGO_VALUE :{BLACK}환승 수익: {LTBLUE}{CURRENCY_LONG} -STR_VEHICLE_DETAILS_SERVICING_INTERVAL_DAYS :{BLACK}정비 간격: {LTBLUE}{COMMA}일{BLACK}마다 마지막 정비 날짜: {LTBLUE}{DATE_LONG} -STR_VEHICLE_DETAILS_SERVICING_INTERVAL_PERCENT :{BLACK}정비 기준: {LTBLUE}{COMMA}%{BLACK} 떨어지면 마지막 정비 날짜: {LTBLUE}{DATE_LONG} +STR_VEHICLE_DETAILS_SERVICING_INTERVAL_DAYS :{BLACK}점검 간격: {LTBLUE}{COMMA}일{BLACK}마다{NBSP} 마지막 점검 날짜: {LTBLUE}{DATE_LONG} +STR_VEHICLE_DETAILS_SERVICING_INTERVAL_PERCENT :{BLACK}점검 기준: {LTBLUE}{COMMA}%{BLACK} 떨어지면 마지막 점검 날짜: {LTBLUE}{DATE_LONG} STR_VEHICLE_DETAILS_INCREASE_SERVICING_INTERVAL_TOOLTIP :{BLACK}점검 기준값을 10만큼 올립니다. CTRL+클릭하면 점검 기준값을 5만큼 올립니다 STR_VEHICLE_DETAILS_DECREASE_SERVICING_INTERVAL_TOOLTIP :{BLACK}점검 기준값을 10만큼 내립니다. CTRL+클릭하면 점검 기준값을 5만큼 내립니다 -STR_SERVICE_INTERVAL_DROPDOWN_TOOLTIP :{BLACK}정비 기준 설정을 변경합니다 +STR_SERVICE_INTERVAL_DROPDOWN_TOOLTIP :{BLACK}점검 기준 설정을 변경합니다 STR_VEHICLE_DETAILS_DEFAULT :기본 STR_VEHICLE_DETAILS_DAYS :날짜 STR_VEHICLE_DETAILS_PERCENT :신뢰도 @@ -4020,11 +4021,11 @@ STR_ORDER_REFIT_AUTO_TOOLTIP :{BLACK}이 경 STR_ORDER_DROP_REFIT_AUTO :특정 화물로 STR_ORDER_DROP_REFIT_AUTO_ANY :이용 가능한 화물로 -STR_ORDER_SERVICE :{BLACK}정비 +STR_ORDER_SERVICE :{BLACK}점검 STR_ORDER_DROP_GO_ALWAYS_DEPOT :항상 감 -STR_ORDER_DROP_SERVICE_DEPOT :필요하면 정비 +STR_ORDER_DROP_SERVICE_DEPOT :필요하면 점검 STR_ORDER_DROP_HALT_DEPOT :멈춤 -STR_ORDER_SERVICE_TOOLTIP :{BLACK}정비가 필요하지 않으면 이 경로를 건너뜁니다 +STR_ORDER_SERVICE_TOOLTIP :{BLACK}점검이 필요하지 않으면 이 경로를 건너뜁니다 STR_ORDER_CONDITIONAL_VARIABLE_TOOLTIP :{BLACK}경로를 건너뛰기 위한 비교 조건을 선택합니다 @@ -4033,7 +4034,7 @@ STR_ORDER_CONDITIONAL_LOAD_PERCENTAGE :적재율 STR_ORDER_CONDITIONAL_RELIABILITY :신뢰도 STR_ORDER_CONDITIONAL_MAX_SPEED :최고 속력 STR_ORDER_CONDITIONAL_AGE :연령 (년) -STR_ORDER_CONDITIONAL_REQUIRES_SERVICE :정비 필요성 +STR_ORDER_CONDITIONAL_REQUIRES_SERVICE :점검 필요성 STR_ORDER_CONDITIONAL_UNCONDITIONALLY :항상 STR_ORDER_CONDITIONAL_REMAINING_LIFETIME :남은 수명 (년) STR_ORDER_CONDITIONAL_MAX_RELIABILITY :최대 신뢰도 @@ -4065,7 +4066,7 @@ STR_ORDER_GO_TO_NEAREST_DEPOT :가까운 차 STR_ORDER_GO_TO_NEAREST_HANGAR :가까운 격납고로 STR_ORDER_CONDITIONAL :조건부 경로 건너뛰기 STR_ORDER_SHARE :경로 공유하기 -STR_ORDERS_GO_TO_TOOLTIP :{BLACK}선택된 경로 바로 전이나 목록 맨 끝에 새 경로를 삽입합니다. CTRL 키와 함께 누르면, 역에서는 '아무 화물이나 가득 싣기'로, 경유지에서는 '직행'으로, 차량기지에서는 '점검'으로 지정됩니다. '공유된 경로'를 클릭하거나 CTRL 키를 누르면 선택했던 차량과 이 차량의 경로를 공유하게 됩니다. 단순히 클릭하면 그 차량의 경로를 복사하기만 합니다. 차량기지를 경로에 포함시키면 이 차량은 자동 정비를 할 수 없게 됩니다 +STR_ORDERS_GO_TO_TOOLTIP :{BLACK}선택된 경로 바로 전이나 목록 맨 끝에 새 경로를 삽입합니다. CTRL 키와 함께 누르면, 역에서는 '아무 화물이나 가득 싣기'로, 경유지에서는 '직행'으로, 차량기지에서는 '점검'으로 지정됩니다. '공유된 경로'를 클릭하거나 CTRL 키를 누르면 선택했던 차량과 이 차량의 경로를 공유하게 됩니다. 단순히 클릭하면 그 차량의 경로를 복사하기만 합니다. 차량기지를 경로에 포함시키면 이 차량은 자동 점검를 할 수 없게 됩니다 STR_ORDERS_VEH_WITH_SHARED_ORDERS_LIST_TOOLTIP :{BLACK}이 경로를 공유하고 있는 모든 차량을 표시합니다. @@ -4073,8 +4074,8 @@ STR_ORDERS_VEH_WITH_SHARED_ORDERS_LIST_TOOLTIP :{BLACK}이 경 STR_ORDER_GO_TO_WAYPOINT :완행 경유 {WAYPOINT} STR_ORDER_GO_NON_STOP_TO_WAYPOINT :직행 경유 {WAYPOINT} -STR_ORDER_SERVICE_AT :완행 정비 -STR_ORDER_SERVICE_NON_STOP_AT :직행 정비 +STR_ORDER_SERVICE_AT :완행 점검 +STR_ORDER_SERVICE_NON_STOP_AT :직행 점검 STR_ORDER_NEAREST_DEPOT :가까운 STR_ORDER_NEAREST_HANGAR :가까운 격납고 @@ -4682,7 +4683,7 @@ STR_ERROR_SHIP_NOT_AVAILABLE :{WHITE}이 선 STR_ERROR_AIRCRAFT_NOT_AVAILABLE :{WHITE}이 항공기는 사용할 수 없는 상태입니다 STR_ERROR_TOO_MANY_VEHICLES_IN_GAME :{WHITE}게임에 차량이 너무 많습니다! -STR_ERROR_CAN_T_CHANGE_SERVICING :{WHITE}정비 간격 설정을 바꿀 수 없습니다... +STR_ERROR_CAN_T_CHANGE_SERVICING :{WHITE}점검 간격 설정을 바꿀 수 없습니다... STR_ERROR_VEHICLE_IS_DESTROYED :{WHITE}... 차량이 파괴되었습니다 diff --git a/src/lang/latin.txt b/src/lang/latin.txt index a29caeadf1..bb12707a3c 100644 --- a/src/lang/latin.txt +++ b/src/lang/latin.txt @@ -1166,6 +1166,7 @@ STR_GAME_OPTIONS_RESOLUTION_TOOLTIP :{BLACK}Eligere STR_GAME_OPTIONS_RESOLUTION_OTHER :alia + STR_GAME_OPTIONS_GUI_ZOOM_FRAME :{BLACK}Magnitudo interfaciei STR_GAME_OPTIONS_GUI_ZOOM_DROPDOWN_TOOLTIP :{BLACK}Eligere magnitudinem interfaciei adhibendam diff --git a/src/lang/latvian.txt b/src/lang/latvian.txt index b5993c60f1..213635648b 100644 --- a/src/lang/latvian.txt +++ b/src/lang/latvian.txt @@ -1004,6 +1004,7 @@ STR_GAME_OPTIONS_RESOLUTION_TOOLTIP :{BLACK}Ekrāna STR_GAME_OPTIONS_RESOLUTION_OTHER :Cita + STR_GAME_OPTIONS_GUI_ZOOM_FRAME :{BLACK}Lietotāja saskarnes lielums STR_GAME_OPTIONS_GUI_ZOOM_DROPDOWN_TOOLTIP :{BLACK}Lietotāja saskarnes elementu lieluma izvēle diff --git a/src/lang/lithuanian.txt b/src/lang/lithuanian.txt index 188791cd13..a1e9e63cc7 100644 --- a/src/lang/lithuanian.txt +++ b/src/lang/lithuanian.txt @@ -1211,6 +1211,7 @@ STR_GAME_OPTIONS_VIDEO_ACCELERATION :{BLACK}Aparatin STR_GAME_OPTIONS_VIDEO_ACCELERATION_TOOLTIP :{BLACK}Pažymėkite šį langelį, jei norite, kad OpenTTD taikytų aparatinį spartinimą. Kad nuostata įsigaliotų, reiks perkrauti OpenTTD STR_GAME_OPTIONS_VIDEO_ACCELERATION_RESTART :{WHITE}Kad pakeitimai įsigaliotų, reikia paleisti OpenTTD iš naujo + STR_GAME_OPTIONS_GUI_ZOOM_FRAME :{BLACK}Sąsajos elementų dydis STR_GAME_OPTIONS_GUI_ZOOM_DROPDOWN_TOOLTIP :{BLACK}Pasirinkite vartotojo sąsajos elementų santykinį dydį diff --git a/src/lang/luxembourgish.txt b/src/lang/luxembourgish.txt index bc97755609..688daa891b 100644 --- a/src/lang/luxembourgish.txt +++ b/src/lang/luxembourgish.txt @@ -1006,6 +1006,7 @@ STR_GAME_OPTIONS_VIDEO_ACCELERATION :{BLACK}Hardware STR_GAME_OPTIONS_VIDEO_ACCELERATION_TOOLTIP :{BLACK}Dës Optioun wielen, dass OpenTTD Hardwarebeschleunigung dierf notzen. Wäert just geännert ginn wann d'Spill nei gestart gëtt STR_GAME_OPTIONS_VIDEO_ACCELERATION_RESTART :{WHITE}D'Astellung huet réicht en Afloss no engem Neistart vum Spill + STR_GAME_OPTIONS_GUI_ZOOM_FRAME :{BLACK}Interfacegréisst STR_GAME_OPTIONS_GUI_ZOOM_DROPDOWN_TOOLTIP :{BLACK}Wiel d'Gréisst déi fir den Interface soll benotzt ginn diff --git a/src/lang/malay.txt b/src/lang/malay.txt index 7346d8eda6..03b92a0323 100644 --- a/src/lang/malay.txt +++ b/src/lang/malay.txt @@ -944,6 +944,7 @@ STR_GAME_OPTIONS_RESOLUTION_TOOLTIP :{BLACK}Pilih re STR_GAME_OPTIONS_RESOLUTION_OTHER :lain + STR_GAME_OPTIONS_GUI_ZOOM_FRAME :{BLACK}Saiz Antaramuka STR_GAME_OPTIONS_GUI_ZOOM_DROPDOWN_TOOLTIP :{BLACK}Sila pilih saiz elemen antara muka untuk digunakan diff --git a/src/lang/norwegian_bokmal.txt b/src/lang/norwegian_bokmal.txt index 93333cc2e5..a32514a115 100644 --- a/src/lang/norwegian_bokmal.txt +++ b/src/lang/norwegian_bokmal.txt @@ -1008,6 +1008,7 @@ STR_GAME_OPTIONS_VIDEO_ACCELERATION :{BLACK}Maskinva STR_GAME_OPTIONS_VIDEO_ACCELERATION_TOOLTIP :{BLACK}Merk av i denne boksen for å la OpenTTD prøve å bruke maskinvareakselerasjon. En endret innstilling blir bare brukt ved omstart av spillet STR_GAME_OPTIONS_VIDEO_ACCELERATION_RESTART :{WHITE}Innstillingen vil ikke tre i kraft før spillet er restartet + STR_GAME_OPTIONS_GUI_ZOOM_FRAME :{BLACK}Grensesnitt-størrelse STR_GAME_OPTIONS_GUI_ZOOM_DROPDOWN_TOOLTIP :{BLACK}Velg grensesnitt-størrelsen som skal benyttes diff --git a/src/lang/norwegian_nynorsk.txt b/src/lang/norwegian_nynorsk.txt index 8c1ff1eed0..4a2bf807fc 100644 --- a/src/lang/norwegian_nynorsk.txt +++ b/src/lang/norwegian_nynorsk.txt @@ -971,6 +971,7 @@ STR_GAME_OPTIONS_RESOLUTION_TOOLTIP :{BLACK}Vel skje STR_GAME_OPTIONS_RESOLUTION_OTHER :anna + STR_GAME_OPTIONS_GUI_ZOOM_FRAME :{BLACK}Grensesnittstorleik STR_GAME_OPTIONS_GUI_ZOOM_DROPDOWN_TOOLTIP :{BLACK}Vel storleik å bruke på grensesnittet diff --git a/src/lang/polish.txt b/src/lang/polish.txt index 373e06e51c..38c132732f 100644 --- a/src/lang/polish.txt +++ b/src/lang/polish.txt @@ -1386,6 +1386,7 @@ STR_GAME_OPTIONS_VIDEO_ACCELERATION :{BLACK}Przyspie STR_GAME_OPTIONS_VIDEO_ACCELERATION_TOOLTIP :{BLACK}Zaznacz to pole, aby zezwolić OpenTTD na użycie przyspieszenia sprzętowego. Ustawienia zostaną zastosowane dopiero po ponownym uruchomieniu gry. STR_GAME_OPTIONS_VIDEO_ACCELERATION_RESTART :{WHITE}Ustawienie to zacznie obowiązywać dopiero po ponownym uruchomieniu gry. + STR_GAME_OPTIONS_GUI_ZOOM_FRAME :{BLACK}Rozmiar interfejsu STR_GAME_OPTIONS_GUI_ZOOM_DROPDOWN_TOOLTIP :{BLACK}Wybierz rozmiar elementów interfejsu diff --git a/src/lang/portuguese.txt b/src/lang/portuguese.txt index f98ef16211..431a69f809 100644 --- a/src/lang/portuguese.txt +++ b/src/lang/portuguese.txt @@ -1007,6 +1007,7 @@ STR_GAME_OPTIONS_VIDEO_ACCELERATION :{BLACK}Acelera STR_GAME_OPTIONS_VIDEO_ACCELERATION_TOOLTIP :{BLACK}Marque esta caixa para permitir que o OpenTTD tente usar a aceleração por hardware. Uma configuração alterada só será aplicada após reiniciar o jogo STR_GAME_OPTIONS_VIDEO_ACCELERATION_RESTART :{WHITE}A definição só terá efeito após reiniciar o jogo + STR_GAME_OPTIONS_GUI_ZOOM_FRAME :{BLACK}Tamanho interface STR_GAME_OPTIONS_GUI_ZOOM_DROPDOWN_TOOLTIP :{BLACK}Seleccionar tamanho do elemento de interface a usar diff --git a/src/lang/romanian.txt b/src/lang/romanian.txt index 88113eac2d..ebe7bd4f89 100644 --- a/src/lang/romanian.txt +++ b/src/lang/romanian.txt @@ -151,6 +151,7 @@ STR_ABBREV_NONE :{TINY_FONT}NU STR_ABBREV_ALL :{TINY_FONT}TOT # 'Mode' of transport for cargoes +STR_BAGS :{COMMA}{NBSP}sac{P "" "" "de "}sac{P "" i i} STR_TONS :{COMMA} tone STR_LITERS :{COMMA} litri STR_ITEMS :{COMMA} bucăți @@ -972,6 +973,7 @@ STR_GAME_OPTIONS_RESOLUTION_OTHER :(alta/nespecifi STR_GAME_OPTIONS_RESOLUTION_ITEM :{NUM}x{NUM} + STR_GAME_OPTIONS_GUI_ZOOM_FRAME :{BLACK}Mărime interfată STR_GAME_OPTIONS_GUI_ZOOM_DROPDOWN_TOOLTIP :{BLACK}Alege mărimea elementelor de interfaţa diff --git a/src/lang/russian.txt b/src/lang/russian.txt index 1a67358636..2ce6e0dc64 100644 --- a/src/lang/russian.txt +++ b/src/lang/russian.txt @@ -1151,6 +1151,7 @@ STR_GAME_OPTIONS_VIDEO_ACCELERATION :{BLACK}Аппа STR_GAME_OPTIONS_VIDEO_ACCELERATION_TOOLTIP :{BLACK}Поставьте отметку, чтобы включить аппаратное ускорение в OpenTTD. Но для этого игру придётся перезапустить. STR_GAME_OPTIONS_VIDEO_ACCELERATION_RESTART :{WHITE}Эта настройка будет применена только после перезапуска игры + STR_GAME_OPTIONS_GUI_ZOOM_FRAME :{BLACK}Размер элементов интерфейса STR_GAME_OPTIONS_GUI_ZOOM_DROPDOWN_TOOLTIP :{BLACK}Выберите размер элементов интерфейса @@ -1514,7 +1515,7 @@ STR_CONFIG_SETTING_SNOW_COVERAGE :Снежное STR_CONFIG_SETTING_SNOW_COVERAGE_HELPTEXT :Это значение определяет примерную относительную площадь суши, покрытой снегом в субарктическом климате.Наличие снега влияет на расстановку предприятий и на условия роста городов.{}Используется только при создании карты. Земля чуть выше уровня моря никогда не покрывается снегом. STR_CONFIG_SETTING_SNOW_COVERAGE_VALUE :{NUM}% STR_CONFIG_SETTING_DESERT_COVERAGE :Песчаное покрытие: {STRING} -STR_CONFIG_SETTING_DESERT_COVERAGE_HELPTEXT :Укажите приблизительное количество пустыни на тропическом ландшафте. Пустыня также влияет на промышленное производство. Используется только во время создания карты +STR_CONFIG_SETTING_DESERT_COVERAGE_HELPTEXT :Это значение определяет примерную относительную площадь суши, покрытой песком в тропическом климате. Пустыни влияют на расположение предприятий.{}Используется только при создании карты. STR_CONFIG_SETTING_DESERT_COVERAGE_VALUE :{NUM}% STR_CONFIG_SETTING_ROUGHNESS_OF_TERRAIN :Грубость ландшафта: {STRING} STR_CONFIG_SETTING_ROUGHNESS_OF_TERRAIN_HELPTEXT :(Только для TerraGenesis){}Выберите количество гор и холмов на карте. На гладком ландшафте холмов немного и они более пологие. На грубом - много гор, и ландшафт может показаться слишком однообразным. diff --git a/src/lang/serbian.txt b/src/lang/serbian.txt index e3b8dc012d..a9362e5710 100644 --- a/src/lang/serbian.txt +++ b/src/lang/serbian.txt @@ -1201,6 +1201,7 @@ STR_GAME_OPTIONS_VIDEO_ACCELERATION :{BLACK}Hardvers STR_GAME_OPTIONS_VIDEO_ACCELERATION_TOOLTIP :{BLACK}Označavanje ove opcije čini da OpenTTD pokuša da koristi hardversko ubrzanje. Promena ovog podešavanja će imati efekta tek nakon ponvnog pokretanja igre STR_GAME_OPTIONS_VIDEO_ACCELERATION_RESTART :{WHITE}Podešavanje će imati efekta tek nakon ponovnog pokretanja igre + STR_GAME_OPTIONS_GUI_ZOOM_FRAME :{BLACK}Veličina interfejsa STR_GAME_OPTIONS_GUI_ZOOM_DROPDOWN_TOOLTIP :{BLACK}Izaberite većinu elementa koja će se koristiti diff --git a/src/lang/simplified_chinese.txt b/src/lang/simplified_chinese.txt index 9c46f8d189..4f7aa144bc 100644 --- a/src/lang/simplified_chinese.txt +++ b/src/lang/simplified_chinese.txt @@ -1005,6 +1005,7 @@ STR_GAME_OPTIONS_VIDEO_ACCELERATION :{BLACK}硬件 STR_GAME_OPTIONS_VIDEO_ACCELERATION_TOOLTIP :{BLACK}点击该复选框,让 OpenTTD 尝试使用硬件加速。修改后的设置将在游戏重启后生效 STR_GAME_OPTIONS_VIDEO_ACCELERATION_RESTART :{WHITE}修改后的设置将在游戏重启后生效 + STR_GAME_OPTIONS_GUI_ZOOM_FRAME :{BLACK}界面大小 STR_GAME_OPTIONS_GUI_ZOOM_DROPDOWN_TOOLTIP :{BLACK}选择使用的界面元素大小 diff --git a/src/lang/slovak.txt b/src/lang/slovak.txt index 2b2fd5994e..683f7958c1 100644 --- a/src/lang/slovak.txt +++ b/src/lang/slovak.txt @@ -1074,6 +1074,7 @@ STR_GAME_OPTIONS_VIDEO_ACCELERATION :{BLACK}Hardvér STR_GAME_OPTIONS_VIDEO_ACCELERATION_TOOLTIP :{BLACK}Zaškrtnutím políčka dovolíte, aby sa OpenTTD pokúsilo použiť hardvérové zrýchlenie. Zmena nastavenia sa uplatní až po reštartovaní hry STR_GAME_OPTIONS_VIDEO_ACCELERATION_RESTART :{WHITE}Nastavenie sa uplatní až po reštartovaní hry + STR_GAME_OPTIONS_GUI_ZOOM_FRAME :{BLACK}Veľkosť rozhrania STR_GAME_OPTIONS_GUI_ZOOM_DROPDOWN_TOOLTIP :{BLACK}Výber veľkosti prvkov rozhrania diff --git a/src/lang/slovenian.txt b/src/lang/slovenian.txt index 6b854c34a2..1e05e9f290 100644 --- a/src/lang/slovenian.txt +++ b/src/lang/slovenian.txt @@ -1123,6 +1123,7 @@ STR_GAME_OPTIONS_RESOLUTION_TOOLTIP :{BLACK}Izberi l STR_GAME_OPTIONS_RESOLUTION_OTHER :drugo + STR_GAME_OPTIONS_GUI_ZOOM_FRAME :{BLACK}Velikost vmesnika STR_GAME_OPTIONS_GUI_ZOOM_DROPDOWN_TOOLTIP :{BLACK}Izberi velikost elementa vmesnika diff --git a/src/lang/spanish.txt b/src/lang/spanish.txt index 855546f875..28ec796454 100644 --- a/src/lang/spanish.txt +++ b/src/lang/spanish.txt @@ -1007,6 +1007,7 @@ STR_GAME_OPTIONS_VIDEO_ACCELERATION :{BLACK}Acelerac STR_GAME_OPTIONS_VIDEO_ACCELERATION_TOOLTIP :{BLACK}Marca esta casilla para permitir que OpenTTD intente usar aceleración por hardware. El cambio de configuración sólo tendrá efecto después de reiniciar el juego STR_GAME_OPTIONS_VIDEO_ACCELERATION_RESTART :{WHITE}El ajuste sólo tendrá efecto después de reiniciar el juego + STR_GAME_OPTIONS_GUI_ZOOM_FRAME :{BLACK}Tamaño de la interfaz STR_GAME_OPTIONS_GUI_ZOOM_DROPDOWN_TOOLTIP :{BLACK}Selecciona el tamaño de los elementos de la interfaz a usar diff --git a/src/lang/spanish_MX.txt b/src/lang/spanish_MX.txt index b3cada6dbd..8934a35209 100644 --- a/src/lang/spanish_MX.txt +++ b/src/lang/spanish_MX.txt @@ -1007,6 +1007,7 @@ STR_GAME_OPTIONS_VIDEO_ACCELERATION :{BLACK}Acelerac STR_GAME_OPTIONS_VIDEO_ACCELERATION_TOOLTIP :{BLACK}Activar esta casilla para intentar emplear la aceleración por hardware. Este cambio solo tiene efecto tras reiniciar el juego STR_GAME_OPTIONS_VIDEO_ACCELERATION_RESTART :{WHITE}Esta configuración solo tendrá efecto después de reiniciar el juego + STR_GAME_OPTIONS_GUI_ZOOM_FRAME :{BLACK}Tamaño de la interfaz STR_GAME_OPTIONS_GUI_ZOOM_DROPDOWN_TOOLTIP :{BLACK}Elegir el tamaño de los elementos de la interfaz diff --git a/src/lang/swedish.txt b/src/lang/swedish.txt index 4e2c8487de..77d5b6db7d 100644 --- a/src/lang/swedish.txt +++ b/src/lang/swedish.txt @@ -1006,6 +1006,7 @@ STR_GAME_OPTIONS_VIDEO_ACCELERATION :{BLACK}Hårdvar STR_GAME_OPTIONS_VIDEO_ACCELERATION_TOOLTIP :{BLACK}Markera denna rutan för att tillåta OpenTTD att försöka använda hårdvaruacceleration. Ändrad inställning kommer bara att gälla efter omstart. STR_GAME_OPTIONS_VIDEO_ACCELERATION_RESTART :{WHITE}Inställningen kommer bara att gälla efter omstart av spelet + STR_GAME_OPTIONS_GUI_ZOOM_FRAME :{BLACK}Gränssnittstorlek STR_GAME_OPTIONS_GUI_ZOOM_DROPDOWN_TOOLTIP :{BLACK}Välj vilken gränssnittsstorlek som ska användas diff --git a/src/lang/tamil.txt b/src/lang/tamil.txt index 94d0c64700..a919a195aa 100644 --- a/src/lang/tamil.txt +++ b/src/lang/tamil.txt @@ -964,6 +964,7 @@ STR_GAME_OPTIONS_RESOLUTION_TOOLTIP :{BLACK}தி STR_GAME_OPTIONS_RESOLUTION_OTHER :மற்றவை + STR_GAME_OPTIONS_GUI_ZOOM_FRAME :{BLACK}இடைமுக அளவு STR_GAME_OPTIONS_GUI_ZOOM_DROPDOWN_NORMAL :சராசரி diff --git a/src/lang/thai.txt b/src/lang/thai.txt index 12966b73ac..11a7f55b1b 100644 --- a/src/lang/thai.txt +++ b/src/lang/thai.txt @@ -960,6 +960,7 @@ STR_GAME_OPTIONS_RESOLUTION_TOOLTIP :{BLACK}เล STR_GAME_OPTIONS_RESOLUTION_OTHER :อื่นๆ + STR_GAME_OPTIONS_GUI_ZOOM_FRAME :{BLACK}ขนาดของแผงควบคุม diff --git a/src/lang/traditional_chinese.txt b/src/lang/traditional_chinese.txt index 410b6cd64e..d21dd76f19 100644 --- a/src/lang/traditional_chinese.txt +++ b/src/lang/traditional_chinese.txt @@ -974,6 +974,7 @@ STR_GAME_OPTIONS_RESOLUTION_TOOLTIP :{BLACK}選擇 STR_GAME_OPTIONS_RESOLUTION_OTHER :其它 + STR_GAME_OPTIONS_GUI_ZOOM_FRAME :{BLACK}介面大小 STR_GAME_OPTIONS_GUI_ZOOM_DROPDOWN_TOOLTIP :{BLACK}選擇使用的介面元素大小 diff --git a/src/lang/turkish.txt b/src/lang/turkish.txt index 5d135721ff..11f3042fbf 100644 --- a/src/lang/turkish.txt +++ b/src/lang/turkish.txt @@ -993,6 +993,7 @@ STR_GAME_OPTIONS_RESOLUTION_OTHER :diğer STR_GAME_OPTIONS_RESOLUTION_ITEM :{NUM}x{NUM} + STR_GAME_OPTIONS_GUI_ZOOM_FRAME :{BLACK}Arayüz boyutu STR_GAME_OPTIONS_GUI_ZOOM_DROPDOWN_TOOLTIP :{BLACK}Kullanmak üzere arayüz bileşen boyutunu seçin diff --git a/src/lang/ukrainian.txt b/src/lang/ukrainian.txt index f68ac35ffe..8c90c222b4 100644 --- a/src/lang/ukrainian.txt +++ b/src/lang/ukrainian.txt @@ -1134,6 +1134,7 @@ STR_GAME_OPTIONS_VIDEO_ACCELERATION :{BLACK}Прис STR_GAME_OPTIONS_VIDEO_ACCELERATION_TOOLTIP :{BLACK}Увімкнутий прапорець дозволить використання грою прискорення апаратного забезпечення. Налаштування запрацює тільки після перезапуску гри STR_GAME_OPTIONS_VIDEO_ACCELERATION_RESTART :{WHITE}Налаштування запрацює тільки після перезапуску гри + STR_GAME_OPTIONS_GUI_ZOOM_FRAME :{BLACK}Розмір інтерфейсу STR_GAME_OPTIONS_GUI_ZOOM_DROPDOWN_TOOLTIP :{BLACK}Оберіть розмір елементів інтерфейсу diff --git a/src/lang/unfinished/chuvash.txt b/src/lang/unfinished/chuvash.txt index e70e1b774c..6686a54277 100644 --- a/src/lang/unfinished/chuvash.txt +++ b/src/lang/unfinished/chuvash.txt @@ -520,6 +520,7 @@ STR_GAME_OPTIONS_RESOLUTION_OTHER :расна + # Custom currency window diff --git a/src/lang/unfinished/frisian.txt b/src/lang/unfinished/frisian.txt index dbbb6905ee..4344540a31 100644 --- a/src/lang/unfinished/frisian.txt +++ b/src/lang/unfinished/frisian.txt @@ -969,6 +969,7 @@ STR_GAME_OPTIONS_RESOLUTION_TOOLTIP :{BLACK}Selektea STR_GAME_OPTIONS_RESOLUTION_OTHER :oars + STR_GAME_OPTIONS_GUI_ZOOM_FRAME :{BLACK}Interfacegrutte STR_GAME_OPTIONS_GUI_ZOOM_DROPDOWN_NORMAL :Normaal diff --git a/src/lang/unfinished/ido.txt b/src/lang/unfinished/ido.txt index 0f7b1d1573..a98bbd7f26 100644 --- a/src/lang/unfinished/ido.txt +++ b/src/lang/unfinished/ido.txt @@ -484,6 +484,7 @@ STR_GAME_OPTIONS_RESOLUTION_OTHER :altra + # Custom currency window diff --git a/src/lang/unfinished/macedonian.txt b/src/lang/unfinished/macedonian.txt index a4614cb1f9..e0657af69c 100644 --- a/src/lang/unfinished/macedonian.txt +++ b/src/lang/unfinished/macedonian.txt @@ -804,6 +804,7 @@ STR_GAME_OPTIONS_AUTOSAVE_DROPDOWN_EVERY_1_MONTH :Секој ме + # Custom currency window diff --git a/src/lang/unfinished/maltese.txt b/src/lang/unfinished/maltese.txt index 7261c292f2..a30a718b7e 100644 --- a/src/lang/unfinished/maltese.txt +++ b/src/lang/unfinished/maltese.txt @@ -417,6 +417,7 @@ STR_GAME_OPTIONS_RESOLUTION_OTHER :oħrajn + # Custom currency window diff --git a/src/lang/unfinished/marathi.txt b/src/lang/unfinished/marathi.txt index f7717c2c0e..de8d4bec13 100644 --- a/src/lang/unfinished/marathi.txt +++ b/src/lang/unfinished/marathi.txt @@ -757,6 +757,7 @@ STR_GAME_OPTIONS_RESOLUTION_OTHER :अन्य + # Custom currency window diff --git a/src/lang/unfinished/persian.txt b/src/lang/unfinished/persian.txt index 049ba91447..715539577f 100644 --- a/src/lang/unfinished/persian.txt +++ b/src/lang/unfinished/persian.txt @@ -962,6 +962,7 @@ STR_GAME_OPTIONS_RESOLUTION_OTHER :دیگر + STR_GAME_OPTIONS_GUI_ZOOM_DROPDOWN_2X_ZOOM :دو برابر diff --git a/src/lang/unfinished/urdu.txt b/src/lang/unfinished/urdu.txt index fc4fe0191f..9c59f4b14e 100644 --- a/src/lang/unfinished/urdu.txt +++ b/src/lang/unfinished/urdu.txt @@ -942,6 +942,7 @@ STR_GAME_OPTIONS_RESOLUTION_OTHER :دیگر + STR_GAME_OPTIONS_BASE_GRF :{BLACK}بُنیادی گرافک سیٹ STR_GAME_OPTIONS_BASE_GRF_TOOLTIP :{BLACK}بُنیادی گرافک سیٹ اختیار کریں STR_GAME_OPTIONS_BASE_GRF_STATUS :{RED}{NUM} missing/corrupted file{P "" s} diff --git a/src/lang/vietnamese.txt b/src/lang/vietnamese.txt index 4b51cb67a9..5a94da6acf 100644 --- a/src/lang/vietnamese.txt +++ b/src/lang/vietnamese.txt @@ -1006,6 +1006,7 @@ STR_GAME_OPTIONS_VIDEO_ACCELERATION :{BLACK}Tăng t STR_GAME_OPTIONS_VIDEO_ACCELERATION_TOOLTIP :{BLACK}Đánh dấu vào ô này để cho phép OpenTTD thử sử dụng tăng tốc phần cứng. Sẽ có tác dụng sau khi khởi động lại trò chơi STR_GAME_OPTIONS_VIDEO_ACCELERATION_RESTART :{WHITE}Thiết lập chỉ có tác dụng sau khi khởi động lại trò chơi + STR_GAME_OPTIONS_GUI_ZOOM_FRAME :{BLACK}Kích thước giao diện STR_GAME_OPTIONS_GUI_ZOOM_DROPDOWN_TOOLTIP :{BLACK}Chọn kích thước của các đối tượng trên giao diện diff --git a/src/lang/welsh.txt b/src/lang/welsh.txt index 0d534bfb8e..12bbec9bd4 100644 --- a/src/lang/welsh.txt +++ b/src/lang/welsh.txt @@ -972,6 +972,7 @@ STR_GAME_OPTIONS_RESOLUTION_TOOLTIP :{BLACK}Dewiswch STR_GAME_OPTIONS_RESOLUTION_OTHER :arall + STR_GAME_OPTIONS_GUI_ZOOM_FRAME :{BLACK}Maint rhyngwyneb STR_GAME_OPTIONS_GUI_ZOOM_DROPDOWN_TOOLTIP :{BLACK}Dewis maint yr elfennau rhyngwyneb i'w defnyddio From e722ea89f0ebda60fd251ee2ba33635df367acd4 Mon Sep 17 00:00:00 2001 From: translators Date: Mon, 12 Apr 2021 17:51:14 +0000 Subject: [PATCH 062/268] Update: Translations from eints norwegian (bokmal): 2 changes by Anolitt english (us): 2 changes by 2TallTyler korean: 3 changes by telk5093 german: 2 changes by danidoedel romanian: 35 changes by kneekoo finnish: 2 changes by hpiirai spanish: 4 changes by MontyMontana french: 3 changes by glx22 portuguese: 4 changes by azulcosta --- src/lang/english_US.txt | 2 ++ src/lang/finnish.txt | 2 ++ src/lang/french.txt | 4 ++- src/lang/german.txt | 2 ++ src/lang/korean.txt | 4 ++- src/lang/norwegian_bokmal.txt | 2 ++ src/lang/portuguese.txt | 6 +++-- src/lang/romanian.txt | 51 ++++++++++++++++++++++++----------- src/lang/spanish.txt | 6 +++-- 9 files changed, 57 insertions(+), 22 deletions(-) diff --git a/src/lang/english_US.txt b/src/lang/english_US.txt index c9763aeeb0..eb32fbc55e 100644 --- a/src/lang/english_US.txt +++ b/src/lang/english_US.txt @@ -1006,6 +1006,8 @@ STR_GAME_OPTIONS_VIDEO_ACCELERATION :{BLACK}Hardware STR_GAME_OPTIONS_VIDEO_ACCELERATION_TOOLTIP :{BLACK}Check this box to allow OpenTTD to try to use hardware acceleration. A changed setting will only be applied upon game restart STR_GAME_OPTIONS_VIDEO_ACCELERATION_RESTART :{WHITE}The setting will only take effect after a game restart +STR_GAME_OPTIONS_VIDEO_VSYNC :{BLACK}VSync +STR_GAME_OPTIONS_VIDEO_VSYNC_TOOLTIP :{BLACK}Check this box to v-sync the screen. A changed setting will only be applied upon game restart. Only works with hardware acceleration enabled STR_GAME_OPTIONS_GUI_ZOOM_FRAME :{BLACK}Interface size STR_GAME_OPTIONS_GUI_ZOOM_DROPDOWN_TOOLTIP :{BLACK}Select the interface element size to use diff --git a/src/lang/finnish.txt b/src/lang/finnish.txt index 00173ff33e..3fea792d3c 100644 --- a/src/lang/finnish.txt +++ b/src/lang/finnish.txt @@ -1006,6 +1006,8 @@ STR_GAME_OPTIONS_VIDEO_ACCELERATION :{BLACK}Laitteis STR_GAME_OPTIONS_VIDEO_ACCELERATION_TOOLTIP :{BLACK}Valitse tämä, jos haluat, että OpenTTD yrittää käyttää laitteistokiihdytystä. Muutettu asetus tulee voimaan vasta pelin uudelleenkäynnistyksen jälkeen. STR_GAME_OPTIONS_VIDEO_ACCELERATION_RESTART :{WHITE}Asetus tulee voimaan vasta pelin uudelleenkäynnistyksen jälkeen +STR_GAME_OPTIONS_VIDEO_VSYNC :{BLACK}Pystytahdistus +STR_GAME_OPTIONS_VIDEO_VSYNC_TOOLTIP :{BLACK}Valitse tämä ottaaksesi käyttöön näytön pystytahdistuksen. Muutettu asetus tulee voimaan vasta pelin uudelleenkäynnistyksen jälkeen. Edellyttää, että laitteistokiihdytys on käytössä. STR_GAME_OPTIONS_GUI_ZOOM_FRAME :{BLACK}Käyttöliittymän koko STR_GAME_OPTIONS_GUI_ZOOM_DROPDOWN_TOOLTIP :{BLACK}Valitse käyttöliittymäelementtien koko diff --git a/src/lang/french.txt b/src/lang/french.txt index 80ddd9833d..cf6763c8c1 100644 --- a/src/lang/french.txt +++ b/src/lang/french.txt @@ -1007,6 +1007,8 @@ STR_GAME_OPTIONS_VIDEO_ACCELERATION :{BLACK} Accél STR_GAME_OPTIONS_VIDEO_ACCELERATION_TOOLTIP :{BLACK}Cochez cette case permet à OpenTTD d'utiliser l'accélération matérielle, si possible. Un paramètre modifié ne sera pris en compte qu'au redémarrage du jeu STR_GAME_OPTIONS_VIDEO_ACCELERATION_RESTART :{WHITE}Les paramètres ne prendront effet qu'après le redémarrage du jeu +STR_GAME_OPTIONS_VIDEO_VSYNC :{BLACK}VSync +STR_GAME_OPTIONS_VIDEO_VSYNC_TOOLTIP :{BLACK}Cochez cette case pour activer la synchronisation verticale de l'écran. La modification de ce paramètres ne sera effective qu'après le redémarrage du jeu. Fonctionne uniquement si l’accélération matérielle est active STR_GAME_OPTIONS_GUI_ZOOM_FRAME :{BLACK}Taille d'interface STR_GAME_OPTIONS_GUI_ZOOM_DROPDOWN_TOOLTIP :{BLACK}Choisir la taille d'élément d'interface à utiliser @@ -3441,7 +3443,7 @@ STR_COMPANY_VIEW_RELOCATE_HQ :{BLACK}Déména STR_COMPANY_VIEW_RELOCATE_COMPANY_HEADQUARTERS :{BLACK}Reconstruire ailleurs le siège de la compagnie pour le prix de 1{NBSP}% de sa valeur.{}Shift-clic pour afficher seulement le coût estimé. STR_COMPANY_VIEW_INFRASTRUCTURE_BUTTON :{BLACK}Détails STR_COMPANY_VIEW_INFRASTRUCTURE_TOOLTIP :{BLACK}Afficher le détail des calculs d'infrastructure -STR_COMPANY_VIEW_GIVE_MONEY_BUTTON :Donner de l’argent +STR_COMPANY_VIEW_GIVE_MONEY_BUTTON :{BLACK}Donner de l’argent STR_COMPANY_VIEW_GIVE_MONEY_TOOLTIP :{BLACK}Donner de l’argent à cette compagnie STR_COMPANY_VIEW_NEW_FACE_BUTTON :{BLACK}Nouveau visage diff --git a/src/lang/german.txt b/src/lang/german.txt index ea70ed2f2b..6fcc09166b 100644 --- a/src/lang/german.txt +++ b/src/lang/german.txt @@ -1007,6 +1007,8 @@ STR_GAME_OPTIONS_VIDEO_ACCELERATION :{BLACK}Hardware STR_GAME_OPTIONS_VIDEO_ACCELERATION_TOOLTIP :{BLACK}Dieses Kästchen ankreuzen, um OpenTTD zu erlauben, die Hardwarebeschleunigung zu verwenden. Eine geänderte Einstellung wird nur beim Spielneustart wirksam STR_GAME_OPTIONS_VIDEO_ACCELERATION_RESTART :{WHITE}Die Einstellung tritt nur nach einem Neustart des Spiels in Kraft +STR_GAME_OPTIONS_VIDEO_VSYNC :{BLACK}VSync +STR_GAME_OPTIONS_VIDEO_VSYNC_TOOLTIP :{BLACK}Dieses Kästchen ankreuzen, um VSync zu aktivieren. Eine geänderte Einstellung wird nur beim Spielneustart wirksam. Funktioniert nur mit aktivierter Hardwarebeschleunigung STR_GAME_OPTIONS_GUI_ZOOM_FRAME :{BLACK}Größe der Bedienelemente STR_GAME_OPTIONS_GUI_ZOOM_DROPDOWN_TOOLTIP :{BLACK}Wähle die Größe der Bedienelemente diff --git a/src/lang/korean.txt b/src/lang/korean.txt index a0190dd42f..2f8d593cde 100644 --- a/src/lang/korean.txt +++ b/src/lang/korean.txt @@ -1004,9 +1004,11 @@ STR_GAME_OPTIONS_RESOLUTION_OTHER :기타 STR_GAME_OPTIONS_RESOLUTION_ITEM :{NUM}x{NUM} STR_GAME_OPTIONS_VIDEO_ACCELERATION :{BLACK}하드웨어 가속 -STR_GAME_OPTIONS_VIDEO_ACCELERATION_TOOLTIP :{BLACK}OpenTTD가 하드웨어 가속을 사용하게 하려면 체크하세요. 변경된 설정은 게임을 재시작한 뒤에 적용됩니다. +STR_GAME_OPTIONS_VIDEO_ACCELERATION_TOOLTIP :{BLACK}OpenTTD가 하드웨어 가속을 사용하게 하려면 체크하세요. 변경한 설정은 게임을 재시작한 뒤에 적용될 것입니다. STR_GAME_OPTIONS_VIDEO_ACCELERATION_RESTART :{WHITE}이 설정은 게임을 재시작한 뒤에 적용될 것입니다 +STR_GAME_OPTIONS_VIDEO_VSYNC :{BLACK}수직 동기화 +STR_GAME_OPTIONS_VIDEO_VSYNC_TOOLTIP :{BLACK}화면과 수직 동기화하려면 체크하세요. 변경한 설정은 게임을 재시작한 뒤에 적용될 것입니다. 하드웨어 가속을 켠 경우에만 작동합니다. STR_GAME_OPTIONS_GUI_ZOOM_FRAME :{BLACK}인터페이스 크기 STR_GAME_OPTIONS_GUI_ZOOM_DROPDOWN_TOOLTIP :{BLACK}인터페이스의 크기를 선택합니다. diff --git a/src/lang/norwegian_bokmal.txt b/src/lang/norwegian_bokmal.txt index a32514a115..568f45972a 100644 --- a/src/lang/norwegian_bokmal.txt +++ b/src/lang/norwegian_bokmal.txt @@ -1008,6 +1008,8 @@ STR_GAME_OPTIONS_VIDEO_ACCELERATION :{BLACK}Maskinva STR_GAME_OPTIONS_VIDEO_ACCELERATION_TOOLTIP :{BLACK}Merk av i denne boksen for å la OpenTTD prøve å bruke maskinvareakselerasjon. En endret innstilling blir bare brukt ved omstart av spillet STR_GAME_OPTIONS_VIDEO_ACCELERATION_RESTART :{WHITE}Innstillingen vil ikke tre i kraft før spillet er restartet +STR_GAME_OPTIONS_VIDEO_VSYNC :{BLACK}VSync +STR_GAME_OPTIONS_VIDEO_VSYNC_TOOLTIP :{BLACK}Merk av i denne boksen for å v-synkronisere skjermen. Endring av innstillinger krever omstart av spillet. Fungerer bare med maskinvareakselerasjon aktivert STR_GAME_OPTIONS_GUI_ZOOM_FRAME :{BLACK}Grensesnitt-størrelse STR_GAME_OPTIONS_GUI_ZOOM_DROPDOWN_TOOLTIP :{BLACK}Velg grensesnitt-størrelsen som skal benyttes diff --git a/src/lang/portuguese.txt b/src/lang/portuguese.txt index 431a69f809..d82a547295 100644 --- a/src/lang/portuguese.txt +++ b/src/lang/portuguese.txt @@ -1007,6 +1007,8 @@ STR_GAME_OPTIONS_VIDEO_ACCELERATION :{BLACK}Acelera STR_GAME_OPTIONS_VIDEO_ACCELERATION_TOOLTIP :{BLACK}Marque esta caixa para permitir que o OpenTTD tente usar a aceleração por hardware. Uma configuração alterada só será aplicada após reiniciar o jogo STR_GAME_OPTIONS_VIDEO_ACCELERATION_RESTART :{WHITE}A definição só terá efeito após reiniciar o jogo +STR_GAME_OPTIONS_VIDEO_VSYNC :{BLACK}VSync +STR_GAME_OPTIONS_VIDEO_VSYNC_TOOLTIP :{BLACK}Marque esta caixa para ativar "V-Sync" no ecrã. Uma configuração alterada só terá efeito quando reiniciar o jogo. Só funciona com a aceleração por hardware ativada STR_GAME_OPTIONS_GUI_ZOOM_FRAME :{BLACK}Tamanho interface STR_GAME_OPTIONS_GUI_ZOOM_DROPDOWN_TOOLTIP :{BLACK}Seleccionar tamanho do elemento de interface a usar @@ -1270,7 +1272,7 @@ STR_CONFIG_SETTING_SHOWFINANCES_HELPTEXT :Se ativado, os STR_CONFIG_SETTING_NONSTOP_BY_DEFAULT :Novas ordens são 'sem parar' por predefinição: {STRING} STR_CONFIG_SETTING_NONSTOP_BY_DEFAULT_HELPTEXT :Normalmente, um veículo para em cada estação por onde passa. Ao ativar esta configuração, um veículo irá passar por todas as estações no seu percurso, parando apenas no destino final. Esta opção só tem efeito para novas rotas, mas as rotas existentes podem ser alteradas para funcionarem de forma igual. STR_CONFIG_SETTING_STOP_LOCATION :Ordens novas do comboio param {STRING} da plataforma -STR_CONFIG_SETTING_STOP_LOCATION_HELPTEXT :Local onde um combóio parará na plataforma por omissão. A opção 'extremo mais próximo' significa perto do ponto de entrada, 'meio' significa no meio da plataforma e 'extremo mais distante' significa o mais distante possível do ponto de entrada. Esta opção apenas afecta o valor por omissão para novas encomendas. Encomendas individuais podem utilizar qualquer uma das opções independentemente desta +STR_CONFIG_SETTING_STOP_LOCATION_HELPTEXT :Local de paragem do comboio nas plataformas por omissão. A opção 'extremo mais próximo' significa perto do ponto de entrada, 'meio' significa no meio da plataforma e 'extremo mais distante' significa o mais distante possível do ponto de entrada. Esta opção apenas afeta o valor por omissão para novas ordens. Ordens individuais podem utilizar qualquer uma das opções independentemente desta STR_CONFIG_SETTING_STOP_LOCATION_NEAR_END :no extremo perto STR_CONFIG_SETTING_STOP_LOCATION_MIDDLE :no meio STR_CONFIG_SETTING_STOP_LOCATION_FAR_END :no extremo longe @@ -1317,7 +1319,7 @@ STR_CONFIG_SETTING_NEVER_EXPIRE_AIRPORTS_HELPTEXT :Activar esta pr STR_CONFIG_SETTING_WARN_LOST_VEHICLE :Avisar se o veículo está perdido: {STRING} STR_CONFIG_SETTING_WARN_LOST_VEHICLE_HELPTEXT :Mostrar mensagens sobre veículos que não conseguem encontrar o caminho para o seu próximo destino. STR_CONFIG_SETTING_ORDER_REVIEW :Analisar ordens dos veículos: {STRING} -STR_CONFIG_SETTING_ORDER_REVIEW_HELPTEXT :Quando activo, as encomendas dos veículos são periodicamente revistas e algumas falhas óbvias são anunciadas através de notícias, quando detectadas +STR_CONFIG_SETTING_ORDER_REVIEW_HELPTEXT :Quando ativo, as ordens dos veículos são periodicamente revistas e algumas falhas óbvias são anunciadas através de notícias, quando detetadas STR_CONFIG_SETTING_ORDER_REVIEW_OFF :Não STR_CONFIG_SETTING_ORDER_REVIEW_EXDEPOT :Sim, mas excluir veículos parados STR_CONFIG_SETTING_ORDER_REVIEW_ON :De todos os veículos diff --git a/src/lang/romanian.txt b/src/lang/romanian.txt index ebe7bd4f89..002f7500ae 100644 --- a/src/lang/romanian.txt +++ b/src/lang/romanian.txt @@ -356,7 +356,7 @@ STR_SCENEDIT_FILE_MENU_SAVE_SCENARIO :Salvează scena STR_SCENEDIT_FILE_MENU_LOAD_SCENARIO :Încarcă scenariu STR_SCENEDIT_FILE_MENU_SAVE_HEIGHTMAP :Salvează harta înălţimilor STR_SCENEDIT_FILE_MENU_LOAD_HEIGHTMAP :Încarcă harta de înălţimi -STR_SCENEDIT_FILE_MENU_QUIT_EDITOR :Ieşire din editorul de scenarii +STR_SCENEDIT_FILE_MENU_QUIT_EDITOR :Ieșire din editorul de scenarii STR_SCENEDIT_FILE_MENU_SEPARATOR : STR_SCENEDIT_FILE_MENU_QUIT :Ieşire din joc ############ range for SE file menu starts @@ -381,7 +381,7 @@ STR_SETTINGS_MENU_TRANSPARENT_SIGNS :Nume staţii/se ############ range for file menu starts STR_FILE_MENU_SAVE_GAME :Salvează jocul STR_FILE_MENU_LOAD_GAME :Încarcă joc -STR_FILE_MENU_QUIT_GAME :Ieşire în meniul principal +STR_FILE_MENU_QUIT_GAME :Ieșire în meniul principal STR_FILE_MENU_SEPARATOR : STR_FILE_MENU_EXIT :Ieşire din joc ############ range ends here @@ -637,6 +637,7 @@ STR_MUSIC_EFFECTS_VOLUME :{TINY_FONT}{BLA STR_MUSIC_TRACK_NONE :{TINY_FONT}{DKGREEN}-- STR_MUSIC_TRACK_DIGIT :{TINY_FONT}{DKGREEN}{ZEROFILL_NUM} STR_MUSIC_TITLE_NONE :{TINY_FONT}{DKGREEN}------ +STR_MUSIC_TITLE_NOMUSIC :{TINY_FONT}{DKGREEN}Nu există muzică disponibilă STR_MUSIC_TITLE_NAME :{TINY_FONT}{DKGREEN}"{STRING}" STR_MUSIC_TRACK :{TINY_FONT}{BLACK}Piesa STR_MUSIC_XTITLE :{TINY_FONT}{BLACK}Titlul @@ -972,6 +973,7 @@ STR_GAME_OPTIONS_RESOLUTION_TOOLTIP :{BLACK}Alege re STR_GAME_OPTIONS_RESOLUTION_OTHER :(alta/nespecificată) STR_GAME_OPTIONS_RESOLUTION_ITEM :{NUM}x{NUM} +STR_GAME_OPTIONS_VIDEO_ACCELERATION :{BLACK}Accelerare hardware STR_GAME_OPTIONS_GUI_ZOOM_FRAME :{BLACK}Mărime interfată @@ -989,6 +991,7 @@ STR_GAME_OPTIONS_FONT_ZOOM_DROPDOWN_2X_ZOOM :Mărime dublă STR_GAME_OPTIONS_REFRESH_RATE_TOOLTIP :{BLACK}Alegeți rata de reîmprospătare dorită +STR_GAME_OPTIONS_REFRESH_RATE_ITEM :{NUM}Hz STR_GAME_OPTIONS_BASE_GRF :{BLACK}Set grafic de bază STR_GAME_OPTIONS_BASE_GRF_TOOLTIP :{BLACK}Selectează setul grafic de bază utilizat în joc @@ -1162,7 +1165,8 @@ STR_CONFIG_SETTING_DISASTERS_HELPTEXT :Comută dezastr STR_CONFIG_SETTING_CITY_APPROVAL :Atitudinea consiliului orașului cu privire la restructurarea zonei: {STRING} STR_CONFIG_SETTING_CITY_APPROVAL_HELPTEXT :Alege în ce măsură poluarea fonică si deranjamentul local provocat de o companie va afecta impresia orașului despre aceasta, si viitoarele planuri de construcție in zonă -STR_CONFIG_SETTING_TOO_HIGH_MOUNTAIN :{WHITE}Nu poţi seta înălţimea maxima a hărţii la aceasta valoare. Cel puţin un munte pe hartă are o înălţime mai mare. +STR_CONFIG_SETTING_MAP_HEIGHT_LIMIT :Înălțimea limită a hărții: {STRING} +STR_CONFIG_SETTING_TOO_HIGH_MOUNTAIN :{WHITE}Nu poți seta înălțimea maximă a hărții la această valoare. Cel puțin un munte de pe hartă este mai înalt de-atât. STR_CONFIG_SETTING_AUTOSLOPE :Permite terra-formarea sub clădiri, şine, etc. (auto-pante): {STRING} STR_CONFIG_SETTING_AUTOSLOPE_HELPTEXT :Permite terraformarea sub clădiri şi şine fără eliminarea acestora STR_CONFIG_SETTING_CATCHMENT :Permite arii de cuprindere mai realiste: {STRING} @@ -1176,7 +1180,7 @@ STR_CONFIG_SETTING_SMOKE_AMOUNT :Cantitatea de f STR_CONFIG_SETTING_SMOKE_AMOUNT_HELPTEXT :Configurează cât de mult fum sau cât de multe scântei sunt emise de vehicule STR_CONFIG_SETTING_TRAIN_ACCELERATION_MODEL :Modelul de acceleraţie al trenurilor: {STRING} STR_CONFIG_SETTING_TRAIN_ACCELERATION_MODEL_HELPTEXT :Selectează modelul fizic pentru accelerarea trenurilor. Modelul "original" penalizează pantele în mod egal pentru toate vehiculele. Modelul "realistic" penalizează pantele şi curbele în funcţie de mai mulţi parametrii, cum ar fi lungimea şi efortul tractor -STR_CONFIG_SETTING_ROAD_VEHICLE_ACCELERATION_MODEL :Modelul de acceleraţie al vehiculelor rutiere: {STRING} +STR_CONFIG_SETTING_ROAD_VEHICLE_ACCELERATION_MODEL :Modelul de accelerație al vehiculelor rutiere: {STRING} STR_CONFIG_SETTING_ROAD_VEHICLE_ACCELERATION_MODEL_HELPTEXT :Selectează modelul fizic pentru accelerarea autovehiculelor. Modelul "original" penalizează pantele în mod egal pentru toate autovehiculele. Modelul "realistic" penalizează pantele şi curbele în funcţie de mai mulţi parametrii ai motorului, cum ar fi efortul tractor STR_CONFIG_SETTING_TRAIN_SLOPE_STEEPNESS :Înclinarea pantelor pentru trenuri: {STRING} STR_CONFIG_SETTING_TRAIN_SLOPE_STEEPNESS_HELPTEXT :Înclinarea unui pătrăţel de pantă pentru trenuri. O valoare mai mare face urcarea mai dificilă @@ -1191,8 +1195,8 @@ STR_CONFIG_SETTING_INFLATION :Inflaţia: {STR STR_CONFIG_SETTING_INFLATION_HELPTEXT :Activează inflaţia în economie, unde costurile cresc ceva mai rapid decât plăţile STR_CONFIG_SETTING_MAX_BRIDGE_LENGTH :Lungimea maximă a podurilor: {STRING} STR_CONFIG_SETTING_MAX_BRIDGE_LENGTH_HELPTEXT :Lungimea maximă pentru construcţia de poduri -STR_CONFIG_SETTING_MAX_BRIDGE_HEIGHT :Întăltimea maximă a podurilor: {STRING} -STR_CONFIG_SETTING_MAX_BRIDGE_HEIGHT_HELPTEXT :Întăltimea maximă pentru construcţia de poduri +STR_CONFIG_SETTING_MAX_BRIDGE_HEIGHT :Înălțimea maximă a podurilor: {STRING} +STR_CONFIG_SETTING_MAX_BRIDGE_HEIGHT_HELPTEXT :Înălțimea maximă pentru construcția de poduri STR_CONFIG_SETTING_MAX_TUNNEL_LENGTH :Lungimea maximă a tunelurilor: {STRING} STR_CONFIG_SETTING_MAX_TUNNEL_LENGTH_HELPTEXT :Lungimea maximă pentru construcţia de tuneluri STR_CONFIG_SETTING_RAW_INDUSTRY_CONSTRUCTION_METHOD :Metoda manuală de construcţie a industriilor primare: {STRING} @@ -1298,7 +1302,9 @@ STR_CONFIG_SETTING_INDUSTRY_DENSITY_HELPTEXT :Stabilește câ STR_CONFIG_SETTING_OIL_REF_EDGE_DISTANCE :Distanța maximă de la marginea hărții pentru rafinării: {STRING} STR_CONFIG_SETTING_OIL_REF_EDGE_DISTANCE_HELPTEXT :Rafinăriile de petrol vor fi construite doar la marginea hărţii, sau pe coastă, în cazul harţilor insulare STR_CONFIG_SETTING_SNOWLINE_HEIGHT :Grosimea stratului de zăpadă: {STRING} -STR_CONFIG_SETTING_SNOWLINE_HEIGHT_HELPTEXT :Controlează înălțimea de la care zăpada apare în peisajul sub-arctic. De asemenea, zăpada afectează generarea industriilor și cerințele de creștere a orașelor. +STR_CONFIG_SETTING_SNOWLINE_HEIGHT_HELPTEXT :Controlează înălțimea de la care zăpada apare în peisajul sub-arctic. Zăpada afectează și generarea industriilor și cerințele de creștere a orașelor. Se poate modifica doar prin Editorul de scenarii sau este calculat prin „acoperirea cu zăpadă” +STR_CONFIG_SETTING_SNOW_COVERAGE :Acoperire cu zăpadă: {STRING} +STR_CONFIG_SETTING_SNOW_COVERAGE_VALUE :{NUM}% STR_CONFIG_SETTING_DESERT_COVERAGE :Acoperire cu deșert: {STRING} STR_CONFIG_SETTING_DESERT_COVERAGE_HELPTEXT :Controlează întinderea aproximativă de deșert din peisajul tropical. Deșert afectează și generarea industriilor. Parametrul se folosește doar la generarea hărții STR_CONFIG_SETTING_DESERT_COVERAGE_VALUE :{NUM}% @@ -1338,6 +1344,7 @@ STR_CONFIG_SETTING_SMALLMAP_LAND_COLOUR_DARK_GREEN :Verde închis STR_CONFIG_SETTING_SMALLMAP_LAND_COLOUR_VIOLET :Mov STR_CONFIG_SETTING_SCROLLMODE_HELPTEXT :Comportamentul derulării hărții STR_CONFIG_SETTING_SCROLLMODE_RMB_LOCKED :Mută harta ținând apăsat click dreapta, poziția cursorului rămânând fixă +STR_CONFIG_SETTING_SCROLLMODE_RMB :Mută harta cu clic dreapta STR_CONFIG_SETTING_SCROLLMODE_LMB :Mută harta cu clic stânga STR_CONFIG_SETTING_SMOOTH_SCROLLING :Derulare uşoară ecran: {STRING} STR_CONFIG_SETTING_SMOOTH_SCROLLING_HELPTEXT :Controlează modul de deplasare a imaginii din ecranul principal când se face click pe harta mică sau când se execută o comandă de deplasare către un obiect anume de pe hartă. Dacă este activată, imaginea se deplasează în mod fluid, altfel imaginea sare direct la zona dorită @@ -1576,6 +1583,7 @@ STR_CONFIG_SETTING_TOWN_CARGOGENMODE_BITCOUNT :Liniar STR_CONFIG_SETTING_EXTRA_TREE_PLACEMENT :Poziţionarea copacilor în joc: {STRING} STR_CONFIG_SETTING_EXTRA_TREE_PLACEMENT_HELPTEXT :Controlează apariția aleatoare a copacilor în joc. Este posibil ca această opțiune să afecteze industrii care depind de creșterea copacilor, cum ar fi fabricile de cherestea STR_CONFIG_SETTING_EXTRA_TREE_PLACEMENT_NO_SPREAD :Cresc dar nu se extind {RED}(strică fabrica de cherestea) +STR_CONFIG_SETTING_EXTRA_TREE_PLACEMENT_SPREAD_RAINFOREST :Cresc dar se extind doar în păduri tropicale STR_CONFIG_SETTING_EXTRA_TREE_PLACEMENT_SPREAD_ALL :Cresc și se extind peste tot STR_CONFIG_SETTING_TOOLBAR_POS :Poziţia barei principale de instrumente: {STRING} @@ -1597,6 +1605,7 @@ STR_CONFIG_SETTING_ZOOM_LVL_NORMAL :Normal STR_CONFIG_SETTING_ZOOM_LVL_OUT_2X :x2 STR_CONFIG_SETTING_ZOOM_LVL_OUT_4X :x4 STR_CONFIG_SETTING_ZOOM_LVL_OUT_8X :x8 +STR_CONFIG_SETTING_SPRITE_ZOOM_LVL_IN_2X :2x STR_CONFIG_SETTING_TOWN_GROWTH :Viteza de dezvoltare a oraşului: {STRING} STR_CONFIG_SETTING_TOWN_GROWTH_HELPTEXT :Viteza creşterii oraşelor STR_CONFIG_SETTING_TOWN_GROWTH_NONE :Deloc @@ -1771,9 +1780,9 @@ STR_QUIT_YES :{BLACK}Da STR_QUIT_NO :{BLACK}Nu # Abandon game -STR_ABANDON_GAME_CAPTION :{WHITE}Ieşire din joc -STR_ABANDON_GAME_QUERY :{YELLOW}Eşti sigur că vrei să renunţi la acest joc? -STR_ABANDON_SCENARIO_QUERY :{YELLOW}Eşti sigur că vrei să renunţi la acest scenariu? +STR_ABANDON_GAME_CAPTION :{WHITE}Ieșire din joc +STR_ABANDON_GAME_QUERY :{YELLOW}Sigur vrei să renunți la acest joc? +STR_ABANDON_SCENARIO_QUERY :{YELLOW}Sigur vrei să renunți la acest scenariu? # Cheat window STR_CHEATS :{WHITE}Cheat-uri @@ -1783,8 +1792,8 @@ STR_CHEAT_CHANGE_COMPANY :{LTBLUE}Joacă STR_CHEAT_EXTRA_DYNAMITE :{LTBLUE}Buldozer magic (demolează industrii şi lucruri amovibile): {ORANGE}{STRING} STR_CHEAT_CROSSINGTUNNELS :{LTBLUE}Tunelele se pot intersecta: {ORANGE}{STRING} STR_CHEAT_NO_JETCRASH :{LTBLUE}Avioanele cu reacţie nu se vor prăbuşi (frecvent) pe aeroporturile mici: {ORANGE}{STRING} -STR_CHEAT_EDIT_MAX_HL :{LTBLUE}Schimbă înălţimea maximă a harţii: {ORANGE}{NUM} -STR_CHEAT_EDIT_MAX_HL_QUERY_CAPT :{WHITE}Schimbă înălţimea maxima a munţilor pe hartă +STR_CHEAT_EDIT_MAX_HL :{LTBLUE}Schimbă înălțimea maximă a hărții: {ORANGE}{NUM} +STR_CHEAT_EDIT_MAX_HL_QUERY_CAPT :{WHITE}Schimbă înălțimea maximă a munților pe hartă STR_CHEAT_SWITCH_CLIMATE_TEMPERATE_LANDSCAPE :peisajul temperat STR_CHEAT_SWITCH_CLIMATE_SUB_ARCTIC_LANDSCAPE :peisajul sub-arctic STR_CHEAT_SWITCH_CLIMATE_SUB_TROPICAL_LANDSCAPE :peisajul sub-tropical @@ -1929,8 +1938,10 @@ STR_NETWORK_SERVER_LIST_JOIN_GAME :{BLACK}Intră STR_NETWORK_SERVER_LIST_REFRESH :{BLACK}Actualizează serverul STR_NETWORK_SERVER_LIST_REFRESH_TOOLTIP :{BLACK}Actualizează informaţiile despre server +STR_NETWORK_SERVER_LIST_SEARCH_SERVER_INTERNET :{BLACK}Caută pe internet STR_NETWORK_SERVER_LIST_SEARCH_SERVER_INTERNET_TOOLTIP :{BLACK}Caută servere publice în internet STR_NETWORK_SERVER_LIST_SEARCH_SERVER_LAN :{BLACK}Caută în LAN +STR_NETWORK_SERVER_LIST_SEARCH_SERVER_LAN_TOOLTIP :{BLACK}Caută servere în rețeaua locală STR_NETWORK_SERVER_LIST_ADD_SERVER :{BLACK}Adaugă un server STR_NETWORK_SERVER_LIST_ADD_SERVER_TOOLTIP :{BLACK}Adaugă un server la lista care va fi verificată pentru jocuri active STR_NETWORK_SERVER_LIST_START_SERVER :{BLACK}Porneşte serverul @@ -2541,6 +2552,7 @@ STR_FUND_INDUSTRY_INDUSTRY_BUILD_COST :{BLACK}Cost: {Y STR_FUND_INDUSTRY_PROSPECT_NEW_INDUSTRY :{BLACK}Prospectează STR_FUND_INDUSTRY_BUILD_NEW_INDUSTRY :{BLACK}Construieşte STR_FUND_INDUSTRY_FUND_NEW_INDUSTRY :{BLACK}Finanţează +STR_FUND_INDUSTRY_REMOVE_ALL_INDUSTRIES :{BLACK}Elimină toate industriile # Industry cargoes window STR_INDUSTRY_CARGOES_INDUSTRY_CAPTION :{WHITE}Lanţ industrial pentru industria {STRING} @@ -2561,6 +2573,7 @@ STR_INDUSTRY_CARGOES_SELECT_INDUSTRY_TOOLTIP :{BLACK}Alege in # Land area window STR_LAND_AREA_INFORMATION_CAPTION :{WHITE}Informaţii teren +STR_LAND_AREA_INFORMATION_LOCATION_TOOLTIP :{BLACK}Centrează vizorul principal pe locația dalei. Ctrl+clic deschide un vizor nou pe locația dalei STR_LAND_AREA_INFORMATION_COST_TO_CLEAR_N_A :{BLACK}Costul demolării: {LTBLUE}nu este cazul STR_LAND_AREA_INFORMATION_COST_TO_CLEAR :{BLACK}Costul demolării: {RED}{CURRENCY_LONG} STR_LAND_AREA_INFORMATION_REVENUE_WHEN_CLEARED :{BLACK}Încasări după curăţare: {LTBLUE}{CURRENCY_LONG} @@ -3308,6 +3321,7 @@ STR_INDUSTRY_VIEW_INDUSTRY_ANNOUNCED_CLOSURE :{YELLOW}Industr STR_INDUSTRY_VIEW_REQUIRES_N_CARGO :{BLACK}Necesită: {YELLOW}{STRING}{STRING} +STR_INDUSTRY_VIEW_REQUIRES :{BLACK}Necesită: STR_INDUSTRY_VIEW_ACCEPT_CARGO :{YELLOW}{STRING}{BLACK}{3:STRING} STR_CONFIG_GAME_PRODUCTION :{WHITE}Schimba productia (multiplu de 8, până la 2040) @@ -3372,6 +3386,7 @@ STR_GROUP_REMOVE_ALL_VEHICLES :Elimină toate STR_GROUP_RENAME_CAPTION :{BLACK}Redenumeşte un grup STR_GROUP_PROFIT_THIS_YEAR :Profitul pe anul acesta: +STR_GROUP_PROFIT_LAST_YEAR :Profitul anului trecut: STR_GROUP_OCCUPANCY :Utilizare curentă: STR_GROUP_OCCUPANCY_VALUE :{NUM}% @@ -3419,10 +3434,11 @@ STR_BUY_VEHICLE_AIRCRAFT_LIST_TOOLTIP :{BLACK}Lista de STR_BUY_VEHICLE_TRAIN_BUY_VEHICLE_BUTTON :{BLACK}Cumpără vehicul STR_BUY_VEHICLE_ROAD_VEHICLE_BUY_VEHICLE_BUTTON :{BLACK}Cumpără vehicul -STR_BUY_VEHICLE_SHIP_BUY_VEHICLE_BUTTON :{BLACK}Cumpără navă +STR_BUY_VEHICLE_SHIP_BUY_VEHICLE_BUTTON :{BLACK}Cumpără nava STR_BUY_VEHICLE_AIRCRAFT_BUY_VEHICLE_BUTTON :{BLACK}Cumpără aeronavă STR_BUY_VEHICLE_TRAIN_BUY_REFIT_VEHICLE_BUTTON :{BLACK}Cumpără și adaptează vehiculul +STR_BUY_VEHICLE_ROAD_VEHICLE_BUY_REFIT_VEHICLE_BUTTON :{BLACK}Cumpără și repară vehiculul STR_BUY_VEHICLE_AIRCRAFT_BUY_REFIT_VEHICLE_BUTTON :{BLACK}Cumpără și schimbă marfa transportată de aeronavă STR_BUY_VEHICLE_TRAIN_BUY_VEHICLE_TOOLTIP :{BLACK}Cumpără vehiculul feroviar selectat. Shift+Click arată costul estimat fără să cumpere vehiculul @@ -3530,7 +3546,7 @@ STR_DEPOT_MASS_STOP_DEPOT_ROAD_VEHICLE_TOOLTIP :{BLACK}Click pt STR_DEPOT_MASS_STOP_DEPOT_SHIP_TOOLTIP :{BLACK}Click pt oprirea tuturor vaselor din depou STR_DEPOT_MASS_STOP_HANGAR_TOOLTIP :{BLACK}Click pt oprirea tuturor aeronavelor din hangar -STR_DEPOT_MASS_START_DEPOT_TRAIN_TOOLTIP :{BLACK}Click pt pornirea tuturor trenurilor din depou +STR_DEPOT_MASS_START_DEPOT_TRAIN_TOOLTIP :{BLACK}Clic pentru pornirea tuturor trenurilor din depou STR_DEPOT_MASS_START_DEPOT_ROAD_VEHICLE_TOOLTIP :{BLACK}Click pt pornirea tuturor autovehiculelor din depou STR_DEPOT_MASS_START_DEPOT_SHIP_TOOLTIP :{BLACK}Click pt pornirea tuturor vaselor din depou STR_DEPOT_MASS_START_HANGAR_TOOLTIP :{BLACK}Click pt pornirea tuturor aeronavelor din hangar @@ -3619,7 +3635,7 @@ STR_VEHICLE_VIEW_CLONE_AIRCRAFT_INFO :{BLACK}Acest bu STR_VEHICLE_VIEW_TRAIN_IGNORE_SIGNAL_TOOLTIP :{BLACK}Forţează trenul să ignore semnalizarea de oprire STR_VEHICLE_VIEW_TRAIN_REFIT_TOOLTIP :{BLACK}Schimba tipul încãrcãturii cãratã de acest tren -STR_VEHICLE_VIEW_ROAD_VEHICLE_REFIT_TOOLTIP :{BLACK}Adapteaza autovehiculul pentru o incarcatura diferita +STR_VEHICLE_VIEW_ROAD_VEHICLE_REFIT_TOOLTIP :{BLACK}Adaptează autovehiculul pentru o încărcătură diferită STR_VEHICLE_VIEW_SHIP_REFIT_TOOLTIP :{BLACK}Schimbă tipul de marfă transportat de navă STR_VEHICLE_VIEW_AIRCRAFT_REFIT_TOOLTIP :{BLACK}Schimbă tipul de marfă transportat de aeronavă @@ -3636,7 +3652,8 @@ STR_VEHICLE_VIEW_ROAD_VEHICLE_SHOW_DETAILS_TOOLTIP :{BLACK}Afişeaz STR_VEHICLE_VIEW_SHIP_SHOW_DETAILS_TOOLTIP :{BLACK}Afişează detaliile navei STR_VEHICLE_VIEW_AIRCRAFT_SHOW_DETAILS_TOOLTIP :{BLACK}Afişează detaliile aeronavei -STR_VEHICLE_VIEW_TRAIN_STATUS_START_STOP_TOOLTIP :{BLACK}Acțiunea trenului actual - clic pentru oprirea/pornirea trenului +STR_VEHICLE_VIEW_TRAIN_STATUS_START_STOP_TOOLTIP :{BLACK}Acțiunea trenului curent - clic pentru oprirea/pornirea trenului +STR_VEHICLE_VIEW_AIRCRAFT_STATUS_START_STOP_TOOLTIP :{BLACK}Acțiunea avionului curent - clic pentru oprirea/pornirea avionului # Messages in the start stop button in the vehicle view @@ -4308,6 +4325,7 @@ STR_ERROR_DEPOT_WRONG_DEPOT_TYPE :Tip incorect de STR_ERROR_TRAIN_TOO_LONG_AFTER_REPLACEMENT :{WHITE}{VEHICLE} este prea lung după înlocuire STR_ERROR_AUTOREPLACE_NOTHING_TO_DO :{WHITE}Nicio regulă autoînlocuire/reînnoire aplicată STR_ERROR_AUTOREPLACE_MONEY_LIMIT :(fonduri limitate) +STR_ERROR_AUTOREPLACE_INCOMPATIBLE_CARGO :{WHITE}Noul vehicul nu poate transporta {STRING} # Rail construction errors STR_ERROR_IMPOSSIBLE_TRACK_COMBINATION :{WHITE}Combinaţie de linii imposibilă @@ -4888,6 +4906,7 @@ STR_FORMAT_BUOY_NAME :{TOWN} Baliza STR_FORMAT_BUOY_NAME_SERIAL :{TOWN} Baliza #{COMMA} STR_FORMAT_COMPANY_NUM :(Companie {COMMA}) STR_FORMAT_GROUP_NAME :Grup {COMMA} +STR_FORMAT_GROUP_VEHICLE_NAME :{GROUP} #{COMMA} STR_FORMAT_INDUSTRY_NAME :{TOWN} {STRING} STR_FORMAT_WAYPOINT_NAME :Halta {TOWN} STR_FORMAT_WAYPOINT_NAME_SERIAL :Halta {TOWN} #{COMMA} diff --git a/src/lang/spanish.txt b/src/lang/spanish.txt index 28ec796454..0214ec0ef5 100644 --- a/src/lang/spanish.txt +++ b/src/lang/spanish.txt @@ -1007,6 +1007,8 @@ STR_GAME_OPTIONS_VIDEO_ACCELERATION :{BLACK}Acelerac STR_GAME_OPTIONS_VIDEO_ACCELERATION_TOOLTIP :{BLACK}Marca esta casilla para permitir que OpenTTD intente usar aceleración por hardware. El cambio de configuración sólo tendrá efecto después de reiniciar el juego STR_GAME_OPTIONS_VIDEO_ACCELERATION_RESTART :{WHITE}El ajuste sólo tendrá efecto después de reiniciar el juego +STR_GAME_OPTIONS_VIDEO_VSYNC :{BLACK}Sincronización vertical +STR_GAME_OPTIONS_VIDEO_VSYNC_TOOLTIP :{BLACK}Marca esta casilla para activar la sincronización vertical en la pantalla. El cambio sólo se aplicará después de reiniciar el juego. Sólo funciona si la aceleración por hardware está activada. STR_GAME_OPTIONS_GUI_ZOOM_FRAME :{BLACK}Tamaño de la interfaz STR_GAME_OPTIONS_GUI_ZOOM_DROPDOWN_TOOLTIP :{BLACK}Selecciona el tamaño de los elementos de la interfaz a usar @@ -3224,9 +3226,9 @@ STR_LOCAL_AUTHORITY_ACTION_NEW_BUILDINGS :Financiar la co STR_LOCAL_AUTHORITY_ACTION_EXCLUSIVE_TRANSPORT :Comprar derechos de transporte exclusivos STR_LOCAL_AUTHORITY_ACTION_BRIBE :Sobornar a la autoridad local -STR_LOCAL_AUTHORITY_ACTION_TOOLTIP_SMALL_ADVERTISING :{YELLOW}Iniciar una pequeña campaña publicitaria local para atraer más pasajeros y carga a sus servicios de transporte.{}Proporciona un incremento temporal en la calificación de las estaciones en un radio pequeño alrededor del centro del municipio.{}Coste: {CURRENCY_LONG} +STR_LOCAL_AUTHORITY_ACTION_TOOLTIP_SMALL_ADVERTISING :{YELLOW}Iniciar una campaña publicitaria local pequeña para atraer más pasajeros y carga a sus servicios de transporte.{}Proporciona un incremento temporal en la calificación de las estaciones en un radio pequeño alrededor del centro del municipio.{}Coste: {CURRENCY_LONG} STR_LOCAL_AUTHORITY_ACTION_TOOLTIP_MEDIUM_ADVERTISING :{YELLOW}Iniciar una campaña publicitaria local mediana para atraer más pasajeros y carga a sus servicios de transporte.{}Proporciona un incremento temporal en la calificación de las estaciones en un radio mediano alrededor del centro del municipio.{}Coste: {CURRENCY_LONG} -STR_LOCAL_AUTHORITY_ACTION_TOOLTIP_LARGE_ADVERTISING :{YELLOW}Iniciar una gran campaña publicitaria local para atraer más pasajeros y carga a sus servicios de transporte.{}Proporciona un incremento temporal en la calificación de las estaciones en un radio grande alrededor del centro del municipio.{}Coste: {CURRENCY_LONG} +STR_LOCAL_AUTHORITY_ACTION_TOOLTIP_LARGE_ADVERTISING :{YELLOW}Iniciar una campaña publicitaria local grande para atraer más pasajeros y carga a sus servicios de transporte.{}Proporciona un incremento temporal en la calificación de las estaciones en un radio grande alrededor del centro del municipio.{}Coste: {CURRENCY_LONG} STR_LOCAL_AUTHORITY_ACTION_TOOLTIP_ROAD_RECONSTRUCTION :{YELLOW}Pagar la reconstrucción de las carreteras locales.{}Provoca considerables complicaciones de tráfico durante 6 meses.{}Coste: {CURRENCY_LONG} STR_LOCAL_AUTHORITY_ACTION_TOOLTIP_STATUE_OF_COMPANY :{YELLOW}Construye una estatua en honor a su empresa.{}Proporciona un incremento permanente en la calificación de las estaciones de este municipio.{}Coste: {CURRENCY_LONG} STR_LOCAL_AUTHORITY_ACTION_TOOLTIP_NEW_BUILDINGS :{YELLOW}Pagar la construcción de nuevos edificios comerciales en el municipio.{}Proporciona un incremento temporal en el crecimiento del municipio.{}Coste: {CURRENCY_LONG} From c4bccd4f70cc369d5b3867f8dac74b0ac1d9ea5c Mon Sep 17 00:00:00 2001 From: rubidium42 Date: Mon, 12 Apr 2021 20:53:04 +0200 Subject: [PATCH 063/268] Fix #8874: show a warning when a NewGRF scan is requested multiple times from the console (#9022) --- src/console_cmds.cpp | 4 +++- src/openttd.cpp | 6 +++++- src/openttd.h | 2 +- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/console_cmds.cpp b/src/console_cmds.cpp index f50644cf5b..cebf701981 100644 --- a/src/console_cmds.cpp +++ b/src/console_cmds.cpp @@ -1351,7 +1351,9 @@ DEF_CONSOLE_CMD(ConRescanNewGRF) return true; } - RequestNewGRFScan(); + if (!RequestNewGRFScan()) { + IConsoleWarning("NewGRF scanning is already running. Please wait until completed to run again."); + } return true; } diff --git a/src/openttd.cpp b/src/openttd.cpp index 250aa0db38..4d820d4db2 100644 --- a/src/openttd.cpp +++ b/src/openttd.cpp @@ -1450,11 +1450,15 @@ static void DoAutosave() * done in the game-thread, and not in the draw-thread (which most often * triggers this request). * @param callback Optional callback to call when NewGRF scan is completed. + * @return True when the NewGRF scan was actually requested, false when the scan was already running. */ -void RequestNewGRFScan(NewGRFScanCallback *callback) +bool RequestNewGRFScan(NewGRFScanCallback *callback) { + if (_request_newgrf_scan) return false; + _request_newgrf_scan = true; _request_newgrf_scan_callback = callback; + return true; } void GameLoop() diff --git a/src/openttd.h b/src/openttd.h index 38c7f80644..77fafab1d1 100644 --- a/src/openttd.h +++ b/src/openttd.h @@ -81,6 +81,6 @@ void HandleExitGameRequest(); void SwitchToMode(SwitchMode new_mode); -void RequestNewGRFScan(struct NewGRFScanCallback *callback = nullptr); +bool RequestNewGRFScan(struct NewGRFScanCallback *callback = nullptr); #endif /* OPENTTD_H */ From ca6b9ad8b0a64ebc812cf2e748d8ea9c874b4860 Mon Sep 17 00:00:00 2001 From: Rubidium Date: Sun, 11 Apr 2021 11:22:50 +0200 Subject: [PATCH 064/268] Change: move some things only relevant to UDP from network.cpp to network_udp.cpp --- src/network/network.cpp | 10 +--------- src/network/network_internal.h | 5 ----- src/network/network_udp.cpp | 11 +++++++++++ src/network/network_udp.h | 1 + 4 files changed, 13 insertions(+), 14 deletions(-) diff --git a/src/network/network.cpp b/src/network/network.cpp index 1d5563c3d9..f4640191c8 100644 --- a/src/network/network.cpp +++ b/src/network/network.cpp @@ -74,19 +74,12 @@ uint32 _sync_seed_2; ///< Second part of the seed. #endif uint32 _sync_frame; ///< The frame to perform the sync check. bool _network_first_time; ///< Whether we have finished joining or not. -bool _network_udp_server; ///< Is the UDP server started? -uint16 _network_udp_broadcast; ///< Timeout for the UDP broadcasts. -uint8 _network_advertise_retries; ///< The number of advertisement retries we did. CompanyMask _network_company_passworded; ///< Bitmask of the password status of all companies. /* Check whether NETWORK_NUM_LANDSCAPES is still in sync with NUM_LANDSCAPE */ static_assert((int)NETWORK_NUM_LANDSCAPES == (int)NUM_LANDSCAPE); static_assert((int)NETWORK_COMPANY_NAME_LENGTH == MAX_LENGTH_COMPANY_NAME_CHARS * MAX_CHAR_LENGTH); -extern NetworkUDPSocketHandler *_udp_client_socket; ///< udp client socket -extern NetworkUDPSocketHandler *_udp_server_socket; ///< udp server socket -extern NetworkUDPSocketHandler *_udp_master_socket; ///< udp master socket - /** The amount of clients connected */ byte _network_clients_connected = 0; @@ -724,7 +717,7 @@ bool NetworkServerStart() /* Try to start UDP-server */ DEBUG(net, 1, "starting listeners for incoming server queries"); - _network_udp_server = _udp_server_socket->Listen(); + NetworkUDPServerListen(); _network_company_states = CallocT(MAX_COMPANIES); _network_server = true; @@ -1060,7 +1053,6 @@ void NetworkStartUp() _network_available = NetworkCoreInitialize(); _network_dedicated = false; _network_need_advertise = true; - _network_advertise_retries = 0; /* Generate an server id when there is none yet */ if (StrEmpty(_settings_client.network.network_id)) NetworkGenerateServerId(); diff --git a/src/network/network_internal.h b/src/network/network_internal.h index 8cae502e10..290922f385 100644 --- a/src/network/network_internal.h +++ b/src/network/network_internal.h @@ -126,11 +126,6 @@ extern uint32 _network_join_bytes_total; extern uint8 _network_reconnect; -extern bool _network_udp_server; -extern uint16 _network_udp_broadcast; - -extern uint8 _network_advertise_retries; - extern CompanyMask _network_company_passworded; void NetworkTCPQueryServer(NetworkAddress address); diff --git a/src/network/network_udp.cpp b/src/network/network_udp.cpp index 99939fab44..e4276c58f1 100644 --- a/src/network/network_udp.cpp +++ b/src/network/network_udp.cpp @@ -47,6 +47,10 @@ NetworkUDPSocketHandler *_udp_client_socket = nullptr; ///< udp client socket NetworkUDPSocketHandler *_udp_server_socket = nullptr; ///< udp server socket NetworkUDPSocketHandler *_udp_master_socket = nullptr; ///< udp master socket +static bool _network_udp_server; ///< Is the UDP server started? +static uint16 _network_udp_broadcast; ///< Timeout for the UDP broadcasts. +static uint8 _network_advertise_retries; ///< The number of advertisement retries we did. + /** * Helper function doing the actual work for querying the server. * @param address The address of the server. @@ -622,6 +626,13 @@ void NetworkUDPInitialize() _network_udp_server = false; _network_udp_broadcast = 0; + _network_advertise_retries = 0; +} + +/** Start the listening of the UDP server component. */ +void NetworkUDPServerListen() +{ + _network_udp_server = _udp_server_socket->Listen(); } /** Close all UDP related stuff. */ diff --git a/src/network/network_udp.h b/src/network/network_udp.h index c042bea404..189657bc87 100644 --- a/src/network/network_udp.h +++ b/src/network/network_udp.h @@ -19,6 +19,7 @@ void NetworkUDPQueryServer(NetworkAddress address, bool manually = false); void NetworkUDPAdvertise(); void NetworkUDPRemoveAdvertise(bool blocking); void NetworkUDPClose(); +void NetworkUDPServerListen(); void NetworkBackgroundUDPLoop(); #endif /* NETWORK_UDP_H */ From 7597740bff711d78e3f005bdfad214154281559a Mon Sep 17 00:00:00 2001 From: Rubidium Date: Sun, 11 Apr 2021 11:30:44 +0200 Subject: [PATCH 065/268] Fix: split the UDP blocking of sockets to only the socket involved, and when another thread is busy do not attempt to process the packets of that socket --- src/network/network_udp.cpp | 96 +++++++++++++++++++++++-------------- 1 file changed, 59 insertions(+), 37 deletions(-) diff --git a/src/network/network_udp.cpp b/src/network/network_udp.cpp index e4276c58f1..6d7e6ca698 100644 --- a/src/network/network_udp.cpp +++ b/src/network/network_udp.cpp @@ -33,9 +33,6 @@ #include "../safeguards.h" -/** Mutex for all out threaded udp resolution and such. */ -static std::mutex _network_udp_mutex; - /** Session key to register ourselves to the master server */ static uint64 _session_key = 0; @@ -43,14 +40,46 @@ static const std::chrono::minutes ADVERTISE_NORMAL_INTERVAL(15); ///< interval b static const std::chrono::seconds ADVERTISE_RETRY_INTERVAL(10); ///< re-advertise when no response after this amount of time. static const uint32 ADVERTISE_RETRY_TIMES = 3; ///< give up re-advertising after this much failed retries -NetworkUDPSocketHandler *_udp_client_socket = nullptr; ///< udp client socket -NetworkUDPSocketHandler *_udp_server_socket = nullptr; ///< udp server socket -NetworkUDPSocketHandler *_udp_master_socket = nullptr; ///< udp master socket - static bool _network_udp_server; ///< Is the UDP server started? static uint16 _network_udp_broadcast; ///< Timeout for the UDP broadcasts. static uint8 _network_advertise_retries; ///< The number of advertisement retries we did. +/** Some information about a socket, which exists before the actual socket has been created to provide locking and the likes. */ +struct UDPSocket { + const std::string name; ///< The name of the socket. + std::mutex mutex; ///< Mutex for everything that (indirectly) touches the sockets within the handler. + NetworkUDPSocketHandler *socket; ///< The actual socket, which may be nullptr when not initialized yet. + std::atomic receive_iterations_locked; ///< The number of receive iterations the mutex was locked. + + UDPSocket(const std::string &name_) : name(name_), socket(nullptr) {} + + void Close() + { + std::lock_guard lock(mutex); + socket->Close(); + delete socket; + socket = nullptr; + } + + void ReceivePackets() + { + std::unique_lock lock(mutex, std::defer_lock); + if (!lock.try_lock()) { + if (++receive_iterations_locked % 32 == 0) { + DEBUG(net, 0, "[udp] %s background UDP loop processing appears to be blocked. Your OS may be low on UDP send buffers.", name.c_str()); + } + return; + } + + receive_iterations_locked.store(0); + socket->ReceivePackets(); + } +}; + +static UDPSocket _udp_client("Client"); ///< udp client socket +static UDPSocket _udp_server("Server"); ///< udp server socket +static UDPSocket _udp_master("Master"); ///< udp master socket + /** * Helper function doing the actual work for querying the server. * @param address The address of the server. @@ -67,11 +96,11 @@ static void DoNetworkUDPQueryServer(NetworkAddress &address, bool needs_mutex, b item->manually = manually; NetworkGameListAddItemDelayed(item); - std::unique_lock lock(_network_udp_mutex, std::defer_lock); + std::unique_lock lock(_udp_client.mutex, std::defer_lock); if (needs_mutex) lock.lock(); /* Init the packet */ Packet p(PACKET_UDP_CLIENT_FIND_SERVER); - if (_udp_client_socket != nullptr) _udp_client_socket->SendPacket(&p, &address); + if (_udp_client.socket != nullptr) _udp_client.socket->SendPacket(&p, &address); } /** @@ -479,7 +508,8 @@ void NetworkUDPQueryMasterServer() p.Send_uint8(NETWORK_MASTER_SERVER_VERSION); p.Send_uint8(SLT_AUTODETECT); - _udp_client_socket->SendPacket(&p, &out_addr, true); + std::lock_guard lock(_udp_client.mutex); + _udp_client.socket->SendPacket(&p, &out_addr, true); DEBUG(net, 2, "[udp] master server queried at %s", out_addr.GetAddressAsString().c_str()); } @@ -492,7 +522,7 @@ void NetworkUDPSearchGame() DEBUG(net, 0, "[udp] searching server"); - NetworkUDPBroadCast(_udp_client_socket); + NetworkUDPBroadCast(_udp_client.socket); _network_udp_broadcast = 300; // Stay searching for 300 ticks } @@ -512,8 +542,8 @@ static void NetworkUDPRemoveAdvertiseThread() p.Send_uint8 (NETWORK_MASTER_SERVER_VERSION); p.Send_uint16(_settings_client.network.server_port); - std::lock_guard lock(_network_udp_mutex); - if (_udp_master_socket != nullptr) _udp_master_socket->SendPacket(&p, &out_addr, true); + std::lock_guard lock(_udp_master.mutex); + if (_udp_master.socket != nullptr) _udp_master.socket->SendPacket(&p, &out_addr, true); } /** @@ -564,8 +594,8 @@ static void NetworkUDPAdvertiseThread() p.Send_uint16(_settings_client.network.server_port); p.Send_uint64(_session_key); - std::lock_guard lock(_network_udp_mutex); - if (_udp_master_socket != nullptr) _udp_master_socket->SendPacket(&p, &out_addr, true); + std::lock_guard lock(_udp_master.mutex); + if (_udp_master.socket != nullptr) _udp_master.socket->SendPacket(&p, &out_addr, true); } /** @@ -607,22 +637,22 @@ void NetworkUDPAdvertise() void NetworkUDPInitialize() { /* If not closed, then do it. */ - if (_udp_server_socket != nullptr) NetworkUDPClose(); + if (_udp_server.socket != nullptr) NetworkUDPClose(); DEBUG(net, 1, "[udp] initializing listeners"); - assert(_udp_client_socket == nullptr && _udp_server_socket == nullptr && _udp_master_socket == nullptr); + assert(_udp_client.socket == nullptr && _udp_server.socket == nullptr && _udp_master.socket == nullptr); - std::lock_guard lock(_network_udp_mutex); + std::scoped_lock lock(_udp_client.mutex, _udp_server.mutex, _udp_master.mutex); - _udp_client_socket = new ClientNetworkUDPSocketHandler(); + _udp_client.socket = new ClientNetworkUDPSocketHandler(); NetworkAddressList server; GetBindAddresses(&server, _settings_client.network.server_port); - _udp_server_socket = new ServerNetworkUDPSocketHandler(&server); + _udp_server.socket = new ServerNetworkUDPSocketHandler(&server); server.clear(); GetBindAddresses(&server, 0); - _udp_master_socket = new MasterNetworkUDPSocketHandler(&server); + _udp_master.socket = new MasterNetworkUDPSocketHandler(&server); _network_udp_server = false; _network_udp_broadcast = 0; @@ -632,22 +662,16 @@ void NetworkUDPInitialize() /** Start the listening of the UDP server component. */ void NetworkUDPServerListen() { - _network_udp_server = _udp_server_socket->Listen(); + std::lock_guard lock(_udp_server.mutex); + _network_udp_server = _udp_server.socket->Listen(); } /** Close all UDP related stuff. */ void NetworkUDPClose() { - std::lock_guard lock(_network_udp_mutex); - _udp_server_socket->Close(); - _udp_master_socket->Close(); - _udp_client_socket->Close(); - delete _udp_client_socket; - delete _udp_server_socket; - delete _udp_master_socket; - _udp_client_socket = nullptr; - _udp_server_socket = nullptr; - _udp_master_socket = nullptr; + _udp_client.Close(); + _udp_server.Close(); + _udp_master.Close(); _network_udp_server = false; _network_udp_broadcast = 0; @@ -657,13 +681,11 @@ void NetworkUDPClose() /** Receive the UDP packets. */ void NetworkBackgroundUDPLoop() { - std::lock_guard lock(_network_udp_mutex); - if (_network_udp_server) { - _udp_server_socket->ReceivePackets(); - _udp_master_socket->ReceivePackets(); + _udp_server.ReceivePackets(); + _udp_master.ReceivePackets(); } else { - _udp_client_socket->ReceivePackets(); + _udp_client.ReceivePackets(); if (_network_udp_broadcast > 0) _network_udp_broadcast--; } } From d2fe8c2842c7930d5fca4f10bad276b706393946 Mon Sep 17 00:00:00 2001 From: Rubidium Date: Sun, 11 Apr 2021 11:32:31 +0200 Subject: [PATCH 066/268] Change: warn the user about the resolving of an address being extra very slow --- src/network/core/address.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/network/core/address.cpp b/src/network/core/address.cpp index a45bd49142..8c69094385 100644 --- a/src/network/core/address.cpp +++ b/src/network/core/address.cpp @@ -239,7 +239,18 @@ SOCKET NetworkAddress::Resolve(int family, int socktype, int flags, SocketList * strecpy(this->hostname, fam == AF_INET ? "0.0.0.0" : "::", lastof(this->hostname)); } + 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); + 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()); + 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)); From 4fb1e34b1ed84d228ce5d88abe9a78437a3760d3 Mon Sep 17 00:00:00 2001 From: Rubidium Date: Sun, 11 Apr 2021 11:39:43 +0200 Subject: [PATCH 067/268] Change: add some hints about the getaddrinfo warning to the troubleshooting part of the documentation --- docs/multiplayer.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/docs/multiplayer.md b/docs/multiplayer.md index 89a490602e..daccbf06d6 100644 --- a/docs/multiplayer.md +++ b/docs/multiplayer.md @@ -211,3 +211,10 @@ Last updated: 2011-02-16 communication from an admin tool reach the programme. See section 1 'Starting a server' further up for the ports and protocols used by OpenTTD. The ports can be configured in the config file. + + - My advertising server warns a lot about getaddrinfo taking N seconds + This could be a transient issue with your (local) DNS server, but if the + problem persists there is likely a configuration issue in DNS resolving + on your computer. This seems to be a common configuration issue for + Docker instances, where the DNS resolving waits for a time out of usually + 5 seconds. From 64e830587455a3baa0354f550ce9b179a070215e Mon Sep 17 00:00:00 2001 From: Rubidium Date: Mon, 12 Apr 2021 19:57:08 +0200 Subject: [PATCH 068/268] Fix: [SDL] buffer_locked state not initialised, causing _screen.dst_ptr to be potentially not set --- src/video/sdl2_v.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/video/sdl2_v.h b/src/video/sdl2_v.h index bd43f71f89..d706cc6654 100644 --- a/src/video/sdl2_v.h +++ b/src/video/sdl2_v.h @@ -17,7 +17,7 @@ /** The SDL video driver. */ class VideoDriver_SDL_Base : public VideoDriver { public: - VideoDriver_SDL_Base() : sdl_window(nullptr) {} + VideoDriver_SDL_Base() : sdl_window(nullptr), buffer_locked(false) {} const char *Start(const StringList ¶m) override; From 856239541393df6d6a1443b15d7c4c8b213fbc5e Mon Sep 17 00:00:00 2001 From: Rubidium Date: Mon, 12 Apr 2021 20:52:00 +0200 Subject: [PATCH 069/268] Fix: [Video] fast forward boolean states not initialised, potentially causing unstoppable fast forward --- src/video/video_driver.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/video/video_driver.hpp b/src/video/video_driver.hpp index f143b61a80..4964e01cb3 100644 --- a/src/video/video_driver.hpp +++ b/src/video/video_driver.hpp @@ -36,7 +36,7 @@ class VideoDriver : public Driver { const uint DEFAULT_WINDOW_HEIGHT = 480u; ///< Default window height. public: - VideoDriver() : is_game_threaded(true), change_blitter(nullptr) {} + VideoDriver() : fast_forward_key_pressed(false), fast_forward_via_key(false), is_game_threaded(true), change_blitter(nullptr) {} /** * Mark a particular area dirty. From 468b1c6c5d1949f4d8aad90dad0f3d8770973113 Mon Sep 17 00:00:00 2001 From: Rubidium Date: Mon, 12 Apr 2021 20:58:44 +0200 Subject: [PATCH 070/268] Fix: [win32] buffer_locked state not initialised, causing _screen.dst_ptr to be potentially not set --- src/video/win32_v.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/video/win32_v.h b/src/video/win32_v.h index 8c63aeedf4..4686df7160 100644 --- a/src/video/win32_v.h +++ b/src/video/win32_v.h @@ -17,7 +17,7 @@ /** Base class for Windows video drivers. */ class VideoDriver_Win32Base : public VideoDriver { public: - VideoDriver_Win32Base() : main_wnd(nullptr), fullscreen(false) {} + VideoDriver_Win32Base() : main_wnd(nullptr), fullscreen(false), buffer_locked(false) {} void Stop() override; From 433602b072aaf6a2c07a9989e7989f2dd0bb8317 Mon Sep 17 00:00:00 2001 From: Michael Lutz Date: Mon, 12 Apr 2021 21:44:32 +0200 Subject: [PATCH 071/268] Fix #9028: [OpenGL] Clear cursor cache on destroying the OpenGL backend. --- src/video/opengl.cpp | 22 +++++++++++++++------- src/video/opengl.h | 2 ++ 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/src/video/opengl.cpp b/src/video/opengl.cpp index c1c1afaaec..bb509bcd74 100644 --- a/src/video/opengl.cpp +++ b/src/video/opengl.cpp @@ -510,7 +510,7 @@ OpenGLBackend::~OpenGLBackend() _glDeleteBuffers(1, &this->anim_pbo); } if (_glDeleteTextures != nullptr) { - ClearCursorCache(); + this->InternalClearCursorCache(); OpenGLSprite::Destroy(); _glDeleteTextures(1, &this->vid_texture); @@ -1082,12 +1082,7 @@ void OpenGLBackend::PopulateCursorCache() this->clear_cursor_cache = false; this->last_sprite_pal = (PaletteID)-1; - Sprite *sp; - while ((sp = this->cursor_cache.Pop()) != nullptr) { - OpenGLSprite *sprite = (OpenGLSprite *)sp->data; - sprite->~OpenGLSprite(); - free(sp); - } + this->InternalClearCursorCache(); } this->cursor_pos = _cursor.pos; @@ -1113,6 +1108,19 @@ void OpenGLBackend::PopulateCursorCache() /** * Clear all cached cursor sprites. */ +void OpenGLBackend::InternalClearCursorCache() +{ + Sprite *sp; + while ((sp = this->cursor_cache.Pop()) != nullptr) { + OpenGLSprite *sprite = (OpenGLSprite *)sp->data; + sprite->~OpenGLSprite(); + free(sp); + } +} + +/** + * Queue a request for cursor cache clear. + */ void OpenGLBackend::ClearCursorCache() { /* If the game loop is threaded, this function might be called diff --git a/src/video/opengl.h b/src/video/opengl.h index 7e42b20bed..b0318f9882 100644 --- a/src/video/opengl.h +++ b/src/video/opengl.h @@ -77,6 +77,8 @@ private: const char *Init(); bool InitShaders(); + void InternalClearCursorCache(); + void RenderOglSprite(OpenGLSprite *gl_sprite, PaletteID pal, int x, int y, ZoomLevel zoom); public: From 4cd9e0f41bb753545511b1672640911f228e67f1 Mon Sep 17 00:00:00 2001 From: Milek7 Date: Tue, 13 Apr 2021 17:12:56 +0200 Subject: [PATCH 072/268] Fix: Add virtual destructor to link graph Path. Classes derived from Path were freed through base class pointer, but no virtual destructor was present. --- src/linkgraph/linkgraphjob.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/linkgraph/linkgraphjob.h b/src/linkgraph/linkgraphjob.h index caeacd19de..4913b5a357 100644 --- a/src/linkgraph/linkgraphjob.h +++ b/src/linkgraph/linkgraphjob.h @@ -367,6 +367,7 @@ public: static Path *invalid_path; Path(NodeID n, bool source = false); + virtual ~Path() = default; /** Get the node this leg passes. */ inline NodeID GetNode() const { return this->node; } From 3e0a16c027a42c84678b723540532d1f89fc4fbc Mon Sep 17 00:00:00 2001 From: translators Date: Wed, 14 Apr 2021 17:49:26 +0000 Subject: [PATCH 073/268] Update: Translations from eints romanian: 57 changes by kneekoo russian: 3 changes by Ln-Wolf spanish: 3 changes by MontyMontana portuguese: 7 changes by azulcosta --- src/lang/portuguese.txt | 14 ++++---- src/lang/romanian.txt | 74 +++++++++++++++++++++++++++++++---------- src/lang/russian.txt | 4 ++- src/lang/spanish.txt | 6 ++-- 4 files changed, 70 insertions(+), 28 deletions(-) diff --git a/src/lang/portuguese.txt b/src/lang/portuguese.txt index d82a547295..046e9442ce 100644 --- a/src/lang/portuguese.txt +++ b/src/lang/portuguese.txt @@ -242,7 +242,7 @@ STR_TOOLTIP_CLOSE_WINDOW :{BLACK}Fechar j STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS :{BLACK}Título da janela - arraste isto para mover a janela STR_TOOLTIP_SHADE :{BLACK}Encolher janela - apenas mostra a barra de título STR_TOOLTIP_DEBUG :{BLACK}Mostrar informação de depuração de NewGRF -STR_TOOLTIP_DEFSIZE :{BLACK}Reajusta janela para tamanho por defeito. Ctrl+Clique para manter o tamanho actual como o por defeito +STR_TOOLTIP_DEFSIZE :{BLACK}Redimensionar a janela para o tamanho padrão. Ctrl+Clique para guardar o tamanho atual como padrão STR_TOOLTIP_STICKY :{BLACK}Marcar esta janela como não-encerrável pela tecla 'Fechar Todas as Janelas'. Ctrl+Clique para tambem salvar o estado como por omissão STR_TOOLTIP_RESIZE :{BLACK}Clique e arraste para reajustar janela STR_TOOLTIP_TOGGLE_LARGE_SMALL_WINDOW :{BLACK}Alternar entre janela grande/pequena @@ -2448,7 +2448,7 @@ STR_STATION_BUILD_DRAG_DROP_TOOLTIP :{BLACK}Construi STR_STATION_BUILD_STATION_CLASS_TOOLTIP :{BLACK}Escolher a classe da estação a mostrar STR_STATION_BUILD_STATION_TYPE_TOOLTIP :{BLACK}Escolher o tipo de estação a construir -STR_STATION_CLASS_DFLT :Estação por defeito +STR_STATION_CLASS_DFLT :Estação padrão STR_STATION_CLASS_WAYP :Pontos de passagem # Signal window @@ -2878,7 +2878,7 @@ STR_SAVELOAD_SAVE_SCENARIO :{WHITE}Guardar STR_SAVELOAD_LOAD_SCENARIO :{WHITE}Abrir Cenário STR_SAVELOAD_LOAD_HEIGHTMAP :{WHITE}Carregar mapa de alturas STR_SAVELOAD_SAVE_HEIGHTMAP :{WHITE}Guardar mapa de alturas -STR_SAVELOAD_HOME_BUTTON :{BLACK}Carregue aqui para saltar para a directoria de gravação/carregamento por defeito +STR_SAVELOAD_HOME_BUTTON :{BLACK}Clique aqui para saltar para o diretório atual de gravação/carregamento padrão STR_SAVELOAD_BYTES_FREE :{BLACK}{BYTES} livres STR_SAVELOAD_LIST_TOOLTIP :{BLACK}Lista de unidades, directorias e ficheiros de jogos guardados STR_SAVELOAD_EDITBOX_TOOLTIP :{BLACK}Nome escolhido para guardar o jogo @@ -3903,11 +3903,11 @@ STR_VEHICLE_INFO_AGE_RUNNING_COST_YR :{BLACK}Idade: { STR_VEHICLE_INFO_AGE :{COMMA} ano{P "" s} ({COMMA}) STR_VEHICLE_INFO_AGE_RED :{RED}{COMMA} ano{P "" s} ({COMMA}) -STR_VEHICLE_INFO_MAX_SPEED :{BLACK}Máx. velocidade: {LTBLUE}{VELOCITY} +STR_VEHICLE_INFO_MAX_SPEED :{BLACK}Velocidade máx.: {LTBLUE}{VELOCITY} STR_VEHICLE_INFO_MAX_SPEED_TYPE :{BLACK}Vel. máxima: {LTBLUE}{VELOCITY} {BLACK}Tipo de Aeronave: {LTBLUE}{STRING} STR_VEHICLE_INFO_MAX_SPEED_TYPE_RANGE :{BLACK}Velocidade Máx.: {LTBLUE}{VELOCITY} {BLACK}Tipo de Aeronave: {LTBLUE}{STRING} {BLACK}Alcance: {LTBLUE}{COMMA} quadrados -STR_VEHICLE_INFO_WEIGHT_POWER_MAX_SPEED :{BLACK}Peso: {LTBLUE}{WEIGHT_SHORT} {BLACK}Potência: {LTBLUE}{POWER}{BLACK} Max. velocidade: {LTBLUE}{VELOCITY} -STR_VEHICLE_INFO_WEIGHT_POWER_MAX_SPEED_MAX_TE :{BLACK}Peso: {LTBLUE}{WEIGHT_SHORT} {BLACK}Potência: {LTBLUE}{POWER}{BLACK} Máx. velocidade: {LTBLUE}{VELOCITY} {BLACK}Máx. E.T.: {LTBLUE}{FORCE} +STR_VEHICLE_INFO_WEIGHT_POWER_MAX_SPEED :{BLACK}Peso: {LTBLUE}{WEIGHT_SHORT} {BLACK}Potência: {LTBLUE}{POWER}{BLACK} Velocidade máx.: {LTBLUE}{VELOCITY} +STR_VEHICLE_INFO_WEIGHT_POWER_MAX_SPEED_MAX_TE :{BLACK}Peso: {LTBLUE}{WEIGHT_SHORT} {BLACK}Potência: {LTBLUE}{POWER}{BLACK} Velocidade máx.: {LTBLUE}{VELOCITY} {BLACK}Tração Máx.: {LTBLUE}{FORCE} STR_VEHICLE_INFO_PROFIT_THIS_YEAR_LAST_YEAR :{BLACK}Lucro neste ano: {LTBLUE}{CURRENCY_LONG} (último ano: {CURRENCY_LONG}) STR_VEHICLE_INFO_RELIABILITY_BREAKDOWNS :{BLACK}Fiabilidade: {LTBLUE}{COMMA}% {BLACK}Avarias desde o último serviço: {LTBLUE}{COMMA} @@ -3926,7 +3926,7 @@ STR_VEHICLE_DETAILS_INCREASE_SERVICING_INTERVAL_TOOLTIP :{BLACK}Aumentar STR_VEHICLE_DETAILS_DECREASE_SERVICING_INTERVAL_TOOLTIP :{BLACK}Diminuir intervalo de serviço por 10. Ctrl+Clique diminui o intervalo de serviço por 5 STR_SERVICE_INTERVAL_DROPDOWN_TOOLTIP :{BLACK}Alterar tipo de intervalo de manutenção -STR_VEHICLE_DETAILS_DEFAULT :Por Defeito +STR_VEHICLE_DETAILS_DEFAULT :Padrão STR_VEHICLE_DETAILS_DAYS :Dias STR_VEHICLE_DETAILS_PERCENT :Percentagem diff --git a/src/lang/romanian.txt b/src/lang/romanian.txt index 002f7500ae..ec045eacee 100644 --- a/src/lang/romanian.txt +++ b/src/lang/romanian.txt @@ -180,6 +180,7 @@ STR_COLOUR_DEFAULT :Prestabilit STR_UNITS_VELOCITY_IMPERIAL :{COMMA} mph STR_UNITS_VELOCITY_METRIC :{COMMA} km/h STR_UNITS_VELOCITY_SI :{COMMA} m/s +STR_UNITS_VELOCITY_GAMEUNITS :{DECIMAL}{NBSP}dale/zi STR_UNITS_POWER_IMPERIAL :{COMMA}cp STR_UNITS_POWER_METRIC :{COMMA}cp @@ -297,6 +298,7 @@ STR_SORT_BY_RATING :Cotaţie STR_SORT_BY_NUM_VEHICLES :Număr de vehicule STR_SORT_BY_TOTAL_PROFIT_LAST_YEAR :Profit total în anul trecut STR_SORT_BY_AVERAGE_PROFIT_LAST_YEAR :Profit mediu în anul trecut +STR_SORT_BY_AVERAGE_PROFIT_THIS_YEAR :Profit mediu în acest an # Group by options for vehicle list STR_GROUP_BY_SHARED_ORDERS :Comenzi comune @@ -643,8 +645,8 @@ STR_MUSIC_TRACK :{TINY_FONT}{BLA STR_MUSIC_XTITLE :{TINY_FONT}{BLACK}Titlul STR_MUSIC_SHUFFLE :{TINY_FONT}{BLACK}Aleator STR_MUSIC_PROGRAM :{TINY_FONT}{BLACK}Program -STR_MUSIC_TOOLTIP_SKIP_TO_PREVIOUS_TRACK :{BLACK}Sari la piesa precedentă din selecţie -STR_MUSIC_TOOLTIP_SKIP_TO_NEXT_TRACK_IN_SELECTION :{BLACK}Sari la piesa următoare din selecţie +STR_MUSIC_TOOLTIP_SKIP_TO_PREVIOUS_TRACK :{BLACK}Sari la piesa precedentă din selecție +STR_MUSIC_TOOLTIP_SKIP_TO_NEXT_TRACK_IN_SELECTION :{BLACK}Sari la următoarea piesă din selecție STR_MUSIC_TOOLTIP_STOP_PLAYING_MUSIC :{BLACK}Opreşte muzica STR_MUSIC_TOOLTIP_START_PLAYING_MUSIC :{BLACK}Porneşte muzica STR_MUSIC_TOOLTIP_DRAG_SLIDERS_TO_SET_MUSIC :{BLACK}Foloseşte aceste indicatoare pentru a regla volumul muzicii şi al efectelor sonore @@ -655,7 +657,7 @@ STR_MUSIC_TOOLTIP_SELECT_EZY_STREET_STYLE :{BLACK}Selectea STR_MUSIC_TOOLTIP_SELECT_CUSTOM_1_USER_DEFINED :{BLACK}Selectează programul personal 1 STR_MUSIC_TOOLTIP_SELECT_CUSTOM_2_USER_DEFINED :{BLACK}Selectează programul personal 2 STR_MUSIC_TOOLTIP_TOGGLE_PROGRAM_SHUFFLE :{BLACK}Comutator pentru amestecarea melodiilor (pornit/oprit) -STR_MUSIC_TOOLTIP_SHOW_MUSIC_TRACK_SELECTION :{BLACK}Afişeaza fereastra pentru selecţia melodiilor +STR_MUSIC_TOOLTIP_SHOW_MUSIC_TRACK_SELECTION :{BLACK}Afișează fereastra pentru selecția melodiilor # Playlist window STR_PLAYLIST_TRACK_NAME :{TINY_FONT}{LTBLUE}{ZEROFILL_NUM} "{STRING}" @@ -801,6 +803,7 @@ STR_NEWS_MERGER_TAKEOVER_TITLE :{BIG_FONT}{BLAC STR_PRESIDENT_NAME_MANAGER :{BLACK}{PRESIDENT_NAME}{}(Preşedinte) STR_NEWS_NEW_TOWN :{BLACK}{BIG_FONT}{STRING} a sponsorizat construcţia unui nou oras {TOWN}! +STR_NEWS_NEW_TOWN_UNSPONSORED :{BLACK}{BIG_FONT}Un nou oraș, numit {TOWN}, a fost construit! STR_NEWS_INDUSTRY_CONSTRUCTION :{BIG_FONT}{BLACK}Un nou obiectiv industrial ({STRING}) se construieşte lângă {TOWN}! STR_NEWS_INDUSTRY_PLANTED :{BIG_FONT}{BLACK}O nouă {STRING} se plantează lângă {TOWN}! @@ -919,6 +922,7 @@ STR_GAME_OPTIONS_CURRENCY_MXN :Peso Mexican (M STR_GAME_OPTIONS_CURRENCY_NTD :Noul Dolar Taiwanez (NTD) STR_GAME_OPTIONS_CURRENCY_CNY :Renminbi Chinezesc (CNY) STR_GAME_OPTIONS_CURRENCY_INR :Rupia Indiană (INR) +STR_GAME_OPTIONS_CURRENCY_MYR :Ringgit Malaysian (MYR) ############ end of currency region STR_GAME_OPTIONS_ROAD_VEHICLES_DROPDOWN_LEFT :Pe partea stângă @@ -974,11 +978,14 @@ STR_GAME_OPTIONS_RESOLUTION_OTHER :(alta/nespecifi STR_GAME_OPTIONS_RESOLUTION_ITEM :{NUM}x{NUM} STR_GAME_OPTIONS_VIDEO_ACCELERATION :{BLACK}Accelerare hardware +STR_GAME_OPTIONS_VIDEO_ACCELERATION_RESTART :{WHITE}Setarea va avea efect doar după repornirea jocului +STR_GAME_OPTIONS_VIDEO_VSYNC :{BLACK}VSync STR_GAME_OPTIONS_GUI_ZOOM_FRAME :{BLACK}Mărime interfată STR_GAME_OPTIONS_GUI_ZOOM_DROPDOWN_TOOLTIP :{BLACK}Alege mărimea elementelor de interfaţa +STR_GAME_OPTIONS_GUI_ZOOM_DROPDOWN_AUTO :(auto-detecție) STR_GAME_OPTIONS_GUI_ZOOM_DROPDOWN_NORMAL :Normală STR_GAME_OPTIONS_GUI_ZOOM_DROPDOWN_2X_ZOOM :Mărime dublă STR_GAME_OPTIONS_GUI_ZOOM_DROPDOWN_4X_ZOOM :Mărime împătrită @@ -989,9 +996,11 @@ STR_GAME_OPTIONS_FONT_ZOOM_DROPDOWN_AUTO :(auto-detecție STR_GAME_OPTIONS_FONT_ZOOM_DROPDOWN_NORMAL :Normal STR_GAME_OPTIONS_FONT_ZOOM_DROPDOWN_2X_ZOOM :Mărime dublă +STR_GAME_OPTIONS_GRAPHICS :{BLACK}Grafică STR_GAME_OPTIONS_REFRESH_RATE_TOOLTIP :{BLACK}Alegeți rata de reîmprospătare dorită STR_GAME_OPTIONS_REFRESH_RATE_ITEM :{NUM}Hz +STR_GAME_OPTIONS_REFRESH_RATE_WARNING :{WHITE}Ratele de împrospătare de peste 60Hz ar putea afecta performanța. STR_GAME_OPTIONS_BASE_GRF :{BLACK}Set grafic de bază STR_GAME_OPTIONS_BASE_GRF_TOOLTIP :{BLACK}Selectează setul grafic de bază utilizat în joc @@ -1085,6 +1094,7 @@ STR_TERRAIN_TYPE_FLAT :Plat STR_TERRAIN_TYPE_HILLY :Deluros STR_TERRAIN_TYPE_MOUNTAINOUS :Muntos STR_TERRAIN_TYPE_ALPINIST :Alpinist +STR_TERRAIN_TYPE_CUSTOM_VALUE :Înălțime personalizată ({NUM}) STR_CITY_APPROVAL_PERMISSIVE :Permisivă STR_CITY_APPROVAL_TOLERANT :Tolerantă @@ -1166,6 +1176,7 @@ STR_CONFIG_SETTING_CITY_APPROVAL :Atitudinea cons STR_CONFIG_SETTING_CITY_APPROVAL_HELPTEXT :Alege în ce măsură poluarea fonică si deranjamentul local provocat de o companie va afecta impresia orașului despre aceasta, si viitoarele planuri de construcție in zonă STR_CONFIG_SETTING_MAP_HEIGHT_LIMIT :Înălțimea limită a hărții: {STRING} +STR_CONFIG_SETTING_MAP_HEIGHT_LIMIT_VALUE :{NUM} STR_CONFIG_SETTING_TOO_HIGH_MOUNTAIN :{WHITE}Nu poți seta înălțimea maximă a hărții la această valoare. Cel puțin un munte de pe hartă este mai înalt de-atât. STR_CONFIG_SETTING_AUTOSLOPE :Permite terra-formarea sub clădiri, şine, etc. (auto-pante): {STRING} STR_CONFIG_SETTING_AUTOSLOPE_HELPTEXT :Permite terraformarea sub clădiri şi şine fără eliminarea acestora @@ -1231,7 +1242,7 @@ STR_CONFIG_SETTING_AUTOSCROLL_EVERY_VIEWPORT :fiecare ecran STR_CONFIG_SETTING_BRIBE :Permite mituirea autorităţilor locale: {STRING} STR_CONFIG_SETTING_BRIBE_HELPTEXT :Permite companiilor să încerce să mituiască autoritatea locală. Daca încercarea este descoperită de către un inspector, compania nu va mai putea executa nici o acțiune în oraș timp de șase luni STR_CONFIG_SETTING_ALLOW_EXCLUSIVE :Permite cumpărarea de drepturi exclusive de transport: {STRING} -STR_CONFIG_SETTING_ALLOW_EXCLUSIVE_HELPTEXT :Dacă o companie cumpără drepturi exclusive de transport într-un oras, staţiilor oponenţilor (de pasageri şi mărfuri) nu vor primi niciun fel de cargo pentru un an întreg +STR_CONFIG_SETTING_ALLOW_EXCLUSIVE_HELPTEXT :Dacă o companie cumpără drepturi exclusive de transport într-un oraș, stațiile concurenței (de pasageri și mărfuri) nu vor primi niciun fel de marfă timp de un an STR_CONFIG_SETTING_ALLOW_FUND_BUILDINGS :Permite finaţarea clădirilor noi: {STRING} STR_CONFIG_SETTING_ALLOW_FUND_BUILDINGS_HELPTEXT :Permite companiilor să doneze bani orașelor pentru construcția de noi locuințe STR_CONFIG_SETTING_ALLOW_FUND_ROAD :Permite finanțarea reconstrucției străzilor locale: {STRING} @@ -1316,6 +1327,7 @@ STR_CONFIG_SETTING_ROUGHNESS_OF_TERRAIN_ROUGH :Dur STR_CONFIG_SETTING_ROUGHNESS_OF_TERRAIN_VERY_ROUGH :Foarte dur STR_CONFIG_SETTING_VARIETY :Distribuția varietății: {STRING} STR_CONFIG_SETTING_VARIETY_HELPTEXT :(TerraGenesis only) Specifică dacă harta conține și zone muntoase și teren plat. Deoarece aceasta face harta mai plată, alte setări ar trebui să adauge zone muntoase +STR_CONFIG_SETTING_RIVER_AMOUNT :Numărul de râuri: {STRING} STR_CONFIG_SETTING_RIVER_AMOUNT_HELPTEXT :Alege câte râuri să fie generate STR_CONFIG_SETTING_TREE_PLACER :Algoritm amplasare arbori: {STRING} STR_CONFIG_SETTING_TREE_PLACER_HELPTEXT :Alegeți distribuția copacilor pe hartă: 'Original' plantează copacii dispersați uniform, 'Îmbunătățit' îi plantează grupat @@ -1421,6 +1433,7 @@ STR_CONFIG_SETTING_EXPENSES_LAYOUT_HELPTEXT :Definește stil STR_CONFIG_SETTING_AUTO_REMOVE_SIGNALS_HELPTEXT :Elimină automat semnalele când construiești căi ferate dacă ele îți vin în cale. Nu uita că asta ar putea conduce la accidente feroviare. STR_CONFIG_SETTING_FAST_FORWARD_SPEED_LIMIT :Limita de viteză pentru trecerea timpului: {STRING} STR_CONFIG_SETTING_FAST_FORWARD_SPEED_LIMIT_VAL :{NUM}% din viteza normală a jocului +STR_CONFIG_SETTING_FAST_FORWARD_SPEED_LIMIT_ZERO :Fără limită (atât de rapid pe cât permite calculatorul tău) STR_CONFIG_SETTING_SOUND_TICKER :Afișaj știri: {STRING} STR_CONFIG_SETTING_SOUND_TICKER_HELPTEXT :Redă sunet la afișarea sumarului știrilor @@ -1540,7 +1553,7 @@ STR_CONFIG_SETTING_ALLOW_SHARES_HELPTEXT :Dacă este acti STR_CONFIG_SETTING_FEEDER_PAYMENT_SHARE :Procentul din profitul pe secţiune care să fie plătit pentru alimentare: {STRING} STR_CONFIG_SETTING_FEEDER_PAYMENT_SHARE_HELPTEXT :Procentul din câştig care este oferit legăturilor intermediare pentru alimentare, oferind mai mult control asupra încasărilor STR_CONFIG_SETTING_DRAG_SIGNALS_DENSITY :Când se trage cu mouse-ul, plasează semnale la fiecare: {STRING} -STR_CONFIG_SETTING_DRAG_SIGNALS_DENSITY_HELPTEXT :Configurează distanţa la care se vor construi semnale pe şină până la următorul obstacol (semnal, intersecţie), dacâ se trage cu mouse-ul +STR_CONFIG_SETTING_DRAG_SIGNALS_DENSITY_HELPTEXT :Configurează distanța la care se vor construi semnale pe șină până la următorul obstacol (semnal, intersecție), dacă se trage cu mausul STR_CONFIG_SETTING_DRAG_SIGNALS_FIXED_DISTANCE :La plasarea mai multor semale, păstrează distanţa fixă între acestea: {STRING} STR_CONFIG_SETTING_SEMAPHORE_BUILD_BEFORE_DATE :Construieşte automat semafoare înainte de: {STRING} STR_CONFIG_SETTING_SEMAPHORE_BUILD_BEFORE_DATE_HELPTEXT :Alege anul din care se vor folosi semnale electrice pe calea feroviară. Înainte de acest an, se vor folosi semnale non-electrice care au aceeasi funcționalitate dar arată diferit @@ -1642,6 +1655,7 @@ STR_CONFIG_SETTING_LOCALISATION_UNITS_VELOCITY_HELPTEXT :Afişează vite STR_CONFIG_SETTING_LOCALISATION_UNITS_VELOCITY_IMPERIAL :Imperial (mph) STR_CONFIG_SETTING_LOCALISATION_UNITS_VELOCITY_METRIC :Metric (km/h) STR_CONFIG_SETTING_LOCALISATION_UNITS_VELOCITY_SI :SI (m/s) +STR_CONFIG_SETTING_LOCALISATION_UNITS_VELOCITY_GAMEUNITS :Unități de joc (dale/zi) STR_CONFIG_SETTING_LOCALISATION_UNITS_POWER :Unitate putere vehicule: {STRING} STR_CONFIG_SETTING_LOCALISATION_UNITS_POWER_HELPTEXT :Afişează puterea vehiculelor în interfaţă folosind unităţile selectate @@ -2202,7 +2216,7 @@ STR_CONTENT_FILTER_TITLE :{BLACK}Filtru n STR_CONTENT_OPEN_URL :{BLACK}Vizitează site-ul web STR_CONTENT_OPEN_URL_TOOLTIP :{BLACK}Vizitează site-ul web al acestei resurse STR_CONTENT_DOWNLOAD_CAPTION :{BLACK}Descarcă -STR_CONTENT_DOWNLOAD_CAPTION_TOOLTIP :{BLACK}Porneşte descărcarea resurselor selectate +STR_CONTENT_DOWNLOAD_CAPTION_TOOLTIP :{BLACK}Pornește descărcarea resurselor selectate STR_CONTENT_TOTAL_DOWNLOAD_SIZE :{SILVER}Total de descărcat: {WHITE}{BYTES} STR_CONTENT_DETAIL_TITLE :{SILVER}INFO RESURSĂ STR_CONTENT_DETAIL_SUBTITLE_UNSELECTED :{SILVER}Această resursă nu a fost selectată pentru descărcare @@ -2433,7 +2447,7 @@ STR_WATERWAYS_TOOLBAR_BUILD_DOCK_TOOLTIP :{BLACK}Construi STR_WATERWAYS_TOOLBAR_BUOY_TOOLTIP :{BLACK}Amplasează o baliză ce poate fi utilizată pentru direcţionare. Shift comută între amplasare/afişare cost estimat STR_WATERWAYS_TOOLBAR_BUILD_AQUEDUCT_TOOLTIP :{BLACK}Construieşte apeduct. Shift comută între construire/afişare cost estimat STR_WATERWAYS_TOOLBAR_CREATE_LAKE_TOOLTIP :{BLACK}Defineşte zona apei.{}Creează un canal, excepţie când Ctrl este apăsat la nivelul mării, ce va determina inundarea împrejurimilor -STR_WATERWAYS_TOOLBAR_CREATE_RIVER_TOOLTIP :{BLACK}Amplasează râuri +STR_WATERWAYS_TOOLBAR_CREATE_RIVER_TOOLTIP :{BLACK}Amplasează râuri. Ctrl selectează aria pe diagonală # Ship depot construction window STR_DEPOT_BUILD_SHIP_CAPTION :{WHITE}Orientarea şantierului naval @@ -2495,6 +2509,8 @@ STR_TREES_RANDOM_TREES_BUTTON :{BLACK}Arbori a STR_TREES_RANDOM_TREES_TOOLTIP :{BLACK}Plantează aleator arbori pe uscat STR_TREES_MODE_NORMAL_BUTTON :{BLACK}Normal STR_TREES_MODE_NORMAL_TOOLTIP :{BLACK}Plantează copaci trăgându-i peste peisaj. +STR_TREES_MODE_FOREST_SM_BUTTON :{BLACK}Dumbravă +STR_TREES_MODE_FOREST_LG_BUTTON :{BLACK}Pădure STR_TREES_MODE_FOREST_LG_TOOLTIP :{BLACK}Plantează păduri întinse prin tragerea peste peisaj. # Land generation window (SE) @@ -2553,6 +2569,7 @@ STR_FUND_INDUSTRY_PROSPECT_NEW_INDUSTRY :{BLACK}Prospect STR_FUND_INDUSTRY_BUILD_NEW_INDUSTRY :{BLACK}Construieşte STR_FUND_INDUSTRY_FUND_NEW_INDUSTRY :{BLACK}Finanţează STR_FUND_INDUSTRY_REMOVE_ALL_INDUSTRIES :{BLACK}Elimină toate industriile +STR_FUND_INDUSTRY_REMOVE_ALL_INDUSTRIES_QUERY :{YELLOW}Sigur vrei să elimini toate industriile? # Industry cargoes window STR_INDUSTRY_CARGOES_INDUSTRY_CAPTION :{WHITE}Lanţ industrial pentru industria {STRING} @@ -2700,13 +2717,16 @@ STR_ABOUT_COPYRIGHT_OPENTTD :{BLACK}OpenTTD # Framerate display window STR_FRAMERATE_CAPTION :{WHITE}FPS STR_FRAMERATE_CAPTION_SMALL :{STRING}{WHITE} ({DECIMAL}x) +STR_FRAMERATE_RATE_GAMELOOP :{BLACK}Viteza simulării: {STRING} STR_FRAMERATE_RATE_GAMELOOP_TOOLTIP :{BLACK}Număr de evenimente de joc simulate per secundă. STR_FRAMERATE_RATE_BLITTER_TOOLTIP :{BLACK}Numărul de cadre video randate per secundă. +STR_FRAMERATE_SPEED_FACTOR :{BLACK}Factorul actual de viteză a jocului: {DECIMAL}x STR_FRAMERATE_AVERAGE :{WHITE}Medie STR_FRAMERATE_MEMORYUSE :{WHITE}Memorie STR_FRAMERATE_DATA_POINTS :{BLACK}Date bazate pe măsurători {COMMA} STR_FRAMERATE_MS_GOOD :{LTBLUE}{DECIMAL} ms STR_FRAMERATE_FPS_GOOD :{LTBLUE}{DECIMAL} cadre/s +STR_FRAMERATE_FPS_WARN :{YELLOW}{DECIMAL} cadre/s STR_FRAMERATE_FPS_BAD :{RED}{DECIMAL} cadre/s STR_FRAMERATE_BYTES_WARN :{YELLOW}{BYTES} STR_FRAMERATE_BYTES_BAD :{RED}{BYTES} @@ -2718,10 +2738,12 @@ STR_FRAMERATE_DRAWING :{BLACK}Randare STR_FRAMERATE_DRAWING_VIEWPORTS :{BLACK} Vizoare globale: STR_FRAMERATE_VIDEO :{BLACK}Ieșire video: STR_FRAMERATE_GAMESCRIPT :{BLACK} Script joc: +STR_FRAMERATE_AI :{BLACK} IA {NUM} {STRING} ############ End of leave-in-this-order ############ Leave those lines in this order!! STR_FRAMETIME_CAPTION_GAMELOOP :Buclă de joc STR_FRAMETIME_CAPTION_GL_ECONOMY :Manipularea încărcăturilor +STR_FRAMETIME_CAPTION_DRAWING :Randare grafică STR_FRAMETIME_CAPTION_DRAWING_VIEWPORTS :Randarea vizorului global STR_FRAMETIME_CAPTION_SOUND :Mixaj de sunet STR_FRAMETIME_CAPTION_AI :IA {NUM} {STRING} @@ -2751,6 +2773,8 @@ STR_SAVELOAD_DETAIL_NOT_AVAILABLE :{BLACK}Nicio in STR_SAVELOAD_DETAIL_COMPANY_INDEX :{SILVER}{COMMA}: {WHITE}{STRING} STR_SAVELOAD_DETAIL_GRFSTATUS :{SILVER}NewGRF: {WHITE}{STRING} STR_SAVELOAD_FILTER_TITLE :{BLACK}Filtrare după: +STR_SAVELOAD_OVERWRITE_TITLE :{WHITE}Suprascrie fișierul +STR_SAVELOAD_DIRECTORY :{STRING} (listă) STR_SAVELOAD_OSKTITLE :{BLACK}Introduceţi un nume pentru salvare @@ -2763,8 +2787,10 @@ STR_MAPGEN_NUMBER_OF_TOWNS :{BLACK}Nr. de o STR_MAPGEN_DATE :{BLACK}Data: STR_MAPGEN_NUMBER_OF_INDUSTRIES :{BLACK}Nr. de industrii: STR_MAPGEN_HEIGHTMAP_HEIGHT :{BLACK}Cel mai înalt vârf: +STR_MAPGEN_SNOW_COVERAGE_UP :{BLACK}Mărește acoperirea cu zăpadă cu zece procente STR_MAPGEN_DESERT_COVERAGE_UP :{BLACK}Mărește întinderea deșertului cu zece procente STR_MAPGEN_DESERT_COVERAGE_DOWN :{BLACK}Reduce întinderea deșertului cu zece procente +STR_MAPGEN_DESERT_COVERAGE_TEXT :{BLACK}{NUM}% STR_MAPGEN_LAND_GENERATOR :{BLACK}Generator de teren: STR_MAPGEN_TERRAIN_TYPE :{BLACK}Tip teren: STR_MAPGEN_QUANTITY_OF_SEA_LAKES :{BLACK}Nivelul mării: @@ -2790,6 +2816,7 @@ STR_MAPGEN_HEIGHTMAP_NAME :{BLACK}Nume har STR_MAPGEN_HEIGHTMAP_SIZE_LABEL :{BLACK}Dimensiune: STR_MAPGEN_HEIGHTMAP_SIZE :{ORANGE}{NUM} x {NUM} +STR_MAPGEN_HEIGHTMAP_HEIGHT_QUERY_CAPT :{WHITE}Cel mai înalt vârf STR_MAPGEN_SNOW_COVERAGE_QUERY_CAPT :{WHITE}Acoperire cu zăpadă (în %) STR_MAPGEN_DESERT_COVERAGE_QUERY_CAPT :{WHITE}Acoperire cu deșert (în %) STR_MAPGEN_START_DATE_QUERY_CAPT :{WHITE}Modifică anul de început @@ -3011,6 +3038,7 @@ STR_EDIT_SIGN_SIGN_OSKTITLE :{BLACK}Introdu STR_TOWN_DIRECTORY_CAPTION :{WHITE}Oraşe STR_TOWN_DIRECTORY_NONE :{ORANGE}- Nimic - STR_TOWN_DIRECTORY_TOWN :{ORANGE}{TOWN}{BLACK} ({COMMA}) +STR_TOWN_DIRECTORY_CITY :{ORANGE}{TOWN}{YELLOW} (oraș){BLACK} ({COMMA}) STR_TOWN_DIRECTORY_LIST_TOOLTIP :{BLACK}Numele oraşelor - clic pe un nume pentru a centra imaginea pe oraşul respectiv. Ctrl+Click deshide o fereastra cu locaţia oraşului STR_TOWN_POPULATION :{BLACK}Populaţia totală: {COMMA} @@ -3041,6 +3069,7 @@ STR_TOWN_VIEW_RENAME_TOWN_BUTTON :Redenumire ora # Town local authority window STR_LOCAL_AUTHORITY_CAPTION :{WHITE}Autoritatea locală din {TOWN} +STR_LOCAL_AUTHORITY_ZONE :{BLACK}Zonă STR_LOCAL_AUTHORITY_COMPANY_RATINGS :{BLACK}Evaluarea companiilor de transport: STR_LOCAL_AUTHORITY_COMPANY_RATING :{YELLOW}{COMPANY} {COMPANY_NUM}: {ORANGE}{STRING} STR_LOCAL_AUTHORITY_ACTIONS_TITLE :{BLACK}Acţiuni disponibile: @@ -3057,18 +3086,19 @@ STR_LOCAL_AUTHORITY_ACTION_NEW_BUILDINGS :Finanţează co STR_LOCAL_AUTHORITY_ACTION_EXCLUSIVE_TRANSPORT :Cumpără drepturi exclusive de transport STR_LOCAL_AUTHORITY_ACTION_BRIBE :Mituieşte autoritatea locală -STR_LOCAL_AUTHORITY_ACTION_TOOLTIP_SMALL_ADVERTISING :{YELLOW}Iniţiază o campanie publicitară mică pentru a atrage mai mulţi călători şi mai multe mărfuri spre compania ta.{} Cost: {CURRENCY_LONG} -STR_LOCAL_AUTHORITY_ACTION_TOOLTIP_MEDIUM_ADVERTISING :{YELLOW}Iniţiază o campanie publicitară medie pentru a atrage mai mulţi călători şi mai multe mărfuri spre compania ta.{} Cost: {CURRENCY_LONG} -STR_LOCAL_AUTHORITY_ACTION_TOOLTIP_LARGE_ADVERTISING :{YELLOW}Iniţiază o mare campanie publicitară pentru a atrage mai mulţi călători şi mai multe mărfuri spre compania ta.{} Cost: {CURRENCY_LONG} +STR_LOCAL_AUTHORITY_ACTION_TOOLTIP_SMALL_ADVERTISING :{YELLOW}Inițiază o campanie publicitară mică pentru a atrage mai mulți călători și mai multe mărfuri spre compania ta.{}Oferă temporar un spor la cotația stației tale pe o rază mică în jurul centrului orașului.{}Cost: {CURRENCY_LONG} +STR_LOCAL_AUTHORITY_ACTION_TOOLTIP_MEDIUM_ADVERTISING :{YELLOW}Inițiază o campanie publicitară medie pentru a atrage mai mulți călători și mai multe mărfuri spre compania ta.{}Oferă temporar un spor la cotația stației tale pe o rază medie în jurul centrului orașului.{}Cost: {CURRENCY_LONG} +STR_LOCAL_AUTHORITY_ACTION_TOOLTIP_LARGE_ADVERTISING :{YELLOW}Inițiază o mare campanie publicitară pentru a atrage mai mulți călători și mai multe mărfuri spre compania ta.{}Oferă temporar un spor la cotația stației tale pe o rază mare în jurul centrului orașului.{}Cost: {CURRENCY_LONG} STR_LOCAL_AUTHORITY_ACTION_TOOLTIP_ROAD_RECONSTRUCTION :{YELLOW}Finanțează reconstrucția rețelei locale de drumuri.{}Aceasta cauzează perturbări majore ale traficului rutier timp de până la 6 luni.{}Cost: {CURRENCY_LONG} -STR_LOCAL_AUTHORITY_ACTION_TOOLTIP_STATUE_OF_COMPANY :{YELLOW}Construieşte o statuie în cinstea companiei tale.{} Cost: {CURRENCY_LONG} -STR_LOCAL_AUTHORITY_ACTION_TOOLTIP_NEW_BUILDINGS :{YELLOW}Finanţează construcţia de noi clădiri comerciale în oraş.{} Cost: {CURRENCY_LONG} -STR_LOCAL_AUTHORITY_ACTION_TOOLTIP_EXCLUSIVE_TRANSPORT :{YELLOW}Cumpără drepturi exclusive de transport în acest oraş pe o perioadă de un an. Autorităţile locale vor permite doar companiei tale să transporte călători şi mărfuri.{} Cost: {CURRENCY_LONG} +STR_LOCAL_AUTHORITY_ACTION_TOOLTIP_STATUE_OF_COMPANY :{YELLOW}Construiește o statuie în cinstea companiei tale.{}Oferă un spor permanent la cotația stației tale în acest oraș.{}Cost: {CURRENCY_LONG} +STR_LOCAL_AUTHORITY_ACTION_TOOLTIP_NEW_BUILDINGS :{YELLOW}Finanțează construcția de noi clădiri comerciale în oraș.{}Oferă un spor temporar dezvoltării în acest oraș.{}Cost: {CURRENCY_LONG} +STR_LOCAL_AUTHORITY_ACTION_TOOLTIP_EXCLUSIVE_TRANSPORT :{YELLOW}Cumpără drepturi exclusive de transport în acest oraș, timp de un an.{}Autoritățile locale nu vor permite călătorilor și mărfurilor să folosească stațiile competitorilor.{}Cost: {CURRENCY_LONG} STR_LOCAL_AUTHORITY_ACTION_TOOLTIP_BRIBE :{YELLOW}Mituieşte autorităţile locale pentru a-ţi îmbunătăţi ratingul, dar cu riscul de a fi prins şi de a plăti amenzi serioase.{} Cost: {CURRENCY_LONG} # Goal window STR_GOALS_CAPTION :{WHITE}{COMPANY} Scopuri STR_GOALS_SPECTATOR_CAPTION :{WHITE}Obiective globale +STR_GOALS_SPECTATOR :Obiective globale STR_GOALS_GLOBAL_BUTTON_HELPTEXT :{BLACK}Afișează obiectivele globale STR_GOALS_COMPANY_BUTTON :{BLACK}Companie STR_GOALS_COMPANY_BUTTON_HELPTEXT :{BLACK}Afișează obiectivele companiei @@ -3246,6 +3276,7 @@ STR_COMPANY_VIEW_PRESIDENT_MANAGER_TITLE :{WHITE}{PRESIDE STR_COMPANY_VIEW_INAUGURATED_TITLE :{GOLD}Anul înfiinţării: {WHITE}{NUM} STR_COMPANY_VIEW_COLOUR_SCHEME_TITLE :{GOLD}Schemă culori: STR_COMPANY_VIEW_VEHICLES_TITLE :{GOLD}Vehicule: +STR_COMPANY_VIEW_AIRCRAFT :{WHITE}{COMMA} {P "" "" "de "}aeronav{P ă e e} STR_COMPANY_VIEW_VEHICLES_NONE :{WHITE}Niciunul STR_COMPANY_VIEW_COMPANY_VALUE :{GOLD}Valoarea companiei: {WHITE}{CURRENCY_LONG} STR_COMPANY_VIEW_SHARES_OWNED_BY :{WHITE}({COMMA}% deţinute de {COMPANY}) @@ -3254,6 +3285,7 @@ STR_COMPANY_VIEW_INFRASTRUCTURE_RAIL :{WHITE}{COMMA} STR_COMPANY_VIEW_INFRASTRUCTURE_ROAD :{WHITE}{COMMA} pătrățele cu drumuri STR_COMPANY_VIEW_INFRASTRUCTURE_WATER :{WHITE}{COMMA} pătrățele de apă STR_COMPANY_VIEW_INFRASTRUCTURE_STATION :{WHITE}{COMMA} pătrățele stații +STR_COMPANY_VIEW_INFRASTRUCTURE_AIRPORT :{WHITE}{COMMA} {P "" "" "de "}aeroport{P "" uri uri} STR_COMPANY_VIEW_INFRASTRUCTURE_NONE :{WHITE}Niciuna STR_COMPANY_VIEW_BUILD_HQ_BUTTON :{BLACK}Constr. sediu @@ -3303,6 +3335,7 @@ STR_INDUSTRY_DIRECTORY_CAPTION :{WHITE}Industri STR_INDUSTRY_DIRECTORY_NONE :{ORANGE}- Nimic- STR_INDUSTRY_DIRECTORY_ITEM_INFO :{BLACK}{CARGO_LONG}{STRING}{YELLOW} ({COMMA}% transportat){BLACK} STR_INDUSTRY_DIRECTORY_ITEM_NOPROD :{ORANGE}{INDUSTRY} +STR_INDUSTRY_DIRECTORY_ITEM_PROD2 :{ORANGE}{INDUSTRY} {STRING}, {STRING} STR_INDUSTRY_DIRECTORY_ITEM_PROD3 :{ORANGE}{INDUSTRY} {STRING}, {STRING}, {STRING} STR_INDUSTRY_DIRECTORY_ITEM_PRODMORE :{ORANGE}{INDUSTRY} {STRING}, {STRING}, {STRING} și încă {NUM}... STR_INDUSTRY_DIRECTORY_LIST_CAPTION :{BLACK}Numele industriilor - clic pe nume pentru focalizarea pe industrie. Ctrl+Click deschide o fereastră cu locaţia industriei @@ -3320,6 +3353,7 @@ STR_INDUSTRY_VIEW_PRODUCTION_LEVEL :{BLACK}Nivelul STR_INDUSTRY_VIEW_INDUSTRY_ANNOUNCED_CLOSURE :{YELLOW}Industria a anunţat închiderea iminentă! STR_INDUSTRY_VIEW_REQUIRES_N_CARGO :{BLACK}Necesită: {YELLOW}{STRING}{STRING} +STR_INDUSTRY_VIEW_CARGO_LIST_EXTENSION :, {STRING}{STRING} STR_INDUSTRY_VIEW_REQUIRES :{BLACK}Necesită: STR_INDUSTRY_VIEW_ACCEPT_CARGO :{YELLOW}{STRING}{BLACK}{3:STRING} @@ -3692,6 +3726,7 @@ STR_VEHICLE_INFO_AGE_RUNNING_COST_YR :{BLACK}Vechime: # The next two need to stay in this order STR_VEHICLE_INFO_MAX_SPEED :{BLACK}Viteză max.: {LTBLUE}{VELOCITY} +STR_VEHICLE_INFO_MAX_SPEED_TYPE :{BLACK}Viteză max.: {LTBLUE}{VELOCITY} {BLACK}Tip de aeronavă: {LTBLUE}{STRING} STR_VEHICLE_INFO_WEIGHT_POWER_MAX_SPEED :{BLACK}Greutate: {LTBLUE}{WEIGHT_SHORT} {BLACK}Putere: {LTBLUE}{POWER}{BLACK} Viteză max.: {LTBLUE}{VELOCITY} STR_VEHICLE_INFO_WEIGHT_POWER_MAX_SPEED_MAX_TE :{BLACK}Greutate: {LTBLUE}{WEIGHT_SHORT} {BLACK}Putere: {LTBLUE}{POWER}{BLACK} Viteză max.: {LTBLUE}{VELOCITY} {BLACK}Efort tractiv: {LTBLUE}{FORCE} @@ -3841,7 +3876,7 @@ STR_ORDER_CONDITIONAL_VALUE_TOOLTIP :{BLACK}Valoarea STR_ORDER_CONDITIONAL_VALUE_CAPT :{WHITE}Introduceţi valoarea de comparat STR_ORDERS_SKIP_BUTTON :{BLACK}Treci la următoarea -STR_ORDERS_SKIP_TOOLTIP :{BLACK}Renunţă la comanda actuală si preia-o pe urmatoarea. CTRL + click face salt la comanda selectată +STR_ORDERS_SKIP_TOOLTIP :{BLACK}Sari peste comanda actuală și preia-o pe următoarea. Ctrl+clic sare la comanda selectată STR_ORDERS_DELETE_BUTTON :{BLACK}Şterge STR_ORDERS_DELETE_TOOLTIP :{BLACK}Şterge comanda selectată @@ -4147,6 +4182,7 @@ STR_WARNING_FALLBACK_SOUNDSET :{WHITE}Doar un STR_WARNING_SCREENSHOT_SIZE_CAPTION :{WHITE}Imagine de dimensiune foarte mare STR_WARNING_SCREENSHOT_SIZE_MESSAGE :{YELLOW}Imaginea capturată va avea o dimensiune de {COMMA} x {COMMA} pixeli. Realizarea unei capturi va dura ceva timp. Dorești să continui? +STR_MESSAGE_HEIGHTMAP_SUCCESSFULLY :{WHITE}Harta înălțimilor s-a salvat cu succes ca '{STRING}'. Cel mai înalt vârf este {NUM} STR_MESSAGE_SCREENSHOT_SUCCESSFULLY :{WHITE}Imagine salvată cu succes pe disc în fişierul '{STRING}' STR_ERROR_SCREENSHOT_FAILED :{WHITE}Imaginea nu a putut fi capturată! @@ -4200,6 +4236,7 @@ STR_ERROR_LOAN_ALREADY_REPAYED :{WHITE}... nu a STR_ERROR_CURRENCY_REQUIRED :{WHITE}... ai nevoie de {CURRENCY_LONG} STR_ERROR_CAN_T_REPAY_LOAN :{WHITE}Nu poţi plăti creditul... STR_ERROR_INSUFFICIENT_FUNDS :{WHITE}Nu poţi dona din banii împrumutaţi de la bancă... +STR_ERROR_CAN_T_GIVE_MONEY :{WHITE}Nu se pot da bani acestei companii... STR_ERROR_CAN_T_BUY_COMPANY :{WHITE}Nu se poate cumpăra compania... STR_ERROR_CAN_T_BUILD_COMPANY_HEADQUARTERS :{WHITE}Nu se poate construi sediul companiei... STR_ERROR_CAN_T_BUY_25_SHARE_IN_THIS :{WHITE}Nu se pot cumpăra 25% din acţiunile acestei companii... @@ -4312,7 +4349,7 @@ STR_ERROR_TRAINS_CAN_ONLY_BE_ALTERED_INSIDE_A_DEPOT :{WHITE}Trenuril STR_ERROR_TRAIN_TOO_LONG :{WHITE}Tren prea lung STR_ERROR_CAN_T_REVERSE_DIRECTION_RAIL_VEHICLE :{WHITE}Nu se poate inversa direcţia vehiculului... STR_ERROR_CAN_T_REVERSE_DIRECTION_RAIL_VEHICLE_MULTIPLE_UNITS :{WHITE}... este alcătuit din mai multe unități -STR_ERROR_INCOMPATIBLE_RAIL_TYPES :Tipuri de sine incompatibile +STR_ERROR_INCOMPATIBLE_RAIL_TYPES :Tipurile de șine sunt incompatibile STR_ERROR_CAN_T_MOVE_VEHICLE :{WHITE}Nu se poate muta vehiculul... STR_ERROR_REAR_ENGINE_FOLLOW_FRONT :{WHITE}Al doilea vagon+motor va avea mereu aceeasi destinatie ca si primul @@ -4353,7 +4390,10 @@ STR_ERROR_CAN_T_REMOVE_ROAD_FROM :{WHITE}Nu se po STR_ERROR_CAN_T_REMOVE_TRAMWAY_FROM :{WHITE}Nu pot înlătura şina de tramvai de aici... STR_ERROR_THERE_IS_NO_ROAD :{WHITE}...nu există drum aici STR_ERROR_THERE_IS_NO_TRAMWAY :{WHITE}...nu există şină de tramvai aici +STR_ERROR_CAN_T_CONVERT_ROAD :{WHITE}Tipul de drum nu poate fi convertit aici... +STR_ERROR_NO_SUITABLE_ROAD :{WHITE}Niciun drum adecvat STR_ERROR_NO_SUITABLE_TRAMWAY :{WHITE}Niciun tramvai adecvat +STR_ERROR_INCOMPATIBLE_TRAMWAY :{WHITE}... tramvai incompatibil # Waterway construction errors STR_ERROR_CAN_T_BUILD_CANALS :{WHITE}Nu pot construi un canal aici... @@ -4482,8 +4522,8 @@ STR_ERROR_CAN_T_INSERT_NEW_ORDER :{WHITE}Nu se po STR_ERROR_CAN_T_DELETE_THIS_ORDER :{WHITE}Nu se poate şterge această comandă... STR_ERROR_CAN_T_MODIFY_THIS_ORDER :{WHITE}Nu se poate modifica această comandă... STR_ERROR_CAN_T_MOVE_THIS_ORDER :{WHITE}Nu pot muta acest ordin... -STR_ERROR_CAN_T_SKIP_ORDER :{WHITE}Nu pot renunta la comanda actuala... -STR_ERROR_CAN_T_SKIP_TO_ORDER :{WHITE}Nu pot sări la ordinul selectat... +STR_ERROR_CAN_T_SKIP_ORDER :{WHITE}Nu se poate sări peste comanda actuală... +STR_ERROR_CAN_T_SKIP_TO_ORDER :{WHITE}Nu pot sări la comanda selectată... STR_ERROR_CAN_T_COPY_SHARE_ORDER :{WHITE}... vehiculul nu poate ajunge la toate staţiile STR_ERROR_CAN_T_ADD_ORDER :{WHITE}... vehiculul nu poate ajunge la acea staţie STR_ERROR_CAN_T_ADD_ORDER_SHARED :{WHITE}... un vehicul care are acest ordin nu poate ajunge la acea staţie diff --git a/src/lang/russian.txt b/src/lang/russian.txt index 2ce6e0dc64..c1b4b261c0 100644 --- a/src/lang/russian.txt +++ b/src/lang/russian.txt @@ -1148,9 +1148,11 @@ STR_GAME_OPTIONS_RESOLUTION_OTHER :Другое STR_GAME_OPTIONS_RESOLUTION_ITEM :{NUM}x{NUM} STR_GAME_OPTIONS_VIDEO_ACCELERATION :{BLACK}Аппаратное ускорение -STR_GAME_OPTIONS_VIDEO_ACCELERATION_TOOLTIP :{BLACK}Поставьте отметку, чтобы включить аппаратное ускорение в OpenTTD. Но для этого игру придётся перезапустить. +STR_GAME_OPTIONS_VIDEO_ACCELERATION_TOOLTIP :{BLACK}Нажмите здесь, чтобы включить/выключить аппаратное ускорение в OpenTTD. После этого игру потребуется перезапустить. STR_GAME_OPTIONS_VIDEO_ACCELERATION_RESTART :{WHITE}Эта настройка будет применена только после перезапуска игры +STR_GAME_OPTIONS_VIDEO_VSYNC :{BLACK}Вертикальная синхронизация +STR_GAME_OPTIONS_VIDEO_VSYNC_TOOLTIP :{BLACK}Нажмите здесь, чтобы включить/выключить вертикальную синхронизацию. После этого игру потребуется перезапустить. Работает только при включённом аппаратном ускорении. STR_GAME_OPTIONS_GUI_ZOOM_FRAME :{BLACK}Размер элементов интерфейса STR_GAME_OPTIONS_GUI_ZOOM_DROPDOWN_TOOLTIP :{BLACK}Выберите размер элементов интерфейса diff --git a/src/lang/spanish.txt b/src/lang/spanish.txt index 0214ec0ef5..292d32426f 100644 --- a/src/lang/spanish.txt +++ b/src/lang/spanish.txt @@ -3227,11 +3227,11 @@ STR_LOCAL_AUTHORITY_ACTION_EXCLUSIVE_TRANSPORT :Comprar derecho STR_LOCAL_AUTHORITY_ACTION_BRIBE :Sobornar a la autoridad local STR_LOCAL_AUTHORITY_ACTION_TOOLTIP_SMALL_ADVERTISING :{YELLOW}Iniciar una campaña publicitaria local pequeña para atraer más pasajeros y carga a sus servicios de transporte.{}Proporciona un incremento temporal en la calificación de las estaciones en un radio pequeño alrededor del centro del municipio.{}Coste: {CURRENCY_LONG} -STR_LOCAL_AUTHORITY_ACTION_TOOLTIP_MEDIUM_ADVERTISING :{YELLOW}Iniciar una campaña publicitaria local mediana para atraer más pasajeros y carga a sus servicios de transporte.{}Proporciona un incremento temporal en la calificación de las estaciones en un radio mediano alrededor del centro del municipio.{}Coste: {CURRENCY_LONG} +STR_LOCAL_AUTHORITY_ACTION_TOOLTIP_MEDIUM_ADVERTISING :{YELLOW}Inicia una campaña publicitaria local mediana para atraer más pasajeros y carga a sus servicios de transporte.{}Proporciona un incremento temporal en la calificación de las estaciones en un radio mediano alrededor del centro del municipio.{}Coste: {CURRENCY_LONG} STR_LOCAL_AUTHORITY_ACTION_TOOLTIP_LARGE_ADVERTISING :{YELLOW}Iniciar una campaña publicitaria local grande para atraer más pasajeros y carga a sus servicios de transporte.{}Proporciona un incremento temporal en la calificación de las estaciones en un radio grande alrededor del centro del municipio.{}Coste: {CURRENCY_LONG} -STR_LOCAL_AUTHORITY_ACTION_TOOLTIP_ROAD_RECONSTRUCTION :{YELLOW}Pagar la reconstrucción de las carreteras locales.{}Provoca considerables complicaciones de tráfico durante 6 meses.{}Coste: {CURRENCY_LONG} +STR_LOCAL_AUTHORITY_ACTION_TOOLTIP_ROAD_RECONSTRUCTION :{YELLOW}Paga la reconstrucción de las carreteras locales.{}Provoca considerables complicaciones de tráfico durante 6 meses.{}Coste: {CURRENCY_LONG} STR_LOCAL_AUTHORITY_ACTION_TOOLTIP_STATUE_OF_COMPANY :{YELLOW}Construye una estatua en honor a su empresa.{}Proporciona un incremento permanente en la calificación de las estaciones de este municipio.{}Coste: {CURRENCY_LONG} -STR_LOCAL_AUTHORITY_ACTION_TOOLTIP_NEW_BUILDINGS :{YELLOW}Pagar la construcción de nuevos edificios comerciales en el municipio.{}Proporciona un incremento temporal en el crecimiento del municipio.{}Coste: {CURRENCY_LONG} +STR_LOCAL_AUTHORITY_ACTION_TOOLTIP_NEW_BUILDINGS :{YELLOW}Paga la construcción de nuevos edificios comerciales en el municipio.{}Proporciona un incremento temporal en el crecimiento del municipio.{}Coste: {CURRENCY_LONG} STR_LOCAL_AUTHORITY_ACTION_TOOLTIP_EXCLUSIVE_TRANSPORT :{YELLOW}Compra derechos de transporte exclusivos en este municipio durante un año.{}Las autoridades no permitirán el uso de las estaciones de la competencia.{}Coste: {CURRENCY_LONG} STR_LOCAL_AUTHORITY_ACTION_TOOLTIP_BRIBE :{YELLOW}Soborna a las autoridades locales para aumentar su calificación, con el riesgo de sufrir una penalización severa si es descubierto.{}Coste: {CURRENCY_LONG} From 010d977b16cd73955fa9aa128b7a92450d930dc3 Mon Sep 17 00:00:00 2001 From: translators Date: Thu, 15 Apr 2021 17:52:17 +0000 Subject: [PATCH 074/268] Update: Translations from eints hungarian: 5 changes by nemesbala catalan: 2 changes by J0anJosep tamil: 3 changes by Saran-S-Menon --- src/lang/catalan.txt | 2 ++ src/lang/hungarian.txt | 5 +++++ src/lang/tamil.txt | 3 +++ 3 files changed, 10 insertions(+) diff --git a/src/lang/catalan.txt b/src/lang/catalan.txt index 65f17997bd..e8b0892d42 100644 --- a/src/lang/catalan.txt +++ b/src/lang/catalan.txt @@ -1007,6 +1007,8 @@ STR_GAME_OPTIONS_VIDEO_ACCELERATION :{BLACK}Accelera STR_GAME_OPTIONS_VIDEO_ACCELERATION_TOOLTIP :{BLACK}Seleccioneu aquesta opció per permetre que l'OpenTTD provi d'usar acceleració per maquinari. Si es canvia l'opció, s'aplicarà quan es reiniciï el programa. STR_GAME_OPTIONS_VIDEO_ACCELERATION_RESTART :{WHITE}La configuració tindrà efecte quan es reiniciï el programa. +STR_GAME_OPTIONS_VIDEO_VSYNC :{BLACK}VSync +STR_GAME_OPTIONS_VIDEO_VSYNC_TOOLTIP :{BLACK}Selecciona aquesta casella per activar la sincronització vertical de la pantalla. Els canvis s'aplicaran quan es reiniciï el programa. Només funciona si s'activa l'acceleració per maquinari. STR_GAME_OPTIONS_GUI_ZOOM_FRAME :{BLACK}Mida de la interfície STR_GAME_OPTIONS_GUI_ZOOM_DROPDOWN_TOOLTIP :{BLACK}Escull la mida dels elements de la interfície diff --git a/src/lang/hungarian.txt b/src/lang/hungarian.txt index f9c689f7f8..ca6483c5b3 100644 --- a/src/lang/hungarian.txt +++ b/src/lang/hungarian.txt @@ -1070,6 +1070,8 @@ STR_GAME_OPTIONS_VIDEO_ACCELERATION :{BLACK}Hardvere STR_GAME_OPTIONS_VIDEO_ACCELERATION_TOOLTIP :{BLACK}Bekapcsolásával az OpenTTD hardveres gyorsítást próbál alkalmazni. A beállítás csak a játék újraindítása után lép érvénybe. STR_GAME_OPTIONS_VIDEO_ACCELERATION_RESTART :{WHITE}Ez a beállítás csak a játék újraindítása után lép érvénybe +STR_GAME_OPTIONS_VIDEO_VSYNC :{BLACK}VSync (Vertikális Szinkronizáció) +STR_GAME_OPTIONS_VIDEO_VSYNC_TOOLTIP :{BLACK}Jelöld be ezt a négyzetet hogy engedélyezd a v-sync-et. A változtatás csak a játék újraindítása után fog érvényesülni. Kizárólag hardware gyorsítással működik! STR_GAME_OPTIONS_GUI_ZOOM_FRAME :{BLACK}Felület mérete STR_GAME_OPTIONS_GUI_ZOOM_DROPDOWN_TOOLTIP :{BLACK}Használni kívánt felületméret kiválasztása @@ -1203,6 +1205,7 @@ STR_CONFIG_SETTING_TREE_CAPTION :{WHITE}Beállí STR_CONFIG_SETTING_FILTER_TITLE :{BLACK}Szűrő kifejezés: STR_CONFIG_SETTING_EXPAND_ALL :{BLACK}Összes szétnyitása STR_CONFIG_SETTING_COLLAPSE_ALL :{BLACK}Összes összecsukása +STR_CONFIG_SETTING_RESET_ALL :{BLACK}Visszaállítja az összes értéket STR_CONFIG_SETTING_NO_EXPLANATION_AVAILABLE_HELPTEXT :(leírás nem elérhető) STR_CONFIG_SETTING_DEFAULT_VALUE :{LTBLUE}Alapértelmezett érték: {ORANGE}{STRING} STR_CONFIG_SETTING_TYPE :{LTBLUE}Beállítás típusa: {ORANGE}{STRING} @@ -1211,6 +1214,8 @@ STR_CONFIG_SETTING_TYPE_GAME_MENU :Játék beáll STR_CONFIG_SETTING_TYPE_GAME_INGAME :Játék beállítás (mentésben tárolva; csak a jelenlegi játékot befolyásolja) STR_CONFIG_SETTING_TYPE_COMPANY_MENU :Vállalat beállítás (mentésben tárolva; csak az új játékokat befolyásolja) STR_CONFIG_SETTING_TYPE_COMPANY_INGAME :Vállalat beállítás (mentésben tárolva; csak a jelenlegi vállalatot befolyásolja) +STR_CONFIG_SETTING_RESET_ALL_CONFIRMATION_DIALOG_CAPTION :{WHITE}Figyelem! +STR_CONFIG_SETTING_RESET_ALL_CONFIRMATION_DIALOG_TEXT :{WHITE}Ez a művelet minden játékbeállítást visszaállít.{}Biztos, hogy folytatni akarja a műveletet? STR_CONFIG_SETTING_RESTRICT_CATEGORY :{BLACK}Kategória: STR_CONFIG_SETTING_RESTRICT_TYPE :{BLACK}Típus: diff --git a/src/lang/tamil.txt b/src/lang/tamil.txt index a919a195aa..99b2384e73 100644 --- a/src/lang/tamil.txt +++ b/src/lang/tamil.txt @@ -962,6 +962,7 @@ STR_GAME_OPTIONS_FULLSCREEN_TOOLTIP :{BLACK}OpenTTD- STR_GAME_OPTIONS_RESOLUTION :{BLACK}திரையின் அளவு STR_GAME_OPTIONS_RESOLUTION_TOOLTIP :{BLACK}திரை அளவினைத் தேர்ந்தெடுக்கவும் STR_GAME_OPTIONS_RESOLUTION_OTHER :மற்றவை +STR_GAME_OPTIONS_RESOLUTION_ITEM :{NUM}x{NUM} @@ -1697,6 +1698,7 @@ STR_FACE_LOAD :{BLACK}ஏற STR_FACE_LOAD_TOOLTIP :{BLACK}பிடித்த முகத்தினை பதிவேற்று STR_FACE_LOAD_DONE :{WHITE}உங்களுடைய பிடித்த முகம் OpenTTD உள்ளமைவு கோப்பிலிருந்து ஏற்றப்பட்டுள்ளது STR_FACE_FACECODE :{BLACK}விளையாடுபவர் முக எண் +STR_FACE_FACECODE_TOOLTIP :{BLACK}நிறுவனரின் முக எண்னை பார் மற்றும்/அல்லது அமை STR_FACE_FACECODE_CAPTION :{WHITE}நிறுவனரின் முக எண்னை பார் அல்லது அமை STR_FACE_FACECODE_SET :{WHITE}புதிய முக எண் குறி அமைக்கப்பட்டது STR_FACE_SAVE :{BLACK}சேமி @@ -2118,6 +2120,7 @@ STR_RAIL_TOOLBAR_MAGLEV_CONSTRUCTION_CAPTION :மேக்ல STR_RAIL_TOOLBAR_TOOLTIP_BUILD_RAILROAD_TRACK :{BLACK}இரயில்வே இருப்புப் பாதையினை கட்டவும். Shift அழுத்தினால் கட்டுமான/செலவு மதிப்பீடு காட்டப்படும் STR_RAIL_TOOLBAR_TOOLTIP_BUILD_TRAIN_DEPOT_FOR_BUILDING :{BLACK}இரயில் பணிமனையினைக் (இரயில்களை வாங்க மற்றும் பழுதுபார்க்க) கட்டவும். Shift அழுத்தினால் கட்டுமான/செலவு மதிப்பீடு காட்டப்படும் +STR_RAIL_TOOLBAR_TOOLTIP_CONVERT_RAIL_TO_WAYPOINT :{BLACK}இரயில் தடத்தை வழிப்புள்ளியாக மாற்றும். வழிப்புள்ளிகளை இணைக்க Ctrl-ஐ அழுத்தவும். கட்டுமான/செலவு மதிப்பீட்டினை காட்டShift-ஐ அழுத்தவும். STR_RAIL_TOOLBAR_TOOLTIP_BUILD_RAILROAD_STATION :{BLACK}இரயில் நிலையத்தினை கட்டவும். Ctrl அழுத்தினால் நிலையங்களினை இணைக்கலாம். Shift அழுத்தினால் கட்டுமான/செலவு மதிப்பீடு காட்டப்படும் STR_RAIL_TOOLBAR_TOOLTIP_BUILD_RAILROAD_SIGNALS :{BLACK}இரயில்வே சிக்னல்களைக் கட்டவும். Ctrl அழுத்தினால் சிக்னல் வகைகளை மாற்றலாம்{}இரயில் தடத்தின்மேல் இழுத்தினால் சிக்னல்களை அமைக்கலாம். Ctrl அழுத்தினால் அடுத்த எணைப்பு வரை சிக்னல்கள் அமைக்கப்படும்{}Ctrl+Click அழுத்தினால் சிக்னல் தேர்ந்தெடுக்கும் திரை தெரியும்/மரையும். Shift அழுத்தினால் கட்டுமான/செலவு மதிப்பீடு காட்டப்படும் STR_RAIL_TOOLBAR_TOOLTIP_BUILD_RAILROAD_BRIDGE :{BLACK}இரயில்வே பாலத்தினை கட்டவும். Shift அழுத்தினால் கட்டுமான/செலவு மதிப்பீடு காட்டப்படும் From a4db7c844d761a68b19ad88c9bfee8fcf5dc71b5 Mon Sep 17 00:00:00 2001 From: translators Date: Fri, 16 Apr 2021 17:52:24 +0000 Subject: [PATCH 075/268] Update: Translations from eints korean: 6 changes by telk5093 portuguese (brazilian): 2 changes by Greavez --- src/lang/brazilian_portuguese.txt | 2 ++ src/lang/korean.txt | 12 ++++++------ 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/lang/brazilian_portuguese.txt b/src/lang/brazilian_portuguese.txt index 3d902f7234..89d8f32043 100644 --- a/src/lang/brazilian_portuguese.txt +++ b/src/lang/brazilian_portuguese.txt @@ -1007,6 +1007,8 @@ STR_GAME_OPTIONS_VIDEO_ACCELERATION :{BLACK}Acelera STR_GAME_OPTIONS_VIDEO_ACCELERATION_TOOLTIP :{BLACK}Marque esta caixa para permitir que o OpenTTD tente usar a aceleração de hardware. Qualquer mudança nesta configuração só será aplicada após reiniciar o jogo. STR_GAME_OPTIONS_VIDEO_ACCELERATION_RESTART :{WHITE}A configuração só terá efeito após reiniciar o jogo +STR_GAME_OPTIONS_VIDEO_VSYNC :{BLACK}VSync +STR_GAME_OPTIONS_VIDEO_VSYNC_TOOLTIP :{BLACK}Marque esta caixa para habilitar o v-sync na tela. Qualquer mudança nesta configuração só será aplicada após reiniciar o jogo. Só funciona com a aceleração de hardware habilitada STR_GAME_OPTIONS_GUI_ZOOM_FRAME :{BLACK}Tamanho da interface STR_GAME_OPTIONS_GUI_ZOOM_DROPDOWN_TOOLTIP :{BLACK}Selecione o tamanho de elemento de interface a ser usado diff --git a/src/lang/korean.txt b/src/lang/korean.txt index 2f8d593cde..d6efaa457f 100644 --- a/src/lang/korean.txt +++ b/src/lang/korean.txt @@ -1729,38 +1729,38 @@ STR_CONFIG_SETTING_SHORT_PATH_SATURATION_HELPTEXT :종종 두 역 STR_CONFIG_SETTING_LOCALISATION_UNITS_VELOCITY :속력 단위: {STRING} STR_CONFIG_SETTING_LOCALISATION_UNITS_VELOCITY_HELPTEXT :속력를 표시할 때 선택한 단위를 사용하여 나타냅니다. -STR_CONFIG_SETTING_LOCALISATION_UNITS_VELOCITY_IMPERIAL :임페리얼법 (mph) +STR_CONFIG_SETTING_LOCALISATION_UNITS_VELOCITY_IMPERIAL :야드파운드법 (mph) STR_CONFIG_SETTING_LOCALISATION_UNITS_VELOCITY_METRIC :미터법 (km/h) STR_CONFIG_SETTING_LOCALISATION_UNITS_VELOCITY_SI :국제표준규격 (m/s) STR_CONFIG_SETTING_LOCALISATION_UNITS_VELOCITY_GAMEUNITS :게임 단위 (칸/일) STR_CONFIG_SETTING_LOCALISATION_UNITS_POWER :차량의 힘 단위: {STRING} STR_CONFIG_SETTING_LOCALISATION_UNITS_POWER_HELPTEXT :출력할 차량의 힘 단위를 선택합니다. -STR_CONFIG_SETTING_LOCALISATION_UNITS_POWER_IMPERIAL :임페리얼법 (마력) +STR_CONFIG_SETTING_LOCALISATION_UNITS_POWER_IMPERIAL :야드파운드법 (마력) STR_CONFIG_SETTING_LOCALISATION_UNITS_POWER_METRIC :미터법 (마력) STR_CONFIG_SETTING_LOCALISATION_UNITS_POWER_SI :국제표준규격 (kW) STR_CONFIG_SETTING_LOCALISATION_UNITS_WEIGHT :무게 단위: {STRING} STR_CONFIG_SETTING_LOCALISATION_UNITS_WEIGHT_HELPTEXT :무게를 표시할 때 선택한 단위를 사용하여 나타냅니다. -STR_CONFIG_SETTING_LOCALISATION_UNITS_WEIGHT_IMPERIAL :임페리얼법 (미국 톤) +STR_CONFIG_SETTING_LOCALISATION_UNITS_WEIGHT_IMPERIAL :야드파운드법 (미국 톤) STR_CONFIG_SETTING_LOCALISATION_UNITS_WEIGHT_METRIC :미터법 (톤) STR_CONFIG_SETTING_LOCALISATION_UNITS_WEIGHT_SI :국제표준규격 (kg) STR_CONFIG_SETTING_LOCALISATION_UNITS_VOLUME :부피 단위: {STRING} STR_CONFIG_SETTING_LOCALISATION_UNITS_VOLUME_HELPTEXT :부피를 표시할 때 선택한 단위를 사용하여 나타냅니다. -STR_CONFIG_SETTING_LOCALISATION_UNITS_VOLUME_IMPERIAL :임페리얼법 (갤런) +STR_CONFIG_SETTING_LOCALISATION_UNITS_VOLUME_IMPERIAL :야드파운드법 (갤런) STR_CONFIG_SETTING_LOCALISATION_UNITS_VOLUME_METRIC :미터법 (리터) STR_CONFIG_SETTING_LOCALISATION_UNITS_VOLUME_SI :국제표준규격 (m³) STR_CONFIG_SETTING_LOCALISATION_UNITS_FORCE :견인 효과 단위: {STRING} STR_CONFIG_SETTING_LOCALISATION_UNITS_FORCE_HELPTEXT :견인 효과(견인력)를 표시할 때 선택한 단위를 사용하여 나타냅니다. -STR_CONFIG_SETTING_LOCALISATION_UNITS_FORCE_IMPERIAL :임페리얼법 (파운드중) +STR_CONFIG_SETTING_LOCALISATION_UNITS_FORCE_IMPERIAL :야드파운드법 (파운드중) STR_CONFIG_SETTING_LOCALISATION_UNITS_FORCE_METRIC :미터법 (kgf) STR_CONFIG_SETTING_LOCALISATION_UNITS_FORCE_SI :국제표준규격 (kN) STR_CONFIG_SETTING_LOCALISATION_UNITS_HEIGHT :높이 단위: {STRING} STR_CONFIG_SETTING_LOCALISATION_UNITS_HEIGHT_HELPTEXT :높이를 표시할 때 선택한 단위를 사용하여 나타냅니다. -STR_CONFIG_SETTING_LOCALISATION_UNITS_HEIGHT_IMPERIAL :임페리얼법 (ft) +STR_CONFIG_SETTING_LOCALISATION_UNITS_HEIGHT_IMPERIAL :야드파운드법 (ft) STR_CONFIG_SETTING_LOCALISATION_UNITS_HEIGHT_METRIC :미터법 (m) STR_CONFIG_SETTING_LOCALISATION_UNITS_HEIGHT_SI :국제표준규격 (m) From 837994034d1836e30658b9e38d5ce1b2df1e2846 Mon Sep 17 00:00:00 2001 From: Peter Nelson Date: Thu, 15 Apr 2021 23:58:49 +0100 Subject: [PATCH 076/268] Fix: Sizing of Multiplayer server list incorrect when GUI zoom doesn't match Font zoom. The server information panel was scaled by GUI scale, which could result in a panel that is longer than the server list. This height difference is then maintained when the window is resized to fill the screen. Instead, specify the minimum size by number of text lines and (summed total) padding. --- src/network/network_gui.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/network/network_gui.cpp b/src/network/network_gui.cpp index ae263cf00c..c1f11e84e3 100644 --- a/src/network/network_gui.cpp +++ b/src/network/network_gui.cpp @@ -954,7 +954,7 @@ static const NWidgetPart _nested_network_game_widgets[] = { EndContainer(), NWidget(WWT_PANEL, COLOUR_LIGHT_BLUE, WID_NG_DETAILS), NWidget(NWID_VERTICAL, NC_EQUALSIZE), SetPIP(5, 5, 5), - NWidget(WWT_EMPTY, INVALID_COLOUR, WID_NG_DETAILS_SPACER), SetMinimalSize(140, 155), SetResize(0, 1), SetFill(1, 1), // Make sure it's at least this wide + NWidget(WWT_EMPTY, INVALID_COLOUR, WID_NG_DETAILS_SPACER), SetMinimalSize(140, 0), SetMinimalTextLines(15, 24 + WD_PAR_VSEP_NORMAL), SetResize(0, 1), SetFill(1, 1), // Make sure it's at least this wide NWidget(NWID_HORIZONTAL, NC_NONE), SetPIP(5, 5, 5), NWidget(NWID_SELECTION, INVALID_COLOUR, WID_NG_NEWGRF_MISSING_SEL), NWidget(WWT_PUSHTXTBTN, COLOUR_WHITE, WID_NG_NEWGRF_MISSING), SetFill(1, 0), SetDataTip(STR_NEWGRF_SETTINGS_FIND_MISSING_CONTENT_BUTTON, STR_NEWGRF_SETTINGS_FIND_MISSING_CONTENT_TOOLTIP), From fbef63822c9bcc3b2bb26988cd14a6767eba5fd8 Mon Sep 17 00:00:00 2001 From: Peter Nelson Date: Sun, 11 Apr 2021 23:02:59 +0100 Subject: [PATCH 077/268] Codechange: Use std::deque for chat history instead of fixed array --- src/network/network_chat_gui.cpp | 60 +++++++++----------------------- 1 file changed, 16 insertions(+), 44 deletions(-) diff --git a/src/network/network_chat_gui.cpp b/src/network/network_chat_gui.cpp index 80a0b78695..a1157e2615 100644 --- a/src/network/network_chat_gui.cpp +++ b/src/network/network_chat_gui.cpp @@ -8,6 +8,7 @@ /** @file network_chat_gui.cpp GUI for handling chat messages. */ #include /* va_list */ +#include #include "../stdafx.h" #include "../strings_func.h" @@ -44,7 +45,7 @@ struct ChatMessage { }; /* used for chat window */ -static ChatMessage *_chatmsg_list = nullptr; ///< The actual chat message list. +static std::deque _chatmsg_list; ///< The actual chat message list. static bool _chatmessage_dirty = false; ///< Does the chat message need repainting? static bool _chatmessage_visible = false; ///< Is a chat message visible. static bool _chat_tab_completion_active; ///< Whether tab completion is active. @@ -57,20 +58,6 @@ static uint MAX_CHAT_MESSAGES = 0; ///< The limit of chat messages to sho static PointDimension _chatmsg_box; static uint8 *_chatmessage_backup = nullptr; ///< Backup in case text is moved. -/** - * Count the chat messages. - * @return The number of chat messages. - */ -static inline uint GetChatMessageCount() -{ - uint i = 0; - for (; i < MAX_CHAT_MESSAGES; i++) { - if (_chatmsg_list[i].message[0] == '\0') break; - } - - return i; -} - /** * Add a text message to the 'chat window' to be shown * @param colour The colour this message is to be shown in @@ -88,13 +75,11 @@ void CDECL NetworkAddChatMessage(TextColour colour, uint duration, const char *m Utf8TrimString(buf, DRAW_STRING_BUFFER); - uint msg_count = GetChatMessageCount(); - if (MAX_CHAT_MESSAGES == msg_count) { - memmove(&_chatmsg_list[0], &_chatmsg_list[1], sizeof(_chatmsg_list[0]) * (msg_count - 1)); - msg_count = MAX_CHAT_MESSAGES - 1; + if (_chatmsg_list.size() == MAX_CHAT_MESSAGES) { + _chatmsg_list.pop_back(); } - ChatMessage *cmsg = &_chatmsg_list[msg_count++]; + ChatMessage *cmsg = &_chatmsg_list.emplace_front(); strecpy(cmsg->message, buf, lastof(cmsg->message)); cmsg->colour = (colour & TC_IS_PALETTE_COLOUR) ? colour : TC_WHITE; cmsg->remove_time = std::chrono::steady_clock::now() + std::chrono::seconds(duration); @@ -115,15 +100,11 @@ void NetworkInitChatMessage() { MAX_CHAT_MESSAGES = _settings_client.gui.network_chat_box_height; - _chatmsg_list = ReallocT(_chatmsg_list, _settings_client.gui.network_chat_box_height); + _chatmsg_list.clear(); _chatmsg_box.x = 10; _chatmsg_box.width = _settings_client.gui.network_chat_box_width_pct * _screen.width / 100; NetworkReInitChatBoxSize(); _chatmessage_visible = false; - - for (uint i = 0; i < MAX_CHAT_MESSAGES; i++) { - _chatmsg_list[i].message[0] = '\0'; - } } /** Hide the chatbox */ @@ -175,21 +156,13 @@ void NetworkUndrawChatMessage() /** Check if a message is expired. */ void NetworkChatMessageLoop() { - for (uint i = 0; i < MAX_CHAT_MESSAGES; i++) { - ChatMessage *cmsg = &_chatmsg_list[i]; - if (cmsg->message[0] == '\0') continue; - + for (auto it = _chatmsg_list.begin(); it != _chatmsg_list.end();) { /* Message has expired, remove from the list */ - if (std::chrono::steady_clock::now() > cmsg->remove_time) { - /* Move the remaining messages over the current message */ - if (i != MAX_CHAT_MESSAGES - 1) memmove(cmsg, cmsg + 1, sizeof(*cmsg) * (MAX_CHAT_MESSAGES - i - 1)); - - /* Mark the last item as empty */ - _chatmsg_list[MAX_CHAT_MESSAGES - 1].message[0] = '\0'; + if (std::chrono::steady_clock::now() > it->remove_time) { + it = _chatmsg_list.erase(it); _chatmessage_dirty = true; - - /* Go one item back, because we moved the array 1 to the left */ - i--; + } else { + it++; } } } @@ -206,8 +179,7 @@ void NetworkDrawChatMessage() if (_iconsole_mode == ICONSOLE_FULL) return; /* Check if we have anything to draw at all */ - uint count = GetChatMessageCount(); - if (count == 0) return; + if (_chatmsg_list.size() == 0) return; int x = _chatmsg_box.x; int y = _screen.height - _chatmsg_box.y - _chatmsg_box.height; @@ -230,8 +202,8 @@ void NetworkDrawChatMessage() _cur_dpi = &_screen; // switch to _screen painting int string_height = 0; - for (uint i = 0; i < count; i++) { - SetDParamStr(0, _chatmsg_list[i].message); + for (auto &cmsg : _chatmsg_list) { + SetDParamStr(0, cmsg.message); string_height += GetStringLineCount(STR_JUST_RAW_STRING, width - 1) * FONT_HEIGHT_NORMAL + NETWORK_CHAT_LINE_SPACING; } @@ -247,8 +219,8 @@ void NetworkDrawChatMessage() /* Paint the chat messages starting with the lowest at the bottom */ int ypos = bottom - 2; - for (int i = count - 1; i >= 0; i--) { - ypos = DrawStringMultiLine(_chatmsg_box.x + 3, _chatmsg_box.x + _chatmsg_box.width - 1, top, ypos, _chatmsg_list[i].message, _chatmsg_list[i].colour, SA_LEFT | SA_BOTTOM | SA_FORCE) - NETWORK_CHAT_LINE_SPACING; + for (auto &cmsg : _chatmsg_list) { + ypos = DrawStringMultiLine(_chatmsg_box.x + 3, _chatmsg_box.x + _chatmsg_box.width - 1, top, ypos, cmsg.message, cmsg.colour, SA_LEFT | SA_BOTTOM | SA_FORCE) - NETWORK_CHAT_LINE_SPACING; if (ypos < top) break; } From cb9f56df0c8eb6e711402e9f840bc78f23614bca Mon Sep 17 00:00:00 2001 From: Peter Nelson Date: Sun, 11 Apr 2021 23:26:57 +0100 Subject: [PATCH 078/268] Feature: Show previous chat history when the chat message box is open --- src/network/network_chat_gui.cpp | 43 +++++++++++++++++++++++++++----- 1 file changed, 37 insertions(+), 6 deletions(-) diff --git a/src/network/network_chat_gui.cpp b/src/network/network_chat_gui.cpp index a1157e2615..5a92ef0db6 100644 --- a/src/network/network_chat_gui.cpp +++ b/src/network/network_chat_gui.cpp @@ -51,6 +51,12 @@ static bool _chatmessage_visible = false; ///< Is a chat message visible. static bool _chat_tab_completion_active; ///< Whether tab completion is active. static uint MAX_CHAT_MESSAGES = 0; ///< The limit of chat messages to show. +/** + * Time the chat history was marked dirty. This is used to determine if expired + * messages have recently expired and should cause a redraw to hide them. + */ +static std::chrono::steady_clock::time_point _chatmessage_dirty_time; + /** * The chatbox grows from the bottom so the coordinates are pixels from * the left and pixels from the bottom. The height is the maximum height. @@ -58,6 +64,23 @@ static uint MAX_CHAT_MESSAGES = 0; ///< The limit of chat messages to sho static PointDimension _chatmsg_box; static uint8 *_chatmessage_backup = nullptr; ///< Backup in case text is moved. +/** + * Test if there are any chat messages to display. + * @param show_all Set if all messages should be included, instead of unexpired only. + * @return True iff there are chat messages to display. + */ +static inline bool HaveChatMessages(bool show_all) +{ + if (show_all) return _chatmsg_list.size() != 0; + + auto now = std::chrono::steady_clock::now(); + for (auto &cmsg : _chatmsg_list) { + if (cmsg.remove_time >= now) return true; + } + + return false; +} + /** * Add a text message to the 'chat window' to be shown * @param colour The colour this message is to be shown in @@ -84,6 +107,7 @@ void CDECL NetworkAddChatMessage(TextColour colour, uint duration, const char *m cmsg->colour = (colour & TC_IS_PALETTE_COLOUR) ? colour : TC_WHITE; cmsg->remove_time = std::chrono::steady_clock::now() + std::chrono::seconds(duration); + _chatmessage_dirty_time = std::chrono::steady_clock::now(); _chatmessage_dirty = true; } @@ -149,6 +173,7 @@ void NetworkUndrawChatMessage() /* And make sure it is updated next time */ VideoDriver::GetInstance()->MakeDirty(x, y, width, height); + _chatmessage_dirty_time = std::chrono::steady_clock::now(); _chatmessage_dirty = true; } } @@ -156,13 +181,13 @@ void NetworkUndrawChatMessage() /** Check if a message is expired. */ void NetworkChatMessageLoop() { - for (auto it = _chatmsg_list.begin(); it != _chatmsg_list.end();) { + auto now = std::chrono::steady_clock::now(); + for (auto &cmsg : _chatmsg_list) { /* Message has expired, remove from the list */ - if (std::chrono::steady_clock::now() > it->remove_time) { - it = _chatmsg_list.erase(it); + if (now > cmsg.remove_time && _chatmessage_dirty_time < cmsg.remove_time) { + _chatmessage_dirty_time = now; _chatmessage_dirty = true; - } else { - it++; + break; } } } @@ -173,13 +198,16 @@ void NetworkDrawChatMessage() Blitter *blitter = BlitterFactory::GetCurrentBlitter(); if (!_chatmessage_dirty) return; + const Window *w = FindWindowByClass(WC_SEND_NETWORK_MSG); + bool show_all = (w != nullptr); + /* First undraw if needed */ NetworkUndrawChatMessage(); if (_iconsole_mode == ICONSOLE_FULL) return; /* Check if we have anything to draw at all */ - if (_chatmsg_list.size() == 0) return; + if (!HaveChatMessages(show_all)) return; int x = _chatmsg_box.x; int y = _screen.height - _chatmsg_box.y - _chatmsg_box.height; @@ -201,8 +229,10 @@ void NetworkDrawChatMessage() _cur_dpi = &_screen; // switch to _screen painting + auto now = std::chrono::steady_clock::now(); int string_height = 0; for (auto &cmsg : _chatmsg_list) { + if (!show_all && cmsg.remove_time < now) continue; SetDParamStr(0, cmsg.message); string_height += GetStringLineCount(STR_JUST_RAW_STRING, width - 1) * FONT_HEIGHT_NORMAL + NETWORK_CHAT_LINE_SPACING; } @@ -220,6 +250,7 @@ void NetworkDrawChatMessage() int ypos = bottom - 2; for (auto &cmsg : _chatmsg_list) { + if (!show_all && cmsg.remove_time < now) continue; ypos = DrawStringMultiLine(_chatmsg_box.x + 3, _chatmsg_box.x + _chatmsg_box.width - 1, top, ypos, cmsg.message, cmsg.colour, SA_LEFT | SA_BOTTOM | SA_FORCE) - NETWORK_CHAT_LINE_SPACING; if (ypos < top) break; } From 6c49ae9cd7612a0e7a4542cc49d7361889d0f345 Mon Sep 17 00:00:00 2001 From: translators Date: Sat, 17 Apr 2021 17:50:40 +0000 Subject: [PATCH 079/268] Update: Translations from eints chinese (simplified): 3 changes by clzls spanish: 1 change by MontyMontana polish: 6 changes by Milek7 --- src/lang/polish.txt | 7 ++++++- src/lang/simplified_chinese.txt | 6 +++--- src/lang/spanish.txt | 2 +- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/lang/polish.txt b/src/lang/polish.txt index 38c132732f..09d44f2fc1 100644 --- a/src/lang/polish.txt +++ b/src/lang/polish.txt @@ -1386,6 +1386,8 @@ STR_GAME_OPTIONS_VIDEO_ACCELERATION :{BLACK}Przyspie STR_GAME_OPTIONS_VIDEO_ACCELERATION_TOOLTIP :{BLACK}Zaznacz to pole, aby zezwolić OpenTTD na użycie przyspieszenia sprzętowego. Ustawienia zostaną zastosowane dopiero po ponownym uruchomieniu gry. STR_GAME_OPTIONS_VIDEO_ACCELERATION_RESTART :{WHITE}Ustawienie to zacznie obowiązywać dopiero po ponownym uruchomieniu gry. +STR_GAME_OPTIONS_VIDEO_VSYNC :{BLACK}Synchronizacja pionowa +STR_GAME_OPTIONS_VIDEO_VSYNC_TOOLTIP :{BLACK}Zaznacz to pole aby włączyć synchronizację pionową. Zmiany zostaną zastosowane po restarcie gry. Działa tylko z włączoną akceleracją sprzętową. STR_GAME_OPTIONS_GUI_ZOOM_FRAME :{BLACK}Rozmiar interfejsu STR_GAME_OPTIONS_GUI_ZOOM_DROPDOWN_TOOLTIP :{BLACK}Wybierz rozmiar elementów interfejsu @@ -1519,6 +1521,7 @@ STR_CONFIG_SETTING_TREE_CAPTION :{WHITE}Ustawien STR_CONFIG_SETTING_FILTER_TITLE :{BLACK}Filtrowanie po frazie: STR_CONFIG_SETTING_EXPAND_ALL :{BLACK}Otwórz wszystko STR_CONFIG_SETTING_COLLAPSE_ALL :{BLACK}Zamknij wszystko +STR_CONFIG_SETTING_RESET_ALL :{BLACK}Zresetuj wszystkie ustawienia STR_CONFIG_SETTING_NO_EXPLANATION_AVAILABLE_HELPTEXT :(wyjaśnienie niedostępne) STR_CONFIG_SETTING_DEFAULT_VALUE :{LTBLUE}Domyślna wartość: {ORANGE}{STRING} STR_CONFIG_SETTING_TYPE :{LTBLUE}Typ ustawienia: {ORANGE}{STRING} @@ -1527,6 +1530,8 @@ STR_CONFIG_SETTING_TYPE_GAME_MENU :Ustawienie gry STR_CONFIG_SETTING_TYPE_GAME_INGAME :Ustawienie gry (przechowywane w pliku zapisu; ma wpływ tylko na aktualną grę) STR_CONFIG_SETTING_TYPE_COMPANY_MENU :Ustawienie firmy (przechowywane w plikach zapisu; ma wpływ tylko na nowe gry) STR_CONFIG_SETTING_TYPE_COMPANY_INGAME :Ustawienie firmy (przechowywane w pliku zapisu; ma wpływ tylko na aktualną firmę) +STR_CONFIG_SETTING_RESET_ALL_CONFIRMATION_DIALOG_CAPTION :{WHITE}Uwaga! +STR_CONFIG_SETTING_RESET_ALL_CONFIRMATION_DIALOG_TEXT :{WHITE}Wszystkie ustawienia zostaną przywrócone do wartości domyślnych.{}Jesteś pewien czy chcesz kontynuować? STR_CONFIG_SETTING_RESTRICT_CATEGORY :{BLACK}Kategoria: STR_CONFIG_SETTING_RESTRICT_TYPE :{BLACK}Typ: @@ -2912,7 +2917,7 @@ STR_WATERWAYS_TOOLBAR_BUILD_DOCK_TOOLTIP :{BLACK}Zbuduj p STR_WATERWAYS_TOOLBAR_BUOY_TOOLTIP :{BLACK}Ustaw boję, która może być użyta jako pkt. orientacyjny. Shift przełącza pomiędzy trybem budowania a szacowaniem jego kosztów STR_WATERWAYS_TOOLBAR_BUILD_AQUEDUCT_TOOLTIP :{BLACK}Zbuduj akwedukt. Shift przełącza pomiędzy trybem budowania a szacowaniem jego kosztów STR_WATERWAYS_TOOLBAR_CREATE_LAKE_TOOLTIP :{BLACK}Stwórz akwen wodny.{}Tworzy kanał, chyba że przyrzymany jest CTRL na poziomie morza, wtedy pobliski teren zostanie zatopiony -STR_WATERWAYS_TOOLBAR_CREATE_RIVER_TOOLTIP :{BLACK}Umieszczanie rzek +STR_WATERWAYS_TOOLBAR_CREATE_RIVER_TOOLTIP :{BLACK}Umieszczanie rzek. Ctrl zaznacza obszar po przekątnej. # Ship depot construction window STR_DEPOT_BUILD_SHIP_CAPTION :{WHITE}Ukierunkowanie stoczni diff --git a/src/lang/simplified_chinese.txt b/src/lang/simplified_chinese.txt index 4f7aa144bc..891c597f75 100644 --- a/src/lang/simplified_chinese.txt +++ b/src/lang/simplified_chinese.txt @@ -3965,8 +3965,8 @@ STR_ORDERS_END_OF_SHARED_ORDERS :- - 共享调 STR_ORDER_NON_STOP :{BLACK}不停车 STR_ORDER_GO_TO :前往 STR_ORDER_GO_NON_STOP_TO :不停车前往 -STR_ORDER_GO_VIA :通过 -STR_ORDER_GO_NON_STOP_VIA :前往不停车 +STR_ORDER_GO_VIA :经由 +STR_ORDER_GO_NON_STOP_VIA :经由(不停车) STR_ORDER_TOOLTIP_NON_STOP :{BLACK}改变当前选中车站停车时的执行动作 STR_ORDER_TOGGLE_FULL_LOAD :{BLACK}装满任意货物 @@ -4659,7 +4659,7 @@ STR_ERROR_NO_VEHICLES_AVAILABLE_YET_EXPLANATION :{WHITE}在{DATE # Specific vehicle errors STR_ERROR_CAN_T_MAKE_TRAIN_PASS_SIGNAL :{WHITE}不能让列车冒险通过信号... STR_ERROR_CAN_T_REVERSE_DIRECTION_TRAIN :{WHITE}不能命令列车调头... -STR_ERROR_TRAIN_START_NO_POWER :木有接触网! +STR_ERROR_TRAIN_START_NO_POWER :没有接触网! STR_ERROR_CAN_T_MAKE_ROAD_VEHICLE_TURN :{WHITE}车辆无法调头... diff --git a/src/lang/spanish.txt b/src/lang/spanish.txt index 292d32426f..95630e6d31 100644 --- a/src/lang/spanish.txt +++ b/src/lang/spanish.txt @@ -315,7 +315,7 @@ STR_SORT_BY_RANGE :Alcance STR_SORT_BY_POPULATION :Población STR_SORT_BY_RATING :Calificación STR_SORT_BY_NUM_VEHICLES :Número de vehículos -STR_SORT_BY_TOTAL_PROFIT_LAST_YEAR :Beneficios total del último año +STR_SORT_BY_TOTAL_PROFIT_LAST_YEAR :Beneficio total del último año STR_SORT_BY_TOTAL_PROFIT_THIS_YEAR :Beneficio total este año STR_SORT_BY_AVERAGE_PROFIT_LAST_YEAR :Beneficio medio el año pasado STR_SORT_BY_AVERAGE_PROFIT_THIS_YEAR :Beneficio medio este año From 47a99bb67632b6c05b70024f0639c767d057c6ae Mon Sep 17 00:00:00 2001 From: Rubidium Date: Thu, 15 Apr 2021 18:47:41 +0200 Subject: [PATCH 080/268] Fix #7513: recursive garbage collection caused stack overflow --- src/3rdparty/squirrel/squirrel/sqarray.h | 2 +- src/3rdparty/squirrel/squirrel/sqclass.h | 4 +- src/3rdparty/squirrel/squirrel/sqclosure.h | 6 +- src/3rdparty/squirrel/squirrel/sqobject.cpp | 131 ++++++++------------ src/3rdparty/squirrel/squirrel/sqobject.h | 38 +++++- src/3rdparty/squirrel/squirrel/sqstate.cpp | 65 +++++----- src/3rdparty/squirrel/squirrel/sqstate.h | 4 +- src/3rdparty/squirrel/squirrel/sqtable.h | 2 +- src/3rdparty/squirrel/squirrel/squserdata.h | 2 +- src/3rdparty/squirrel/squirrel/sqvm.h | 2 +- 10 files changed, 139 insertions(+), 117 deletions(-) diff --git a/src/3rdparty/squirrel/squirrel/sqarray.h b/src/3rdparty/squirrel/squirrel/sqarray.h index 5c26352079..37d91bc4e1 100644 --- a/src/3rdparty/squirrel/squirrel/sqarray.h +++ b/src/3rdparty/squirrel/squirrel/sqarray.h @@ -17,7 +17,7 @@ public: return newarray; } #ifndef NO_GARBAGE_COLLECTOR - void Mark(SQCollectable **chain); + void EnqueueMarkObjectForChildren(SQGCMarkerQueue &queue); #endif void Finalize(){ _values.resize(0); diff --git a/src/3rdparty/squirrel/squirrel/sqclass.h b/src/3rdparty/squirrel/squirrel/sqclass.h index 895c053c24..cb0973bbee 100644 --- a/src/3rdparty/squirrel/squirrel/sqclass.h +++ b/src/3rdparty/squirrel/squirrel/sqclass.h @@ -59,7 +59,7 @@ public: } void Finalize(); #ifndef NO_GARBAGE_COLLECTOR - void Mark(SQCollectable ** ); + void EnqueueMarkObjectForChildren(SQGCMarkerQueue &queue); #endif SQInteger Next(const SQObjectPtr &refpos, SQObjectPtr &outkey, SQObjectPtr &outval); SQInstance *CreateInstance(); @@ -147,7 +147,7 @@ public: } void Finalize(); #ifndef NO_GARBAGE_COLLECTOR - void Mark(SQCollectable ** ); + void EnqueueMarkObjectForChildren(SQGCMarkerQueue &queue); #endif bool InstanceOf(SQClass *trg); bool GetMetaMethod(SQVM *v,SQMetaMethod mm,SQObjectPtr &res); diff --git a/src/3rdparty/squirrel/squirrel/sqclosure.h b/src/3rdparty/squirrel/squirrel/sqclosure.h index a42dcd575a..8480fb8af2 100644 --- a/src/3rdparty/squirrel/squirrel/sqclosure.h +++ b/src/3rdparty/squirrel/squirrel/sqclosure.h @@ -32,7 +32,7 @@ public: bool Save(SQVM *v,SQUserPointer up,SQWRITEFUNC write); static bool Load(SQVM *v,SQUserPointer up,SQREADFUNC read,SQObjectPtr &ret); #ifndef NO_GARBAGE_COLLECTOR - void Mark(SQCollectable **chain); + void EnqueueMarkObjectForChildren(SQGCMarkerQueue &queue); void Finalize(){_outervalues.resize(0); } #endif SQObjectPtr _env; @@ -66,7 +66,7 @@ public: bool Yield(SQVM *v); bool Resume(SQVM *v,SQInteger target); #ifndef NO_GARBAGE_COLLECTOR - void Mark(SQCollectable **chain); + void EnqueueMarkObjectForChildren(SQGCMarkerQueue &queue); void Finalize(){_stack.resize(0);_closure=_null_;} #endif SQObjectPtr _closure; @@ -106,7 +106,7 @@ public: sq_delete(this,SQNativeClosure); } #ifndef NO_GARBAGE_COLLECTOR - void Mark(SQCollectable **chain); + void EnqueueMarkObjectForChildren(SQGCMarkerQueue &queue); void Finalize(){_outervalues.resize(0);} #endif SQInteger _nparamscheck; diff --git a/src/3rdparty/squirrel/squirrel/sqobject.cpp b/src/3rdparty/squirrel/squirrel/sqobject.cpp index d48baca1e9..a113f316de 100644 --- a/src/3rdparty/squirrel/squirrel/sqobject.cpp +++ b/src/3rdparty/squirrel/squirrel/sqobject.cpp @@ -486,104 +486,81 @@ bool SQFunctionProto::Load(SQVM *v,SQUserPointer up,SQREADFUNC read,SQObjectPtr #ifndef NO_GARBAGE_COLLECTOR -#define START_MARK() if(!(_uiRef&MARK_FLAG)){ \ - _uiRef|=MARK_FLAG; - -#define END_MARK() RemoveFromChain(&_sharedstate->_gc_chain, this); \ - AddToChain(chain, this); } - -void SQVM::Mark(SQCollectable **chain) +void SQVM::EnqueueMarkObjectForChildren(SQGCMarkerQueue &queue) { - START_MARK() - SQSharedState::MarkObject(_lasterror,chain); - SQSharedState::MarkObject(_errorhandler,chain); - SQSharedState::MarkObject(_debughook,chain); - SQSharedState::MarkObject(_roottable, chain); - SQSharedState::MarkObject(temp_reg, chain); - for(SQUnsignedInteger i = 0; i < _stack.size(); i++) SQSharedState::MarkObject(_stack[i], chain); - for(SQUnsignedInteger j = 0; j < _vargsstack.size(); j++) SQSharedState::MarkObject(_vargsstack[j], chain); - for(SQInteger k = 0; k < _callsstacksize; k++) SQSharedState::MarkObject(_callsstack[k]._closure, chain); - END_MARK() + SQSharedState::EnqueueMarkObject(_lasterror,queue); + SQSharedState::EnqueueMarkObject(_errorhandler,queue); + SQSharedState::EnqueueMarkObject(_debughook,queue); + SQSharedState::EnqueueMarkObject(_roottable, queue); + SQSharedState::EnqueueMarkObject(temp_reg, queue); + for(SQUnsignedInteger i = 0; i < _stack.size(); i++) SQSharedState::EnqueueMarkObject(_stack[i], queue); + for(SQUnsignedInteger j = 0; j < _vargsstack.size(); j++) SQSharedState::EnqueueMarkObject(_vargsstack[j], queue); + for(SQInteger k = 0; k < _callsstacksize; k++) SQSharedState::EnqueueMarkObject(_callsstack[k]._closure, queue); } -void SQArray::Mark(SQCollectable **chain) +void SQArray::EnqueueMarkObjectForChildren(SQGCMarkerQueue &queue) { - START_MARK() - SQInteger len = _values.size(); - for(SQInteger i = 0;i < len; i++) SQSharedState::MarkObject(_values[i], chain); - END_MARK() -} -void SQTable::Mark(SQCollectable **chain) -{ - START_MARK() - if(_delegate) _delegate->Mark(chain); - SQInteger len = _numofnodes; - for(SQInteger i = 0; i < len; i++){ - SQSharedState::MarkObject(_nodes[i].key, chain); - SQSharedState::MarkObject(_nodes[i].val, chain); - } - END_MARK() + SQInteger len = _values.size(); + for(SQInteger i = 0;i < len; i++) SQSharedState::EnqueueMarkObject(_values[i], queue); } -void SQClass::Mark(SQCollectable **chain) +void SQTable::EnqueueMarkObjectForChildren(SQGCMarkerQueue &queue) { - START_MARK() - _members->Mark(chain); - if(_base) _base->Mark(chain); - SQSharedState::MarkObject(_attributes, chain); - for(SQUnsignedInteger i =0; i< _defaultvalues.size(); i++) { - SQSharedState::MarkObject(_defaultvalues[i].val, chain); - SQSharedState::MarkObject(_defaultvalues[i].attrs, chain); - } - for(SQUnsignedInteger j =0; j< _methods.size(); j++) { - SQSharedState::MarkObject(_methods[j].val, chain); - SQSharedState::MarkObject(_methods[j].attrs, chain); - } - for(SQUnsignedInteger k =0; k< _metamethods.size(); k++) { - SQSharedState::MarkObject(_metamethods[k], chain); - } - END_MARK() + if(_delegate) queue.Enqueue(_delegate); + SQInteger len = _numofnodes; + for(SQInteger i = 0; i < len; i++){ + SQSharedState::EnqueueMarkObject(_nodes[i].key, queue); + SQSharedState::EnqueueMarkObject(_nodes[i].val, queue); + } } -void SQInstance::Mark(SQCollectable **chain) +void SQClass::EnqueueMarkObjectForChildren(SQGCMarkerQueue &queue) { - START_MARK() - _class->Mark(chain); - SQUnsignedInteger nvalues = _class->_defaultvalues.size(); - for(SQUnsignedInteger i =0; i< nvalues; i++) { - SQSharedState::MarkObject(_values[i], chain); - } - END_MARK() + queue.Enqueue(_members); + if(_base) queue.Enqueue(_base); + SQSharedState::EnqueueMarkObject(_attributes, queue); + for(SQUnsignedInteger i =0; i< _defaultvalues.size(); i++) { + SQSharedState::EnqueueMarkObject(_defaultvalues[i].val, queue); + SQSharedState::EnqueueMarkObject(_defaultvalues[i].attrs, queue); + } + for(SQUnsignedInteger j =0; j< _methods.size(); j++) { + SQSharedState::EnqueueMarkObject(_methods[j].val, queue); + SQSharedState::EnqueueMarkObject(_methods[j].attrs, queue); + } + for(SQUnsignedInteger k =0; k< _metamethods.size(); k++) { + SQSharedState::EnqueueMarkObject(_metamethods[k], queue); + } } -void SQGenerator::Mark(SQCollectable **chain) +void SQInstance::EnqueueMarkObjectForChildren(SQGCMarkerQueue &queue) { - START_MARK() - for(SQUnsignedInteger i = 0; i < _stack.size(); i++) SQSharedState::MarkObject(_stack[i], chain); - for(SQUnsignedInteger j = 0; j < _vargsstack.size(); j++) SQSharedState::MarkObject(_vargsstack[j], chain); - SQSharedState::MarkObject(_closure, chain); - END_MARK() + queue.Enqueue(_class); + SQUnsignedInteger nvalues = _class->_defaultvalues.size(); + for(SQUnsignedInteger i =0; i< nvalues; i++) { + SQSharedState::EnqueueMarkObject(_values[i], queue); + } } -void SQClosure::Mark(SQCollectable **chain) +void SQGenerator::EnqueueMarkObjectForChildren(SQGCMarkerQueue &queue) { - START_MARK() - for(SQUnsignedInteger i = 0; i < _outervalues.size(); i++) SQSharedState::MarkObject(_outervalues[i], chain); - for(SQUnsignedInteger i = 0; i < _defaultparams.size(); i++) SQSharedState::MarkObject(_defaultparams[i], chain); - END_MARK() + for(SQUnsignedInteger i = 0; i < _stack.size(); i++) SQSharedState::EnqueueMarkObject(_stack[i], queue); + for(SQUnsignedInteger j = 0; j < _vargsstack.size(); j++) SQSharedState::EnqueueMarkObject(_vargsstack[j], queue); + SQSharedState::EnqueueMarkObject(_closure, queue); } -void SQNativeClosure::Mark(SQCollectable **chain) +void SQClosure::EnqueueMarkObjectForChildren(SQGCMarkerQueue &queue) { - START_MARK() - for(SQUnsignedInteger i = 0; i < _outervalues.size(); i++) SQSharedState::MarkObject(_outervalues[i], chain); - END_MARK() + for(SQUnsignedInteger i = 0; i < _outervalues.size(); i++) SQSharedState::EnqueueMarkObject(_outervalues[i], queue); + for(SQUnsignedInteger i = 0; i < _defaultparams.size(); i++) SQSharedState::EnqueueMarkObject(_defaultparams[i], queue); } -void SQUserData::Mark(SQCollectable **chain){ - START_MARK() - if(_delegate) _delegate->Mark(chain); - END_MARK() +void SQNativeClosure::EnqueueMarkObjectForChildren(SQGCMarkerQueue &queue) +{ + for(SQUnsignedInteger i = 0; i < _outervalues.size(); i++) SQSharedState::EnqueueMarkObject(_outervalues[i], queue); +} + +void SQUserData::EnqueueMarkObjectForChildren(SQGCMarkerQueue &queue){ + if(_delegate) queue.Enqueue(_delegate); } void SQCollectable::UnMark() { _uiRef&=~MARK_FLAG; } diff --git a/src/3rdparty/squirrel/squirrel/sqobject.h b/src/3rdparty/squirrel/squirrel/sqobject.h index d71e515a8d..ed4f31fcbb 100644 --- a/src/3rdparty/squirrel/squirrel/sqobject.h +++ b/src/3rdparty/squirrel/squirrel/sqobject.h @@ -2,6 +2,7 @@ #ifndef _SQOBJECT_H_ #define _SQOBJECT_H_ +#include #include "squtils.h" #define SQ_CLOSURESTREAM_HEAD (('S'<<24)|('Q'<<16)|('I'<<8)|('R')) @@ -344,13 +345,48 @@ struct SQCollectable : public SQRefCounted { SQCollectable *_prev; SQSharedState *_sharedstate; virtual void Release()=0; - virtual void Mark(SQCollectable **chain)=0; + virtual void EnqueueMarkObjectForChildren(class SQGCMarkerQueue &queue)=0; void UnMark(); virtual void Finalize()=0; static void AddToChain(SQCollectable **chain,SQCollectable *c); static void RemoveFromChain(SQCollectable **chain,SQCollectable *c); }; +/** + * Helper container for state to change the garbage collection from a recursive to an iterative approach. + * The iterative approach provides effectively a depth first search approach. + */ +class SQGCMarkerQueue { + std::forward_list queue; ///< The queue of elements to still process. +public: + /** Whether there are any elements left to process. */ + bool IsEmpty() { return this->queue.empty(); } + + /** + * Remove the first element from the queue. + * Removal when the queue is empty results in undefined behaviour. + */ + SQCollectable *Pop() + { + SQCollectable *collectable = this->queue.front(); + this->queue.pop_front(); + return collectable; + } + + /** + * Add a collectable to the queue, but only when it has not been marked yet. + * When adding it to the queue, the collectable will be marked, so subsequent calls + * will not add it again. + */ + void Enqueue(SQCollectable *collectable) + { + if ((collectable->_uiRef & MARK_FLAG) == 0) { + collectable->_uiRef |= MARK_FLAG; + this->queue.push_front(collectable); + } + } +}; + #define ADD_TO_CHAIN(chain,obj) AddToChain(chain,obj) #define REMOVE_FROM_CHAIN(chain,obj) {if(!(_uiRef&MARK_FLAG))RemoveFromChain(chain,obj);} diff --git a/src/3rdparty/squirrel/squirrel/sqstate.cpp b/src/3rdparty/squirrel/squirrel/sqstate.cpp index 8233ad1789..9d91c78996 100644 --- a/src/3rdparty/squirrel/squirrel/sqstate.cpp +++ b/src/3rdparty/squirrel/squirrel/sqstate.cpp @@ -228,18 +228,18 @@ SQInteger SQSharedState::GetMetaMethodIdxByName(const SQObjectPtr &name) #ifndef NO_GARBAGE_COLLECTOR -void SQSharedState::MarkObject(SQObjectPtr &o,SQCollectable **chain) +void SQSharedState::EnqueueMarkObject(SQObjectPtr &o,SQGCMarkerQueue &queue) { switch(type(o)){ - case OT_TABLE:_table(o)->Mark(chain);break; - case OT_ARRAY:_array(o)->Mark(chain);break; - case OT_USERDATA:_userdata(o)->Mark(chain);break; - case OT_CLOSURE:_closure(o)->Mark(chain);break; - case OT_NATIVECLOSURE:_nativeclosure(o)->Mark(chain);break; - case OT_GENERATOR:_generator(o)->Mark(chain);break; - case OT_THREAD:_thread(o)->Mark(chain);break; - case OT_CLASS:_class(o)->Mark(chain);break; - case OT_INSTANCE:_instance(o)->Mark(chain);break; + case OT_TABLE:queue.Enqueue(_table(o));break; + case OT_ARRAY:queue.Enqueue(_array(o));break; + case OT_USERDATA:queue.Enqueue(_userdata(o));break; + case OT_CLOSURE:queue.Enqueue(_closure(o));break; + case OT_NATIVECLOSURE:queue.Enqueue(_nativeclosure(o));break; + case OT_GENERATOR:queue.Enqueue(_generator(o));break; + case OT_THREAD:queue.Enqueue(_thread(o));break; + case OT_CLASS:queue.Enqueue(_class(o));break; + case OT_INSTANCE:queue.Enqueue(_instance(o));break; default: break; //shutup compiler } } @@ -248,27 +248,36 @@ void SQSharedState::MarkObject(SQObjectPtr &o,SQCollectable **chain) SQInteger SQSharedState::CollectGarbage(SQVM *vm) { SQInteger n=0; - SQCollectable *tchain=NULL; SQVM *vms = _thread(_root_vm); - vms->Mark(&tchain); + SQGCMarkerQueue queue; + queue.Enqueue(vms); #ifdef WITH_ASSERT SQInteger x = _table(_thread(_root_vm)->_roottable)->CountUsed(); #endif - _refs_table.Mark(&tchain); - MarkObject(_registry,&tchain); - MarkObject(_consts,&tchain); - MarkObject(_metamethodsmap,&tchain); - MarkObject(_table_default_delegate,&tchain); - MarkObject(_array_default_delegate,&tchain); - MarkObject(_string_default_delegate,&tchain); - MarkObject(_number_default_delegate,&tchain); - MarkObject(_generator_default_delegate,&tchain); - MarkObject(_thread_default_delegate,&tchain); - MarkObject(_closure_default_delegate,&tchain); - MarkObject(_class_default_delegate,&tchain); - MarkObject(_instance_default_delegate,&tchain); - MarkObject(_weakref_default_delegate,&tchain); + _refs_table.EnqueueMarkObject(queue); + EnqueueMarkObject(_registry,queue); + EnqueueMarkObject(_consts,queue); + EnqueueMarkObject(_metamethodsmap,queue); + EnqueueMarkObject(_table_default_delegate,queue); + EnqueueMarkObject(_array_default_delegate,queue); + EnqueueMarkObject(_string_default_delegate,queue); + EnqueueMarkObject(_number_default_delegate,queue); + EnqueueMarkObject(_generator_default_delegate,queue); + EnqueueMarkObject(_thread_default_delegate,queue); + EnqueueMarkObject(_closure_default_delegate,queue); + EnqueueMarkObject(_class_default_delegate,queue); + EnqueueMarkObject(_instance_default_delegate,queue); + EnqueueMarkObject(_weakref_default_delegate,queue); + + SQCollectable *tchain=NULL; + + while (!queue.IsEmpty()) { + SQCollectable *q = queue.Pop(); + q->EnqueueMarkObjectForChildren(queue); + SQCollectable::RemoveFromChain(&_gc_chain, q); + SQCollectable::AddToChain(&tchain, q); + } SQCollectable *t = _gc_chain; SQCollectable *nx = NULL; @@ -357,12 +366,12 @@ RefTable::~RefTable() } #ifndef NO_GARBAGE_COLLECTOR -void RefTable::Mark(SQCollectable **chain) +void RefTable::EnqueueMarkObject(SQGCMarkerQueue &queue) { RefNode *nodes = (RefNode *)_nodes; for(SQUnsignedInteger n = 0; n < _numofslots; n++) { if(type(nodes->obj) != OT_NULL) { - SQSharedState::MarkObject(nodes->obj,chain); + SQSharedState::EnqueueMarkObject(nodes->obj,queue); } nodes++; } diff --git a/src/3rdparty/squirrel/squirrel/sqstate.h b/src/3rdparty/squirrel/squirrel/sqstate.h index da6bf9ae64..d1a1f8dd63 100644 --- a/src/3rdparty/squirrel/squirrel/sqstate.h +++ b/src/3rdparty/squirrel/squirrel/sqstate.h @@ -34,7 +34,7 @@ struct RefTable { void AddRef(SQObject &obj); SQBool Release(SQObject &obj); #ifndef NO_GARBAGE_COLLECTOR - void Mark(SQCollectable **chain); + void EnqueueMarkObject(SQGCMarkerQueue &queue); #endif void Finalize(); private: @@ -63,7 +63,7 @@ public: SQInteger GetMetaMethodIdxByName(const SQObjectPtr &name); #ifndef NO_GARBAGE_COLLECTOR SQInteger CollectGarbage(SQVM *vm); - static void MarkObject(SQObjectPtr &o,SQCollectable **chain); + static void EnqueueMarkObject(SQObjectPtr &o,SQGCMarkerQueue &queue); #endif SQObjectPtrVec *_metamethods; SQObjectPtr _metamethodsmap; diff --git a/src/3rdparty/squirrel/squirrel/sqtable.h b/src/3rdparty/squirrel/squirrel/sqtable.h index 52d9ba41ab..0083a90a9d 100644 --- a/src/3rdparty/squirrel/squirrel/sqtable.h +++ b/src/3rdparty/squirrel/squirrel/sqtable.h @@ -60,7 +60,7 @@ public: SQ_FREE(_nodes, _numofnodes * sizeof(_HashNode)); } #ifndef NO_GARBAGE_COLLECTOR - void Mark(SQCollectable **chain); + void EnqueueMarkObjectForChildren(SQGCMarkerQueue &queue); #endif inline _HashNode *_Get(const SQObjectPtr &key,SQHash hash) { diff --git a/src/3rdparty/squirrel/squirrel/squserdata.h b/src/3rdparty/squirrel/squirrel/squserdata.h index 3bf1a9dbad..ca4933de2d 100644 --- a/src/3rdparty/squirrel/squirrel/squserdata.h +++ b/src/3rdparty/squirrel/squirrel/squserdata.h @@ -18,7 +18,7 @@ struct SQUserData : SQDelegable return ud; } #ifndef NO_GARBAGE_COLLECTOR - void Mark(SQCollectable **chain); + void EnqueueMarkObjectForChildren(SQGCMarkerQueue &queue); void Finalize(){SetDelegate(NULL);} #endif void Release() { diff --git a/src/3rdparty/squirrel/squirrel/sqvm.h b/src/3rdparty/squirrel/squirrel/sqvm.h index 9c8e986fbc..dbfe2309c7 100644 --- a/src/3rdparty/squirrel/squirrel/sqvm.h +++ b/src/3rdparty/squirrel/squirrel/sqvm.h @@ -113,7 +113,7 @@ public: #endif #ifndef NO_GARBAGE_COLLECTOR - void Mark(SQCollectable **chain); + void EnqueueMarkObjectForChildren(SQGCMarkerQueue &queue); #endif void Finalize(); void GrowCallStack() { From 44d1b964bf1c87324b82b1793218b5f79a9be9eb Mon Sep 17 00:00:00 2001 From: Rubidium Date: Thu, 15 Apr 2021 19:44:43 +0200 Subject: [PATCH 081/268] Fix #7513: recursive array/class/table release caused stack overflow --- src/3rdparty/squirrel/squirrel/sqarray.h | 12 ++++++--- src/3rdparty/squirrel/squirrel/sqclass.h | 16 ++++++------ src/3rdparty/squirrel/squirrel/sqobject.h | 22 ++++++++++------ src/3rdparty/squirrel/squirrel/sqstate.cpp | 29 ++++++++++++++++++++++ src/3rdparty/squirrel/squirrel/sqstate.h | 5 ++++ src/3rdparty/squirrel/squirrel/sqtable.h | 10 +++++--- 6 files changed, 73 insertions(+), 21 deletions(-) diff --git a/src/3rdparty/squirrel/squirrel/sqarray.h b/src/3rdparty/squirrel/squirrel/sqarray.h index 37d91bc4e1..13ae11619c 100644 --- a/src/3rdparty/squirrel/squirrel/sqarray.h +++ b/src/3rdparty/squirrel/squirrel/sqarray.h @@ -17,9 +17,9 @@ public: return newarray; } #ifndef NO_GARBAGE_COLLECTOR - void EnqueueMarkObjectForChildren(SQGCMarkerQueue &queue); + void EnqueueMarkObjectForChildren(SQGCMarkerQueue &queue) override; #endif - void Finalize(){ + void Finalize() override { _values.resize(0); } bool Get(const SQInteger nidx,SQObjectPtr &val) @@ -78,9 +78,13 @@ public: ShrinkIfNeeded(); return true; } - void Release() + void Release() override { - sq_delete(this,SQArray); + this->_sharedstate->DelayFinalFree(this); + } + void FinalFree() override + { + sq_delete(this, SQArray); } SQObjectPtrVec _values; }; diff --git a/src/3rdparty/squirrel/squirrel/sqclass.h b/src/3rdparty/squirrel/squirrel/sqclass.h index cb0973bbee..4fb6ecbd97 100644 --- a/src/3rdparty/squirrel/squirrel/sqclass.h +++ b/src/3rdparty/squirrel/squirrel/sqclass.h @@ -126,31 +126,33 @@ public: } return false; } - void Release() { + void Release() override { _uiRef++; try { if (_hook) { _hook(_userpointer,0);} } catch (...) { _uiRef--; if (_uiRef == 0) { - SQInteger size = _memsize; - this->~SQInstance(); - SQ_FREE(this, size); + this->_sharedstate->DelayFinalFree(this); } throw; } _uiRef--; if(_uiRef > 0) return; + this->_sharedstate->DelayFinalFree(this); + } + void FinalFree() override + { SQInteger size = _memsize; this->~SQInstance(); SQ_FREE(this, size); } - void Finalize(); + void Finalize() override; #ifndef NO_GARBAGE_COLLECTOR - void EnqueueMarkObjectForChildren(SQGCMarkerQueue &queue); + void EnqueueMarkObjectForChildren(SQGCMarkerQueue &queue) override; #endif bool InstanceOf(SQClass *trg); - bool GetMetaMethod(SQVM *v,SQMetaMethod mm,SQObjectPtr &res); + bool GetMetaMethod(SQVM *v,SQMetaMethod mm,SQObjectPtr &res) override; SQClass *_class; SQUserPointer _userpointer; diff --git a/src/3rdparty/squirrel/squirrel/sqobject.h b/src/3rdparty/squirrel/squirrel/sqobject.h index ed4f31fcbb..9212766eef 100644 --- a/src/3rdparty/squirrel/squirrel/sqobject.h +++ b/src/3rdparty/squirrel/squirrel/sqobject.h @@ -2,7 +2,7 @@ #ifndef _SQOBJECT_H_ #define _SQOBJECT_H_ -#include +#include #include "squtils.h" #define SQ_CLOSURESTREAM_HEAD (('S'<<24)|('Q'<<16)|('I'<<8)|('R')) @@ -350,6 +350,14 @@ struct SQCollectable : public SQRefCounted { virtual void Finalize()=0; static void AddToChain(SQCollectable **chain,SQCollectable *c); static void RemoveFromChain(SQCollectable **chain,SQCollectable *c); + + /** + * Helper to perform the final memory freeing of this instance. Since the destructor might + * release more objects, this can cause a very deep recursion. As such, the calls to this + * are to be done via _sharedstate->DelayFinalFree which ensures the calls to this method + * are done in an iterative instead of recursive approach. + */ + virtual void FinalFree() {} }; /** @@ -357,19 +365,19 @@ struct SQCollectable : public SQRefCounted { * The iterative approach provides effectively a depth first search approach. */ class SQGCMarkerQueue { - std::forward_list queue; ///< The queue of elements to still process. + std::vector stack; ///< The elements to still process, with the most recent elements at the back. public: /** Whether there are any elements left to process. */ - bool IsEmpty() { return this->queue.empty(); } + bool IsEmpty() { return this->stack.empty(); } /** - * Remove the first element from the queue. + * Remove the most recently added element from the queue. * Removal when the queue is empty results in undefined behaviour. */ SQCollectable *Pop() { - SQCollectable *collectable = this->queue.front(); - this->queue.pop_front(); + SQCollectable *collectable = this->stack.back(); + this->stack.pop_back(); return collectable; } @@ -382,7 +390,7 @@ public: { if ((collectable->_uiRef & MARK_FLAG) == 0) { collectable->_uiRef |= MARK_FLAG; - this->queue.push_front(collectable); + this->stack.push_back(collectable); } } }; diff --git a/src/3rdparty/squirrel/squirrel/sqstate.cpp b/src/3rdparty/squirrel/squirrel/sqstate.cpp index 9d91c78996..31345d6640 100644 --- a/src/3rdparty/squirrel/squirrel/sqstate.cpp +++ b/src/3rdparty/squirrel/squirrel/sqstate.cpp @@ -99,6 +99,7 @@ SQSharedState::SQSharedState() _notifyallexceptions = false; _scratchpad=NULL; _scratchpadsize=0; + _collectable_free_processing = false; #ifndef NO_GARBAGE_COLLECTOR _gc_chain=NULL; #endif @@ -226,6 +227,34 @@ SQInteger SQSharedState::GetMetaMethodIdxByName(const SQObjectPtr &name) return -1; } +/** + * Helper function that is to be used instead of calling FinalFree directly on the instance, + * so the frees can happen iteratively. This as in the FinalFree the references to any other + * objects are released, which can cause those object to be freed yielding a potentially + * very deep stack in case of for example a link list. + * + * This is done internally by a vector onto which the to be freed instances are pushed. When + * this is called when not already processing, this method will actually call the FinalFree + * function which might cause more elements to end up in the queue which this method then + * picks up continueing until it has processed all instances in that queue. + * @param collectable The collectable to (eventually) free. + */ +void SQSharedState::DelayFinalFree(SQCollectable *collectable) +{ + this->_collectable_free_queue.push_back(collectable); + + if (!this->_collectable_free_processing) { + this->_collectable_free_processing = true; + while (!this->_collectable_free_queue.empty()) { + SQCollectable *collectable = this->_collectable_free_queue.back(); + this->_collectable_free_queue.pop_back(); + collectable->FinalFree(); + } + this->_collectable_free_processing = false; + } +} + + #ifndef NO_GARBAGE_COLLECTOR void SQSharedState::EnqueueMarkObject(SQObjectPtr &o,SQGCMarkerQueue &queue) diff --git a/src/3rdparty/squirrel/squirrel/sqstate.h b/src/3rdparty/squirrel/squirrel/sqstate.h index d1a1f8dd63..547e6de47d 100644 --- a/src/3rdparty/squirrel/squirrel/sqstate.h +++ b/src/3rdparty/squirrel/squirrel/sqstate.h @@ -61,6 +61,7 @@ struct SQSharedState public: SQChar* GetScratchPad(SQInteger size); SQInteger GetMetaMethodIdxByName(const SQObjectPtr &name); + void DelayFinalFree(SQCollectable *collectable); #ifndef NO_GARBAGE_COLLECTOR SQInteger CollectGarbage(SQVM *vm); static void EnqueueMarkObject(SQObjectPtr &o,SQGCMarkerQueue &queue); @@ -74,6 +75,10 @@ public: SQObjectPtr _registry; SQObjectPtr _consts; SQObjectPtr _constructoridx; + /** Queue to make freeing of collectables iterative. */ + std::vector _collectable_free_queue; + /** Whether someone is already processing the _collectable_free_queue. */ + bool _collectable_free_processing; #ifndef NO_GARBAGE_COLLECTOR SQCollectable *_gc_chain; #endif diff --git a/src/3rdparty/squirrel/squirrel/sqtable.h b/src/3rdparty/squirrel/squirrel/sqtable.h index 0083a90a9d..fad2fdc605 100644 --- a/src/3rdparty/squirrel/squirrel/sqtable.h +++ b/src/3rdparty/squirrel/squirrel/sqtable.h @@ -50,7 +50,7 @@ public: newtable->_delegate = NULL; return newtable; } - void Finalize(); + void Finalize() override; SQTable *Clone(); ~SQTable() { @@ -60,7 +60,7 @@ public: SQ_FREE(_nodes, _numofnodes * sizeof(_HashNode)); } #ifndef NO_GARBAGE_COLLECTOR - void EnqueueMarkObjectForChildren(SQGCMarkerQueue &queue); + void EnqueueMarkObjectForChildren(SQGCMarkerQueue &queue) override; #endif inline _HashNode *_Get(const SQObjectPtr &key,SQHash hash) { @@ -81,7 +81,11 @@ public: SQInteger CountUsed(){ return _usednodes;} void Clear(); - void Release() + void Release() override + { + this->_sharedstate->DelayFinalFree(this); + } + void FinalFree() override { sq_delete(this, SQTable); } From 8e539ce293def7d307743282721a6e7174bf0350 Mon Sep 17 00:00:00 2001 From: PeterN Date: Sat, 17 Apr 2021 19:19:06 +0100 Subject: [PATCH 082/268] Change: Improve layout and spacing of vehicle group widgets. (#9041) Existing layout included a blank widget above the group list to align with the vehicle list, however since then an additional sort-by row was added. Group list size tweaks to match normal row size (at least with normal gui and text size.) Removed reduction of 2 rows in the group list <- main culprit of odd sizing. Removed fill attribute on buttons which gave strange sizes, and put it on the group info widget instead. Tweaked various soft-padding values to line up (centreing text with a 1px offset does not make centred text.) --- src/group_gui.cpp | 42 ++++++++++++++++++------------------------ 1 file changed, 18 insertions(+), 24 deletions(-) diff --git a/src/group_gui.cpp b/src/group_gui.cpp index 0573837688..a8b4c5f17e 100644 --- a/src/group_gui.cpp +++ b/src/group_gui.cpp @@ -46,7 +46,6 @@ static const NWidgetPart _nested_group_widgets[] = { NWidget(NWID_HORIZONTAL), /* left part */ NWidget(NWID_VERTICAL), - NWidget(WWT_PANEL, COLOUR_GREY), SetMinimalTextLines(1, WD_DROPDOWNTEXT_TOP + WD_DROPDOWNTEXT_BOTTOM), SetFill(1, 0), EndContainer(), NWidget(WWT_PANEL, COLOUR_GREY, WID_GL_ALL_VEHICLES), SetFill(1, 0), EndContainer(), NWidget(WWT_PANEL, COLOUR_GREY, WID_GL_DEFAULT_VEHICLES), SetFill(1, 0), EndContainer(), NWidget(NWID_HORIZONTAL), @@ -54,18 +53,18 @@ static const NWidgetPart _nested_group_widgets[] = { SetFill(1, 0), SetResize(0, 1), SetScrollbar(WID_GL_LIST_GROUP_SCROLLBAR), NWidget(NWID_VSCROLLBAR, COLOUR_GREY, WID_GL_LIST_GROUP_SCROLLBAR), EndContainer(), - NWidget(WWT_PANEL, COLOUR_GREY, WID_GL_INFO), SetFill(1, 0), EndContainer(), + NWidget(WWT_PANEL, COLOUR_GREY, WID_GL_INFO), SetFill(1, 1), SetMinimalTextLines(3, WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM), EndContainer(), NWidget(NWID_HORIZONTAL), - NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_GL_CREATE_GROUP), SetFill(0, 1), + NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_GL_CREATE_GROUP), SetDataTip(SPR_GROUP_CREATE_TRAIN, STR_GROUP_CREATE_TOOLTIP), - NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_GL_DELETE_GROUP), SetFill(0, 1), + NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_GL_DELETE_GROUP), SetDataTip(SPR_GROUP_DELETE_TRAIN, STR_GROUP_DELETE_TOOLTIP), - NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_GL_RENAME_GROUP), SetFill(0, 1), + NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_GL_RENAME_GROUP), SetDataTip(SPR_GROUP_RENAME_TRAIN, STR_GROUP_RENAME_TOOLTIP), - NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_GL_LIVERY_GROUP), SetFill(0, 1), + NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_GL_LIVERY_GROUP), SetDataTip(SPR_GROUP_LIVERY_TRAIN, STR_GROUP_LIVERY_TOOLTIP), - NWidget(WWT_PANEL, COLOUR_GREY), SetFill(1, 1), EndContainer(), - NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_GL_REPLACE_PROTECTION), SetFill(0, 1), + NWidget(WWT_PANEL, COLOUR_GREY), SetFill(1, 0), EndContainer(), + NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_GL_REPLACE_PROTECTION), SetDataTip(SPR_GROUP_REPLACE_OFF_TRAIN, STR_GROUP_REPLACE_PROTECTION_TOOLTIP), EndContainer(), EndContainer(), @@ -87,14 +86,14 @@ static const NWidgetPart _nested_group_widgets[] = { EndContainer(), NWidget(WWT_PANEL, COLOUR_GREY), SetMinimalSize(1, 0), SetFill(1, 1), SetResize(1, 0), EndContainer(), NWidget(NWID_HORIZONTAL), - NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_GL_AVAILABLE_VEHICLES), SetMinimalSize(106, 12), SetFill(0, 1), + NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_GL_AVAILABLE_VEHICLES), SetMinimalSize(106, 12), SetDataTip(STR_BLACK_STRING, STR_VEHICLE_LIST_AVAILABLE_ENGINES_TOOLTIP), - NWidget(WWT_PANEL, COLOUR_GREY), SetMinimalSize(0, 12), SetFill(1, 1), SetResize(1, 0), EndContainer(), - NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_GL_MANAGE_VEHICLES_DROPDOWN), SetMinimalSize(118, 12), SetFill(0, 1), + NWidget(WWT_PANEL, COLOUR_GREY), SetMinimalSize(0, 12), SetFill(1, 0), SetResize(1, 0), EndContainer(), + NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_GL_MANAGE_VEHICLES_DROPDOWN), SetMinimalSize(118, 12), SetDataTip(STR_VEHICLE_LIST_MANAGE_LIST, STR_VEHICLE_LIST_MANAGE_LIST_TOOLTIP), - NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_GL_STOP_ALL), SetMinimalSize(12, 12), SetFill(0, 1), + NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_GL_STOP_ALL), SetMinimalSize(12, 12), SetDataTip(SPR_FLAG_VEH_STOPPED, STR_VEHICLE_LIST_MASS_STOP_LIST_TOOLTIP), - NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_GL_START_ALL), SetMinimalSize(12, 12), SetFill(0, 1), + NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_GL_START_ALL), SetMinimalSize(12, 12), SetDataTip(SPR_FLAG_VEH_RUNNING, STR_VEHICLE_LIST_MASS_START_LIST_TOOLTIP), NWidget(WWT_RESIZEBOX, COLOUR_GREY), EndContainer(), @@ -229,7 +228,7 @@ private: this->column_size[VGC_NUMBER] = GetStringBoundingBox(STR_GROUP_COUNT_WITH_SUBGROUP); this->tiny_step_height = std::max(this->tiny_step_height, this->column_size[VGC_NUMBER].height); - this->tiny_step_height += WD_MATRIX_TOP; + this->tiny_step_height += WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM; return WD_FRAMERECT_LEFT + 8 + this->column_size[VGC_FOLD].width + 2 + @@ -255,7 +254,7 @@ private: { /* Highlight the group if a vehicle is dragged over it */ if (g_id == this->group_over) { - GfxFillRect(left + WD_FRAMERECT_LEFT, y + WD_FRAMERECT_TOP, right - WD_FRAMERECT_RIGHT, y + this->tiny_step_height - WD_FRAMERECT_BOTTOM - WD_MATRIX_TOP, _colour_gradient[COLOUR_GREY][7]); + GfxFillRect(left + WD_FRAMERECT_LEFT, y + WD_FRAMERECT_TOP + 1, right - WD_FRAMERECT_RIGHT, y + this->tiny_step_height - WD_FRAMERECT_BOTTOM - 1, _colour_gradient[COLOUR_GREY][7]); } if (g_id == NEW_GROUP) return; @@ -386,7 +385,7 @@ public: resize->height = this->tiny_step_height; /* Minimum height is the height of the list widget minus all and default vehicles... */ - size->height = 4 * GetVehicleListHeight(this->vli.vtype, this->tiny_step_height) - 2 * this->tiny_step_height; + size->height = 4 * GetVehicleListHeight(this->vli.vtype, this->tiny_step_height); /* ... minus the buttons at the bottom ... */ uint max_icon_height = GetSpriteSize(this->GetWidget(WID_GL_CREATE_GROUP)->widget_data).height; @@ -429,11 +428,6 @@ public: *size = maxdim(*size, d); break; } - - case WID_GL_INFO: { - size->height = (FONT_HEIGHT_NORMAL * 3) + WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM; - break; - } } } @@ -555,11 +549,11 @@ public: { switch (widget) { case WID_GL_ALL_VEHICLES: - DrawGroupInfo(r.top + WD_FRAMERECT_TOP, r.left, r.right, ALL_GROUP); + DrawGroupInfo(r.top, r.left, r.right, ALL_GROUP); break; case WID_GL_DEFAULT_VEHICLES: - DrawGroupInfo(r.top + WD_FRAMERECT_TOP, r.left, r.right, DEFAULT_GROUP); + DrawGroupInfo(r.top, r.left, r.right, DEFAULT_GROUP); break; case WID_GL_INFO: { @@ -600,7 +594,7 @@ public: } case WID_GL_LIST_GROUP: { - int y1 = r.top + WD_FRAMERECT_TOP; + int y1 = r.top; int max = std::min(this->group_sb->GetPosition() + this->group_sb->GetCapacity(), this->groups.size()); for (int i = this->group_sb->GetPosition(); i < max; ++i) { const Group *g = this->groups[i]; From da55286c2c83a554130e7712343ddcd2f3f063c7 Mon Sep 17 00:00:00 2001 From: Milek7 Date: Sat, 17 Apr 2021 20:19:18 +0200 Subject: [PATCH 083/268] Fix: Corrupted savegame could crash the game by providing invalid gamelog enums. (#9045) --- src/saveload/gamelog_sl.cpp | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/saveload/gamelog_sl.cpp b/src/saveload/gamelog_sl.cpp index 6bff1b154c..d68297c9a7 100644 --- a/src/saveload/gamelog_sl.cpp +++ b/src/saveload/gamelog_sl.cpp @@ -107,8 +107,11 @@ static void Load_GLOG_common(LoggedAction *&gamelog_action, uint &gamelog_action assert(gamelog_action == nullptr); assert(gamelog_actions == 0); - GamelogActionType at; - while ((at = (GamelogActionType)SlReadByte()) != GLAT_NONE) { + byte type; + while ((type = SlReadByte()) != GLAT_NONE) { + if (type >= GLAT_END) SlErrorCorrupt("Invalid gamelog action type"); + GamelogActionType at = (GamelogActionType)type; + gamelog_action = ReallocT(gamelog_action, gamelog_actions + 1); LoggedAction *la = &gamelog_action[gamelog_actions++]; @@ -118,8 +121,10 @@ static void Load_GLOG_common(LoggedAction *&gamelog_action, uint &gamelog_action la->change = nullptr; la->changes = 0; - GamelogChangeType ct; - while ((ct = (GamelogChangeType)SlReadByte()) != GLCT_NONE) { + while ((type = SlReadByte()) != GLCT_NONE) { + if (type >= GLCT_END) SlErrorCorrupt("Invalid gamelog change type"); + GamelogChangeType ct = (GamelogChangeType)type; + la->change = ReallocT(la->change, la->changes + 1); LoggedChange *lc = &la->change[la->changes++]; @@ -127,8 +132,6 @@ static void Load_GLOG_common(LoggedAction *&gamelog_action, uint &gamelog_action memset(lc, 0, sizeof(*lc)); lc->ct = ct; - assert((uint)ct < GLCT_END); - SlObject(lc, _glog_desc[ct]); } } From aade177d79259fee204fdac8a5fbaef603a957c9 Mon Sep 17 00:00:00 2001 From: Milek7 Date: Sat, 17 Apr 2021 20:19:37 +0200 Subject: [PATCH 084/268] Fix: Corrupted savegame could cause heap corruption by writing outside link graph edge matrix. (#9046) --- src/saveload/linkgraph_sl.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/saveload/linkgraph_sl.cpp b/src/saveload/linkgraph_sl.cpp index a597edfc43..f571e331a5 100644 --- a/src/saveload/linkgraph_sl.cpp +++ b/src/saveload/linkgraph_sl.cpp @@ -151,6 +151,7 @@ void SaveLoad_LinkGraph(LinkGraph &lg) } else { /* ... but as that wasted a lot of space we save a sparse matrix now. */ for (NodeID to = from; to != INVALID_NODE; to = lg.edges[from][to].next_edge) { + if (to >= size) SlErrorCorrupt("Link graph structure overflow"); SlObject(&lg.edges[from][to], _edge_desc); } } From 195cf31cb9611594e8266d4145c8dbb0373fb4ab Mon Sep 17 00:00:00 2001 From: PeterN Date: Sat, 17 Apr 2021 19:20:22 +0100 Subject: [PATCH 085/268] Fix: Inconsistent button sizing on AI/GS setting window depending on scale settings. (#9044) --- src/ai/ai_gui.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/ai/ai_gui.cpp b/src/ai/ai_gui.cpp index 4c0330bc77..14fc65cc8c 100644 --- a/src/ai/ai_gui.cpp +++ b/src/ai/ai_gui.cpp @@ -704,16 +704,16 @@ static const NWidgetPart _nested_ai_config_widgets[] = { NWidget(WWT_MATRIX, COLOUR_MAUVE, WID_AIC_GAMELIST), SetMinimalSize(288, 14), SetFill(1, 0), SetMatrixDataTip(1, 1, STR_AI_CONFIG_GAMELIST_TOOLTIP), EndContainer(), NWidget(NWID_HORIZONTAL, NC_EQUALSIZE), SetPIP(7, 0, 7), - NWidget(WWT_PUSHTXTBTN, COLOUR_YELLOW, WID_AIC_CHANGE), SetFill(1, 0), SetMinimalSize(93, 12), SetDataTip(STR_AI_CONFIG_CHANGE, STR_AI_CONFIG_CHANGE_TOOLTIP), - NWidget(WWT_PUSHTXTBTN, COLOUR_YELLOW, WID_AIC_CONFIGURE), SetFill(1, 0), SetMinimalSize(93, 12), SetDataTip(STR_AI_CONFIG_CONFIGURE, STR_AI_CONFIG_CONFIGURE_TOOLTIP), - NWidget(WWT_PUSHTXTBTN, COLOUR_YELLOW, WID_AIC_CLOSE), SetFill(1, 0), SetMinimalSize(93, 12), SetDataTip(STR_AI_SETTINGS_CLOSE, STR_NULL), - EndContainer(), + NWidget(WWT_PUSHTXTBTN, COLOUR_YELLOW, WID_AIC_CHANGE), SetFill(1, 0), SetMinimalSize(93, 0), SetDataTip(STR_AI_CONFIG_CHANGE, STR_AI_CONFIG_CHANGE_TOOLTIP), + NWidget(WWT_PUSHTXTBTN, COLOUR_YELLOW, WID_AIC_CONFIGURE), SetFill(1, 0), SetMinimalSize(93, 0), SetDataTip(STR_AI_CONFIG_CONFIGURE, STR_AI_CONFIG_CONFIGURE_TOOLTIP), + NWidget(WWT_PUSHTXTBTN, COLOUR_YELLOW, WID_AIC_CLOSE), SetFill(1, 0), SetMinimalSize(93, 0), SetDataTip(STR_AI_SETTINGS_CLOSE, STR_NULL), + EndContainer(), NWidget(NWID_HORIZONTAL, NC_EQUALSIZE), SetPIP(7, 0, 7), NWidget(WWT_PUSHTXTBTN, COLOUR_YELLOW, WID_AIC_TEXTFILE + TFT_README), SetFill(1, 0), SetResize(1, 0), SetDataTip(STR_TEXTFILE_VIEW_README, STR_NULL), NWidget(WWT_PUSHTXTBTN, COLOUR_YELLOW, WID_AIC_TEXTFILE + TFT_CHANGELOG), SetFill(1, 0), SetResize(1, 0), SetDataTip(STR_TEXTFILE_VIEW_CHANGELOG, STR_NULL), NWidget(WWT_PUSHTXTBTN, COLOUR_YELLOW, WID_AIC_TEXTFILE + TFT_LICENSE), SetFill(1, 0), SetResize(1, 0), SetDataTip(STR_TEXTFILE_VIEW_LICENCE, STR_NULL), EndContainer(), - NWidget(WWT_PUSHTXTBTN, COLOUR_YELLOW, WID_AIC_CONTENT_DOWNLOAD), SetFill(1, 0), SetMinimalSize(279, 12), SetPadding(0, 7, 9, 7), SetDataTip(STR_INTRO_ONLINE_CONTENT, STR_INTRO_TOOLTIP_ONLINE_CONTENT), + NWidget(WWT_PUSHTXTBTN, COLOUR_YELLOW, WID_AIC_CONTENT_DOWNLOAD), SetFill(1, 0), SetMinimalSize(279, 0), SetPadding(0, 7, 9, 7), SetDataTip(STR_INTRO_ONLINE_CONTENT, STR_INTRO_TOOLTIP_ONLINE_CONTENT), EndContainer(), }; From 3248a6c12b27801d44fbb67b405d204d10672c9f Mon Sep 17 00:00:00 2001 From: Peter Nelson Date: Sat, 17 Apr 2021 21:08:47 +0100 Subject: [PATCH 086/268] Fix #9042: Make multiplayer server list height auto-fill window. #9042 did not fix all combinations of scaling options. This additional change makes the server list automatically fill available height. --- src/network/network_gui.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/network/network_gui.cpp b/src/network/network_gui.cpp index c1f11e84e3..33639584a7 100644 --- a/src/network/network_gui.cpp +++ b/src/network/network_gui.cpp @@ -500,6 +500,7 @@ public: switch (widget) { case WID_NG_MATRIX: resize->height = WD_MATRIX_TOP + std::max(GetSpriteSize(SPR_BLOT).height, (uint)FONT_HEIGHT_NORMAL) + WD_MATRIX_BOTTOM; + fill->height = resize->height; size->height = 12 * resize->height; break; From 4400bbfa964dd6353f0cf7591f6be6a789039399 Mon Sep 17 00:00:00 2001 From: Rubidium Date: Sat, 17 Apr 2021 09:59:18 +0200 Subject: [PATCH 087/268] Change: [Script] Let Script_FatalError use std::string instead of const char * --- src/script/script_fatalerror.hpp | 6 +++--- src/script/script_instance.cpp | 8 ++++---- src/script/script_scanner.cpp | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/script/script_fatalerror.hpp b/src/script/script_fatalerror.hpp index 5d1a2c1250..96c64e0b9d 100644 --- a/src/script/script_fatalerror.hpp +++ b/src/script/script_fatalerror.hpp @@ -19,7 +19,7 @@ public: * Creates a "fatal error" exception. * @param msg The message describing the cause of the fatal error. */ - Script_FatalError(const char *msg) : + Script_FatalError(const std::string &msg) : msg(msg) {} @@ -27,10 +27,10 @@ public: * The error message associated with the fatal error. * @return The error message. */ - const char *GetErrorMessage() { return msg; } + const std::string &GetErrorMessage() const { return msg; } private: - const char *msg; ///< The error message. + const std::string msg; ///< The error message. }; #endif /* SCRIPT_FATALERROR_HPP */ diff --git a/src/script/script_instance.cpp b/src/script/script_instance.cpp index 8227060d35..f7f9de4fc8 100644 --- a/src/script/script_instance.cpp +++ b/src/script/script_instance.cpp @@ -100,7 +100,7 @@ void ScriptInstance::Initialize(const char *main_script, const char *instance_na ScriptObject::SetAllowDoCommand(true); } catch (Script_FatalError &e) { this->is_dead = true; - this->engine->ThrowError(e.GetErrorMessage()); + this->engine->ThrowError(e.GetErrorMessage().c_str()); this->engine->ResumeError(); this->Died(); } @@ -228,7 +228,7 @@ void ScriptInstance::GameLoop() this->callback = e.GetSuspendCallback(); } catch (Script_FatalError &e) { this->is_dead = true; - this->engine->ThrowError(e.GetErrorMessage()); + this->engine->ThrowError(e.GetErrorMessage().c_str()); this->engine->ResumeError(); this->Died(); } @@ -249,7 +249,7 @@ void ScriptInstance::GameLoop() this->callback = e.GetSuspendCallback(); } catch (Script_FatalError &e) { this->is_dead = true; - this->engine->ThrowError(e.GetErrorMessage()); + this->engine->ThrowError(e.GetErrorMessage().c_str()); this->engine->ResumeError(); this->Died(); } @@ -505,7 +505,7 @@ void ScriptInstance::Save() /* If we don't mark the script as dead here cleaning up the squirrel * stack could throw Script_FatalError again. */ this->is_dead = true; - this->engine->ThrowError(e.GetErrorMessage()); + this->engine->ThrowError(e.GetErrorMessage().c_str()); this->engine->ResumeError(); SaveEmpty(); /* We can't kill the script here, so mark it as crashed (not dead) and diff --git a/src/script/script_scanner.cpp b/src/script/script_scanner.cpp index 6fa88bfee5..8b48809bf7 100644 --- a/src/script/script_scanner.cpp +++ b/src/script/script_scanner.cpp @@ -38,7 +38,7 @@ bool ScriptScanner::AddFile(const std::string &filename, size_t basepath_length, try { this->engine->LoadScript(filename.c_str()); } catch (Script_FatalError &e) { - DEBUG(script, 0, "Fatal error '%s' when trying to load the script '%s'.", e.GetErrorMessage(), filename.c_str()); + DEBUG(script, 0, "Fatal error '%s' when trying to load the script '%s'.", e.GetErrorMessage().c_str(), filename.c_str()); return false; } return true; From e5fedcd6dab98d6ba6e225ab6a05fd5b05fd3edd Mon Sep 17 00:00:00 2001 From: Rubidium Date: Sat, 17 Apr 2021 10:01:24 +0200 Subject: [PATCH 088/268] Fix #6322: [Script] Try to let the script die when no memory can be allocated instead of crashing the whole game --- src/script/squirrel.cpp | 71 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 68 insertions(+), 3 deletions(-) diff --git a/src/script/squirrel.cpp b/src/script/squirrel.cpp index f97896eb93..b9d614e8d0 100644 --- a/src/script/squirrel.cpp +++ b/src/script/squirrel.cpp @@ -21,7 +21,13 @@ #include <../squirrel/sqvm.h> #include "../core/alloc_func.hpp" -#include "../safeguards.h" +/** + * In the memory allocator for Squirrel we want to directly use malloc/realloc, so when the OS + * does not have enough memory the game does not go into unrecoverable error mode and kill the + * whole game, but rather let the AI die though then we need to circumvent MallocT/ReallocT. + * + * So no #include "../safeguards.h" here as is required, but after the allocator's implementation. + */ /* * If changing the call paths into the scripting engine, define this symbol to enable full debugging of allocations. @@ -32,6 +38,13 @@ struct ScriptAllocator { size_t allocated_size; ///< Sum of allocated data size size_t allocation_limit; ///< Maximum this allocator may use before allocations fail + /** + * Whether the error has already been thrown, so to not throw secondary errors in + * the handling of the allocation error. This as the handling of the error will + * throw a Squirrel error so the Squirrel stack can be dumped, however that gets + * allocated by this allocator and then you might end up in an infinite loop. + */ + bool error_thrown; static const size_t SAFE_LIMIT = 0x8000000; ///< 128 MiB, a safe choice for almost any situation @@ -44,11 +57,52 @@ struct ScriptAllocator { if (this->allocated_size > this->allocation_limit) throw Script_FatalError("Maximum memory allocation exceeded"); } + /** + * Catch all validation for the allocation; did it allocate too much memory according + * to the allocation limit or did the allocation at the OS level maybe fail? In those + * error situations a Script_FatalError is thrown, but once that has been done further + * allocations are allowed to make it possible for Squirrel to throw the error and + * clean everything up. + * @param requested_size The requested size that was requested to be allocated. + * @param p The pointer to the allocated object, or null if allocation failed. + */ + void CheckAllocation(size_t requested_size, const void *p) + { + if (this->allocated_size > this->allocation_limit && !this->error_thrown) { + /* Do not allow allocating more than the allocation limit, except when an error is + * already as then the allocation is for throwing that error in Squirrel, the + * associated stack trace information and while cleaning up the AI. */ + this->error_thrown = true; + char buff[128]; + seprintf(buff, lastof(buff), "Maximum memory allocation exceeded by " PRINTF_SIZE " bytes when allocating " PRINTF_SIZE " bytes", + this->allocated_size - this->allocation_limit, requested_size); + throw Script_FatalError(buff); + } + + if (p == nullptr) { + /* The OS did not have enough memory to allocate the object, regardless of the + * limit imposed by OpenTTD on the amount of memory that may be allocated. */ + if (this->error_thrown) { + /* The allocation is called in the error handling of a memory allocation + * failure, then not being able to allocate that small amount of memory + * means there is no other choice than to bug out completely. */ + MallocError(requested_size); + } + + this->error_thrown = true; + char buff[64]; + seprintf(buff, lastof(buff), "Out of memory. Cannot allocate " PRINTF_SIZE " bytes", requested_size); + throw Script_FatalError(buff); + } + } + void *Malloc(SQUnsignedInteger size) { - void *p = MallocT(size); + void *p = malloc(size); this->allocated_size += size; + this->CheckAllocation(size, p); + #ifdef SCRIPT_DEBUG_ALLOCATIONS assert(p != nullptr); assert(this->allocations.find(p) == this->allocations.end()); @@ -73,11 +127,13 @@ struct ScriptAllocator { this->allocations.erase(p); #endif - void *new_p = ReallocT(static_cast(p), size); + void *new_p = realloc(p, size); this->allocated_size -= oldsize; this->allocated_size += size; + this->CheckAllocation(size, p); + #ifdef SCRIPT_DEBUG_ALLOCATIONS assert(new_p != nullptr); assert(this->allocations.find(p) == this->allocations.end()); @@ -104,6 +160,7 @@ struct ScriptAllocator { this->allocated_size = 0; this->allocation_limit = static_cast(_settings_game.script.script_max_memory_megabytes) << 20; if (this->allocation_limit == 0) this->allocation_limit = SAFE_LIMIT; // in case the setting is somehow zero + this->error_thrown = false; } ~ScriptAllocator() @@ -114,6 +171,14 @@ struct ScriptAllocator { } }; +/** + * In the memory allocator for Squirrel we want to directly use malloc/realloc, so when the OS + * does not have enough memory the game does not go into unrecoverable error mode and kill the + * whole game, but rather let the AI die though then we need to circumvent MallocT/ReallocT. + * For the rest of this code, the safeguards should be in place though! + */ +#include "../safeguards.h" + ScriptAllocator *_squirrel_allocator = nullptr; /* See 3rdparty/squirrel/squirrel/sqmem.cpp for the default allocator implementation, which this overrides */ From 7c7c6cde0344298d7144c85fb97ec9123f32fdca Mon Sep 17 00:00:00 2001 From: dP Date: Sun, 11 Apr 2021 20:38:29 +0300 Subject: [PATCH 089/268] Fix: Do not unlock railtypes when enabling wagons with GameScript --- src/engine.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/engine.cpp b/src/engine.cpp index 596e870636..60b0d42224 100644 --- a/src/engine.cpp +++ b/src/engine.cpp @@ -722,11 +722,9 @@ static void EnableEngineForCompany(EngineID eid, CompanyID company) SetBit(e->company_avail, company); if (e->type == VEH_TRAIN) { - assert(e->u.rail.railtype < RAILTYPE_END); - c->avail_railtypes = AddDateIntroducedRailTypes(c->avail_railtypes | GetRailTypeInfo(e->u.rail.railtype)->introduces_railtypes, _date); + c->avail_railtypes = GetCompanyRailtypes(c->index); } else if (e->type == VEH_ROAD) { - assert(e->u.road.roadtype < ROADTYPE_END); - c->avail_roadtypes = AddDateIntroducedRoadTypes(c->avail_roadtypes | GetRoadTypeInfo(e->u.road.roadtype)->introduces_roadtypes, _date); + c->avail_roadtypes = GetCompanyRoadTypes(c->index); } if (company == _local_company) { From f757d07bb3884b55854ccd3035c3321c97d993dc Mon Sep 17 00:00:00 2001 From: translators Date: Sun, 18 Apr 2021 17:49:06 +0000 Subject: [PATCH 090/268] Update: Translations from eints swedish: 3 changes by DonaldDuck313 spanish: 39 changes by MontyMontana --- src/lang/spanish.txt | 78 ++++++++++++++++++++++---------------------- src/lang/swedish.txt | 4 ++- 2 files changed, 42 insertions(+), 40 deletions(-) diff --git a/src/lang/spanish.txt b/src/lang/spanish.txt index 95630e6d31..e007965478 100644 --- a/src/lang/spanish.txt +++ b/src/lang/spanish.txt @@ -149,14 +149,14 @@ STR_ABBREV_PAPER :{TINY_FONT}PP STR_ABBREV_GOLD :{TINY_FONT}OR STR_ABBREV_WATER :{TINY_FONT}AG STR_ABBREV_WHEAT :{TINY_FONT}TG -STR_ABBREV_RUBBER :{TINY_FONT}GM +STR_ABBREV_RUBBER :{TINY_FONT}CA STR_ABBREV_SUGAR :{TINY_FONT}AZ STR_ABBREV_TOYS :{TINY_FONT}JG STR_ABBREV_SWEETS :{TINY_FONT}DC STR_ABBREV_COLA :{TINY_FONT}CL STR_ABBREV_CANDYFLOSS :{TINY_FONT}AA STR_ABBREV_BUBBLES :{TINY_FONT}BU -STR_ABBREV_TOFFEE :{TINY_FONT}CM +STR_ABBREV_TOFFEE :{TINY_FONT}TF STR_ABBREV_BATTERIES :{TINY_FONT}PI STR_ABBREV_PLASTIC :{TINY_FONT}PL STR_ABBREV_FIZZY_DRINKS :{TINY_FONT}RF @@ -242,8 +242,8 @@ STR_TOOLTIP_CLOSE_WINDOW :{BLACK}Cierra l STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS :{BLACK}Título de la ventana - arrastra para moverla STR_TOOLTIP_SHADE :{BLACK}Oculta la ventana - muestra sólo el título de la ventana STR_TOOLTIP_DEBUG :{BLACK}Muestra la información de depuración NewGRF -STR_TOOLTIP_DEFSIZE :{BLACK}Redimensionar ventana al tamaño por defecto. Ctrl+Clic permite almacenar el tamaño actual como tamaño por defecto -STR_TOOLTIP_STICKY :{BLACK}Esta ventana no se cerrará tras usar el comando 'Cerrar Todas las Ventanas'. Ctrl+Clic permite guardar el estado como estado por defecto +STR_TOOLTIP_DEFSIZE :{BLACK}Redimensionar ventana al tamaño por defecto. Ctrl+clic permite almacenar el tamaño actual como tamaño por defecto +STR_TOOLTIP_STICKY :{BLACK}Esta ventana no se cerrará tras usar el comando 'Cerrar Todas las Ventanas'. Ctrl+clic permite guardar el estado como estado por defecto STR_TOOLTIP_RESIZE :{BLACK}Clica y arrastra para redimensionar la ventana STR_TOOLTIP_TOGGLE_LARGE_SMALL_WINDOW :{BLACK}Alterna el tamaño de ventana entre grande/pequeño STR_TOOLTIP_VSCROLL_BAR_SCROLLS_LIST :{BLACK}Barra de desplazamiento - mueve la lista hacia arriba/abajo @@ -340,10 +340,10 @@ STR_TOOLBAR_TOOLTIP_DISPLAY_GOALS_LIST :{BLACK}Muestra STR_TOOLBAR_TOOLTIP_DISPLAY_GRAPHS :{BLACK}Mostrar gráficas STR_TOOLBAR_TOOLTIP_DISPLAY_COMPANY_LEAGUE :{BLACK}Mostrar tabla de clasificación de empresas STR_TOOLBAR_TOOLTIP_FUND_CONSTRUCTION_OF_NEW :{BLACK}Financiar la construcción de una industria nueva o mostrar lista de industrias -STR_TOOLBAR_TOOLTIP_DISPLAY_LIST_OF_COMPANY_TRAINS :{BLACK}Mostrar lista de trenes de la empresa. Ctrl+Clic hace que no aparezca la lista de grupos -STR_TOOLBAR_TOOLTIP_DISPLAY_LIST_OF_COMPANY_ROAD_VEHICLES :{BLACK}Mostrar lista de vehículos de carretera de la empresa. Ctrl+Clic hace que no aparezca la lista de grupos -STR_TOOLBAR_TOOLTIP_DISPLAY_LIST_OF_COMPANY_SHIPS :{BLACK}Mostrar lista de barcos de la empresa. Ctrl+Clic hace que no aparezca la lista de grupos -STR_TOOLBAR_TOOLTIP_DISPLAY_LIST_OF_COMPANY_AIRCRAFT :{BLACK}Mostrar lista de aeronaves de la empresa. Ctrl+Clic hace que no aparezca la lista de grupos +STR_TOOLBAR_TOOLTIP_DISPLAY_LIST_OF_COMPANY_TRAINS :{BLACK}Mostrar lista de trenes de la empresa. Ctrl+clic hace que no aparezca la lista de grupos +STR_TOOLBAR_TOOLTIP_DISPLAY_LIST_OF_COMPANY_ROAD_VEHICLES :{BLACK}Mostrar lista de vehículos de carretera de la empresa. Ctrl+clic hace que no aparezca la lista de grupos +STR_TOOLBAR_TOOLTIP_DISPLAY_LIST_OF_COMPANY_SHIPS :{BLACK}Mostrar lista de barcos de la empresa. Ctrl+clic hace que no aparezca la lista de grupos +STR_TOOLBAR_TOOLTIP_DISPLAY_LIST_OF_COMPANY_AIRCRAFT :{BLACK}Mostrar lista de aeronaves de la empresa. Ctrl+clic hace que no aparezca la lista de grupos STR_TOOLBAR_TOOLTIP_ZOOM_THE_VIEW_IN :{BLACK}Acercar vista STR_TOOLBAR_TOOLTIP_ZOOM_THE_VIEW_OUT :{BLACK}Alejar vista STR_TOOLBAR_TOOLTIP_BUILD_RAILROAD_TRACK :{BLACK}Construir ferrocarril @@ -1907,7 +1907,7 @@ STR_LIVERY_SHIP_TOOLTIP :{BLACK}Muestra STR_LIVERY_AIRCRAFT_TOOLTIP :{BLACK}Muestra el esquema de color de las aeronaves STR_LIVERY_PRIMARY_TOOLTIP :{BLACK}Selecciona el color primario para el esquema seleccionado. Ctrl+clic fijará este color para todo el esquema STR_LIVERY_SECONDARY_TOOLTIP :{BLACK}Selecciona el color secundario para el esquema seleccionado. Ctrl+clic fijará este color para todo el esquema -STR_LIVERY_PANEL_TOOLTIP :{BLACK}Selecciona un esquema de color a modificar, o selecciona varios pulsando Ctrl+Clic. Pulsa en la caja para cambiar el uso del esquema +STR_LIVERY_PANEL_TOOLTIP :{BLACK}Selecciona un esquema de color a modificar, o selecciona varios pulsando Ctrl+clic. Pulsa en la caja para cambiar el uso del esquema STR_LIVERY_DEFAULT :Estación Normal STR_LIVERY_STEAM :Locomotora a Vapor @@ -2362,15 +2362,15 @@ STR_MISSING_GRAPHICS_ERROR_QUIT :{BLACK}Salir de # Transparency settings window STR_TRANSPARENCY_CAPTION :{WHITE}Opciones de Transparencia -STR_TRANSPARENT_SIGNS_TOOLTIP :{BLACK}Ajustar transparencia para carteles. Ctrl+Clic para bloquear -STR_TRANSPARENT_TREES_TOOLTIP :{BLACK}Ajustar transparencia para árboles. Ctrl+Clic para bloquear -STR_TRANSPARENT_HOUSES_TOOLTIP :{BLACK}Ajustar transparencia para casas. Ctrl+Clic para bloquear -STR_TRANSPARENT_INDUSTRIES_TOOLTIP :{BLACK}Ajustar transparencia para industrias. Ctrl+Clic para bloquear -STR_TRANSPARENT_BUILDINGS_TOOLTIP :{BLACK}Ajustar transparencia para construcciones como estaciones, depósitos o puntos de ruta. Ctrl+Clic para bloquear -STR_TRANSPARENT_BRIDGES_TOOLTIP :{BLACK}Ajustar transparencia para puentes. Ctrl+Clic para bloquear -STR_TRANSPARENT_STRUCTURES_TOOLTIP :{BLACK}Ajustar transparencia para estructuras como faros o antenas. Ctrl+Clic para bloquear -STR_TRANSPARENT_CATENARY_TOOLTIP :{BLACK}Ajustar transparencia para catenaria. Ctrl+Clic para bloquear -STR_TRANSPARENT_LOADING_TOOLTIP :{BLACK}Ajustar transparencia para los indicadores de carga. Ctrl+Clic para bloquear +STR_TRANSPARENT_SIGNS_TOOLTIP :{BLACK}Ajustar transparencia para carteles. Ctrl+clic para bloquear +STR_TRANSPARENT_TREES_TOOLTIP :{BLACK}Ajustar transparencia para árboles. Ctrl+clic para bloquear +STR_TRANSPARENT_HOUSES_TOOLTIP :{BLACK}Ajustar transparencia para casas. Ctrl+clic para bloquear +STR_TRANSPARENT_INDUSTRIES_TOOLTIP :{BLACK}Ajustar transparencia para industrias. Ctrl+clic para bloquear +STR_TRANSPARENT_BUILDINGS_TOOLTIP :{BLACK}Ajustar transparencia para construcciones como estaciones, depósitos o puntos de ruta. Ctrl+clic para bloquear +STR_TRANSPARENT_BRIDGES_TOOLTIP :{BLACK}Ajustar transparencia para puentes. Ctrl+clic para bloquear +STR_TRANSPARENT_STRUCTURES_TOOLTIP :{BLACK}Ajustar transparencia para estructuras como faros o antenas. Ctrl+clic para bloquear +STR_TRANSPARENT_CATENARY_TOOLTIP :{BLACK}Ajustar transparencia para catenaria. Ctrl+clic para bloquear +STR_TRANSPARENT_LOADING_TOOLTIP :{BLACK}Ajustar transparencia para los indicadores de carga. Ctrl+clic para bloquear STR_TRANSPARENT_INVISIBLE_TOOLTIP :{BLACK}Establece los objetos como invisibles en vez de transparentes # Linkgraph legend window @@ -3072,7 +3072,7 @@ STR_SPRITE_ALIGNER_GOTO_TOOLTIP :{BLACK}Va al sp STR_SPRITE_ALIGNER_PREVIOUS_BUTTON :{BLACK}Sprite anterior STR_SPRITE_ALIGNER_PREVIOUS_TOOLTIP :{BLACK}Salta al sprite anterior (ignorando pseudosprites, sprites recoloreados y sprites de fuente) y pasa del primer al último sprite STR_SPRITE_ALIGNER_SPRITE_TOOLTIP :{BLACK}Representa el sprite seleccionado. Su alineamiento es ignorado al dibujarlo -STR_SPRITE_ALIGNER_MOVE_TOOLTIP :{BLACK}Mover el sprite, cambiando los ajustes X e Y. Ctrl+Clic mueve el sprite ocho unidades de una sola vez +STR_SPRITE_ALIGNER_MOVE_TOOLTIP :{BLACK}Mover el sprite, cambiando los ajustes X e Y. Ctrl+clic mueve el sprite ocho unidades de una sola vez STR_SPRITE_ALIGNER_RESET_BUTTON :{BLACK}Reiniciar coordenadas relativas STR_SPRITE_ALIGNER_RESET_TOOLTIP :{BLACK}Reinicia las coordenadas relativas actuales STR_SPRITE_ALIGNER_OFFSETS_ABS :{BLACK}Coordenada X: {NUM}, Coordenada Y: {NUM} (Absoluta) @@ -3176,7 +3176,7 @@ STR_TOWN_DIRECTORY_CAPTION :{WHITE}Municipi STR_TOWN_DIRECTORY_NONE :{ORANGE}- Ninguna - STR_TOWN_DIRECTORY_TOWN :{ORANGE}{TOWN}{BLACK} ({COMMA}) STR_TOWN_DIRECTORY_CITY :{ORANGE}{TOWN}{YELLOW} (Ciudad){BLACK} ({COMMA}) -STR_TOWN_DIRECTORY_LIST_TOOLTIP :{BLACK}Nombres de los municipios - Clic sobre un nombre para centrar la vista principal en él. Ctrl+Clic abre una ventana de visualización en dicha posición +STR_TOWN_DIRECTORY_LIST_TOOLTIP :{BLACK}Nombres de los municipios - Clic sobre un nombre para centrar la vista principal en él. Ctrl+clic abre una ventana de visualización en dicha posición STR_TOWN_POPULATION :{BLACK}Población mundial: {COMMA} # Town view window @@ -3194,7 +3194,7 @@ STR_TOWN_VIEW_TOWN_GROWS_EVERY :{BLACK}El munic STR_TOWN_VIEW_TOWN_GROWS_EVERY_FUNDED :{BLACK}El municipio crece cada {ORANGE}{COMMA}{BLACK}{NBSP}día{P "" s} (edificios financiados) STR_TOWN_VIEW_TOWN_GROW_STOPPED :{BLACK}El municipio {RED}no{BLACK} está creciendo STR_TOWN_VIEW_NOISE_IN_TOWN :{BLACK}Nivel de ruido en el municipio: {ORANGE}{COMMA}{BLACK} Máx.: {ORANGE}{COMMA} -STR_TOWN_VIEW_CENTER_TOOLTIP :{BLACK}Centrar vista sobre el municipio. Ctrl+Clic abre un punto de vista en dicha posición +STR_TOWN_VIEW_CENTER_TOOLTIP :{BLACK}Centrar vista sobre el municipio. Ctrl+clic abre un punto de vista en dicha posición STR_TOWN_VIEW_LOCAL_AUTHORITY_BUTTON :{BLACK}Autoridad local STR_TOWN_VIEW_LOCAL_AUTHORITY_TOOLTIP :{BLACK}Muestra la información sobre las autoridades locales STR_TOWN_VIEW_RENAME_TOOLTIP :{BLACK}Cambia el nombre del municipio @@ -3283,7 +3283,7 @@ STR_SUBSIDIES_OFFERED_FROM_TO :{ORANGE}{STRING STR_SUBSIDIES_NONE :{ORANGE}- Ninguno - STR_SUBSIDIES_SUBSIDISED_TITLE :{BLACK}Líneas ya subvencionadas: STR_SUBSIDIES_SUBSIDISED_FROM_TO :{ORANGE}{STRING} desde {STRING} a {STRING}{YELLOW} ({COMPANY}{YELLOW}, hasta {DATE_SHORT}) -STR_SUBSIDIES_TOOLTIP_CLICK_ON_SERVICE_TO_CENTER :{BLACK}Clic sobre el servicio para centrar la vista principal en esta industria/municipio. Ctrl+Clic abre un punto de vista en dicha posición +STR_SUBSIDIES_TOOLTIP_CLICK_ON_SERVICE_TO_CENTER :{BLACK}Clic sobre el servicio para centrar la vista principal en esta industria/municipio. Ctrl+clic abre un punto de vista en dicha posición # Story book window STR_STORY_BOOK_CAPTION :{WHITE}Historia de {COMPANY} @@ -3299,7 +3299,7 @@ STR_STORY_BOOK_NEXT_PAGE_TOOLTIP :{BLACK}Ir a la STR_STORY_BOOK_INVALID_GOAL_REF :{RED}Referencia de objetivo inválida # Station list window -STR_STATION_LIST_TOOLTIP :{BLACK}Nombres de estación - Clic sobre un nombre para centrar la vista principal en la estación. Ctrl+Clic abre un punto de vista en dicha posición +STR_STATION_LIST_TOOLTIP :{BLACK}Nombres de estación - Clic sobre un nombre para centrar la vista principal en la estación. Ctrl+clic abre un punto de vista en dicha posición STR_STATION_LIST_USE_CTRL_TO_SELECT_MORE :{BLACK}Mantén pulsado Ctrl para seleccionar más de un elemento STR_STATION_LIST_CAPTION :{WHITE}{COMPANY} - {COMMA} Estaciones STR_STATION_LIST_STATION :{YELLOW}{STATION} {STATION_FEATURES} @@ -3361,7 +3361,7 @@ STR_CARGO_RATING_EXCELLENT :Excelente STR_CARGO_RATING_OUTSTANDING :Excepcional ############ range for rating ends -STR_STATION_VIEW_CENTER_TOOLTIP :{BLACK}Centrar vista en la posición de la estación. Ctrl+Clic abre un punto de vista en dicha posición +STR_STATION_VIEW_CENTER_TOOLTIP :{BLACK}Centrar vista en la posición de la estación. Ctrl+clic abre un punto de vista en dicha posición STR_STATION_VIEW_RENAME_TOOLTIP :{BLACK}Cambia el nombre de la estación STR_STATION_VIEW_SCHEDULED_TRAINS_TOOLTIP :{BLACK}Muestra todos los trenes que tienen esta estación en su horario @@ -3376,7 +3376,7 @@ STR_STATION_VIEW_CLOSE_AIRPORT_TOOLTIP :{BLACK}Evita qu # Waypoint/buoy view window STR_WAYPOINT_VIEW_CAPTION :{WHITE}{WAYPOINT} -STR_WAYPOINT_VIEW_CENTER_TOOLTIP :{BLACK}Centrar vista en posición del punto de ruta. Ctrl+Clic abre un punto de vista en dicha posición +STR_WAYPOINT_VIEW_CENTER_TOOLTIP :{BLACK}Centrar vista en posición del punto de ruta. Ctrl+clic abre un punto de vista en dicha posición STR_WAYPOINT_VIEW_CHANGE_WAYPOINT_NAME :{BLACK}Cambiar nombre del punto de ruta STR_BUOY_VIEW_CENTER_TOOLTIP :{BLACK}Centra la vista en posición de la boya. Ctrl+clic abre un punto de vista en dicha posición STR_BUOY_VIEW_CHANGE_BUOY_NAME :{BLACK}Cambiar nombre de boya @@ -3829,15 +3829,15 @@ STR_VEHICLE_VIEW_ROAD_VEHICLE_CENTER_TOOLTIP :{BLACK}Centra l STR_VEHICLE_VIEW_SHIP_CENTER_TOOLTIP :{BLACK}Centra la vista principal en la posición del barco. Doble clic seguirá al barco en la vista principal. Ctrl+clic abre un nuevo punto de vista en dicha posición. STR_VEHICLE_VIEW_AIRCRAFT_CENTER_TOOLTIP :{BLACK}Centra la vista principal en la posición de la aeronave. Doble clic seguirá la aeronave en la vista principal. Ctrl+clic abre un nuevo punto de vista en dicha posición. -STR_VEHICLE_VIEW_TRAIN_SEND_TO_DEPOT_TOOLTIP :{BLACK}Enviar tren al depósito. Ctrl+Clic para realizar solamente mantenimiento -STR_VEHICLE_VIEW_ROAD_VEHICLE_SEND_TO_DEPOT_TOOLTIP :{BLACK}Enviar vehículo al depósito. Ctrl+Clic para realizar solamente mantenimiento -STR_VEHICLE_VIEW_SHIP_SEND_TO_DEPOT_TOOLTIP :{BLACK}Enviar barco al astillero. Ctrl+Clic para realizar solamente mantenimiento -STR_VEHICLE_VIEW_AIRCRAFT_SEND_TO_DEPOT_TOOLTIP :{BLACK}Enviar aeronave al hangar. Ctrl+Clic para realizar solamente mantenimiento +STR_VEHICLE_VIEW_TRAIN_SEND_TO_DEPOT_TOOLTIP :{BLACK}Enviar tren al depósito. Ctrl+clic para realizar solamente mantenimiento +STR_VEHICLE_VIEW_ROAD_VEHICLE_SEND_TO_DEPOT_TOOLTIP :{BLACK}Enviar vehículo al depósito. Ctrl+clic para realizar solamente mantenimiento +STR_VEHICLE_VIEW_SHIP_SEND_TO_DEPOT_TOOLTIP :{BLACK}Enviar barco al astillero. Ctrl+clic para realizar solamente mantenimiento +STR_VEHICLE_VIEW_AIRCRAFT_SEND_TO_DEPOT_TOOLTIP :{BLACK}Enviar aeronave al hangar. Ctrl+clic para realizar solamente mantenimiento -STR_VEHICLE_VIEW_CLONE_TRAIN_INFO :{BLACK}Esto comprará una copia del tren incluyendo sus vagones. Ctrl+Clic compartirá las órdenes. Mayús+Clic muestra una estimación del precio sin realizar la compra -STR_VEHICLE_VIEW_CLONE_ROAD_VEHICLE_INFO :{BLACK}Esto comprará una copia del vehículo. Ctrl+Clic compartirá las órdenes. Mayús+Clic muestra una estimación del precio sin realizar la compra -STR_VEHICLE_VIEW_CLONE_SHIP_INFO :{BLACK}Esto comprará una copia del barco. Ctrl+Clic compartirá las órdenes. Mayús+Clic muestra una estimación del precio sin realizar la compra -STR_VEHICLE_VIEW_CLONE_AIRCRAFT_INFO :{BLACK}Esto comprará una copia de la aeronave. Ctrl+Clic compartirá las órdenes. Mayús+Clic muestra una estimación del precio sin realizar la compra +STR_VEHICLE_VIEW_CLONE_TRAIN_INFO :{BLACK}Esto comprará una copia del tren incluyendo sus vagones. Ctrl+clic compartirá las órdenes. Mayús+clic muestra una estimación del precio sin realizar la compra +STR_VEHICLE_VIEW_CLONE_ROAD_VEHICLE_INFO :{BLACK}Esto comprará una copia del vehículo. Ctrl+clic compartirá las órdenes. Mayús+clic muestra una estimación del precio sin realizar la compra +STR_VEHICLE_VIEW_CLONE_SHIP_INFO :{BLACK}Esto comprará una copia del barco. Ctrl+clic compartirá las órdenes. Mayús+clic muestra una estimación del precio sin realizar la compra +STR_VEHICLE_VIEW_CLONE_AIRCRAFT_INFO :{BLACK}Esto comprará una copia de la aeronave. Ctrl+clic compartirá las órdenes. Mayús+clic muestra una estimación del precio sin realizar la compra STR_VEHICLE_VIEW_TRAIN_IGNORE_SIGNAL_TOOLTIP :{BLACK}Fuerza al tren a proceder sin esperar a la apertura de señal @@ -3849,10 +3849,10 @@ STR_VEHICLE_VIEW_AIRCRAFT_REFIT_TOOLTIP :{BLACK}Reforma STR_VEHICLE_VIEW_TRAIN_REVERSE_TOOLTIP :{BLACK}Cambia la dirección del tren STR_VEHICLE_VIEW_ROAD_VEHICLE_REVERSE_TOOLTIP :{BLACK}Fuerza al vehículo a dar la vuelta -STR_VEHICLE_VIEW_TRAIN_ORDERS_TOOLTIP :{BLACK}Ver órdenes del tren. Ctrl+Clic muestra su horario -STR_VEHICLE_VIEW_ROAD_VEHICLE_ORDERS_TOOLTIP :{BLACK}Ver órdenes del vehículo Ctrl+Clic muestra su horario -STR_VEHICLE_VIEW_SHIP_ORDERS_TOOLTIP :{BLACK}Ver órdenes del barco. Ctrl+Clic muestra su horario -STR_VEHICLE_VIEW_AIRCRAFT_ORDERS_TOOLTIP :{BLACK}Ver órdenes de la aeronave. Ctrl+Clic muestra su horario +STR_VEHICLE_VIEW_TRAIN_ORDERS_TOOLTIP :{BLACK}Ver órdenes del tren. Ctrl+clic muestra su horario +STR_VEHICLE_VIEW_ROAD_VEHICLE_ORDERS_TOOLTIP :{BLACK}Ver órdenes del vehículo Ctrl+clic muestra su horario +STR_VEHICLE_VIEW_SHIP_ORDERS_TOOLTIP :{BLACK}Ver órdenes del barco. Ctrl+clic muestra su horario +STR_VEHICLE_VIEW_AIRCRAFT_ORDERS_TOOLTIP :{BLACK}Ver órdenes de la aeronave. Ctrl+clic muestra su horario STR_VEHICLE_VIEW_TRAIN_SHOW_DETAILS_TOOLTIP :{BLACK}Muestra los detalles del tren STR_VEHICLE_VIEW_ROAD_VEHICLE_SHOW_DETAILS_TOOLTIP :{BLACK}Muestra los detalles del vehículo @@ -3922,8 +3922,8 @@ STR_VEHICLE_INFO_FEEDER_CARGO_VALUE :{BLACK}Crédito STR_VEHICLE_DETAILS_SERVICING_INTERVAL_DAYS :{BLACK}Intervalo de mantenimiento: {LTBLUE}{COMMA}{NBSP}días{BLACK} Último mantenimiento: {LTBLUE}{DATE_LONG} STR_VEHICLE_DETAILS_SERVICING_INTERVAL_PERCENT :{BLACK}Intervalo de mantenimiento: {LTBLUE}{COMMA}%{BLACK} Último mantenimiento: {LTBLUE}{DATE_LONG} -STR_VEHICLE_DETAILS_INCREASE_SERVICING_INTERVAL_TOOLTIP :{BLACK}Incrementar intervalo de mantenimiento en 10. Ctrl+Clic incrementa el intervalo de mantenimiento en 5 -STR_VEHICLE_DETAILS_DECREASE_SERVICING_INTERVAL_TOOLTIP :{BLACK}Reducir intervalo de mantenimiento en 10. Ctrl+Clic reduce el intervalo de mantenimiento en 5 +STR_VEHICLE_DETAILS_INCREASE_SERVICING_INTERVAL_TOOLTIP :{BLACK}Incrementar intervalo de mantenimiento en 10. Ctrl+clic incrementa el intervalo de mantenimiento en 5 +STR_VEHICLE_DETAILS_DECREASE_SERVICING_INTERVAL_TOOLTIP :{BLACK}Reducir intervalo de mantenimiento en 10. Ctrl+clic reduce el intervalo de mantenimiento en 5 STR_SERVICE_INTERVAL_DROPDOWN_TOOLTIP :{BLACK}Cambia el tipo de intervalo de mantenimiento STR_VEHICLE_DETAILS_DEFAULT :Por defecto diff --git a/src/lang/swedish.txt b/src/lang/swedish.txt index 77d5b6db7d..83a98a2edd 100644 --- a/src/lang/swedish.txt +++ b/src/lang/swedish.txt @@ -1006,6 +1006,8 @@ STR_GAME_OPTIONS_VIDEO_ACCELERATION :{BLACK}Hårdvar STR_GAME_OPTIONS_VIDEO_ACCELERATION_TOOLTIP :{BLACK}Markera denna rutan för att tillåta OpenTTD att försöka använda hårdvaruacceleration. Ändrad inställning kommer bara att gälla efter omstart. STR_GAME_OPTIONS_VIDEO_ACCELERATION_RESTART :{WHITE}Inställningen kommer bara att gälla efter omstart av spelet +STR_GAME_OPTIONS_VIDEO_VSYNC :{BLACK}VSync +STR_GAME_OPTIONS_VIDEO_VSYNC_TOOLTIP :{BLACK}Markera den här rutan för att v-synka skärmen. Ändringar i den här inställningen kommer bara att tillämpas vid omstart av spelet. Fungerar bara med hårdvaruacceleration aktiverad. STR_GAME_OPTIONS_GUI_ZOOM_FRAME :{BLACK}Gränssnittstorlek STR_GAME_OPTIONS_GUI_ZOOM_DROPDOWN_TOOLTIP :{BLACK}Välj vilken gränssnittsstorlek som ska användas @@ -3887,7 +3889,7 @@ STR_VEHICLE_COMMAND_STARTED_SMALL :{TINY_FONT}{GRE STR_VEHICLE_COMMAND_STARTED :{GREEN}Startad # Vehicle details -STR_VEHICLE_DETAILS_CAPTION :{WHITE}{VEHICLE} (Details) +STR_VEHICLE_DETAILS_CAPTION :{WHITE}{VEHICLE} (Detaljer) STR_VEHICLE_NAME_BUTTON :{BLACK}Namn STR_VEHICLE_DETAILS_TRAIN_RENAME :{BLACK}Byt namn på tåg From 17d00537a15b30f06b5a3bb800a7f26ce4eec359 Mon Sep 17 00:00:00 2001 From: translators Date: Mon, 19 Apr 2021 17:50:20 +0000 Subject: [PATCH 091/268] Update: Translations from eints chinese (simplified): 52 changes by clzls --- src/lang/simplified_chinese.txt | 67 +++++++++++++++++++++++++-------- 1 file changed, 52 insertions(+), 15 deletions(-) diff --git a/src/lang/simplified_chinese.txt b/src/lang/simplified_chinese.txt index 891c597f75..1957f157ab 100644 --- a/src/lang/simplified_chinese.txt +++ b/src/lang/simplified_chinese.txt @@ -946,6 +946,7 @@ STR_GAME_OPTIONS_CURRENCY_NTD :新台币 (NTD) STR_GAME_OPTIONS_CURRENCY_CNY :中国人民币 (CNY) STR_GAME_OPTIONS_CURRENCY_HKD :港币 (HKD) STR_GAME_OPTIONS_CURRENCY_INR :印度卢布(INR) +STR_GAME_OPTIONS_CURRENCY_IDR :印尼盾 (IDR) STR_GAME_OPTIONS_CURRENCY_MYR :马来西亚林吉特 (MYR) ############ end of currency region @@ -1005,6 +1006,8 @@ STR_GAME_OPTIONS_VIDEO_ACCELERATION :{BLACK}硬件 STR_GAME_OPTIONS_VIDEO_ACCELERATION_TOOLTIP :{BLACK}点击该复选框,让 OpenTTD 尝试使用硬件加速。修改后的设置将在游戏重启后生效 STR_GAME_OPTIONS_VIDEO_ACCELERATION_RESTART :{WHITE}修改后的设置将在游戏重启后生效 +STR_GAME_OPTIONS_VIDEO_VSYNC :{BLACK}垂直同步 +STR_GAME_OPTIONS_VIDEO_VSYNC_TOOLTIP :{BLACK}点击该复选框开启垂直同步(V-sync)。修改后的设置将在游戏重启后生效。仅开启硬件加速时有效 STR_GAME_OPTIONS_GUI_ZOOM_FRAME :{BLACK}界面大小 STR_GAME_OPTIONS_GUI_ZOOM_DROPDOWN_TOOLTIP :{BLACK}选择使用的界面元素大小 @@ -1026,6 +1029,8 @@ STR_GAME_OPTIONS_GRAPHICS :{BLACK}图像 STR_GAME_OPTIONS_REFRESH_RATE :{BLACK}显示刷新率 STR_GAME_OPTIONS_REFRESH_RATE_TOOLTIP :{BLACK}选择需要的屏幕刷新率 +STR_GAME_OPTIONS_REFRESH_RATE_OTHER :其他 +STR_GAME_OPTIONS_REFRESH_RATE_ITEM :{NUM}Hz STR_GAME_OPTIONS_REFRESH_RATE_WARNING :{WHITE}高于 60Hz 的刷新率可能会影响性能。 STR_GAME_OPTIONS_BASE_GRF :{BLACK}基础图形组 @@ -1136,6 +1141,7 @@ STR_CONFIG_SETTING_TREE_CAPTION :{WHITE}设置 STR_CONFIG_SETTING_FILTER_TITLE :{BLACK}关键字过滤: STR_CONFIG_SETTING_EXPAND_ALL :{BLACK}展开全部 STR_CONFIG_SETTING_COLLAPSE_ALL :{BLACK}关闭全部 +STR_CONFIG_SETTING_RESET_ALL :{BLACK}重置所有值 STR_CONFIG_SETTING_NO_EXPLANATION_AVAILABLE_HELPTEXT :(没有可用的提示) STR_CONFIG_SETTING_DEFAULT_VALUE :{LTBLUE}默认值: {ORANGE}{STRING} STR_CONFIG_SETTING_TYPE :{LTBLUE}设置类型: {ORANGE}{STRING} @@ -1144,6 +1150,8 @@ STR_CONFIG_SETTING_TYPE_GAME_MENU :当前游戏设 STR_CONFIG_SETTING_TYPE_GAME_INGAME :当前游戏设置(可保存在存档中,仅对当前游戏生效) STR_CONFIG_SETTING_TYPE_COMPANY_MENU :当前公司设置(可保存在存档中,仅对新游戏生效) STR_CONFIG_SETTING_TYPE_COMPANY_INGAME :当前公司设置(可保存在存档中,仅对现行公司生效) +STR_CONFIG_SETTING_RESET_ALL_CONFIRMATION_DIALOG_CAPTION :{WHITE}注意! +STR_CONFIG_SETTING_RESET_ALL_CONFIRMATION_DIALOG_TEXT :{WHITE}该操作将重置所有游戏设置为默认值。{}确认继续? STR_CONFIG_SETTING_RESTRICT_CATEGORY :{BLACK}筛选设定: STR_CONFIG_SETTING_RESTRICT_TYPE :{BLACK}适用范围: @@ -1206,8 +1214,10 @@ STR_CONFIG_SETTING_CITY_APPROVAL :地区政府对 STR_CONFIG_SETTING_CITY_APPROVAL_HELPTEXT :设置相关参数以决定各公司造成的噪音及环境破坏时,各城镇对该公司的评价及未来区域建设的影响。 STR_CONFIG_SETTING_MAP_HEIGHT_LIMIT :地图高度限制:{STRING} +STR_CONFIG_SETTING_MAP_HEIGHT_LIMIT_HELPTEXT :设置高原的最大高度。选择 "(自动)" 则在地形生成后自动选取合适的值 STR_CONFIG_SETTING_MAP_HEIGHT_LIMIT_VALUE :{NUM} -STR_CONFIG_SETTING_TOO_HIGH_MOUNTAIN :{WHITE}您不能把最高地面高度设为这个值,因为地图上至少有一座山丘的高度比这个值还大 +STR_CONFIG_SETTING_MAP_HEIGHT_LIMIT_AUTO :(自动) +STR_CONFIG_SETTING_TOO_HIGH_MOUNTAIN :{WHITE}您不能把最高地面高度限制设为这个值,因为地图上至少有一座山的高度大于该值 STR_CONFIG_SETTING_AUTOSLOPE :允许在建筑、轨道等下方改变地形(自动斜坡): {STRING} STR_CONFIG_SETTING_AUTOSLOPE_HELPTEXT :允许在建筑和轨道下方改变地形而不需要拆除他们 STR_CONFIG_SETTING_CATCHMENT :允许更真实的客源范围:{STRING} @@ -1352,6 +1362,7 @@ STR_CONFIG_SETTING_OIL_REF_EDGE_DISTANCE :石油工业距 STR_CONFIG_SETTING_OIL_REF_EDGE_DISTANCE_HELPTEXT :限制炼油厂和油井到地图边缘或海岛海岸的最大距离。对于大于 256 格的地图,该值将按比例放大。 STR_CONFIG_SETTING_SNOWLINE_HEIGHT :雪线高度:{STRING} STR_CONFIG_SETTING_SNOWLINE_HEIGHT_HELPTEXT :控制在寒带气候中雪线高度。大雪会影响工业和城镇发展需求。只能在场景编辑器中更改或由“积雪覆盖率”计算得到。 +STR_CONFIG_SETTING_SNOW_COVERAGE :积雪覆盖率:{STRING} STR_CONFIG_SETTING_SNOW_COVERAGE_HELPTEXT :控制寒带气候中大致的雪量。雪会影响工业和城镇发展需求。只在地图生成时使用。海平面以上一格的土地永远没有积雪 STR_CONFIG_SETTING_SNOW_COVERAGE_VALUE :{NUM}% STR_CONFIG_SETTING_DESERT_COVERAGE :沙漠覆盖率:{STRING} @@ -1471,9 +1482,11 @@ STR_CONFIG_SETTING_PERSISTENT_BUILDINGTOOLS_HELPTEXT :“打开”时 STR_CONFIG_SETTING_EXPENSES_LAYOUT :企业财政窗口中的组群支出:{STRING} STR_CONFIG_SETTING_EXPENSES_LAYOUT_HELPTEXT :“打开”时公司财务报表将分组显示 STR_CONFIG_SETTING_AUTO_REMOVE_SIGNALS :建造铁路时自动移除信号灯:{STRING} +STR_CONFIG_SETTING_AUTO_REMOVE_SIGNALS_HELPTEXT :建造铁路时自动移除路过的信号灯。注意,这可能导致火车事故。 STR_CONFIG_SETTING_FAST_FORWARD_SPEED_LIMIT :快进速度上限:{STRING} +STR_CONFIG_SETTING_FAST_FORWARD_SPEED_LIMIT_HELPTEXT :限制快进时的最大速度。0 = 无限制(在您计算机允许的范围内)。低于 100% 的值将使游戏变慢。上限取决于您计算机的配置,并随着游戏情况浮动。 STR_CONFIG_SETTING_FAST_FORWARD_SPEED_LIMIT_VAL :{NUM}% 正常游戏速度 -STR_CONFIG_SETTING_FAST_FORWARD_SPEED_LIMIT_ZERO :无限制(在您的计算机能允许的范围内) +STR_CONFIG_SETTING_FAST_FORWARD_SPEED_LIMIT_ZERO :无限制(在您计算机允许的范围内) STR_CONFIG_SETTING_SOUND_TICKER :产业新闻: {STRING} STR_CONFIG_SETTING_SOUND_TICKER_HELPTEXT :每月初产业新闻音效 @@ -1643,7 +1656,10 @@ STR_CONFIG_SETTING_TOWN_CARGOGENMODE_BITCOUNT :线性 STR_CONFIG_SETTING_EXTRA_TREE_PLACEMENT :树木自动生长: {STRING} STR_CONFIG_SETTING_EXTRA_TREE_PLACEMENT_HELPTEXT :控制游戏中数目的随机生长,这将影响依赖树木的工业,比如木材厂 +STR_CONFIG_SETTING_EXTRA_TREE_PLACEMENT_NO_SPREAD :生长但不扩散 {RED}(损坏伐木场) STR_CONFIG_SETTING_EXTRA_TREE_PLACEMENT_SPREAD_RAINFOREST :只生长在雨林 +STR_CONFIG_SETTING_EXTRA_TREE_PLACEMENT_SPREAD_ALL :生长并四处扩散 +STR_CONFIG_SETTING_EXTRA_TREE_PLACEMENT_NO_GROWTH_NO_SPREAD :不生长,不扩散 {RED}(损坏伐木场) STR_CONFIG_SETTING_TOOLBAR_POS :主工具栏位置:{STRING} STR_CONFIG_SETTING_TOOLBAR_POS_HELPTEXT :主工具栏在屏幕上方的位置 @@ -1661,6 +1677,8 @@ STR_CONFIG_SETTING_ZOOM_MIN :最大放大倍 STR_CONFIG_SETTING_ZOOM_MIN_HELPTEXT :画面的最大放大倍数,注意:提高放大倍数增加内存需求 STR_CONFIG_SETTING_ZOOM_MAX :最大视角缩小倍数: {STRING} STR_CONFIG_SETTING_ZOOM_MAX_HELPTEXT :画面的最大缩小倍数,过大的缩放级别在使用时会引起延迟 +STR_CONFIG_SETTING_SPRITE_ZOOM_MIN :贴图的最高分辨率:{STRING} +STR_CONFIG_SETTING_SPRITE_ZOOM_MIN_HELPTEXT :限制贴图的最高分辨率。限制贴图分辨率将避免使用高分辨率贴图,即使它们可用。有助于在混用有/无高分辨率图形的 GRF 文件时保持游戏外观的统一。 STR_CONFIG_SETTING_ZOOM_LVL_MIN :4倍 STR_CONFIG_SETTING_ZOOM_LVL_IN_2X :2倍 STR_CONFIG_SETTING_ZOOM_LVL_NORMAL :普通 @@ -1713,6 +1731,7 @@ STR_CONFIG_SETTING_LOCALISATION_UNITS_VELOCITY_HELPTEXT :在界面上以 STR_CONFIG_SETTING_LOCALISATION_UNITS_VELOCITY_IMPERIAL :英制(英里/小时) STR_CONFIG_SETTING_LOCALISATION_UNITS_VELOCITY_METRIC :公制(千米/小时) STR_CONFIG_SETTING_LOCALISATION_UNITS_VELOCITY_SI :国际单位制(米/秒) +STR_CONFIG_SETTING_LOCALISATION_UNITS_VELOCITY_GAMEUNITS :游戏单位 (格/日) STR_CONFIG_SETTING_LOCALISATION_UNITS_POWER :运输工具功率单位:{STRING} STR_CONFIG_SETTING_LOCALISATION_UNITS_POWER_HELPTEXT :在界面上以所选择的单位表示运输工具的功率 @@ -2518,7 +2537,7 @@ STR_WATERWAYS_TOOLBAR_BUILD_DOCK_TOOLTIP :{BLACK}建造 STR_WATERWAYS_TOOLBAR_BUOY_TOOLTIP :{BLACK}放置一个浮标,该浮标可以用作路点,按住 Shift 键操作可以显示所需资金 STR_WATERWAYS_TOOLBAR_BUILD_AQUEDUCT_TOOLTIP :{BLACK}建设水渠,按住 Shift 键操作可以显示所需资金 STR_WATERWAYS_TOOLBAR_CREATE_LAKE_TOOLTIP :{BLACK}修建运河{}当地形处于海平面时{}按住 Ctrl键 可以定义水域 -STR_WATERWAYS_TOOLBAR_CREATE_RIVER_TOOLTIP :{BLACK}放置河流 +STR_WATERWAYS_TOOLBAR_CREATE_RIVER_TOOLTIP :{BLACK}放置河流。按住Ctrl键选择对角线区域 # Ship depot construction window STR_DEPOT_BUILD_SHIP_CAPTION :{WHITE}船坞方向…… @@ -2578,8 +2597,12 @@ STR_TREES_RANDOM_TYPE :{BLACK}随机 STR_TREES_RANDOM_TYPE_TOOLTIP :{BLACK}种植随机类型的树木,按住 Shift 键可以显示所需资金 STR_TREES_RANDOM_TREES_BUTTON :{BLACK}随机树木 STR_TREES_RANDOM_TREES_TOOLTIP :{BLACK}随机地种植一些树木 +STR_TREES_MODE_NORMAL_BUTTON :{BLACK}正常 +STR_TREES_MODE_NORMAL_TOOLTIP :{BLACK}在地面上拖动以种植单个树木 STR_TREES_MODE_FOREST_SM_BUTTON :{BLACK}树丛 +STR_TREES_MODE_FOREST_SM_TOOLTIP :{BLACK}在地面上拖动以种植一些树木 STR_TREES_MODE_FOREST_LG_BUTTON :{BLACK}森林 +STR_TREES_MODE_FOREST_LG_TOOLTIP :{BLACK}在地面上拖动以种植大量树木 # Land generation window (SE) STR_TERRAFORM_TOOLBAR_LAND_GENERATION_CAPTION :{WHITE}生成土地 @@ -2630,7 +2653,7 @@ STR_FOUND_TOWN_SELECT_LAYOUT_RANDOM :{BLACK}随机 # Fund new industry window STR_FUND_INDUSTRY_CAPTION :{WHITE}建设新的工业设施 STR_FUND_INDUSTRY_SELECTION_TOOLTIP :{BLACK}请选择工业设施 -STR_FUND_INDUSTRY_MANY_RANDOM_INDUSTRIES :大量随机工业 +STR_FUND_INDUSTRY_MANY_RANDOM_INDUSTRIES :{BLACK}生成随机工业 STR_FUND_INDUSTRY_MANY_RANDOM_INDUSTRIES_TOOLTIP :{BLACK}在地图上创建大量随机的工业设施 STR_FUND_INDUSTRY_MANY_RANDOM_INDUSTRIES_CAPTION :{WHITE}生成随机工业 STR_FUND_INDUSTRY_MANY_RANDOM_INDUSTRIES_QUERY :{YELLOW}你确定要生成大量随机工业吗? @@ -2662,6 +2685,7 @@ STR_INDUSTRY_CARGOES_SELECT_INDUSTRY_TOOLTIP :{BLACK}选择 # Land area window STR_LAND_AREA_INFORMATION_CAPTION :{WHITE}地块信息 +STR_LAND_AREA_INFORMATION_LOCATION_TOOLTIP :{BLACK}将屏幕中心移动到地块所在的位置。单击的同时按住Ctrl会在新视点中显示地块位置 STR_LAND_AREA_INFORMATION_COST_TO_CLEAR_N_A :{BLACK}清除费用:{LTBLUE}N/A 不能清除 STR_LAND_AREA_INFORMATION_COST_TO_CLEAR :{BLACK}清除费用:{RED}{CURRENCY_LONG} STR_LAND_AREA_INFORMATION_REVENUE_WHEN_CLEARED :{BLACK}清除收入: {LTBLUE}{CURRENCY_LONG} @@ -2886,8 +2910,11 @@ STR_MAPGEN_DATE :{BLACK}日期: STR_MAPGEN_NUMBER_OF_INDUSTRIES :{BLACK}工业数量: STR_MAPGEN_HEIGHTMAP_HEIGHT :{BLACK}最高峰: STR_MAPGEN_HEIGHTMAP_HEIGHT_UP :{BLACK}提高最高峰的最大高度一格 +STR_MAPGEN_HEIGHTMAP_HEIGHT_DOWN :{BLACK}降低最高峰的最大高度一格 +STR_MAPGEN_SNOW_COVERAGE :{BLACK}积雪覆盖率: STR_MAPGEN_SNOW_COVERAGE_UP :{BLACK}增加 10% 积雪覆盖率 STR_MAPGEN_SNOW_COVERAGE_DOWN :{BLACK}减少 10% 积雪覆盖率 +STR_MAPGEN_SNOW_COVERAGE_TEXT :{BLACK}{NUM}% STR_MAPGEN_DESERT_COVERAGE :{BLACK}沙漠覆盖率: STR_MAPGEN_DESERT_COVERAGE_UP :{BLACK}增加 10% 沙漠覆盖率 STR_MAPGEN_DESERT_COVERAGE_DOWN :{BLACK}减少 10% 沙漠覆盖率 @@ -2918,6 +2945,7 @@ STR_MAPGEN_HEIGHTMAP_SIZE_LABEL :{BLACK}地图 STR_MAPGEN_HEIGHTMAP_SIZE :{ORANGE}{NUM} × {NUM} STR_MAPGEN_TERRAIN_TYPE_QUERY_CAPT :{WHITE}最高峰目标高度 +STR_MAPGEN_HEIGHTMAP_HEIGHT_QUERY_CAPT :{WHITE}最高峰 STR_MAPGEN_SNOW_COVERAGE_QUERY_CAPT :{WHITE}积雪覆盖率 (百分比) STR_MAPGEN_DESERT_COVERAGE_QUERY_CAPT :{WHITE}沙漠覆盖率 (百分比) STR_MAPGEN_START_DATE_QUERY_CAPT :{WHITE}改变游戏开始的日期 @@ -3136,6 +3164,7 @@ STR_SIGN_LIST_MATCH_CASE_TOOLTIP :{BLACK}显示 # Sign window STR_EDIT_SIGN_CAPTION :{WHITE}标记标志文字 +STR_EDIT_SIGN_LOCATION_TOOLTIP :{BLACK}将屏幕中心移动到标志的位置. 单击的同时按住Ctrl会在新视点中显示标志位置 STR_EDIT_SIGN_NEXT_SIGN_TOOLTIP :{BLACK}前往下个标记 STR_EDIT_SIGN_PREVIOUS_SIGN_TOOLTIP :{BLACK}前往上个标记 @@ -3196,13 +3225,13 @@ STR_LOCAL_AUTHORITY_ACTION_NEW_BUILDINGS :资助新房屋 STR_LOCAL_AUTHORITY_ACTION_EXCLUSIVE_TRANSPORT :购买运输专营权 STR_LOCAL_AUTHORITY_ACTION_BRIBE :贿赂地方政府 -STR_LOCAL_AUTHORITY_ACTION_TOOLTIP_SMALL_ADVERTISING :{YELLOW}进行小型的广告宣传,以吸引更多的旅客和货物选择贵公司的服务.{}费用:{CURRENCY_LONG} -STR_LOCAL_AUTHORITY_ACTION_TOOLTIP_MEDIUM_ADVERTISING :{YELLOW}进行中型的广告宣传,以吸引更多的旅客和货物选择贵公司的服务.{}费用:{CURRENCY_LONG} -STR_LOCAL_AUTHORITY_ACTION_TOOLTIP_LARGE_ADVERTISING :{YELLOW}进行大型的广告宣传,以吸引更多的旅客和货物选择贵公司的服务.{}费用:{CURRENCY_LONG} -STR_LOCAL_AUTHORITY_ACTION_TOOLTIP_ROAD_RECONSTRUCTION :{YELLOW}资助市政道路进行重建,将造成市内交通阻断 6 个月。{}费用:{CURRENCY_LONG} -STR_LOCAL_AUTHORITY_ACTION_TOOLTIP_STATUE_OF_COMPANY :{YELLOW}以公司的名义设立一尊塑像。{}费用:{CURRENCY_LONG} -STR_LOCAL_AUTHORITY_ACTION_TOOLTIP_NEW_BUILDINGS :{YELLOW}资助市内建设新的商业设施。{}费用:{CURRENCY_LONG} -STR_LOCAL_AUTHORITY_ACTION_TOOLTIP_EXCLUSIVE_TRANSPORT :{YELLOW}购买该市一年的运输专营权, 其间该市的乘客及货物只允许选用贵公司的运输服务。{}费用:{CURRENCY_LONG} +STR_LOCAL_AUTHORITY_ACTION_TOOLTIP_SMALL_ADVERTISING :{YELLOW}进行小型的广告宣传,以吸引更多的旅客和货物选择贵公司的服务。{}为位于该城镇中心较近距离内的车站提供暂时的评分增益。{}费用:{CURRENCY_LONG} +STR_LOCAL_AUTHORITY_ACTION_TOOLTIP_MEDIUM_ADVERTISING :{YELLOW}进行中型的广告宣传,以吸引更多的旅客和货物选择贵公司的服务。{}为位于该城镇中心中等距离内的车站提供暂时的评分增益。{}费用:{CURRENCY_LONG} +STR_LOCAL_AUTHORITY_ACTION_TOOLTIP_LARGE_ADVERTISING :{YELLOW}进行大型的广告宣传,以吸引更多的旅客和货物选择贵公司的服务。{}为位于该城镇中心较远距离内的车站提供暂时的评分增益。{}费用:{CURRENCY_LONG} +STR_LOCAL_AUTHORITY_ACTION_TOOLTIP_ROAD_RECONSTRUCTION :{YELLOW}资助市政道路进行重建。{}将造成市内交通阻断 6 个月。{}费用:{CURRENCY_LONG} +STR_LOCAL_AUTHORITY_ACTION_TOOLTIP_STATUE_OF_COMPANY :{YELLOW}以公司的名义设立一尊塑像。{}为位于该城镇的车站提供永久的评分增益。{}费用:{CURRENCY_LONG} +STR_LOCAL_AUTHORITY_ACTION_TOOLTIP_NEW_BUILDINGS :{YELLOW}资助市内建设新的商业设施。{}为城镇提供暂时的成长速度增益。{}费用:{CURRENCY_LONG} +STR_LOCAL_AUTHORITY_ACTION_TOOLTIP_EXCLUSIVE_TRANSPORT :{YELLOW}购买该市一年的运输专营权。{}其间该市的乘客及货物只允许选用贵公司的运输服务。{}费用:{CURRENCY_LONG} STR_LOCAL_AUTHORITY_ACTION_TOOLTIP_BRIBE :{YELLOW}贿赂地方政府以提高评价,但有被发现后严厉惩罚的风险。{}费用:{CURRENCY_LONG} # Goal window @@ -3220,10 +3249,10 @@ STR_GOALS_PROGRESS_COMPLETE :{GREEN}{STRING} STR_GOALS_TOOLTIP_CLICK_ON_SERVICE_TO_CENTER :{BLACK}点击使得视图移动到该工业/城镇/地块. Ctrl+左键 在该处创建一个视点. # Goal question window -STR_GOAL_QUESTION_CAPTION_QUESTION :帮助索引 -STR_GOAL_QUESTION_CAPTION_INFORMATION :信息 -STR_GOAL_QUESTION_CAPTION_WARNING :警告 -STR_GOAL_QUESTION_CAPTION_ERROR :错误 +STR_GOAL_QUESTION_CAPTION_QUESTION :{BLACK}帮助索引 +STR_GOAL_QUESTION_CAPTION_INFORMATION :{BLACK}信息 +STR_GOAL_QUESTION_CAPTION_WARNING :{BLACK}警告 +STR_GOAL_QUESTION_CAPTION_ERROR :{YELLOW}错误 ############ Start of Goal Question button list STR_GOAL_QUESTION_BUTTON_CANCEL :取消 @@ -3432,6 +3461,7 @@ STR_COMPANY_VIEW_SELL_SHARE_TOOLTIP :{BLACK}出售 STR_COMPANY_VIEW_COMPANY_NAME_QUERY_CAPTION :公司名称 STR_COMPANY_VIEW_PRESIDENT_S_NAME_QUERY_CAPTION :总裁姓名 +STR_COMPANY_VIEW_GIVE_MONEY_QUERY_CAPTION :输入你要给予的金额 STR_BUY_COMPANY_MESSAGE :{WHITE}我们正在寻找一家愿意收购我们的公司。{}{}您愿意收购 {COMPANY} ({CURRENCY_LONG}) 吗? @@ -3831,7 +3861,9 @@ STR_VEHICLE_VIEW_AIRCRAFT_SHOW_DETAILS_TOOLTIP :{BLACK}显示 STR_VEHICLE_VIEW_TRAIN_STATUS_START_STOP_TOOLTIP :{BLACK}当前列车动作 - 点击以 停止/启动 列车 STR_VEHICLE_VIEW_ROAD_VEHICLE_STATUS_START_STOP_TOOLTIP :{BLACK}当前车辆动作 - 点击以 停止/启动 车辆 STR_VEHICLE_VIEW_SHIP_STATE_STATUS_STOP_TOOLTIP :{BLACK}当前船只动作 - 点击以 停止/启动 船只 +STR_VEHICLE_VIEW_AIRCRAFT_STATUS_START_STOP_TOOLTIP :{BLACK}当前飞机动作 - 点击以 停止/启动 飞机 +STR_VEHICLE_VIEW_ORDER_LOCATION_TOOLTIP :{BLACK}将屏幕中心移动到当前指令的目的地。单击的同时按住Ctrl会在新视点中显示指令目的地的位置 # Messages in the start stop button in the vehicle view STR_VEHICLE_STATUS_LOADING_UNLOADING :{LTBLUE}装载/卸货 @@ -4059,6 +4091,7 @@ STR_ORDER_REFIT_STOP_ORDER :(改装为{STRI STR_ORDER_STOP_ORDER :(停留) STR_ORDER_GO_TO_STATION :{STRING} {STATION} {STRING} +STR_ORDER_GO_TO_STATION_CAN_T_USE_STATION :{PUSH_COLOUR}{RED}(不能使用车站){POP_COLOUR} {STRING} {STATION} {STRING} STR_ORDER_IMPLICIT :(自动) @@ -4303,6 +4336,7 @@ STR_GAME_SAVELOAD_ERROR_TOO_NEW_SAVEGAME :该存档是新 STR_GAME_SAVELOAD_ERROR_FILE_NOT_READABLE :文件无法读取 STR_GAME_SAVELOAD_ERROR_FILE_NOT_WRITEABLE :文件无法写入 STR_GAME_SAVELOAD_ERROR_DATA_INTEGRITY_CHECK_FAILED :数据完整性检查失败 +STR_GAME_SAVELOAD_ERROR_PATCHPACK :该存档是来自修改版的游戏 STR_GAME_SAVELOAD_NOT_AVAILABLE :<不可用> STR_WARNING_LOADGAME_REMOVED_TRAMS :{WHITE}游戏已保存为无电车版。所有电车已被去除。 @@ -4330,6 +4364,7 @@ STR_WARNING_FALLBACK_SOUNDSET :{WHITE}只找 STR_WARNING_SCREENSHOT_SIZE_CAPTION :{WHITE}超大截图 STR_WARNING_SCREENSHOT_SIZE_MESSAGE :{YELLOW}屏幕截图需要 {COMMA} x {COMMA} 像素。 截图将需要一段时间。确认继续截图? +STR_MESSAGE_HEIGHTMAP_SUCCESSFULLY :{WHITE}高度图已经被成功保存为 '{STRING}'。最高峰高度是 {NUM} STR_MESSAGE_SCREENSHOT_SUCCESSFULLY :{WHITE}屏幕截图已经被成功保存为{} '{STRING}' STR_ERROR_SCREENSHOT_FAILED :{WHITE}屏幕截图失败! @@ -4510,6 +4545,8 @@ STR_ERROR_DEPOT_WRONG_DEPOT_TYPE :车库类型错 STR_ERROR_TRAIN_TOO_LONG_AFTER_REPLACEMENT :{WHITE}{VEHICLE}在更新后总长会过长 STR_ERROR_AUTOREPLACE_NOTHING_TO_DO :{WHITE}当前没有进行中的车辆更新计划. STR_ERROR_AUTOREPLACE_MONEY_LIMIT :(现金不够) +STR_ERROR_AUTOREPLACE_INCOMPATIBLE_CARGO :{WHITE}新车辆不能装载 {STRING} +STR_ERROR_AUTOREPLACE_INCOMPATIBLE_REFIT :{WHITE}新车辆无法在调度计划 {NUM} 时改装 # Rail construction errors STR_ERROR_IMPOSSIBLE_TRACK_COMBINATION :{WHITE}不可能的轨道组合 From f4bd3fff5ebfdd7c58ccaf23977b8447c740b48a Mon Sep 17 00:00:00 2001 From: Patric Stout Date: Tue, 20 Apr 2021 16:42:37 +0200 Subject: [PATCH 092/268] Remove: "map_name" from server announcements / listing The idea back in the days was nice, but it never resulted in anything useful. Most servers either read "(loaded game)" or "Random Map", neither being useful. It was meant for heightmaps, so you could find a server that was using a specific one .. but there are many things wrong with that idea. Mostly, servers tend to save and load savegames from time to time, after which the original heightmap used was lost. All in all, removing map_name all together is just better. --- src/network/core/game.h | 1 - src/network/core/udp.cpp | 4 ++-- src/network/network_admin.cpp | 2 +- src/network/network_gui.cpp | 1 - src/network/network_udp.cpp | 1 - src/openttd.cpp | 9 --------- 6 files changed, 3 insertions(+), 15 deletions(-) diff --git a/src/network/core/game.h b/src/network/core/game.h index 151ebef284..0a10cfb1f4 100644 --- a/src/network/core/game.h +++ b/src/network/core/game.h @@ -22,7 +22,6 @@ * be sent to the clients. */ struct NetworkServerGameInfo { - char map_name[NETWORK_NAME_LENGTH]; ///< Map which is played ["random" for a randomized map] byte clients_on; ///< Current count of clients on server }; diff --git a/src/network/core/udp.cpp b/src/network/core/udp.cpp index aa6d39cbba..985c8fa720 100644 --- a/src/network/core/udp.cpp +++ b/src/network/core/udp.cpp @@ -205,7 +205,7 @@ void NetworkUDPSocketHandler::SendNetworkGameInfo(Packet *p, const NetworkGameIn p->Send_uint8 (info->clients_max); p->Send_uint8 (info->clients_on); p->Send_uint8 (info->spectators_on); - p->Send_string(info->map_name); + 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); @@ -275,7 +275,7 @@ void NetworkUDPSocketHandler::ReceiveNetworkGameInfo(Packet *p, NetworkGameInfo info->game_date = p->Recv_uint16() + DAYS_TILL_ORIGINAL_BASE_YEAR; info->start_date = p->Recv_uint16() + DAYS_TILL_ORIGINAL_BASE_YEAR; } - p->Recv_string(info->map_name, sizeof(info->map_name)); + 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 (); diff --git a/src/network/network_admin.cpp b/src/network/network_admin.cpp index aa2859ef88..825acfed10 100644 --- a/src/network/network_admin.cpp +++ b/src/network/network_admin.cpp @@ -170,7 +170,7 @@ NetworkRecvStatus ServerNetworkAdminSocketHandler::SendWelcome() p->Send_string(GetNetworkRevisionString()); p->Send_bool (_network_dedicated); - p->Send_string(_network_game_info.map_name); + p->Send_string(""); // Used to be map-name. p->Send_uint32(_settings_game.game_creation.generation_seed); p->Send_uint8 (_settings_game.game_creation.landscape); p->Send_uint32(ConvertYMDToDate(_settings_game.game_creation.starting_year, 0, 1)); diff --git a/src/network/network_gui.cpp b/src/network/network_gui.cpp index 33639584a7..591a45ae96 100644 --- a/src/network/network_gui.cpp +++ b/src/network/network_gui.cpp @@ -628,7 +628,6 @@ public: DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, r.top + 6, STR_NETWORK_SERVER_LIST_GAME_INFO, TC_FROMSTRING, SA_HOR_CENTER); DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, r.top + 6 + 4 + FONT_HEIGHT_NORMAL, sel->info.server_name, TC_ORANGE, SA_HOR_CENTER); // game name - DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, r.top + 6 + 8 + 2 * FONT_HEIGHT_NORMAL, sel->info.map_name, TC_BLACK, SA_HOR_CENTER); // map name uint16 y = r.top + detail_height + 4; diff --git a/src/network/network_udp.cpp b/src/network/network_udp.cpp index 6d7e6ca698..5b150c45a9 100644 --- a/src/network/network_udp.cpp +++ b/src/network/network_udp.cpp @@ -190,7 +190,6 @@ void ServerNetworkUDPSocketHandler::Receive_CLIENT_FIND_SERVER(Packet *p, Networ ngi.dedicated = _network_dedicated; ngi.grfconfig = _grfconfig; - strecpy(ngi.map_name, _network_game_info.map_name, lastof(ngi.map_name)); strecpy(ngi.server_name, _settings_client.network.server_name, lastof(ngi.server_name)); strecpy(ngi.server_revision, GetNetworkRevisionString(), lastof(ngi.server_revision)); diff --git a/src/openttd.cpp b/src/openttd.cpp index 4d820d4db2..ff6867c4b5 100644 --- a/src/openttd.cpp +++ b/src/openttd.cpp @@ -1048,9 +1048,6 @@ void SwitchToMode(SwitchMode new_mode) case SM_RESTARTGAME: // Restart --> 'Random game' with current settings case SM_NEWGAME: // New Game --> 'Random game' - if (_network_server) { - seprintf(_network_game_info.map_name, lastof(_network_game_info.map_name), "Random Map"); - } MakeNewGame(false, new_mode == SM_NEWGAME); break; @@ -1073,18 +1070,12 @@ void SwitchToMode(SwitchMode new_mode) IConsoleCmdExec("exec scripts/game_start.scr 0"); /* Decrease pause counter (was increased from opening load dialog) */ DoCommandP(0, PM_PAUSED_SAVELOAD, 0, CMD_PAUSE); - if (_network_server) { - seprintf(_network_game_info.map_name, lastof(_network_game_info.map_name), "%s (Loaded game)", _file_to_saveload.title); - } } break; } case SM_RESTART_HEIGHTMAP: // Load a heightmap and start a new game from it with current settings case SM_START_HEIGHTMAP: // Load a heightmap and start a new game from it - if (_network_server) { - seprintf(_network_game_info.map_name, lastof(_network_game_info.map_name), "%s (Heightmap)", _file_to_saveload.title); - } MakeNewGame(true, new_mode == SM_START_HEIGHTMAP); break; From 05612d60ae0af94b9313d5a8b78ebf58a3eeab66 Mon Sep 17 00:00:00 2001 From: Patric Stout Date: Tue, 20 Apr 2021 16:51:15 +0200 Subject: [PATCH 093/268] Remove: "language" field from server/client The original idea was that people could find a server they could talk in their native language on. This isn't really used in that way. There are several reasons for removing this: - the client also sends his "language" to the server, but nothing is doing anything with this. - flags are a bad way to represent languages, and over the years we had several (rightfully) complaints about this. - most servers have their language set to "All", and prefix the servername with the language it is about. This is a much more efficient way to do the same. All in all, this feature should go back to the drawing board. Maybe it could work in another form, but this form is not it. --- src/lang/english.txt | 40 ------------------------------- src/network/core/config.h | 2 -- src/network/core/game.h | 1 - src/network/core/udp.cpp | 7 +++--- src/network/network_admin.cpp | 2 +- src/network/network_base.h | 1 - src/network/network_client.cpp | 2 +- src/network/network_gui.cpp | 44 ---------------------------------- src/network/network_internal.h | 41 ------------------------------- src/network/network_server.cpp | 3 --- src/network/network_udp.cpp | 1 - src/settings_type.h | 1 - src/strings.cpp | 3 --- src/table/gameopt_settings.ini | 1 - src/table/settings.ini | 10 -------- src/widgets/network_widget.h | 3 --- 16 files changed, 5 insertions(+), 157 deletions(-) diff --git a/src/lang/english.txt b/src/lang/english.txt index 9bccd86df2..125273ed94 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -2070,46 +2070,6 @@ STR_NETWORK_START_SERVER_LANGUAGE_TOOLTIP :{BLACK}Other pl STR_NETWORK_START_SERVER_NEW_GAME_NAME_OSKTITLE :{BLACK}Enter a name for the network game -# Network game languages -############ Leave those lines in this order!! -STR_NETWORK_LANG_ANY :Any -STR_NETWORK_LANG_ENGLISH :English -STR_NETWORK_LANG_GERMAN :German -STR_NETWORK_LANG_FRENCH :French -STR_NETWORK_LANG_BRAZILIAN :Brazilian -STR_NETWORK_LANG_BULGARIAN :Bulgarian -STR_NETWORK_LANG_CHINESE :Chinese -STR_NETWORK_LANG_CZECH :Czech -STR_NETWORK_LANG_DANISH :Danish -STR_NETWORK_LANG_DUTCH :Dutch -STR_NETWORK_LANG_ESPERANTO :Esperanto -STR_NETWORK_LANG_FINNISH :Finnish -STR_NETWORK_LANG_HUNGARIAN :Hungarian -STR_NETWORK_LANG_ICELANDIC :Icelandic -STR_NETWORK_LANG_ITALIAN :Italian -STR_NETWORK_LANG_JAPANESE :Japanese -STR_NETWORK_LANG_KOREAN :Korean -STR_NETWORK_LANG_LITHUANIAN :Lithuanian -STR_NETWORK_LANG_NORWEGIAN :Norwegian -STR_NETWORK_LANG_POLISH :Polish -STR_NETWORK_LANG_PORTUGUESE :Portuguese -STR_NETWORK_LANG_ROMANIAN :Romanian -STR_NETWORK_LANG_RUSSIAN :Russian -STR_NETWORK_LANG_SLOVAK :Slovak -STR_NETWORK_LANG_SLOVENIAN :Slovenian -STR_NETWORK_LANG_SPANISH :Spanish -STR_NETWORK_LANG_SWEDISH :Swedish -STR_NETWORK_LANG_TURKISH :Turkish -STR_NETWORK_LANG_UKRAINIAN :Ukrainian -STR_NETWORK_LANG_AFRIKAANS :Afrikaans -STR_NETWORK_LANG_CROATIAN :Croatian -STR_NETWORK_LANG_CATALAN :Catalan -STR_NETWORK_LANG_ESTONIAN :Estonian -STR_NETWORK_LANG_GALICIAN :Galician -STR_NETWORK_LANG_GREEK :Greek -STR_NETWORK_LANG_LATVIAN :Latvian -############ End of leave-in-this-order - # Network game lobby STR_NETWORK_GAME_LOBBY_CAPTION :{WHITE}Multiplayer game lobby diff --git a/src/network/core/config.h b/src/network/core/config.h index 3488b1ff28..1483419ea5 100644 --- a/src/network/core/config.h +++ b/src/network/core/config.h @@ -57,8 +57,6 @@ static const uint NETWORK_GRF_NAME_LENGTH = 80; ///< Maximum l */ static const uint NETWORK_MAX_GRF_COUNT = 62; -static const uint NETWORK_NUM_LANGUAGES = 36; ///< Number of known languages (to the network protocol) + 1 for 'any'. - /** * The number of landscapes in OpenTTD. * This number must be equal to NUM_LANDSCAPE, but as this number is used diff --git a/src/network/core/game.h b/src/network/core/game.h index 0a10cfb1f4..29eca418f0 100644 --- a/src/network/core/game.h +++ b/src/network/core/game.h @@ -42,7 +42,6 @@ struct NetworkGameInfo : NetworkServerGameInfo { 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 server_lang; ///< Language of the server (we should make a nice table for this) 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 diff --git a/src/network/core/udp.cpp b/src/network/core/udp.cpp index 985c8fa720..72fec49e1e 100644 --- a/src/network/core/udp.cpp +++ b/src/network/core/udp.cpp @@ -200,7 +200,7 @@ void NetworkUDPSocketHandler::SendNetworkGameInfo(Packet *p, const NetworkGameIn /* NETWORK_GAME_INFO_VERSION = 1 */ p->Send_string(info->server_name); p->Send_string(info->server_revision); - p->Send_uint8 (info->server_lang); + p->Send_uint8 (0); // Used to be server-lang. p->Send_bool (info->use_password); p->Send_uint8 (info->clients_max); p->Send_uint8 (info->clients_on); @@ -266,7 +266,7 @@ void NetworkUDPSocketHandler::ReceiveNetworkGameInfo(Packet *p, NetworkGameInfo case 1: p->Recv_string(info->server_name, sizeof(info->server_name)); p->Recv_string(info->server_revision, sizeof(info->server_revision)); - info->server_lang = p->Recv_uint8 (); + 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 (); @@ -281,8 +281,7 @@ void NetworkUDPSocketHandler::ReceiveNetworkGameInfo(Packet *p, NetworkGameInfo info->map_set = p->Recv_uint8 (); info->dedicated = p->Recv_bool (); - if (info->server_lang >= NETWORK_NUM_LANGUAGES) info->server_lang = 0; - if (info->map_set >= NETWORK_NUM_LANDSCAPES) info->map_set = 0; + if (info->map_set >= NETWORK_NUM_LANDSCAPES) info->map_set = 0; } } diff --git a/src/network/network_admin.cpp b/src/network/network_admin.cpp index 825acfed10..fa97b7e578 100644 --- a/src/network/network_admin.cpp +++ b/src/network/network_admin.cpp @@ -238,7 +238,7 @@ NetworkRecvStatus ServerNetworkAdminSocketHandler::SendClientInfo(const NetworkC p->Send_uint32(ci->client_id); p->Send_string(cs == nullptr ? "" : const_cast(cs->client_address).GetHostname()); p->Send_string(ci->client_name); - p->Send_uint8 (ci->client_lang); + p->Send_uint8 (0); // Used to be language p->Send_uint32(ci->join_date); p->Send_uint8 (ci->client_playas); diff --git a/src/network/network_base.h b/src/network/network_base.h index cbb7e88f2c..15f410dbc4 100644 --- a/src/network/network_base.h +++ b/src/network/network_base.h @@ -23,7 +23,6 @@ extern NetworkClientInfoPool _networkclientinfo_pool; struct NetworkClientInfo : NetworkClientInfoPool::PoolItem<&_networkclientinfo_pool> { ClientID client_id; ///< Client identifier (same as ClientState->client_id) char client_name[NETWORK_CLIENT_NAME_LENGTH]; ///< Name of the client - byte client_lang; ///< The language of the client CompanyID client_playas; ///< As which company is this client playing (CompanyID) Date join_date; ///< Gamedate the client has joined diff --git a/src/network/network_client.cpp b/src/network/network_client.cpp index cd19042691..5858859cb9 100644 --- a/src/network/network_client.cpp +++ b/src/network/network_client.cpp @@ -362,7 +362,7 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::SendJoin() p->Send_uint32(_openttd_newgrf_version); p->Send_string(_settings_client.network.client_name); // Client name p->Send_uint8 (_network_join_as); // PlayAs - p->Send_uint8 (NETLANG_ANY); // Language + p->Send_uint8 (0); // Used to be language my_client->SendPacket(p); return NETWORK_RECV_STATUS_OKAY; } diff --git a/src/network/network_gui.cpp b/src/network/network_gui.cpp index 591a45ae96..78b0d9dfc3 100644 --- a/src/network/network_gui.cpp +++ b/src/network/network_gui.cpp @@ -56,20 +56,6 @@ static const StringID _connection_types_dropdown[] = { INVALID_STRING_ID }; -static std::vector _language_dropdown; - -void SortNetworkLanguages() -{ - /* Init the strings */ - if (_language_dropdown.empty()) { - for (int i = 0; i < NETLANG_COUNT; i++) _language_dropdown.emplace_back(STR_NETWORK_LANG_ANY + i); - _language_dropdown.emplace_back(INVALID_STRING_ID); - } - - /* Sort the strings (we don't move 'any' and the 'invalid' one) */ - std::sort(_language_dropdown.begin() + 1, _language_dropdown.end() - 1, StringIDSorter); -} - /** * Update the network new window because a new server is * found on the network. @@ -430,9 +416,6 @@ protected: /* draw red or green icon, depending on compatibility with server */ DrawSprite(SPR_BLOT, (cur_item->info.compatible ? PALETTE_TO_GREEN : (cur_item->info.version_compatible ? PALETTE_TO_YELLOW : PALETTE_TO_RED)), nwi_info->pos_x + this->blot_offset, y + icon_y_offset + 1); - - /* draw flag according to server language */ - DrawSprite(SPR_FLAGS_BASE + cur_item->info.server_lang, PAL_NONE, nwi_info->pos_x + this->flag_offset, y + (this->resize.step_height - GetSpriteSize(SPR_FLAGS_BASE + cur_item->info.server_lang).height) / 2); } } @@ -638,10 +621,6 @@ 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_NETWORK_LANG_ANY + sel->info.server_lang); - DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, STR_NETWORK_SERVER_LIST_LANGUAGE); // server language - y += FONT_HEIGHT_NORMAL; - SetDParam(0, STR_CHEAT_SWITCH_CLIMATE_TEMPERATE_LANDSCAPE + sel->info.map_set); DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, STR_NETWORK_SERVER_LIST_LANDSCAPE); // landscape y += FONT_HEIGHT_NORMAL; @@ -1054,10 +1033,6 @@ struct NetworkStartServerWindow : public Window { case WID_NSS_SPECTATORS_TXT: SetDParam(0, _settings_client.network.max_spectators); break; - - case WID_NSS_LANGUAGE_BTN: - SetDParam(0, STR_NETWORK_LANG_ANY + _settings_client.network.server_lang); - break; } } @@ -1139,18 +1114,6 @@ struct NetworkStartServerWindow : public Window { ShowQueryString(STR_JUST_INT, STR_NETWORK_START_SERVER_NUMBER_OF_SPECTATORS, 4, this, CS_NUMERAL, QSF_NONE); break; - case WID_NSS_LANGUAGE_BTN: { // Language - uint sel = 0; - for (uint i = 0; i < _language_dropdown.size() - 1; i++) { - if (_language_dropdown[i] == STR_NETWORK_LANG_ANY + _settings_client.network.server_lang) { - sel = i; - break; - } - } - ShowDropDownMenu(this, _language_dropdown.data(), sel, WID_NSS_LANGUAGE_BTN, 0, 0); - break; - } - case WID_NSS_GENERATE_GAME: // Start game _is_network_server = true; if (_ctrl_pressed) { @@ -1183,9 +1146,6 @@ struct NetworkStartServerWindow : public Window { case WID_NSS_CONNTYPE_BTN: _settings_client.network.server_advertise = (index != 0); break; - case WID_NSS_LANGUAGE_BTN: - _settings_client.network.server_lang = _language_dropdown[index] - STR_NETWORK_LANG_ANY; - break; default: NOT_REACHED(); } @@ -1252,10 +1212,6 @@ static const NWidgetPart _nested_network_start_server_window_widgets[] = { NWidget(WWT_TEXT, COLOUR_LIGHT_BLUE, WID_NSS_CONNTYPE_LABEL), SetFill(1, 0), SetDataTip(STR_NETWORK_START_SERVER_ADVERTISED_LABEL, STR_NULL), NWidget(WWT_DROPDOWN, COLOUR_LIGHT_BLUE, WID_NSS_CONNTYPE_BTN), SetFill(1, 0), SetDataTip(STR_BLACK_STRING, STR_NETWORK_START_SERVER_ADVERTISED_TOOLTIP), EndContainer(), - NWidget(NWID_VERTICAL), SetPIP(0, 1, 0), - NWidget(WWT_TEXT, COLOUR_LIGHT_BLUE, WID_NSS_LANGUAGE_LABEL), SetFill(1, 0), SetDataTip(STR_NETWORK_START_SERVER_LANGUAGE_SPOKEN, STR_NULL), - NWidget(WWT_DROPDOWN, COLOUR_LIGHT_BLUE, WID_NSS_LANGUAGE_BTN), SetFill(1, 0), SetDataTip(STR_BLACK_STRING, STR_NETWORK_START_SERVER_LANGUAGE_TOOLTIP), - EndContainer(), NWidget(NWID_VERTICAL), SetPIP(0, 1, 0), NWidget(NWID_SPACER), SetFill(1, 1), NWidget(WWT_PUSHTXTBTN, COLOUR_WHITE, WID_NSS_SETPWD), SetFill(1, 0), SetDataTip(STR_NETWORK_START_SERVER_SET_PASSWORD, STR_NETWORK_START_SERVER_PASSWORD_TOOLTIP), diff --git a/src/network/network_internal.h b/src/network/network_internal.h index 290922f385..352416320d 100644 --- a/src/network/network_internal.h +++ b/src/network/network_internal.h @@ -62,47 +62,6 @@ enum NetworkJoinStatus { NETWORK_JOIN_STATUS_END, }; -/** Language ids for server_lang and client_lang. Do NOT modify the order. */ -enum NetworkLanguage { - NETLANG_ANY = 0, - NETLANG_ENGLISH, - NETLANG_GERMAN, - NETLANG_FRENCH, - NETLANG_BRAZILIAN, - NETLANG_BULGARIAN, - NETLANG_CHINESE, - NETLANG_CZECH, - NETLANG_DANISH, - NETLANG_DUTCH, - NETLANG_ESPERANTO, - NETLANG_FINNISH, - NETLANG_HUNGARIAN, - NETLANG_ICELANDIC, - NETLANG_ITALIAN, - NETLANG_JAPANESE, - NETLANG_KOREAN, - NETLANG_LITHUANIAN, - NETLANG_NORWEGIAN, - NETLANG_POLISH, - NETLANG_PORTUGUESE, - NETLANG_ROMANIAN, - NETLANG_RUSSIAN, - NETLANG_SLOVAK, - NETLANG_SLOVENIAN, - NETLANG_SPANISH, - NETLANG_SWEDISH, - NETLANG_TURKISH, - NETLANG_UKRAINIAN, - NETLANG_AFRIKAANS, - NETLANG_CROATIAN, - NETLANG_CATALAN, - NETLANG_ESTONIAN, - NETLANG_GALICIAN, - NETLANG_GREEK, - NETLANG_LATVIAN, - NETLANG_COUNT -}; - extern uint32 _frame_counter_server; // The frame_counter of the server, if in network-mode extern uint32 _frame_counter_max; // To where we may go with our clients extern uint32 _frame_counter; diff --git a/src/network/network_server.cpp b/src/network/network_server.cpp index 86885d5980..5521710271 100644 --- a/src/network/network_server.cpp +++ b/src/network/network_server.cpp @@ -910,7 +910,6 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::Receive_CLIENT_JOIN(Packet *p) char name[NETWORK_CLIENT_NAME_LENGTH]; CompanyID playas; - NetworkLanguage client_lang; char client_revision[NETWORK_REVISION_LENGTH]; p->Recv_string(client_revision, sizeof(client_revision)); @@ -924,7 +923,6 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::Receive_CLIENT_JOIN(Packet *p) p->Recv_string(name, sizeof(name)); playas = (Owner)p->Recv_uint8(); - client_lang = (NetworkLanguage)p->Recv_uint8(); if (this->HasClientQuit()) return NETWORK_RECV_STATUS_CONN_LOST; @@ -961,7 +959,6 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::Receive_CLIENT_JOIN(Packet *p) ci->join_date = _date; strecpy(ci->client_name, name, lastof(ci->client_name)); ci->client_playas = playas; - ci->client_lang = client_lang; DEBUG(desync, 1, "client: %08x; %02x; %02x; %02x", _date, _date_fract, (int)ci->client_playas, (int)ci->index); /* Make sure companies to which people try to join are not autocleaned */ diff --git a/src/network/network_udp.cpp b/src/network/network_udp.cpp index 5b150c45a9..46a21fc87d 100644 --- a/src/network/network_udp.cpp +++ b/src/network/network_udp.cpp @@ -176,7 +176,6 @@ void ServerNetworkUDPSocketHandler::Receive_CLIENT_FIND_SERVER(Packet *p, Networ ngi.clients_on = _network_game_info.clients_on; ngi.start_date = ConvertYMDToDate(_settings_game.game_creation.starting_year, 0, 1); - ngi.server_lang = _settings_client.network.server_lang; ngi.use_password = !StrEmpty(_settings_client.network.server_password); ngi.clients_max = _settings_client.network.max_clients; ngi.companies_on = (byte)Company::GetNumItems(); diff --git a/src/settings_type.h b/src/settings_type.h index 42bdc27d4a..bb078205b8 100644 --- a/src/settings_type.h +++ b/src/settings_type.h @@ -280,7 +280,6 @@ struct NetworkSettings { uint8 max_spectators; ///< maximum amount of spectators Year restart_game_year; ///< year the server restarts uint8 min_active_clients; ///< minimum amount of active clients to unpause the game - uint8 server_lang; ///< language of the server bool reload_cfg; ///< reload the config file before restarting char last_host[NETWORK_HOSTNAME_LENGTH]; ///< IP address of the last joined server uint16 last_port; ///< port of the last joined server diff --git a/src/strings.cpp b/src/strings.cpp index 33724158c7..aeea7c071a 100644 --- a/src/strings.cpp +++ b/src/strings.cpp @@ -1695,8 +1695,6 @@ static char *GetSpecialNameString(char *buff, int ind, StringParameters *args, c NOT_REACHED(); } -extern void SortNetworkLanguages(); - /** * Check whether the header is a valid header for OpenTTD. * @return true iff the header is deemed valid. @@ -1813,7 +1811,6 @@ bool ReadLanguagePack(const LanguageMetadata *lang) InitializeSortedCargoSpecs(); SortIndustryTypes(); BuildIndustriesLegend(); - SortNetworkLanguages(); BuildContentTypeStringList(); InvalidateWindowClassesData(WC_BUILD_VEHICLE); // Build vehicle window. InvalidateWindowClassesData(WC_TRAINS_LIST); // Train group window. diff --git a/src/table/gameopt_settings.ini b/src/table/gameopt_settings.ini index c4140f4282..b0b1798ab9 100644 --- a/src/table/gameopt_settings.ini +++ b/src/table/gameopt_settings.ini @@ -19,7 +19,6 @@ static const char *_climates = "temperate|arctic|tropic|toyland"; static const char *_autosave_interval = "off|monthly|quarterly|half year|yearly"; static const char *_roadsides = "left|right"; static const char *_savegame_date = "long|short|iso"; -static const char *_server_langs = "ANY|ENGLISH|GERMAN|FRENCH|BRAZILIAN|BULGARIAN|CHINESE|CZECH|DANISH|DUTCH|ESPERANTO|FINNISH|HUNGARIAN|ICELANDIC|ITALIAN|JAPANESE|KOREAN|LITHUANIAN|NORWEGIAN|POLISH|PORTUGUESE|ROMANIAN|RUSSIAN|SLOVAK|SLOVENIAN|SPANISH|SWEDISH|TURKISH|UKRAINIAN|AFRIKAANS|CROATIAN|CATALAN|ESTONIAN|GALICIAN|GREEK|LATVIAN"; static const char *_osk_activation = "disabled|double|single|immediately"; static const char *_settings_profiles = "easy|medium|hard"; static const char *_news_display = "off|summarized|full"; diff --git a/src/table/settings.ini b/src/table/settings.ini index efbb3e0c78..391a5b427f 100644 --- a/src/table/settings.ini +++ b/src/table/settings.ini @@ -4061,16 +4061,6 @@ def = 0 min = 0 max = MAX_CLIENTS -[SDTC_OMANY] -var = network.server_lang -type = SLE_UINT8 -flags = SLF_NOT_IN_SAVE | SLF_NO_NETWORK_SYNC -guiflags = SGF_NETWORK_ONLY -def = 0 -max = 35 -full = _server_langs -cat = SC_BASIC - [SDTC_BOOL] var = network.reload_cfg flags = SLF_NOT_IN_SAVE | SLF_NO_NETWORK_SYNC diff --git a/src/widgets/network_widget.h b/src/widgets/network_widget.h index 8372b6360d..79d33fb067 100644 --- a/src/widgets/network_widget.h +++ b/src/widgets/network_widget.h @@ -71,9 +71,6 @@ enum NetworkStartServerWidgets { WID_NSS_SPECTATORS_TXT, ///< 'Max spectators' text. WID_NSS_SPECTATORS_BTNU, ///< 'Max spectators' uparrow. - WID_NSS_LANGUAGE_LABEL, ///< Label for 'language spoken'. - WID_NSS_LANGUAGE_BTN, ///< 'Language spoken' droplist button. - WID_NSS_GENERATE_GAME, ///< New game button. WID_NSS_LOAD_GAME, ///< Load game button. WID_NSS_PLAY_SCENARIO, ///< Play scenario button. From 31897eaa7de865d41b46f7158c70f94debe1ec66 Mon Sep 17 00:00:00 2001 From: Patric Stout Date: Tue, 20 Apr 2021 16:26:07 +0200 Subject: [PATCH 094/268] Codechange: split ParseConnectionString into two functions One also looks for a company, the other doesn't. There were more uses of the latter than the first, leaving very weird code all over the place. --- src/console_cmds.cpp | 2 +- src/network/core/tcp_http.cpp | 4 +--- src/network/network.cpp | 38 ++++++++++++++++++++++++++++++----- src/network/network_func.h | 3 ++- src/openttd.cpp | 10 +++------ 5 files changed, 40 insertions(+), 17 deletions(-) diff --git a/src/console_cmds.cpp b/src/console_cmds.cpp index cebf701981..b3819115cf 100644 --- a/src/console_cmds.cpp +++ b/src/console_cmds.cpp @@ -916,7 +916,7 @@ DEF_CONSOLE_CMD(ConNetworkConnect) uint16 rport = NETWORK_DEFAULT_PORT; CompanyID join_as = COMPANY_NEW_COMPANY; - ParseConnectionString(&company, &port, ip); + ParseGameConnectionString(&company, &port, ip); IConsolePrintF(CC_DEFAULT, "Connecting to %s...", ip); if (company != nullptr) { diff --git a/src/network/core/tcp_http.cpp b/src/network/core/tcp_http.cpp index be5a2c39a7..d88ea711d3 100644 --- a/src/network/core/tcp_http.cpp +++ b/src/network/core/tcp_http.cpp @@ -203,10 +203,8 @@ int NetworkHTTPSocketHandler::HandleHeader() *url = '\0'; /* Fetch the hostname, and possible port number. */ - const char *company = nullptr; const char *port = nullptr; - ParseConnectionString(&company, &port, hname); - if (company != nullptr) return_error("[tcp/http] invalid hostname"); + ParseConnectionString(&port, hname); NetworkAddress address(hname, port == nullptr ? 80 : atoi(port)); diff --git a/src/network/network.cpp b/src/network/network.cpp index f4640191c8..e0355ce2ea 100644 --- a/src/network/network.cpp +++ b/src/network/network.cpp @@ -446,6 +446,36 @@ static void CheckPauseOnJoin() CheckPauseHelper(NetworkHasJoiningClient(), PM_PAUSED_JOIN); } +/** + * Converts a string to ip/port + * Format: IP:port + * + * connection_string will be re-terminated to separate out the hostname, port will + * be set to the port strings given by the user, inside the memory area originally + * occupied by connection_string. + */ +void ParseConnectionString(const char **port, char *connection_string) +{ + bool ipv6 = (strchr(connection_string, ':') != strrchr(connection_string, ':')); + for (char *p = connection_string; *p != '\0'; p++) { + switch (*p) { + case '[': + ipv6 = true; + break; + + case ']': + ipv6 = false; + break; + + case ':': + if (ipv6) break; + *port = p + 1; + *p = '\0'; + break; + } + } +} + /** * Converts a string to ip/port/company * Format: IP:port#company @@ -454,11 +484,10 @@ static void CheckPauseOnJoin() * be set to the company and port strings given by the user, inside the memory area originally * occupied by connection_string. */ -void ParseConnectionString(const char **company, const char **port, char *connection_string) +void ParseGameConnectionString(const char **company, const char **port, char *connection_string) { bool ipv6 = (strchr(connection_string, ':') != strrchr(connection_string, ':')); - char *p; - for (p = connection_string; *p != '\0'; p++) { + for (char *p = connection_string; *p != '\0'; p++) { switch (*p) { case '[': ipv6 = true; @@ -592,7 +621,6 @@ void NetworkAddServer(const char *b) { if (*b != '\0') { const char *port = nullptr; - const char *company = nullptr; char host[NETWORK_HOSTNAME_LENGTH]; uint16 rport; @@ -601,7 +629,7 @@ void NetworkAddServer(const char *b) strecpy(_settings_client.network.connect_to_ip, b, lastof(_settings_client.network.connect_to_ip)); rport = NETWORK_DEFAULT_PORT; - ParseConnectionString(&company, &port, host); + ParseConnectionString(&port, host); if (port != nullptr) rport = atoi(port); NetworkUDPQueryServer(NetworkAddress(host, rport), true); diff --git a/src/network/network_func.h b/src/network/network_func.h index cbb89820cf..d14dd290f0 100644 --- a/src/network/network_func.h +++ b/src/network/network_func.h @@ -43,7 +43,8 @@ void NetworkReboot(); void NetworkDisconnect(bool blocking = false, bool close_admins = true); void NetworkGameLoop(); void NetworkBackgroundLoop(); -void ParseConnectionString(const char **company, const char **port, char *connection_string); +void ParseConnectionString(const char **port, char *connection_string); +void ParseGameConnectionString(const char **company, const char **port, char *connection_string); void NetworkStartDebugLog(NetworkAddress address); void NetworkPopulateCompanyStats(NetworkCompanyStats *stats); diff --git a/src/openttd.cpp b/src/openttd.cpp index ff6867c4b5..b204ca5a2f 100644 --- a/src/openttd.cpp +++ b/src/openttd.cpp @@ -478,7 +478,7 @@ struct AfterNewGRFScan : NewGRFScanCallback { uint16 rport = NETWORK_DEFAULT_PORT; CompanyID join_as = COMPANY_NEW_COMPANY; - ParseConnectionString(&company, &port, network_conn); + ParseGameConnectionString(&company, &port, network_conn); if (company != nullptr) { join_as = (CompanyID)atoi(company); @@ -584,11 +584,8 @@ int openttd_main(int argc, char *argv[]) dedicated = true; SetDebugString("net=6"); if (mgo.opt != nullptr) { - /* Use the existing method for parsing (openttd -n). - * However, we do ignore the #company part. */ - const char *temp = nullptr; const char *port = nullptr; - ParseConnectionString(&temp, &port, mgo.opt); + ParseConnectionString(&port, mgo.opt); if (!StrEmpty(mgo.opt)) scanner->dedicated_host = mgo.opt; if (port != nullptr) scanner->dedicated_port = atoi(port); } @@ -774,13 +771,12 @@ int openttd_main(int argc, char *argv[]) NetworkStartUp(); // initialize network-core if (debuglog_conn != nullptr && _network_available) { - const char *not_used = nullptr; const char *port = nullptr; uint16 rport; rport = NETWORK_DEFAULT_DEBUGLOG_PORT; - ParseConnectionString(¬_used, &port, debuglog_conn); + ParseConnectionString(&port, debuglog_conn); if (port != nullptr) rport = atoi(port); NetworkStartDebugLog(NetworkAddress(debuglog_conn, rport)); From cd757d53cae20e2f330f0905ec7797ca1a0f8f00 Mon Sep 17 00:00:00 2001 From: Patric Stout Date: Tue, 20 Apr 2021 16:34:14 +0200 Subject: [PATCH 095/268] Codechange: remove always-empty "address" from NetworkContentSocketHandler When ever you saw this debug lines (which you never should), they showed an empty address. It is also not very useful to have, as it always points to a known server anyway. --- src/network/core/tcp_content.cpp | 6 +++--- src/network/core/tcp_content.h | 6 ++---- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/network/core/tcp_content.cpp b/src/network/core/tcp_content.cpp index 6fb3b33794..55319e430d 100644 --- a/src/network/core/tcp_content.cpp +++ b/src/network/core/tcp_content.cpp @@ -171,9 +171,9 @@ bool NetworkContentSocketHandler::HandlePacket(Packet *p) default: if (this->HasClientQuit()) { - DEBUG(net, 0, "[tcp/content] received invalid packet type %d from %s", type, this->client_addr.GetAddressAsString().c_str()); + DEBUG(net, 0, "[tcp/content] received invalid packet type %d", type); } else { - DEBUG(net, 0, "[tcp/content] received illegal packet from %s", this->client_addr.GetAddressAsString().c_str()); + DEBUG(net, 0, "[tcp/content] received illegal packet"); } return false; } @@ -224,7 +224,7 @@ bool NetworkContentSocketHandler::ReceivePackets() */ bool NetworkContentSocketHandler::ReceiveInvalidPacket(PacketContentType type) { - DEBUG(net, 0, "[tcp/content] received illegal packet type %d from %s", type, this->client_addr.GetAddressAsString().c_str()); + DEBUG(net, 0, "[tcp/content] received illegal packet type %d", type); return false; } diff --git a/src/network/core/tcp_content.h b/src/network/core/tcp_content.h index be1cc6e77e..ef8ae3a10a 100644 --- a/src/network/core/tcp_content.h +++ b/src/network/core/tcp_content.h @@ -95,7 +95,6 @@ struct ContentInfo { /** Base socket handler for all Content TCP sockets */ class NetworkContentSocketHandler : public NetworkTCPSocketHandler { protected: - NetworkAddress client_addr; ///< The address we're connected to. void Close() override; bool ReceiveInvalidPacket(PacketContentType type); @@ -193,9 +192,8 @@ public: * @param s the socket we are connected with * @param address IP etc. of the client */ - NetworkContentSocketHandler(SOCKET s = INVALID_SOCKET, const NetworkAddress &address = NetworkAddress()) : - NetworkTCPSocketHandler(s), - client_addr(address) + NetworkContentSocketHandler(SOCKET s = INVALID_SOCKET) : + NetworkTCPSocketHandler(s) { } From ce6a74427855ccadee7abfd9b444fad73b5a4578 Mon Sep 17 00:00:00 2001 From: Patric Stout Date: Tue, 20 Apr 2021 16:36:26 +0200 Subject: [PATCH 096/268] Doc: server name doesn't need to be advertised to be valid Strictly seen the comment is true, as it says 'e.g.', but it is misleading. The server name is just that: the name of the server as configured. No need to mention advertising. --- src/network/core/tcp_admin.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/network/core/tcp_admin.h b/src/network/core/tcp_admin.h index dc9753e89f..e5bcefa86f 100644 --- a/src/network/core/tcp_admin.h +++ b/src/network/core/tcp_admin.h @@ -222,7 +222,7 @@ protected: /** * Welcome a connected admin to the game: - * string Name of the Server (e.g. as advertised to master server). + * string Name of the Server. * string OpenTTD version string. * bool Server is dedicated. * string Name of the Map. From 8395c6532e3a36e67db38df945273bd17166c13f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Guilloux?= Date: Tue, 20 Apr 2021 18:46:15 +0200 Subject: [PATCH 097/268] Fix 799eb31: [CMake] Don't include regression AIs in bundles (#9068) --- cmake/InstallAndPackage.cmake | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cmake/InstallAndPackage.cmake b/cmake/InstallAndPackage.cmake index fa36518d5b..9b7d657887 100644 --- a/cmake/InstallAndPackage.cmake +++ b/cmake/InstallAndPackage.cmake @@ -30,7 +30,9 @@ install(DIRECTORY ${CMAKE_BINARY_DIR}/game ${CMAKE_SOURCE_DIR}/bin/scripts DESTINATION ${DATA_DESTINATION_DIR} - COMPONENT language_files) + COMPONENT language_files + REGEX "ai/[^\.]+$" EXCLUDE # Ignore subdirs in ai dir +) install(FILES ${CMAKE_SOURCE_DIR}/COPYING.md From fe3cd185d7a00d116e0ff06d24b3ac369b297a0e Mon Sep 17 00:00:00 2001 From: translators Date: Tue, 20 Apr 2021 17:50:52 +0000 Subject: [PATCH 098/268] Update: Translations from eints --- src/lang/afrikaans.txt | 40 ------------------------------ src/lang/arabic_egypt.txt | 40 ------------------------------ src/lang/basque.txt | 40 ------------------------------ src/lang/belarusian.txt | 40 ------------------------------ src/lang/brazilian_portuguese.txt | 40 ------------------------------ src/lang/bulgarian.txt | 40 ------------------------------ src/lang/catalan.txt | 40 ------------------------------ src/lang/croatian.txt | 40 ------------------------------ src/lang/czech.txt | 40 ------------------------------ src/lang/danish.txt | 40 ------------------------------ src/lang/dutch.txt | 40 ------------------------------ src/lang/english_AU.txt | 40 ------------------------------ src/lang/english_US.txt | 40 ------------------------------ src/lang/esperanto.txt | 40 ------------------------------ src/lang/estonian.txt | 40 ------------------------------ src/lang/faroese.txt | 40 ------------------------------ src/lang/finnish.txt | 40 ------------------------------ src/lang/french.txt | 40 ------------------------------ src/lang/gaelic.txt | 40 ------------------------------ src/lang/galician.txt | 40 ------------------------------ src/lang/german.txt | 40 ------------------------------ src/lang/greek.txt | 40 ------------------------------ src/lang/hebrew.txt | 40 ------------------------------ src/lang/hungarian.txt | 40 ------------------------------ src/lang/icelandic.txt | 40 ------------------------------ src/lang/indonesian.txt | 40 ------------------------------ src/lang/irish.txt | 40 ------------------------------ src/lang/italian.txt | 40 ------------------------------ src/lang/japanese.txt | 40 ------------------------------ src/lang/korean.txt | 40 ------------------------------ src/lang/latin.txt | 40 ------------------------------ src/lang/latvian.txt | 40 ------------------------------ src/lang/lithuanian.txt | 40 ------------------------------ src/lang/luxembourgish.txt | 40 ------------------------------ src/lang/malay.txt | 40 ------------------------------ src/lang/norwegian_bokmal.txt | 40 ------------------------------ src/lang/norwegian_nynorsk.txt | 40 ------------------------------ src/lang/polish.txt | 40 ------------------------------ src/lang/portuguese.txt | 40 ------------------------------ src/lang/romanian.txt | 40 ------------------------------ src/lang/russian.txt | 40 ------------------------------ src/lang/serbian.txt | 40 ------------------------------ src/lang/simplified_chinese.txt | 40 ------------------------------ src/lang/slovak.txt | 40 ------------------------------ src/lang/slovenian.txt | 40 ------------------------------ src/lang/spanish.txt | 40 ------------------------------ src/lang/spanish_MX.txt | 40 ------------------------------ src/lang/swedish.txt | 40 ------------------------------ src/lang/tamil.txt | 40 ------------------------------ src/lang/thai.txt | 40 ------------------------------ src/lang/traditional_chinese.txt | 40 ------------------------------ src/lang/turkish.txt | 40 ------------------------------ src/lang/ukrainian.txt | 40 ------------------------------ src/lang/unfinished/chuvash.txt | 4 --- src/lang/unfinished/frisian.txt | 40 ------------------------------ src/lang/unfinished/ido.txt | 4 --- src/lang/unfinished/macedonian.txt | 4 --- src/lang/unfinished/maltese.txt | 4 --- src/lang/unfinished/marathi.txt | 31 ----------------------- src/lang/unfinished/persian.txt | 40 ------------------------------ src/lang/unfinished/urdu.txt | 40 ------------------------------ src/lang/vietnamese.txt | 40 ------------------------------ src/lang/welsh.txt | 40 ------------------------------ 63 files changed, 2367 deletions(-) diff --git a/src/lang/afrikaans.txt b/src/lang/afrikaans.txt index a598df6299..cf3d05321e 100644 --- a/src/lang/afrikaans.txt +++ b/src/lang/afrikaans.txt @@ -1991,46 +1991,6 @@ STR_NETWORK_START_SERVER_LANGUAGE_TOOLTIP :{BLACK}Ander sp STR_NETWORK_START_SERVER_NEW_GAME_NAME_OSKTITLE :{BLACK}Tik 'n naam in vir die netwerk speeletjie -# Network game languages -############ Leave those lines in this order!! -STR_NETWORK_LANG_ANY :Enige -STR_NETWORK_LANG_ENGLISH :Engels -STR_NETWORK_LANG_GERMAN :Duits -STR_NETWORK_LANG_FRENCH :Frans -STR_NETWORK_LANG_BRAZILIAN :Brazilian -STR_NETWORK_LANG_BULGARIAN :Bulgarian -STR_NETWORK_LANG_CHINESE :Chinese -STR_NETWORK_LANG_CZECH :Czech -STR_NETWORK_LANG_DANISH :Danish -STR_NETWORK_LANG_DUTCH :Dutch -STR_NETWORK_LANG_ESPERANTO :Esperanto -STR_NETWORK_LANG_FINNISH :Finnish -STR_NETWORK_LANG_HUNGARIAN :Hungarian -STR_NETWORK_LANG_ICELANDIC :Icelandic -STR_NETWORK_LANG_ITALIAN :Italian -STR_NETWORK_LANG_JAPANESE :Japanese -STR_NETWORK_LANG_KOREAN :Korean -STR_NETWORK_LANG_LITHUANIAN :Lithuanian -STR_NETWORK_LANG_NORWEGIAN :Norwegian -STR_NETWORK_LANG_POLISH :Polish -STR_NETWORK_LANG_PORTUGUESE :Portuguese -STR_NETWORK_LANG_ROMANIAN :Romanian -STR_NETWORK_LANG_RUSSIAN :Russian -STR_NETWORK_LANG_SLOVAK :Slovak -STR_NETWORK_LANG_SLOVENIAN :Slovenian -STR_NETWORK_LANG_SPANISH :Spanish -STR_NETWORK_LANG_SWEDISH :Swedish -STR_NETWORK_LANG_TURKISH :Turkish -STR_NETWORK_LANG_UKRAINIAN :Ukrainian -STR_NETWORK_LANG_AFRIKAANS :Afrikaans -STR_NETWORK_LANG_CROATIAN :Croatian -STR_NETWORK_LANG_CATALAN :Catalan -STR_NETWORK_LANG_ESTONIAN :Estonian -STR_NETWORK_LANG_GALICIAN :Galician -STR_NETWORK_LANG_GREEK :Grieks -STR_NETWORK_LANG_LATVIAN :Latvian -############ End of leave-in-this-order - # Network game lobby STR_NETWORK_GAME_LOBBY_CAPTION :{WHITE}Multispeler spel tuiste diff --git a/src/lang/arabic_egypt.txt b/src/lang/arabic_egypt.txt index bb89534dad..61bb91a186 100644 --- a/src/lang/arabic_egypt.txt +++ b/src/lang/arabic_egypt.txt @@ -1683,46 +1683,6 @@ STR_NETWORK_START_SERVER_LANGUAGE_TOOLTIP :{BLACK}اللا STR_NETWORK_START_SERVER_NEW_GAME_NAME_OSKTITLE :{BLACK}أدخل أسم اللعبة للشبكة -# Network game languages -############ Leave those lines in this order!! -STR_NETWORK_LANG_ANY :أي -STR_NETWORK_LANG_ENGLISH :إنجليزي -STR_NETWORK_LANG_GERMAN :ألماني -STR_NETWORK_LANG_FRENCH :فرنسي -STR_NETWORK_LANG_BRAZILIAN :برازيلي -STR_NETWORK_LANG_BULGARIAN :بلغاري -STR_NETWORK_LANG_CHINESE :صيني -STR_NETWORK_LANG_CZECH :شيكي -STR_NETWORK_LANG_DANISH :دانماركي -STR_NETWORK_LANG_DUTCH :هولندي -STR_NETWORK_LANG_ESPERANTO :اسبرانتو -STR_NETWORK_LANG_FINNISH :الفنلندية -STR_NETWORK_LANG_HUNGARIAN :هنغاري -STR_NETWORK_LANG_ICELANDIC :الأيسلاندية -STR_NETWORK_LANG_ITALIAN :إيطالي -STR_NETWORK_LANG_JAPANESE :يابانية -STR_NETWORK_LANG_KOREAN :كورية -STR_NETWORK_LANG_LITHUANIAN :اللتوانية -STR_NETWORK_LANG_NORWEGIAN :نرويجية -STR_NETWORK_LANG_POLISH :البولندية -STR_NETWORK_LANG_PORTUGUESE :برتغالية -STR_NETWORK_LANG_ROMANIAN :رومانية -STR_NETWORK_LANG_RUSSIAN :روسية -STR_NETWORK_LANG_SLOVAK :السلوفاكية -STR_NETWORK_LANG_SLOVENIAN :السلوفانية -STR_NETWORK_LANG_SPANISH :أسبانية -STR_NETWORK_LANG_SWEDISH :سويدية -STR_NETWORK_LANG_TURKISH :تركية -STR_NETWORK_LANG_UKRAINIAN :الأوكرانية -STR_NETWORK_LANG_AFRIKAANS :الأفريقية -STR_NETWORK_LANG_CROATIAN :كرواتية -STR_NETWORK_LANG_CATALAN :الكاتالوينية -STR_NETWORK_LANG_ESTONIAN :الأستونية -STR_NETWORK_LANG_GALICIAN :الجاليكية -STR_NETWORK_LANG_GREEK :اليونانية -STR_NETWORK_LANG_LATVIAN :اللاتفية -############ End of leave-in-this-order - # Network game lobby STR_NETWORK_GAME_LOBBY_CAPTION :{WHITE}ردهة تعدد اللاعبين diff --git a/src/lang/basque.txt b/src/lang/basque.txt index b9f89ac069..66b4a38eff 100644 --- a/src/lang/basque.txt +++ b/src/lang/basque.txt @@ -1864,46 +1864,6 @@ STR_NETWORK_START_SERVER_LANGUAGE_TOOLTIP :{BLACK}Beste jo STR_NETWORK_START_SERVER_NEW_GAME_NAME_OSKTITLE :{BLACK}Sare joko batentzako izena sartu -# Network game languages -############ Leave those lines in this order!! -STR_NETWORK_LANG_ANY :Edozein -STR_NETWORK_LANG_ENGLISH :Ingelera -STR_NETWORK_LANG_GERMAN :Alemaniera -STR_NETWORK_LANG_FRENCH :Frantsesa -STR_NETWORK_LANG_BRAZILIAN :Brasilera -STR_NETWORK_LANG_BULGARIAN :Bulgariera -STR_NETWORK_LANG_CHINESE :Txinera -STR_NETWORK_LANG_CZECH :Txekiera -STR_NETWORK_LANG_DANISH :Danimarkiera -STR_NETWORK_LANG_DUTCH :Holandera -STR_NETWORK_LANG_ESPERANTO :Esperantoa -STR_NETWORK_LANG_FINNISH :Finlandera -STR_NETWORK_LANG_HUNGARIAN :Hungariera -STR_NETWORK_LANG_ICELANDIC :Islandiera -STR_NETWORK_LANG_ITALIAN :Italiera -STR_NETWORK_LANG_JAPANESE :Japoniera -STR_NETWORK_LANG_KOREAN :Koreera -STR_NETWORK_LANG_LITHUANIAN :Lituaniera -STR_NETWORK_LANG_NORWEGIAN :Norbegiera -STR_NETWORK_LANG_POLISH :Poloniera -STR_NETWORK_LANG_PORTUGUESE :Portugesa -STR_NETWORK_LANG_ROMANIAN :Errumaniera -STR_NETWORK_LANG_RUSSIAN :Errusiera -STR_NETWORK_LANG_SLOVAK :Eslabiera -STR_NETWORK_LANG_SLOVENIAN :Eslobeniera -STR_NETWORK_LANG_SPANISH :Gaztelera -STR_NETWORK_LANG_SWEDISH :Suediera -STR_NETWORK_LANG_TURKISH :Turkiera -STR_NETWORK_LANG_UKRAINIAN :Ukrainera -STR_NETWORK_LANG_AFRIKAANS :Afrikaanera -STR_NETWORK_LANG_CROATIAN :Kroatiera -STR_NETWORK_LANG_CATALAN :Katalana -STR_NETWORK_LANG_ESTONIAN :Estoniera -STR_NETWORK_LANG_GALICIAN :Galiziera -STR_NETWORK_LANG_GREEK :Greziera -STR_NETWORK_LANG_LATVIAN :Letoniera -############ End of leave-in-this-order - # Network game lobby STR_NETWORK_GAME_LOBBY_CAPTION :{WHITE}Multijokalari joko itxia diff --git a/src/lang/belarusian.txt b/src/lang/belarusian.txt index 9a9c84ba6e..21f4e795c2 100644 --- a/src/lang/belarusian.txt +++ b/src/lang/belarusian.txt @@ -2301,46 +2301,6 @@ STR_NETWORK_START_SERVER_LANGUAGE_TOOLTIP :{BLACK}Іншы STR_NETWORK_START_SERVER_NEW_GAME_NAME_OSKTITLE :{BLACK}Увядзіце назву сеткавай гульні -# Network game languages -############ Leave those lines in this order!! -STR_NETWORK_LANG_ANY :Любая -STR_NETWORK_LANG_ENGLISH :Анґельская -STR_NETWORK_LANG_GERMAN :Нямецкая -STR_NETWORK_LANG_FRENCH :Француская -STR_NETWORK_LANG_BRAZILIAN :Бразыльская -STR_NETWORK_LANG_BULGARIAN :Балґарская -STR_NETWORK_LANG_CHINESE :Кітайская -STR_NETWORK_LANG_CZECH :Чэская -STR_NETWORK_LANG_DANISH :Дацкая -STR_NETWORK_LANG_DUTCH :Нідэрляндзкая -STR_NETWORK_LANG_ESPERANTO :Эспэранта -STR_NETWORK_LANG_FINNISH :Фінская -STR_NETWORK_LANG_HUNGARIAN :Вугорская -STR_NETWORK_LANG_ICELANDIC :Ісьляндзкая -STR_NETWORK_LANG_ITALIAN :Італьянская -STR_NETWORK_LANG_JAPANESE :Японская -STR_NETWORK_LANG_KOREAN :Карэйская -STR_NETWORK_LANG_LITHUANIAN :Летувіская -STR_NETWORK_LANG_NORWEGIAN :Нарвэская -STR_NETWORK_LANG_POLISH :Польская -STR_NETWORK_LANG_PORTUGUESE :Партуґальская -STR_NETWORK_LANG_ROMANIAN :Румынская -STR_NETWORK_LANG_RUSSIAN :Расейская -STR_NETWORK_LANG_SLOVAK :Славацкая -STR_NETWORK_LANG_SLOVENIAN :Славенская -STR_NETWORK_LANG_SPANISH :Гішпанская -STR_NETWORK_LANG_SWEDISH :Швэдзкая -STR_NETWORK_LANG_TURKISH :Турэцкая -STR_NETWORK_LANG_UKRAINIAN :Украінская -STR_NETWORK_LANG_AFRIKAANS :Афрыкаанс -STR_NETWORK_LANG_CROATIAN :Харвацкая -STR_NETWORK_LANG_CATALAN :Каталёнская -STR_NETWORK_LANG_ESTONIAN :Эстонская -STR_NETWORK_LANG_GALICIAN :Ґалісійская -STR_NETWORK_LANG_GREEK :Грэцкая -STR_NETWORK_LANG_LATVIAN :Латыская -############ End of leave-in-this-order - # Network game lobby STR_NETWORK_GAME_LOBBY_CAPTION :{WHITE}Вітальня сеткавай гульні diff --git a/src/lang/brazilian_portuguese.txt b/src/lang/brazilian_portuguese.txt index 89d8f32043..6ea84d97b2 100644 --- a/src/lang/brazilian_portuguese.txt +++ b/src/lang/brazilian_portuguese.txt @@ -2071,46 +2071,6 @@ STR_NETWORK_START_SERVER_LANGUAGE_TOOLTIP :{BLACK}Outros j STR_NETWORK_START_SERVER_NEW_GAME_NAME_OSKTITLE :{BLACK}Coloque o nome para o jogo em rede -# Network game languages -############ Leave those lines in this order!! -STR_NETWORK_LANG_ANY :Qualquer -STR_NETWORK_LANG_ENGLISH :Inglês -STR_NETWORK_LANG_GERMAN :Alemão -STR_NETWORK_LANG_FRENCH :Francês -STR_NETWORK_LANG_BRAZILIAN :Brasileiro -STR_NETWORK_LANG_BULGARIAN :Búlgaro -STR_NETWORK_LANG_CHINESE :Chinês -STR_NETWORK_LANG_CZECH :Checo -STR_NETWORK_LANG_DANISH :Dinamarquês -STR_NETWORK_LANG_DUTCH :Holandês -STR_NETWORK_LANG_ESPERANTO :Esperanto -STR_NETWORK_LANG_FINNISH :Finlandês -STR_NETWORK_LANG_HUNGARIAN :Húngaro -STR_NETWORK_LANG_ICELANDIC :Islandês -STR_NETWORK_LANG_ITALIAN :Italiano -STR_NETWORK_LANG_JAPANESE :Japonês -STR_NETWORK_LANG_KOREAN :Coreano -STR_NETWORK_LANG_LITHUANIAN :Lituano -STR_NETWORK_LANG_NORWEGIAN :Norueguês -STR_NETWORK_LANG_POLISH :Polandês -STR_NETWORK_LANG_PORTUGUESE :Português -STR_NETWORK_LANG_ROMANIAN :Romeno -STR_NETWORK_LANG_RUSSIAN :Russo -STR_NETWORK_LANG_SLOVAK :Eslovaco -STR_NETWORK_LANG_SLOVENIAN :Esloveno -STR_NETWORK_LANG_SPANISH :Espanhol -STR_NETWORK_LANG_SWEDISH :Sueco -STR_NETWORK_LANG_TURKISH :Turco -STR_NETWORK_LANG_UKRAINIAN :Ucraniano -STR_NETWORK_LANG_AFRIKAANS :Africano -STR_NETWORK_LANG_CROATIAN :Croata -STR_NETWORK_LANG_CATALAN :Catalão -STR_NETWORK_LANG_ESTONIAN :Estoniano -STR_NETWORK_LANG_GALICIAN :Galego -STR_NETWORK_LANG_GREEK :Grego -STR_NETWORK_LANG_LATVIAN :Letão -############ End of leave-in-this-order - # Network game lobby STR_NETWORK_GAME_LOBBY_CAPTION :{WHITE}Sala de espera do jogo diff --git a/src/lang/bulgarian.txt b/src/lang/bulgarian.txt index 3f40d2c0af..aa11c5df73 100644 --- a/src/lang/bulgarian.txt +++ b/src/lang/bulgarian.txt @@ -1912,46 +1912,6 @@ STR_NETWORK_START_SERVER_LANGUAGE_TOOLTIP :{BLACK}Друг STR_NETWORK_START_SERVER_NEW_GAME_NAME_OSKTITLE :{BLACK}Въведете име за мрежовата игра -# Network game languages -############ Leave those lines in this order!! -STR_NETWORK_LANG_ANY :Всеки -STR_NETWORK_LANG_ENGLISH :Английски -STR_NETWORK_LANG_GERMAN :Немски -STR_NETWORK_LANG_FRENCH :Френски -STR_NETWORK_LANG_BRAZILIAN :Бразилски -STR_NETWORK_LANG_BULGARIAN :Български -STR_NETWORK_LANG_CHINESE :Китайски -STR_NETWORK_LANG_CZECH :Чешки -STR_NETWORK_LANG_DANISH :Датски -STR_NETWORK_LANG_DUTCH :Холандски -STR_NETWORK_LANG_ESPERANTO :Eсперанто -STR_NETWORK_LANG_FINNISH :Финландски -STR_NETWORK_LANG_HUNGARIAN :Унгарски -STR_NETWORK_LANG_ICELANDIC :Исландски -STR_NETWORK_LANG_ITALIAN :Италиански -STR_NETWORK_LANG_JAPANESE :Японски -STR_NETWORK_LANG_KOREAN :Корейски -STR_NETWORK_LANG_LITHUANIAN :Литовски -STR_NETWORK_LANG_NORWEGIAN :Норвежки -STR_NETWORK_LANG_POLISH :Полски -STR_NETWORK_LANG_PORTUGUESE :Португалски -STR_NETWORK_LANG_ROMANIAN :Румънски -STR_NETWORK_LANG_RUSSIAN :Руски -STR_NETWORK_LANG_SLOVAK :Словашки -STR_NETWORK_LANG_SLOVENIAN :Словенски -STR_NETWORK_LANG_SPANISH :Испански -STR_NETWORK_LANG_SWEDISH :Шведски -STR_NETWORK_LANG_TURKISH :Турски -STR_NETWORK_LANG_UKRAINIAN :Украински -STR_NETWORK_LANG_AFRIKAANS :Африкаанс -STR_NETWORK_LANG_CROATIAN :Хърватски -STR_NETWORK_LANG_CATALAN :Каталонски -STR_NETWORK_LANG_ESTONIAN :Естонски -STR_NETWORK_LANG_GALICIAN :Галиматия -STR_NETWORK_LANG_GREEK :Гръцки -STR_NETWORK_LANG_LATVIAN :Латвийски -############ End of leave-in-this-order - # Network game lobby STR_NETWORK_GAME_LOBBY_CAPTION :{WHITE}Преддверие на мрежовите игри diff --git a/src/lang/catalan.txt b/src/lang/catalan.txt index e8b0892d42..6df8a5e4b7 100644 --- a/src/lang/catalan.txt +++ b/src/lang/catalan.txt @@ -2071,46 +2071,6 @@ STR_NETWORK_START_SERVER_LANGUAGE_TOOLTIP :{BLACK}Els altr STR_NETWORK_START_SERVER_NEW_GAME_NAME_OSKTITLE :{BLACK}Posa el nom de la partida en xarxa -# Network game languages -############ Leave those lines in this order!! -STR_NETWORK_LANG_ANY :Qualsevol -STR_NETWORK_LANG_ENGLISH :Anglès -STR_NETWORK_LANG_GERMAN :Alemany -STR_NETWORK_LANG_FRENCH :Francès -STR_NETWORK_LANG_BRAZILIAN :Brasiler -STR_NETWORK_LANG_BULGARIAN :Búlgar -STR_NETWORK_LANG_CHINESE :Xinès -STR_NETWORK_LANG_CZECH :Txec -STR_NETWORK_LANG_DANISH :Danès -STR_NETWORK_LANG_DUTCH :Holandès -STR_NETWORK_LANG_ESPERANTO :Esperanto -STR_NETWORK_LANG_FINNISH :Finès -STR_NETWORK_LANG_HUNGARIAN :Hongarès -STR_NETWORK_LANG_ICELANDIC :Islandès -STR_NETWORK_LANG_ITALIAN :Italià -STR_NETWORK_LANG_JAPANESE :Japonès -STR_NETWORK_LANG_KOREAN :Coreà -STR_NETWORK_LANG_LITHUANIAN :Lituà -STR_NETWORK_LANG_NORWEGIAN :Noruec -STR_NETWORK_LANG_POLISH :Polonès -STR_NETWORK_LANG_PORTUGUESE :Portuguès -STR_NETWORK_LANG_ROMANIAN :Romanès -STR_NETWORK_LANG_RUSSIAN :Rus -STR_NETWORK_LANG_SLOVAK :Eslovac -STR_NETWORK_LANG_SLOVENIAN :Eslovè -STR_NETWORK_LANG_SPANISH :Espanyol -STR_NETWORK_LANG_SWEDISH :Suec -STR_NETWORK_LANG_TURKISH :Turc -STR_NETWORK_LANG_UKRAINIAN :Ucraïnès -STR_NETWORK_LANG_AFRIKAANS :Afrikaans -STR_NETWORK_LANG_CROATIAN :Croat -STR_NETWORK_LANG_CATALAN :Català -STR_NETWORK_LANG_ESTONIAN :Estonià -STR_NETWORK_LANG_GALICIAN :Gallec -STR_NETWORK_LANG_GREEK :Grec -STR_NETWORK_LANG_LATVIAN :Letó -############ End of leave-in-this-order - # Network game lobby STR_NETWORK_GAME_LOBBY_CAPTION :{WHITE}Lobby de partida multijugador diff --git a/src/lang/croatian.txt b/src/lang/croatian.txt index f534c46cdc..b28690c848 100644 --- a/src/lang/croatian.txt +++ b/src/lang/croatian.txt @@ -2096,46 +2096,6 @@ STR_NETWORK_START_SERVER_LANGUAGE_TOOLTIP :{BLACK}Drugi ig STR_NETWORK_START_SERVER_NEW_GAME_NAME_OSKTITLE :{BLACK}Upišite ime mrežne igre -# Network game languages -############ Leave those lines in this order!! -STR_NETWORK_LANG_ANY :Bilo koji -STR_NETWORK_LANG_ENGLISH :Engleski -STR_NETWORK_LANG_GERMAN :Njemački -STR_NETWORK_LANG_FRENCH :Francuski -STR_NETWORK_LANG_BRAZILIAN :Brazilski -STR_NETWORK_LANG_BULGARIAN :Bugarski -STR_NETWORK_LANG_CHINESE :Kineski -STR_NETWORK_LANG_CZECH :Češki -STR_NETWORK_LANG_DANISH :Danski -STR_NETWORK_LANG_DUTCH :Nizozemski -STR_NETWORK_LANG_ESPERANTO :Esperanto -STR_NETWORK_LANG_FINNISH :Finski -STR_NETWORK_LANG_HUNGARIAN :Mađarski -STR_NETWORK_LANG_ICELANDIC :Islandski -STR_NETWORK_LANG_ITALIAN :Talijanski -STR_NETWORK_LANG_JAPANESE :Japanski -STR_NETWORK_LANG_KOREAN :Korejski -STR_NETWORK_LANG_LITHUANIAN :Litavski -STR_NETWORK_LANG_NORWEGIAN :Norveški -STR_NETWORK_LANG_POLISH :Poljski -STR_NETWORK_LANG_PORTUGUESE :Portugalski -STR_NETWORK_LANG_ROMANIAN :Rumunjski -STR_NETWORK_LANG_RUSSIAN :Ruski -STR_NETWORK_LANG_SLOVAK :Slovački -STR_NETWORK_LANG_SLOVENIAN :Slovenski -STR_NETWORK_LANG_SPANISH :Španjolski -STR_NETWORK_LANG_SWEDISH :Švedski -STR_NETWORK_LANG_TURKISH :Turski -STR_NETWORK_LANG_UKRAINIAN :Ukrajinski -STR_NETWORK_LANG_AFRIKAANS :afrikaanski -STR_NETWORK_LANG_CROATIAN :hrvatski -STR_NETWORK_LANG_CATALAN :katalonski -STR_NETWORK_LANG_ESTONIAN :estonski -STR_NETWORK_LANG_GALICIAN :galicijski -STR_NETWORK_LANG_GREEK :Grčki -STR_NETWORK_LANG_LATVIAN :Latvijski -############ End of leave-in-this-order - # Network game lobby STR_NETWORK_GAME_LOBBY_CAPTION :{WHITE}Predvorje igre za više igrača diff --git a/src/lang/czech.txt b/src/lang/czech.txt index eefb64a7ea..444bdc7dfa 100644 --- a/src/lang/czech.txt +++ b/src/lang/czech.txt @@ -2152,46 +2152,6 @@ STR_NETWORK_START_SERVER_LANGUAGE_TOOLTIP :{BLACK}Aby osta STR_NETWORK_START_SERVER_NEW_GAME_NAME_OSKTITLE :{BLACK}Zadej jméno této síťové hry -# Network game languages -############ Leave those lines in this order!! -STR_NETWORK_LANG_ANY :jakýkoli -STR_NETWORK_LANG_ENGLISH :angličtina -STR_NETWORK_LANG_GERMAN :němčina -STR_NETWORK_LANG_FRENCH :francouzština -STR_NETWORK_LANG_BRAZILIAN :brazilská portugalština -STR_NETWORK_LANG_BULGARIAN :bulharština -STR_NETWORK_LANG_CHINESE :čínština -STR_NETWORK_LANG_CZECH :čeština -STR_NETWORK_LANG_DANISH :dánština -STR_NETWORK_LANG_DUTCH :nizozemština -STR_NETWORK_LANG_ESPERANTO :esperanto -STR_NETWORK_LANG_FINNISH :finština -STR_NETWORK_LANG_HUNGARIAN :maďarština -STR_NETWORK_LANG_ICELANDIC :islandština -STR_NETWORK_LANG_ITALIAN :italština -STR_NETWORK_LANG_JAPANESE :japonština -STR_NETWORK_LANG_KOREAN :korejština -STR_NETWORK_LANG_LITHUANIAN :litevština -STR_NETWORK_LANG_NORWEGIAN :norština -STR_NETWORK_LANG_POLISH :polština -STR_NETWORK_LANG_PORTUGUESE :portugalština -STR_NETWORK_LANG_ROMANIAN :rumunština -STR_NETWORK_LANG_RUSSIAN :ruština -STR_NETWORK_LANG_SLOVAK :slovenština -STR_NETWORK_LANG_SLOVENIAN :slovinština -STR_NETWORK_LANG_SPANISH :španělština -STR_NETWORK_LANG_SWEDISH :švédština -STR_NETWORK_LANG_TURKISH :turečtina -STR_NETWORK_LANG_UKRAINIAN :ukrajinština -STR_NETWORK_LANG_AFRIKAANS :afrikánština -STR_NETWORK_LANG_CROATIAN :chorvatština -STR_NETWORK_LANG_CATALAN :katalánština -STR_NETWORK_LANG_ESTONIAN :estonština -STR_NETWORK_LANG_GALICIAN :galicijština -STR_NETWORK_LANG_GREEK :řečtina -STR_NETWORK_LANG_LATVIAN :lotyština -############ End of leave-in-this-order - # Network game lobby STR_NETWORK_GAME_LOBBY_CAPTION :{WHITE}Vstupní místnost do hry více hráčů diff --git a/src/lang/danish.txt b/src/lang/danish.txt index 7f0e804fec..cff944dd92 100644 --- a/src/lang/danish.txt +++ b/src/lang/danish.txt @@ -2005,46 +2005,6 @@ STR_NETWORK_START_SERVER_LANGUAGE_TOOLTIP :{BLACK}Andre sp STR_NETWORK_START_SERVER_NEW_GAME_NAME_OSKTITLE :{BLACK}Skriv et navn for netværksspillet -# Network game languages -############ Leave those lines in this order!! -STR_NETWORK_LANG_ANY :Hvilket som helst -STR_NETWORK_LANG_ENGLISH :Engelsk -STR_NETWORK_LANG_GERMAN :Tysk -STR_NETWORK_LANG_FRENCH :Fransk -STR_NETWORK_LANG_BRAZILIAN :Brasiliansk -STR_NETWORK_LANG_BULGARIAN :Bulgarsk -STR_NETWORK_LANG_CHINESE :Kinesisk -STR_NETWORK_LANG_CZECH :Tjekkisk -STR_NETWORK_LANG_DANISH :Dansk -STR_NETWORK_LANG_DUTCH :Hollandsk -STR_NETWORK_LANG_ESPERANTO :Esperanto -STR_NETWORK_LANG_FINNISH :Finsk -STR_NETWORK_LANG_HUNGARIAN :Ungarsk -STR_NETWORK_LANG_ICELANDIC :Islandsk -STR_NETWORK_LANG_ITALIAN :Italiensk -STR_NETWORK_LANG_JAPANESE :Japansk -STR_NETWORK_LANG_KOREAN :Koreansk -STR_NETWORK_LANG_LITHUANIAN :Litauisk -STR_NETWORK_LANG_NORWEGIAN :Norsk -STR_NETWORK_LANG_POLISH :Polsk -STR_NETWORK_LANG_PORTUGUESE :Portugisisk -STR_NETWORK_LANG_ROMANIAN :Rumænsk -STR_NETWORK_LANG_RUSSIAN :Russisk -STR_NETWORK_LANG_SLOVAK :Slovakisk -STR_NETWORK_LANG_SLOVENIAN :Slovensk -STR_NETWORK_LANG_SPANISH :Spansk -STR_NETWORK_LANG_SWEDISH :Svensk -STR_NETWORK_LANG_TURKISH :Tyrkisk -STR_NETWORK_LANG_UKRAINIAN :Ukrainsk -STR_NETWORK_LANG_AFRIKAANS :Afrikaans -STR_NETWORK_LANG_CROATIAN :Kroatisk -STR_NETWORK_LANG_CATALAN :Catalansk -STR_NETWORK_LANG_ESTONIAN :Estisk -STR_NETWORK_LANG_GALICIAN :Galicisk -STR_NETWORK_LANG_GREEK :Græsk -STR_NETWORK_LANG_LATVIAN :Lettisk -############ End of leave-in-this-order - # Network game lobby STR_NETWORK_GAME_LOBBY_CAPTION :{WHITE}Netværksspils lobby diff --git a/src/lang/dutch.txt b/src/lang/dutch.txt index 799ce679b1..4bd4a48e7a 100644 --- a/src/lang/dutch.txt +++ b/src/lang/dutch.txt @@ -2065,46 +2065,6 @@ STR_NETWORK_START_SERVER_LANGUAGE_TOOLTIP :{BLACK}Andere s STR_NETWORK_START_SERVER_NEW_GAME_NAME_OSKTITLE :{BLACK}Geef de naam van het netwerkspel -# Network game languages -############ Leave those lines in this order!! -STR_NETWORK_LANG_ANY :Elke -STR_NETWORK_LANG_ENGLISH :Engels -STR_NETWORK_LANG_GERMAN :Duits -STR_NETWORK_LANG_FRENCH :Frans -STR_NETWORK_LANG_BRAZILIAN :Braziliaans -STR_NETWORK_LANG_BULGARIAN :Bulgaars -STR_NETWORK_LANG_CHINESE :Chinees -STR_NETWORK_LANG_CZECH :Tsjechisch -STR_NETWORK_LANG_DANISH :Deens -STR_NETWORK_LANG_DUTCH :Nederlands -STR_NETWORK_LANG_ESPERANTO :Esperanto -STR_NETWORK_LANG_FINNISH :Fins -STR_NETWORK_LANG_HUNGARIAN :Hongaars -STR_NETWORK_LANG_ICELANDIC :IJslands -STR_NETWORK_LANG_ITALIAN :Italiaans -STR_NETWORK_LANG_JAPANESE :Japans -STR_NETWORK_LANG_KOREAN :Koreaans -STR_NETWORK_LANG_LITHUANIAN :Litouws -STR_NETWORK_LANG_NORWEGIAN :Noors -STR_NETWORK_LANG_POLISH :Pools -STR_NETWORK_LANG_PORTUGUESE :Portugees -STR_NETWORK_LANG_ROMANIAN :Roemeens -STR_NETWORK_LANG_RUSSIAN :Russisch -STR_NETWORK_LANG_SLOVAK :Slowaaks -STR_NETWORK_LANG_SLOVENIAN :Sloveens -STR_NETWORK_LANG_SPANISH :Spaans -STR_NETWORK_LANG_SWEDISH :Zweeds -STR_NETWORK_LANG_TURKISH :Turks -STR_NETWORK_LANG_UKRAINIAN :Oekraïens -STR_NETWORK_LANG_AFRIKAANS :Afrikaans -STR_NETWORK_LANG_CROATIAN :Kroatisch -STR_NETWORK_LANG_CATALAN :Catalaans -STR_NETWORK_LANG_ESTONIAN :Estisch -STR_NETWORK_LANG_GALICIAN :Galiciaans -STR_NETWORK_LANG_GREEK :Grieks -STR_NETWORK_LANG_LATVIAN :Lets -############ End of leave-in-this-order - # Network game lobby STR_NETWORK_GAME_LOBBY_CAPTION :{WHITE}Verzamelkamer voor netwerkspellen diff --git a/src/lang/english_AU.txt b/src/lang/english_AU.txt index 159af46cd3..1fd94cbd87 100644 --- a/src/lang/english_AU.txt +++ b/src/lang/english_AU.txt @@ -1922,46 +1922,6 @@ STR_NETWORK_START_SERVER_LANGUAGE_TOOLTIP :{BLACK}Other pl STR_NETWORK_START_SERVER_NEW_GAME_NAME_OSKTITLE :{BLACK}Enter a name for the network game -# Network game languages -############ Leave those lines in this order!! -STR_NETWORK_LANG_ANY :Any -STR_NETWORK_LANG_ENGLISH :English -STR_NETWORK_LANG_GERMAN :German -STR_NETWORK_LANG_FRENCH :French -STR_NETWORK_LANG_BRAZILIAN :Brazilian -STR_NETWORK_LANG_BULGARIAN :Bulgarian -STR_NETWORK_LANG_CHINESE :Chinese -STR_NETWORK_LANG_CZECH :Czech -STR_NETWORK_LANG_DANISH :Danish -STR_NETWORK_LANG_DUTCH :Dutch -STR_NETWORK_LANG_ESPERANTO :Esperanto -STR_NETWORK_LANG_FINNISH :Finnish -STR_NETWORK_LANG_HUNGARIAN :Hungarian -STR_NETWORK_LANG_ICELANDIC :Icelandic -STR_NETWORK_LANG_ITALIAN :Italian -STR_NETWORK_LANG_JAPANESE :Japanese -STR_NETWORK_LANG_KOREAN :Korean -STR_NETWORK_LANG_LITHUANIAN :Lithuanian -STR_NETWORK_LANG_NORWEGIAN :Norwegian -STR_NETWORK_LANG_POLISH :Polish -STR_NETWORK_LANG_PORTUGUESE :Portuguese -STR_NETWORK_LANG_ROMANIAN :Romanian -STR_NETWORK_LANG_RUSSIAN :Russian -STR_NETWORK_LANG_SLOVAK :Slovak -STR_NETWORK_LANG_SLOVENIAN :Slovenian -STR_NETWORK_LANG_SPANISH :Spanish -STR_NETWORK_LANG_SWEDISH :Swedish -STR_NETWORK_LANG_TURKISH :Turkish -STR_NETWORK_LANG_UKRAINIAN :Ukrainian -STR_NETWORK_LANG_AFRIKAANS :Afrikaans -STR_NETWORK_LANG_CROATIAN :Croatian -STR_NETWORK_LANG_CATALAN :Catalan -STR_NETWORK_LANG_ESTONIAN :Estonian -STR_NETWORK_LANG_GALICIAN :Galician -STR_NETWORK_LANG_GREEK :Greek -STR_NETWORK_LANG_LATVIAN :Latvian -############ End of leave-in-this-order - # Network game lobby STR_NETWORK_GAME_LOBBY_CAPTION :{WHITE}Multiplayer game lobby diff --git a/src/lang/english_US.txt b/src/lang/english_US.txt index eb32fbc55e..530813d62b 100644 --- a/src/lang/english_US.txt +++ b/src/lang/english_US.txt @@ -2070,46 +2070,6 @@ STR_NETWORK_START_SERVER_LANGUAGE_TOOLTIP :{BLACK}Other pl STR_NETWORK_START_SERVER_NEW_GAME_NAME_OSKTITLE :{BLACK}Enter a name for the network game -# Network game languages -############ Leave those lines in this order!! -STR_NETWORK_LANG_ANY :Any -STR_NETWORK_LANG_ENGLISH :English -STR_NETWORK_LANG_GERMAN :German -STR_NETWORK_LANG_FRENCH :French -STR_NETWORK_LANG_BRAZILIAN :Brazilian -STR_NETWORK_LANG_BULGARIAN :Bulgarian -STR_NETWORK_LANG_CHINESE :Chinese -STR_NETWORK_LANG_CZECH :Czech -STR_NETWORK_LANG_DANISH :Danish -STR_NETWORK_LANG_DUTCH :Dutch -STR_NETWORK_LANG_ESPERANTO :Esperanto -STR_NETWORK_LANG_FINNISH :Finnish -STR_NETWORK_LANG_HUNGARIAN :Hungarian -STR_NETWORK_LANG_ICELANDIC :Icelandic -STR_NETWORK_LANG_ITALIAN :Italian -STR_NETWORK_LANG_JAPANESE :Japanese -STR_NETWORK_LANG_KOREAN :Korean -STR_NETWORK_LANG_LITHUANIAN :Lithuanian -STR_NETWORK_LANG_NORWEGIAN :Norwegian -STR_NETWORK_LANG_POLISH :Polish -STR_NETWORK_LANG_PORTUGUESE :Portuguese -STR_NETWORK_LANG_ROMANIAN :Romanian -STR_NETWORK_LANG_RUSSIAN :Russian -STR_NETWORK_LANG_SLOVAK :Slovak -STR_NETWORK_LANG_SLOVENIAN :Slovenian -STR_NETWORK_LANG_SPANISH :Spanish -STR_NETWORK_LANG_SWEDISH :Swedish -STR_NETWORK_LANG_TURKISH :Turkish -STR_NETWORK_LANG_UKRAINIAN :Ukrainian -STR_NETWORK_LANG_AFRIKAANS :Afrikaans -STR_NETWORK_LANG_CROATIAN :Croatian -STR_NETWORK_LANG_CATALAN :Catalan -STR_NETWORK_LANG_ESTONIAN :Estonian -STR_NETWORK_LANG_GALICIAN :Galician -STR_NETWORK_LANG_GREEK :Greek -STR_NETWORK_LANG_LATVIAN :Latvian -############ End of leave-in-this-order - # Network game lobby STR_NETWORK_GAME_LOBBY_CAPTION :{WHITE}Multiplayer game lobby diff --git a/src/lang/esperanto.txt b/src/lang/esperanto.txt index c8893d3ab4..97f1e625f4 100644 --- a/src/lang/esperanto.txt +++ b/src/lang/esperanto.txt @@ -1602,46 +1602,6 @@ STR_NETWORK_START_SERVER_LANGUAGE_TOOLTIP :{BLACK}Aliaj lu STR_NETWORK_START_SERVER_NEW_GAME_NAME_OSKTITLE :{BLACK}Tajpu nomon por la retludo -# Network game languages -############ Leave those lines in this order!! -STR_NETWORK_LANG_ANY :Ajna -STR_NETWORK_LANG_ENGLISH :Angla -STR_NETWORK_LANG_GERMAN :Germana -STR_NETWORK_LANG_FRENCH :Franca -STR_NETWORK_LANG_BRAZILIAN :Brazila -STR_NETWORK_LANG_BULGARIAN :Bulgara -STR_NETWORK_LANG_CHINESE :Ĉina -STR_NETWORK_LANG_CZECH :Ĉeĥa -STR_NETWORK_LANG_DANISH :Dana -STR_NETWORK_LANG_DUTCH :Nederlanda -STR_NETWORK_LANG_ESPERANTO :Esperanto -STR_NETWORK_LANG_FINNISH :Finna -STR_NETWORK_LANG_HUNGARIAN :Hungara -STR_NETWORK_LANG_ICELANDIC :Islanda -STR_NETWORK_LANG_ITALIAN :Itala -STR_NETWORK_LANG_JAPANESE :Japana -STR_NETWORK_LANG_KOREAN :Korea -STR_NETWORK_LANG_LITHUANIAN :Litova -STR_NETWORK_LANG_NORWEGIAN :Norveĝa -STR_NETWORK_LANG_POLISH :Pola -STR_NETWORK_LANG_PORTUGUESE :Portugala -STR_NETWORK_LANG_ROMANIAN :Romana -STR_NETWORK_LANG_RUSSIAN :Rusa -STR_NETWORK_LANG_SLOVAK :Slovaka -STR_NETWORK_LANG_SLOVENIAN :Slovena -STR_NETWORK_LANG_SPANISH :Hispana -STR_NETWORK_LANG_SWEDISH :Sveda -STR_NETWORK_LANG_TURKISH :Turka -STR_NETWORK_LANG_UKRAINIAN :Ukraina -STR_NETWORK_LANG_AFRIKAANS :Afrikansa -STR_NETWORK_LANG_CROATIAN :Kroata -STR_NETWORK_LANG_CATALAN :Kataluna -STR_NETWORK_LANG_ESTONIAN :Estona -STR_NETWORK_LANG_GALICIAN :Galica -STR_NETWORK_LANG_GREEK :Greka -STR_NETWORK_LANG_LATVIAN :Latva -############ End of leave-in-this-order - # Network game lobby STR_NETWORK_GAME_LOBBY_CAPTION :{WHITE}Atendejo por plurludantaj ludoj diff --git a/src/lang/estonian.txt b/src/lang/estonian.txt index 4fc6af2b96..fdf2958841 100644 --- a/src/lang/estonian.txt +++ b/src/lang/estonian.txt @@ -2123,46 +2123,6 @@ STR_NETWORK_START_SERVER_LANGUAGE_TOOLTIP :{BLACK}Teavitab STR_NETWORK_START_SERVER_NEW_GAME_NAME_OSKTITLE :{BLACK}Sisesta mitmikmängu nimi -# Network game languages -############ Leave those lines in this order!! -STR_NETWORK_LANG_ANY :Suvaline -STR_NETWORK_LANG_ENGLISH :Inglise keel -STR_NETWORK_LANG_GERMAN :Saksa keel -STR_NETWORK_LANG_FRENCH :Prantsuse keel -STR_NETWORK_LANG_BRAZILIAN :Brasiilia -STR_NETWORK_LANG_BULGARIAN :Bulgaaria -STR_NETWORK_LANG_CHINESE :Hiina -STR_NETWORK_LANG_CZECH :Tšehhi -STR_NETWORK_LANG_DANISH :Taani -STR_NETWORK_LANG_DUTCH :Hollandi -STR_NETWORK_LANG_ESPERANTO :Esperanto -STR_NETWORK_LANG_FINNISH :Soome -STR_NETWORK_LANG_HUNGARIAN :Ungari -STR_NETWORK_LANG_ICELANDIC :Islandi -STR_NETWORK_LANG_ITALIAN :Itaalia -STR_NETWORK_LANG_JAPANESE :Jaapani -STR_NETWORK_LANG_KOREAN :Korea -STR_NETWORK_LANG_LITHUANIAN :Leedu -STR_NETWORK_LANG_NORWEGIAN :Norra -STR_NETWORK_LANG_POLISH :Poola -STR_NETWORK_LANG_PORTUGUESE :Portugali -STR_NETWORK_LANG_ROMANIAN :Rumeenia -STR_NETWORK_LANG_RUSSIAN :Vene -STR_NETWORK_LANG_SLOVAK :Slovakkia -STR_NETWORK_LANG_SLOVENIAN :Sloveenia -STR_NETWORK_LANG_SPANISH :Hispaania -STR_NETWORK_LANG_SWEDISH :Rootsi -STR_NETWORK_LANG_TURKISH :Türgi -STR_NETWORK_LANG_UKRAINIAN :Ukraina -STR_NETWORK_LANG_AFRIKAANS :Afrikaani -STR_NETWORK_LANG_CROATIAN :Horvaadi -STR_NETWORK_LANG_CATALAN :Katalaani -STR_NETWORK_LANG_ESTONIAN :Eesti -STR_NETWORK_LANG_GALICIAN :Galeegi -STR_NETWORK_LANG_GREEK :Kreeka -STR_NETWORK_LANG_LATVIAN :Läti -############ End of leave-in-this-order - # Network game lobby STR_NETWORK_GAME_LOBBY_CAPTION :{WHITE}Mitmikmängu jututuba diff --git a/src/lang/faroese.txt b/src/lang/faroese.txt index e6519df5f2..a0c6ed76bb 100644 --- a/src/lang/faroese.txt +++ b/src/lang/faroese.txt @@ -1770,46 +1770,6 @@ STR_NETWORK_START_SERVER_LANGUAGE_TOOLTIP :{BLACK}Aðrir s STR_NETWORK_START_SERVER_NEW_GAME_NAME_OSKTITLE :{BLACK}Gev netverks spælinum eitt navn -# Network game languages -############ Leave those lines in this order!! -STR_NETWORK_LANG_ANY :Hvat sum helst -STR_NETWORK_LANG_ENGLISH :Enskt -STR_NETWORK_LANG_GERMAN :Týskt -STR_NETWORK_LANG_FRENCH :Franskt -STR_NETWORK_LANG_BRAZILIAN :Brasilienskt -STR_NETWORK_LANG_BULGARIAN :Bulgariskt -STR_NETWORK_LANG_CHINESE :Kinverskt -STR_NETWORK_LANG_CZECH :Tjekkiskt -STR_NETWORK_LANG_DANISH :Danskt -STR_NETWORK_LANG_DUTCH :Niðurlendskt -STR_NETWORK_LANG_ESPERANTO :Esperanto -STR_NETWORK_LANG_FINNISH :Finskt -STR_NETWORK_LANG_HUNGARIAN :Ungarskt -STR_NETWORK_LANG_ICELANDIC :Íslenskt -STR_NETWORK_LANG_ITALIAN :Italienskt -STR_NETWORK_LANG_JAPANESE :Japanskt -STR_NETWORK_LANG_KOREAN :Koreanskt -STR_NETWORK_LANG_LITHUANIAN :Litauiskt -STR_NETWORK_LANG_NORWEGIAN :Norskt -STR_NETWORK_LANG_POLISH :Polskt -STR_NETWORK_LANG_PORTUGUESE :Portugisiskt -STR_NETWORK_LANG_ROMANIAN :Rumenskt -STR_NETWORK_LANG_RUSSIAN :Russiskt -STR_NETWORK_LANG_SLOVAK :Slovakiskt -STR_NETWORK_LANG_SLOVENIAN :Slovenskt -STR_NETWORK_LANG_SPANISH :Spanskt -STR_NETWORK_LANG_SWEDISH :Svenskt -STR_NETWORK_LANG_TURKISH :Turkiskt -STR_NETWORK_LANG_UKRAINIAN :Ukrainskt -STR_NETWORK_LANG_AFRIKAANS :Afrikaans -STR_NETWORK_LANG_CROATIAN :Kroatiskt -STR_NETWORK_LANG_CATALAN :Catalan -STR_NETWORK_LANG_ESTONIAN :Estoniskt -STR_NETWORK_LANG_GALICIAN :Galisiskt -STR_NETWORK_LANG_GREEK :Grikskt -STR_NETWORK_LANG_LATVIAN :Latviskt -############ End of leave-in-this-order - # Network game lobby STR_NETWORK_GAME_LOBBY_CAPTION :{WHITE}Hópspæls forhøll diff --git a/src/lang/finnish.txt b/src/lang/finnish.txt index 3fea792d3c..a3b2a8cb09 100644 --- a/src/lang/finnish.txt +++ b/src/lang/finnish.txt @@ -2070,46 +2070,6 @@ STR_NETWORK_START_SERVER_LANGUAGE_TOOLTIP :{BLACK}Toiset p STR_NETWORK_START_SERVER_NEW_GAME_NAME_OSKTITLE :{BLACK}Syötä nimi verkkopelille -# Network game languages -############ Leave those lines in this order!! -STR_NETWORK_LANG_ANY :mikä tahansa -STR_NETWORK_LANG_ENGLISH :englanti -STR_NETWORK_LANG_GERMAN :saksa -STR_NETWORK_LANG_FRENCH :ranska -STR_NETWORK_LANG_BRAZILIAN :brasilianportugali -STR_NETWORK_LANG_BULGARIAN :bulgaria -STR_NETWORK_LANG_CHINESE :kiina -STR_NETWORK_LANG_CZECH :tšekki -STR_NETWORK_LANG_DANISH :tanska -STR_NETWORK_LANG_DUTCH :hollanti -STR_NETWORK_LANG_ESPERANTO :esperanto -STR_NETWORK_LANG_FINNISH :suomi -STR_NETWORK_LANG_HUNGARIAN :unkari -STR_NETWORK_LANG_ICELANDIC :islanti -STR_NETWORK_LANG_ITALIAN :italia -STR_NETWORK_LANG_JAPANESE :japani -STR_NETWORK_LANG_KOREAN :korea -STR_NETWORK_LANG_LITHUANIAN :liettua -STR_NETWORK_LANG_NORWEGIAN :norja -STR_NETWORK_LANG_POLISH :puola -STR_NETWORK_LANG_PORTUGUESE :portugali -STR_NETWORK_LANG_ROMANIAN :romania -STR_NETWORK_LANG_RUSSIAN :venäjä -STR_NETWORK_LANG_SLOVAK :slovakia -STR_NETWORK_LANG_SLOVENIAN :sloveeni -STR_NETWORK_LANG_SPANISH :espanja -STR_NETWORK_LANG_SWEDISH :ruotsi -STR_NETWORK_LANG_TURKISH :turkki -STR_NETWORK_LANG_UKRAINIAN :ukraina -STR_NETWORK_LANG_AFRIKAANS :afrikaans -STR_NETWORK_LANG_CROATIAN :kroatia -STR_NETWORK_LANG_CATALAN :katalaani -STR_NETWORK_LANG_ESTONIAN :viro -STR_NETWORK_LANG_GALICIAN :galego -STR_NETWORK_LANG_GREEK :kreikka -STR_NETWORK_LANG_LATVIAN :latvia -############ End of leave-in-this-order - # Network game lobby STR_NETWORK_GAME_LOBBY_CAPTION :{WHITE}Moninpeliaula diff --git a/src/lang/french.txt b/src/lang/french.txt index cf6763c8c1..bfad801ea5 100644 --- a/src/lang/french.txt +++ b/src/lang/french.txt @@ -2071,46 +2071,6 @@ STR_NETWORK_START_SERVER_LANGUAGE_TOOLTIP :{BLACK}Les autr STR_NETWORK_START_SERVER_NEW_GAME_NAME_OSKTITLE :{BLACK}Entrer un nom pour la partie en réseau -# Network game languages -############ Leave those lines in this order!! -STR_NETWORK_LANG_ANY :Aucune -STR_NETWORK_LANG_ENGLISH :Anglais -STR_NETWORK_LANG_GERMAN :Allemand -STR_NETWORK_LANG_FRENCH :Français -STR_NETWORK_LANG_BRAZILIAN :Brésilien -STR_NETWORK_LANG_BULGARIAN :Bulgare -STR_NETWORK_LANG_CHINESE :Chinois -STR_NETWORK_LANG_CZECH :Tchèque -STR_NETWORK_LANG_DANISH :Danois -STR_NETWORK_LANG_DUTCH :Néerlandais -STR_NETWORK_LANG_ESPERANTO :Espéranto -STR_NETWORK_LANG_FINNISH :Finlandais -STR_NETWORK_LANG_HUNGARIAN :Hongrois -STR_NETWORK_LANG_ICELANDIC :Islandais -STR_NETWORK_LANG_ITALIAN :Italien -STR_NETWORK_LANG_JAPANESE :Japonais -STR_NETWORK_LANG_KOREAN :Coréen -STR_NETWORK_LANG_LITHUANIAN :Lituanien -STR_NETWORK_LANG_NORWEGIAN :Norvégien -STR_NETWORK_LANG_POLISH :Polonais -STR_NETWORK_LANG_PORTUGUESE :Portugais -STR_NETWORK_LANG_ROMANIAN :Roumain -STR_NETWORK_LANG_RUSSIAN :Russe -STR_NETWORK_LANG_SLOVAK :Slovaque -STR_NETWORK_LANG_SLOVENIAN :Slovène -STR_NETWORK_LANG_SPANISH :Espagnol -STR_NETWORK_LANG_SWEDISH :Suédois -STR_NETWORK_LANG_TURKISH :Turc -STR_NETWORK_LANG_UKRAINIAN :Ukrainien -STR_NETWORK_LANG_AFRIKAANS :Afrikaans -STR_NETWORK_LANG_CROATIAN :Croate -STR_NETWORK_LANG_CATALAN :Catalan -STR_NETWORK_LANG_ESTONIAN :Estonien -STR_NETWORK_LANG_GALICIAN :Galicien -STR_NETWORK_LANG_GREEK :Grec -STR_NETWORK_LANG_LATVIAN :Letton -############ End of leave-in-this-order - # Network game lobby STR_NETWORK_GAME_LOBBY_CAPTION :{WHITE}Préparation de la partie diff --git a/src/lang/gaelic.txt b/src/lang/gaelic.txt index 7e15bd033d..c38ac7e026 100644 --- a/src/lang/gaelic.txt +++ b/src/lang/gaelic.txt @@ -2157,46 +2157,6 @@ STR_NETWORK_START_SERVER_LANGUAGE_TOOLTIP :{BLACK}Bidh fio STR_NETWORK_START_SERVER_NEW_GAME_NAME_OSKTITLE :{BLACK}Cuir a-steach ainm airson a' gheama lìonraidh -# Network game languages -############ Leave those lines in this order!! -STR_NETWORK_LANG_ANY :Cànan sam bith -STR_NETWORK_LANG_ENGLISH :Beurla -STR_NETWORK_LANG_GERMAN :Gearmailtis -STR_NETWORK_LANG_FRENCH :Fraingis -STR_NETWORK_LANG_BRAZILIAN :Portagailis Bhraisileach -STR_NETWORK_LANG_BULGARIAN :Bulgarais -STR_NETWORK_LANG_CHINESE :Sìnis -STR_NETWORK_LANG_CZECH :Seacais -STR_NETWORK_LANG_DANISH :Danmhairgis -STR_NETWORK_LANG_DUTCH :Duitsis -STR_NETWORK_LANG_ESPERANTO :Esperanto -STR_NETWORK_LANG_FINNISH :Fionnlannais -STR_NETWORK_LANG_HUNGARIAN :Ungairis -STR_NETWORK_LANG_ICELANDIC :Tìlis -STR_NETWORK_LANG_ITALIAN :Eadailtis -STR_NETWORK_LANG_JAPANESE :Seapanais -STR_NETWORK_LANG_KOREAN :Coirèanais -STR_NETWORK_LANG_LITHUANIAN :Liotuainis -STR_NETWORK_LANG_NORWEGIAN :Nirribhis -STR_NETWORK_LANG_POLISH :Pòlainnis -STR_NETWORK_LANG_PORTUGUESE :Portagailis -STR_NETWORK_LANG_ROMANIAN :Romàinis -STR_NETWORK_LANG_RUSSIAN :Ruisis -STR_NETWORK_LANG_SLOVAK :Slòbhacais -STR_NETWORK_LANG_SLOVENIAN :Slòbhainis -STR_NETWORK_LANG_SPANISH :Spàinntis -STR_NETWORK_LANG_SWEDISH :Suainis -STR_NETWORK_LANG_TURKISH :Turcais -STR_NETWORK_LANG_UKRAINIAN :Ucràinis -STR_NETWORK_LANG_AFRIKAANS :Afraganais -STR_NETWORK_LANG_CROATIAN :Cròthaisis -STR_NETWORK_LANG_CATALAN :Catalanais -STR_NETWORK_LANG_ESTONIAN :Eastoinis -STR_NETWORK_LANG_GALICIAN :Gailìsis -STR_NETWORK_LANG_GREEK :Greugais -STR_NETWORK_LANG_LATVIAN :Laitbheis -############ End of leave-in-this-order - # Network game lobby STR_NETWORK_GAME_LOBBY_CAPTION :{WHITE}Lobaidh nan geamannan ioma-chluicheadair diff --git a/src/lang/galician.txt b/src/lang/galician.txt index d993322b42..255d5e8838 100644 --- a/src/lang/galician.txt +++ b/src/lang/galician.txt @@ -1993,46 +1993,6 @@ STR_NETWORK_START_SERVER_LANGUAGE_TOOLTIP :{BLACK}Outros x STR_NETWORK_START_SERVER_NEW_GAME_NAME_OSKTITLE :{BLACK}Escribe un nome para a partida en rede -# Network game languages -############ Leave those lines in this order!! -STR_NETWORK_LANG_ANY :Calquera -STR_NETWORK_LANG_ENGLISH :Inglés -STR_NETWORK_LANG_GERMAN :Alemán -STR_NETWORK_LANG_FRENCH :Francés -STR_NETWORK_LANG_BRAZILIAN :Brasileiro -STR_NETWORK_LANG_BULGARIAN :Búlgaro -STR_NETWORK_LANG_CHINESE :Chinés -STR_NETWORK_LANG_CZECH :Checo -STR_NETWORK_LANG_DANISH :Danés -STR_NETWORK_LANG_DUTCH :Holandés -STR_NETWORK_LANG_ESPERANTO :Esperanto -STR_NETWORK_LANG_FINNISH :Finés -STR_NETWORK_LANG_HUNGARIAN :Húngaro -STR_NETWORK_LANG_ICELANDIC :Islandés -STR_NETWORK_LANG_ITALIAN :Italiano -STR_NETWORK_LANG_JAPANESE :Xaponés -STR_NETWORK_LANG_KOREAN :Coreano -STR_NETWORK_LANG_LITHUANIAN :Lituano -STR_NETWORK_LANG_NORWEGIAN :Noruegués -STR_NETWORK_LANG_POLISH :Polaco -STR_NETWORK_LANG_PORTUGUESE :Portugués -STR_NETWORK_LANG_ROMANIAN :Rumano -STR_NETWORK_LANG_RUSSIAN :Ruso -STR_NETWORK_LANG_SLOVAK :Eslovaco -STR_NETWORK_LANG_SLOVENIAN :Esloveno -STR_NETWORK_LANG_SPANISH :Español -STR_NETWORK_LANG_SWEDISH :Sueco -STR_NETWORK_LANG_TURKISH :Turco -STR_NETWORK_LANG_UKRAINIAN :Ucraniano -STR_NETWORK_LANG_AFRIKAANS :Africano -STR_NETWORK_LANG_CROATIAN :Croata -STR_NETWORK_LANG_CATALAN :Catalán -STR_NETWORK_LANG_ESTONIAN :Estonio -STR_NETWORK_LANG_GALICIAN :Galego -STR_NETWORK_LANG_GREEK :Grego -STR_NETWORK_LANG_LATVIAN :Letón -############ End of leave-in-this-order - # Network game lobby STR_NETWORK_GAME_LOBBY_CAPTION :{WHITE}Sala de espera de partida multixogador diff --git a/src/lang/german.txt b/src/lang/german.txt index 6fcc09166b..d0f89a9265 100644 --- a/src/lang/german.txt +++ b/src/lang/german.txt @@ -2071,46 +2071,6 @@ STR_NETWORK_START_SERVER_LANGUAGE_TOOLTIP :{BLACK}Damit an STR_NETWORK_START_SERVER_NEW_GAME_NAME_OSKTITLE :{BLACK}Namen für das Netzwerkspiel eingeben -# Network game languages -############ Leave those lines in this order!! -STR_NETWORK_LANG_ANY :Egal -STR_NETWORK_LANG_ENGLISH :Englisch -STR_NETWORK_LANG_GERMAN :Deutsch -STR_NETWORK_LANG_FRENCH :Französisch -STR_NETWORK_LANG_BRAZILIAN :Brasilianisch -STR_NETWORK_LANG_BULGARIAN :Bulgarisch -STR_NETWORK_LANG_CHINESE :Chinesisch -STR_NETWORK_LANG_CZECH :Tschechisch -STR_NETWORK_LANG_DANISH :Dänisch -STR_NETWORK_LANG_DUTCH :Niederländisch -STR_NETWORK_LANG_ESPERANTO :Esperanto -STR_NETWORK_LANG_FINNISH :Finnisch -STR_NETWORK_LANG_HUNGARIAN :Ungarisch -STR_NETWORK_LANG_ICELANDIC :Isländisch -STR_NETWORK_LANG_ITALIAN :Italienisch -STR_NETWORK_LANG_JAPANESE :Japanisch -STR_NETWORK_LANG_KOREAN :Koreanisch -STR_NETWORK_LANG_LITHUANIAN :Litauisch -STR_NETWORK_LANG_NORWEGIAN :Norwegisch -STR_NETWORK_LANG_POLISH :Polnisch -STR_NETWORK_LANG_PORTUGUESE :Portugiesisch -STR_NETWORK_LANG_ROMANIAN :Rumänisch -STR_NETWORK_LANG_RUSSIAN :Russisch -STR_NETWORK_LANG_SLOVAK :Slowakisch -STR_NETWORK_LANG_SLOVENIAN :Slowenisch -STR_NETWORK_LANG_SPANISH :Spanisch -STR_NETWORK_LANG_SWEDISH :Schwedisch -STR_NETWORK_LANG_TURKISH :Türkisch -STR_NETWORK_LANG_UKRAINIAN :Ukrainisch -STR_NETWORK_LANG_AFRIKAANS :Afrikaans -STR_NETWORK_LANG_CROATIAN :Kroatisch -STR_NETWORK_LANG_CATALAN :Katalanisch -STR_NETWORK_LANG_ESTONIAN :Estnisch -STR_NETWORK_LANG_GALICIAN :Galizisch -STR_NETWORK_LANG_GREEK :Griechisch -STR_NETWORK_LANG_LATVIAN :Lettisch -############ End of leave-in-this-order - # Network game lobby STR_NETWORK_GAME_LOBBY_CAPTION :{WHITE}Mehrspielerlobby diff --git a/src/lang/greek.txt b/src/lang/greek.txt index 6593038b4f..fc55858dc8 100644 --- a/src/lang/greek.txt +++ b/src/lang/greek.txt @@ -2114,46 +2114,6 @@ STR_NETWORK_START_SERVER_LANGUAGE_TOOLTIP :{BLACK}Οι υ STR_NETWORK_START_SERVER_NEW_GAME_NAME_OSKTITLE :{BLACK}Εισάγετε το όνομα του δικτυακού παιχνιδιού -# Network game languages -############ Leave those lines in this order!! -STR_NETWORK_LANG_ANY :Οποιαδήποτε -STR_NETWORK_LANG_ENGLISH :Αγγλικά -STR_NETWORK_LANG_GERMAN :Γερμανικά -STR_NETWORK_LANG_FRENCH :Γαλλικά -STR_NETWORK_LANG_BRAZILIAN :Βραζιλιάνικα -STR_NETWORK_LANG_BULGARIAN :Βουλγαρικά -STR_NETWORK_LANG_CHINESE :Κινέζικα -STR_NETWORK_LANG_CZECH :Τσεχικά -STR_NETWORK_LANG_DANISH :Δανικά -STR_NETWORK_LANG_DUTCH :Ολλανδικά -STR_NETWORK_LANG_ESPERANTO :Εσπεράντο -STR_NETWORK_LANG_FINNISH :Φιλανδικά -STR_NETWORK_LANG_HUNGARIAN :Ουγγρικά -STR_NETWORK_LANG_ICELANDIC :Ισλανδικά -STR_NETWORK_LANG_ITALIAN :Ιταλικά -STR_NETWORK_LANG_JAPANESE :Ιαπωνικά -STR_NETWORK_LANG_KOREAN :Κορεατικά -STR_NETWORK_LANG_LITHUANIAN :Λιθουανικά -STR_NETWORK_LANG_NORWEGIAN :Νορβηγικά -STR_NETWORK_LANG_POLISH :Πολωνικά -STR_NETWORK_LANG_PORTUGUESE :Πορτογαλικά -STR_NETWORK_LANG_ROMANIAN :Ρουμανικά -STR_NETWORK_LANG_RUSSIAN :Ρωσικά -STR_NETWORK_LANG_SLOVAK :Σλοβακικά -STR_NETWORK_LANG_SLOVENIAN :Σλοβενικά -STR_NETWORK_LANG_SPANISH :Ισπανικά -STR_NETWORK_LANG_SWEDISH :Σουηδικά -STR_NETWORK_LANG_TURKISH :Τουρκικά -STR_NETWORK_LANG_UKRAINIAN :Ουκρανικά -STR_NETWORK_LANG_AFRIKAANS :Αφρικάνς -STR_NETWORK_LANG_CROATIAN :Κροατικά -STR_NETWORK_LANG_CATALAN :Καταλανικά -STR_NETWORK_LANG_ESTONIAN :Εσθονικά -STR_NETWORK_LANG_GALICIAN :Γαλικιανά -STR_NETWORK_LANG_GREEK :Ελληνικά -STR_NETWORK_LANG_LATVIAN :Λετονικά -############ End of leave-in-this-order - # Network game lobby STR_NETWORK_GAME_LOBBY_CAPTION :{WHITE}Αίθουσα παιχνιδιού για πολλούς παίκτες diff --git a/src/lang/hebrew.txt b/src/lang/hebrew.txt index ba5d417ab0..c36119dd48 100644 --- a/src/lang/hebrew.txt +++ b/src/lang/hebrew.txt @@ -1971,46 +1971,6 @@ STR_NETWORK_START_SERVER_LANGUAGE_TOOLTIP :{BLACK}שחקנ STR_NETWORK_START_SERVER_NEW_GAME_NAME_OSKTITLE :{BLACK}הזן שם למשחק רשת -# Network game languages -############ Leave those lines in this order!! -STR_NETWORK_LANG_ANY :כלשהו -STR_NETWORK_LANG_ENGLISH :אנגלית -STR_NETWORK_LANG_GERMAN :גרמנית -STR_NETWORK_LANG_FRENCH :צרפתית -STR_NETWORK_LANG_BRAZILIAN :ברזילאית -STR_NETWORK_LANG_BULGARIAN :בולגרית -STR_NETWORK_LANG_CHINESE :סינית -STR_NETWORK_LANG_CZECH :צ'כית -STR_NETWORK_LANG_DANISH :דנית -STR_NETWORK_LANG_DUTCH :הולנדית -STR_NETWORK_LANG_ESPERANTO :אספרנטו -STR_NETWORK_LANG_FINNISH :פינית -STR_NETWORK_LANG_HUNGARIAN :הונגרית -STR_NETWORK_LANG_ICELANDIC :איסלנדית -STR_NETWORK_LANG_ITALIAN :איטלקית -STR_NETWORK_LANG_JAPANESE :יפנית -STR_NETWORK_LANG_KOREAN :קוריאנית -STR_NETWORK_LANG_LITHUANIAN :לטבית -STR_NETWORK_LANG_NORWEGIAN :נורבגית -STR_NETWORK_LANG_POLISH :פולנית -STR_NETWORK_LANG_PORTUGUESE :פורטוגזית -STR_NETWORK_LANG_ROMANIAN :רומנית -STR_NETWORK_LANG_RUSSIAN :רוסית -STR_NETWORK_LANG_SLOVAK :סלובקית -STR_NETWORK_LANG_SLOVENIAN :סלובנית -STR_NETWORK_LANG_SPANISH :ספרדית -STR_NETWORK_LANG_SWEDISH :שבדית -STR_NETWORK_LANG_TURKISH :טורקית -STR_NETWORK_LANG_UKRAINIAN :אוקראינית -STR_NETWORK_LANG_AFRIKAANS :אפריקנית -STR_NETWORK_LANG_CROATIAN :קרואטית -STR_NETWORK_LANG_CATALAN :קטלונית -STR_NETWORK_LANG_ESTONIAN :אסטונית -STR_NETWORK_LANG_GALICIAN :גליציאנית -STR_NETWORK_LANG_GREEK :יוונית -STR_NETWORK_LANG_LATVIAN :לטבית -############ End of leave-in-this-order - # Network game lobby STR_NETWORK_GAME_LOBBY_CAPTION :{WHITE}לובי של משחק רשת diff --git a/src/lang/hungarian.txt b/src/lang/hungarian.txt index ca6483c5b3..cba2a6aef0 100644 --- a/src/lang/hungarian.txt +++ b/src/lang/hungarian.txt @@ -2134,46 +2134,6 @@ STR_NETWORK_START_SERVER_LANGUAGE_TOOLTIP :{BLACK}A többi STR_NETWORK_START_SERVER_NEW_GAME_NAME_OSKTITLE :{BLACK}Add meg a hálózati játékhoz a neved -# Network game languages -############ Leave those lines in this order!! -STR_NETWORK_LANG_ANY :Bármilyen -STR_NETWORK_LANG_ENGLISH :Angol -STR_NETWORK_LANG_GERMAN :Német -STR_NETWORK_LANG_FRENCH :Francia -STR_NETWORK_LANG_BRAZILIAN :Brazil -STR_NETWORK_LANG_BULGARIAN :Bolgár -STR_NETWORK_LANG_CHINESE :Kínai -STR_NETWORK_LANG_CZECH :Cseh -STR_NETWORK_LANG_DANISH :Dán -STR_NETWORK_LANG_DUTCH :Holland -STR_NETWORK_LANG_ESPERANTO :Eszperantó -STR_NETWORK_LANG_FINNISH :Finn -STR_NETWORK_LANG_HUNGARIAN :Magyar -STR_NETWORK_LANG_ICELANDIC :Izlandi -STR_NETWORK_LANG_ITALIAN :Olasz -STR_NETWORK_LANG_JAPANESE :Japán -STR_NETWORK_LANG_KOREAN :Koreai -STR_NETWORK_LANG_LITHUANIAN :Litván -STR_NETWORK_LANG_NORWEGIAN :Norvég -STR_NETWORK_LANG_POLISH :Lengyel -STR_NETWORK_LANG_PORTUGUESE :Portugál -STR_NETWORK_LANG_ROMANIAN :Román -STR_NETWORK_LANG_RUSSIAN :Orosz -STR_NETWORK_LANG_SLOVAK :Szlovák -STR_NETWORK_LANG_SLOVENIAN :Szlovén -STR_NETWORK_LANG_SPANISH :Spanyol -STR_NETWORK_LANG_SWEDISH :Svéd -STR_NETWORK_LANG_TURKISH :Török -STR_NETWORK_LANG_UKRAINIAN :Ukrán -STR_NETWORK_LANG_AFRIKAANS :Afrikai -STR_NETWORK_LANG_CROATIAN :Horvát -STR_NETWORK_LANG_CATALAN :Katalán -STR_NETWORK_LANG_ESTONIAN :Észt -STR_NETWORK_LANG_GALICIAN :Gall -STR_NETWORK_LANG_GREEK :Görög -STR_NETWORK_LANG_LATVIAN :Lett -############ End of leave-in-this-order - # Network game lobby STR_NETWORK_GAME_LOBBY_CAPTION :{WHITE}Hálózati játék előszoba diff --git a/src/lang/icelandic.txt b/src/lang/icelandic.txt index 778c072250..47a4d268bc 100644 --- a/src/lang/icelandic.txt +++ b/src/lang/icelandic.txt @@ -1806,46 +1806,6 @@ STR_NETWORK_START_SERVER_LANGUAGE_TOOLTIP :{BLACK}Aðrir n STR_NETWORK_START_SERVER_NEW_GAME_NAME_OSKTITLE :{BLACK}Sláðu inn nafn netleiksins -# Network game languages -############ Leave those lines in this order!! -STR_NETWORK_LANG_ANY :Hvað sem er -STR_NETWORK_LANG_ENGLISH :Enska -STR_NETWORK_LANG_GERMAN :Þýska -STR_NETWORK_LANG_FRENCH :Franska -STR_NETWORK_LANG_BRAZILIAN :Brasilíska -STR_NETWORK_LANG_BULGARIAN :Búlgarska -STR_NETWORK_LANG_CHINESE :Kínverska -STR_NETWORK_LANG_CZECH :Tékkneska -STR_NETWORK_LANG_DANISH :Danska -STR_NETWORK_LANG_DUTCH :Hollenska -STR_NETWORK_LANG_ESPERANTO :Esperanto -STR_NETWORK_LANG_FINNISH :Finnska -STR_NETWORK_LANG_HUNGARIAN :Ungverska -STR_NETWORK_LANG_ICELANDIC :Íslenska -STR_NETWORK_LANG_ITALIAN :Ítalska -STR_NETWORK_LANG_JAPANESE :Japanska -STR_NETWORK_LANG_KOREAN :Kóreiska -STR_NETWORK_LANG_LITHUANIAN :Litháenska -STR_NETWORK_LANG_NORWEGIAN :Norska -STR_NETWORK_LANG_POLISH :Pólska -STR_NETWORK_LANG_PORTUGUESE :Portúgalska -STR_NETWORK_LANG_ROMANIAN :Rómanska -STR_NETWORK_LANG_RUSSIAN :Rússneska -STR_NETWORK_LANG_SLOVAK :Slóvakíska -STR_NETWORK_LANG_SLOVENIAN :Slóveska -STR_NETWORK_LANG_SPANISH :Spænska -STR_NETWORK_LANG_SWEDISH :Sænska -STR_NETWORK_LANG_TURKISH :Tyrkneska -STR_NETWORK_LANG_UKRAINIAN :Úkraínska -STR_NETWORK_LANG_AFRIKAANS :Afríkanska -STR_NETWORK_LANG_CROATIAN :Króatíska -STR_NETWORK_LANG_CATALAN :Katalónska -STR_NETWORK_LANG_ESTONIAN :Eistneska -STR_NETWORK_LANG_GALICIAN :Gelíska -STR_NETWORK_LANG_GREEK :Gríska -STR_NETWORK_LANG_LATVIAN :Lettneska -############ End of leave-in-this-order - # Network game lobby STR_NETWORK_GAME_LOBBY_CAPTION :{WHITE}Anddyri fjölspilunarleiks diff --git a/src/lang/indonesian.txt b/src/lang/indonesian.txt index 20ee7ef74d..a6401981a2 100644 --- a/src/lang/indonesian.txt +++ b/src/lang/indonesian.txt @@ -2047,46 +2047,6 @@ STR_NETWORK_START_SERVER_LANGUAGE_TOOLTIP :{BLACK}Agar pem STR_NETWORK_START_SERVER_NEW_GAME_NAME_OSKTITLE :{BLACK}Masukkan nama dari permainan di jaringan -# Network game languages -############ Leave those lines in this order!! -STR_NETWORK_LANG_ANY :Segala bahasa -STR_NETWORK_LANG_ENGLISH :Inggris -STR_NETWORK_LANG_GERMAN :Jerman -STR_NETWORK_LANG_FRENCH :Perancis -STR_NETWORK_LANG_BRAZILIAN :Brazil -STR_NETWORK_LANG_BULGARIAN :Bulgaria -STR_NETWORK_LANG_CHINESE :China -STR_NETWORK_LANG_CZECH :Ceko -STR_NETWORK_LANG_DANISH :Denmark -STR_NETWORK_LANG_DUTCH :Belanda -STR_NETWORK_LANG_ESPERANTO :Esperanto -STR_NETWORK_LANG_FINNISH :Finlandia -STR_NETWORK_LANG_HUNGARIAN :Hungaria -STR_NETWORK_LANG_ICELANDIC :Islandia -STR_NETWORK_LANG_ITALIAN :Italia -STR_NETWORK_LANG_JAPANESE :Jepang -STR_NETWORK_LANG_KOREAN :Korea -STR_NETWORK_LANG_LITHUANIAN :Lithuania -STR_NETWORK_LANG_NORWEGIAN :Norwegia -STR_NETWORK_LANG_POLISH :Polandia -STR_NETWORK_LANG_PORTUGUESE :Portugis -STR_NETWORK_LANG_ROMANIAN :Rumania -STR_NETWORK_LANG_RUSSIAN :Rusia -STR_NETWORK_LANG_SLOVAK :Slovakia -STR_NETWORK_LANG_SLOVENIAN :Slovenia -STR_NETWORK_LANG_SPANISH :Spanyol -STR_NETWORK_LANG_SWEDISH :Swedia -STR_NETWORK_LANG_TURKISH :Turki -STR_NETWORK_LANG_UKRAINIAN :Ukrainia -STR_NETWORK_LANG_AFRIKAANS :Afrika -STR_NETWORK_LANG_CROATIAN :Kroasia -STR_NETWORK_LANG_CATALAN :Catalan -STR_NETWORK_LANG_ESTONIAN :Estonia -STR_NETWORK_LANG_GALICIAN :Galisia -STR_NETWORK_LANG_GREEK :Yunani -STR_NETWORK_LANG_LATVIAN :Latvia -############ End of leave-in-this-order - # Network game lobby STR_NETWORK_GAME_LOBBY_CAPTION :{WHITE}Lobi Bermain bersama diff --git a/src/lang/irish.txt b/src/lang/irish.txt index a6b192cf7f..18e4bc2df2 100644 --- a/src/lang/irish.txt +++ b/src/lang/irish.txt @@ -1945,46 +1945,6 @@ STR_NETWORK_START_SERVER_LANGUAGE_TOOLTIP :{BLACK}Beidh a STR_NETWORK_START_SERVER_NEW_GAME_NAME_OSKTITLE :{BLACK}Iontráil ainm don chluiche líonra -# Network game languages -############ Leave those lines in this order!! -STR_NETWORK_LANG_ANY :Teanga ar bith -STR_NETWORK_LANG_ENGLISH :Béarla -STR_NETWORK_LANG_GERMAN :Gearmáinis -STR_NETWORK_LANG_FRENCH :Fraincis -STR_NETWORK_LANG_BRAZILIAN :Brasaílis -STR_NETWORK_LANG_BULGARIAN :Bulgáiris -STR_NETWORK_LANG_CHINESE :Sínis -STR_NETWORK_LANG_CZECH :Seicis -STR_NETWORK_LANG_DANISH :Danmhairgis -STR_NETWORK_LANG_DUTCH :Ollainnis -STR_NETWORK_LANG_ESPERANTO :Esperanto -STR_NETWORK_LANG_FINNISH :Fionlainnis -STR_NETWORK_LANG_HUNGARIAN :Ungáiris -STR_NETWORK_LANG_ICELANDIC :Íoslainnis -STR_NETWORK_LANG_ITALIAN :Iodáilis -STR_NETWORK_LANG_JAPANESE :Seapáinis -STR_NETWORK_LANG_KOREAN :Cóiréis -STR_NETWORK_LANG_LITHUANIAN :Liotuáinis -STR_NETWORK_LANG_NORWEGIAN :Ioruais -STR_NETWORK_LANG_POLISH :Polainnis -STR_NETWORK_LANG_PORTUGUESE :Portaingéilis -STR_NETWORK_LANG_ROMANIAN :Rómáinis -STR_NETWORK_LANG_RUSSIAN :Rúisis -STR_NETWORK_LANG_SLOVAK :Slóvaicis -STR_NETWORK_LANG_SLOVENIAN :Slóivéinis -STR_NETWORK_LANG_SPANISH :Spáinnis -STR_NETWORK_LANG_SWEDISH :Sualainnis -STR_NETWORK_LANG_TURKISH :Tuircis -STR_NETWORK_LANG_UKRAINIAN :Úcráinis -STR_NETWORK_LANG_AFRIKAANS :Afracáinis -STR_NETWORK_LANG_CROATIAN :Cróitis -STR_NETWORK_LANG_CATALAN :Catalóinis -STR_NETWORK_LANG_ESTONIAN :Eastóinis -STR_NETWORK_LANG_GALICIAN :Gailísis -STR_NETWORK_LANG_GREEK :Gréigis -STR_NETWORK_LANG_LATVIAN :Laitvis -############ End of leave-in-this-order - # Network game lobby STR_NETWORK_GAME_LOBBY_CAPTION :{WHITE}Forsheomra cluichí ilimreoirí diff --git a/src/lang/italian.txt b/src/lang/italian.txt index 2627a7b6ce..9cad946207 100644 --- a/src/lang/italian.txt +++ b/src/lang/italian.txt @@ -2085,46 +2085,6 @@ STR_NETWORK_START_SERVER_LANGUAGE_TOOLTIP :{BLACK}Fa saper STR_NETWORK_START_SERVER_NEW_GAME_NAME_OSKTITLE :{BLACK}Inserire il nome della partita -# Network game languages -############ Leave those lines in this order!! -STR_NETWORK_LANG_ANY :Qualsiasi -STR_NETWORK_LANG_ENGLISH :Inglese -STR_NETWORK_LANG_GERMAN :Tedesco -STR_NETWORK_LANG_FRENCH :Francese -STR_NETWORK_LANG_BRAZILIAN :Brasiliano -STR_NETWORK_LANG_BULGARIAN :Bùlgaro -STR_NETWORK_LANG_CHINESE :Cinese -STR_NETWORK_LANG_CZECH :Ceco -STR_NETWORK_LANG_DANISH :Danese -STR_NETWORK_LANG_DUTCH :Olandese -STR_NETWORK_LANG_ESPERANTO :Esperanto -STR_NETWORK_LANG_FINNISH :Finlandese -STR_NETWORK_LANG_HUNGARIAN :Ungherese -STR_NETWORK_LANG_ICELANDIC :Islandese -STR_NETWORK_LANG_ITALIAN :Italiano -STR_NETWORK_LANG_JAPANESE :Giapponese -STR_NETWORK_LANG_KOREAN :Coreano -STR_NETWORK_LANG_LITHUANIAN :Lituano -STR_NETWORK_LANG_NORWEGIAN :Norvegese -STR_NETWORK_LANG_POLISH :Polacco -STR_NETWORK_LANG_PORTUGUESE :Portoghese -STR_NETWORK_LANG_ROMANIAN :Rumeno -STR_NETWORK_LANG_RUSSIAN :Russo -STR_NETWORK_LANG_SLOVAK :Slovacco -STR_NETWORK_LANG_SLOVENIAN :Sloveno -STR_NETWORK_LANG_SPANISH :Spagnolo -STR_NETWORK_LANG_SWEDISH :Svedese -STR_NETWORK_LANG_TURKISH :Turco -STR_NETWORK_LANG_UKRAINIAN :Ucraino -STR_NETWORK_LANG_AFRIKAANS :Afrikaans -STR_NETWORK_LANG_CROATIAN :Croato -STR_NETWORK_LANG_CATALAN :Catalano -STR_NETWORK_LANG_ESTONIAN :Estone -STR_NETWORK_LANG_GALICIAN :Galiziano -STR_NETWORK_LANG_GREEK :Greco -STR_NETWORK_LANG_LATVIAN :Lèttone -############ End of leave-in-this-order - # Network game lobby STR_NETWORK_GAME_LOBBY_CAPTION :{WHITE}Stanza principale partita multigiocatore diff --git a/src/lang/japanese.txt b/src/lang/japanese.txt index 228b16dffb..d9c676f1e2 100644 --- a/src/lang/japanese.txt +++ b/src/lang/japanese.txt @@ -1989,46 +1989,6 @@ STR_NETWORK_START_SERVER_LANGUAGE_TOOLTIP :{BLACK}この STR_NETWORK_START_SERVER_NEW_GAME_NAME_OSKTITLE :{BLACK}ネットワークゲーム名を入力 -# Network game languages -############ Leave those lines in this order!! -STR_NETWORK_LANG_ANY :言語未指定 -STR_NETWORK_LANG_ENGLISH :英語 -STR_NETWORK_LANG_GERMAN :ドイツ語 -STR_NETWORK_LANG_FRENCH :フランス語 -STR_NETWORK_LANG_BRAZILIAN :ポルトガル語(ブラジル) -STR_NETWORK_LANG_BULGARIAN :ブルガリア語 -STR_NETWORK_LANG_CHINESE :中国語 -STR_NETWORK_LANG_CZECH :チェコ語 -STR_NETWORK_LANG_DANISH :デンマーク語 -STR_NETWORK_LANG_DUTCH :オランダ語 -STR_NETWORK_LANG_ESPERANTO :エスペラント語 -STR_NETWORK_LANG_FINNISH :フィンランド語 -STR_NETWORK_LANG_HUNGARIAN :ハンガリー語 -STR_NETWORK_LANG_ICELANDIC :アイスランド語 -STR_NETWORK_LANG_ITALIAN :イタリア語 -STR_NETWORK_LANG_JAPANESE :日本語 -STR_NETWORK_LANG_KOREAN :韓国語 -STR_NETWORK_LANG_LITHUANIAN :リトアニア語 -STR_NETWORK_LANG_NORWEGIAN :ノルウェイ語 -STR_NETWORK_LANG_POLISH :ポーランド語 -STR_NETWORK_LANG_PORTUGUESE :ポルトガル語 -STR_NETWORK_LANG_ROMANIAN :ルーマニア語 -STR_NETWORK_LANG_RUSSIAN :ロシア語 -STR_NETWORK_LANG_SLOVAK :スロバキア語 -STR_NETWORK_LANG_SLOVENIAN :スロベニア語 -STR_NETWORK_LANG_SPANISH :スペイン語 -STR_NETWORK_LANG_SWEDISH :スウェーデン語 -STR_NETWORK_LANG_TURKISH :トルコ語 -STR_NETWORK_LANG_UKRAINIAN :ウクライナ語 -STR_NETWORK_LANG_AFRIKAANS :アフリカーンス語 -STR_NETWORK_LANG_CROATIAN :クロアチア語 -STR_NETWORK_LANG_CATALAN :カタロニア語 -STR_NETWORK_LANG_ESTONIAN :エストニア語 -STR_NETWORK_LANG_GALICIAN :ガリシア語 -STR_NETWORK_LANG_GREEK :ギリシア語 -STR_NETWORK_LANG_LATVIAN :ラトビア語 -############ End of leave-in-this-order - # Network game lobby STR_NETWORK_GAME_LOBBY_CAPTION :{WHITE}マルチプレイヤーゲーム ロビー diff --git a/src/lang/korean.txt b/src/lang/korean.txt index d6efaa457f..226d10c89a 100644 --- a/src/lang/korean.txt +++ b/src/lang/korean.txt @@ -2071,46 +2071,6 @@ STR_NETWORK_START_SERVER_LANGUAGE_TOOLTIP :{BLACK}서버 STR_NETWORK_START_SERVER_NEW_GAME_NAME_OSKTITLE :{BLACK}네트워크 게임에서 사용할 이름을 입력하세요 -# Network game languages -############ Leave those lines in this order!! -STR_NETWORK_LANG_ANY :모든 언어 -STR_NETWORK_LANG_ENGLISH :영어 -STR_NETWORK_LANG_GERMAN :독일어 -STR_NETWORK_LANG_FRENCH :프랑스어 -STR_NETWORK_LANG_BRAZILIAN :브라질어 -STR_NETWORK_LANG_BULGARIAN :불가리아어 -STR_NETWORK_LANG_CHINESE :중국어 -STR_NETWORK_LANG_CZECH :체코어 -STR_NETWORK_LANG_DANISH :덴마크어 -STR_NETWORK_LANG_DUTCH :네덜란드어 -STR_NETWORK_LANG_ESPERANTO :에스페란토 -STR_NETWORK_LANG_FINNISH :핀란드어 -STR_NETWORK_LANG_HUNGARIAN :헝가리어 -STR_NETWORK_LANG_ICELANDIC :아이슬란드어 -STR_NETWORK_LANG_ITALIAN :이탈리아어 -STR_NETWORK_LANG_JAPANESE :일본어 -STR_NETWORK_LANG_KOREAN :한국어 -STR_NETWORK_LANG_LITHUANIAN :리투아니아어 -STR_NETWORK_LANG_NORWEGIAN :노르웨이어 -STR_NETWORK_LANG_POLISH :폴란드어 -STR_NETWORK_LANG_PORTUGUESE :포르투갈어 -STR_NETWORK_LANG_ROMANIAN :루마니아어 -STR_NETWORK_LANG_RUSSIAN :러시아어 -STR_NETWORK_LANG_SLOVAK :슬로바키아어 -STR_NETWORK_LANG_SLOVENIAN :슬로베니아어 -STR_NETWORK_LANG_SPANISH :스페인어 -STR_NETWORK_LANG_SWEDISH :스웨덴어 -STR_NETWORK_LANG_TURKISH :터키어 -STR_NETWORK_LANG_UKRAINIAN :우크라이나어 -STR_NETWORK_LANG_AFRIKAANS :아프리카어 -STR_NETWORK_LANG_CROATIAN :크로아티아어 -STR_NETWORK_LANG_CATALAN :카탈로니아어 -STR_NETWORK_LANG_ESTONIAN :에스토니아어 -STR_NETWORK_LANG_GALICIAN :갈리시아어 -STR_NETWORK_LANG_GREEK :그리스어 -STR_NETWORK_LANG_LATVIAN :라트비아어 -############ End of leave-in-this-order - # Network game lobby STR_NETWORK_GAME_LOBBY_CAPTION :{WHITE}멀티플레이 게임 대기실 diff --git a/src/lang/latin.txt b/src/lang/latin.txt index bb12707a3c..a6eeb3cca2 100644 --- a/src/lang/latin.txt +++ b/src/lang/latin.txt @@ -2164,46 +2164,6 @@ STR_NETWORK_START_SERVER_LANGUAGE_TOOLTIP :{BLACK}Lingua i STR_NETWORK_START_SERVER_NEW_GAME_NAME_OSKTITLE :{BLACK}Inscribe nomen ludi retis -# Network game languages -############ Leave those lines in this order!! -STR_NETWORK_LANG_ANY :Quaevis -STR_NETWORK_LANG_ENGLISH :Anglica -STR_NETWORK_LANG_GERMAN :Theodisca -STR_NETWORK_LANG_FRENCH :Francogallica -STR_NETWORK_LANG_BRAZILIAN :Brasilica -STR_NETWORK_LANG_BULGARIAN :Bulgarica -STR_NETWORK_LANG_CHINESE :Sinica -STR_NETWORK_LANG_CZECH :Cecha -STR_NETWORK_LANG_DANISH :Danica -STR_NETWORK_LANG_DUTCH :Batavica -STR_NETWORK_LANG_ESPERANTO :Esperantica -STR_NETWORK_LANG_FINNISH :Finnica -STR_NETWORK_LANG_HUNGARIAN :Hungarica -STR_NETWORK_LANG_ICELANDIC :Islandica -STR_NETWORK_LANG_ITALIAN :Italiana -STR_NETWORK_LANG_JAPANESE :Iaponica -STR_NETWORK_LANG_KOREAN :Coreana -STR_NETWORK_LANG_LITHUANIAN :Lithuanica -STR_NETWORK_LANG_NORWEGIAN :Norvegica -STR_NETWORK_LANG_POLISH :Polonica -STR_NETWORK_LANG_PORTUGUESE :Lusitana -STR_NETWORK_LANG_ROMANIAN :Romanica -STR_NETWORK_LANG_RUSSIAN :Russica -STR_NETWORK_LANG_SLOVAK :Slovaca -STR_NETWORK_LANG_SLOVENIAN :Slovena -STR_NETWORK_LANG_SPANISH :Hispanica -STR_NETWORK_LANG_SWEDISH :Suecica -STR_NETWORK_LANG_TURKISH :Turcica -STR_NETWORK_LANG_UKRAINIAN :Ucrainica -STR_NETWORK_LANG_AFRIKAANS :Africana -STR_NETWORK_LANG_CROATIAN :Croatica -STR_NETWORK_LANG_CATALAN :Catalana -STR_NETWORK_LANG_ESTONIAN :Estonica -STR_NETWORK_LANG_GALICIAN :Gallaica -STR_NETWORK_LANG_GREEK :Graeca -STR_NETWORK_LANG_LATVIAN :Lettonica -############ End of leave-in-this-order - # Network game lobby STR_NETWORK_GAME_LOBBY_CAPTION :{WHITE}Atrium Plurium Lusorum diff --git a/src/lang/latvian.txt b/src/lang/latvian.txt index 213635648b..f1a2888642 100644 --- a/src/lang/latvian.txt +++ b/src/lang/latvian.txt @@ -2040,46 +2040,6 @@ STR_NETWORK_START_SERVER_LANGUAGE_TOOLTIP :{BLACK}Citi lie STR_NETWORK_START_SERVER_NEW_GAME_NAME_OSKTITLE :{BLACK}Ievadīt tīkla spēles nosaukumu -# Network game languages -############ Leave those lines in this order!! -STR_NETWORK_LANG_ANY :Jebkura -STR_NETWORK_LANG_ENGLISH :Angļu -STR_NETWORK_LANG_GERMAN :Vācu -STR_NETWORK_LANG_FRENCH :Franču -STR_NETWORK_LANG_BRAZILIAN :Brazīļu -STR_NETWORK_LANG_BULGARIAN :Bulgāru -STR_NETWORK_LANG_CHINESE :Ķīniešu -STR_NETWORK_LANG_CZECH :Čehu -STR_NETWORK_LANG_DANISH :Dāņu -STR_NETWORK_LANG_DUTCH :Holandiešu -STR_NETWORK_LANG_ESPERANTO :Esperanto -STR_NETWORK_LANG_FINNISH :Somu -STR_NETWORK_LANG_HUNGARIAN :Ungāru -STR_NETWORK_LANG_ICELANDIC :Īslandiešu -STR_NETWORK_LANG_ITALIAN :Itāliešu -STR_NETWORK_LANG_JAPANESE :Japāņu -STR_NETWORK_LANG_KOREAN :Korejiešu -STR_NETWORK_LANG_LITHUANIAN :Lietuviešu -STR_NETWORK_LANG_NORWEGIAN :Norvēģu -STR_NETWORK_LANG_POLISH :Poļu -STR_NETWORK_LANG_PORTUGUESE :Portugāļu -STR_NETWORK_LANG_ROMANIAN :Rumāņu -STR_NETWORK_LANG_RUSSIAN :Krievu -STR_NETWORK_LANG_SLOVAK :Slovāku -STR_NETWORK_LANG_SLOVENIAN :Slovēņu -STR_NETWORK_LANG_SPANISH :Spāņu -STR_NETWORK_LANG_SWEDISH :Zviedru -STR_NETWORK_LANG_TURKISH :Turku -STR_NETWORK_LANG_UKRAINIAN :Ukraiņu -STR_NETWORK_LANG_AFRIKAANS :Āfrikāņu -STR_NETWORK_LANG_CROATIAN :Horvātu -STR_NETWORK_LANG_CATALAN :Kataloņu -STR_NETWORK_LANG_ESTONIAN :Igauņu -STR_NETWORK_LANG_GALICIAN :Galisiešu -STR_NETWORK_LANG_GREEK :Grieķu -STR_NETWORK_LANG_LATVIAN :Latviešu -############ End of leave-in-this-order - # Network game lobby STR_NETWORK_GAME_LOBBY_CAPTION :{WHITE}Vairākspēlētāju spēles vestibils diff --git a/src/lang/lithuanian.txt b/src/lang/lithuanian.txt index a1e9e63cc7..e5816348c2 100644 --- a/src/lang/lithuanian.txt +++ b/src/lang/lithuanian.txt @@ -2284,46 +2284,6 @@ STR_NETWORK_START_SERVER_LANGUAGE_TOOLTIP :{BLACK}Kiti ža STR_NETWORK_START_SERVER_NEW_GAME_NAME_OSKTITLE :{BLACK}Įveskite tinklo žaidimo pavadinimą -# Network game languages -############ Leave those lines in this order!! -STR_NETWORK_LANG_ANY :Bet kokia -STR_NETWORK_LANG_ENGLISH :Anglų -STR_NETWORK_LANG_GERMAN :Vokiečių -STR_NETWORK_LANG_FRENCH :Prancūzų -STR_NETWORK_LANG_BRAZILIAN :Brazilų -STR_NETWORK_LANG_BULGARIAN :Bulgarų -STR_NETWORK_LANG_CHINESE :Kinų -STR_NETWORK_LANG_CZECH :Čekų -STR_NETWORK_LANG_DANISH :Danų -STR_NETWORK_LANG_DUTCH :Olandų -STR_NETWORK_LANG_ESPERANTO :Esperanto -STR_NETWORK_LANG_FINNISH :Suomių -STR_NETWORK_LANG_HUNGARIAN :Vengrų -STR_NETWORK_LANG_ICELANDIC :Islandų -STR_NETWORK_LANG_ITALIAN :Italų -STR_NETWORK_LANG_JAPANESE :Japonų -STR_NETWORK_LANG_KOREAN :Korėjiečių -STR_NETWORK_LANG_LITHUANIAN :Lietuvių -STR_NETWORK_LANG_NORWEGIAN :Norvegų -STR_NETWORK_LANG_POLISH :Lenkų -STR_NETWORK_LANG_PORTUGUESE :Portugalų -STR_NETWORK_LANG_ROMANIAN :Rumunų -STR_NETWORK_LANG_RUSSIAN :Rusų -STR_NETWORK_LANG_SLOVAK :Slovakų -STR_NETWORK_LANG_SLOVENIAN :Slovėnų -STR_NETWORK_LANG_SPANISH :Ispanų -STR_NETWORK_LANG_SWEDISH :Švedų -STR_NETWORK_LANG_TURKISH :Turkų -STR_NETWORK_LANG_UKRAINIAN :Ukrainiečių -STR_NETWORK_LANG_AFRIKAANS :Afrikiečių -STR_NETWORK_LANG_CROATIAN :Kroatų -STR_NETWORK_LANG_CATALAN :Katalonų -STR_NETWORK_LANG_ESTONIAN :Estų -STR_NETWORK_LANG_GALICIAN :Galicijos -STR_NETWORK_LANG_GREEK :Graikų -STR_NETWORK_LANG_LATVIAN :Latvių -############ End of leave-in-this-order - # Network game lobby STR_NETWORK_GAME_LOBBY_CAPTION :{WHITE}Kelių žaidėjų meniu diff --git a/src/lang/luxembourgish.txt b/src/lang/luxembourgish.txt index 688daa891b..a5c21ae4a0 100644 --- a/src/lang/luxembourgish.txt +++ b/src/lang/luxembourgish.txt @@ -2065,46 +2065,6 @@ STR_NETWORK_START_SERVER_LANGUAGE_TOOLTIP :{BLACK}Aner Lei STR_NETWORK_START_SERVER_NEW_GAME_NAME_OSKTITLE :{BLACK}Gëff en Numm fir d'Spill un -# Network game languages -############ Leave those lines in this order!! -STR_NETWORK_LANG_ANY :Egal -STR_NETWORK_LANG_ENGLISH :Englesch -STR_NETWORK_LANG_GERMAN :Däitsch -STR_NETWORK_LANG_FRENCH :Franséisch -STR_NETWORK_LANG_BRAZILIAN :Brasilianesch -STR_NETWORK_LANG_BULGARIAN :Bulgaresch -STR_NETWORK_LANG_CHINESE :Chinesesch -STR_NETWORK_LANG_CZECH :Tschechesch -STR_NETWORK_LANG_DANISH :Dänesch -STR_NETWORK_LANG_DUTCH :Holländesch -STR_NETWORK_LANG_ESPERANTO :Esperanto -STR_NETWORK_LANG_FINNISH :Finnesch -STR_NETWORK_LANG_HUNGARIAN :Ungaresch -STR_NETWORK_LANG_ICELANDIC :Isländesch -STR_NETWORK_LANG_ITALIAN :Italienesch -STR_NETWORK_LANG_JAPANESE :Japanesch -STR_NETWORK_LANG_KOREAN :Koreanesch -STR_NETWORK_LANG_LITHUANIAN :Litauesch -STR_NETWORK_LANG_NORWEGIAN :Norwegesch -STR_NETWORK_LANG_POLISH :Polnesch -STR_NETWORK_LANG_PORTUGUESE :Portugiesesch -STR_NETWORK_LANG_ROMANIAN :Rumänesch -STR_NETWORK_LANG_RUSSIAN :Russesch -STR_NETWORK_LANG_SLOVAK :Slovakesch -STR_NETWORK_LANG_SLOVENIAN :Slovenesch -STR_NETWORK_LANG_SPANISH :Spuenesch -STR_NETWORK_LANG_SWEDISH :Schwedesch -STR_NETWORK_LANG_TURKISH :Türkesch -STR_NETWORK_LANG_UKRAINIAN :Ukrainesch -STR_NETWORK_LANG_AFRIKAANS :Afrikaans -STR_NETWORK_LANG_CROATIAN :Kroatesch -STR_NETWORK_LANG_CATALAN :Katalanesch -STR_NETWORK_LANG_ESTONIAN :Estnesch -STR_NETWORK_LANG_GALICIAN :Gälesch -STR_NETWORK_LANG_GREEK :Griechesch -STR_NETWORK_LANG_LATVIAN :Lettesch -############ End of leave-in-this-order - # Network game lobby STR_NETWORK_GAME_LOBBY_CAPTION :{WHITE}Multiplayer-Spill Lobby diff --git a/src/lang/malay.txt b/src/lang/malay.txt index 03b92a0323..76a99e9b06 100644 --- a/src/lang/malay.txt +++ b/src/lang/malay.txt @@ -1704,46 +1704,6 @@ STR_NETWORK_START_SERVER_LANGUAGE_TOOLTIP :{BLACK}Pemain l STR_NETWORK_START_SERVER_NEW_GAME_NAME_OSKTITLE :{BLACK}Masukkan nama untuk permainan rangkaian -# Network game languages -############ Leave those lines in this order!! -STR_NETWORK_LANG_ANY :Mana-mana -STR_NETWORK_LANG_ENGLISH :Inggeris -STR_NETWORK_LANG_GERMAN :Jerman -STR_NETWORK_LANG_FRENCH :Perancis -STR_NETWORK_LANG_BRAZILIAN :Brazil -STR_NETWORK_LANG_BULGARIAN :Bulgaria -STR_NETWORK_LANG_CHINESE :Cina -STR_NETWORK_LANG_CZECH :Czech -STR_NETWORK_LANG_DANISH :Denmark -STR_NETWORK_LANG_DUTCH :Belanda -STR_NETWORK_LANG_ESPERANTO :Esperanto -STR_NETWORK_LANG_FINNISH :Finland -STR_NETWORK_LANG_HUNGARIAN :Hungary -STR_NETWORK_LANG_ICELANDIC :Iceland -STR_NETWORK_LANG_ITALIAN :Itali -STR_NETWORK_LANG_JAPANESE :Jepun -STR_NETWORK_LANG_KOREAN :Korea -STR_NETWORK_LANG_LITHUANIAN :Lituania -STR_NETWORK_LANG_NORWEGIAN :Norway -STR_NETWORK_LANG_POLISH :Poland -STR_NETWORK_LANG_PORTUGUESE :Portugis -STR_NETWORK_LANG_ROMANIAN :Romania -STR_NETWORK_LANG_RUSSIAN :Rusia -STR_NETWORK_LANG_SLOVAK :Slovak -STR_NETWORK_LANG_SLOVENIAN :Slovenia -STR_NETWORK_LANG_SPANISH :Sepanyol -STR_NETWORK_LANG_SWEDISH :Sweden -STR_NETWORK_LANG_TURKISH :Turki -STR_NETWORK_LANG_UKRAINIAN :Ukraine -STR_NETWORK_LANG_AFRIKAANS :Afrikaans -STR_NETWORK_LANG_CROATIAN :Croatia -STR_NETWORK_LANG_CATALAN :Catalan -STR_NETWORK_LANG_ESTONIAN :Estonia -STR_NETWORK_LANG_GALICIAN :Galician -STR_NETWORK_LANG_GREEK :Yunani -STR_NETWORK_LANG_LATVIAN :Latvia -############ End of leave-in-this-order - # Network game lobby STR_NETWORK_GAME_LOBBY_CAPTION :{WHITE}Ruang tunggu berbilang pemain diff --git a/src/lang/norwegian_bokmal.txt b/src/lang/norwegian_bokmal.txt index 568f45972a..9e65d9502e 100644 --- a/src/lang/norwegian_bokmal.txt +++ b/src/lang/norwegian_bokmal.txt @@ -2074,46 +2074,6 @@ STR_NETWORK_START_SERVER_LANGUAGE_TOOLTIP :{BLACK}Andre sp STR_NETWORK_START_SERVER_NEW_GAME_NAME_OSKTITLE :{BLACK}Skriv inn et navn for nettverksspillet -# Network game languages -############ Leave those lines in this order!! -STR_NETWORK_LANG_ANY :Hvilket som helst -STR_NETWORK_LANG_ENGLISH :Engelsk -STR_NETWORK_LANG_GERMAN :Tysk -STR_NETWORK_LANG_FRENCH :Fransk -STR_NETWORK_LANG_BRAZILIAN :Brasiliansk -STR_NETWORK_LANG_BULGARIAN :Bulgarsk -STR_NETWORK_LANG_CHINESE :Kinesisk -STR_NETWORK_LANG_CZECH :Tsjekkisk -STR_NETWORK_LANG_DANISH :Dansk -STR_NETWORK_LANG_DUTCH :Nederlandsk -STR_NETWORK_LANG_ESPERANTO :Esperanto -STR_NETWORK_LANG_FINNISH :Finsk -STR_NETWORK_LANG_HUNGARIAN :Ungarsk -STR_NETWORK_LANG_ICELANDIC :Islandsk -STR_NETWORK_LANG_ITALIAN :Italiensk -STR_NETWORK_LANG_JAPANESE :Japansk -STR_NETWORK_LANG_KOREAN :Koreansk -STR_NETWORK_LANG_LITHUANIAN :Litauisk -STR_NETWORK_LANG_NORWEGIAN :Norsk -STR_NETWORK_LANG_POLISH :Polsk -STR_NETWORK_LANG_PORTUGUESE :Portugisisk -STR_NETWORK_LANG_ROMANIAN :Rumensk -STR_NETWORK_LANG_RUSSIAN :Russisk -STR_NETWORK_LANG_SLOVAK :Slovakisk -STR_NETWORK_LANG_SLOVENIAN :Slovensk -STR_NETWORK_LANG_SPANISH :Spansk -STR_NETWORK_LANG_SWEDISH :Svensk -STR_NETWORK_LANG_TURKISH :Tyrkisk -STR_NETWORK_LANG_UKRAINIAN :Ukrainsk -STR_NETWORK_LANG_AFRIKAANS :Afrikaans -STR_NETWORK_LANG_CROATIAN :Kroatisk -STR_NETWORK_LANG_CATALAN :Katalansk -STR_NETWORK_LANG_ESTONIAN :Estisk -STR_NETWORK_LANG_GALICIAN :Galicisk -STR_NETWORK_LANG_GREEK :Gresk -STR_NETWORK_LANG_LATVIAN :Latvisk -############ End of leave-in-this-order - # Network game lobby STR_NETWORK_GAME_LOBBY_CAPTION :{WHITE}Flerspillerlobby diff --git a/src/lang/norwegian_nynorsk.txt b/src/lang/norwegian_nynorsk.txt index 4a2bf807fc..9488472931 100644 --- a/src/lang/norwegian_nynorsk.txt +++ b/src/lang/norwegian_nynorsk.txt @@ -1869,46 +1869,6 @@ STR_NETWORK_START_SERVER_LANGUAGE_TOOLTIP :{BLACK}Andre sp STR_NETWORK_START_SERVER_NEW_GAME_NAME_OSKTITLE :{BLACK}Gje eit namn til nettverksspelet -# Network game languages -############ Leave those lines in this order!! -STR_NETWORK_LANG_ANY :Kva som helst -STR_NETWORK_LANG_ENGLISH :Engelsk -STR_NETWORK_LANG_GERMAN :Tysk -STR_NETWORK_LANG_FRENCH :Fransk -STR_NETWORK_LANG_BRAZILIAN :Brasiliansk -STR_NETWORK_LANG_BULGARIAN :Bulgarsk -STR_NETWORK_LANG_CHINESE :Kinesisk -STR_NETWORK_LANG_CZECH :Tsjekkisk -STR_NETWORK_LANG_DANISH :Dansk -STR_NETWORK_LANG_DUTCH :Nederlandsk -STR_NETWORK_LANG_ESPERANTO :Esperanto -STR_NETWORK_LANG_FINNISH :Finsk -STR_NETWORK_LANG_HUNGARIAN :Ungarsk -STR_NETWORK_LANG_ICELANDIC :Islandsk -STR_NETWORK_LANG_ITALIAN :Italiensk -STR_NETWORK_LANG_JAPANESE :Japansk -STR_NETWORK_LANG_KOREAN :Koreansk -STR_NETWORK_LANG_LITHUANIAN :Litauisk -STR_NETWORK_LANG_NORWEGIAN :Norsk -STR_NETWORK_LANG_POLISH :Polsk -STR_NETWORK_LANG_PORTUGUESE :Portugisisk -STR_NETWORK_LANG_ROMANIAN :Rumensk -STR_NETWORK_LANG_RUSSIAN :Russisk -STR_NETWORK_LANG_SLOVAK :Slovakisk -STR_NETWORK_LANG_SLOVENIAN :Slovensk -STR_NETWORK_LANG_SPANISH :Spansk -STR_NETWORK_LANG_SWEDISH :Svensk -STR_NETWORK_LANG_TURKISH :Tyrkisk -STR_NETWORK_LANG_UKRAINIAN :Ukrainsk -STR_NETWORK_LANG_AFRIKAANS :Afrikaans -STR_NETWORK_LANG_CROATIAN :Kroatisk -STR_NETWORK_LANG_CATALAN :Katalansk -STR_NETWORK_LANG_ESTONIAN :Estisk -STR_NETWORK_LANG_GALICIAN :Gælisk -STR_NETWORK_LANG_GREEK :Gresk -STR_NETWORK_LANG_LATVIAN :Latvisk -############ End of leave-in-this-order - # Network game lobby STR_NETWORK_GAME_LOBBY_CAPTION :{WHITE}Fleirspelarspel-lobby diff --git a/src/lang/polish.txt b/src/lang/polish.txt index 09d44f2fc1..81a477fbbb 100644 --- a/src/lang/polish.txt +++ b/src/lang/polish.txt @@ -2450,46 +2450,6 @@ STR_NETWORK_START_SERVER_LANGUAGE_TOOLTIP :{BLACK}Inni gra STR_NETWORK_START_SERVER_NEW_GAME_NAME_OSKTITLE :{BLACK}Wpisz nazwę dla gry sieciowej -# Network game languages -############ Leave those lines in this order!! -STR_NETWORK_LANG_ANY :Dowolny -STR_NETWORK_LANG_ENGLISH :Angielski -STR_NETWORK_LANG_GERMAN :Niemiecki -STR_NETWORK_LANG_FRENCH :Francuski -STR_NETWORK_LANG_BRAZILIAN :Brazylijski -STR_NETWORK_LANG_BULGARIAN :Bułgarski -STR_NETWORK_LANG_CHINESE :Chiński -STR_NETWORK_LANG_CZECH :Czeski -STR_NETWORK_LANG_DANISH :Duński -STR_NETWORK_LANG_DUTCH :Holenderski -STR_NETWORK_LANG_ESPERANTO :Esperanto -STR_NETWORK_LANG_FINNISH :Fiński -STR_NETWORK_LANG_HUNGARIAN :Węgierski -STR_NETWORK_LANG_ICELANDIC :Islandzki -STR_NETWORK_LANG_ITALIAN :Włoski -STR_NETWORK_LANG_JAPANESE :Japoński -STR_NETWORK_LANG_KOREAN :Koreański -STR_NETWORK_LANG_LITHUANIAN :Litewski -STR_NETWORK_LANG_NORWEGIAN :Norweski -STR_NETWORK_LANG_POLISH :Polski -STR_NETWORK_LANG_PORTUGUESE :Portugalski -STR_NETWORK_LANG_ROMANIAN :Rumuński -STR_NETWORK_LANG_RUSSIAN :Rosyjski -STR_NETWORK_LANG_SLOVAK :Słowacki -STR_NETWORK_LANG_SLOVENIAN :Słoweński -STR_NETWORK_LANG_SPANISH :Hiszpański -STR_NETWORK_LANG_SWEDISH :Szwedzki -STR_NETWORK_LANG_TURKISH :Turecki -STR_NETWORK_LANG_UKRAINIAN :Ukraiński -STR_NETWORK_LANG_AFRIKAANS :Afrykanerski -STR_NETWORK_LANG_CROATIAN :Chorwacki -STR_NETWORK_LANG_CATALAN :Kataloński -STR_NETWORK_LANG_ESTONIAN :Estoński -STR_NETWORK_LANG_GALICIAN :Galicyjski -STR_NETWORK_LANG_GREEK :Grecki -STR_NETWORK_LANG_LATVIAN :Łotewski -############ End of leave-in-this-order - # Network game lobby STR_NETWORK_GAME_LOBBY_CAPTION :{WHITE}Rozpoczęcie gry wieloosobowej diff --git a/src/lang/portuguese.txt b/src/lang/portuguese.txt index 046e9442ce..36742f76fe 100644 --- a/src/lang/portuguese.txt +++ b/src/lang/portuguese.txt @@ -2071,46 +2071,6 @@ STR_NETWORK_START_SERVER_LANGUAGE_TOOLTIP :{BLACK}Outros j STR_NETWORK_START_SERVER_NEW_GAME_NAME_OSKTITLE :{BLACK}Digite nome para o jogo de rede -# Network game languages -############ Leave those lines in this order!! -STR_NETWORK_LANG_ANY :Qualquer -STR_NETWORK_LANG_ENGLISH :Inglês -STR_NETWORK_LANG_GERMAN :Alemão -STR_NETWORK_LANG_FRENCH :Francês -STR_NETWORK_LANG_BRAZILIAN :Brasileiro -STR_NETWORK_LANG_BULGARIAN :Búlgaro -STR_NETWORK_LANG_CHINESE :Chinês -STR_NETWORK_LANG_CZECH :Checo -STR_NETWORK_LANG_DANISH :Dinamarquês -STR_NETWORK_LANG_DUTCH :Holandês -STR_NETWORK_LANG_ESPERANTO :Esperanto -STR_NETWORK_LANG_FINNISH :Finlandês -STR_NETWORK_LANG_HUNGARIAN :Húngaro -STR_NETWORK_LANG_ICELANDIC :Islandês -STR_NETWORK_LANG_ITALIAN :Italiano -STR_NETWORK_LANG_JAPANESE :Japonês -STR_NETWORK_LANG_KOREAN :Coreano -STR_NETWORK_LANG_LITHUANIAN :Lituano -STR_NETWORK_LANG_NORWEGIAN :Norueguês -STR_NETWORK_LANG_POLISH :Polaco -STR_NETWORK_LANG_PORTUGUESE :Português -STR_NETWORK_LANG_ROMANIAN :Romeno -STR_NETWORK_LANG_RUSSIAN :Russo -STR_NETWORK_LANG_SLOVAK :Eslovaco -STR_NETWORK_LANG_SLOVENIAN :Esloveno -STR_NETWORK_LANG_SPANISH :Espanhol -STR_NETWORK_LANG_SWEDISH :Sueco -STR_NETWORK_LANG_TURKISH :Turco -STR_NETWORK_LANG_UKRAINIAN :Ucraniano -STR_NETWORK_LANG_AFRIKAANS :Dialecto Africano Afrikaans -STR_NETWORK_LANG_CROATIAN :Croata -STR_NETWORK_LANG_CATALAN :Catalão -STR_NETWORK_LANG_ESTONIAN :Estónio -STR_NETWORK_LANG_GALICIAN :Galego -STR_NETWORK_LANG_GREEK :Grego -STR_NETWORK_LANG_LATVIAN :Letão -############ End of leave-in-this-order - # Network game lobby STR_NETWORK_GAME_LOBBY_CAPTION :{WHITE}Sala de espera de jogo multi-jogador diff --git a/src/lang/romanian.txt b/src/lang/romanian.txt index ec045eacee..847b41debf 100644 --- a/src/lang/romanian.txt +++ b/src/lang/romanian.txt @@ -1987,46 +1987,6 @@ STR_NETWORK_START_SERVER_LANGUAGE_TOOLTIP :{BLACK}Ceilalţ STR_NETWORK_START_SERVER_NEW_GAME_NAME_OSKTITLE :{BLACK}Introduceţi un nume pentru joc -# Network game languages -############ Leave those lines in this order!! -STR_NETWORK_LANG_ANY :Oricare -STR_NETWORK_LANG_ENGLISH :Engleză -STR_NETWORK_LANG_GERMAN :Germană -STR_NETWORK_LANG_FRENCH :Franceză -STR_NETWORK_LANG_BRAZILIAN :Braziliană -STR_NETWORK_LANG_BULGARIAN :Bulgară -STR_NETWORK_LANG_CHINESE :Chineză -STR_NETWORK_LANG_CZECH :Cehă -STR_NETWORK_LANG_DANISH :Daneză -STR_NETWORK_LANG_DUTCH :Olandeză -STR_NETWORK_LANG_ESPERANTO :Esperanto -STR_NETWORK_LANG_FINNISH :Finlandeză -STR_NETWORK_LANG_HUNGARIAN :Maghiară -STR_NETWORK_LANG_ICELANDIC :Islandeză -STR_NETWORK_LANG_ITALIAN :Italiană -STR_NETWORK_LANG_JAPANESE :Japoneză -STR_NETWORK_LANG_KOREAN :Coreană -STR_NETWORK_LANG_LITHUANIAN :Lituaniană -STR_NETWORK_LANG_NORWEGIAN :Norvegiană -STR_NETWORK_LANG_POLISH :Poloneză -STR_NETWORK_LANG_PORTUGUESE :Portugheză -STR_NETWORK_LANG_ROMANIAN :Română -STR_NETWORK_LANG_RUSSIAN :Rusă -STR_NETWORK_LANG_SLOVAK :Slovacă -STR_NETWORK_LANG_SLOVENIAN :Slovenă -STR_NETWORK_LANG_SPANISH :Spaniolă -STR_NETWORK_LANG_SWEDISH :Suedeză -STR_NETWORK_LANG_TURKISH :Turcă -STR_NETWORK_LANG_UKRAINIAN :Ucrainiană -STR_NETWORK_LANG_AFRIKAANS :Afrikaans -STR_NETWORK_LANG_CROATIAN :Croată -STR_NETWORK_LANG_CATALAN :Catalană -STR_NETWORK_LANG_ESTONIAN :Estonă -STR_NETWORK_LANG_GALICIAN :Galiciană -STR_NETWORK_LANG_GREEK :Greacă -STR_NETWORK_LANG_LATVIAN :Letonă -############ End of leave-in-this-order - # Network game lobby STR_NETWORK_GAME_LOBBY_CAPTION :{WHITE}Chatul jocului multiplayer diff --git a/src/lang/russian.txt b/src/lang/russian.txt index c1b4b261c0..b8c303733e 100644 --- a/src/lang/russian.txt +++ b/src/lang/russian.txt @@ -2221,46 +2221,6 @@ STR_NETWORK_START_SERVER_LANGUAGE_TOOLTIP :{BLACK}Друг STR_NETWORK_START_SERVER_NEW_GAME_NAME_OSKTITLE :{BLACK}Введите название сетевой игры -# Network game languages -############ Leave those lines in this order!! -STR_NETWORK_LANG_ANY :Любой -STR_NETWORK_LANG_ENGLISH :Английский -STR_NETWORK_LANG_GERMAN :Немецкий -STR_NETWORK_LANG_FRENCH :Французский -STR_NETWORK_LANG_BRAZILIAN :Бразильский -STR_NETWORK_LANG_BULGARIAN :Болгарский -STR_NETWORK_LANG_CHINESE :Китайский -STR_NETWORK_LANG_CZECH :Чешский -STR_NETWORK_LANG_DANISH :Датский -STR_NETWORK_LANG_DUTCH :Нидерландский -STR_NETWORK_LANG_ESPERANTO :Эсперанто -STR_NETWORK_LANG_FINNISH :Финский -STR_NETWORK_LANG_HUNGARIAN :Венгерский -STR_NETWORK_LANG_ICELANDIC :Исландский -STR_NETWORK_LANG_ITALIAN :Итальянский -STR_NETWORK_LANG_JAPANESE :Японский -STR_NETWORK_LANG_KOREAN :Корейский -STR_NETWORK_LANG_LITHUANIAN :Литовский -STR_NETWORK_LANG_NORWEGIAN :Норвежский -STR_NETWORK_LANG_POLISH :Польский -STR_NETWORK_LANG_PORTUGUESE :Португальский -STR_NETWORK_LANG_ROMANIAN :Румынский -STR_NETWORK_LANG_RUSSIAN :Русский -STR_NETWORK_LANG_SLOVAK :Словацкий -STR_NETWORK_LANG_SLOVENIAN :Словенский -STR_NETWORK_LANG_SPANISH :Испанский -STR_NETWORK_LANG_SWEDISH :Шведский -STR_NETWORK_LANG_TURKISH :Турецкий -STR_NETWORK_LANG_UKRAINIAN :Украинский -STR_NETWORK_LANG_AFRIKAANS :Африкаанс -STR_NETWORK_LANG_CROATIAN :Хорватский -STR_NETWORK_LANG_CATALAN :Каталанский -STR_NETWORK_LANG_ESTONIAN :Эстонский -STR_NETWORK_LANG_GALICIAN :Галисийский -STR_NETWORK_LANG_GREEK :Греческий -STR_NETWORK_LANG_LATVIAN :Латвийский -############ End of leave-in-this-order - # Network game lobby STR_NETWORK_GAME_LOBBY_CAPTION :{WHITE}Состояние сетевой игры diff --git a/src/lang/serbian.txt b/src/lang/serbian.txt index a9362e5710..78fb879283 100644 --- a/src/lang/serbian.txt +++ b/src/lang/serbian.txt @@ -2260,46 +2260,6 @@ STR_NETWORK_START_SERVER_LANGUAGE_TOOLTIP :{BLACK}Drugi ig STR_NETWORK_START_SERVER_NEW_GAME_NAME_OSKTITLE :{BLACK}Unos naziva mrežne partije -# Network game languages -############ Leave those lines in this order!! -STR_NETWORK_LANG_ANY :Bilo koji -STR_NETWORK_LANG_ENGLISH :Engleski -STR_NETWORK_LANG_GERMAN :Nemački -STR_NETWORK_LANG_FRENCH :Francuski -STR_NETWORK_LANG_BRAZILIAN :Brazilski -STR_NETWORK_LANG_BULGARIAN :Bugarski -STR_NETWORK_LANG_CHINESE :Kineski -STR_NETWORK_LANG_CZECH :Češki -STR_NETWORK_LANG_DANISH :Danski -STR_NETWORK_LANG_DUTCH :Holandski -STR_NETWORK_LANG_ESPERANTO :Esperanto -STR_NETWORK_LANG_FINNISH :Finski -STR_NETWORK_LANG_HUNGARIAN :Mađarski -STR_NETWORK_LANG_ICELANDIC :Islandski -STR_NETWORK_LANG_ITALIAN :Italijanski -STR_NETWORK_LANG_JAPANESE :Japanski -STR_NETWORK_LANG_KOREAN :Koreanski -STR_NETWORK_LANG_LITHUANIAN :Litvanski -STR_NETWORK_LANG_NORWEGIAN :Norveški -STR_NETWORK_LANG_POLISH :Poljski -STR_NETWORK_LANG_PORTUGUESE :Portugalski -STR_NETWORK_LANG_ROMANIAN :Rumunski -STR_NETWORK_LANG_RUSSIAN :Ruski -STR_NETWORK_LANG_SLOVAK :Slovački -STR_NETWORK_LANG_SLOVENIAN :Slovenački -STR_NETWORK_LANG_SPANISH :Španski -STR_NETWORK_LANG_SWEDISH :Švedski -STR_NETWORK_LANG_TURKISH :Turski -STR_NETWORK_LANG_UKRAINIAN :Ukrajinski -STR_NETWORK_LANG_AFRIKAANS :Afrikans -STR_NETWORK_LANG_CROATIAN :Hrvatski -STR_NETWORK_LANG_CATALAN :Katalonski -STR_NETWORK_LANG_ESTONIAN :Estonski -STR_NETWORK_LANG_GALICIAN :Galicijski -STR_NETWORK_LANG_GREEK :Grčki -STR_NETWORK_LANG_LATVIAN :Letonski -############ End of leave-in-this-order - # Network game lobby STR_NETWORK_GAME_LOBBY_CAPTION :{WHITE}Predvorje partije sa više igrača diff --git a/src/lang/simplified_chinese.txt b/src/lang/simplified_chinese.txt index 1957f157ab..8b568b51dd 100644 --- a/src/lang/simplified_chinese.txt +++ b/src/lang/simplified_chinese.txt @@ -2070,46 +2070,6 @@ STR_NETWORK_START_SERVER_LANGUAGE_TOOLTIP :{BLACK}其他 STR_NETWORK_START_SERVER_NEW_GAME_NAME_OSKTITLE :{BLACK}为您的联机游戏取名 -# Network game languages -############ Leave those lines in this order!! -STR_NETWORK_LANG_ANY :任意 -STR_NETWORK_LANG_ENGLISH :英语 -STR_NETWORK_LANG_GERMAN :德语 -STR_NETWORK_LANG_FRENCH :法语 -STR_NETWORK_LANG_BRAZILIAN :巴西语 -STR_NETWORK_LANG_BULGARIAN :保加利亚语 -STR_NETWORK_LANG_CHINESE :中文 -STR_NETWORK_LANG_CZECH :捷克语 -STR_NETWORK_LANG_DANISH :丹麦语 -STR_NETWORK_LANG_DUTCH :荷兰语 -STR_NETWORK_LANG_ESPERANTO :世界语 -STR_NETWORK_LANG_FINNISH :芬兰语 -STR_NETWORK_LANG_HUNGARIAN :匈牙利语 -STR_NETWORK_LANG_ICELANDIC :冰岛语 -STR_NETWORK_LANG_ITALIAN :意大利语 -STR_NETWORK_LANG_JAPANESE :日语 -STR_NETWORK_LANG_KOREAN :韩文 -STR_NETWORK_LANG_LITHUANIAN :立陶宛语 -STR_NETWORK_LANG_NORWEGIAN :挪威语 -STR_NETWORK_LANG_POLISH :波兰语 -STR_NETWORK_LANG_PORTUGUESE :葡萄牙语 -STR_NETWORK_LANG_ROMANIAN :罗马尼亚语 -STR_NETWORK_LANG_RUSSIAN :俄语 -STR_NETWORK_LANG_SLOVAK :斯洛伐克语 -STR_NETWORK_LANG_SLOVENIAN :斯洛文尼亚语 -STR_NETWORK_LANG_SPANISH :西班牙语 -STR_NETWORK_LANG_SWEDISH :瑞典语 -STR_NETWORK_LANG_TURKISH :土耳其语 -STR_NETWORK_LANG_UKRAINIAN :乌克兰语 -STR_NETWORK_LANG_AFRIKAANS :南非荷兰语 -STR_NETWORK_LANG_CROATIAN :克罗地亚语 -STR_NETWORK_LANG_CATALAN :加泰罗尼亚语 -STR_NETWORK_LANG_ESTONIAN :爱沙尼亚语 -STR_NETWORK_LANG_GALICIAN :加利西亚语 -STR_NETWORK_LANG_GREEK :希腊语 -STR_NETWORK_LANG_LATVIAN :拉脱维亚语 -############ End of leave-in-this-order - # Network game lobby STR_NETWORK_GAME_LOBBY_CAPTION :{WHITE}联机游戏大厅 diff --git a/src/lang/slovak.txt b/src/lang/slovak.txt index 683f7958c1..c3e9dee60e 100644 --- a/src/lang/slovak.txt +++ b/src/lang/slovak.txt @@ -2133,46 +2133,6 @@ STR_NETWORK_START_SERVER_LANGUAGE_TOOLTIP :{BLACK}Aby osta STR_NETWORK_START_SERVER_NEW_GAME_NAME_OSKTITLE :{BLACK}Zadajte názov sieťovej hry -# Network game languages -############ Leave those lines in this order!! -STR_NETWORK_LANG_ANY :Lubovolny -STR_NETWORK_LANG_ENGLISH :Anglicky -STR_NETWORK_LANG_GERMAN :Nemecky -STR_NETWORK_LANG_FRENCH :Francuzsky -STR_NETWORK_LANG_BRAZILIAN :Brazílsky -STR_NETWORK_LANG_BULGARIAN :Bulharsky -STR_NETWORK_LANG_CHINESE :Cinsky -STR_NETWORK_LANG_CZECH :Cesky -STR_NETWORK_LANG_DANISH :Dánsky -STR_NETWORK_LANG_DUTCH :Holadnsky -STR_NETWORK_LANG_ESPERANTO :Esperanto -STR_NETWORK_LANG_FINNISH :Fínsky -STR_NETWORK_LANG_HUNGARIAN :Madarsky -STR_NETWORK_LANG_ICELANDIC :Islandsky -STR_NETWORK_LANG_ITALIAN :Taliansky -STR_NETWORK_LANG_JAPANESE :Japonsky -STR_NETWORK_LANG_KOREAN :Kórejsky -STR_NETWORK_LANG_LITHUANIAN :Litovksy -STR_NETWORK_LANG_NORWEGIAN :Nórsky -STR_NETWORK_LANG_POLISH :Polsky -STR_NETWORK_LANG_PORTUGUESE :Portugalsky -STR_NETWORK_LANG_ROMANIAN :Rumunsky -STR_NETWORK_LANG_RUSSIAN :Rusky -STR_NETWORK_LANG_SLOVAK :Slovenský -STR_NETWORK_LANG_SLOVENIAN :Slovinsky -STR_NETWORK_LANG_SPANISH :Spanielsky -STR_NETWORK_LANG_SWEDISH :Svédsky -STR_NETWORK_LANG_TURKISH :Turecky -STR_NETWORK_LANG_UKRAINIAN :Ukrajinsky -STR_NETWORK_LANG_AFRIKAANS :Africky -STR_NETWORK_LANG_CROATIAN :Chorvátsky -STR_NETWORK_LANG_CATALAN :Katalánsky -STR_NETWORK_LANG_ESTONIAN :Estónsky -STR_NETWORK_LANG_GALICIAN :Gálsky -STR_NETWORK_LANG_GREEK :Grécky -STR_NETWORK_LANG_LATVIAN :Litovsky -############ End of leave-in-this-order - # Network game lobby STR_NETWORK_GAME_LOBBY_CAPTION :{WHITE}Sieťová hra - čakáreň diff --git a/src/lang/slovenian.txt b/src/lang/slovenian.txt index 1e05e9f290..d1614d12eb 100644 --- a/src/lang/slovenian.txt +++ b/src/lang/slovenian.txt @@ -2101,46 +2101,6 @@ STR_NETWORK_START_SERVER_LANGUAGE_TOOLTIP :{BLACK}Drugi ig STR_NETWORK_START_SERVER_NEW_GAME_NAME_OSKTITLE :{BLACK}Vpiši ime za mrežno igro -# Network game languages -############ Leave those lines in this order!! -STR_NETWORK_LANG_ANY :Kakršen koli -STR_NETWORK_LANG_ENGLISH :Angleški -STR_NETWORK_LANG_GERMAN :Nemški -STR_NETWORK_LANG_FRENCH :Francoski -STR_NETWORK_LANG_BRAZILIAN :Brazilski -STR_NETWORK_LANG_BULGARIAN :Bolgarski -STR_NETWORK_LANG_CHINESE :Kitajski -STR_NETWORK_LANG_CZECH :Češki -STR_NETWORK_LANG_DANISH :Danski -STR_NETWORK_LANG_DUTCH :Nizozemski -STR_NETWORK_LANG_ESPERANTO :Esperanto -STR_NETWORK_LANG_FINNISH :Finski -STR_NETWORK_LANG_HUNGARIAN :Madžarski -STR_NETWORK_LANG_ICELANDIC :Islandski -STR_NETWORK_LANG_ITALIAN :Italijanski -STR_NETWORK_LANG_JAPANESE :Japonski -STR_NETWORK_LANG_KOREAN :Korejski -STR_NETWORK_LANG_LITHUANIAN :Litvijski -STR_NETWORK_LANG_NORWEGIAN :Norveški -STR_NETWORK_LANG_POLISH :Poljski -STR_NETWORK_LANG_PORTUGUESE :Portugalski -STR_NETWORK_LANG_ROMANIAN :Romunski -STR_NETWORK_LANG_RUSSIAN :Ruski -STR_NETWORK_LANG_SLOVAK :Slovaški -STR_NETWORK_LANG_SLOVENIAN :Slovenski -STR_NETWORK_LANG_SPANISH :Španski -STR_NETWORK_LANG_SWEDISH :Švedski -STR_NETWORK_LANG_TURKISH :Turški -STR_NETWORK_LANG_UKRAINIAN :Ukrajinski -STR_NETWORK_LANG_AFRIKAANS :Afriški -STR_NETWORK_LANG_CROATIAN :Hrvaški -STR_NETWORK_LANG_CATALAN :Katalonski -STR_NETWORK_LANG_ESTONIAN :Estonski -STR_NETWORK_LANG_GALICIAN :Galijski -STR_NETWORK_LANG_GREEK :Grški -STR_NETWORK_LANG_LATVIAN :Latvijski -############ End of leave-in-this-order - # Network game lobby STR_NETWORK_GAME_LOBBY_CAPTION :{WHITE}Seje večigralskih iger diff --git a/src/lang/spanish.txt b/src/lang/spanish.txt index e007965478..32625a802e 100644 --- a/src/lang/spanish.txt +++ b/src/lang/spanish.txt @@ -2071,46 +2071,6 @@ STR_NETWORK_START_SERVER_LANGUAGE_TOOLTIP :{BLACK}Los otro STR_NETWORK_START_SERVER_NEW_GAME_NAME_OSKTITLE :{BLACK}Introduce un nombre para el juego en red -# Network game languages -############ Leave those lines in this order!! -STR_NETWORK_LANG_ANY :Cualquiera -STR_NETWORK_LANG_ENGLISH :Inglés -STR_NETWORK_LANG_GERMAN :Alemán -STR_NETWORK_LANG_FRENCH :Francés -STR_NETWORK_LANG_BRAZILIAN :Brasileño -STR_NETWORK_LANG_BULGARIAN :Búlgaro -STR_NETWORK_LANG_CHINESE :Chino -STR_NETWORK_LANG_CZECH :Checo -STR_NETWORK_LANG_DANISH :Danés -STR_NETWORK_LANG_DUTCH :Alemán -STR_NETWORK_LANG_ESPERANTO :Esperanto -STR_NETWORK_LANG_FINNISH :Fines -STR_NETWORK_LANG_HUNGARIAN :Húngaro -STR_NETWORK_LANG_ICELANDIC :Islandés -STR_NETWORK_LANG_ITALIAN :Italiano -STR_NETWORK_LANG_JAPANESE :Japonés -STR_NETWORK_LANG_KOREAN :Coreano -STR_NETWORK_LANG_LITHUANIAN :Lituano -STR_NETWORK_LANG_NORWEGIAN :Noruego -STR_NETWORK_LANG_POLISH :Polaco -STR_NETWORK_LANG_PORTUGUESE :Portugués -STR_NETWORK_LANG_ROMANIAN :Rumano -STR_NETWORK_LANG_RUSSIAN :Ruso -STR_NETWORK_LANG_SLOVAK :Eslovaco -STR_NETWORK_LANG_SLOVENIAN :Esloveno -STR_NETWORK_LANG_SPANISH :Español -STR_NETWORK_LANG_SWEDISH :Sueco -STR_NETWORK_LANG_TURKISH :Turco -STR_NETWORK_LANG_UKRAINIAN :Ucraniano -STR_NETWORK_LANG_AFRIKAANS :Africano -STR_NETWORK_LANG_CROATIAN :Croata -STR_NETWORK_LANG_CATALAN :Catalán -STR_NETWORK_LANG_ESTONIAN :Estonio -STR_NETWORK_LANG_GALICIAN :Gallego -STR_NETWORK_LANG_GREEK :Griego -STR_NETWORK_LANG_LATVIAN :Letón -############ End of leave-in-this-order - # Network game lobby STR_NETWORK_GAME_LOBBY_CAPTION :{WHITE}Sala de espera de la partida multijugador diff --git a/src/lang/spanish_MX.txt b/src/lang/spanish_MX.txt index 8934a35209..32a336c3e4 100644 --- a/src/lang/spanish_MX.txt +++ b/src/lang/spanish_MX.txt @@ -2069,46 +2069,6 @@ STR_NETWORK_START_SERVER_LANGUAGE_TOOLTIP :{BLACK}Otros ju STR_NETWORK_START_SERVER_NEW_GAME_NAME_OSKTITLE :{BLACK}Introducir un nombre para la partida en red -# Network game languages -############ Leave those lines in this order!! -STR_NETWORK_LANG_ANY :Cualquiera -STR_NETWORK_LANG_ENGLISH :Inglés -STR_NETWORK_LANG_GERMAN :Alemán -STR_NETWORK_LANG_FRENCH :Francés -STR_NETWORK_LANG_BRAZILIAN :Portugués (Brasil) -STR_NETWORK_LANG_BULGARIAN :Búlgaro -STR_NETWORK_LANG_CHINESE :Chino -STR_NETWORK_LANG_CZECH :Checo -STR_NETWORK_LANG_DANISH :Danés -STR_NETWORK_LANG_DUTCH :Alemán -STR_NETWORK_LANG_ESPERANTO :Esperanto -STR_NETWORK_LANG_FINNISH :Finés -STR_NETWORK_LANG_HUNGARIAN :Húngaro -STR_NETWORK_LANG_ICELANDIC :Islandés -STR_NETWORK_LANG_ITALIAN :Italiano -STR_NETWORK_LANG_JAPANESE :Japonés -STR_NETWORK_LANG_KOREAN :Coreano -STR_NETWORK_LANG_LITHUANIAN :Lituano -STR_NETWORK_LANG_NORWEGIAN :Noruego -STR_NETWORK_LANG_POLISH :Polaco -STR_NETWORK_LANG_PORTUGUESE :Portugués -STR_NETWORK_LANG_ROMANIAN :Rumano -STR_NETWORK_LANG_RUSSIAN :Ruso -STR_NETWORK_LANG_SLOVAK :Eslovaco -STR_NETWORK_LANG_SLOVENIAN :Esloveno -STR_NETWORK_LANG_SPANISH :Español -STR_NETWORK_LANG_SWEDISH :Sueco -STR_NETWORK_LANG_TURKISH :Turco -STR_NETWORK_LANG_UKRAINIAN :Ucraniano -STR_NETWORK_LANG_AFRIKAANS :Afrikáans -STR_NETWORK_LANG_CROATIAN :Croata -STR_NETWORK_LANG_CATALAN :Catalán -STR_NETWORK_LANG_ESTONIAN :Estonio -STR_NETWORK_LANG_GALICIAN :Gallego -STR_NETWORK_LANG_GREEK :Griego -STR_NETWORK_LANG_LATVIAN :Letón -############ End of leave-in-this-order - # Network game lobby STR_NETWORK_GAME_LOBBY_CAPTION :{WHITE}Sala virtual de partidas multijugador diff --git a/src/lang/swedish.txt b/src/lang/swedish.txt index 83a98a2edd..54bfa40661 100644 --- a/src/lang/swedish.txt +++ b/src/lang/swedish.txt @@ -2070,46 +2070,6 @@ STR_NETWORK_START_SERVER_LANGUAGE_TOOLTIP :{BLACK}Andra sp STR_NETWORK_START_SERVER_NEW_GAME_NAME_OSKTITLE :{BLACK}Mata in ett namn för nätverksspelet -# Network game languages -############ Leave those lines in this order!! -STR_NETWORK_LANG_ANY :Valfritt -STR_NETWORK_LANG_ENGLISH :Engelska -STR_NETWORK_LANG_GERMAN :Tyska -STR_NETWORK_LANG_FRENCH :Franska -STR_NETWORK_LANG_BRAZILIAN :Brasilianska -STR_NETWORK_LANG_BULGARIAN :Belgiska -STR_NETWORK_LANG_CHINESE :Kinesiska -STR_NETWORK_LANG_CZECH :Tjeckiska -STR_NETWORK_LANG_DANISH :Danska -STR_NETWORK_LANG_DUTCH :Holländska -STR_NETWORK_LANG_ESPERANTO :Spanska -STR_NETWORK_LANG_FINNISH :Finska -STR_NETWORK_LANG_HUNGARIAN :Ungerska -STR_NETWORK_LANG_ICELANDIC :Isländska -STR_NETWORK_LANG_ITALIAN :Italienska -STR_NETWORK_LANG_JAPANESE :Japanska -STR_NETWORK_LANG_KOREAN :Koreanska -STR_NETWORK_LANG_LITHUANIAN :Litauiska -STR_NETWORK_LANG_NORWEGIAN :Norska -STR_NETWORK_LANG_POLISH :Polska -STR_NETWORK_LANG_PORTUGUESE :Portugisiska -STR_NETWORK_LANG_ROMANIAN :Rumänska -STR_NETWORK_LANG_RUSSIAN :Ryska -STR_NETWORK_LANG_SLOVAK :Slovakiska -STR_NETWORK_LANG_SLOVENIAN :Slovenska -STR_NETWORK_LANG_SPANISH :Spanska -STR_NETWORK_LANG_SWEDISH :Svenska -STR_NETWORK_LANG_TURKISH :Turkiska -STR_NETWORK_LANG_UKRAINIAN :Ukrainska -STR_NETWORK_LANG_AFRIKAANS :Afrikaans -STR_NETWORK_LANG_CROATIAN :Kroatiska -STR_NETWORK_LANG_CATALAN :Katalanska -STR_NETWORK_LANG_ESTONIAN :Estniska -STR_NETWORK_LANG_GALICIAN :Galiciska -STR_NETWORK_LANG_GREEK :Grekiska -STR_NETWORK_LANG_LATVIAN :Lettiska -############ End of leave-in-this-order - # Network game lobby STR_NETWORK_GAME_LOBBY_CAPTION :{WHITE}Nätverksspel diff --git a/src/lang/tamil.txt b/src/lang/tamil.txt index 99b2384e73..0f3b782e84 100644 --- a/src/lang/tamil.txt +++ b/src/lang/tamil.txt @@ -1805,46 +1805,6 @@ STR_NETWORK_START_SERVER_LANGUAGE_SPOKEN :{BLACK}பே STR_NETWORK_START_SERVER_NEW_GAME_NAME_OSKTITLE :{BLACK}இணைய ஆட்டத்திற்கு ஒரு பெயரினை இடு -# Network game languages -############ Leave those lines in this order!! -STR_NETWORK_LANG_ANY :எதுவும் -STR_NETWORK_LANG_ENGLISH :ஆங்கிலம் -STR_NETWORK_LANG_GERMAN :ஜெர்மன் -STR_NETWORK_LANG_FRENCH :பிரெஞ்சு -STR_NETWORK_LANG_BRAZILIAN :பிரசில்லியன் -STR_NETWORK_LANG_BULGARIAN :பல்கேரியன் -STR_NETWORK_LANG_CHINESE :சீன மொழி -STR_NETWORK_LANG_CZECH :செக் -STR_NETWORK_LANG_DANISH :டேனிஷ் -STR_NETWORK_LANG_DUTCH :டச் -STR_NETWORK_LANG_ESPERANTO :உலகப் பொதுச் செயற்கைமொழி -STR_NETWORK_LANG_FINNISH :பின்னிஷ் -STR_NETWORK_LANG_HUNGARIAN :ஹங்கேரியன் -STR_NETWORK_LANG_ICELANDIC :இஸ்லாந்திய மொழி -STR_NETWORK_LANG_ITALIAN :இத்தாலியன் -STR_NETWORK_LANG_JAPANESE :ஜப்பானிய மொழி -STR_NETWORK_LANG_KOREAN :கோரிய மொழி -STR_NETWORK_LANG_LITHUANIAN :லிதுவேனியன் -STR_NETWORK_LANG_NORWEGIAN :நார்வேஜியன் -STR_NETWORK_LANG_POLISH :போலிஷ் -STR_NETWORK_LANG_PORTUGUESE :போர்த்துகீசியம் -STR_NETWORK_LANG_ROMANIAN :ரோமானிய மொழி -STR_NETWORK_LANG_RUSSIAN :ரஷ்ய மொழி -STR_NETWORK_LANG_SLOVAK :ஸ்லவோனிய மொழி -STR_NETWORK_LANG_SLOVENIAN :ஸ்லவோனிய மொழி -STR_NETWORK_LANG_SPANISH :ஸ்பானிஷ் -STR_NETWORK_LANG_SWEDISH :சவீடிஷ் -STR_NETWORK_LANG_TURKISH :டர்கிஷ் -STR_NETWORK_LANG_UKRAINIAN :உக்ரேனியம் -STR_NETWORK_LANG_AFRIKAANS :ஆப்ரிகான்ஸ் -STR_NETWORK_LANG_CROATIAN :குரோஏசியன் -STR_NETWORK_LANG_CATALAN :கடலான் -STR_NETWORK_LANG_ESTONIAN :எஸ்தோனியம் -STR_NETWORK_LANG_GALICIAN :கலிசியம் -STR_NETWORK_LANG_GREEK :கிரேக்க மொழி -STR_NETWORK_LANG_LATVIAN :லாத்வியன் -############ End of leave-in-this-order - # Network game lobby STR_NETWORK_GAME_LOBBY_PREPARE_TO_JOIN :{BLACK}சேர தொடங்கப்படுகிறது: {ORANGE}{STRING} diff --git a/src/lang/thai.txt b/src/lang/thai.txt index 11a7f55b1b..2a3b5aed10 100644 --- a/src/lang/thai.txt +++ b/src/lang/thai.txt @@ -1896,46 +1896,6 @@ STR_NETWORK_START_SERVER_LANGUAGE_TOOLTIP :{BLACK}ผู STR_NETWORK_START_SERVER_NEW_GAME_NAME_OSKTITLE :{BLACK}พิมพ์ชื่อสำหรับเล่นในเครือข่าย -# Network game languages -############ Leave those lines in this order!! -STR_NETWORK_LANG_ANY :อะไรก็ได้ -STR_NETWORK_LANG_ENGLISH :ภาษาอังกฤษ -STR_NETWORK_LANG_GERMAN :ภาษาเยอรมัน -STR_NETWORK_LANG_FRENCH :ภาษาฝรั่งเศส -STR_NETWORK_LANG_BRAZILIAN :ภาษาบราซิล -STR_NETWORK_LANG_BULGARIAN :ภาษาบัลกาเรีย -STR_NETWORK_LANG_CHINESE :จีน -STR_NETWORK_LANG_CZECH :เช็ก -STR_NETWORK_LANG_DANISH :เดนมาร์ก -STR_NETWORK_LANG_DUTCH :เนเธอร์แลนด์ -STR_NETWORK_LANG_ESPERANTO :ภาษาโลก(Esperanto) -STR_NETWORK_LANG_FINNISH :ฟินแลนด์ -STR_NETWORK_LANG_HUNGARIAN :ฮังการี -STR_NETWORK_LANG_ICELANDIC :ไอร์แลนด์ -STR_NETWORK_LANG_ITALIAN :อิตาลี -STR_NETWORK_LANG_JAPANESE :ญี่ปุ่น -STR_NETWORK_LANG_KOREAN :เกาหลี -STR_NETWORK_LANG_LITHUANIAN :ลิทัวเนีย -STR_NETWORK_LANG_NORWEGIAN :นอร์เวย์ -STR_NETWORK_LANG_POLISH :โปแลนด์ -STR_NETWORK_LANG_PORTUGUESE :โปรตุเกส -STR_NETWORK_LANG_ROMANIAN :โรมาเนีย -STR_NETWORK_LANG_RUSSIAN :รัสเซีย -STR_NETWORK_LANG_SLOVAK :สโลวัก -STR_NETWORK_LANG_SLOVENIAN :สโลวาเนีย -STR_NETWORK_LANG_SPANISH :สเปน -STR_NETWORK_LANG_SWEDISH :สวีเดน -STR_NETWORK_LANG_TURKISH :ตุรกี -STR_NETWORK_LANG_UKRAINIAN :ยูเครน -STR_NETWORK_LANG_AFRIKAANS :อัฟกานิสถาน -STR_NETWORK_LANG_CROATIAN :โครเอเชีย -STR_NETWORK_LANG_CATALAN :คาตาลัน -STR_NETWORK_LANG_ESTONIAN :เอสโตเนีย -STR_NETWORK_LANG_GALICIAN :กาลีเซีย -STR_NETWORK_LANG_GREEK :กรีก -STR_NETWORK_LANG_LATVIAN :ลัทเวีย -############ End of leave-in-this-order - # Network game lobby STR_NETWORK_GAME_LOBBY_CAPTION :{WHITE}ห้องรับรองโหมดผู้เล่นหลายคน diff --git a/src/lang/traditional_chinese.txt b/src/lang/traditional_chinese.txt index d21dd76f19..99b446d669 100644 --- a/src/lang/traditional_chinese.txt +++ b/src/lang/traditional_chinese.txt @@ -1957,46 +1957,6 @@ STR_NETWORK_START_SERVER_LANGUAGE_TOOLTIP :{BLACK}讓其 STR_NETWORK_START_SERVER_NEW_GAME_NAME_OSKTITLE :{BLACK}為連線遊戲輸入一個名稱 -# Network game languages -############ Leave those lines in this order!! -STR_NETWORK_LANG_ANY :不限 -STR_NETWORK_LANG_ENGLISH :英語 -STR_NETWORK_LANG_GERMAN :德語 -STR_NETWORK_LANG_FRENCH :法語 -STR_NETWORK_LANG_BRAZILIAN :巴西葡萄牙語 -STR_NETWORK_LANG_BULGARIAN :保加利亞語 -STR_NETWORK_LANG_CHINESE :中文 -STR_NETWORK_LANG_CZECH :捷克語 -STR_NETWORK_LANG_DANISH :丹麥語 -STR_NETWORK_LANG_DUTCH :荷蘭語 -STR_NETWORK_LANG_ESPERANTO :世界語 -STR_NETWORK_LANG_FINNISH :芬蘭語 -STR_NETWORK_LANG_HUNGARIAN :匈牙利語 -STR_NETWORK_LANG_ICELANDIC :冰島語 -STR_NETWORK_LANG_ITALIAN :義大利語 -STR_NETWORK_LANG_JAPANESE :日文 -STR_NETWORK_LANG_KOREAN :韓文 -STR_NETWORK_LANG_LITHUANIAN :立陶宛語 -STR_NETWORK_LANG_NORWEGIAN :挪威語 -STR_NETWORK_LANG_POLISH :波蘭語 -STR_NETWORK_LANG_PORTUGUESE :葡萄牙語 -STR_NETWORK_LANG_ROMANIAN :羅馬尼亞語 -STR_NETWORK_LANG_RUSSIAN :俄語 -STR_NETWORK_LANG_SLOVAK :斯洛伐克語 -STR_NETWORK_LANG_SLOVENIAN :斯洛維尼亞語 -STR_NETWORK_LANG_SPANISH :西班牙語 -STR_NETWORK_LANG_SWEDISH :瑞典語 -STR_NETWORK_LANG_TURKISH :土耳其語 -STR_NETWORK_LANG_UKRAINIAN :烏克蘭語 -STR_NETWORK_LANG_AFRIKAANS :南非荷蘭語 -STR_NETWORK_LANG_CROATIAN :克羅埃西亞語 -STR_NETWORK_LANG_CATALAN :加泰隆語 (西班牙) -STR_NETWORK_LANG_ESTONIAN :愛沙尼亞語 -STR_NETWORK_LANG_GALICIAN :加里西亞語 (西班牙) -STR_NETWORK_LANG_GREEK :現代希臘語 -STR_NETWORK_LANG_LATVIAN :拉脫維亞語 -############ End of leave-in-this-order - # Network game lobby STR_NETWORK_GAME_LOBBY_CAPTION :{WHITE}多人遊戲大廳 diff --git a/src/lang/turkish.txt b/src/lang/turkish.txt index 11f3042fbf..fb378c4c9f 100644 --- a/src/lang/turkish.txt +++ b/src/lang/turkish.txt @@ -2004,46 +2004,6 @@ STR_NETWORK_START_SERVER_LANGUAGE_TOOLTIP :{BLACK}Diğer o STR_NETWORK_START_SERVER_NEW_GAME_NAME_OSKTITLE :{BLACK}Ağ oyunu için bir isim girin -# Network game languages -############ Leave those lines in this order!! -STR_NETWORK_LANG_ANY :Farketmez -STR_NETWORK_LANG_ENGLISH :İngilizce -STR_NETWORK_LANG_GERMAN :Almanca -STR_NETWORK_LANG_FRENCH :Fransızca -STR_NETWORK_LANG_BRAZILIAN :Brezilyaca -STR_NETWORK_LANG_BULGARIAN :Bulgarca -STR_NETWORK_LANG_CHINESE :Çince -STR_NETWORK_LANG_CZECH :Çekce -STR_NETWORK_LANG_DANISH :Danimarkaca -STR_NETWORK_LANG_DUTCH :Hollandaca -STR_NETWORK_LANG_ESPERANTO :Esperanto -STR_NETWORK_LANG_FINNISH :Fince -STR_NETWORK_LANG_HUNGARIAN :Bulgarca -STR_NETWORK_LANG_ICELANDIC :İzlandaca -STR_NETWORK_LANG_ITALIAN :İtalyanca -STR_NETWORK_LANG_JAPANESE :Japonca -STR_NETWORK_LANG_KOREAN :Korece -STR_NETWORK_LANG_LITHUANIAN :Litvanyaca -STR_NETWORK_LANG_NORWEGIAN :Norveççe -STR_NETWORK_LANG_POLISH :Lehçe -STR_NETWORK_LANG_PORTUGUESE :Portekizce -STR_NETWORK_LANG_ROMANIAN :Romanca -STR_NETWORK_LANG_RUSSIAN :Rusça -STR_NETWORK_LANG_SLOVAK :Slovakça -STR_NETWORK_LANG_SLOVENIAN :Slovence -STR_NETWORK_LANG_SPANISH :İspanyolca -STR_NETWORK_LANG_SWEDISH :İsveççe -STR_NETWORK_LANG_TURKISH :Türkçe -STR_NETWORK_LANG_UKRAINIAN :Ukraynaca -STR_NETWORK_LANG_AFRIKAANS :Afrikaanca -STR_NETWORK_LANG_CROATIAN :Hırvatça -STR_NETWORK_LANG_CATALAN :Katalanca -STR_NETWORK_LANG_ESTONIAN :Estonyaca -STR_NETWORK_LANG_GALICIAN :Galiçyaca -STR_NETWORK_LANG_GREEK :Yunanca -STR_NETWORK_LANG_LATVIAN :Letonca -############ End of leave-in-this-order - # Network game lobby STR_NETWORK_GAME_LOBBY_CAPTION :{WHITE}Çok oyunculu oyun lobisi diff --git a/src/lang/ukrainian.txt b/src/lang/ukrainian.txt index 8c90c222b4..0d26d41cc3 100644 --- a/src/lang/ukrainian.txt +++ b/src/lang/ukrainian.txt @@ -2193,46 +2193,6 @@ STR_NETWORK_START_SERVER_LANGUAGE_TOOLTIP :{BLACK}Інші STR_NETWORK_START_SERVER_NEW_GAME_NAME_OSKTITLE :{BLACK}Введіть назву мережевої гри -# Network game languages -############ Leave those lines in this order!! -STR_NETWORK_LANG_ANY :Будь-яка -STR_NETWORK_LANG_ENGLISH :Англійська -STR_NETWORK_LANG_GERMAN :Німецька -STR_NETWORK_LANG_FRENCH :Французька -STR_NETWORK_LANG_BRAZILIAN :Бразильська -STR_NETWORK_LANG_BULGARIAN :Болгарська -STR_NETWORK_LANG_CHINESE :Китайська -STR_NETWORK_LANG_CZECH :Чеська -STR_NETWORK_LANG_DANISH :Данська -STR_NETWORK_LANG_DUTCH :Нідерландська -STR_NETWORK_LANG_ESPERANTO :Есперанто -STR_NETWORK_LANG_FINNISH :Фінська -STR_NETWORK_LANG_HUNGARIAN :Угорська -STR_NETWORK_LANG_ICELANDIC :Ісландська -STR_NETWORK_LANG_ITALIAN :Італійська -STR_NETWORK_LANG_JAPANESE :Японська -STR_NETWORK_LANG_KOREAN :Корейська -STR_NETWORK_LANG_LITHUANIAN :Литовська -STR_NETWORK_LANG_NORWEGIAN :Норвезька -STR_NETWORK_LANG_POLISH :Польська -STR_NETWORK_LANG_PORTUGUESE :Португальська -STR_NETWORK_LANG_ROMANIAN :Румунська -STR_NETWORK_LANG_RUSSIAN :Російська -STR_NETWORK_LANG_SLOVAK :Словацька -STR_NETWORK_LANG_SLOVENIAN :Словенська -STR_NETWORK_LANG_SPANISH :Іспанська -STR_NETWORK_LANG_SWEDISH :Шведська -STR_NETWORK_LANG_TURKISH :Турецька -STR_NETWORK_LANG_UKRAINIAN :Українська -STR_NETWORK_LANG_AFRIKAANS :Афрікаанс -STR_NETWORK_LANG_CROATIAN :Хорватська -STR_NETWORK_LANG_CATALAN :Каталонська -STR_NETWORK_LANG_ESTONIAN :Естонська -STR_NETWORK_LANG_GALICIAN :Галісійська -STR_NETWORK_LANG_GREEK :Грецька -STR_NETWORK_LANG_LATVIAN :Латвійська -############ End of leave-in-this-order - # Network game lobby STR_NETWORK_GAME_LOBBY_CAPTION :{WHITE}Мережева гра - кімната diff --git a/src/lang/unfinished/chuvash.txt b/src/lang/unfinished/chuvash.txt index 6686a54277..c5e617e841 100644 --- a/src/lang/unfinished/chuvash.txt +++ b/src/lang/unfinished/chuvash.txt @@ -769,10 +769,6 @@ STR_NETWORK_START_SERVER_SET_PASSWORD :{BLACK}Вырн -# Network game languages -############ Leave those lines in this order!! -############ End of leave-in-this-order - # Network game lobby diff --git a/src/lang/unfinished/frisian.txt b/src/lang/unfinished/frisian.txt index 4344540a31..4a7178629e 100644 --- a/src/lang/unfinished/frisian.txt +++ b/src/lang/unfinished/frisian.txt @@ -1855,46 +1855,6 @@ STR_NETWORK_START_SERVER_LANGUAGE_SPOKEN :{BLACK}Sprutsen STR_NETWORK_START_SERVER_NEW_GAME_NAME_OSKTITLE :{BLACK}Fier in namme yn foar it networkspul -# Network game languages -############ Leave those lines in this order!! -STR_NETWORK_LANG_ANY :Alle -STR_NETWORK_LANG_ENGLISH :Ingelsk -STR_NETWORK_LANG_GERMAN :Dútsk -STR_NETWORK_LANG_FRENCH :Frânsk -STR_NETWORK_LANG_BRAZILIAN :Braziliaansk -STR_NETWORK_LANG_BULGARIAN :Bulgaarsk -STR_NETWORK_LANG_CHINESE :Sinees -STR_NETWORK_LANG_CZECH :Tsjechysk -STR_NETWORK_LANG_DANISH :Deensk -STR_NETWORK_LANG_DUTCH :Nederlânsk -STR_NETWORK_LANG_ESPERANTO :Esperanto -STR_NETWORK_LANG_FINNISH :Finsk -STR_NETWORK_LANG_HUNGARIAN :Hongaarsk -STR_NETWORK_LANG_ICELANDIC :Yslânsk -STR_NETWORK_LANG_ITALIAN :Italjaansk -STR_NETWORK_LANG_JAPANESE :Japansk -STR_NETWORK_LANG_KOREAN :Koreaansk -STR_NETWORK_LANG_LITHUANIAN :Litousk -STR_NETWORK_LANG_NORWEGIAN :Noarsk -STR_NETWORK_LANG_POLISH :Poalsk -STR_NETWORK_LANG_PORTUGUESE :Portegeesk -STR_NETWORK_LANG_ROMANIAN :Roemeensk -STR_NETWORK_LANG_RUSSIAN :Russysk -STR_NETWORK_LANG_SLOVAK :Slowaaksk -STR_NETWORK_LANG_SLOVENIAN :Sloveensk -STR_NETWORK_LANG_SPANISH :Spaansk -STR_NETWORK_LANG_SWEDISH :Sweedsk -STR_NETWORK_LANG_TURKISH :Turksk -STR_NETWORK_LANG_UKRAINIAN :Oekraynsk -STR_NETWORK_LANG_AFRIKAANS :Afrikaansk -STR_NETWORK_LANG_CROATIAN :Kroätysk -STR_NETWORK_LANG_CATALAN :Katalaansk -STR_NETWORK_LANG_ESTONIAN :Estlânsk -STR_NETWORK_LANG_GALICIAN :Galisysk -STR_NETWORK_LANG_GREEK :Gryksk -STR_NETWORK_LANG_LATVIAN :Letsk -############ End of leave-in-this-order - # Network game lobby diff --git a/src/lang/unfinished/ido.txt b/src/lang/unfinished/ido.txt index a98bbd7f26..ba04de0dee 100644 --- a/src/lang/unfinished/ido.txt +++ b/src/lang/unfinished/ido.txt @@ -617,10 +617,6 @@ STR_NETWORK_SERVER_LIST_MAP_SIZE_SHORT :{BLACK}{COMMA}x -# Network game languages -############ Leave those lines in this order!! -############ End of leave-in-this-order - # Network game lobby diff --git a/src/lang/unfinished/macedonian.txt b/src/lang/unfinished/macedonian.txt index e0657af69c..064d0b8e91 100644 --- a/src/lang/unfinished/macedonian.txt +++ b/src/lang/unfinished/macedonian.txt @@ -975,10 +975,6 @@ STR_NETWORK_SERVER_LIST_MAP_SIZE_SHORT :{BLACK}{COMMA}x -# Network game languages -############ Leave those lines in this order!! -############ End of leave-in-this-order - # Network game lobby diff --git a/src/lang/unfinished/maltese.txt b/src/lang/unfinished/maltese.txt index a30a718b7e..ea5b7518d8 100644 --- a/src/lang/unfinished/maltese.txt +++ b/src/lang/unfinished/maltese.txt @@ -543,10 +543,6 @@ STR_NETWORK_SERVER_LIST_MAP_SIZE_SHORT :{BLACK}{COMMA}x -# Network game languages -############ Leave those lines in this order!! -############ End of leave-in-this-order - # Network game lobby diff --git a/src/lang/unfinished/marathi.txt b/src/lang/unfinished/marathi.txt index de8d4bec13..e7ac24eac5 100644 --- a/src/lang/unfinished/marathi.txt +++ b/src/lang/unfinished/marathi.txt @@ -911,37 +911,6 @@ STR_NETWORK_SERVER_LIST_LANGUAGE :{SILVER}भा -# Network game languages -############ Leave those lines in this order!! -STR_NETWORK_LANG_ENGLISH :इंग्रजी -STR_NETWORK_LANG_GERMAN :जर्मन -STR_NETWORK_LANG_FRENCH :फ्रेंच -STR_NETWORK_LANG_BRAZILIAN :ब्रज़िलिअन -STR_NETWORK_LANG_BULGARIAN :बल्जेरिअन -STR_NETWORK_LANG_CHINESE :चिनी -STR_NETWORK_LANG_CZECH :झेख -STR_NETWORK_LANG_DANISH :डेनिश -STR_NETWORK_LANG_DUTCH :डच -STR_NETWORK_LANG_ESPERANTO :एस्परांतो -STR_NETWORK_LANG_FINNISH :फिन्निश -STR_NETWORK_LANG_HUNGARIAN :हंगेरियन -STR_NETWORK_LANG_ICELANDIC :आईसलेंडीक -STR_NETWORK_LANG_JAPANESE :जपानी -STR_NETWORK_LANG_POLISH :पोलिश -STR_NETWORK_LANG_PORTUGUESE :पोर्चुगीस -STR_NETWORK_LANG_ROMANIAN :रोमेनिअन -STR_NETWORK_LANG_RUSSIAN :रशिअन -STR_NETWORK_LANG_SLOVAK :स्लोवेक -STR_NETWORK_LANG_SLOVENIAN :स्लोवेनिअन -STR_NETWORK_LANG_SPANISH :स्पेनिश -STR_NETWORK_LANG_SWEDISH :स्वीडिश -STR_NETWORK_LANG_TURKISH :तुर्की -STR_NETWORK_LANG_UKRAINIAN :युक्रेनिअन -STR_NETWORK_LANG_AFRIKAANS :आफ्रिकान्स -STR_NETWORK_LANG_CROATIAN :क्रोएशिअन -STR_NETWORK_LANG_GREEK :ग्रीक -############ End of leave-in-this-order - # Network game lobby diff --git a/src/lang/unfinished/persian.txt b/src/lang/unfinished/persian.txt index 715539577f..ac5c6ae40c 100644 --- a/src/lang/unfinished/persian.txt +++ b/src/lang/unfinished/persian.txt @@ -1666,46 +1666,6 @@ STR_NETWORK_START_SERVER_LANGUAGE_TOOLTIP :{BLACK}دیگر STR_NETWORK_START_SERVER_NEW_GAME_NAME_OSKTITLE :{BLACK}برای بازی شبکه‌ای یک نام وارد نمایید -# Network game languages -############ Leave those lines in this order!! -STR_NETWORK_LANG_ANY :هر زبانی -STR_NETWORK_LANG_ENGLISH :انگلیسی -STR_NETWORK_LANG_GERMAN :آلمانی -STR_NETWORK_LANG_FRENCH :فرانسوی -STR_NETWORK_LANG_BRAZILIAN :برزیلی -STR_NETWORK_LANG_BULGARIAN :بلغاری -STR_NETWORK_LANG_CHINESE :چینی -STR_NETWORK_LANG_CZECH :چکی -STR_NETWORK_LANG_DANISH :دانمارکی -STR_NETWORK_LANG_DUTCH :هلندی -STR_NETWORK_LANG_ESPERANTO :اسپرانتو -STR_NETWORK_LANG_FINNISH :فنلاندی -STR_NETWORK_LANG_HUNGARIAN :مجارستانی -STR_NETWORK_LANG_ICELANDIC :ایسلندی -STR_NETWORK_LANG_ITALIAN :ایتالیایی -STR_NETWORK_LANG_JAPANESE :زاپنی -STR_NETWORK_LANG_KOREAN :کُره‌ای -STR_NETWORK_LANG_LITHUANIAN :لیتوانیایی -STR_NETWORK_LANG_NORWEGIAN :نروژی -STR_NETWORK_LANG_POLISH :لهستانی -STR_NETWORK_LANG_PORTUGUESE :پرتغالی -STR_NETWORK_LANG_ROMANIAN :رومانیایی -STR_NETWORK_LANG_RUSSIAN :روسی -STR_NETWORK_LANG_SLOVAK :اسلوواکیایی -STR_NETWORK_LANG_SLOVENIAN :اسلوونیایی -STR_NETWORK_LANG_SPANISH :اسپانیایی -STR_NETWORK_LANG_SWEDISH :سوئدی -STR_NETWORK_LANG_TURKISH :ترکی -STR_NETWORK_LANG_UKRAINIAN :اوکراینی -STR_NETWORK_LANG_AFRIKAANS :آفریقایی -STR_NETWORK_LANG_CROATIAN :کرواسی -STR_NETWORK_LANG_CATALAN :کاتالانی -STR_NETWORK_LANG_ESTONIAN :استوونی -STR_NETWORK_LANG_GALICIAN :گالیسیایی -STR_NETWORK_LANG_GREEK :یونانی -STR_NETWORK_LANG_LATVIAN :لاتویانی -############ End of leave-in-this-order - # Network game lobby STR_NETWORK_GAME_LOBBY_CAPTION :{WHITE}سالن انتظار بازی چندنفره diff --git a/src/lang/unfinished/urdu.txt b/src/lang/unfinished/urdu.txt index 9c59f4b14e..70cb48636b 100644 --- a/src/lang/unfinished/urdu.txt +++ b/src/lang/unfinished/urdu.txt @@ -1554,46 +1554,6 @@ STR_NETWORK_START_SERVER_LANGUAGE_TOOLTIP :{BLACK}دوسر STR_NETWORK_START_SERVER_NEW_GAME_NAME_OSKTITLE :{BLACK}نیٹ ورک والے کھیل کا نام لکھیں -# Network game languages -############ Leave those lines in this order!! -STR_NETWORK_LANG_ANY :کوئی بھی -STR_NETWORK_LANG_ENGLISH :انگریزی -STR_NETWORK_LANG_GERMAN :جرمن -STR_NETWORK_LANG_FRENCH :فرانسیسی -STR_NETWORK_LANG_BRAZILIAN :برازیلین -STR_NETWORK_LANG_BULGARIAN :بلغارین -STR_NETWORK_LANG_CHINESE :چائینیز -STR_NETWORK_LANG_CZECH :چیک -STR_NETWORK_LANG_DANISH :ڈینیش -STR_NETWORK_LANG_DUTCH :ڈچ -STR_NETWORK_LANG_ESPERANTO :Esperanto -STR_NETWORK_LANG_FINNISH :فِنش -STR_NETWORK_LANG_HUNGARIAN :ہنگرین -STR_NETWORK_LANG_ICELANDIC :Icelandic -STR_NETWORK_LANG_ITALIAN :اٹالین -STR_NETWORK_LANG_JAPANESE :جاپانی -STR_NETWORK_LANG_KOREAN :کورین -STR_NETWORK_LANG_LITHUANIAN :Lithuanian -STR_NETWORK_LANG_NORWEGIAN :ناورویجین -STR_NETWORK_LANG_POLISH :پولش -STR_NETWORK_LANG_PORTUGUESE :پُرتگیزی -STR_NETWORK_LANG_ROMANIAN :رومانین -STR_NETWORK_LANG_RUSSIAN :رشین -STR_NETWORK_LANG_SLOVAK :Slovak -STR_NETWORK_LANG_SLOVENIAN :Slovenian -STR_NETWORK_LANG_SPANISH :اسپینش -STR_NETWORK_LANG_SWEDISH :سویڈیش -STR_NETWORK_LANG_TURKISH :ترکش -STR_NETWORK_LANG_UKRAINIAN :یوکرانین -STR_NETWORK_LANG_AFRIKAANS :افریقین -STR_NETWORK_LANG_CROATIAN :کروشین -STR_NETWORK_LANG_CATALAN :Catalan -STR_NETWORK_LANG_ESTONIAN :Estonian -STR_NETWORK_LANG_GALICIAN :Galician -STR_NETWORK_LANG_GREEK :گریک -STR_NETWORK_LANG_LATVIAN :Latvian -############ End of leave-in-this-order - # Network game lobby STR_NETWORK_GAME_LOBBY_CAPTION :{WHITE}زیادہ کھلاڑیوں والے کھیل کی لابی diff --git a/src/lang/vietnamese.txt b/src/lang/vietnamese.txt index 5a94da6acf..da4bf597b5 100644 --- a/src/lang/vietnamese.txt +++ b/src/lang/vietnamese.txt @@ -2068,46 +2068,6 @@ STR_NETWORK_START_SERVER_LANGUAGE_TOOLTIP :{BLACK}Những STR_NETWORK_START_SERVER_NEW_GAME_NAME_OSKTITLE :{BLACK}Nhập tên của ván chơi mạng -# Network game languages -############ Leave those lines in this order!! -STR_NETWORK_LANG_ANY :bất kỳ -STR_NETWORK_LANG_ENGLISH :Tiếng Anh -STR_NETWORK_LANG_GERMAN :Tiếng Đức -STR_NETWORK_LANG_FRENCH :Tiếng Pháp -STR_NETWORK_LANG_BRAZILIAN :Tiếng Brazil -STR_NETWORK_LANG_BULGARIAN :Tiếng Bun-ga-ri -STR_NETWORK_LANG_CHINESE :Tiếng Trung Quốc -STR_NETWORK_LANG_CZECH :Tiếng Séc -STR_NETWORK_LANG_DANISH :Tiếng Đan Mạch -STR_NETWORK_LANG_DUTCH :Tiếng Hà Lan -STR_NETWORK_LANG_ESPERANTO :Tiếng Quốc tế ngữ Esperanto -STR_NETWORK_LANG_FINNISH :Tiếng Phần Lan -STR_NETWORK_LANG_HUNGARIAN :Tiếng Hung-ga-ri -STR_NETWORK_LANG_ICELANDIC :Tiếng Ai-len -STR_NETWORK_LANG_ITALIAN :Tiếng Ý -STR_NETWORK_LANG_JAPANESE :Tiếng Nhật Bản -STR_NETWORK_LANG_KOREAN :Tiếng Hàn Quốc -STR_NETWORK_LANG_LITHUANIAN :Tiếng Lát-vi -STR_NETWORK_LANG_NORWEGIAN :Tiếng Na-uy -STR_NETWORK_LANG_POLISH :Tiếng Ba Lan -STR_NETWORK_LANG_PORTUGUESE :Tiếng Bồ Đào Nha -STR_NETWORK_LANG_ROMANIAN :Tiếng Ru-ma-ni -STR_NETWORK_LANG_RUSSIAN :Tiếng Nga -STR_NETWORK_LANG_SLOVAK :Tiếng Slo-va-kia -STR_NETWORK_LANG_SLOVENIAN :Tiếng Slo-ven-nhi-a -STR_NETWORK_LANG_SPANISH :Tiếng Tây Ban Nha -STR_NETWORK_LANG_SWEDISH :Tiếng Thuỵ Điển -STR_NETWORK_LANG_TURKISH :Tiếng Thổ Nhĩ Kỳ -STR_NETWORK_LANG_UKRAINIAN :Tiếng U-crai-na -STR_NETWORK_LANG_AFRIKAANS :Tiếng Nam Phi -STR_NETWORK_LANG_CROATIAN :Tiếng Croa-ti-a -STR_NETWORK_LANG_CATALAN :Tiếng Catalan -STR_NETWORK_LANG_ESTONIAN :Tiếng Esto-nhi-a -STR_NETWORK_LANG_GALICIAN :Tiếng Galician -STR_NETWORK_LANG_GREEK :Tiếng Hy Lạp -STR_NETWORK_LANG_LATVIAN :Tiếng Lat-via -############ End of leave-in-this-order - # Network game lobby STR_NETWORK_GAME_LOBBY_CAPTION :{WHITE}Sảnh chơi game nhiều người diff --git a/src/lang/welsh.txt b/src/lang/welsh.txt index 12bbec9bd4..f08bd8ff12 100644 --- a/src/lang/welsh.txt +++ b/src/lang/welsh.txt @@ -1953,46 +1953,6 @@ STR_NETWORK_START_SERVER_LANGUAGE_TOOLTIP :{BLACK}Rhoi gwy STR_NETWORK_START_SERVER_NEW_GAME_NAME_OSKTITLE :{BLACK}Rhowch enw ar gyfer y gêm rhwydwaith -# Network game languages -############ Leave those lines in this order!! -STR_NETWORK_LANG_ANY :Unrhyw -STR_NETWORK_LANG_ENGLISH :Saesneg -STR_NETWORK_LANG_GERMAN :Almaeneg -STR_NETWORK_LANG_FRENCH :Ffrangeg -STR_NETWORK_LANG_BRAZILIAN :Brasilaidd -STR_NETWORK_LANG_BULGARIAN :Bwlgaraidd -STR_NETWORK_LANG_CHINESE :Tseineeg -STR_NETWORK_LANG_CZECH :Tsiecaidd -STR_NETWORK_LANG_DANISH :Daneg -STR_NETWORK_LANG_DUTCH :Iseldireg -STR_NETWORK_LANG_ESPERANTO :Esperanto -STR_NETWORK_LANG_FINNISH :Ffineg -STR_NETWORK_LANG_HUNGARIAN :Hwngareg -STR_NETWORK_LANG_ICELANDIC :Islandaidd -STR_NETWORK_LANG_ITALIAN :Eidaleg -STR_NETWORK_LANG_JAPANESE :Siapaneaidd -STR_NETWORK_LANG_KOREAN :Coreeg -STR_NETWORK_LANG_LITHUANIAN :Lithiwaneg -STR_NETWORK_LANG_NORWEGIAN :Norwyeg -STR_NETWORK_LANG_POLISH :Pwyleg -STR_NETWORK_LANG_PORTUGUESE :Portiwgaleg -STR_NETWORK_LANG_ROMANIAN :Rwmaneg -STR_NETWORK_LANG_RUSSIAN :Rwsieg -STR_NETWORK_LANG_SLOVAK :Slofaceg -STR_NETWORK_LANG_SLOVENIAN :Slofeneg -STR_NETWORK_LANG_SPANISH :Sbaeneg -STR_NETWORK_LANG_SWEDISH :Swedeg -STR_NETWORK_LANG_TURKISH :Twrceg -STR_NETWORK_LANG_UKRAINIAN :Wcraneg -STR_NETWORK_LANG_AFRIKAANS :Affricâns -STR_NETWORK_LANG_CROATIAN :Croatieg -STR_NETWORK_LANG_CATALAN :Catalaneg -STR_NETWORK_LANG_ESTONIAN :Estoneg -STR_NETWORK_LANG_GALICIAN :Galiseg -STR_NETWORK_LANG_GREEK :Groeg -STR_NETWORK_LANG_LATVIAN :Latfieg -############ End of leave-in-this-order - # Network game lobby STR_NETWORK_GAME_LOBBY_CAPTION :{WHITE}Cyntedd Gemau Amlchwaraewr From d4f0b6f434911840e2762f89ca0de460f38133f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Guilloux?= Date: Tue, 20 Apr 2021 21:38:46 +0200 Subject: [PATCH 099/268] Fix: [CMake] Auto-fill version details in rev.cpp and ottres.rc (#9066) --- CMakeLists.txt | 7 ++++++- cmake/scripts/FindVersion.cmake | 10 ++++++++++ src/os/windows/ottdres.rc.in | 4 ++-- src/rev.cpp.in | 2 +- 4 files changed, 19 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 63a0ce2fe8..87d055047f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,7 +4,9 @@ if(NOT BINARY_NAME) set(BINARY_NAME openttd) endif() -project(${BINARY_NAME}) +project(${BINARY_NAME} + VERSION 1.12.0 +) if(CMAKE_SOURCE_DIR STREQUAL CMAKE_BINARY_DIR) message(FATAL_ERROR "In-source builds not allowed. Please run \"cmake ..\" from the build directory. You may need to delete \"${CMAKE_SOURCE_DIR}/CMakeCache.txt\" first.") @@ -72,6 +74,9 @@ add_custom_target(find_version ${CMAKE_COMMAND} -DFIND_VERSION_BINARY_DIR=${CMAKE_BINARY_DIR}/generated -DCPACK_BINARY_DIR=${CMAKE_BINARY_DIR} + -DREV_MAJOR=${CMAKE_PROJECT_VERSION_MAJOR} + -DREV_MINOR=${CMAKE_PROJECT_VERSION_MINOR} + -DREV_BUILD=${CMAKE_PROJECT_VERSION_PATCH} -P "${CMAKE_SOURCE_DIR}/cmake/scripts/FindVersion.cmake" WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} BYPRODUCTS ${GENERATED_SOURCE_FILES} diff --git a/cmake/scripts/FindVersion.cmake b/cmake/scripts/FindVersion.cmake index ebbe58244a..6e4a2b3909 100644 --- a/cmake/scripts/FindVersion.cmake +++ b/cmake/scripts/FindVersion.cmake @@ -1,5 +1,15 @@ cmake_minimum_required(VERSION 3.5) +if(NOT REV_MAJOR) + set(REV_MAJOR 0) +endif() +if(NOT REV_MINOR) + set(REV_MINOR 0) +endif() +if(NOT REV_BUILD) + set(REV_BUILD 0) +endif() + # # Finds the current version of the current folder. # diff --git a/src/os/windows/ottdres.rc.in b/src/os/windows/ottdres.rc.in index 741fa0e105..31309382cf 100644 --- a/src/os/windows/ottdres.rc.in +++ b/src/os/windows/ottdres.rc.in @@ -77,8 +77,8 @@ END // VS_VERSION_INFO VERSIONINFO - FILEVERSION 1,11,0,${REV_ISODATE} - PRODUCTVERSION 1,11,0,${REV_ISODATE} + FILEVERSION ${REV_MAJOR},${REV_MINOR},${REV_BUILD},${REV_ISODATE} + PRODUCTVERSION ${REV_MAJOR},${REV_MINOR},${REV_BUILD},${REV_ISODATE} FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L diff --git a/src/rev.cpp.in b/src/rev.cpp.in index 208588a711..3939ca5a0e 100644 --- a/src/rev.cpp.in +++ b/src/rev.cpp.in @@ -85,4 +85,4 @@ const byte _openttd_revision_tagged = ${REV_ISTAG}; * final release will always have a lower version number than the released * version, thus making comparisons on specific revisions easy. */ -const uint32 _openttd_newgrf_version = 1 << 28 | 12 << 24 | 0 << 20 | ${REV_ISSTABLETAG} << 19 | 28004; +const uint32 _openttd_newgrf_version = ${REV_MAJOR} << 28 | ${REV_MINOR} << 24 | ${REV_BUILD} << 20 | ${REV_ISSTABLETAG} << 19 | 28004; From b3495f1a1323eb6d236c6fecb1127a0bd716a4d5 Mon Sep 17 00:00:00 2001 From: translators Date: Wed, 21 Apr 2021 17:51:39 +0000 Subject: [PATCH 100/268] Update: Translations from eints spanish (mexican): 2 changes by absay --- src/lang/spanish_MX.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/lang/spanish_MX.txt b/src/lang/spanish_MX.txt index 32a336c3e4..dff43e4eab 100644 --- a/src/lang/spanish_MX.txt +++ b/src/lang/spanish_MX.txt @@ -1007,6 +1007,8 @@ STR_GAME_OPTIONS_VIDEO_ACCELERATION :{BLACK}Acelerac STR_GAME_OPTIONS_VIDEO_ACCELERATION_TOOLTIP :{BLACK}Activar esta casilla para intentar emplear la aceleración por hardware. Este cambio solo tiene efecto tras reiniciar el juego STR_GAME_OPTIONS_VIDEO_ACCELERATION_RESTART :{WHITE}Esta configuración solo tendrá efecto después de reiniciar el juego +STR_GAME_OPTIONS_VIDEO_VSYNC :{BLACK}VSync +STR_GAME_OPTIONS_VIDEO_VSYNC_TOOLTIP :{BLACK}Activar esta casilla para realizar sincronización vertical de pantalla (VSync). Este cambio solo tiene efecto tras reiniciar el juego y si la aceleración por hardware está activada. STR_GAME_OPTIONS_GUI_ZOOM_FRAME :{BLACK}Tamaño de la interfaz STR_GAME_OPTIONS_GUI_ZOOM_DROPDOWN_TOOLTIP :{BLACK}Elegir el tamaño de los elementos de la interfaz From bf4fe19a6684231d04764c3bee5ed51e4124a925 Mon Sep 17 00:00:00 2001 From: Rubidium Date: Sun, 18 Apr 2021 20:29:46 +0200 Subject: [PATCH 101/268] Codechange: merge duplicated logic to scroll in lists by key into a single function --- src/network/network_content_gui.cpp | 64 +++++++++-------------------- src/network/network_gui.cpp | 39 ++---------------- src/newgrf_gui.cpp | 36 +--------------- src/widget.cpp | 60 +++++++++++++++++++++++++++ src/widget_type.h | 1 + 5 files changed, 86 insertions(+), 114 deletions(-) diff --git a/src/network/network_content_gui.cpp b/src/network/network_content_gui.cpp index 4662791257..0cd711877d 100644 --- a/src/network/network_content_gui.cpp +++ b/src/network/network_content_gui.cpp @@ -863,55 +863,31 @@ public: EventState OnKeyPress(WChar key, uint16 keycode) override { - switch (keycode) { - case WKC_UP: - /* scroll up by one */ - if (this->list_pos > 0) this->list_pos--; - break; - case WKC_DOWN: - /* scroll down by one */ - if (this->list_pos < (int)this->content.size() - 1) this->list_pos++; - break; - case WKC_PAGEUP: - /* scroll up a page */ - this->list_pos = (this->list_pos < this->vscroll->GetCapacity()) ? 0 : this->list_pos - this->vscroll->GetCapacity(); - break; - case WKC_PAGEDOWN: - /* scroll down a page */ - this->list_pos = std::min(this->list_pos + this->vscroll->GetCapacity(), (int)this->content.size() - 1); - break; - case WKC_HOME: - /* jump to beginning */ - this->list_pos = 0; - break; - case WKC_END: - /* jump to end */ - this->list_pos = (int)this->content.size() - 1; - break; - - case WKC_SPACE: - case WKC_RETURN: - if (keycode == WKC_RETURN || !IsWidgetFocused(WID_NCL_FILTER)) { - if (this->selected != nullptr) { - _network_content_client.ToggleSelectedState(this->selected); - this->content.ForceResort(); - this->InvalidateData(); + if (this->vscroll->UpdateListPositionOnKeyPress(this->list_pos, keycode) == ES_NOT_HANDLED) { + switch (keycode) { + case WKC_SPACE: + case WKC_RETURN: + if (keycode == WKC_RETURN || !IsWidgetFocused(WID_NCL_FILTER)) { + if (this->selected != nullptr) { + _network_content_client.ToggleSelectedState(this->selected); + this->content.ForceResort(); + this->InvalidateData(); + } + if (this->filter_data.types.any()) { + this->content.ForceRebuild(); + this->InvalidateData(); + } + return ES_HANDLED; } - if (this->filter_data.types.any()) { - this->content.ForceRebuild(); - this->InvalidateData(); - } - return ES_HANDLED; - } - /* space is pressed and filter is focused. */ - FALLTHROUGH; + /* space is pressed and filter is focused. */ + FALLTHROUGH; - default: - return ES_NOT_HANDLED; + default: + return ES_NOT_HANDLED; + } } if (this->content.size() == 0) { - this->list_pos = 0; // above stuff may result in "-1". if (this->UpdateFilterState()) { this->content.ForceRebuild(); this->InvalidateData(); diff --git a/src/network/network_gui.cpp b/src/network/network_gui.cpp index 78b0d9dfc3..1c80f7f3ce 100644 --- a/src/network/network_gui.cpp +++ b/src/network/network_gui.cpp @@ -66,8 +66,8 @@ void UpdateNetworkGameWindow() } typedef GUIList GUIGameServerList; -typedef uint16 ServerListPosition; -static const ServerListPosition SLP_INVALID = 0xFFFF; +typedef int ServerListPosition; +static const ServerListPosition SLP_INVALID = -1; /** Full blown container to make it behave exactly as we want :) */ class NWidgetServerListHeader : public NWidgetContainer { @@ -771,39 +771,8 @@ public: EventState state = ES_NOT_HANDLED; /* handle up, down, pageup, pagedown, home and end */ - if (keycode == WKC_UP || keycode == WKC_DOWN || keycode == WKC_PAGEUP || keycode == WKC_PAGEDOWN || keycode == WKC_HOME || keycode == WKC_END) { - if (this->servers.size() == 0) return ES_HANDLED; - switch (keycode) { - case WKC_UP: - /* scroll up by one */ - if (this->list_pos == SLP_INVALID) return ES_HANDLED; - if (this->list_pos > 0) this->list_pos--; - break; - case WKC_DOWN: - /* scroll down by one */ - if (this->list_pos == SLP_INVALID) return ES_HANDLED; - if (this->list_pos < this->servers.size() - 1) this->list_pos++; - break; - case WKC_PAGEUP: - /* scroll up a page */ - if (this->list_pos == SLP_INVALID) return ES_HANDLED; - this->list_pos = (this->list_pos < this->vscroll->GetCapacity()) ? 0 : this->list_pos - this->vscroll->GetCapacity(); - break; - case WKC_PAGEDOWN: - /* scroll down a page */ - if (this->list_pos == SLP_INVALID) return ES_HANDLED; - this->list_pos = std::min(this->list_pos + this->vscroll->GetCapacity(), (int)this->servers.size() - 1); - break; - case WKC_HOME: - /* jump to beginning */ - this->list_pos = 0; - break; - case WKC_END: - /* jump to end */ - this->list_pos = (ServerListPosition)this->servers.size() - 1; - break; - default: NOT_REACHED(); - } + if (this->vscroll->UpdateListPositionOnKeyPress(this->list_pos, keycode) == ES_HANDLED) { + if (this->list_pos == SLP_INVALID) return ES_HANDLED; this->server = this->servers[this->list_pos]; diff --git a/src/newgrf_gui.cpp b/src/newgrf_gui.cpp index 50e402ffb9..fe2510f74b 100644 --- a/src/newgrf_gui.cpp +++ b/src/newgrf_gui.cpp @@ -1318,42 +1318,8 @@ struct NewGRFWindow : public Window, NewGRFScanCallback { { if (!this->editable) return ES_NOT_HANDLED; - switch (keycode) { - case WKC_UP: - /* scroll up by one */ - if (this->avail_pos > 0) this->avail_pos--; - break; + if (this->vscroll2->UpdateListPositionOnKeyPress(this->avail_pos, keycode) == ES_NOT_HANDLED) return ES_NOT_HANDLED; - case WKC_DOWN: - /* scroll down by one */ - if (this->avail_pos < (int)this->avails.size() - 1) this->avail_pos++; - break; - - case WKC_PAGEUP: - /* scroll up a page */ - this->avail_pos = (this->avail_pos < this->vscroll2->GetCapacity()) ? 0 : this->avail_pos - this->vscroll2->GetCapacity(); - break; - - case WKC_PAGEDOWN: - /* scroll down a page */ - this->avail_pos = std::min(this->avail_pos + this->vscroll2->GetCapacity(), (int)this->avails.size() - 1); - break; - - case WKC_HOME: - /* jump to beginning */ - this->avail_pos = 0; - break; - - case WKC_END: - /* jump to end */ - this->avail_pos = (uint)this->avails.size() - 1; - break; - - default: - return ES_NOT_HANDLED; - } - - if (this->avails.size() == 0) this->avail_pos = -1; if (this->avail_pos >= 0) { this->active_sel = nullptr; DeleteWindowByClass(WC_GRF_PARAMETERS); diff --git a/src/widget.cpp b/src/widget.cpp index 9de848a637..4be4cf3317 100644 --- a/src/widget.cpp +++ b/src/widget.cpp @@ -1974,6 +1974,66 @@ int Scrollbar::GetScrolledRowFromWidget(int clickpos, const Window * const w, in return (pos >= this->GetCount()) ? INT_MAX : pos; } +/** + * Update the given list position as if it were on this scroll bar when the given keycode was pressed. + * This does not update the actual position of this scroll bar, that is left to the caller. It does, + * however use the capacity and count of the scroll bar for the bounds and amount to scroll. + * + * When the count is 0 or the return is ES_NOT_HANDLED, then the position is not updated. + * With WKC_UP and WKC_DOWN the position goes one up or down respectively. + * With WKC_PAGEUP and WKC_PAGEDOWN the position goes one capacity up or down respectively. + * With WKC_HOME the first position is selected and with WKC_END the last position is selected. + * This function ensures that pos is in the range [0..count). + * @param list_position The current position in the list. + * @param key_code The pressed key code. + * @return ES_NOT_HANDLED when another key than the 6 specific keys was pressed, otherwise ES_HANDLED. + */ +EventState Scrollbar::UpdateListPositionOnKeyPress(int &list_position, uint16 keycode) const +{ + int new_pos = list_position; + switch (keycode) { + case WKC_UP: + /* scroll up by one */ + new_pos--; + break; + + case WKC_DOWN: + /* scroll down by one */ + new_pos++; + break; + + case WKC_PAGEUP: + /* scroll up a page */ + new_pos -= this->GetCapacity(); + break; + + case WKC_PAGEDOWN: + /* scroll down a page */ + new_pos += this->GetCapacity(); + break; + + case WKC_HOME: + /* jump to beginning */ + new_pos = 0; + break; + + case WKC_END: + /* jump to end */ + new_pos = this->GetCount() - 1; + break; + + default: + return ES_NOT_HANDLED; + } + + /* If there are no elements, there is nothing to scroll/update. */ + if (this->GetCount() != 0) { + list_position = Clamp(new_pos, 0, this->GetCount() - 1); + } + return ES_HANDLED; +} + + /** * Set capacity of visible elements from the size and resize properties of a widget. * @param w Window. diff --git a/src/widget_type.h b/src/widget_type.h index 1692ef2266..c88727a2f0 100644 --- a/src/widget_type.h +++ b/src/widget_type.h @@ -749,6 +749,7 @@ public: } int GetScrolledRowFromWidget(int clickpos, const Window * const w, int widget, int padding = 0, int line_height = -1) const; + EventState UpdateListPositionOnKeyPress(int &list_position, uint16 keycode) const; }; /** From 5ff15443e9160d2669ee9f54a3cefd0c43537320 Mon Sep 17 00:00:00 2001 From: Peter Nelson Date: Wed, 21 Apr 2021 13:42:30 +0100 Subject: [PATCH 102/268] Cleanup: Replace single-use Pair struct with std::pair. This struct is defined in geometry_type but not used by any geometry-related code, only for subsidy code where both parameters are cast from int to NewsReferenceType. --- src/core/geometry_type.hpp | 6 ------ src/subsidy.cpp | 23 ++++++++++------------- src/subsidy_func.h | 3 ++- 3 files changed, 12 insertions(+), 20 deletions(-) diff --git a/src/core/geometry_type.hpp b/src/core/geometry_type.hpp index 92a1d97df6..cba944fb6f 100644 --- a/src/core/geometry_type.hpp +++ b/src/core/geometry_type.hpp @@ -62,10 +62,4 @@ struct PointDimension { int height; }; -/** A pair of two integers */ -struct Pair { - int a; - int b; -}; - #endif /* GEOMETRY_TYPE_HPP */ diff --git a/src/subsidy.cpp b/src/subsidy.cpp index ff420455d8..2668a62bd7 100644 --- a/src/subsidy.cpp +++ b/src/subsidy.cpp @@ -50,14 +50,14 @@ void Subsidy::AwardTo(CompanyID company) char *cn = stredup(company_name); /* Add a news item */ - Pair reftype = SetupSubsidyDecodeParam(this, false); + std::pair reftype = SetupSubsidyDecodeParam(this, false); InjectDParam(1); SetDParamStr(0, cn); AddNewsItem( STR_NEWS_SERVICE_SUBSIDY_AWARDED_HALF + _settings_game.difficulty.subsidy_multiplier, NT_SUBSIDIES, NF_NORMAL, - (NewsReferenceType)reftype.a, this->src, (NewsReferenceType)reftype.b, this->dst, + reftype.first, this->src, reftype.second, this->dst, cn ); AI::BroadcastNewEvent(new ScriptEventSubsidyAwarded(this->index)); @@ -72,7 +72,7 @@ void Subsidy::AwardTo(CompanyID company) * @param mode Unit of cargo used, \c true means general name, \c false means singular form. * @return Reference of the subsidy in the news system. */ -Pair SetupSubsidyDecodeParam(const Subsidy *s, bool mode) +std::pair SetupSubsidyDecodeParam(const Subsidy *s, bool mode) { NewsReferenceType reftype1 = NR_NONE; NewsReferenceType reftype2 = NR_NONE; @@ -107,10 +107,7 @@ Pair SetupSubsidyDecodeParam(const Subsidy *s, bool mode) } SetDParam(5, s->dst); - Pair p; - p.a = reftype1; - p.b = reftype2; - return p; + return std::pair(reftype1, reftype2); } /** @@ -219,8 +216,8 @@ void CreateSubsidy(CargoID cid, SourceType src_type, SourceID src, SourceType ds s->remaining = SUBSIDY_OFFER_MONTHS; s->awarded = INVALID_COMPANY; - Pair reftype = SetupSubsidyDecodeParam(s, false); - AddNewsItem(STR_NEWS_SERVICE_SUBSIDY_OFFERED, NT_SUBSIDIES, NF_NORMAL, (NewsReferenceType)reftype.a, s->src, (NewsReferenceType)reftype.b, s->dst); + std::pair reftype = SetupSubsidyDecodeParam(s, false); + AddNewsItem(STR_NEWS_SERVICE_SUBSIDY_OFFERED, NT_SUBSIDIES, NF_NORMAL, reftype.first, s->src, reftype.second, s->dst); SetPartOfSubsidyFlag(s->src_type, s->src, POS_SRC); SetPartOfSubsidyFlag(s->dst_type, s->dst, POS_DST); AI::BroadcastNewEvent(new ScriptEventSubsidyOffer(s->index)); @@ -494,14 +491,14 @@ void SubsidyMonthlyLoop() for (Subsidy *s : Subsidy::Iterate()) { if (--s->remaining == 0) { if (!s->IsAwarded()) { - Pair reftype = SetupSubsidyDecodeParam(s, true); - AddNewsItem(STR_NEWS_OFFER_OF_SUBSIDY_EXPIRED, NT_SUBSIDIES, NF_NORMAL, (NewsReferenceType)reftype.a, s->src, (NewsReferenceType)reftype.b, s->dst); + std::pair reftype = SetupSubsidyDecodeParam(s, true); + AddNewsItem(STR_NEWS_OFFER_OF_SUBSIDY_EXPIRED, NT_SUBSIDIES, NF_NORMAL, reftype.first, s->src, reftype.second, s->dst); AI::BroadcastNewEvent(new ScriptEventSubsidyOfferExpired(s->index)); Game::NewEvent(new ScriptEventSubsidyOfferExpired(s->index)); } else { if (s->awarded == _local_company) { - Pair reftype = SetupSubsidyDecodeParam(s, true); - AddNewsItem(STR_NEWS_SUBSIDY_WITHDRAWN_SERVICE, NT_SUBSIDIES, NF_NORMAL, (NewsReferenceType)reftype.a, s->src, (NewsReferenceType)reftype.b, s->dst); + std::pair reftype = SetupSubsidyDecodeParam(s, true); + AddNewsItem(STR_NEWS_SUBSIDY_WITHDRAWN_SERVICE, NT_SUBSIDIES, NF_NORMAL, reftype.first, s->src, reftype.second, s->dst); } AI::BroadcastNewEvent(new ScriptEventSubsidyExpired(s->index)); Game::NewEvent(new ScriptEventSubsidyExpired(s->index)); diff --git a/src/subsidy_func.h b/src/subsidy_func.h index 4889ead249..cc63577d33 100644 --- a/src/subsidy_func.h +++ b/src/subsidy_func.h @@ -14,8 +14,9 @@ #include "station_type.h" #include "company_type.h" #include "cargo_type.h" +#include "news_type.h" -Pair SetupSubsidyDecodeParam(const struct Subsidy *s, bool mode); +std::pair SetupSubsidyDecodeParam(const struct Subsidy *s, bool mode); void DeleteSubsidyWith(SourceType type, SourceID index); bool CheckSubsidised(CargoID cargo_type, CompanyID company, SourceType src_type, SourceID src, const Station *st); void RebuildSubsidisedSourceAndDestinationCache(); From e53313391a77eacc23b9aac4a68b4ed0471e0271 Mon Sep 17 00:00:00 2001 From: Michael Lutz Date: Wed, 21 Apr 2021 22:06:04 +0200 Subject: [PATCH 103/268] Fix: [OpenGL] Check maximum supported texture size against screen resolution. --- src/video/cocoa/cocoa_ogl.mm | 2 +- src/video/opengl.cpp | 13 ++++++++++--- src/video/opengl.h | 4 ++-- src/video/sdl2_opengl_v.cpp | 2 +- src/video/win32_v.cpp | 2 +- 5 files changed, 15 insertions(+), 8 deletions(-) diff --git a/src/video/cocoa/cocoa_ogl.mm b/src/video/cocoa/cocoa_ogl.mm index f8c2e97e0d..96c3ea1bfe 100644 --- a/src/video/cocoa/cocoa_ogl.mm +++ b/src/video/cocoa/cocoa_ogl.mm @@ -254,7 +254,7 @@ const char *VideoDriver_CocoaOpenGL::AllocateContext(bool allow_software) CGLSetCurrentContext(this->gl_context); - return OpenGLBackend::Create(&GetOGLProcAddressCallback); + return OpenGLBackend::Create(&GetOGLProcAddressCallback, this->GetScreenSize()); } NSView *VideoDriver_CocoaOpenGL::AllocateDrawView() diff --git a/src/video/opengl.cpp b/src/video/opengl.cpp index bb509bcd74..8a4ce34514 100644 --- a/src/video/opengl.cpp +++ b/src/video/opengl.cpp @@ -464,16 +464,17 @@ void SetupDebugOutput() /** * Create and initialize the singleton back-end class. * @param get_proc Callback to get an OpenGL function from the OS driver. + * @param screen_res Current display resolution. * @return nullptr on success, error message otherwise. */ -/* static */ const char *OpenGLBackend::Create(GetOGLProcAddressProc get_proc) +/* static */ const char *OpenGLBackend::Create(GetOGLProcAddressProc get_proc, const Dimension &screen_res) { if (OpenGLBackend::instance != nullptr) OpenGLBackend::Destroy(); GetOGLProcAddress = get_proc; OpenGLBackend::instance = new OpenGLBackend(); - return OpenGLBackend::instance->Init(); + return OpenGLBackend::instance->Init(screen_res); } /** @@ -521,9 +522,10 @@ OpenGLBackend::~OpenGLBackend() /** * Check for the needed OpenGL functionality and allocate all resources. + * @param screen_res Current display resolution. * @return Error string or nullptr if successful. */ -const char *OpenGLBackend::Init() +const char *OpenGLBackend::Init(const Dimension &screen_res) { if (!BindBasicInfoProcs()) return "OpenGL not supported"; @@ -581,6 +583,11 @@ const char *OpenGLBackend::Init() } if (this->persistent_mapping_supported) DEBUG(driver, 3, "OpenGL: Using persistent buffer mapping"); + /* Check maximum texture size against screen resolution. */ + GLint max_tex_size = 0; + _glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max_tex_size); + if (std::max(screen_res.width, screen_res.height) > (uint)max_tex_size) return "Max supported texture size is too small"; + /* Check available texture units. */ GLint max_tex_units = 0; _glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &max_tex_units); diff --git a/src/video/opengl.h b/src/video/opengl.h index b0318f9882..e5cd749f2e 100644 --- a/src/video/opengl.h +++ b/src/video/opengl.h @@ -74,7 +74,7 @@ private: OpenGLBackend(); ~OpenGLBackend(); - const char *Init(); + const char *Init(const Dimension &screen_res); bool InitShaders(); void InternalClearCursorCache(); @@ -87,7 +87,7 @@ public: { return OpenGLBackend::instance; } - static const char *Create(GetOGLProcAddressProc get_proc); + static const char *Create(GetOGLProcAddressProc get_proc, const Dimension &screen_res); static void Destroy(); void PrepareContext(); diff --git a/src/video/sdl2_opengl_v.cpp b/src/video/sdl2_opengl_v.cpp index 202593e64a..9c84c36b1c 100644 --- a/src/video/sdl2_opengl_v.cpp +++ b/src/video/sdl2_opengl_v.cpp @@ -117,7 +117,7 @@ const char *VideoDriver_SDL_OpenGL::AllocateContext() ToggleVsync(_video_vsync); - return OpenGLBackend::Create(&GetOGLProcAddressCallback); + return OpenGLBackend::Create(&GetOGLProcAddressCallback, this->GetScreenSize()); } void VideoDriver_SDL_OpenGL::PopulateSystemSprites() diff --git a/src/video/win32_v.cpp b/src/video/win32_v.cpp index 08ab27c539..19e186ae2a 100644 --- a/src/video/win32_v.cpp +++ b/src/video/win32_v.cpp @@ -1380,7 +1380,7 @@ const char *VideoDriver_Win32OpenGL::AllocateContext() this->ToggleVsync(_video_vsync); this->gl_rc = rc; - return OpenGLBackend::Create(&GetOGLProcAddressCallback); + return OpenGLBackend::Create(&GetOGLProcAddressCallback, this->GetScreenSize()); } bool VideoDriver_Win32OpenGL::ToggleFullscreen(bool full_screen) From 91644df491fa652f2490b158e9eb153d9d6bcfd0 Mon Sep 17 00:00:00 2001 From: Patric Stout Date: Wed, 21 Apr 2021 19:22:59 +0200 Subject: [PATCH 104/268] Doc: update the supported platforms to match current master --- README.md | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 14104e041a..ee40e801b6 100644 --- a/README.md +++ b/README.md @@ -46,15 +46,13 @@ OpenTTD has a [community-maintained wiki](https://wiki.openttd.org/), including OpenTTD has been ported to several platforms and operating systems. -The currently working platforms are: +The currently supported platforms are: -- FreeBSD (SDL) -- Haiku (SDL) -- Linux (SDL) -- macOS (universal) (Cocoa video and sound drivers) -- OpenBSD (SDL) -- OS/2 (SDL) -- Windows (Win32 GDI (faster) or SDL) +- Linux (SDL (OpenGL and non-OpenGL)) +- macOS (universal) (Cocoa) +- Windows (Win32 GDI / OpenGL) + +Other platforms may also work (in particular various BSD systems), but we don't actively test or maintain these. ### 1.3.1) Legacy support Platforms, languages and compilers change. From e3150d0a85ad77f0fa5413f32794c9280c95286c Mon Sep 17 00:00:00 2001 From: Patric Stout Date: Wed, 21 Apr 2021 19:23:09 +0200 Subject: [PATCH 105/268] Doc: fix OpenGFX / OpenSFX / OpenMSX links in README --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index ee40e801b6..a5142ffecb 100644 --- a/README.md +++ b/README.md @@ -77,9 +77,9 @@ For some platforms, you will need to refer to [the installation guide](https://w The free data files, split into OpenGFX for graphics, OpenSFX for sounds and OpenMSX for music can be found at: -- https://www.openttd.org/download-opengfx for OpenGFX -- https://www.openttd.org/download-opensfx for OpenSFX -- https://www.openttd.org/download-openmsx for OpenMSX +- https://www.openttd.org/downloads/opengfx-releases/ for OpenGFX +- https://www.openttd.org/downloads/opensfx-releases/ for OpenSFX +- https://www.openttd.org/downloads/openmsx-releases/ for OpenMSX Please follow the readme of these packages about the installation procedure. The Windows installer can optionally download and install these packages. From 69355293aa2012f3dc778e88c76b71f0e65476cb Mon Sep 17 00:00:00 2001 From: Patric Stout Date: Wed, 21 Apr 2021 19:42:42 +0200 Subject: [PATCH 106/268] Doc: split up which libraries are used for which situation --- COMPILING.md | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/COMPILING.md b/COMPILING.md index 5e642dc874..4e573f0127 100644 --- a/COMPILING.md +++ b/COMPILING.md @@ -2,26 +2,27 @@ ## Required/optional libraries -The following libraries are used by OpenTTD for: +OpenTTD makes use of the following external libraries: -- zlib: (de)compressing of old (0.3.0-1.0.5) savegames, content downloads, +- (encouraged) zlib: (de)compressing of old (0.3.0-1.0.5) savegames, content downloads, heightmaps -- liblzo2: (de)compressing of old (pre 0.3.0) savegames -- liblzma: (de)compressing of savegames (1.1.0 and later) -- libpng: making screenshots and loading heightmaps +- (encouraged) liblzma: (de)compressing of savegames (1.1.0 and later) +- (encouraged) libpng: making screenshots and loading heightmaps +- (optional) liblzo2: (de)compressing of old (pre 0.3.0) savegames + +For Linux, the following additional libraries are used (for non-dedicated only): + +- libSDL2: hardware access (video, sound, mouse) - libfreetype: loading generic fonts and rendering them - libfontconfig: searching for fonts, resolving font names to actual fonts - libicu: handling of right-to-left scripts (e.g. Arabic and Persian) and - natural sorting of strings (Linux only) -- libSDL2: hardware access (video, sound, mouse) (not required for Windows or macOS) + natural sorting of strings OpenTTD does not require any of the libraries to be present, but without liblzma you cannot open most recent savegames and without zlib you cannot open most older savegames or use the content downloading system. -Without libSDL/liballegro on non-Windows and non-macOS machines you have -no graphical user interface; you would be building a dedicated server. -## Windows: +## Windows You need Microsoft Visual Studio 2017 or more recent. From 96b78bc2cd9790e89228c53b4803a78320fc20bd Mon Sep 17 00:00:00 2001 From: Patric Stout Date: Wed, 21 Apr 2021 19:43:00 +0200 Subject: [PATCH 107/268] Doc: indicate the most common options for CMake --- COMPILING.md | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/COMPILING.md b/COMPILING.md index 4e573f0127..06f54262e4 100644 --- a/COMPILING.md +++ b/COMPILING.md @@ -78,6 +78,8 @@ files himself via the `ZERO_CHECK` project. ## All other platforms Minimum required version of CMake is 3.9. +By default this produces a Debug build with assertations enabled. +This is a far slower build than release builds. ```bash mkdir build @@ -89,6 +91,25 @@ make For more information on how to use CMake (including how to make Release builds), we urge you to read [their excellent manual](https://cmake.org/cmake/help/latest/guide/user-interaction/index.html). +## CMake Options + +Via CMake, several options can be influenced to get different types of +builds. + +- `-DCMAKE_BUILD_TYPE=RelWithDebInfo`: build a release build. This is + significant faster than a debug build, but has far less useful information + in case of a crash. +- `-DOPTION_DEDICATED=ON`: build OpenTTD without a GUI. Useful if you are + running a headless server, as it requires less libraries to operate. +- `-DOPTION_USE_ASSERTS=OFF`: disable asserts. Use with care, as assert + statements capture early signs of trouble. Release builds have them + disabled by default. +- `-DOPTION_USE_THREADS=OFF`: disable the use of threads. This will block + the interface in many places, and in general gives a worse experience of + the game. Use with care. +- `-DOPTION_TOOLS_ONLY=ON`: only build tools like `strgen`. Does not build + the game itself. Useful for cross-compiling. + ## Supported compilers Every compiler that is supported by CMake and supports C++17, should be From 2a0365b3d992fb602fd91b218b4a8647ddb46975 Mon Sep 17 00:00:00 2001 From: Peter Nelson Date: Wed, 21 Apr 2021 15:22:45 +0100 Subject: [PATCH 108/268] Cleanup: Remove unnecessary parameter of GetScrolledRowFromWidget() Line height defaults to the resize height of the relevant widget, which is set in all cases. Therefore it is not necessary to specify this value every time. Additionally fixes scrolled padding for the framerate window. --- src/ai/ai_gui.cpp | 5 +++-- src/company_gui.cpp | 2 +- src/framerate_gui.cpp | 2 +- src/graph_gui.cpp | 2 +- src/group_gui.cpp | 8 ++++---- src/news_gui.cpp | 2 +- src/rail_gui.cpp | 2 +- src/station_gui.cpp | 4 ++-- src/widget.cpp | 5 ++--- src/widget_type.h | 2 +- 10 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/ai/ai_gui.cpp b/src/ai/ai_gui.cpp index 14fc65cc8c..49e37621e5 100644 --- a/src/ai/ai_gui.cpp +++ b/src/ai/ai_gui.cpp @@ -190,7 +190,7 @@ struct AIListWindow : public Window { { switch (widget) { case WID_AIL_LIST: { // Select one of the AIs - int sel = this->vscroll->GetScrolledRowFromWidget(pt.y, this, WID_AIL_LIST, 0, this->line_height) - 1; + int sel = this->vscroll->GetScrolledRowFromWidget(pt.y, this, WID_AIL_LIST) - 1; if (sel < (int)this->info_list->size()) { this->selected = sel; this->SetDirty(); @@ -784,6 +784,7 @@ struct AIConfigWindow : public Window { case WID_AIC_LIST: this->line_height = FONT_HEIGHT_NORMAL + WD_MATRIX_TOP + WD_MATRIX_BOTTOM; + resize->height = this->line_height; size->height = 8 * this->line_height; break; @@ -895,7 +896,7 @@ struct AIConfigWindow : public Window { } case WID_AIC_LIST: { // Select a slot - this->selected_slot = (CompanyID)this->vscroll->GetScrolledRowFromWidget(pt.y, this, widget, 0, this->line_height); + this->selected_slot = (CompanyID)this->vscroll->GetScrolledRowFromWidget(pt.y, this, widget); this->InvalidateData(); if (click_count > 1 && this->selected_slot != INVALID_COMPANY) ShowAIListWindow((CompanyID)this->selected_slot); break; diff --git a/src/company_gui.cpp b/src/company_gui.cpp index 29701b5144..2fe20d1353 100644 --- a/src/company_gui.cpp +++ b/src/company_gui.cpp @@ -956,7 +956,7 @@ public: break; case WID_SCL_MATRIX: { - uint row = this->vscroll->GetScrolledRowFromWidget(pt.y, this, WID_SCL_MATRIX, 0, this->line_height); + uint row = this->vscroll->GetScrolledRowFromWidget(pt.y, this, WID_SCL_MATRIX); if (row >= this->rows) return; if (this->livery_class < LC_GROUP_RAIL) { diff --git a/src/framerate_gui.cpp b/src/framerate_gui.cpp index 9ac78dc68b..a2fd03044e 100644 --- a/src/framerate_gui.cpp +++ b/src/framerate_gui.cpp @@ -683,7 +683,7 @@ struct FramerateWindow : Window { case WID_FRW_TIMES_AVERAGE: { /* Open time graph windows when clicking detail measurement lines */ const Scrollbar *sb = this->GetScrollbar(WID_FRW_SCROLLBAR); - int line = sb->GetScrolledRowFromWidget(pt.y - FONT_HEIGHT_NORMAL - VSPACING, this, widget, VSPACING, FONT_HEIGHT_NORMAL); + int line = sb->GetScrolledRowFromWidget(pt.y, this, widget, VSPACING + FONT_HEIGHT_NORMAL); if (line != INT_MAX) { line++; /* Find the visible line that was clicked */ diff --git a/src/graph_gui.cpp b/src/graph_gui.cpp index 5b04592bff..67399c327f 100644 --- a/src/graph_gui.cpp +++ b/src/graph_gui.cpp @@ -992,7 +992,7 @@ struct PaymentRatesGraphWindow : BaseGraphWindow { } case WID_CPR_MATRIX: { - uint row = this->vscroll->GetScrolledRowFromWidget(pt.y, this, WID_CPR_MATRIX, 0, this->line_height); + uint row = this->vscroll->GetScrolledRowFromWidget(pt.y, this, WID_CPR_MATRIX); if (row >= this->vscroll->GetCount()) return; const CargoSpec *cs; diff --git a/src/group_gui.cpp b/src/group_gui.cpp index a8b4c5f17e..3720533043 100644 --- a/src/group_gui.cpp +++ b/src/group_gui.cpp @@ -676,7 +676,7 @@ public: break; case WID_GL_LIST_GROUP: { // Matrix Group - uint id_g = this->group_sb->GetScrolledRowFromWidget(pt.y, this, WID_GL_LIST_GROUP, 0, this->tiny_step_height); + uint id_g = this->group_sb->GetScrolledRowFromWidget(pt.y, this, WID_GL_LIST_GROUP); if (id_g >= this->groups.size()) return; if (groups[id_g]->folded || (id_g + 1 < this->groups.size() && this->indents[id_g + 1] > this->indents[id_g])) { @@ -822,7 +822,7 @@ public: break; case WID_GL_LIST_GROUP: { // Matrix group - uint id_g = this->group_sb->GetScrolledRowFromWidget(pt.y, this, WID_GL_LIST_GROUP, 0, this->tiny_step_height); + uint id_g = this->group_sb->GetScrolledRowFromWidget(pt.y, this, WID_GL_LIST_GROUP); GroupID new_g = id_g >= this->groups.size() ? INVALID_GROUP : this->groups[id_g]->index; if (this->group_sel != new_g && g->parent != new_g) { @@ -855,7 +855,7 @@ public: this->group_over = INVALID_GROUP; this->SetDirty(); - uint id_g = this->group_sb->GetScrolledRowFromWidget(pt.y, this, WID_GL_LIST_GROUP, 0, this->tiny_step_height); + uint id_g = this->group_sb->GetScrolledRowFromWidget(pt.y, this, WID_GL_LIST_GROUP); GroupID new_g = id_g >= this->groups.size() ? NEW_GROUP : this->groups[id_g]->index; DoCommandP(0, new_g, vindex | (_ctrl_pressed || this->grouping == GB_SHARED_ORDERS ? 1 << 31 : 0), CMD_ADD_VEHICLE_GROUP | CMD_MSG(STR_ERROR_GROUP_CAN_T_ADD_VEHICLE), new_g == NEW_GROUP ? CcAddVehicleNewGroup : nullptr); @@ -996,7 +996,7 @@ public: break; case WID_GL_LIST_GROUP: { // ... the list of custom groups. - uint id_g = this->group_sb->GetScrolledRowFromWidget(pt.y, this, WID_GL_LIST_GROUP, 0, this->tiny_step_height); + uint id_g = this->group_sb->GetScrolledRowFromWidget(pt.y, this, WID_GL_LIST_GROUP); new_group_over = id_g >= this->groups.size() ? NEW_GROUP : this->groups[id_g]->index; break; } diff --git a/src/news_gui.cpp b/src/news_gui.cpp index 74c9bbbec9..fdcb56a37f 100644 --- a/src/news_gui.cpp +++ b/src/news_gui.cpp @@ -1205,7 +1205,7 @@ struct MessageHistoryWindow : Window { NewsItem *ni = _latest_news; if (ni == nullptr) return; - for (int n = this->vscroll->GetScrolledRowFromWidget(pt.y, this, WID_MH_BACKGROUND, WD_FRAMERECT_TOP, this->line_height); n > 0; n--) { + for (int n = this->vscroll->GetScrolledRowFromWidget(pt.y, this, WID_MH_BACKGROUND, WD_FRAMERECT_TOP); n > 0; n--) { ni = ni->prev; if (ni == nullptr) return; } diff --git a/src/rail_gui.cpp b/src/rail_gui.cpp index 6956d0e046..f0f5beccac 100644 --- a/src/rail_gui.cpp +++ b/src/rail_gui.cpp @@ -1464,7 +1464,7 @@ public: break; case WID_BRAS_NEWST_LIST: { - int y = this->vscroll->GetScrolledRowFromWidget(pt.y, this, WID_BRAS_NEWST_LIST, 0, this->line_height); + int y = this->vscroll->GetScrolledRowFromWidget(pt.y, this, WID_BRAS_NEWST_LIST); if (y >= (int)this->station_classes.size()) return; StationClassID station_class_id = this->station_classes[y]; if (_railstation.station_class != station_class_id) { diff --git a/src/station_gui.cpp b/src/station_gui.cpp index b4156261bc..d21ba5f735 100644 --- a/src/station_gui.cpp +++ b/src/station_gui.cpp @@ -529,7 +529,7 @@ public: { switch (widget) { case WID_STL_LIST: { - uint id_v = this->vscroll->GetScrolledRowFromWidget(pt.y, this, WID_STL_LIST, 0, FONT_HEIGHT_NORMAL); + uint id_v = this->vscroll->GetScrolledRowFromWidget(pt.y, this, WID_STL_LIST); if (id_v >= this->stations.size()) return; // click out of list bound const Station *st = this->stations[id_v]; @@ -1906,7 +1906,7 @@ struct StationViewWindow : public Window { { switch (widget) { case WID_SV_WAITING: - this->HandleCargoWaitingClick(this->vscroll->GetScrolledRowFromWidget(pt.y, this, WID_SV_WAITING, WD_FRAMERECT_TOP, FONT_HEIGHT_NORMAL) - this->vscroll->GetPosition()); + this->HandleCargoWaitingClick(this->vscroll->GetScrolledRowFromWidget(pt.y, this, WID_SV_WAITING, WD_FRAMERECT_TOP) - this->vscroll->GetPosition()); break; case WID_SV_CATCHMENT: diff --git a/src/widget.cpp b/src/widget.cpp index 4be4cf3317..123e8a2f9d 100644 --- a/src/widget.cpp +++ b/src/widget.cpp @@ -1964,12 +1964,11 @@ void NWidgetViewport::UpdateViewportCoordinates(Window *w) * @param w The window the click was in. * @param widget Widget number of the widget clicked in. * @param padding Amount of empty space between the widget edge and the top of the first row. Default value is \c 0. - * @param line_height Height of a single row. A negative value means using the vertical resize step of the widget. * @return Row number clicked at. If clicked at a wrong position, #INT_MAX is returned. */ -int Scrollbar::GetScrolledRowFromWidget(int clickpos, const Window * const w, int widget, int padding, int line_height) const +int Scrollbar::GetScrolledRowFromWidget(int clickpos, const Window * const w, int widget, int padding) const { - uint pos = w->GetRowFromWidget(clickpos, widget, padding, line_height); + uint pos = w->GetRowFromWidget(clickpos, widget, padding, -1); if (pos != INT_MAX) pos += this->GetPosition(); return (pos >= this->GetCount()) ? INT_MAX : pos; } diff --git a/src/widget_type.h b/src/widget_type.h index c88727a2f0..a5b323b870 100644 --- a/src/widget_type.h +++ b/src/widget_type.h @@ -748,7 +748,7 @@ public: } } - int GetScrolledRowFromWidget(int clickpos, const Window * const w, int widget, int padding = 0, int line_height = -1) const; + int GetScrolledRowFromWidget(int clickpos, const Window * const w, int widget, int padding = 0) const; EventState UpdateListPositionOnKeyPress(int &list_position, uint16 keycode) const; }; From 636e37d1832dbcb3aa12c915ede5a9d26d231d24 Mon Sep 17 00:00:00 2001 From: Peter Nelson Date: Mon, 19 Apr 2021 12:12:07 +0100 Subject: [PATCH 109/268] Codechange: Add internal widget alignment property, along with widget part. --- src/gfx_func.h | 18 ------- src/gfx_type.h | 18 +++++++ src/news_gui.cpp | 2 +- src/widget.cpp | 130 +++++++++++++++++++++++++++++++++------------- src/widget_type.h | 27 ++++++++++ src/window_gui.h | 2 +- 6 files changed, 142 insertions(+), 55 deletions(-) diff --git a/src/gfx_func.h b/src/gfx_func.h index e8f7d0345a..a4db8a045a 100644 --- a/src/gfx_func.h +++ b/src/gfx_func.h @@ -91,24 +91,6 @@ Dimension GetSpriteSize(SpriteID sprid, Point *offset = nullptr, ZoomLevel zoom void DrawSpriteViewport(SpriteID img, PaletteID pal, int x, int y, const SubSprite *sub = nullptr); void DrawSprite(SpriteID img, PaletteID pal, int x, int y, const SubSprite *sub = nullptr, ZoomLevel zoom = ZOOM_LVL_GUI); -/** How to align the to-be drawn text. */ -enum StringAlignment { - SA_LEFT = 0 << 0, ///< Left align the text. - SA_HOR_CENTER = 1 << 0, ///< Horizontally center the text. - SA_RIGHT = 2 << 0, ///< Right align the text (must be a single bit). - SA_HOR_MASK = 3 << 0, ///< Mask for horizontal alignment. - - SA_TOP = 0 << 2, ///< Top align the text. - SA_VERT_CENTER = 1 << 2, ///< Vertically center the text. - SA_BOTTOM = 2 << 2, ///< Bottom align the text. - SA_VERT_MASK = 3 << 2, ///< Mask for vertical alignment. - - SA_CENTER = SA_HOR_CENTER | SA_VERT_CENTER, ///< Center both horizontally and vertically. - - SA_FORCE = 1 << 4, ///< Force the alignment, i.e. don't swap for RTL languages. -}; -DECLARE_ENUM_AS_BIT_SET(StringAlignment) - 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, 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); diff --git a/src/gfx_type.h b/src/gfx_type.h index 3b9f04d9e8..a6bf3cf6d5 100644 --- a/src/gfx_type.h +++ b/src/gfx_type.h @@ -323,4 +323,22 @@ enum Support8bpp { S8BPP_HARDWARE, ///< Full 8bpp support by OS and hardware. }; + /** How to align the to-be drawn text. */ +enum StringAlignment { + SA_LEFT = 0 << 0, ///< Left align the text. + SA_HOR_CENTER = 1 << 0, ///< Horizontally center the text. + SA_RIGHT = 2 << 0, ///< Right align the text (must be a single bit). + SA_HOR_MASK = 3 << 0, ///< Mask for horizontal alignment. + + SA_TOP = 0 << 2, ///< Top align the text. + SA_VERT_CENTER = 1 << 2, ///< Vertically center the text. + SA_BOTTOM = 2 << 2, ///< Bottom align the text. + SA_VERT_MASK = 3 << 2, ///< Mask for vertical alignment. + + SA_CENTER = SA_HOR_CENTER | SA_VERT_CENTER, ///< Center both horizontally and vertically. + + SA_FORCE = 1 << 4, ///< Force the alignment, i.e. don't swap for RTL languages. +}; +DECLARE_ENUM_AS_BIT_SET(StringAlignment) + #endif /* GFX_TYPE_H */ diff --git a/src/news_gui.cpp b/src/news_gui.cpp index fdcb56a37f..fa601eb567 100644 --- a/src/news_gui.cpp +++ b/src/news_gui.cpp @@ -422,7 +422,7 @@ struct NewsWindow : Window { { switch (widget) { case WID_N_CAPTION: - DrawCaption(r, COLOUR_LIGHT_BLUE, this->owner, STR_NEWS_MESSAGE_CAPTION); + DrawCaption(r, COLOUR_LIGHT_BLUE, this->owner, STR_NEWS_MESSAGE_CAPTION, SA_HOR_CENTER); break; case WID_N_PANEL: diff --git a/src/widget.cpp b/src/widget.cpp index 123e8a2f9d..feaf036b22 100644 --- a/src/widget.cpp +++ b/src/widget.cpp @@ -24,6 +24,33 @@ #include "safeguards.h" +/** + * Calculate x and y coordinates for an aligned object within a window. + * @param r Rectangle of the widget to be drawn in. + * @param d Dimension of the object to be drawn. + * @param align Alignment of the object. + * @return A point containing the position at which to draw. + */ +static inline Point GetAlignedPosition(const Rect &r, const Dimension &d, StringAlignment align) +{ + Point p; + /* In case we have a RTL language we swap the alignment. */ + if (!(align & SA_FORCE) && _current_text_dir == TD_RTL && (align & SA_HOR_MASK) != SA_HOR_CENTER) align ^= SA_RIGHT; + switch (align & SA_HOR_MASK) { + case SA_LEFT: p.x = r.left; break; + case SA_HOR_CENTER: p.x = CenterBounds(r.left, r.right, d.width); break; + case SA_RIGHT: p.x = r.right - d.width; break; + default: NOT_REACHED(); + } + switch (align & SA_VERT_MASK) { + case SA_TOP: p.y = r.top; break; + case SA_VERT_CENTER: p.y = CenterBounds(r.top, r.bottom, d.height); break; + case SA_BOTTOM: p.y = r.bottom - d.height; break; + default: NOT_REACHED(); + } + return p; +} + /** * Compute the vertical position of the draggable part of scrollbar * @param sb Scrollbar list data @@ -212,15 +239,17 @@ void DrawFrameRect(int left, int top, int right, int bottom, Colours colour, Fra * @param colour Colour of the button. * @param clicked Button is lowered. * @param img Sprite to draw. + * @param align Alignment of the sprite. */ -static inline void DrawImageButtons(const Rect &r, WidgetType type, Colours colour, bool clicked, SpriteID img) +static inline void DrawImageButtons(const Rect &r, WidgetType type, Colours colour, bool clicked, SpriteID img, StringAlignment align) { assert(img != 0); DrawFrameRect(r.left, r.top, r.right, r.bottom, colour, (clicked) ? FR_LOWERED : FR_NONE); if ((type & WWT_MASK) == WWT_IMGBTN_2 && clicked) img++; // Show different image when clicked for #WWT_IMGBTN_2. Dimension d = GetSpriteSize(img); - DrawSprite(img, PAL_NONE, CenterBounds(r.left, r.right, d.width) + clicked, CenterBounds(r.top, r.bottom, d.height) + clicked); + Point p = GetAlignedPosition(r, d, align); + DrawSprite(img, PAL_NONE, p.x + clicked, p.y + clicked); } /** @@ -229,14 +258,15 @@ static inline void DrawImageButtons(const Rect &r, WidgetType type, Colours colo * @param type Widget type (#WWT_TEXTBTN, #WWT_TEXTBTN_2, or #WWT_LABEL). * @param clicked Label is rendered lowered. * @param str Text to draw. + * @param align Alignment of the text. */ -static inline void DrawLabel(const Rect &r, WidgetType type, bool clicked, StringID str) +static inline void DrawLabel(const Rect &r, WidgetType type, bool clicked, StringID str, StringAlignment align) { if (str == STR_NULL) return; if ((type & WWT_MASK) == WWT_TEXTBTN_2 && clicked) str++; Dimension d = GetStringBoundingBox(str); - int offset = std::max(0, ((int)(r.bottom - r.top + 1) - (int)d.height) / 2); // Offset for rendering the text vertically centered - DrawString(r.left + clicked, r.right + clicked, r.top + offset + clicked, str, TC_FROMSTRING, SA_HOR_CENTER); + Point p = GetAlignedPosition(r, d, align); + DrawString(r.left + clicked, r.right + clicked, p.y + clicked, str, TC_FROMSTRING, align); } /** @@ -244,12 +274,13 @@ static inline void DrawLabel(const Rect &r, WidgetType type, bool clicked, Strin * @param r Rectangle of the background. * @param colour Colour of the text. * @param str Text to draw. + * @param align Alignment of the text. */ -static inline void DrawText(const Rect &r, TextColour colour, StringID str) +static inline void DrawText(const Rect &r, TextColour colour, StringID str, StringAlignment align) { Dimension d = GetStringBoundingBox(str); - int offset = std::max(0, ((int)(r.bottom - r.top + 1) - (int)d.height) / 2); // Offset for rendering the text vertically centered - if (str != STR_NULL) DrawString(r.left, r.right, r.top + offset, str, colour); + Point p = GetAlignedPosition(r, d, align); + if (str != STR_NULL) DrawString(r.left, r.right, p.y, str, colour, align); } /** @@ -257,11 +288,12 @@ static inline void DrawText(const Rect &r, TextColour colour, StringID str) * @param r Rectangle of the background. * @param colour Colour of the inset. * @param str Text to draw. + * @param align Alignment of the text. */ -static inline void DrawInset(const Rect &r, Colours colour, StringID str) +static inline void DrawInset(const Rect &r, Colours colour, StringID str, StringAlignment align) { DrawFrameRect(r.left, r.top, r.right, r.bottom, colour, FR_LOWERED | FR_DARKENED); - if (str != STR_NULL) DrawString(r.left + WD_INSET_LEFT, r.right - WD_INSET_RIGHT, r.top + WD_INSET_TOP, str); + if (str != STR_NULL) DrawString(r.left + WD_INSET_LEFT, r.right - WD_INSET_RIGHT, r.top + WD_INSET_TOP, str, TC_FROMSTRING, align); } /** @@ -405,12 +437,13 @@ static inline void DrawHorizontalScrollbar(const Rect &r, Colours colour, bool l * @param r Rectangle of the frame. * @param colour Colour of the frame. * @param str Text of the frame. + * @param align Alignment of the text in the frame. */ -static inline void DrawFrame(const Rect &r, Colours colour, StringID str) +static inline void DrawFrame(const Rect &r, Colours colour, StringID str, StringAlignment align) { int x2 = r.left; // by default the left side is the left side of the widget - if (str != STR_NULL) x2 = DrawString(r.left + WD_FRAMETEXT_LEFT, r.right - WD_FRAMETEXT_RIGHT, r.top, str); + if (str != STR_NULL) x2 = DrawString(r.left + WD_FRAMETEXT_LEFT, r.right - WD_FRAMETEXT_RIGHT, r.top, str, TC_FROMSTRING, align); int c1 = _colour_gradient[colour][3]; int c2 = _colour_gradient[colour][7]; @@ -458,7 +491,7 @@ static inline void DrawFrame(const Rect &r, Colours colour, StringID str) */ static inline void DrawShadeBox(const Rect &r, Colours colour, bool clicked) { - DrawImageButtons(r, WWT_SHADEBOX, colour, clicked, clicked ? SPR_WINDOW_SHADE: SPR_WINDOW_UNSHADE); + DrawImageButtons(r, WWT_SHADEBOX, colour, clicked, clicked ? SPR_WINDOW_SHADE: SPR_WINDOW_UNSHADE, SA_CENTER); } /** @@ -469,7 +502,7 @@ static inline void DrawShadeBox(const Rect &r, Colours colour, bool clicked) */ static inline void DrawStickyBox(const Rect &r, Colours colour, bool clicked) { - DrawImageButtons(r, WWT_STICKYBOX, colour, clicked, clicked ? SPR_PIN_UP : SPR_PIN_DOWN); + DrawImageButtons(r, WWT_STICKYBOX, colour, clicked, clicked ? SPR_PIN_UP : SPR_PIN_DOWN, SA_CENTER); } /** @@ -480,7 +513,7 @@ static inline void DrawStickyBox(const Rect &r, Colours colour, bool clicked) */ static inline void DrawDefSizeBox(const Rect &r, Colours colour, bool clicked) { - DrawImageButtons(r, WWT_DEFSIZEBOX, colour, clicked, SPR_WINDOW_DEFSIZE); + DrawImageButtons(r, WWT_DEFSIZEBOX, colour, clicked, SPR_WINDOW_DEFSIZE, SA_CENTER); } /** @@ -491,7 +524,7 @@ static inline void DrawDefSizeBox(const Rect &r, Colours colour, bool clicked) */ static inline void DrawDebugBox(const Rect &r, Colours colour, bool clicked) { - DrawImageButtons(r, WWT_DEBUGBOX, colour, clicked, SPR_WINDOW_DEBUG); + DrawImageButtons(r, WWT_DEBUGBOX, colour, clicked, SPR_WINDOW_DEBUG, SA_CENTER); } /** @@ -534,8 +567,9 @@ static inline void DrawCloseBox(const Rect &r, Colours colour) * @param colour Colour of the window. * @param owner 'Owner' of the window. * @param str Text to draw in the bar. + * @param align Alignment of the text. */ -void DrawCaption(const Rect &r, Colours colour, Owner owner, StringID str) +void DrawCaption(const Rect &r, Colours colour, Owner owner, StringID str, StringAlignment align) { bool company_owned = owner < MAX_COMPANIES; @@ -548,8 +582,8 @@ void DrawCaption(const Rect &r, Colours colour, Owner owner, StringID str) if (str != STR_NULL) { Dimension d = GetStringBoundingBox(str); - int offset = std::max(0, ((int)(r.bottom - r.top + 1) - (int)d.height) / 2); // Offset for rendering the text vertically centered - DrawString(r.left + WD_CAPTIONTEXT_LEFT, r.right - WD_CAPTIONTEXT_RIGHT, r.top + offset, str, TC_FROMSTRING, SA_HOR_CENTER); + Point p = GetAlignedPosition(r, d, align); + DrawString(r.left + WD_CAPTIONTEXT_LEFT, r.right - WD_CAPTIONTEXT_RIGHT, p.y, str, TC_FROMSTRING, align); } } @@ -560,10 +594,11 @@ void DrawCaption(const Rect &r, Colours colour, Owner owner, StringID str) * @param clicked_button The button-part is lowered. * @param clicked_dropdown The drop-down part is lowered. * @param str Text of the button. + * @param align Alignment of the text within the dropdown. * * @note Magic constants are also used in #NWidgetLeaf::ButtonHit. */ -static inline void DrawButtonDropdown(const Rect &r, Colours colour, bool clicked_button, bool clicked_dropdown, StringID str) +static inline void DrawButtonDropdown(const Rect &r, Colours colour, bool clicked_button, bool clicked_dropdown, StringID str, StringAlignment align) { int text_offset = std::max(0, ((int)(r.bottom - r.top + 1) - FONT_HEIGHT_NORMAL) / 2); // Offset for rendering the text vertically centered @@ -575,12 +610,12 @@ static inline void DrawButtonDropdown(const Rect &r, Colours colour, bool clicke DrawFrameRect(r.left, r.top, r.right - dd_width, r.bottom, colour, clicked_button ? FR_LOWERED : FR_NONE); DrawFrameRect(r.right + 1 - dd_width, r.top, r.right, r.bottom, colour, clicked_dropdown ? FR_LOWERED : FR_NONE); DrawSprite(SPR_ARROW_DOWN, PAL_NONE, r.right - (dd_width - 2) + clicked_dropdown, r.top + image_offset + clicked_dropdown); - if (str != STR_NULL) DrawString(r.left + WD_DROPDOWNTEXT_LEFT + clicked_button, r.right - dd_width - WD_DROPDOWNTEXT_RIGHT + clicked_button, r.top + text_offset + clicked_button, str, TC_BLACK); + if (str != STR_NULL) DrawString(r.left + WD_DROPDOWNTEXT_LEFT + clicked_button, r.right - dd_width - WD_DROPDOWNTEXT_RIGHT + clicked_button, r.top + text_offset + clicked_button, str, TC_BLACK, align); } else { DrawFrameRect(r.left + dd_width, r.top, r.right, r.bottom, colour, clicked_button ? FR_LOWERED : FR_NONE); DrawFrameRect(r.left, r.top, r.left + dd_width - 1, r.bottom, colour, clicked_dropdown ? FR_LOWERED : FR_NONE); DrawSprite(SPR_ARROW_DOWN, PAL_NONE, r.left + 1 + clicked_dropdown, r.top + image_offset + clicked_dropdown); - if (str != STR_NULL) DrawString(r.left + dd_width + WD_DROPDOWNTEXT_LEFT + clicked_button, r.right - WD_DROPDOWNTEXT_RIGHT + clicked_button, r.top + text_offset + clicked_button, str, TC_BLACK); + if (str != STR_NULL) DrawString(r.left + dd_width + WD_DROPDOWNTEXT_LEFT + clicked_button, r.right - WD_DROPDOWNTEXT_RIGHT + clicked_button, r.top + text_offset + clicked_button, str, TC_BLACK, align); } } @@ -590,10 +625,11 @@ static inline void DrawButtonDropdown(const Rect &r, Colours colour, bool clicke * @param colour Background colour of the widget. * @param clicked The widget is lowered. * @param str Text of the button. + * @param align Alignment of the text. */ -static inline void DrawDropdown(const Rect &r, Colours colour, bool clicked, StringID str) +static inline void DrawDropdown(const Rect &r, Colours colour, bool clicked, StringID str, StringAlignment align) { - DrawButtonDropdown(r, colour, false, clicked, str); + DrawButtonDropdown(r, colour, false, clicked, str, align); } /** @@ -872,6 +908,7 @@ NWidgetCore::NWidgetCore(WidgetType tp, Colours colour, uint fill_x, uint fill_y this->widget_data = widget_data; this->tool_tip = tool_tip; this->scrollbar_index = -1; + this->align = SA_CENTER; } /** @@ -904,6 +941,15 @@ void NWidgetCore::SetToolTip(StringID tool_tip) this->tool_tip = tool_tip; } +/** + * Set the text/image alignment of the nested widget. + * @param align Alignment to use. + */ +void NWidgetCore::SetAlignment(StringAlignment align) +{ + this->align = align; +} + void NWidgetCore::FillNestedArray(NWidgetBase **array, uint length) { if (this->index >= 0 && (uint)(this->index) < length) array[this->index] = this; @@ -1725,6 +1771,7 @@ NWidgetBackground::NWidgetBackground(WidgetType tp, Colours colour, int index, N assert(tp == WWT_PANEL || tp == WWT_INSET || tp == WWT_FRAME); if (index >= 0) this->SetIndex(index); this->child = child; + this->SetAlignment(SA_TOP | SA_LEFT); } NWidgetBackground::~NWidgetBackground() @@ -1858,12 +1905,12 @@ void NWidgetBackground::Draw(const Window *w) case WWT_FRAME: if (this->index >= 0) w->SetStringParameters(this->index); - DrawFrame(r, this->colour, this->widget_data); + DrawFrame(r, this->colour, this->widget_data, this->align); break; case WWT_INSET: if (this->index >= 0) w->SetStringParameters(this->index); - DrawInset(r, this->colour, this->widget_data); + DrawInset(r, this->colour, this->widget_data, this->align); break; default: @@ -2192,6 +2239,11 @@ NWidgetLeaf::NWidgetLeaf(WidgetType tp, Colours colour, int index, uint32 data, case WWT_EMPTY: break; + case WWT_TEXT: + this->SetFill(0, 0); + this->SetAlignment(SA_LEFT | SA_VERT_CENTER); + break; + case WWT_PUSHBTN: case WWT_IMGBTN: case WWT_PUSHIMGBTN: @@ -2200,7 +2252,6 @@ NWidgetLeaf::NWidgetLeaf(WidgetType tp, Colours colour, int index, uint32 data, case WWT_PUSHTXTBTN: case WWT_TEXTBTN_2: case WWT_LABEL: - case WWT_TEXT: case WWT_MATRIX: case NWID_BUTTON_DROPDOWN: case NWID_PUSHBUTTON_DROPDOWN: @@ -2259,6 +2310,7 @@ NWidgetLeaf::NWidgetLeaf(WidgetType tp, Colours colour, int index, uint32 data, case WWT_DROPDOWN: this->SetFill(0, 0); this->min_y = WD_DROPDOWN_HEIGHT; + this->SetAlignment(SA_TOP | SA_LEFT); break; default: @@ -2493,7 +2545,7 @@ void NWidgetLeaf::Draw(const Window *w) case WWT_IMGBTN: case WWT_PUSHIMGBTN: case WWT_IMGBTN_2: - DrawImageButtons(r, this->type, this->colour, clicked, this->widget_data); + DrawImageButtons(r, this->type, this->colour, clicked, this->widget_data, this->align); break; case WWT_TEXTBTN: @@ -2501,7 +2553,7 @@ void NWidgetLeaf::Draw(const Window *w) case WWT_TEXTBTN_2: if (this->index >= 0) w->SetStringParameters(this->index); DrawFrameRect(r.left, r.top, r.right, r.bottom, this->colour, (clicked) ? FR_LOWERED : FR_NONE); - DrawLabel(r, this->type, clicked, this->widget_data); + DrawLabel(r, this->type, clicked, this->widget_data, this->align); break; case WWT_ARROWBTN: @@ -2514,18 +2566,18 @@ void NWidgetLeaf::Draw(const Window *w) case AWV_RIGHT: sprite = SPR_ARROW_RIGHT; break; default: NOT_REACHED(); } - DrawImageButtons(r, WWT_PUSHIMGBTN, this->colour, clicked, sprite); + DrawImageButtons(r, WWT_PUSHIMGBTN, this->colour, clicked, sprite, this->align); break; } case WWT_LABEL: if (this->index >= 0) w->SetStringParameters(this->index); - DrawLabel(r, this->type, clicked, this->widget_data); + DrawLabel(r, this->type, clicked, this->widget_data, this->align); break; case WWT_TEXT: if (this->index >= 0) w->SetStringParameters(this->index); - DrawText(r, (TextColour)this->colour, this->widget_data); + DrawText(r, (TextColour)this->colour, this->widget_data, this->align); break; case WWT_MATRIX: @@ -2540,7 +2592,7 @@ void NWidgetLeaf::Draw(const Window *w) case WWT_CAPTION: if (this->index >= 0) w->SetStringParameters(this->index); - DrawCaption(r, this->colour, w->owner, this->widget_data); + DrawCaption(r, this->colour, w->owner, this->widget_data, this->align); break; case WWT_SHADEBOX: @@ -2573,13 +2625,13 @@ void NWidgetLeaf::Draw(const Window *w) case WWT_DROPDOWN: if (this->index >= 0) w->SetStringParameters(this->index); - DrawDropdown(r, this->colour, clicked, this->widget_data); + DrawDropdown(r, this->colour, clicked, this->widget_data, this->align); break; case NWID_BUTTON_DROPDOWN: case NWID_PUSHBUTTON_DROPDOWN: if (this->index >= 0) w->SetStringParameters(this->index); - DrawButtonDropdown(r, this->colour, clicked, (this->disp_flags & ND_DROPDOWN_ACTIVE) != 0, this->widget_data); + DrawButtonDropdown(r, this->colour, clicked, (this->disp_flags & ND_DROPDOWN_ACTIVE) != 0, this->widget_data, this->align); break; default: @@ -2718,6 +2770,14 @@ static int MakeNWidget(const NWidgetPart *parts, int count, NWidgetBase **dest, break; } + case WPT_ALIGNMENT: { + NWidgetCore *nwc = dynamic_cast(*dest); + if (nwc != nullptr) { + nwc->SetAlignment(parts->u.align.align); + } + break; + } + case WPT_FILL: { NWidgetResizeBase *nwrb = dynamic_cast(*dest); if (nwrb != nullptr) nwrb->SetFill(parts->u.xy.x, parts->u.xy.y); diff --git a/src/widget_type.h b/src/widget_type.h index a5b323b870..97f64ba1b2 100644 --- a/src/widget_type.h +++ b/src/widget_type.h @@ -89,6 +89,7 @@ enum WidgetType { WPT_DATATIP, ///< Widget part for specifying data and tooltip. WPT_PADDING, ///< Widget part for specifying a padding. WPT_PIPSPACE, ///< Widget part for specifying pre/inter/post space for containers. + WPT_ALIGNMENT, ///< Widget part for specifying text/image alignment. WPT_ENDCONTAINER, ///< Widget part to denote end of a container. WPT_FUNCTION, ///< Widget part for calling a user function. WPT_SCROLLBAR, ///< Widget part for attaching a scrollbar. @@ -296,6 +297,7 @@ public: void SetIndex(int index); void SetDataTip(uint32 widget_data, StringID tool_tip); void SetToolTip(StringID tool_tip); + void SetAlignment(StringAlignment align); inline void SetLowered(bool lowered); inline bool IsLowered() const; @@ -315,6 +317,7 @@ public: StringID tool_tip; ///< Tooltip of the widget. @see Widget::tootips int scrollbar_index; ///< Index of an attached scrollbar. TextColour highlight_colour; ///< Colour of highlight. + StringAlignment align; ///< Alignment of text/image within widget. }; /** @@ -905,6 +908,14 @@ struct NWidgetPartTextLines { FontSize size; ///< Font size of text lines. }; +/** + * Widget part for setting text/image alignment within a widget. + * @ingroup NestedWidgetParts + */ +struct NWidgetPartAlignment { + StringAlignment align; ///< Alignment of text/image. +}; + /** * Pointer to function returning a nested widget. * @param biggest_index Pointer to storage for collecting the biggest index used in the nested widget. @@ -926,6 +937,7 @@ struct NWidgetPart { NWidgetPartPaddings padding; ///< Part with paddings. NWidgetPartPIP pip; ///< Part with pre/inter/post spaces. NWidgetPartTextLines text_lines; ///< Part with text line data. + NWidgetPartAlignment align; ///< Part with internal alignment. NWidgetFunctionType *func_ptr; ///< Part with a function call. NWidContainerFlags cont_flags; ///< Part with container flags. } u; @@ -984,6 +996,21 @@ static inline NWidgetPart SetMinimalTextLines(uint8 lines, uint8 spacing, FontSi return part; } +/** + * Widget part function for setting the alignment of text/images. + * @param align Alignment of text/image within widget. + * @ingroup NestedWidgetParts + */ +static inline NWidgetPart SetAlignment(StringAlignment align) +{ + NWidgetPart part; + + part.type = WPT_ALIGNMENT; + part.u.align.align = align; + + return part; +} + /** * Widget part function for setting filling. * @param fill_x Horizontal filling step from minimal size. diff --git a/src/window_gui.h b/src/window_gui.h index aca7f4486c..1799001c93 100644 --- a/src/window_gui.h +++ b/src/window_gui.h @@ -140,7 +140,7 @@ enum WidgetDrawDistances { /* widget.cpp */ void DrawFrameRect(int left, int top, int right, int bottom, Colours colour, FrameFlags flags); -void DrawCaption(const Rect &r, Colours colour, Owner owner, StringID str); +void DrawCaption(const Rect &r, Colours colour, Owner owner, StringID str, StringAlignment align); /* window.cpp */ extern Window *_z_front_window; From 51b4bd6c38ac69319461a5ba6a582a5e7517cc02 Mon Sep 17 00:00:00 2001 From: Peter Nelson Date: Mon, 19 Apr 2021 16:37:10 +0100 Subject: [PATCH 110/268] Codechange: Add widget text colour override property. --- src/news_gui.cpp | 2 +- src/widget.cpp | 76 ++++++++++++++++++++++++++++++----------------- src/widget_type.h | 27 +++++++++++++++++ src/window_gui.h | 2 +- 4 files changed, 78 insertions(+), 29 deletions(-) diff --git a/src/news_gui.cpp b/src/news_gui.cpp index fa601eb567..8d0531682d 100644 --- a/src/news_gui.cpp +++ b/src/news_gui.cpp @@ -422,7 +422,7 @@ struct NewsWindow : Window { { switch (widget) { case WID_N_CAPTION: - DrawCaption(r, COLOUR_LIGHT_BLUE, this->owner, STR_NEWS_MESSAGE_CAPTION, SA_HOR_CENTER); + DrawCaption(r, COLOUR_LIGHT_BLUE, this->owner, TC_FROMSTRING, STR_NEWS_MESSAGE_CAPTION, SA_HOR_CENTER); break; case WID_N_PANEL: diff --git a/src/widget.cpp b/src/widget.cpp index feaf036b22..1e3afb1557 100644 --- a/src/widget.cpp +++ b/src/widget.cpp @@ -257,16 +257,17 @@ static inline void DrawImageButtons(const Rect &r, WidgetType type, Colours colo * @param r Rectangle of the label background. * @param type Widget type (#WWT_TEXTBTN, #WWT_TEXTBTN_2, or #WWT_LABEL). * @param clicked Label is rendered lowered. + * @param colour Colour of the text. * @param str Text to draw. * @param align Alignment of the text. */ -static inline void DrawLabel(const Rect &r, WidgetType type, bool clicked, StringID str, StringAlignment align) +static inline void DrawLabel(const Rect &r, WidgetType type, bool clicked, TextColour colour, StringID str, StringAlignment align) { if (str == STR_NULL) return; if ((type & WWT_MASK) == WWT_TEXTBTN_2 && clicked) str++; Dimension d = GetStringBoundingBox(str); Point p = GetAlignedPosition(r, d, align); - DrawString(r.left + clicked, r.right + clicked, p.y + clicked, str, TC_FROMSTRING, align); + DrawString(r.left + clicked, r.right + clicked, p.y + clicked, str, colour, align); } /** @@ -285,15 +286,16 @@ static inline void DrawText(const Rect &r, TextColour colour, StringID str, Stri /** * Draw an inset widget. - * @param r Rectangle of the background. - * @param colour Colour of the inset. - * @param str Text to draw. - * @param align Alignment of the text. + * @param r Rectangle of the background. + * @param colour Colour of the inset. + * @param text_colour Colour of the text. + * @param str Text to draw. + * @param align Alignment of the text. */ -static inline void DrawInset(const Rect &r, Colours colour, StringID str, StringAlignment align) +static inline void DrawInset(const Rect &r, Colours colour, TextColour text_colour, StringID str, StringAlignment align) { DrawFrameRect(r.left, r.top, r.right, r.bottom, colour, FR_LOWERED | FR_DARKENED); - if (str != STR_NULL) DrawString(r.left + WD_INSET_LEFT, r.right - WD_INSET_RIGHT, r.top + WD_INSET_TOP, str, TC_FROMSTRING, align); + if (str != STR_NULL) DrawString(r.left + WD_INSET_LEFT, r.right - WD_INSET_RIGHT, r.top + WD_INSET_TOP, str, text_colour, align); } /** @@ -434,16 +436,17 @@ static inline void DrawHorizontalScrollbar(const Rect &r, Colours colour, bool l /** * Draw a frame widget. - * @param r Rectangle of the frame. - * @param colour Colour of the frame. - * @param str Text of the frame. - * @param align Alignment of the text in the frame. + * @param r Rectangle of the frame. + * @param colour Colour of the frame. + * @param text_colour Colour of the text. + * @param str Text of the frame. + * @param align Alignment of the text in the frame. */ -static inline void DrawFrame(const Rect &r, Colours colour, StringID str, StringAlignment align) +static inline void DrawFrame(const Rect &r, Colours colour, TextColour text_colour, StringID str, StringAlignment align) { int x2 = r.left; // by default the left side is the left side of the widget - if (str != STR_NULL) x2 = DrawString(r.left + WD_FRAMETEXT_LEFT, r.right - WD_FRAMETEXT_RIGHT, r.top, str, TC_FROMSTRING, align); + if (str != STR_NULL) x2 = DrawString(r.left + WD_FRAMETEXT_LEFT, r.right - WD_FRAMETEXT_RIGHT, r.top, str, text_colour, align); int c1 = _colour_gradient[colour][3]; int c2 = _colour_gradient[colour][7]; @@ -563,13 +566,14 @@ static inline void DrawCloseBox(const Rect &r, Colours colour) /** * Draw a caption bar. - * @param r Rectangle of the bar. - * @param colour Colour of the window. - * @param owner 'Owner' of the window. - * @param str Text to draw in the bar. - * @param align Alignment of the text. + * @param r Rectangle of the bar. + * @param colour Colour of the window. + * @param owner 'Owner' of the window. + * @param text_colour Colour of the text. + * @param str Text to draw in the bar. + * @param align Alignment of the text. */ -void DrawCaption(const Rect &r, Colours colour, Owner owner, StringID str, StringAlignment align) +void DrawCaption(const Rect &r, Colours colour, Owner owner, TextColour text_colour, StringID str, StringAlignment align) { bool company_owned = owner < MAX_COMPANIES; @@ -583,7 +587,7 @@ void DrawCaption(const Rect &r, Colours colour, Owner owner, StringID str, Strin if (str != STR_NULL) { Dimension d = GetStringBoundingBox(str); Point p = GetAlignedPosition(r, d, align); - DrawString(r.left + WD_CAPTIONTEXT_LEFT, r.right - WD_CAPTIONTEXT_RIGHT, p.y, str, TC_FROMSTRING, align); + DrawString(r.left + WD_CAPTIONTEXT_LEFT, r.right - WD_CAPTIONTEXT_RIGHT, p.y, str, text_colour, align); } } @@ -908,6 +912,7 @@ NWidgetCore::NWidgetCore(WidgetType tp, Colours colour, uint fill_x, uint fill_y this->widget_data = widget_data; this->tool_tip = tool_tip; this->scrollbar_index = -1; + this->text_colour = TC_FROMSTRING; this->align = SA_CENTER; } @@ -932,6 +937,15 @@ void NWidgetCore::SetDataTip(uint32 widget_data, StringID tool_tip) this->tool_tip = tool_tip; } +/** + * Set the text colour of the nested widget. + * @param colour TextColour to use. + */ +void NWidgetCore::SetTextColour(TextColour colour) +{ + this->text_colour = colour; +} + /** * Set the tool tip of the nested widget. * @param tool_tip Tool tip string to use. @@ -1905,12 +1919,12 @@ void NWidgetBackground::Draw(const Window *w) case WWT_FRAME: if (this->index >= 0) w->SetStringParameters(this->index); - DrawFrame(r, this->colour, this->widget_data, this->align); + DrawFrame(r, this->colour, this->text_colour, this->widget_data, this->align); break; case WWT_INSET: if (this->index >= 0) w->SetStringParameters(this->index); - DrawInset(r, this->colour, this->widget_data, this->align); + DrawInset(r, this->colour, this->text_colour, this->widget_data, this->align); break; default: @@ -2553,7 +2567,7 @@ void NWidgetLeaf::Draw(const Window *w) case WWT_TEXTBTN_2: if (this->index >= 0) w->SetStringParameters(this->index); DrawFrameRect(r.left, r.top, r.right, r.bottom, this->colour, (clicked) ? FR_LOWERED : FR_NONE); - DrawLabel(r, this->type, clicked, this->widget_data, this->align); + DrawLabel(r, this->type, clicked, this->text_colour, this->widget_data, this->align); break; case WWT_ARROWBTN: @@ -2572,12 +2586,12 @@ void NWidgetLeaf::Draw(const Window *w) case WWT_LABEL: if (this->index >= 0) w->SetStringParameters(this->index); - DrawLabel(r, this->type, clicked, this->widget_data, this->align); + DrawLabel(r, this->type, clicked, this->text_colour, this->widget_data, this->align); break; case WWT_TEXT: if (this->index >= 0) w->SetStringParameters(this->index); - DrawText(r, (TextColour)this->colour, this->widget_data, this->align); + DrawText(r, this->text_colour, this->widget_data, this->align); break; case WWT_MATRIX: @@ -2592,7 +2606,7 @@ void NWidgetLeaf::Draw(const Window *w) case WWT_CAPTION: if (this->index >= 0) w->SetStringParameters(this->index); - DrawCaption(r, this->colour, w->owner, this->widget_data, this->align); + DrawCaption(r, this->colour, w->owner, this->text_colour, this->widget_data, this->align); break; case WWT_SHADEBOX: @@ -2770,6 +2784,14 @@ static int MakeNWidget(const NWidgetPart *parts, int count, NWidgetBase **dest, break; } + case WPT_TEXTCOLOUR: { + NWidgetCore *nwc = dynamic_cast(*dest); + if (nwc != nullptr) { + nwc->SetTextColour(parts->u.colour.colour); + } + break; + } + case WPT_ALIGNMENT: { NWidgetCore *nwc = dynamic_cast(*dest); if (nwc != nullptr) { diff --git a/src/widget_type.h b/src/widget_type.h index 97f64ba1b2..e1fd92f638 100644 --- a/src/widget_type.h +++ b/src/widget_type.h @@ -89,6 +89,7 @@ enum WidgetType { WPT_DATATIP, ///< Widget part for specifying data and tooltip. WPT_PADDING, ///< Widget part for specifying a padding. WPT_PIPSPACE, ///< Widget part for specifying pre/inter/post space for containers. + WPT_TEXTCOLOUR, ///< Widget part for specifying text colour. WPT_ALIGNMENT, ///< Widget part for specifying text/image alignment. WPT_ENDCONTAINER, ///< Widget part to denote end of a container. WPT_FUNCTION, ///< Widget part for calling a user function. @@ -297,6 +298,7 @@ public: void SetIndex(int index); void SetDataTip(uint32 widget_data, StringID tool_tip); void SetToolTip(StringID tool_tip); + void SetTextColour(TextColour colour); void SetAlignment(StringAlignment align); inline void SetLowered(bool lowered); @@ -317,6 +319,7 @@ public: StringID tool_tip; ///< Tooltip of the widget. @see Widget::tootips int scrollbar_index; ///< Index of an attached scrollbar. TextColour highlight_colour; ///< Colour of highlight. + TextColour text_colour; ///< Colour of text within widget. StringAlignment align; ///< Alignment of text/image within widget. }; @@ -908,6 +911,14 @@ struct NWidgetPartTextLines { FontSize size; ///< Font size of text lines. }; +/** + * Widget part for storing text colour. + * @ingroup NestedWidgetParts + */ +struct NWidgetPartTextColour { + TextColour colour; ///< TextColour for DrawString. +}; + /** * Widget part for setting text/image alignment within a widget. * @ingroup NestedWidgetParts @@ -937,6 +948,7 @@ struct NWidgetPart { NWidgetPartPaddings padding; ///< Part with paddings. NWidgetPartPIP pip; ///< Part with pre/inter/post spaces. NWidgetPartTextLines text_lines; ///< Part with text line data. + NWidgetPartTextColour colour; ///< Part with text colour data. NWidgetPartAlignment align; ///< Part with internal alignment. NWidgetFunctionType *func_ptr; ///< Part with a function call. NWidContainerFlags cont_flags; ///< Part with container flags. @@ -996,6 +1008,21 @@ static inline NWidgetPart SetMinimalTextLines(uint8 lines, uint8 spacing, FontSi return part; } +/** + * Widget part function for setting the text colour. + * @param colour Colour to draw string within widget. + * @ingroup NestedWidgetParts + */ +static inline NWidgetPart SetTextColour(TextColour colour) +{ + NWidgetPart part; + + part.type = WPT_TEXTCOLOUR; + part.u.colour.colour = colour; + + return part; +} + /** * Widget part function for setting the alignment of text/images. * @param align Alignment of text/image within widget. diff --git a/src/window_gui.h b/src/window_gui.h index 1799001c93..67a799c3d3 100644 --- a/src/window_gui.h +++ b/src/window_gui.h @@ -140,7 +140,7 @@ enum WidgetDrawDistances { /* widget.cpp */ void DrawFrameRect(int left, int top, int right, int bottom, Colours colour, FrameFlags flags); -void DrawCaption(const Rect &r, Colours colour, Owner owner, StringID str, StringAlignment align); +void DrawCaption(const Rect &r, Colours colour, Owner owner, TextColour text_colour, StringID str, StringAlignment align); /* window.cpp */ extern Window *_z_front_window; From 9aa64b7885b687cb162ecd33a5a2dc3804db060d Mon Sep 17 00:00:00 2001 From: Peter Nelson Date: Mon, 19 Apr 2021 15:14:03 +0100 Subject: [PATCH 111/268] Codechange: Use new widget features on chat message box. --- src/network/network_chat_gui.cpp | 24 +++++------------------- 1 file changed, 5 insertions(+), 19 deletions(-) diff --git a/src/network/network_chat_gui.cpp b/src/network/network_chat_gui.cpp index 5a92ef0db6..862a0a4cbf 100644 --- a/src/network/network_chat_gui.cpp +++ b/src/network/network_chat_gui.cpp @@ -281,7 +281,6 @@ static void SendChat(const char *buf, DestType type, int dest) /** Window to enter the chat message in. */ struct NetworkChatWindow : public Window { DestType dtype; ///< The type of destination. - StringID dest_string; ///< String representation of the destination. int dest; ///< The identifier of the destination. QueryString message_editbox; ///< Message editbox. @@ -305,9 +304,10 @@ struct NetworkChatWindow : public Window { STR_NETWORK_CHAT_CLIENT_CAPTION }; assert((uint)this->dtype < lengthof(chat_captions)); - this->dest_string = chat_captions[this->dtype]; - this->InitNested(type); + this->CreateNestedTree(); + this->GetWidget(WID_NC_DESTINATION)->widget_data = chat_captions[this->dtype]; + this->FinishInitNested(type); this->SetFocusedWidget(WID_NC_TEXTBOX); InvalidateWindowData(WC_NEWS_WINDOW, 0, this->height); @@ -462,27 +462,13 @@ struct NetworkChatWindow : public Window { return pt; } - void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize) override + void SetStringParameters(int widget) const override { if (widget != WID_NC_DESTINATION) return; if (this->dtype == DESTTYPE_CLIENT) { SetDParamStr(0, NetworkClientInfo::GetByClientID((ClientID)this->dest)->client_name); } - Dimension d = GetStringBoundingBox(this->dest_string); - d.width += WD_FRAMERECT_LEFT + WD_FRAMERECT_RIGHT; - d.height += WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM; - *size = maxdim(*size, d); - } - - void DrawWidget(const Rect &r, int widget) const override - { - if (widget != WID_NC_DESTINATION) return; - - if (this->dtype == DESTTYPE_CLIENT) { - SetDParamStr(0, NetworkClientInfo::GetByClientID((ClientID)this->dest)->client_name); - } - DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, r.top + WD_FRAMERECT_TOP, this->dest_string, TC_BLACK, SA_RIGHT); } void OnClick(Point pt, int widget, int click_count) override @@ -530,7 +516,7 @@ static const NWidgetPart _nested_chat_window_widgets[] = { NWidget(WWT_CLOSEBOX, COLOUR_GREY, WID_NC_CLOSE), NWidget(WWT_PANEL, COLOUR_GREY, WID_NC_BACKGROUND), NWidget(NWID_HORIZONTAL), - NWidget(WWT_TEXT, COLOUR_GREY, WID_NC_DESTINATION), SetMinimalSize(62, 12), SetPadding(1, 0, 1, 0), SetDataTip(STR_NULL, STR_NULL), + NWidget(WWT_TEXT, COLOUR_GREY, WID_NC_DESTINATION), SetMinimalSize(62, 12), SetPadding(1, 0, 1, 0), SetTextColour(TC_BLACK), SetAlignment(SA_TOP | SA_RIGHT), SetDataTip(STR_NULL, STR_NULL), NWidget(WWT_EDITBOX, COLOUR_GREY, WID_NC_TEXTBOX), SetMinimalSize(100, 12), SetPadding(1, 0, 1, 0), SetResize(1, 0), SetDataTip(STR_NETWORK_CHAT_OSKTITLE, STR_NULL), NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_NC_SENDBUTTON), SetMinimalSize(62, 12), SetPadding(1, 0, 1, 0), SetDataTip(STR_NETWORK_CHAT_SEND, STR_NULL), From c361246bec11a3f7a913b73e25cae7bb53402834 Mon Sep 17 00:00:00 2001 From: Peter Nelson Date: Mon, 19 Apr 2021 17:37:41 +0100 Subject: [PATCH 112/268] Codechange: Replace face window custom drawing with new widget features. --- src/company_gui.cpp | 130 +++++++++++++++----------------------------- 1 file changed, 43 insertions(+), 87 deletions(-) diff --git a/src/company_gui.cpp b/src/company_gui.cpp index 2fe20d1353..fd820199ad 100644 --- a/src/company_gui.cpp +++ b/src/company_gui.cpp @@ -1200,70 +1200,82 @@ static const NWidgetPart _nested_select_company_manager_face_widgets[] = { EndContainer(), NWidget(NWID_SPACER), SetMinimalSize(0, 4), NWidget(NWID_HORIZONTAL), - NWidget(WWT_EMPTY, INVALID_COLOUR, WID_SCMF_HAS_MOUSTACHE_EARRING_TEXT), SetFill(1, 0), + NWidget(WWT_TEXT, INVALID_COLOUR, WID_SCMF_HAS_MOUSTACHE_EARRING_TEXT), SetFill(1, 0), SetPadding(0, WD_FRAMERECT_RIGHT, 0, WD_FRAMERECT_LEFT), + SetDataTip(STR_FACE_EYECOLOUR, STR_NULL), SetTextColour(TC_GOLD), SetAlignment(SA_VERT_CENTER | SA_RIGHT), NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SCMF_HAS_MOUSTACHE_EARRING), SetDataTip(STR_EMPTY, STR_FACE_MOUSTACHE_EARRING_TOOLTIP), EndContainer(), NWidget(NWID_HORIZONTAL), - NWidget(WWT_EMPTY, INVALID_COLOUR, WID_SCMF_HAS_GLASSES_TEXT), SetFill(1, 0), + NWidget(WWT_TEXT, INVALID_COLOUR, WID_SCMF_HAS_GLASSES_TEXT), SetFill(1, 0), SetPadding(0, WD_FRAMERECT_RIGHT, 0, WD_FRAMERECT_LEFT), + SetDataTip(STR_FACE_GLASSES, STR_NULL), SetTextColour(TC_GOLD), SetAlignment(SA_VERT_CENTER | SA_RIGHT), NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SCMF_HAS_GLASSES), SetDataTip(STR_EMPTY, STR_FACE_GLASSES_TOOLTIP), EndContainer(), NWidget(NWID_SPACER), SetMinimalSize(0, 2), SetFill(1, 0), NWidget(NWID_HORIZONTAL), - NWidget(WWT_EMPTY, INVALID_COLOUR, WID_SCMF_HAIR_TEXT), SetFill(1, 0), + NWidget(WWT_TEXT, INVALID_COLOUR, WID_SCMF_HAIR_TEXT), SetFill(1, 0), SetPadding(0, WD_FRAMERECT_RIGHT, 0, WD_FRAMERECT_LEFT), + SetDataTip(STR_FACE_HAIR, STR_NULL), SetTextColour(TC_GOLD), SetAlignment(SA_VERT_CENTER | SA_RIGHT), NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_SCMF_HAIR_L), SetDataTip(AWV_DECREASE, STR_FACE_HAIR_TOOLTIP), NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SCMF_HAIR), SetDataTip(STR_EMPTY, STR_FACE_HAIR_TOOLTIP), NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_SCMF_HAIR_R), SetDataTip(AWV_INCREASE, STR_FACE_HAIR_TOOLTIP), EndContainer(), NWidget(NWID_HORIZONTAL), - NWidget(WWT_EMPTY, INVALID_COLOUR, WID_SCMF_EYEBROWS_TEXT), SetFill(1, 0), + NWidget(WWT_TEXT, INVALID_COLOUR, WID_SCMF_EYEBROWS_TEXT), SetFill(1, 0), SetPadding(0, WD_FRAMERECT_RIGHT, 0, WD_FRAMERECT_LEFT), + SetDataTip(STR_FACE_EYEBROWS, STR_NULL), SetTextColour(TC_GOLD), SetAlignment(SA_VERT_CENTER | SA_RIGHT), NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_SCMF_EYEBROWS_L), SetDataTip(AWV_DECREASE, STR_FACE_EYEBROWS_TOOLTIP), NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SCMF_EYEBROWS), SetDataTip(STR_EMPTY, STR_FACE_EYEBROWS_TOOLTIP), NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_SCMF_EYEBROWS_R), SetDataTip(AWV_INCREASE, STR_FACE_EYEBROWS_TOOLTIP), EndContainer(), NWidget(NWID_HORIZONTAL), - NWidget(WWT_EMPTY, INVALID_COLOUR, WID_SCMF_EYECOLOUR_TEXT), SetFill(1, 0), + NWidget(WWT_TEXT, INVALID_COLOUR, WID_SCMF_EYECOLOUR_TEXT), SetFill(1, 0), SetPadding(0, WD_FRAMERECT_RIGHT, 0, WD_FRAMERECT_LEFT), + SetDataTip(STR_FACE_EYECOLOUR, STR_NULL), SetTextColour(TC_GOLD), SetAlignment(SA_VERT_CENTER | SA_RIGHT), NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_SCMF_EYECOLOUR_L), SetDataTip(AWV_DECREASE, STR_FACE_EYECOLOUR_TOOLTIP), NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SCMF_EYECOLOUR), SetDataTip(STR_EMPTY, STR_FACE_EYECOLOUR_TOOLTIP), NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_SCMF_EYECOLOUR_R), SetDataTip(AWV_INCREASE, STR_FACE_EYECOLOUR_TOOLTIP), EndContainer(), NWidget(NWID_HORIZONTAL), - NWidget(WWT_EMPTY, INVALID_COLOUR, WID_SCMF_GLASSES_TEXT), SetFill(1, 0), + NWidget(WWT_TEXT, INVALID_COLOUR, WID_SCMF_GLASSES_TEXT), SetFill(1, 0), SetPadding(0, WD_FRAMERECT_RIGHT, 0, WD_FRAMERECT_LEFT), + SetDataTip(STR_FACE_GLASSES, STR_NULL), SetTextColour(TC_GOLD), SetAlignment(SA_VERT_CENTER | SA_RIGHT), NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_SCMF_GLASSES_L), SetDataTip(AWV_DECREASE, STR_FACE_GLASSES_TOOLTIP_2), NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SCMF_GLASSES), SetDataTip(STR_EMPTY, STR_FACE_GLASSES_TOOLTIP_2), NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_SCMF_GLASSES_R), SetDataTip(AWV_INCREASE, STR_FACE_GLASSES_TOOLTIP_2), EndContainer(), NWidget(NWID_HORIZONTAL), - NWidget(WWT_EMPTY, INVALID_COLOUR, WID_SCMF_NOSE_TEXT), SetFill(1, 0), + NWidget(WWT_TEXT, INVALID_COLOUR, WID_SCMF_NOSE_TEXT), SetFill(1, 0), SetPadding(0, WD_FRAMERECT_RIGHT, 0, WD_FRAMERECT_LEFT), + SetDataTip(STR_FACE_NOSE, STR_NULL), SetTextColour(TC_GOLD), SetAlignment(SA_VERT_CENTER | SA_RIGHT), NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_SCMF_NOSE_L), SetDataTip(AWV_DECREASE, STR_FACE_NOSE_TOOLTIP), NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SCMF_NOSE), SetDataTip(STR_EMPTY, STR_FACE_NOSE_TOOLTIP), NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_SCMF_NOSE_R), SetDataTip(AWV_INCREASE, STR_FACE_NOSE_TOOLTIP), EndContainer(), NWidget(NWID_HORIZONTAL), - NWidget(WWT_EMPTY, INVALID_COLOUR, WID_SCMF_LIPS_MOUSTACHE_TEXT), SetFill(1, 0), + NWidget(WWT_TEXT, INVALID_COLOUR, WID_SCMF_LIPS_MOUSTACHE_TEXT), SetFill(1, 0), SetPadding(0, WD_FRAMERECT_RIGHT, 0, WD_FRAMERECT_LEFT), + SetDataTip(STR_FACE_MOUSTACHE, STR_NULL), SetTextColour(TC_GOLD), SetAlignment(SA_VERT_CENTER | SA_RIGHT), NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_SCMF_LIPS_MOUSTACHE_L), SetDataTip(AWV_DECREASE, STR_FACE_LIPS_MOUSTACHE_TOOLTIP), NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SCMF_LIPS_MOUSTACHE), SetDataTip(STR_EMPTY, STR_FACE_LIPS_MOUSTACHE_TOOLTIP), NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_SCMF_LIPS_MOUSTACHE_R), SetDataTip(AWV_INCREASE, STR_FACE_LIPS_MOUSTACHE_TOOLTIP), EndContainer(), NWidget(NWID_HORIZONTAL), - NWidget(WWT_EMPTY, INVALID_COLOUR, WID_SCMF_CHIN_TEXT), SetFill(1, 0), + NWidget(WWT_TEXT, INVALID_COLOUR, WID_SCMF_CHIN_TEXT), SetFill(1, 0), SetPadding(0, WD_FRAMERECT_RIGHT, 0, WD_FRAMERECT_LEFT), + SetDataTip(STR_FACE_CHIN, STR_NULL), SetTextColour(TC_GOLD), SetAlignment(SA_VERT_CENTER | SA_RIGHT), NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_SCMF_CHIN_L), SetDataTip(AWV_DECREASE, STR_FACE_CHIN_TOOLTIP), NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SCMF_CHIN), SetDataTip(STR_EMPTY, STR_FACE_CHIN_TOOLTIP), NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_SCMF_CHIN_R), SetDataTip(AWV_INCREASE, STR_FACE_CHIN_TOOLTIP), EndContainer(), NWidget(NWID_HORIZONTAL), - NWidget(WWT_EMPTY, INVALID_COLOUR, WID_SCMF_JACKET_TEXT), SetFill(1, 0), + NWidget(WWT_TEXT, INVALID_COLOUR, WID_SCMF_JACKET_TEXT), SetFill(1, 0), SetPadding(0, WD_FRAMERECT_RIGHT, 0, WD_FRAMERECT_LEFT), + SetDataTip(STR_FACE_JACKET, STR_NULL), SetTextColour(TC_GOLD), SetAlignment(SA_VERT_CENTER | SA_RIGHT), NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_SCMF_JACKET_L), SetDataTip(AWV_DECREASE, STR_FACE_JACKET_TOOLTIP), NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SCMF_JACKET), SetDataTip(STR_EMPTY, STR_FACE_JACKET_TOOLTIP), NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_SCMF_JACKET_R), SetDataTip(AWV_INCREASE, STR_FACE_JACKET_TOOLTIP), EndContainer(), NWidget(NWID_HORIZONTAL), - NWidget(WWT_EMPTY, INVALID_COLOUR, WID_SCMF_COLLAR_TEXT), SetFill(1, 0), + NWidget(WWT_TEXT, INVALID_COLOUR, WID_SCMF_COLLAR_TEXT), SetFill(1, 0), SetPadding(0, WD_FRAMERECT_RIGHT, 0, WD_FRAMERECT_LEFT), + SetDataTip(STR_FACE_COLLAR, STR_NULL), SetTextColour(TC_GOLD), SetAlignment(SA_VERT_CENTER | SA_RIGHT), NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_SCMF_COLLAR_L), SetDataTip(AWV_DECREASE, STR_FACE_COLLAR_TOOLTIP), NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SCMF_COLLAR), SetDataTip(STR_EMPTY, STR_FACE_COLLAR_TOOLTIP), NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_SCMF_COLLAR_R), SetDataTip(AWV_INCREASE, STR_FACE_COLLAR_TOOLTIP), EndContainer(), NWidget(NWID_HORIZONTAL), - NWidget(WWT_EMPTY, INVALID_COLOUR, WID_SCMF_TIE_EARRING_TEXT), SetFill(1, 0), + NWidget(WWT_TEXT, INVALID_COLOUR, WID_SCMF_TIE_EARRING_TEXT), SetFill(1, 0), SetPadding(0, WD_FRAMERECT_RIGHT, 0, WD_FRAMERECT_LEFT), + SetDataTip(STR_FACE_EARRING, STR_NULL), SetTextColour(TC_GOLD), SetAlignment(SA_VERT_CENTER | SA_RIGHT), NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_SCMF_TIE_EARRING_L), SetDataTip(AWV_DECREASE, STR_FACE_TIE_EARRING_TOOLTIP), NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SCMF_TIE_EARRING), SetDataTip(STR_EMPTY, STR_FACE_TIE_EARRING_TOOLTIP), NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_SCMF_TIE_EARRING_R), SetDataTip(AWV_INCREASE, STR_FACE_TIE_EARRING_TOOLTIP), @@ -1294,9 +1306,6 @@ class SelectCompanyManagerFaceWindow : public Window Dimension yesno_dim; ///< Dimension of a yes/no button of a part in the advanced face window. Dimension number_dim; ///< Dimension of a number widget of a part in the advanced face window. - static const StringID PART_TEXTS_IS_FEMALE[]; ///< Strings depending on #is_female, used to describe parts (2 entries for a part). - static const StringID PART_TEXTS[]; ///< Fixed strings to describe parts of the face. - /** * Draw dynamic a label to the left of the button and a value in the button * @@ -1329,6 +1338,10 @@ class SelectCompanyManagerFaceWindow : public Window this->ge = (GenderEthnicity)GB(this->face, _cmf_info[CMFV_GEN_ETHN].offset, _cmf_info[CMFV_GEN_ETHN].length); // get the gender and ethnicity this->is_female = HasBit(this->ge, GENDER_FEMALE); // get the gender: 0 == male and 1 == female this->is_moust_male = !is_female && GetCompanyManagerFaceBits(this->face, CMFV_HAS_MOUSTACHE, this->ge) != 0; // is a male face with moustache + + this->GetWidget(WID_SCMF_HAS_MOUSTACHE_EARRING_TEXT)->widget_data = this->is_female ? STR_FACE_EARRING : STR_FACE_MOUSTACHE; + this->GetWidget(WID_SCMF_TIE_EARRING_TEXT)->widget_data = this->is_female ? STR_FACE_EARRING : STR_FACE_TIE; + this->GetWidget(WID_SCMF_LIPS_MOUSTACHE_TEXT)->widget_data = this->is_moust_male ? STR_FACE_MOUSTACHE : STR_FACE_LIPS; } public: @@ -1390,6 +1403,21 @@ public: void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize) override { switch (widget) { + case WID_SCMF_HAS_MOUSTACHE_EARRING_TEXT: + *size = maxdim(*size, GetStringBoundingBox(STR_FACE_EARRING)); + *size = maxdim(*size, GetStringBoundingBox(STR_FACE_MOUSTACHE)); + break; + + case WID_SCMF_TIE_EARRING_TEXT: + *size = maxdim(*size, GetStringBoundingBox(STR_FACE_EARRING)); + *size = maxdim(*size, GetStringBoundingBox(STR_FACE_TIE)); + break; + + case WID_SCMF_LIPS_MOUSTACHE_TEXT: + *size = maxdim(*size, GetStringBoundingBox(STR_FACE_LIPS)); + *size = maxdim(*size, GetStringBoundingBox(STR_FACE_MOUSTACHE)); + break; + case WID_SCMF_FACE: { Dimension face_size = GetSpriteSize(SPR_GRADIENT); size->width = std::max(size->width, face_size.width); @@ -1397,35 +1425,6 @@ public: break; } - case WID_SCMF_HAS_MOUSTACHE_EARRING_TEXT: - case WID_SCMF_TIE_EARRING_TEXT: { - int offset = (widget - WID_SCMF_HAS_MOUSTACHE_EARRING_TEXT) * 2; - *size = maxdim(GetStringBoundingBox(PART_TEXTS_IS_FEMALE[offset]), GetStringBoundingBox(PART_TEXTS_IS_FEMALE[offset + 1])); - size->width += WD_FRAMERECT_LEFT + WD_FRAMERECT_RIGHT; - size->height += WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM; - break; - } - - case WID_SCMF_LIPS_MOUSTACHE_TEXT: - *size = maxdim(GetStringBoundingBox(STR_FACE_LIPS), GetStringBoundingBox(STR_FACE_MOUSTACHE)); - size->width += WD_FRAMERECT_LEFT + WD_FRAMERECT_RIGHT; - size->height += WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM; - break; - - case WID_SCMF_HAS_GLASSES_TEXT: - case WID_SCMF_HAIR_TEXT: - case WID_SCMF_EYEBROWS_TEXT: - case WID_SCMF_EYECOLOUR_TEXT: - case WID_SCMF_GLASSES_TEXT: - case WID_SCMF_NOSE_TEXT: - case WID_SCMF_CHIN_TEXT: - case WID_SCMF_JACKET_TEXT: - case WID_SCMF_COLLAR_TEXT: - *size = GetStringBoundingBox(PART_TEXTS[widget - WID_SCMF_HAS_GLASSES_TEXT]); - size->width += WD_FRAMERECT_LEFT + WD_FRAMERECT_RIGHT; - size->height += WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM; - break; - case WID_SCMF_HAS_MOUSTACHE_EARRING: case WID_SCMF_HAS_GLASSES: *size = this->yesno_dim; @@ -1510,30 +1509,6 @@ public: void DrawWidget(const Rect &r, int widget) const override { switch (widget) { - case WID_SCMF_HAS_MOUSTACHE_EARRING_TEXT: - case WID_SCMF_TIE_EARRING_TEXT: { - StringID str = PART_TEXTS_IS_FEMALE[(widget - WID_SCMF_HAS_MOUSTACHE_EARRING_TEXT) * 2 + this->is_female]; - DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, r.top + WD_FRAMERECT_TOP, str, TC_GOLD, SA_RIGHT); - break; - } - - case WID_SCMF_LIPS_MOUSTACHE_TEXT: - DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, r.top + WD_FRAMERECT_TOP, (this->is_moust_male) ? STR_FACE_MOUSTACHE : STR_FACE_LIPS, TC_GOLD, SA_RIGHT); - break; - - case WID_SCMF_HAS_GLASSES_TEXT: - case WID_SCMF_HAIR_TEXT: - case WID_SCMF_EYEBROWS_TEXT: - case WID_SCMF_EYECOLOUR_TEXT: - case WID_SCMF_GLASSES_TEXT: - case WID_SCMF_NOSE_TEXT: - case WID_SCMF_CHIN_TEXT: - case WID_SCMF_JACKET_TEXT: - case WID_SCMF_COLLAR_TEXT: - DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, r.top + WD_FRAMERECT_TOP, PART_TEXTS[widget - WID_SCMF_HAS_GLASSES_TEXT], TC_GOLD, SA_RIGHT); - break; - - case WID_SCMF_HAS_MOUSTACHE_EARRING: if (this->is_female) { // Only for female faces this->DrawFaceStringLabel(WID_SCMF_HAS_MOUSTACHE_EARRING, GetCompanyManagerFaceBits(this->face, CMFV_HAS_TIE_EARRING, this->ge), true); @@ -1721,25 +1696,6 @@ public: } }; -/** Both text values of parts of the face that depend on the #is_female boolean value. */ -const StringID SelectCompanyManagerFaceWindow::PART_TEXTS_IS_FEMALE[] = { - STR_FACE_MOUSTACHE, STR_FACE_EARRING, // WID_SCMF_HAS_MOUSTACHE_EARRING_TEXT - STR_FACE_TIE, STR_FACE_EARRING, // WID_SCMF_TIE_EARRING_TEXT -}; - -/** Textual names for parts of the face. */ -const StringID SelectCompanyManagerFaceWindow::PART_TEXTS[] = { - STR_FACE_GLASSES, // WID_SCMF_HAS_GLASSES_TEXT - STR_FACE_HAIR, // WID_SCMF_HAIR_TEXT - STR_FACE_EYEBROWS, // WID_SCMF_EYEBROWS_TEXT - STR_FACE_EYECOLOUR, // WID_SCMF_EYECOLOUR_TEXT - STR_FACE_GLASSES, // WID_SCMF_GLASSES_TEXT - STR_FACE_NOSE, // WID_SCMF_NOSE_TEXT - STR_FACE_CHIN, // WID_SCMF_CHIN_TEXT - STR_FACE_JACKET, // WID_SCMF_JACKET_TEXT - STR_FACE_COLLAR, // WID_SCMF_COLLAR_TEXT -}; - /** Company manager face selection window description */ static WindowDesc _select_company_manager_face_desc( WDP_AUTO, "company_face", 0, 0, From 994ffaa382ab14c9b80c5a0a5d704fa53b85d4d2 Mon Sep 17 00:00:00 2001 From: Peter Nelson Date: Mon, 19 Apr 2021 20:57:03 +0100 Subject: [PATCH 113/268] Codechange: Use alignment feature in company finances window. --- src/company_gui.cpp | 45 +++++++++++++++++++++------------------------ 1 file changed, 21 insertions(+), 24 deletions(-) diff --git a/src/company_gui.cpp b/src/company_gui.cpp index fd820199ad..c9e3eaf861 100644 --- a/src/company_gui.cpp +++ b/src/company_gui.cpp @@ -242,10 +242,10 @@ static const NWidgetPart _nested_company_finances_widgets[] = { EndContainer(), NWidget(NWID_SPACER), SetFill(0, 0), SetMinimalSize(30, 0), NWidget(NWID_VERTICAL), // Vertical column with bank balance amount, loan amount, and total. - NWidget(WWT_TEXT, COLOUR_GREY, WID_CF_BALANCE_VALUE), SetDataTip(STR_NULL, STR_NULL), - NWidget(WWT_TEXT, COLOUR_GREY, WID_CF_LOAN_VALUE), SetDataTip(STR_NULL, STR_NULL), + NWidget(WWT_TEXT, COLOUR_GREY, WID_CF_BALANCE_VALUE), SetDataTip(STR_FINANCES_TOTAL_CURRENCY, STR_NULL), SetAlignment(SA_VERT_CENTER | SA_RIGHT), + NWidget(WWT_TEXT, COLOUR_GREY, WID_CF_LOAN_VALUE), SetDataTip(STR_FINANCES_TOTAL_CURRENCY, STR_NULL), SetAlignment(SA_VERT_CENTER | SA_RIGHT), NWidget(WWT_EMPTY, COLOUR_GREY, WID_CF_LOAN_LINE), SetMinimalSize(0, 2), SetFill(1, 0), - NWidget(WWT_TEXT, COLOUR_GREY, WID_CF_TOTAL_VALUE), SetDataTip(STR_NULL, STR_NULL), + NWidget(WWT_TEXT, COLOUR_GREY, WID_CF_TOTAL_VALUE), SetDataTip(STR_FINANCES_TOTAL_CURRENCY, STR_NULL), SetAlignment(SA_VERT_CENTER | SA_RIGHT), EndContainer(), NWidget(NWID_SELECTION, INVALID_COLOUR, WID_CF_SEL_MAXLOAN), NWidget(NWID_HORIZONTAL), @@ -292,6 +292,24 @@ struct CompanyFinancesWindow : Window { SetDParam(1, (CompanyID)this->window_number); break; + case WID_CF_BALANCE_VALUE: { + const Company *c = Company::Get((CompanyID)this->window_number); + SetDParam(0, c->money); + break; + } + + case WID_CF_LOAN_VALUE: { + const Company *c = Company::Get((CompanyID)this->window_number); + SetDParam(0, c->current_loan); + break; + } + + case WID_CF_TOTAL_VALUE: { + const Company *c = Company::Get((CompanyID)this->window_number); + SetDParam(0, c->money - c->current_loan); + break; + } + case WID_CF_MAXLOAN_VALUE: SetDParam(0, _economy.max_loan); break; @@ -350,27 +368,6 @@ struct CompanyFinancesWindow : Window { break; } - case WID_CF_BALANCE_VALUE: { - const Company *c = Company::Get((CompanyID)this->window_number); - SetDParam(0, c->money); - DrawString(r.left, r.right, r.top, STR_FINANCES_TOTAL_CURRENCY, TC_FROMSTRING, SA_RIGHT); - break; - } - - case WID_CF_LOAN_VALUE: { - const Company *c = Company::Get((CompanyID)this->window_number); - SetDParam(0, c->current_loan); - DrawString(r.left, r.right, r.top, STR_FINANCES_TOTAL_CURRENCY, TC_FROMSTRING, SA_RIGHT); - break; - } - - case WID_CF_TOTAL_VALUE: { - const Company *c = Company::Get((CompanyID)this->window_number); - SetDParam(0, c->money - c->current_loan); - DrawString(r.left, r.right, r.top, STR_FINANCES_TOTAL_CURRENCY, TC_FROMSTRING, SA_RIGHT); - break; - } - case WID_CF_LOAN_LINE: GfxFillRect(r.left, r.top, r.right, r.top, PC_BLACK); break; From 9fa38f5d0f3b1bd67028a8d9d5ff06eed1a31cdf Mon Sep 17 00:00:00 2001 From: PeterN Date: Thu, 22 Apr 2021 16:42:40 +0100 Subject: [PATCH 114/268] Codechange: Scale rating minigraphs on station window list. (#9075) Minigraphs did not adjust size to accomodate large text, either by font size or font zoom, leading to cropped labels. Minigraphs and spacing are now scaled by font zoom, as this seems to behave better than gui zoom in this instance. --- src/station_gui.cpp | 113 +++++++++++++++++++++++--------------------- 1 file changed, 60 insertions(+), 53 deletions(-) diff --git a/src/station_gui.cpp b/src/station_gui.cpp index d21ba5f735..98b44c6f98 100644 --- a/src/station_gui.cpp +++ b/src/station_gui.cpp @@ -153,9 +153,6 @@ void CheckRedrawStationCoverage(const Window *w) * @param type Cargo type * @param amount Cargo amount * @param rating ratings data for that particular cargo - * - * @note Each cargo-bar is 16 pixels wide and 6 pixels high - * @note Each rating 14 pixels wide and 1 pixel high and is 1 pixel below the cargo-bar */ static void StationsWndShowStationRating(int left, int right, int y, CargoID type, uint amount, byte rating) { @@ -165,32 +162,33 @@ static void StationsWndShowStationRating(int left, int right, int y, CargoID typ const CargoSpec *cs = CargoSpec::Get(type); if (!cs->IsValid()) return; + int padding = ScaleFontTrad(1); + int width = right - left; int colour = cs->rating_colour; TextColour tc = GetContrastColour(colour); - uint w = (std::min(amount, units_full) + 5) / 36; + uint w = std::min(amount + 5, units_full) * width / units_full; - int height = GetCharacterHeight(FS_SMALL); + int height = GetCharacterHeight(FS_SMALL) + padding - 1; - /* Draw total cargo (limited) on station (fits into 16 pixels) */ - if (w != 0) GfxFillRect(left, y, left + w - 1, y + height, colour); - - /* Draw a one pixel-wide bar of additional cargo meter, useful - * for stations with only a small amount (<=30) */ - if (w == 0) { - uint rest = amount / 5; + if (amount > 30) { + /* Draw total cargo (limited) on station */ + GfxFillRect(left, y, left + w - 1, y + height, colour); + } else { + /* Draw a (scaled) one pixel-wide bar of additional cargo meter, useful + * for stations with only a small amount (<=30) */ + uint rest = ScaleFontTrad(amount) / 5; if (rest != 0) { - w += left; - GfxFillRect(w, y + height - rest, w, y + height, colour); + GfxFillRect(left, y + height - rest, left + padding - 1, y + height, colour); } } - DrawString(left + 1, right, y, cs->abbrev, tc); + DrawString(left + padding, right, y, cs->abbrev, tc); - /* Draw green/red ratings bar (fits into 14 pixels) */ - y += height + 2; - GfxFillRect(left + 1, y, left + 14, y, PC_RED); - rating = std::min(rating, rating_full) / 16; - if (rating != 0) GfxFillRect(left + 1, y, left + rating, y, PC_GREEN); + /* Draw green/red ratings bar (fits under the waiting bar) */ + y += height + padding + 1; + GfxFillRect(left + padding, y, right - padding - 1, y + padding - 1, PC_RED); + w = std::min(rating, rating_full) * (width - padding - padding) / rating_full; + if (w != 0) GfxFillRect(left + padding, y, left + w - 1, y + padding - 1, PC_GREEN); } typedef GUIList GUIStationList; @@ -214,6 +212,7 @@ protected: GUIStationList stations; Scrollbar *vscroll; + uint rating_width; /** * (Re)Build station list @@ -392,16 +391,17 @@ public: } case WID_STL_LIST: - resize->height = FONT_HEIGHT_NORMAL; + resize->height = std::max(FONT_HEIGHT_NORMAL, FONT_HEIGHT_SMALL + ScaleFontTrad(3)); size->height = WD_FRAMERECT_TOP + 5 * resize->height + WD_FRAMERECT_BOTTOM; - break; - case WID_STL_TRAIN: - case WID_STL_TRUCK: - case WID_STL_BUS: - case WID_STL_AIRPLANE: - case WID_STL_SHIP: - size->height = std::max(FONT_HEIGHT_SMALL, 10) + padding.height; + /* Determine appropriate width for mini station rating graph */ + this->rating_width = 0; + const CargoSpec *cs; + FOR_ALL_SORTED_STANDARD_CARGOSPECS(cs) { + this->rating_width = std::max(this->rating_width, GetStringBoundingBox(cs->abbrev).width); + } + /* Approximately match original 16 pixel wide rating bars by multiplying string width by 1.6 */ + this->rating_width = this->rating_width * 16 / 10; break; case WID_STL_CARGOALL: @@ -445,6 +445,12 @@ public: bool rtl = _current_text_dir == TD_RTL; int max = std::min(this->vscroll->GetPosition() + this->vscroll->GetCapacity(), this->stations.size()); int y = r.top + WD_FRAMERECT_TOP; + uint line_height = this->GetWidget(widget)->resize_y; + /* Spacing between station name and first rating graph. */ + int text_spacing = ScaleFontTrad(5); + /* Spacing between additional rating graphs. */ + int rating_spacing = ScaleFontTrad(4); + for (int i = this->vscroll->GetPosition(); i < max; ++i) { // do until max number of stations of owner const Station *st = this->stations[i]; assert(st->xy != INVALID_TILE); @@ -455,8 +461,8 @@ public: SetDParam(0, st->index); SetDParam(1, st->facilities); - int x = DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, STR_STATION_LIST_STATION); - x += rtl ? -5 : 5; + int x = DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y + (line_height - FONT_HEIGHT_NORMAL) / 2, STR_STATION_LIST_STATION); + x += rtl ? -text_spacing : text_spacing; /* show cargo waiting and station ratings */ for (uint j = 0; j < _sorted_standard_cargo_specs_size; j++) { @@ -467,17 +473,17 @@ public: * instead of drawing to the left and then incrementing * the space. */ if (rtl) { - x -= 20; + x -= rating_width + rating_spacing; if (x < r.left + WD_FRAMERECT_LEFT) break; } - StationsWndShowStationRating(x, x + 16, y, cid, st->goods[cid].cargo.TotalCount(), st->goods[cid].rating); + StationsWndShowStationRating(x, x + rating_width, y, cid, st->goods[cid].cargo.TotalCount(), st->goods[cid].rating); if (!rtl) { - x += 20; + x += rating_width + rating_spacing; if (x > r.right - WD_FRAMERECT_RIGHT) break; } } } - y += FONT_HEIGHT_NORMAL; + y += line_height; } if (this->vscroll->GetCount() == 0) { // company has no stations @@ -488,30 +494,30 @@ public: } case WID_STL_NOCARGOWAITING: { - int cg_ofst = this->IsWidgetLowered(widget) ? 2 : 1; - DrawString(r.left + cg_ofst, r.right + cg_ofst, r.top + cg_ofst, STR_ABBREV_NONE, TC_BLACK, SA_HOR_CENTER); + int cg_ofst = this->IsWidgetLowered(widget) ? 1 : 0; + DrawString(r.left + cg_ofst, r.right + cg_ofst, r.top + (r.bottom - r.top - FONT_HEIGHT_SMALL) / 2 + cg_ofst, STR_ABBREV_NONE, TC_BLACK, SA_HOR_CENTER); break; } case WID_STL_CARGOALL: { - int cg_ofst = this->IsWidgetLowered(widget) ? 2 : 1; - DrawString(r.left + cg_ofst, r.right + cg_ofst, r.top + cg_ofst, STR_ABBREV_ALL, TC_BLACK, SA_HOR_CENTER); + int cg_ofst = this->IsWidgetLowered(widget) ? 1 : 0; + DrawString(r.left + cg_ofst, r.right + cg_ofst, r.top + (r.bottom - r.top - FONT_HEIGHT_SMALL) / 2 + cg_ofst, STR_ABBREV_ALL, TC_BLACK, SA_HOR_CENTER); break; } case WID_STL_FACILALL: { - int cg_ofst = this->IsWidgetLowered(widget) ? 2 : 1; - DrawString(r.left + cg_ofst, r.right + cg_ofst, r.top + cg_ofst, STR_ABBREV_ALL, TC_BLACK, SA_HOR_CENTER); + int cg_ofst = this->IsWidgetLowered(widget) ? 1 : 0; + DrawString(r.left + cg_ofst, r.right + cg_ofst, r.top + (r.bottom - r.top - FONT_HEIGHT_SMALL) / 2 + cg_ofst, STR_ABBREV_ALL, TC_BLACK, SA_HOR_CENTER); break; } default: if (widget >= WID_STL_CARGOSTART) { const CargoSpec *cs = _sorted_cargo_specs[widget - WID_STL_CARGOSTART]; - int cg_ofst = HasBit(this->cargo_filter, cs->Index()) ? 2 : 1; - GfxFillRect(r.left + cg_ofst, r.top + cg_ofst, r.right - 2 + cg_ofst, r.bottom - 2 + cg_ofst, cs->rating_colour); + int cg_ofst = HasBit(this->cargo_filter, cs->Index()) ? 1 : 0; + GfxFillRect(r.left + cg_ofst + 1, r.top + cg_ofst + 1, r.right - 1 + cg_ofst, r.bottom - 1 + cg_ofst, cs->rating_colour); TextColour tc = GetContrastColour(cs->rating_colour); - DrawString(r.left + cg_ofst, r.right + cg_ofst, r.top + cg_ofst, cs->abbrev, tc, SA_HOR_CENTER); + DrawString(r.left + cg_ofst, r.right + cg_ofst, r.top + (r.bottom - r.top - FONT_HEIGHT_SMALL) / 2 + cg_ofst, cs->abbrev, tc, SA_HOR_CENTER); } break; } @@ -720,7 +726,8 @@ static NWidgetBase *CargoWidgets(int *biggest_index) for (uint i = 0; i < _sorted_standard_cargo_specs_size; i++) { NWidgetBackground *panel = new NWidgetBackground(WWT_PANEL, COLOUR_GREY, WID_STL_CARGOSTART + i); - panel->SetMinimalSize(14, 11); + panel->SetMinimalSize(14, 0); + panel->SetMinimalTextLines(1, 0, FS_NORMAL); panel->SetResize(0, 0); panel->SetFill(0, 1); panel->SetDataTip(0, STR_STATION_LIST_USE_CTRL_TO_SELECT_MORE); @@ -739,16 +746,16 @@ static const NWidgetPart _nested_company_stations_widgets[] = { NWidget(WWT_STICKYBOX, COLOUR_GREY), EndContainer(), NWidget(NWID_HORIZONTAL), - NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_STL_TRAIN), SetMinimalSize(14, 11), SetDataTip(STR_TRAIN, STR_STATION_LIST_USE_CTRL_TO_SELECT_MORE), SetFill(0, 1), - NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_STL_TRUCK), SetMinimalSize(14, 11), SetDataTip(STR_LORRY, STR_STATION_LIST_USE_CTRL_TO_SELECT_MORE), SetFill(0, 1), - NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_STL_BUS), SetMinimalSize(14, 11), SetDataTip(STR_BUS, STR_STATION_LIST_USE_CTRL_TO_SELECT_MORE), SetFill(0, 1), - NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_STL_SHIP), SetMinimalSize(14, 11), SetDataTip(STR_SHIP, STR_STATION_LIST_USE_CTRL_TO_SELECT_MORE), SetFill(0, 1), - NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_STL_AIRPLANE), SetMinimalSize(14, 11), SetDataTip(STR_PLANE, STR_STATION_LIST_USE_CTRL_TO_SELECT_MORE), SetFill(0, 1), - NWidget(WWT_PUSHBTN, COLOUR_GREY, WID_STL_FACILALL), SetMinimalSize(14, 11), SetDataTip(0x0, STR_STATION_LIST_SELECT_ALL_FACILITIES), SetFill(0, 1), - NWidget(WWT_PANEL, COLOUR_GREY), SetMinimalSize(5, 11), SetFill(0, 1), EndContainer(), + NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_STL_TRAIN), SetMinimalSize(14, 0), SetMinimalTextLines(1, 0), SetDataTip(STR_TRAIN, STR_STATION_LIST_USE_CTRL_TO_SELECT_MORE), SetFill(0, 1), + NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_STL_TRUCK), SetMinimalSize(14, 0), SetMinimalTextLines(1, 0), SetDataTip(STR_LORRY, STR_STATION_LIST_USE_CTRL_TO_SELECT_MORE), SetFill(0, 1), + NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_STL_BUS), SetMinimalSize(14, 0), SetMinimalTextLines(1, 0), SetDataTip(STR_BUS, STR_STATION_LIST_USE_CTRL_TO_SELECT_MORE), SetFill(0, 1), + NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_STL_SHIP), SetMinimalSize(14, 0), SetMinimalTextLines(1, 0), SetDataTip(STR_SHIP, STR_STATION_LIST_USE_CTRL_TO_SELECT_MORE), SetFill(0, 1), + NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_STL_AIRPLANE), SetMinimalSize(14, 0), SetMinimalTextLines(1, 0), SetDataTip(STR_PLANE, STR_STATION_LIST_USE_CTRL_TO_SELECT_MORE), SetFill(0, 1), + NWidget(WWT_PUSHBTN, COLOUR_GREY, WID_STL_FACILALL), SetMinimalSize(14, 0), SetMinimalTextLines(1, 0), SetDataTip(0x0, STR_STATION_LIST_SELECT_ALL_FACILITIES), SetFill(0, 1), + NWidget(WWT_PANEL, COLOUR_GREY), SetMinimalSize(5, 0), SetFill(0, 1), EndContainer(), NWidgetFunction(CargoWidgets), - NWidget(WWT_PANEL, COLOUR_GREY, WID_STL_NOCARGOWAITING), SetMinimalSize(14, 11), SetDataTip(0x0, STR_STATION_LIST_NO_WAITING_CARGO), SetFill(0, 1), EndContainer(), - NWidget(WWT_PUSHBTN, COLOUR_GREY, WID_STL_CARGOALL), SetMinimalSize(14, 11), SetDataTip(0x0, STR_STATION_LIST_SELECT_ALL_TYPES), SetFill(0, 1), + NWidget(WWT_PANEL, COLOUR_GREY, WID_STL_NOCARGOWAITING), SetMinimalSize(14, 0), SetMinimalTextLines(1, 0), SetDataTip(0x0, STR_STATION_LIST_NO_WAITING_CARGO), SetFill(0, 1), EndContainer(), + NWidget(WWT_PUSHBTN, COLOUR_GREY, WID_STL_CARGOALL), SetMinimalSize(14, 0), SetMinimalTextLines(1, 0), SetDataTip(0x0, STR_STATION_LIST_SELECT_ALL_TYPES), SetFill(0, 1), NWidget(WWT_PANEL, COLOUR_GREY), SetDataTip(0x0, STR_NULL), SetResize(1, 0), SetFill(1, 1), EndContainer(), EndContainer(), NWidget(NWID_HORIZONTAL), From ef80baf75c20410358a0d39c02b7f76e714abf86 Mon Sep 17 00:00:00 2001 From: Michael Lutz Date: Wed, 21 Apr 2021 22:57:09 +0200 Subject: [PATCH 115/268] Codechange: [Win32] Try getting an OpenGL 4.5 context first before aiming at 3.2. --- src/video/win32_v.cpp | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/video/win32_v.cpp b/src/video/win32_v.cpp index 19e186ae2a..1dc1db8cef 100644 --- a/src/video/win32_v.cpp +++ b/src/video/win32_v.cpp @@ -1360,14 +1360,22 @@ const char *VideoDriver_Win32OpenGL::AllocateContext() /* Create OpenGL device context. Try to get an 3.2+ context if possible. */ if (_wglCreateContextAttribsARB != nullptr) { + /* Try for OpenGL 4.5 first. */ int attribs[] = { - WGL_CONTEXT_MAJOR_VERSION_ARB, 3, - WGL_CONTEXT_MINOR_VERSION_ARB, 2, + WGL_CONTEXT_MAJOR_VERSION_ARB, 4, + WGL_CONTEXT_MINOR_VERSION_ARB, 5, WGL_CONTEXT_FLAGS_ARB, _debug_driver_level >= 8 ? WGL_CONTEXT_DEBUG_BIT_ARB : 0, _hasWGLARBCreateContextProfile ? WGL_CONTEXT_PROFILE_MASK_ARB : 0, WGL_CONTEXT_CORE_PROFILE_BIT_ARB, // Terminate list if WGL_ARB_create_context_profile isn't supported. 0 }; rc = _wglCreateContextAttribsARB(this->dc, nullptr, attribs); + + if (rc == nullptr) { + /* Try again for a 3.2 context. */ + attribs[1] = 3; + attribs[3] = 2; + rc = _wglCreateContextAttribsARB(this->dc, nullptr, attribs); + } } if (rc == nullptr) { From d1dd997f077fca5e1f969a1b610914f9310f9561 Mon Sep 17 00:00:00 2001 From: Michael Lutz Date: Wed, 21 Apr 2021 22:57:48 +0200 Subject: [PATCH 116/268] Change: [Win32] Limit the OpenGL video driver to OpenGL 3.2 or newer on Windows. --- src/video/opengl.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/video/opengl.cpp b/src/video/opengl.cpp index 8a4ce34514..1eab9b10e3 100644 --- a/src/video/opengl.cpp +++ b/src/video/opengl.cpp @@ -548,6 +548,12 @@ const char *OpenGLBackend::Init(const Dimension &screen_res) _gl_major_ver = atoi(ver); _gl_minor_ver = minor != nullptr ? atoi(minor + 1) : 0; +#ifdef _WIN32 + /* Old drivers on Windows (especially if made by Intel) seem to be + * unstable, so cull the oldest stuff here. */ + if (!IsOpenGLVersionAtLeast(3, 2)) return "Need at least OpenGL version 3.2 on Windows"; +#endif + if (!BindBasicOpenGLProcs()) return "Failed to bind basic OpenGL functions."; SetupDebugOutput(); From a9740cef82b6969be1087bb0cfa2fcfdc3058845 Mon Sep 17 00:00:00 2001 From: frosch Date: Sun, 11 Apr 2021 13:56:24 +0200 Subject: [PATCH 117/268] Add: show completion progress of languages in the language dropdown for non-release builds. --- src/lang/english.txt | 1 + src/settings_gui.cpp | 5 ++++- src/strgen/strgen.cpp | 10 +++++++--- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/lang/english.txt b/src/lang/english.txt index 125273ed94..8eb7616bc0 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -993,6 +993,7 @@ STR_GAME_OPTIONS_AUTOSAVE_DROPDOWN_EVERY_12_MONTHS :Every 12 months STR_GAME_OPTIONS_LANGUAGE :{BLACK}Language STR_GAME_OPTIONS_LANGUAGE_TOOLTIP :{BLACK}Select the interface language to use +STR_GAME_OPTIONS_LANGUAGE_PERCENTAGE :{RAW_STRING} ({NUM}% completed) STR_GAME_OPTIONS_FULLSCREEN :{BLACK}Fullscreen STR_GAME_OPTIONS_FULLSCREEN_TOOLTIP :{BLACK}Check this box to play OpenTTD fullscreen mode diff --git a/src/settings_gui.cpp b/src/settings_gui.cpp index 0705c3fcc7..1e3bdb7d7d 100644 --- a/src/settings_gui.cpp +++ b/src/settings_gui.cpp @@ -36,6 +36,7 @@ #include "querystring_gui.h" #include "fontcache.h" #include "zoom_func.h" +#include "rev.h" #include "video/video_driver.hpp" #include "music/music_driver.hpp" @@ -217,7 +218,8 @@ struct GameOptionsWindow : Window { case WID_GO_LANG_DROPDOWN: { // Setup interface language dropdown for (uint i = 0; i < _languages.size(); i++) { - auto item = new DropDownListParamStringItem(STR_JUST_RAW_STRING, i, false); + bool hide_percentage = IsReleasedVersion() || _languages[i].missing < _settings_client.gui.missing_strings_threshold; + auto item = new DropDownListParamStringItem(hide_percentage ? STR_JUST_RAW_STRING : STR_GAME_OPTIONS_LANGUAGE_PERCENTAGE, i, false); if (&_languages[i] == _current_language) { *selected_index = i; item->SetParamStr(0, _languages[i].own_name); @@ -229,6 +231,7 @@ struct GameOptionsWindow : Window { * entries in the dropdown list. */ item->SetParamStr(0, _languages[i].name); } + item->SetParam(1, (LANGUAGE_TOTAL_STRINGS - _languages[i].missing) * 100 / LANGUAGE_TOTAL_STRINGS); list.emplace_back(item); } std::sort(list.begin(), list.end(), DropDownListStringItem::NatSortFunc); diff --git a/src/strgen/strgen.cpp b/src/strgen/strgen.cpp index 9377e63622..68002cc3f0 100644 --- a/src/strgen/strgen.cpp +++ b/src/strgen/strgen.cpp @@ -273,13 +273,14 @@ struct HeaderFileWriter : HeaderWriter, FileWriter { const char *real_filename; /** The previous string ID that was printed. */ int prev; + uint total_strings; /** * Open a file to write to. * @param filename The file to open. */ HeaderFileWriter(const char *filename) : FileWriter("tmp.xxx"), - real_filename(stredup(filename)), prev(0) + real_filename(stredup(filename)), prev(0), total_strings(0) { fprintf(this->fh, "/* This file is automatically generated. Do not modify */\n\n"); fprintf(this->fh, "#ifndef TABLE_STRINGS_H\n"); @@ -297,6 +298,7 @@ struct HeaderFileWriter : HeaderWriter, FileWriter { if (prev + 1 != stringid) fprintf(this->fh, "\n"); fprintf(this->fh, "static const StringID %s = 0x%X;\n", name, stringid); prev = stringid; + total_strings++; } void Finalise(const StringData &data) @@ -311,8 +313,10 @@ struct HeaderFileWriter : HeaderWriter, FileWriter { "\n" "static const uint LANGUAGE_PACK_VERSION = 0x%X;\n" "static const uint LANGUAGE_MAX_PLURAL = %u;\n" - "static const uint LANGUAGE_MAX_PLURAL_FORMS = %d;\n\n", - (uint)data.Version(), (uint)lengthof(_plural_forms), max_plural_forms + "static const uint LANGUAGE_MAX_PLURAL_FORMS = %d;\n" + "static const uint LANGUAGE_TOTAL_STRINGS = %u;\n" + "\n", + (uint)data.Version(), (uint)lengthof(_plural_forms), max_plural_forms, total_strings ); fprintf(this->fh, "#endif /* TABLE_STRINGS_H */\n"); From 37222c3fa2f558df5f7ef420ad583ba403ceda62 Mon Sep 17 00:00:00 2001 From: frosch Date: Sun, 11 Apr 2021 17:28:47 +0200 Subject: [PATCH 118/268] Change: treat languages as finished, if translations are 75% completed. Unfinished translations are not auto-picked from the locale. In release builds, unfinished translations are not offered in the GUI. Unfinished translations are available in non-release builds, or by editing openttd.cfg. --- src/language.h | 1 + src/settings_gui.cpp | 2 ++ src/strings.cpp | 13 +++++++++++++ 3 files changed, 16 insertions(+) diff --git a/src/language.h b/src/language.h index faac595613..9d2499068b 100644 --- a/src/language.h +++ b/src/language.h @@ -58,6 +58,7 @@ struct LanguagePackHeader { char cases[MAX_NUM_CASES][CASE_GENDER_LEN]; ///< the cases used by this translation bool IsValid() const; + bool IsReasonablyFinished() const; /** * Get the index for the given gender. diff --git a/src/settings_gui.cpp b/src/settings_gui.cpp index 1e3bdb7d7d..f5872c648f 100644 --- a/src/settings_gui.cpp +++ b/src/settings_gui.cpp @@ -218,6 +218,8 @@ struct GameOptionsWindow : Window { case WID_GO_LANG_DROPDOWN: { // Setup interface language dropdown for (uint i = 0; i < _languages.size(); i++) { + bool hide_language = IsReleasedVersion() && !_languages[i].IsReasonablyFinished(); + if (hide_language) continue; bool hide_percentage = IsReleasedVersion() || _languages[i].missing < _settings_client.gui.missing_strings_threshold; auto item = new DropDownListParamStringItem(hide_percentage ? STR_JUST_RAW_STRING : STR_GAME_OPTIONS_LANGUAGE_PERCENTAGE, i, false); if (&_languages[i] == _current_language) { diff --git a/src/strings.cpp b/src/strings.cpp index aeea7c071a..d533db1fce 100644 --- a/src/strings.cpp +++ b/src/strings.cpp @@ -1716,6 +1716,15 @@ bool LanguagePackHeader::IsValid() const StrValid(this->digit_decimal_separator, lastof(this->digit_decimal_separator)); } +/** + * Check whether a translation is sufficiently finished to offer it to the public. + */ +bool LanguagePackHeader::IsReasonablyFinished() const +{ + /* "Less than 25% missing" is "sufficiently finished". */ + return 4 * this->missing < LANGUAGE_TOTAL_STRINGS; +} + /** * Read a particular language. * @param lang The metadata about the language. @@ -1969,6 +1978,10 @@ void InitializeLanguagePacks() } if (strcmp (lng.isocode, "en_GB") == 0) en_GB_fallback = &lng; + + /* Only auto-pick finished translations */ + if (!lng.IsReasonablyFinished()) continue; + if (strncmp(lng.isocode, lang, 5) == 0) chosen_language = &lng; if (strncmp(lng.isocode, lang, 2) == 0) language_fallback = &lng; } From 27a956ba6204e400e82fe10056bf730f286b23fe Mon Sep 17 00:00:00 2001 From: peter1138 Date: Fri, 29 Mar 2019 23:24:40 +0000 Subject: [PATCH 119/268] Codechange: Replace Group::replace_protection with Group::flags --- src/autoreplace.cpp | 3 ++- src/group.h | 6 +++++- src/group_cmd.cpp | 20 +++++++++++--------- src/group_gui.cpp | 6 +++--- src/saveload/group_sl.cpp | 2 +- src/script/api/script_group.cpp | 2 +- 6 files changed, 23 insertions(+), 16 deletions(-) diff --git a/src/autoreplace.cpp b/src/autoreplace.cpp index 59980546ee..e1b73448dd 100644 --- a/src/autoreplace.cpp +++ b/src/autoreplace.cpp @@ -11,6 +11,7 @@ #include "command_func.h" #include "group.h" #include "autoreplace_base.h" +#include "core/bitmath_func.hpp" #include "core/pool_func.hpp" #include "safeguards.h" @@ -64,7 +65,7 @@ void RemoveAllEngineReplacement(EngineRenewList *erl) EngineID EngineReplacement(EngineRenewList erl, EngineID engine, GroupID group, bool *replace_when_old) { const EngineRenew *er = GetEngineReplacement(erl, engine, group); - if (er == nullptr && (group == DEFAULT_GROUP || (Group::IsValidID(group) && !Group::Get(group)->replace_protection))) { + if (er == nullptr && (group == DEFAULT_GROUP || (Group::IsValidID(group) && !HasBit(Group::Get(group)->flags, GroupFlags::GF_REPLACE_PROTECTION)))) { /* We didn't find anything useful in the vehicle's own group so we will try ALL_GROUP */ er = GetEngineReplacement(erl, engine, ALL_GROUP); } diff --git a/src/group.h b/src/group.h index 1be8d8b01f..57d15b448d 100644 --- a/src/group.h +++ b/src/group.h @@ -62,13 +62,17 @@ struct GroupStatistics { static void UpdateAutoreplace(CompanyID company); }; +enum GroupFlags : uint8 { + GF_REPLACE_PROTECTION, ///< If set to true, the global autoreplace has no effect on the group +}; + /** Group data. */ struct Group : GroupPool::PoolItem<&_group_pool> { std::string name; ///< Group Name Owner owner; ///< Group Owner VehicleType vehicle_type; ///< Vehicle type of the group - bool replace_protection; ///< If set to true, the global autoreplace have no effect on the group + uint8 flags; ///< Group flags Livery livery; ///< Custom colour scheme for vehicles in this group GroupStatistics statistics; ///< NOSAVE: Statistics and caches on the vehicles in the group. diff --git a/src/group_cmd.cpp b/src/group_cmd.cpp index bf25dae862..219470c207 100644 --- a/src/group_cmd.cpp +++ b/src/group_cmd.cpp @@ -315,7 +315,7 @@ CommandCost CmdCreateGroup(TileIndex tile, DoCommandFlag flags, uint32 p1, uint3 if (flags & DC_EXEC) { Group *g = new Group(_current_company); - g->replace_protection = false; + ClrBit(g->flags, GroupFlags::GF_REPLACE_PROTECTION); g->vehicle_type = vt; g->parent = INVALID_GROUP; @@ -668,12 +668,18 @@ CommandCost CmdSetGroupLivery(TileIndex tile, DoCommandFlag flags, uint32 p1, ui * @param g initial group. * @param protect 1 to set or 0 to clear protection. */ -static void SetGroupReplaceProtection(Group *g, bool protect) +static void SetGroupReplaceProtection(Group *g, bool protect, bool children) { - g->replace_protection = protect; + if (protect) { + SetBit(g->flags, GroupFlags::GF_REPLACE_PROTECTION); + } else { + ClrBit(g->flags, GroupFlags::GF_REPLACE_PROTECTION); + } + + if (!children) return; for (Group *pg : Group::Iterate()) { - if (pg->parent == g->index) SetGroupReplaceProtection(pg, protect); + if (pg->parent == g->index) SetGroupReplaceProtection(pg, protect, true); } } @@ -695,11 +701,7 @@ CommandCost CmdSetGroupReplaceProtection(TileIndex tile, DoCommandFlag flags, ui if (g == nullptr || g->owner != _current_company) return CMD_ERROR; if (flags & DC_EXEC) { - if (HasBit(p2, 1)) { - SetGroupReplaceProtection(g, HasBit(p2, 0)); - } else { - g->replace_protection = HasBit(p2, 0); - } + SetGroupReplaceProtection(g, HasBit(p2, 0), HasBit(p2, 1)); SetWindowDirty(GetWindowClassForVehicleType(g->vehicle_type), VehicleListIdentifier(VL_GROUP_LIST, g->vehicle_type, _current_company).Pack()); InvalidateWindowData(WC_REPLACE_VEHICLE, g->vehicle_type); diff --git a/src/group_gui.cpp b/src/group_gui.cpp index 3720533043..0f463e030f 100644 --- a/src/group_gui.cpp +++ b/src/group_gui.cpp @@ -533,7 +533,7 @@ public: /* If not a default group and the group has replace protection, show an enabled replace sprite. */ uint16 protect_sprite = SPR_GROUP_REPLACE_OFF_TRAIN; - if (!IsDefaultGroupID(this->vli.index) && !IsAllGroupID(this->vli.index) && Group::Get(this->vli.index)->replace_protection) protect_sprite = SPR_GROUP_REPLACE_ON_TRAIN; + if (!IsDefaultGroupID(this->vli.index) && !IsAllGroupID(this->vli.index) && HasBit(Group::Get(this->vli.index)->flags, GroupFlags::GF_REPLACE_PROTECTION)) protect_sprite = SPR_GROUP_REPLACE_ON_TRAIN; this->GetWidget(WID_GL_REPLACE_PROTECTION)->widget_data = protect_sprite + this->vli.vtype; /* Set text of "group by" dropdown widget. */ @@ -601,7 +601,7 @@ public: assert(g->owner == this->owner); - DrawGroupInfo(y1, r.left, r.right, g->index, this->indents[i] * LEVEL_WIDTH, g->replace_protection, g->folded || (i + 1 < (int)this->groups.size() && indents[i + 1] > this->indents[i])); + DrawGroupInfo(y1, r.left, r.right, g->index, this->indents[i] * LEVEL_WIDTH, HasBit(g->flags, GroupFlags::GF_REPLACE_PROTECTION), g->folded || (i + 1 < (int)this->groups.size() && indents[i + 1] > this->indents[i])); y1 += this->tiny_step_height; } @@ -798,7 +798,7 @@ public: case WID_GL_REPLACE_PROTECTION: { const Group *g = Group::GetIfValid(this->vli.index); if (g != nullptr) { - DoCommandP(0, this->vli.index, (g->replace_protection ? 0 : 1) | (_ctrl_pressed << 1), CMD_SET_GROUP_REPLACE_PROTECTION); + DoCommandP(0, this->vli.index, (HasBit(g->flags, GroupFlags::GF_REPLACE_PROTECTION) ? 0 : 1) | (_ctrl_pressed << 1), CMD_SET_GROUP_REPLACE_PROTECTION); } break; } diff --git a/src/saveload/group_sl.cpp b/src/saveload/group_sl.cpp index cae313ff83..c5f7e2b507 100644 --- a/src/saveload/group_sl.cpp +++ b/src/saveload/group_sl.cpp @@ -21,7 +21,7 @@ static const SaveLoad _group_desc[] = { SLE_CONDNULL(2, SL_MIN_VERSION, SLV_164), // num_vehicle SLE_VAR(Group, owner, SLE_UINT8), SLE_VAR(Group, vehicle_type, SLE_UINT8), - SLE_VAR(Group, replace_protection, SLE_BOOL), + SLE_VAR(Group, flags, SLE_UINT8), SLE_CONDVAR(Group, livery.in_use, SLE_UINT8, SLV_GROUP_LIVERIES, SL_MAX_VERSION), SLE_CONDVAR(Group, livery.colour1, SLE_UINT8, SLV_GROUP_LIVERIES, SL_MAX_VERSION), SLE_CONDVAR(Group, livery.colour2, SLE_UINT8, SLV_GROUP_LIVERIES, SL_MAX_VERSION), diff --git a/src/script/api/script_group.cpp b/src/script/api/script_group.cpp index 7f80815864..ae2e4137b9 100644 --- a/src/script/api/script_group.cpp +++ b/src/script/api/script_group.cpp @@ -96,7 +96,7 @@ { if (!IsValidGroup(group_id)) return false; - return ::Group::Get(group_id)->replace_protection; + return HasBit(::Group::Get(group_id)->flags, GroupFlags::GF_REPLACE_PROTECTION); } /* static */ int32 ScriptGroup::GetNumEngines(GroupID group_id, EngineID engine_id) From c56fce70b440a95914bf374f68ac045ca97b3610 Mon Sep 17 00:00:00 2001 From: peter1138 Date: Fri, 29 Mar 2019 23:44:28 +0000 Subject: [PATCH 120/268] Codechange: Replace CMD_SET_GROUP_REPLACE_PROTECTION with generic CMD_SET_GROUP_FLAG. --- src/command.cpp | 4 ++-- src/command_type.h | 2 +- src/group.h | 1 + src/group_cmd.cpp | 30 ++++++++++++++++++------------ src/group_gui.cpp | 2 +- src/script/api/script_group.cpp | 2 +- 6 files changed, 24 insertions(+), 17 deletions(-) diff --git a/src/command.cpp b/src/command.cpp index 7c7ca13e4b..dd6ce1cbf5 100644 --- a/src/command.cpp +++ b/src/command.cpp @@ -193,7 +193,7 @@ CommandProc CmdDeleteGroup; CommandProc CmdAddVehicleGroup; CommandProc CmdAddSharedVehicleGroup; CommandProc CmdRemoveAllVehiclesGroup; -CommandProc CmdSetGroupReplaceProtection; +CommandProc CmdSetGroupFlag; CommandProc CmdSetGroupLivery; CommandProc CmdMoveOrder; @@ -359,7 +359,7 @@ static const Command _command_proc_table[] = { DEF_CMD(CmdAddVehicleGroup, 0, CMDT_ROUTE_MANAGEMENT ), // CMD_ADD_VEHICLE_GROUP DEF_CMD(CmdAddSharedVehicleGroup, 0, CMDT_ROUTE_MANAGEMENT ), // CMD_ADD_SHARE_VEHICLE_GROUP DEF_CMD(CmdRemoveAllVehiclesGroup, 0, CMDT_ROUTE_MANAGEMENT ), // CMD_REMOVE_ALL_VEHICLES_GROUP - DEF_CMD(CmdSetGroupReplaceProtection, 0, CMDT_ROUTE_MANAGEMENT ), // CMD_SET_GROUP_REPLACE_PROTECTION + DEF_CMD(CmdSetGroupFlag, 0, CMDT_ROUTE_MANAGEMENT ), // CMD_SET_GROUP_FLAG DEF_CMD(CmdSetGroupLivery, 0, CMDT_ROUTE_MANAGEMENT ), // CMD_SET_GROUP_LIVERY DEF_CMD(CmdMoveOrder, 0, CMDT_ROUTE_MANAGEMENT ), // CMD_MOVE_ORDER DEF_CMD(CmdChangeTimetable, 0, CMDT_ROUTE_MANAGEMENT ), // CMD_CHANGE_TIMETABLE diff --git a/src/command_type.h b/src/command_type.h index 2419d7b312..732932b7a8 100644 --- a/src/command_type.h +++ b/src/command_type.h @@ -324,7 +324,7 @@ enum Commands { CMD_ADD_VEHICLE_GROUP, ///< add a vehicle to a group CMD_ADD_SHARED_VEHICLE_GROUP, ///< add all other shared vehicles to a group which are missing CMD_REMOVE_ALL_VEHICLES_GROUP, ///< remove all vehicles from a group - CMD_SET_GROUP_REPLACE_PROTECTION, ///< set the autoreplace-protection for a group + CMD_SET_GROUP_FLAG, ///< set/clear a flag for a group CMD_SET_GROUP_LIVERY, ///< set the livery for a group CMD_MOVE_ORDER, ///< move an order diff --git a/src/group.h b/src/group.h index 57d15b448d..bd6f47bdf6 100644 --- a/src/group.h +++ b/src/group.h @@ -64,6 +64,7 @@ struct GroupStatistics { enum GroupFlags : uint8 { GF_REPLACE_PROTECTION, ///< If set to true, the global autoreplace has no effect on the group + GF_END, }; /** Group data. */ diff --git a/src/group_cmd.cpp b/src/group_cmd.cpp index 219470c207..2c2cfeaab4 100644 --- a/src/group_cmd.cpp +++ b/src/group_cmd.cpp @@ -664,44 +664,50 @@ CommandCost CmdSetGroupLivery(TileIndex tile, DoCommandFlag flags, uint32 p1, ui } /** - * Set replace protection for a group and its sub-groups. + * Set group flag for a group and its sub-groups. * @param g initial group. - * @param protect 1 to set or 0 to clear protection. + * @param set 1 to set or 0 to clear protection. */ -static void SetGroupReplaceProtection(Group *g, bool protect, bool children) +static void SetGroupFlag(Group *g, GroupFlags flag, bool set, bool children) { - if (protect) { - SetBit(g->flags, GroupFlags::GF_REPLACE_PROTECTION); + if (set) { + SetBit(g->flags, flag); } else { - ClrBit(g->flags, GroupFlags::GF_REPLACE_PROTECTION); + ClrBit(g->flags, flag); } if (!children) return; for (Group *pg : Group::Iterate()) { - if (pg->parent == g->index) SetGroupReplaceProtection(pg, protect, true); + if (pg->parent == g->index) SetGroupFlag(pg, flag, set, true); } } /** - * (Un)set global replace protection from a group + * (Un)set group flag from a group * @param tile unused * @param flags type of operation * @param p1 index of group array - * - p1 bit 0-15 : GroupID + * - p1 bit 0-15 : GroupID + * - p1 bit 16-18 : Flag to set, by value not bit. * @param p2 * - p2 bit 0 : 1 to set or 0 to clear protection. * - p2 bit 1 : 1 to apply to sub-groups. * @param text unused * @return the cost of this operation or an error */ -CommandCost CmdSetGroupReplaceProtection(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text) +CommandCost CmdSetGroupFlag(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text) { - Group *g = Group::GetIfValid(p1); + Group *g = Group::GetIfValid(GB(p1, 0, 16)); if (g == nullptr || g->owner != _current_company) return CMD_ERROR; + /* GroupFlags are stored in as an 8 bit bitfield but passed here by value, + * so 3 bits is sufficient to cover each possible value. */ + GroupFlags flag = (GroupFlags)GB(p1, 16, 3); + if (flag >= GroupFlags::GF_END) return CMD_ERROR; + if (flags & DC_EXEC) { - SetGroupReplaceProtection(g, HasBit(p2, 0), HasBit(p2, 1)); + SetGroupFlag(g, flag, HasBit(p2, 0), HasBit(p2, 1)); SetWindowDirty(GetWindowClassForVehicleType(g->vehicle_type), VehicleListIdentifier(VL_GROUP_LIST, g->vehicle_type, _current_company).Pack()); InvalidateWindowData(WC_REPLACE_VEHICLE, g->vehicle_type); diff --git a/src/group_gui.cpp b/src/group_gui.cpp index 0f463e030f..1a6deae2cb 100644 --- a/src/group_gui.cpp +++ b/src/group_gui.cpp @@ -798,7 +798,7 @@ public: case WID_GL_REPLACE_PROTECTION: { const Group *g = Group::GetIfValid(this->vli.index); if (g != nullptr) { - DoCommandP(0, this->vli.index, (HasBit(g->flags, GroupFlags::GF_REPLACE_PROTECTION) ? 0 : 1) | (_ctrl_pressed << 1), CMD_SET_GROUP_REPLACE_PROTECTION); + DoCommandP(0, this->vli.index | (GroupFlags::GF_REPLACE_PROTECTION << 16), (HasBit(g->flags, GroupFlags::GF_REPLACE_PROTECTION) ? 0 : 1) | (_ctrl_pressed << 1), CMD_SET_GROUP_FLAG); } break; } diff --git a/src/script/api/script_group.cpp b/src/script/api/script_group.cpp index ae2e4137b9..9cf6c7c957 100644 --- a/src/script/api/script_group.cpp +++ b/src/script/api/script_group.cpp @@ -89,7 +89,7 @@ { EnforcePrecondition(false, IsValidGroup(group_id)); - return ScriptObject::DoCommand(0, group_id, enable ? 1 : 0, CMD_SET_GROUP_REPLACE_PROTECTION); + return ScriptObject::DoCommand(0, group_id | GroupFlags::GF_REPLACE_PROTECTION, enable ? 1 : 0, CMD_SET_GROUP_FLAG); } /* static */ bool ScriptGroup::GetAutoReplaceProtection(GroupID group_id) From a05bc04b638020e7faa7d990a8dcc5404e58ea91 Mon Sep 17 00:00:00 2001 From: peter1138 Date: Sat, 30 Mar 2019 07:13:08 +0000 Subject: [PATCH 121/268] Feature: Per-group wagon removal flag. --- src/autoreplace_cmd.cpp | 3 +++ src/autoreplace_gui.cpp | 22 ++++++++++++++++++---- src/group.h | 3 ++- src/group_cmd.cpp | 3 ++- src/saveload/afterload.cpp | 17 +++++++++++++++++ src/saveload/saveload.h | 1 + 6 files changed, 43 insertions(+), 6 deletions(-) diff --git a/src/autoreplace_cmd.cpp b/src/autoreplace_cmd.cpp index fe86917c77..d9996bb117 100644 --- a/src/autoreplace_cmd.cpp +++ b/src/autoreplace_cmd.cpp @@ -741,6 +741,9 @@ CommandCost CmdAutoreplaceVehicle(TileIndex tile, DoCommandFlag flags, uint32 p1 const Company *c = Company::Get(_current_company); bool wagon_removal = c->settings.renew_keep_length; + const Group *g = Group::GetIfValid(v->group_id); + if (g != nullptr) wagon_removal = HasBit(g->flags, GroupFlags::GF_REPLACE_WAGON_REMOVAL); + /* Test whether any replacement is set, before issuing a whole lot of commands that would end in nothing changed */ Vehicle *w = v; bool any_replacements = false; diff --git a/src/autoreplace_gui.cpp b/src/autoreplace_gui.cpp index e952dcf190..d16e0916e1 100644 --- a/src/autoreplace_gui.cpp +++ b/src/autoreplace_gui.cpp @@ -375,8 +375,15 @@ public: break; case WID_RV_TRAIN_WAGONREMOVE_TOGGLE: { - const Company *c = Company::Get(_local_company); - SetDParam(0, c->settings.renew_keep_length ? STR_CONFIG_SETTING_ON : STR_CONFIG_SETTING_OFF); + bool remove_wagon; + const Group *g = Group::GetIfValid(this->sel_group); + if (g != nullptr) { + remove_wagon = HasBit(g->flags, GroupFlags::GF_REPLACE_WAGON_REMOVAL); + } else { + const Company *c = Company::Get(_local_company); + remove_wagon = c->settings.renew_keep_length; + } + SetDParam(0, remove_wagon ? STR_CONFIG_SETTING_ON : STR_CONFIG_SETTING_OFF); break; } @@ -528,9 +535,16 @@ public: } break; - case WID_RV_TRAIN_WAGONREMOVE_TOGGLE: // toggle renew_keep_length - DoCommandP(0, GetCompanySettingIndex("company.renew_keep_length"), Company::Get(_local_company)->settings.renew_keep_length ? 0 : 1, CMD_CHANGE_COMPANY_SETTING); + case WID_RV_TRAIN_WAGONREMOVE_TOGGLE: { + const Group *g = Group::GetIfValid(this->sel_group); + if (g != nullptr) { + DoCommandP(0, this->sel_group | (GroupFlags::GF_REPLACE_WAGON_REMOVAL << 16), (HasBit(g->flags, GroupFlags::GF_REPLACE_WAGON_REMOVAL) ? 0 : 1) | (_ctrl_pressed << 1), CMD_SET_GROUP_FLAG); + } else { + // toggle renew_keep_length + DoCommandP(0, GetCompanySettingIndex("company.renew_keep_length"), Company::Get(_local_company)->settings.renew_keep_length ? 0 : 1, CMD_CHANGE_COMPANY_SETTING); + } break; + } case WID_RV_START_REPLACE: { // Start replacing if (this->GetWidget(widget)->ButtonHit(pt)) { diff --git a/src/group.h b/src/group.h index bd6f47bdf6..e0f0b91960 100644 --- a/src/group.h +++ b/src/group.h @@ -63,7 +63,8 @@ struct GroupStatistics { }; enum GroupFlags : uint8 { - GF_REPLACE_PROTECTION, ///< If set to true, the global autoreplace has no effect on the group + GF_REPLACE_PROTECTION, ///< If set to true, the global autoreplace has no effect on the group + GF_REPLACE_WAGON_REMOVAL, ///< If set, autoreplace will perform wagon removal on vehicles in this group. GF_END, }; diff --git a/src/group_cmd.cpp b/src/group_cmd.cpp index 2c2cfeaab4..bbef35ae23 100644 --- a/src/group_cmd.cpp +++ b/src/group_cmd.cpp @@ -315,7 +315,6 @@ CommandCost CmdCreateGroup(TileIndex tile, DoCommandFlag flags, uint32 p1, uint3 if (flags & DC_EXEC) { Group *g = new Group(_current_company); - ClrBit(g->flags, GroupFlags::GF_REPLACE_PROTECTION); g->vehicle_type = vt; g->parent = INVALID_GROUP; @@ -323,10 +322,12 @@ CommandCost CmdCreateGroup(TileIndex tile, DoCommandFlag flags, uint32 p1, uint3 const Company *c = Company::Get(_current_company); g->livery.colour1 = c->livery[LS_DEFAULT].colour1; g->livery.colour2 = c->livery[LS_DEFAULT].colour2; + if (c->settings.renew_keep_length) SetBit(g->flags, GroupFlags::GF_REPLACE_WAGON_REMOVAL); } else { g->parent = pg->index; g->livery.colour1 = pg->livery.colour1; g->livery.colour2 = pg->livery.colour2; + g->flags = pg->flags; } _new_group_id = g->index; diff --git a/src/saveload/afterload.cpp b/src/saveload/afterload.cpp index be6be8de69..d4b230dfd0 100644 --- a/src/saveload/afterload.cpp +++ b/src/saveload/afterload.cpp @@ -3127,6 +3127,23 @@ bool AfterLoadGame() } } + if (IsSavegameVersionBefore(SLV_GROUP_REPLACE_WAGON_REMOVAL)) { + /* Propagate wagon removal flag for compatibility */ + /* Temporary bitmask of company wagon removal setting */ + uint16 wagon_removal = 0; + for (const Company *c : Company::Iterate()) { + if (c->settings.renew_keep_length) SetBit(wagon_removal, c->index); + } + for (Group *g : Group::Iterate()) { + if (g->flags != 0) { + /* Convert old replace_protection value to flag. */ + g->flags = 0; + SetBit(g->flags, GroupFlags::GF_REPLACE_PROTECTION); + } + if (HasBit(wagon_removal, g->owner)) SetBit(g->flags, GroupFlags::GF_REPLACE_WAGON_REMOVAL); + } + } + /* Compute station catchment areas. This is needed here in case UpdateStationAcceptance is called below. */ Station::RecomputeCatchmentForAll(); diff --git a/src/saveload/saveload.h b/src/saveload/saveload.h index 8ad29a6407..cda6af82b6 100644 --- a/src/saveload/saveload.h +++ b/src/saveload/saveload.h @@ -325,6 +325,7 @@ enum SaveLoadVersion : uint16 { SLV_VEH_MOTION_COUNTER, ///< 288 PR#8591 Desync safe motion counter SLV_INDUSTRY_TEXT, ///< 289 PR#8576 v1.11.0-RC1 Additional GS text for industries. SLV_MAPGEN_SETTINGS_REVAMP, ///< 290 PR#8891 v1.11 Revamp of some mapgen settings (snow coverage, desert coverage, heightmap height, custom terrain type). + SLV_GROUP_REPLACE_WAGON_REMOVAL, ///< 291 PR#7441 Per-group wagon removal flag. SL_MAX_VERSION, ///< Highest possible saveload version }; From c52a1154ed8528efc2556c2243d796af55d43d64 Mon Sep 17 00:00:00 2001 From: translators Date: Fri, 23 Apr 2021 17:55:37 +0000 Subject: [PATCH 122/268] Update: Translations from eints dutch: 6 changes by Afoklala --- src/lang/dutch.txt | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/lang/dutch.txt b/src/lang/dutch.txt index 4bd4a48e7a..63ed51c351 100644 --- a/src/lang/dutch.txt +++ b/src/lang/dutch.txt @@ -1006,6 +1006,8 @@ STR_GAME_OPTIONS_VIDEO_ACCELERATION :{BLACK}Hardware STR_GAME_OPTIONS_VIDEO_ACCELERATION_TOOLTIP :{BLACK}Selecteer dit vakje om OpenTTD hardwareversnelling te laten gebruiken. De gewijzigde instelling wordt pas van kracht nadat het spel opnieuw is gestart. STR_GAME_OPTIONS_VIDEO_ACCELERATION_RESTART :{WHITE}De instelling wordt pas van kracht als het spel opnieuw is gestart +STR_GAME_OPTIONS_VIDEO_VSYNC :{BLACK}VSync +STR_GAME_OPTIONS_VIDEO_VSYNC_TOOLTIP :{BLACK}Selecteer dit vakje om het scherm verticaal te synchroniseren. De wijziging gaat pas in nadat je het spel opnieuw hebt opgestart. Werkt alleen als hardwareversnelling is ingeschakeld STR_GAME_OPTIONS_GUI_ZOOM_FRAME :{BLACK}Menupuntgrootte STR_GAME_OPTIONS_GUI_ZOOM_DROPDOWN_TOOLTIP :{BLACK}Kiest de grootte van bedieningselementen @@ -1139,6 +1141,7 @@ STR_CONFIG_SETTING_TREE_CAPTION :{WHITE}Instelli STR_CONFIG_SETTING_FILTER_TITLE :{BLACK}Filtertekst: STR_CONFIG_SETTING_EXPAND_ALL :{BLACK}Alles uitvouwen STR_CONFIG_SETTING_COLLAPSE_ALL :{BLACK}Alles inklappen +STR_CONFIG_SETTING_RESET_ALL :{BLACK}Alle waarden terugstellen STR_CONFIG_SETTING_NO_EXPLANATION_AVAILABLE_HELPTEXT :(geen uitleg beschikbaar) STR_CONFIG_SETTING_DEFAULT_VALUE :{LTBLUE}Standaardwaarde: {ORANGE}{STRING} STR_CONFIG_SETTING_TYPE :{LTBLUE}Instellingstype: {ORANGE}{STRING} @@ -1147,6 +1150,8 @@ STR_CONFIG_SETTING_TYPE_GAME_MENU :Spelinstellinge STR_CONFIG_SETTING_TYPE_GAME_INGAME :Spelinstellingen (opgeslagen in bestand; alleen van invloed op huidig spel) STR_CONFIG_SETTING_TYPE_COMPANY_MENU :Bedrijfsinstellingen (opgeslagen in bestand; alleen van invloed op nieuwe spellen) STR_CONFIG_SETTING_TYPE_COMPANY_INGAME :Bedrijfsinstellingen (opgeslagen in bestand; alleen van invloed op huidig bedrijf) +STR_CONFIG_SETTING_RESET_ALL_CONFIRMATION_DIALOG_CAPTION :{WHITE}Voorzichtig! +STR_CONFIG_SETTING_RESET_ALL_CONFIRMATION_DIALOG_TEXT :{WHITE}Met deze actie herstel je alle spelinstellingen naar hun standaardwaarden.{}Weet je zeker dat je wilt doorgaan? STR_CONFIG_SETTING_RESTRICT_CATEGORY :{BLACK}Categorie: STR_CONFIG_SETTING_RESTRICT_TYPE :{BLACK}Type: @@ -2492,7 +2497,7 @@ STR_WATERWAYS_TOOLBAR_BUILD_DOCK_TOOLTIP :{BLACK}Haven bo STR_WATERWAYS_TOOLBAR_BUOY_TOOLTIP :{BLACK}Boei plaatsen, deze kan gebruikt worden voor extra tussenstops. Shift schakelt tussen bouwen/inschatting van de kosten STR_WATERWAYS_TOOLBAR_BUILD_AQUEDUCT_TOOLTIP :{BLACK}Aquaduct bouwen. Shift schakelt tussen bouwen/inschatting van de kosten. STR_WATERWAYS_TOOLBAR_CREATE_LAKE_TOOLTIP :{BLACK}Hiermee plaats je watermassa's.{}Maakt een kanaal, tenzij je Ctrl vasthoudt op zeeniveau, dan overstroomt de omgeving. -STR_WATERWAYS_TOOLBAR_CREATE_RIVER_TOOLTIP :{BLACK}Hiermee maak je rivieren +STR_WATERWAYS_TOOLBAR_CREATE_RIVER_TOOLTIP :{BLACK}Hiermee maak je rivieren. Ctrl selecteert het gebied diagonaal # Ship depot construction window STR_DEPOT_BUILD_SHIP_CAPTION :{WHITE}Richting van dok From 8dda2c2c176d9b1c0d90fb87eef4cd845bb1f269 Mon Sep 17 00:00:00 2001 From: frosch Date: Fri, 23 Apr 2021 22:31:58 +0200 Subject: [PATCH 123/268] Change: move all 'unstable' language into 'stable'. --- src/lang/CMakeLists.txt | 8 ++++++++ src/lang/{unfinished => }/chuvash.txt | 0 src/lang/{unfinished => }/frisian.txt | 0 src/lang/{unfinished => }/ido.txt | 0 src/lang/{unfinished => }/macedonian.txt | 0 src/lang/{unfinished => }/maltese.txt | 0 src/lang/{unfinished => }/marathi.txt | 0 src/lang/{unfinished => }/persian.txt | 0 src/lang/{unfinished => }/urdu.txt | 0 9 files changed, 8 insertions(+) rename src/lang/{unfinished => }/chuvash.txt (100%) rename src/lang/{unfinished => }/frisian.txt (100%) rename src/lang/{unfinished => }/ido.txt (100%) rename src/lang/{unfinished => }/macedonian.txt (100%) rename src/lang/{unfinished => }/maltese.txt (100%) rename src/lang/{unfinished => }/marathi.txt (100%) rename src/lang/{unfinished => }/persian.txt (100%) rename src/lang/{unfinished => }/urdu.txt (100%) diff --git a/src/lang/CMakeLists.txt b/src/lang/CMakeLists.txt index f3aedca4b4..2c9204ed42 100644 --- a/src/lang/CMakeLists.txt +++ b/src/lang/CMakeLists.txt @@ -11,6 +11,7 @@ set(LANG_SOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/brazilian_portuguese.txt ${CMAKE_CURRENT_SOURCE_DIR}/bulgarian.txt ${CMAKE_CURRENT_SOURCE_DIR}/catalan.txt + ${CMAKE_CURRENT_SOURCE_DIR}/chuvash.txt ${CMAKE_CURRENT_SOURCE_DIR}/croatian.txt ${CMAKE_CURRENT_SOURCE_DIR}/czech.txt ${CMAKE_CURRENT_SOURCE_DIR}/danish.txt @@ -22,6 +23,7 @@ set(LANG_SOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/faroese.txt ${CMAKE_CURRENT_SOURCE_DIR}/finnish.txt ${CMAKE_CURRENT_SOURCE_DIR}/french.txt + ${CMAKE_CURRENT_SOURCE_DIR}/frisian.txt ${CMAKE_CURRENT_SOURCE_DIR}/gaelic.txt ${CMAKE_CURRENT_SOURCE_DIR}/galician.txt ${CMAKE_CURRENT_SOURCE_DIR}/german.txt @@ -29,6 +31,7 @@ set(LANG_SOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/hebrew.txt ${CMAKE_CURRENT_SOURCE_DIR}/hungarian.txt ${CMAKE_CURRENT_SOURCE_DIR}/icelandic.txt + ${CMAKE_CURRENT_SOURCE_DIR}/ido.txt ${CMAKE_CURRENT_SOURCE_DIR}/indonesian.txt ${CMAKE_CURRENT_SOURCE_DIR}/irish.txt ${CMAKE_CURRENT_SOURCE_DIR}/italian.txt @@ -38,9 +41,13 @@ set(LANG_SOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/latvian.txt ${CMAKE_CURRENT_SOURCE_DIR}/lithuanian.txt ${CMAKE_CURRENT_SOURCE_DIR}/luxembourgish.txt + ${CMAKE_CURRENT_SOURCE_DIR}/macedonian.txt ${CMAKE_CURRENT_SOURCE_DIR}/malay.txt + ${CMAKE_CURRENT_SOURCE_DIR}/maltese.txt + ${CMAKE_CURRENT_SOURCE_DIR}/marathi.txt ${CMAKE_CURRENT_SOURCE_DIR}/norwegian_bokmal.txt ${CMAKE_CURRENT_SOURCE_DIR}/norwegian_nynorsk.txt + ${CMAKE_CURRENT_SOURCE_DIR}/persian.txt ${CMAKE_CURRENT_SOURCE_DIR}/polish.txt ${CMAKE_CURRENT_SOURCE_DIR}/portuguese.txt ${CMAKE_CURRENT_SOURCE_DIR}/romanian.txt @@ -56,6 +63,7 @@ set(LANG_SOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/thai.txt ${CMAKE_CURRENT_SOURCE_DIR}/traditional_chinese.txt ${CMAKE_CURRENT_SOURCE_DIR}/turkish.txt + ${CMAKE_CURRENT_SOURCE_DIR}/urdu.txt ${CMAKE_CURRENT_SOURCE_DIR}/ukrainian.txt ${CMAKE_CURRENT_SOURCE_DIR}/vietnamese.txt ${CMAKE_CURRENT_SOURCE_DIR}/welsh.txt diff --git a/src/lang/unfinished/chuvash.txt b/src/lang/chuvash.txt similarity index 100% rename from src/lang/unfinished/chuvash.txt rename to src/lang/chuvash.txt diff --git a/src/lang/unfinished/frisian.txt b/src/lang/frisian.txt similarity index 100% rename from src/lang/unfinished/frisian.txt rename to src/lang/frisian.txt diff --git a/src/lang/unfinished/ido.txt b/src/lang/ido.txt similarity index 100% rename from src/lang/unfinished/ido.txt rename to src/lang/ido.txt diff --git a/src/lang/unfinished/macedonian.txt b/src/lang/macedonian.txt similarity index 100% rename from src/lang/unfinished/macedonian.txt rename to src/lang/macedonian.txt diff --git a/src/lang/unfinished/maltese.txt b/src/lang/maltese.txt similarity index 100% rename from src/lang/unfinished/maltese.txt rename to src/lang/maltese.txt diff --git a/src/lang/unfinished/marathi.txt b/src/lang/marathi.txt similarity index 100% rename from src/lang/unfinished/marathi.txt rename to src/lang/marathi.txt diff --git a/src/lang/unfinished/persian.txt b/src/lang/persian.txt similarity index 100% rename from src/lang/unfinished/persian.txt rename to src/lang/persian.txt diff --git a/src/lang/unfinished/urdu.txt b/src/lang/urdu.txt similarity index 100% rename from src/lang/unfinished/urdu.txt rename to src/lang/urdu.txt From 57b4cc64b7de25721c1a38290a98a4b735a917e4 Mon Sep 17 00:00:00 2001 From: frosch Date: Fri, 23 Apr 2021 22:33:30 +0200 Subject: [PATCH 124/268] Add: Hindi translation. --- src/lang/CMakeLists.txt | 1 + src/lang/hindi.txt | 995 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 996 insertions(+) create mode 100644 src/lang/hindi.txt diff --git a/src/lang/CMakeLists.txt b/src/lang/CMakeLists.txt index 2c9204ed42..1acf27588f 100644 --- a/src/lang/CMakeLists.txt +++ b/src/lang/CMakeLists.txt @@ -29,6 +29,7 @@ set(LANG_SOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/german.txt ${CMAKE_CURRENT_SOURCE_DIR}/greek.txt ${CMAKE_CURRENT_SOURCE_DIR}/hebrew.txt + ${CMAKE_CURRENT_SOURCE_DIR}/hindi.txt ${CMAKE_CURRENT_SOURCE_DIR}/hungarian.txt ${CMAKE_CURRENT_SOURCE_DIR}/icelandic.txt ${CMAKE_CURRENT_SOURCE_DIR}/ido.txt diff --git a/src/lang/hindi.txt b/src/lang/hindi.txt new file mode 100644 index 0000000000..b0d67efb33 --- /dev/null +++ b/src/lang/hindi.txt @@ -0,0 +1,995 @@ +##name Hindi +##ownname हिन्दी +##isocode hi_IN +##plural 0 +##textdir ltr +##digitsep , +##digitsepcur , +##decimalsep . +##winlangid 0x0439 +##grflangid 0x17 + + +# This file is part of OpenTTD. +# OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. +# OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see . + + +##id 0x0000 +STR_NULL : +STR_EMPTY : + +# Cargo related strings +# Plural cargo name +STR_CARGO_PLURAL_NOTHING : + +# Singular cargo name +STR_CARGO_SINGULAR_NOTHING : + +# Quantity of cargo +STR_QUANTITY_NOTHING : + +# Two letter abbreviation of cargo name +STR_ABBREV_NOTHING : + +# 'Mode' of transport for cargoes + +# Colours, do not shuffle + +# Units used in OpenTTD + + + + + + + + +# Common window strings + + + +# Show engines button + + +# Query window + +# On screen keyboard window + +# Measurement tooltip + + +# These are used in buttons +# These are used in dropdowns + +# Group by options for vehicle list + +# Tooltips for the main toolbar + +# Extra tooltips for the scenario editor toolbar + +############ range for SE file menu starts +STR_SCENEDIT_FILE_MENU_SEPARATOR : +############ range for SE file menu starts + +############ range for settings menu starts +############ range ends here + +############ range for file menu starts +STR_FILE_MENU_SEPARATOR : +############ range ends here + +# map menu + +############ range for town menu starts +############ range ends here + +############ range for subsidies menu starts +############ range ends here + +############ range for graph menu starts +############ range ends here + +############ range for company league menu starts +############ range ends here + +############ range for industry menu starts +############ range ends here + +############ range for railway construction menu starts +############ range ends here + +############ range for road construction menu starts +############ range ends here + +############ range for waterways construction menu starts +############ range ends here + +############ range for airport construction menu starts +############ range ends here + +############ range for landscaping menu starts +############ range ends here + +############ range for music menu starts +############ range ends here + +############ range for message menu starts +############ range ends here + +############ range for about menu starts +STR_ABOUT_MENU_SEPARATOR : +############ range ends here + +############ range for ordinal numbers used for the place in the highscore window +############ range for ordinal numbers ends + +############ range for days starts +############ range for days ends + +############ range for months starts + +############ range for months ends + +# Graph window +STR_GRAPH_X_LABEL_MONTH :{TINY_FONT}{STRING} +STR_GRAPH_Y_LABEL :{TINY_FONT}{STRING} +STR_GRAPH_Y_LABEL_NUMBER :{TINY_FONT}{COMMA} + + +STR_GRAPH_CARGO_PAYMENT_CARGO :{TINY_FONT}{BLACK}{STRING} + + +# Graph key window + +# Company league window + +# Performance detail window +############ Those following lines need to be in this order!! +############ End of order list + +# Music window +STR_MUSIC_TRACK_DIGIT :{TINY_FONT}{DKGREEN}{ZEROFILL_NUM} + +# Playlist window + +# Highscore window + +# Smallmap window + + + +STR_SMALLMAP_LINKSTATS :{TINY_FONT}{STRING} +STR_SMALLMAP_COMPANY :{TINY_FONT}{COMPANY} +STR_SMALLMAP_TOWN :{TINY_FONT}{WHITE}{TOWN} + +# Status bar messages + +# News message history + +STR_NEWS_CUSTOM_ITEM :{BIG_FONT}{BLACK}{STRING} + + + + + + + + + + + +# Order review system / warnings + + + +STR_NEWS_NEW_VEHICLE_TYPE :{BIG_FONT}{BLACK}{ENGINE} + + + + + +# Extra view window + +# Game options window + +############ start of currency region +############ end of currency region + + + +############ start of townname region +############ end of townname region + + +############ start of autosave dropdown +############ end of autosave dropdown + + + + + + + + + + + + + + + + +# Custom currency window + + + + + + + + + + + + + + + + + +# Settings tree window + + + + + + + + +STR_CONFIG_SETTING_MAP_HEIGHT_LIMIT_VALUE :{NUM} + + + + + + + + + + + + + + + + + + + +STR_CONFIG_SETTING_ENDING_YEAR_VALUE :{NUM} + + + +STR_CONFIG_SETTING_SOFT_LIMIT_VALUE :{COMMA} + + + + + + + + + + + + +# Config errors + +# Video initalization errors + +# Intro window + + + + + + + +# Quit window + +# Abandon game + +# Cheat window + +# Livery window + + + +# Face selection window + + +# Network server list + + + + + + + +# Start new multiplayer server + + + + +# Network game lobby + + + + + +# Network connecting window + +############ Leave those lines in this order!! + +############ End of leave-in-this-order + + + +# Network company list added strings + +# Network client list + + +# Network set password + +# Network company info join/password + +# Network chat + + +# Network messages + +############ Leave those lines in this order!! +############ End of leave-in-this-order + + +# Network related errors +############ Leave those lines in this order!! +############ End of leave-in-this-order + +# Content downloading window + +# Order of these is important! + +# Content downloading progress window + +# Content downloading error messages + + + +# Transparency settings window + +# Linkgraph legend window + +# Linkgraph legend window and linkgraph legend in smallmap + +# Base for station construction window(s) + +# Join station window + + +# Generic toolbar + +# Rail construction toolbar + + + +# Rail depot construction window + +# Rail waypoint construction window + +# Rail station construction window + + + +# Signal window + +# Bridge selection window + + +# Road construction toolbar + + +# Road depot construction window + +# Road vehicle station construction window + +# Waterways toolbar (last two for SE only) + +# Ship depot construction window + +# Dock construction window + +# Airport toolbar + +# Airport construction window + + + + +# Landscaping toolbar + +# Object construction window + + +# Tree planting window (last eight for SE only) + +# Land generation window (SE) + + +# Town generation window (SE) + + + + +# Fund new industry window + +# Industry cargoes window + +# Land area window + +# Description of land area of different tiles + + + +# Houses come directly from their building names + + + + +# Industries come directly from their industry names + + + + + + +# About OpenTTD window + +# Framerate display window +STR_FRAMERATE_BYTES_GOOD :{LTBLUE}{BYTES} +STR_FRAMERATE_BYTES_WARN :{YELLOW}{BYTES} +STR_FRAMERATE_BYTES_BAD :{RED}{BYTES} +############ Leave those lines in this order!! +############ End of leave-in-this-order +############ Leave those lines in this order!! +############ End of leave-in-this-order + + +# Save/load game/scenario + + +# World generation + +# Strings for map borders at game generation + + + +# SE Map generation + + +# Map generation progress + +# NewGRF settings + + + + + +# NewGRF save preset window + +# NewGRF parameters window + +# NewGRF inspect window + + + +# Sprite aligner window + + +# NewGRF (self) generated warnings/errors +STR_NEWGRF_ERROR_MSG_INFO :{SILVER}{STRING} + +# NewGRF related 'general' warnings + + + +# NewGRF status + +# NewGRF 'it's broken' warnings + + +# 'User removed essential NewGRFs'-placeholders for stuff without specs + +# Placeholders for other invalid stuff, e.g. vehicles that have gone (Game Script). + +# NewGRF scanning window + +# Sign list window + +# Sign window + + +# Town directory window + +# Town view window +STR_TOWN_VIEW_TOWN_CAPTION :{WHITE}{TOWN} + + + +# Town local authority window + + + +# Goal window +STR_GOALS_TEXT :{ORANGE}{STRING} +STR_GOALS_PROGRESS :{ORANGE}{STRING} +STR_GOALS_PROGRESS_COMPLETE :{GREEN}{STRING} + +# Goal question window + +############ Start of Goal Question button list +############ End of Goal Question button list + +# Subsidies window + +# Story book window +STR_STORY_BOOK_TITLE :{YELLOW}{STRING} + +# Station list window +STR_STATION_LIST_STATION :{YELLOW}{STATION} {STATION_FEATURES} +STR_STATION_LIST_WAYPOINT :{YELLOW}{WAYPOINT} + +# Station view window +STR_STATION_VIEW_CAPTION :{WHITE}{STATION} {STATION_FEATURES} +STR_STATION_VIEW_WAITING_CARGO :{WHITE}{CARGO_LONG} + + + + + + +############ range for rating starts +############ range for rating ends + + + + + +# Waypoint/buoy view window +STR_WAYPOINT_VIEW_CAPTION :{WHITE}{WAYPOINT} + + +# Finances window +STR_FINANCES_YEAR :{WHITE}{NUM} +STR_FINANCES_TOTAL_CURRENCY :{BLACK}{CURRENCY_LONG} + +# Company view +STR_COMPANY_VIEW_CAPTION :{WHITE}{COMPANY} {BLACK}{COMPANY_NUM} + + + + + + + +# Company infrastructure window + +# Industry directory +STR_INDUSTRY_DIRECTORY_ITEM_NOPROD :{ORANGE}{INDUSTRY} +STR_INDUSTRY_DIRECTORY_ITEM_PROD1 :{ORANGE}{INDUSTRY} {STRING} + +# Industry view +STR_INDUSTRY_VIEW_CAPTION :{WHITE}{INDUSTRY} + + +STR_INDUSTRY_VIEW_ACCEPT_CARGO :{YELLOW}{STRING}{BLACK}{3:STRING} + + +# Vehicle lists + + + + + + + + +# Group window + + + + + + + + +# Build vehicle window + + +############ range for vehicle availability starts +############ range for vehicle availability ends + + + + + + + + + + + + + +# Depot window +STR_DEPOT_CAPTION :{WHITE}{DEPOT} + + +STR_DEPOT_VEHICLE_TOOLTIP :{BLACK}{ENGINE}{STRING} + + + + + + + + + + + + + + + +# Engine preview window + + + + + +# Autoreplace window + + + + + + + + +# Vehicle view +STR_VEHICLE_VIEW_CAPTION :{WHITE}{VEHICLE} + + + + + + + + + + + +# Messages in the start stop button in the vehicle view + + +# Vehicle stopped/started animations + +# Vehicle details + + +# The next two need to stay in this order + + + + + + + + +# Extra buttons for train details windows + + + + + +# Vehicle refit + + + + +# Order view + +STR_ORDER_TEXT :{STRING} {STRING} {STRING} + + +# Order bottom buttons + + + + + + +# Conditional order variables, must follow order of OrderConditionVariable enum + + + + + + + +# String parts to build the order string + + +STR_ORDER_GO_TO_NEAREST_DEPOT_FORMAT :{STRING} {STRING} {STRING} +STR_ORDER_GO_TO_DEPOT_FORMAT :{STRING} {DEPOT} + + +STR_ORDER_GO_TO_STATION :{STRING} {STATION} {STRING} + + + + + + + + + +# Time table window + + + + + + + + + + + + + + + +# Date window (for timetable) + + +# AI debug window + + +# AI configuration window + + + +STR_AI_CONFIG_CHANGE_NONE : + +# Available AIs window + + + + +# AI Parameters + + +# Textfile window + + +# Vehicle loading indicators + +# Income 'floats' + +# Saveload messages + +# Map generation messages + + + + + +# Soundset messages + +# Screenshot related messages + + +# Error message titles + +# Generic construction errors + +# Local authority errors + +# Levelling errors + +# Company related errors + + +# Town related errors + +# Industry related errors + + +# Station construction related errors + + +# Station destruction related errors + + +# Waypoint related errors + + + +# Depot related errors + + + + + + +# Autoreplace related errors + +# Rail construction errors + + +# Road construction errors + +# Waterway construction errors + +# Tree related errors + +# Bridge related errors + +# Tunnel related errors + +# Object related errors + +# Group related errors + +# Generic vehicle errors + + + + + + + + + + + + +# Specific vehicle errors + + + +# Order related errors + + +# Timetable related errors + +# Sign related errors + +# Translatable comment for OpenTTD's desktop shortcut + +# Translatable descriptions in media/baseset/*.ob* files + +##id 0x2000 +# Town building names + +##id 0x4800 +# industry names + +############ WARNING, using range 0x6000 for strings that are stored in the savegame +############ These strings may never get a new id, or savegames will break! +##id 0x6000 +STR_SV_EMPTY : + +STR_SV_STNAME :{STRING} +STR_SV_STNAME_BUOY :{STRING} +STR_SV_STNAME_WAYPOINT :{STRING} +##id 0x6020 +############ end of savegame specific region! + +##id 0x8000 +# Vehicle names + +##id 0x8800 +# Formatting of some strings +STR_FORMAT_DATE_SHORT :{STRING} {NUM} +STR_FORMAT_DATE_LONG :{STRING} {STRING} {NUM} + +STR_FORMAT_INDUSTRY_NAME :{TOWN} {STRING} + + + + +# Viewport strings +STR_VIEWPORT_TOWN :{WHITE}{TOWN} +STR_VIEWPORT_TOWN_TINY_BLACK :{TINY_FONT}{BLACK}{TOWN} +STR_VIEWPORT_TOWN_TINY_WHITE :{TINY_FONT}{WHITE}{TOWN} + +STR_VIEWPORT_SIGN_SMALL_BLACK :{TINY_FONT}{BLACK}{SIGN} +STR_VIEWPORT_SIGN_SMALL_WHITE :{TINY_FONT}{WHITE}{SIGN} + +STR_VIEWPORT_STATION :{STATION} {STATION_FEATURES} +STR_VIEWPORT_STATION_TINY :{TINY_FONT}{STATION} + +STR_VIEWPORT_WAYPOINT :{WAYPOINT} +STR_VIEWPORT_WAYPOINT_TINY :{TINY_FONT}{WAYPOINT} + +# Simple strings to get specific types of data +STR_COMPANY_NAME :{COMPANY} +STR_COMPANY_NAME_COMPANY_NUM :{COMPANY} {COMPANY_NUM} +STR_DEPOT_NAME :{DEPOT} +STR_ENGINE_NAME :{ENGINE} +STR_GROUP_NAME :{GROUP} +STR_INDUSTRY_NAME :{INDUSTRY} +STR_PRESIDENT_NAME :{PRESIDENT_NAME} +STR_SIGN_NAME :{SIGN} +STR_STATION_NAME :{STATION} +STR_TOWN_NAME :{TOWN} +STR_VEHICLE_NAME :{VEHICLE} +STR_WAYPOINT_NAME :{WAYPOINT} + +STR_JUST_CARGO :{CARGO_LONG} +STR_JUST_CHECKMARK :{CHECKMARK} +STR_JUST_COMMA :{COMMA} +STR_JUST_CURRENCY_SHORT :{CURRENCY_SHORT} +STR_JUST_CURRENCY_LONG :{CURRENCY_LONG} +STR_JUST_CARGO_LIST :{CARGO_LIST} +STR_JUST_INT :{NUM} +STR_JUST_DATE_TINY :{DATE_TINY} +STR_JUST_DATE_SHORT :{DATE_SHORT} +STR_JUST_DATE_LONG :{DATE_LONG} +STR_JUST_DATE_ISO :{DATE_ISO} +STR_JUST_STRING :{STRING} +STR_JUST_STRING_STRING :{STRING}{STRING} +STR_JUST_RAW_STRING :{STRING} +STR_JUST_BIG_RAW_STRING :{BIG_FONT}{STRING} + +# Slightly 'raw' stringcodes with colour or size +STR_BLACK_COMMA :{BLACK}{COMMA} +STR_TINY_BLACK_COMA :{TINY_FONT}{BLACK}{COMMA} +STR_TINY_COMMA :{TINY_FONT}{COMMA} +STR_BLUE_COMMA :{BLUE}{COMMA} +STR_RED_COMMA :{RED}{COMMA} +STR_WHITE_COMMA :{WHITE}{COMMA} +STR_TINY_BLACK_DECIMAL :{TINY_FONT}{BLACK}{DECIMAL} +STR_COMPANY_MONEY :{WHITE}{CURRENCY_LONG} +STR_BLACK_DATE_LONG :{BLACK}{DATE_LONG} +STR_WHITE_DATE_LONG :{WHITE}{DATE_LONG} +STR_SHORT_DATE :{WHITE}{DATE_TINY} +STR_DATE_LONG_SMALL :{TINY_FONT}{BLACK}{DATE_LONG} +STR_TINY_GROUP :{TINY_FONT}{GROUP} +STR_BLACK_INT :{BLACK}{NUM} +STR_ORANGE_INT :{ORANGE}{NUM} +STR_WHITE_SIGN :{WHITE}{SIGN} +STR_TINY_BLACK_STATION :{TINY_FONT}{BLACK}{STATION} +STR_BLACK_STRING :{BLACK}{STRING} +STR_BLACK_RAW_STRING :{BLACK}{STRING} +STR_ORANGE_STRING :{ORANGE}{STRING} +STR_LTBLUE_STRING :{LTBLUE}{STRING} +STR_WHITE_STRING :{WHITE}{STRING} +STR_ORANGE_STRING1_WHITE :{ORANGE}{STRING}{WHITE} +STR_ORANGE_STRING1_LTBLUE :{ORANGE}{STRING}{LTBLUE} +STR_TINY_BLACK_HEIGHT :{TINY_FONT}{BLACK}{HEIGHT} +STR_TINY_BLACK_VEHICLE :{TINY_FONT}{BLACK}{VEHICLE} +STR_TINY_RIGHT_ARROW :{TINY_FONT}{RIGHT_ARROW} + + +STR_TRAIN :{BLACK}{TRAIN} +STR_BUS :{BLACK}{BUS} +STR_LORRY :{BLACK}{LORRY} +STR_PLANE :{BLACK}{PLANE} +STR_SHIP :{BLACK}{SHIP} + From dc0efd5f2efac29e40264881553f779e206d0cc4 Mon Sep 17 00:00:00 2001 From: translators Date: Fri, 23 Apr 2021 22:11:02 +0000 Subject: [PATCH 125/268] Update: Translations from eints spanish (mexican): 1 change by absay english (us): 1 change by 2TallTyler --- src/lang/english_US.txt | 1 + src/lang/spanish_MX.txt | 1 + 2 files changed, 2 insertions(+) diff --git a/src/lang/english_US.txt b/src/lang/english_US.txt index 530813d62b..9573e2f9dd 100644 --- a/src/lang/english_US.txt +++ b/src/lang/english_US.txt @@ -993,6 +993,7 @@ STR_GAME_OPTIONS_AUTOSAVE_DROPDOWN_EVERY_12_MONTHS :Every 12 months STR_GAME_OPTIONS_LANGUAGE :{BLACK}Language STR_GAME_OPTIONS_LANGUAGE_TOOLTIP :{BLACK}Select the interface language to use +STR_GAME_OPTIONS_LANGUAGE_PERCENTAGE :{STRING} ({NUM}% completed) STR_GAME_OPTIONS_FULLSCREEN :{BLACK}Fullscreen STR_GAME_OPTIONS_FULLSCREEN_TOOLTIP :{BLACK}Check this box to play OpenTTD fullscreen mode diff --git a/src/lang/spanish_MX.txt b/src/lang/spanish_MX.txt index dff43e4eab..0c81885e17 100644 --- a/src/lang/spanish_MX.txt +++ b/src/lang/spanish_MX.txt @@ -994,6 +994,7 @@ STR_GAME_OPTIONS_AUTOSAVE_DROPDOWN_EVERY_12_MONTHS :Cada 12 meses STR_GAME_OPTIONS_LANGUAGE :{BLACK}Idioma STR_GAME_OPTIONS_LANGUAGE_TOOLTIP :{BLACK}Elegir el idioma de la interfaz +STR_GAME_OPTIONS_LANGUAGE_PERCENTAGE :{STRING} ({NUM}% completado) STR_GAME_OPTIONS_FULLSCREEN :{BLACK}Pantalla completa STR_GAME_OPTIONS_FULLSCREEN_TOOLTIP :{BLACK}Jugar OpenTTD en pantalla completa From b14f4121170cb75aba785efc9e73a364c232301b Mon Sep 17 00:00:00 2001 From: rubidium42 Date: Thu, 22 Apr 2021 07:01:46 +0200 Subject: [PATCH 126/268] Codechange: [Network] Introduce function to validate the client name --- src/network/network_client.cpp | 14 ++++++++++++++ src/network/network_func.h | 1 + src/network/network_gui.cpp | 3 +-- src/network/network_server.cpp | 2 +- 4 files changed, 17 insertions(+), 3 deletions(-) diff --git a/src/network/network_client.cpp b/src/network/network_client.cpp index 5858859cb9..a12215bbc9 100644 --- a/src/network/network_client.cpp +++ b/src/network/network_client.cpp @@ -1250,6 +1250,20 @@ void NetworkClientsToSpectators(CompanyID cid) cur_company.Restore(); } +/** + * Check whether the given client name is deemed valid for use in network games. + * An empty name (null or '') is not valid as that is essentially no name at all. + * A name starting with white space is not valid for tab completion purposes. + * @param client_name The client name to check for validity. + * @return True iff the name is valid. + */ +bool NetworkIsValidClientName(const char *client_name) +{ + if (StrEmpty(client_name)) return false; + if (*client_name == ' ') return false; + return true; +} + /** * Send the server our name. */ diff --git a/src/network/network_func.h b/src/network/network_func.h index d14dd290f0..a7f2e7cb5b 100644 --- a/src/network/network_func.h +++ b/src/network/network_func.h @@ -36,6 +36,7 @@ extern StringList _network_host_list; extern StringList _network_ban_list; byte NetworkSpectatorCount(); +bool NetworkIsValidClientName(const char *client_name); void NetworkUpdateClientName(); bool NetworkCompanyHasClients(CompanyID company); const char *NetworkChangeCompanyPassword(CompanyID company_id, const char *password); diff --git a/src/network/network_gui.cpp b/src/network/network_gui.cpp index 1c80f7f3ce..15cf24f496 100644 --- a/src/network/network_gui.cpp +++ b/src/network/network_gui.cpp @@ -808,8 +808,7 @@ public: } case WID_NG_CLIENT: - /* Make sure the name does not start with a space, so TAB completion works */ - if (!StrEmpty(this->name_editbox.text.buf) && this->name_editbox.text.buf[0] != ' ') { + if (NetworkIsValidClientName(this->name_editbox.text.buf)) { strecpy(_settings_client.network.client_name, this->name_editbox.text.buf, lastof(_settings_client.network.client_name)); } else { strecpy(_settings_client.network.client_name, "Player", lastof(_settings_client.network.client_name)); diff --git a/src/network/network_server.cpp b/src/network/network_server.cpp index 5521710271..321c8aa096 100644 --- a/src/network/network_server.cpp +++ b/src/network/network_server.cpp @@ -946,7 +946,7 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::Receive_CLIENT_JOIN(Packet *p) } /* We need a valid name.. make it Player */ - if (StrEmpty(name)) strecpy(name, "Player", lastof(name)); + if (!NetworkIsValidClientName(name)) strecpy(name, "Player", lastof(name)); if (!NetworkFindName(name, lastof(name))) { // Change name if duplicate /* We could not create a name for this client */ From 5202869f0fec5512211988b35b1ee4b29a33686d Mon Sep 17 00:00:00 2001 From: rubidium42 Date: Fri, 23 Apr 2021 07:46:50 +0200 Subject: [PATCH 127/268] Add: String functionality to trim spaces from C-style strings --- src/string.cpp | 60 +++++++++++++++++++++++++++++++++++++++++++++++ src/string_func.h | 1 + 2 files changed, 61 insertions(+) diff --git a/src/string.cpp b/src/string.cpp index adc757b871..dfd01450e0 100644 --- a/src/string.cpp +++ b/src/string.cpp @@ -302,6 +302,66 @@ bool StrValid(const char *str, const char *last) return *str == '\0'; } +/** + * Trim the spaces from the begin of given string in place, i.e. the string buffer + * that is passed will be modified whenever spaces exist in the given string. + * When there are spaces at the begin, the whole string is moved forward. + * @param str The string to perform the in place left trimming on. + */ +static void StrLeftTrimInPlace(char *str) +{ + if (StrEmpty(str)) return; + + char *first_non_space = str; + while (*first_non_space == ' ') first_non_space++; + + if (first_non_space == str) return; + + /* The source will reach '\0' first, but set the '\0' on the destination afterwards. */ + char *dst = str; + for (char *src = first_non_space; *src != '\0'; dst++, src++) *dst = *src; + *dst = '\0'; +} + +/** + * Trim the spaces from the end of given string in place, i.e. the string buffer + * that is passed will be modified whenever spaces exist in the given string. + * When there are spaces at the end, the '\0' will be moved forward. + * @param str The string to perform the in place left trimming on. + */ +static void StrRightTrimInPlace(char *str) +{ + if (StrEmpty(str)) return; + + char *end = str; + while (*end != '\0') end++; + + char *last_non_space = end - 1; + while (last_non_space >= str && *last_non_space == ' ') last_non_space--; + + /* The last non space points to the last character of the string that is not + * a space. For a string with only spaces or an empty string this would be + * the position before the begin of the string. The previous search ensures + * that this location before the string is not read. + * In any case, the character after the last non space character will be + * either a space or the existing termination, so it can be set to '\0'. + */ + last_non_space[1] = '\0'; +} + +/** + * Trim the spaces from given string in place, i.e. the string buffer that + * is passed will be modified whenever spaces exist in the given string. + * When there are spaces at the begin, the whole string is moved forward + * and when there are spaces at the back the '\0' termination is moved. + * @param str The string to perform the in place trimming on. + */ +void StrTrimInPlace(char *str) +{ + StrLeftTrimInPlace(str); + StrRightTrimInPlace(str); +} + /** Scans the string for colour codes and strips them */ void str_strip_colours(char *str) { diff --git a/src/string_func.h b/src/string_func.h index 13e14f2d39..6c894b3cba 100644 --- a/src/string_func.h +++ b/src/string_func.h @@ -49,6 +49,7 @@ bool strtolower(char *str); bool strtolower(std::string &str, std::string::size_type offs = 0); bool StrValid(const char *str, const char *last); +void StrTrimInPlace(char *str); /** * Check if a string buffer is empty. From bfb0ab3e2f5a5a09b4bf7f5584199425ffb1de50 Mon Sep 17 00:00:00 2001 From: rubidium42 Date: Thu, 22 Apr 2021 08:01:52 +0200 Subject: [PATCH 128/268] Feature: [Network] Ensure players fill in a name instead of defaulting to "Player" --- src/lang/english.txt | 1 + src/network/network.cpp | 2 ++ src/network/network_client.cpp | 36 ++++++++++++++++++++++++++++++++++ src/network/network_func.h | 2 ++ src/network/network_gui.cpp | 12 +++++++----- 5 files changed, 48 insertions(+), 5 deletions(-) diff --git a/src/lang/english.txt b/src/lang/english.txt index 8eb7616bc0..7ba1768e2a 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -2175,6 +2175,7 @@ STR_NETWORK_ERROR_SERVER_START :{WHITE}Could no STR_NETWORK_ERROR_CLIENT_START :{WHITE}Could not connect STR_NETWORK_ERROR_TIMEOUT :{WHITE}Connection #{NUM} timed out STR_NETWORK_ERROR_SERVER_ERROR :{WHITE}A protocol error was detected and the connection was closed +STR_NETWORK_ERROR_BAD_PLAYER_NAME :{WHITE}Your player name has not been set. The name can be set at the top of the Multiplayer window STR_NETWORK_ERROR_WRONG_REVISION :{WHITE}The revision of this client does not match the server's revision STR_NETWORK_ERROR_WRONG_PASSWORD :{WHITE}Wrong password STR_NETWORK_ERROR_SERVER_FULL :{WHITE}The server is full diff --git a/src/network/network.cpp b/src/network/network.cpp index e0355ce2ea..7c3a9ef72c 100644 --- a/src/network/network.cpp +++ b/src/network/network.cpp @@ -692,6 +692,8 @@ void NetworkClientConnectGame(NetworkAddress address, CompanyID join_as, const c if (address.GetPort() == 0) return; + if (!NetworkValidateClientName()) return; + strecpy(_settings_client.network.last_host, address.GetHostname(), lastof(_settings_client.network.last_host)); _settings_client.network.last_port = address.GetPort(); _network_join_as = join_as; diff --git a/src/network/network_client.cpp b/src/network/network_client.cpp index a12215bbc9..35c52d3bcb 100644 --- a/src/network/network_client.cpp +++ b/src/network/network_client.cpp @@ -1264,6 +1264,42 @@ bool NetworkIsValidClientName(const char *client_name) return true; } +/** + * Trim the given client name in place, i.e. remove leading and trailing spaces. + * After the trim check whether the client name is valid. A client name is valid + * whenever the name is not empty and does not start with spaces. This check is + * done via \c NetworkIsValidClientName. + * When the client name is valid, this function returns true. + * When the client name is not valid a GUI error message is shown telling the + * user to set the client name and this function returns false. + * + * This function is not suitable for ensuring a valid client name at the server + * as the error message will then be shown to the host instead of the client. + * @param client_name The client name to validate. It will be trimmed of leading + * and trailing spaces. + * @return True iff the client name is valid. + */ +bool NetworkValidateClientName(char *client_name) +{ + StrTrimInPlace(client_name); + if (NetworkIsValidClientName(client_name)) return true; + + ShowErrorMessage(STR_NETWORK_ERROR_BAD_PLAYER_NAME, INVALID_STRING_ID, WL_ERROR); + return false; +} + +/** + * Convenience method for NetworkValidateClientName on _settings_client.network.client_name. + * It trims the client name and checks whether it is empty. When it is empty + * an error message is shown to the GUI user. + * See \c NetworkValidateClientName(char*) for details about the functionality. + * @return True iff the client name is valid. + */ +bool NetworkValidateClientName() +{ + return NetworkValidateClientName(_settings_client.network.client_name); +} + /** * Send the server our name. */ diff --git a/src/network/network_func.h b/src/network/network_func.h index a7f2e7cb5b..c1271f69c6 100644 --- a/src/network/network_func.h +++ b/src/network/network_func.h @@ -37,6 +37,8 @@ extern StringList _network_ban_list; byte NetworkSpectatorCount(); bool NetworkIsValidClientName(const char *client_name); +bool NetworkValidateClientName(); +bool NetworkValidateClientName(char *client_name); void NetworkUpdateClientName(); bool NetworkCompanyHasClients(CompanyID company); const char *NetworkChangeCompanyPassword(CompanyID company_id, const char *password); diff --git a/src/network/network_gui.cpp b/src/network/network_gui.cpp index 15cf24f496..dd25ad5bc1 100644 --- a/src/network/network_gui.cpp +++ b/src/network/network_gui.cpp @@ -808,11 +808,9 @@ public: } case WID_NG_CLIENT: - if (NetworkIsValidClientName(this->name_editbox.text.buf)) { - strecpy(_settings_client.network.client_name, this->name_editbox.text.buf, lastof(_settings_client.network.client_name)); - } else { - strecpy(_settings_client.network.client_name, "Player", lastof(_settings_client.network.client_name)); - } + /* Validation of the name will happen once the user tries to join or start a game, as getting + * error messages while typing (e.g. when you clear the name) defeats the purpose of the check. */ + strecpy(_settings_client.network.client_name, this->name_editbox.text.buf, lastof(_settings_client.network.client_name)); break; } } @@ -1245,6 +1243,8 @@ static WindowDesc _network_start_server_window_desc( static void ShowNetworkStartServerWindow() { + if (!NetworkValidateClientName()) return; + DeleteWindowById(WC_NETWORK_WINDOW, WN_NETWORK_WINDOW_GAME); DeleteWindowById(WC_NETWORK_WINDOW, WN_NETWORK_WINDOW_LOBBY); @@ -1537,6 +1537,8 @@ static WindowDesc _network_lobby_window_desc( */ static void ShowNetworkLobbyWindow(NetworkGameList *ngl) { + if (!NetworkValidateClientName()) return; + DeleteWindowById(WC_NETWORK_WINDOW, WN_NETWORK_WINDOW_START); DeleteWindowById(WC_NETWORK_WINDOW, WN_NETWORK_WINDOW_GAME); From 2e0f3799a809086054f6d5ac4120283b5bb6fa3b Mon Sep 17 00:00:00 2001 From: rubidium42 Date: Thu, 22 Apr 2021 08:09:36 +0200 Subject: [PATCH 129/268] Change: [Network] Prevent invalid client names being sent to the server when changing it using the console/settings --- src/console_cmds.cpp | 9 ++++++++- src/network/network_client.cpp | 5 +++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/console_cmds.cpp b/src/console_cmds.cpp index b3819115cf..305444f8cd 100644 --- a/src/console_cmds.cpp +++ b/src/console_cmds.cpp @@ -716,7 +716,14 @@ DEF_CONSOLE_CMD(ConClientNickChange) return true; } - if (!NetworkServerChangeClientName(client_id, argv[2])) { + char *client_name = argv[2]; + StrTrimInPlace(client_name); + if (!NetworkIsValidClientName(client_name)) { + IConsoleError("Cannot give a client an empty name"); + return true; + } + + if (!NetworkServerChangeClientName(client_id, client_name)) { IConsoleError("Cannot give a client a duplicate name"); } diff --git a/src/network/network_client.cpp b/src/network/network_client.cpp index 35c52d3bcb..a80931c33b 100644 --- a/src/network/network_client.cpp +++ b/src/network/network_client.cpp @@ -1308,6 +1308,11 @@ void NetworkUpdateClientName() NetworkClientInfo *ci = NetworkClientInfo::GetByClientID(_network_own_client_id); if (ci == nullptr) return; + /* There is no validation on string settings, it is actually a post change callback. + * This method is called from that post change callback. So, when the client name is + * changed via the console there is no easy way to prevent an invalid name. Though, + * we can prevent it getting sent here. */ + if (!NetworkValidateClientName()) return; /* Don't change the name if it is the same as the old name */ if (strcmp(ci->client_name, _settings_client.network.client_name) != 0) { From e1cebe0ea0719ad408a0a7494efe1d8581b702ce Mon Sep 17 00:00:00 2001 From: rubidium42 Date: Thu, 22 Apr 2021 08:17:36 +0200 Subject: [PATCH 130/268] Add: [Network] Validate the client name server side, so no clients with invalid names can actually join --- src/lang/english.txt | 2 ++ src/network/network.cpp | 1 + src/network/network_client.cpp | 41 +++++++++++++++++----------------- src/network/network_server.cpp | 15 +++++++++++-- src/network/network_type.h | 1 + 5 files changed, 38 insertions(+), 22 deletions(-) diff --git a/src/lang/english.txt b/src/lang/english.txt index 7ba1768e2a..ff12f122af 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -2188,6 +2188,7 @@ STR_NETWORK_ERROR_TIMEOUT_PASSWORD :{WHITE}You took STR_NETWORK_ERROR_TIMEOUT_COMPUTER :{WHITE}Your computer is too slow to keep up with the server 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 ############ Leave those lines in this order!! STR_NETWORK_ERROR_CLIENT_GENERAL :general error @@ -2210,6 +2211,7 @@ STR_NETWORK_ERROR_CLIENT_TIMEOUT_PASSWORD :received no pas STR_NETWORK_ERROR_CLIENT_TIMEOUT_COMPUTER :general timeout STR_NETWORK_ERROR_CLIENT_TIMEOUT_MAP :downloading map took too long STR_NETWORK_ERROR_CLIENT_TIMEOUT_JOIN :processing map took too long +STR_NETWORK_ERROR_CLIENT_INVALID_CLIENT_NAME :invalid client name ############ End of leave-in-this-order STR_NETWORK_ERROR_CLIENT_GUI_LOST_CONNECTION_CAPTION :{WHITE}Possible connection loss diff --git a/src/network/network.cpp b/src/network/network.cpp index 7c3a9ef72c..97eadb9768 100644 --- a/src/network/network.cpp +++ b/src/network/network.cpp @@ -314,6 +314,7 @@ StringID GetNetworkErrorMsg(NetworkErrorCode err) STR_NETWORK_ERROR_CLIENT_TIMEOUT_COMPUTER, STR_NETWORK_ERROR_CLIENT_TIMEOUT_MAP, STR_NETWORK_ERROR_CLIENT_TIMEOUT_JOIN, + STR_NETWORK_ERROR_CLIENT_INVALID_CLIENT_NAME, }; static_assert(lengthof(network_error_strings) == NETWORK_ERROR_END); diff --git a/src/network/network_client.cpp b/src/network/network_client.cpp index a80931c33b..b234880d73 100644 --- a/src/network/network_client.cpp +++ b/src/network/network_client.cpp @@ -668,26 +668,27 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_CLIENT_INFO(Pac NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_ERROR(Packet *p) { static const StringID network_error_strings[] = { - STR_NETWORK_ERROR_LOSTCONNECTION, // NETWORK_ERROR_GENERAL - STR_NETWORK_ERROR_LOSTCONNECTION, // NETWORK_ERROR_DESYNC - STR_NETWORK_ERROR_LOSTCONNECTION, // NETWORK_ERROR_SAVEGAME_FAILED - STR_NETWORK_ERROR_LOSTCONNECTION, // NETWORK_ERROR_CONNECTION_LOST - STR_NETWORK_ERROR_LOSTCONNECTION, // NETWORK_ERROR_ILLEGAL_PACKET - STR_NETWORK_ERROR_LOSTCONNECTION, // NETWORK_ERROR_NEWGRF_MISMATCH - STR_NETWORK_ERROR_SERVER_ERROR, // NETWORK_ERROR_NOT_AUTHORIZED - STR_NETWORK_ERROR_SERVER_ERROR, // NETWORK_ERROR_NOT_EXPECTED - STR_NETWORK_ERROR_WRONG_REVISION, // NETWORK_ERROR_WRONG_REVISION - STR_NETWORK_ERROR_LOSTCONNECTION, // NETWORK_ERROR_NAME_IN_USE - STR_NETWORK_ERROR_WRONG_PASSWORD, // NETWORK_ERROR_WRONG_PASSWORD - STR_NETWORK_ERROR_SERVER_ERROR, // NETWORK_ERROR_COMPANY_MISMATCH - STR_NETWORK_ERROR_KICKED, // NETWORK_ERROR_KICKED - STR_NETWORK_ERROR_CHEATER, // NETWORK_ERROR_CHEATER - STR_NETWORK_ERROR_SERVER_FULL, // NETWORK_ERROR_FULL - STR_NETWORK_ERROR_TOO_MANY_COMMANDS, // NETWORK_ERROR_TOO_MANY_COMMANDS - STR_NETWORK_ERROR_TIMEOUT_PASSWORD, // NETWORK_ERROR_TIMEOUT_PASSWORD - STR_NETWORK_ERROR_TIMEOUT_COMPUTER, // NETWORK_ERROR_TIMEOUT_COMPUTER - STR_NETWORK_ERROR_TIMEOUT_MAP, // NETWORK_ERROR_TIMEOUT_MAP - STR_NETWORK_ERROR_TIMEOUT_JOIN, // NETWORK_ERROR_TIMEOUT_JOIN + STR_NETWORK_ERROR_LOSTCONNECTION, // NETWORK_ERROR_GENERAL + STR_NETWORK_ERROR_LOSTCONNECTION, // NETWORK_ERROR_DESYNC + STR_NETWORK_ERROR_LOSTCONNECTION, // NETWORK_ERROR_SAVEGAME_FAILED + STR_NETWORK_ERROR_LOSTCONNECTION, // NETWORK_ERROR_CONNECTION_LOST + STR_NETWORK_ERROR_LOSTCONNECTION, // NETWORK_ERROR_ILLEGAL_PACKET + STR_NETWORK_ERROR_LOSTCONNECTION, // NETWORK_ERROR_NEWGRF_MISMATCH + STR_NETWORK_ERROR_SERVER_ERROR, // NETWORK_ERROR_NOT_AUTHORIZED + STR_NETWORK_ERROR_SERVER_ERROR, // NETWORK_ERROR_NOT_EXPECTED + STR_NETWORK_ERROR_WRONG_REVISION, // NETWORK_ERROR_WRONG_REVISION + STR_NETWORK_ERROR_LOSTCONNECTION, // NETWORK_ERROR_NAME_IN_USE + STR_NETWORK_ERROR_WRONG_PASSWORD, // NETWORK_ERROR_WRONG_PASSWORD + STR_NETWORK_ERROR_SERVER_ERROR, // NETWORK_ERROR_COMPANY_MISMATCH + STR_NETWORK_ERROR_KICKED, // NETWORK_ERROR_KICKED + STR_NETWORK_ERROR_CHEATER, // NETWORK_ERROR_CHEATER + STR_NETWORK_ERROR_SERVER_FULL, // NETWORK_ERROR_FULL + STR_NETWORK_ERROR_TOO_MANY_COMMANDS, // NETWORK_ERROR_TOO_MANY_COMMANDS + STR_NETWORK_ERROR_TIMEOUT_PASSWORD, // NETWORK_ERROR_TIMEOUT_PASSWORD + STR_NETWORK_ERROR_TIMEOUT_COMPUTER, // NETWORK_ERROR_TIMEOUT_COMPUTER + STR_NETWORK_ERROR_TIMEOUT_MAP, // NETWORK_ERROR_TIMEOUT_MAP + STR_NETWORK_ERROR_TIMEOUT_JOIN, // NETWORK_ERROR_TIMEOUT_JOIN + STR_NETWORK_ERROR_INVALID_CLIENT_NAME, // NETWORK_ERROR_INVALID_CLIENT_NAME }; static_assert(lengthof(network_error_strings) == NETWORK_ERROR_END); diff --git a/src/network/network_server.cpp b/src/network/network_server.cpp index 321c8aa096..fbde713eb4 100644 --- a/src/network/network_server.cpp +++ b/src/network/network_server.cpp @@ -945,8 +945,12 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::Receive_CLIENT_JOIN(Packet *p) break; } - /* We need a valid name.. make it Player */ - if (!NetworkIsValidClientName(name)) strecpy(name, "Player", lastof(name)); + if (!NetworkIsValidClientName(name)) { + /* An invalid client name was given. However, the client ensures the name + * is valid before it is sent over the network, so something went horribly + * wrong. This is probably someone trying to troll us. */ + return this->SendError(NETWORK_ERROR_INVALID_CLIENT_NAME); + } if (!NetworkFindName(name, lastof(name))) { // Change name if duplicate /* We could not create a name for this client */ @@ -1441,6 +1445,13 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::Receive_CLIENT_SET_NAME(Packet if (this->HasClientQuit()) return NETWORK_RECV_STATUS_CONN_LOST; if (ci != nullptr) { + if (!NetworkIsValidClientName(client_name)) { + /* An invalid client name was given. However, the client ensures the name + * is valid before it is sent over the network, so something went horribly + * wrong. This is probably someone trying to troll us. */ + return this->SendError(NETWORK_ERROR_INVALID_CLIENT_NAME); + } + /* Display change */ if (NetworkFindName(client_name, lastof(client_name))) { NetworkTextMessage(NETWORK_ACTION_NAME_CHANGE, CC_DEFAULT, false, ci->client_name, client_name); diff --git a/src/network/network_type.h b/src/network/network_type.h index 5a77053469..4c75346da6 100644 --- a/src/network/network_type.h +++ b/src/network/network_type.h @@ -132,6 +132,7 @@ enum NetworkErrorCode { NETWORK_ERROR_TIMEOUT_COMPUTER, NETWORK_ERROR_TIMEOUT_MAP, NETWORK_ERROR_TIMEOUT_JOIN, + NETWORK_ERROR_INVALID_CLIENT_NAME, NETWORK_ERROR_END, }; From 2999d301adade21ddd253b5d765bb8468e39f224 Mon Sep 17 00:00:00 2001 From: rubidium42 Date: Fri, 23 Apr 2021 19:27:48 +0200 Subject: [PATCH 131/268] Add: [Network] Validate the client name when receiving one from the server This so names from other clients are known valid in the client as well, instead allowing some compromised/bad server to potentially crash clients upon certain expectations. --- src/network/network_client.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/network/network_client.cpp b/src/network/network_client.cpp index b234880d73..72f69f99f7 100644 --- a/src/network/network_client.cpp +++ b/src/network/network_client.cpp @@ -623,6 +623,10 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_CLIENT_INFO(Pac if (this->status < STATUS_AUTHORIZED) return NETWORK_RECV_STATUS_MALFORMED_PACKET; if (this->HasClientQuit()) return NETWORK_RECV_STATUS_CONN_LOST; + /* The server validates the name when receiving it from clients, so when it is wrong + * here something went really wrong. In the best case the packet got malformed on its + * way too us, in the worst case the server is broken or compromised. */ + if (!NetworkIsValidClientName(name)) return NETWORK_RECV_STATUS_MALFORMED_PACKET; ci = NetworkClientInfo::GetByClientID(client_id); if (ci != nullptr) { From d0e40ab31487cdf59c2fb638bcf553f166f8faf1 Mon Sep 17 00:00:00 2001 From: Andy <1780327+andythenorth@users.noreply.github.com> Date: Sat, 24 Apr 2021 12:19:13 +0100 Subject: [PATCH 132/268] Doc: provide a visual representation of the company colour ranges (#8546) Co-authored-by: rubidium42 --- docs/company_colour_indexes.html | 557 +++++++++++++++++++++++++++++++ 1 file changed, 557 insertions(+) create mode 100644 docs/company_colour_indexes.html diff --git a/docs/company_colour_indexes.html b/docs/company_colour_indexes.html new file mode 100644 index 0000000000..fb9dea7add --- /dev/null +++ b/docs/company_colour_indexes.html @@ -0,0 +1,557 @@ + + + + + OpenTTD Company Colour Indexes + + + + +

Company Colour Indexes

+

Hex / dec indexes into the DOS palette

+

+ Visual representation of values derived from https://github.com/frosch123/TTDViewer/blob/master/src/recolor.xml#L186 +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
COLOUR_DARK_BLUE
0xc60xc70xc80xc90xca0xcb0xcc0xcd
198199200201202203204205
COLOUR_PALE_GREEN
0x600x610x620x630x640x650x660x67
96979899100101102103
COLOUR_PINK
0x2a0x2b0x2c0x2d0x2e0x2f0x300x31
4243444546474849
COLOUR_YELLOW
0x3e0x3f0x400x410x420x430x440x45
6263646566676869
COLOUR_RED
0xb30xb40xb50xb60xb70xa40xa50xa6
179180181182183164165166
COLOUR_LIGHT_BLUE
0x9a0x9b0x9c0x9d0x9e0x9f0xa00xa1
154155156157158159160161
COLOUR_GREEN
0x520x530x540x550xce0xcf0xd00xd1
82838485206207208209
COLOUR_DARK_GREEN
0x580x590x5a0x5b0x5c0x5d0x5e0x5f
8889909192939495
COLOUR_BLUE
0x920x930x940x950x960x970x980x99
146147148149150151152153
COLOUR_CREAM
0x720x730x740x750x760x770x780x79
114115116117118119120121
COLOUR_MAUVE
0x800x810x820x830x840x850x860x87
128129130131132133134135
COLOUR_PURPLE
0x880x890x8a0x8b0x8c0x8d0x8e0x8f
136137138139140141142143
COLOUR_ORANGE
0x400xc00xc10xc20xc30xc40xc50x27
6419219319419519619739
COLOUR_BROWN
0x200x210x220x230x240x250x260x27
3233343536373839
COLOUR_GREY
0x40x50x60x70x80x90xa0xb
4567891011
COLOUR_WHITE
0x80x90xa0xb0xc0xd0xe0xf
89101112131415
+ + From 888389c28d5f3ad06154e4d2e36de568b6f7966e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Guilloux?= Date: Sat, 24 Apr 2021 15:19:57 +0200 Subject: [PATCH 133/268] Codechange: Use std::string in console commands/aliases registration, and std::map instead our sorted linked list (#9057) * Codechange: Use std::string in console commands and aliases registration * Codechange: Use std::map to register console commands * Codechange: Use std::map to register console aliases * Cleanup: Remove now unused function --- src/console.cpp | 116 ++++++------------- src/console_cmds.cpp | 256 ++++++++++++++++++++--------------------- src/console_internal.h | 36 +++--- src/music/midifile.cpp | 2 +- 4 files changed, 182 insertions(+), 228 deletions(-) diff --git a/src/console.cpp b/src/console.cpp index 3c782357d2..fbd34c1977 100644 --- a/src/console.cpp +++ b/src/console.cpp @@ -24,8 +24,17 @@ static const uint ICON_TOKEN_COUNT = 20; ///< Maximum number of tokens in on static const uint ICON_MAX_RECURSE = 10; ///< Maximum number of recursion /* console parser */ -IConsoleCmd *_iconsole_cmds; ///< list of registered commands -IConsoleAlias *_iconsole_aliases; ///< list of registered aliases +/* static */ IConsole::CommandList &IConsole::Commands() +{ + static IConsole::CommandList cmds; + return cmds; +} + +/* static */ IConsole::AliasList &IConsole::Aliases() +{ + static IConsole::AliasList aliases; + return aliases; +} FILE *_iconsole_output_file; @@ -195,49 +204,13 @@ bool GetArgumentInteger(uint32 *value, const char *arg) } /** - * Add an item to an alphabetically sorted list. - * @param base first item of the list - * @param item_new the item to add + * Creates a copy of a string with underscores removed from it + * @param name String to remove the underscores from. + * @return A copy of \a name, without underscores. */ -template -void IConsoleAddSorted(T **base, T *item_new) +static std::string RemoveUnderscores(std::string name) { - if (*base == nullptr) { - *base = item_new; - return; - } - - T *item_before = nullptr; - T *item = *base; - /* The list is alphabetically sorted, insert the new item at the correct location */ - while (item != nullptr) { - if (strcmp(item->name, item_new->name) > 0) break; // insert here - - item_before = item; - item = item->next; - } - - if (item_before == nullptr) { - *base = item_new; - } else { - item_before->next = item_new; - } - - item_new->next = item; -} - -/** - * Remove underscores from a string; the string will be modified! - * @param[in,out] name String to remove the underscores from. - * @return \a name, with its contents modified. - */ -char *RemoveUnderscores(char *name) -{ - char *q = name; - for (const char *p = name; *p != '\0'; p++) { - if (*p != '_') *q++ = *p; - } - *q = '\0'; + name.erase(std::remove(name.begin(), name.end(), '_'), name.end()); return name; } @@ -246,15 +219,9 @@ char *RemoveUnderscores(char *name) * @param name name of the command that will be used * @param proc function that will be called upon execution of command */ -void IConsoleCmdRegister(const char *name, IConsoleCmdProc *proc, IConsoleHook *hook) +/* static */ void IConsole::CmdRegister(const std::string &name, IConsoleCmdProc *proc, IConsoleHook *hook) { - IConsoleCmd *item_new = MallocT(1); - item_new->name = RemoveUnderscores(stredup(name)); - item_new->next = nullptr; - item_new->proc = proc; - item_new->hook = hook; - - IConsoleAddSorted(&_iconsole_cmds, item_new); + IConsole::Commands().try_emplace(RemoveUnderscores(name), name, proc, hook); } /** @@ -262,13 +229,10 @@ void IConsoleCmdRegister(const char *name, IConsoleCmdProc *proc, IConsoleHook * * @param name command to be found * @return return Cmdstruct of the found command, or nullptr on failure */ -IConsoleCmd *IConsoleCmdGet(const char *name) +/* static */ IConsoleCmd *IConsole::CmdGet(const std::string &name) { - IConsoleCmd *item; - - for (item = _iconsole_cmds; item != nullptr; item = item->next) { - if (strcmp(item->name, name) == 0) return item; - } + auto item = IConsole::Commands().find(RemoveUnderscores(name)); + if (item != IConsole::Commands().end()) return &item->second; return nullptr; } @@ -277,22 +241,10 @@ IConsoleCmd *IConsoleCmdGet(const char *name) * @param name name of the alias that will be used * @param cmd name of the command that 'name' will be alias of */ -void IConsoleAliasRegister(const char *name, const char *cmd) +/* static */ void IConsole::AliasRegister(const std::string &name, const std::string &cmd) { - if (IConsoleAliasGet(name) != nullptr) { - IConsoleError("an alias with this name already exists; insertion aborted"); - return; - } - - char *new_alias = RemoveUnderscores(stredup(name)); - char *cmd_aliased = stredup(cmd); - IConsoleAlias *item_new = MallocT(1); - - item_new->next = nullptr; - item_new->cmdline = cmd_aliased; - item_new->name = new_alias; - - IConsoleAddSorted(&_iconsole_aliases, item_new); + auto result = IConsole::Aliases().try_emplace(RemoveUnderscores(name), name, cmd); + if (!result.second) IConsoleError("an alias with this name already exists; insertion aborted"); } /** @@ -300,16 +252,13 @@ void IConsoleAliasRegister(const char *name, const char *cmd) * @param name alias to be found * @return return Aliasstruct of the found alias, or nullptr on failure */ -IConsoleAlias *IConsoleAliasGet(const char *name) +/* static */ IConsoleAlias *IConsole::AliasGet(const std::string &name) { - IConsoleAlias *item; - - for (item = _iconsole_aliases; item != nullptr; item = item->next) { - if (strcmp(item->name, name) == 0) return item; - } - + auto item = IConsole::Aliases().find(RemoveUnderscores(name)); + if (item != IConsole::Aliases().end()) return &item->second; return nullptr; } + /** * An alias is just another name for a command, or for more commands * Execute it as well. @@ -329,7 +278,7 @@ static void IConsoleAliasExec(const IConsoleAlias *alias, byte tokencount, char return; } - for (const char *cmdptr = alias->cmdline; *cmdptr != '\0'; cmdptr++) { + for (const char *cmdptr = alias->cmdline.c_str(); *cmdptr != '\0'; cmdptr++) { switch (*cmdptr) { case '\'': // ' will double for "" alias_stream = strecpy(alias_stream, "\"", lastof(alias_buffer)); @@ -372,7 +321,7 @@ static void IConsoleAliasExec(const IConsoleAlias *alias, byte tokencount, char if (param < 0 || param >= tokencount) { IConsoleError("too many or wrong amount of parameters passed to alias, aborting"); - IConsolePrintF(CC_WARNING, "Usage of alias '%s': %s", alias->name, alias->cmdline); + IConsolePrintF(CC_WARNING, "Usage of alias '%s': %s", alias->name.c_str(), alias->cmdline.c_str()); return; } @@ -491,8 +440,7 @@ void IConsoleCmdExec(const char *cmdstr, const uint recurse_count) * First try commands, then aliases. Execute * the found action taking into account its hooking code */ - RemoveUnderscores(tokens[0]); - IConsoleCmd *cmd = IConsoleCmdGet(tokens[0]); + IConsoleCmd *cmd = IConsole::CmdGet(tokens[0]); if (cmd != nullptr) { ConsoleHookResult chr = (cmd->hook == nullptr ? CHR_ALLOW : cmd->hook(true)); switch (chr) { @@ -508,7 +456,7 @@ void IConsoleCmdExec(const char *cmdstr, const uint recurse_count) } t_index--; - IConsoleAlias *alias = IConsoleAliasGet(tokens[0]); + IConsoleAlias *alias = IConsole::AliasGet(tokens[0]); if (alias != nullptr) { IConsoleAliasExec(alias, t_index, &tokens[1], recurse_count + 1); return; diff --git a/src/console_cmds.cpp b/src/console_cmds.cpp index 305444f8cd..f8d9eb1b0c 100644 --- a/src/console_cmds.cpp +++ b/src/console_cmds.cpp @@ -1416,12 +1416,11 @@ DEF_CONSOLE_CMD(ConAlias) if (argc < 3) return false; - alias = IConsoleAliasGet(argv[1]); + alias = IConsole::AliasGet(argv[1]); if (alias == nullptr) { - IConsoleAliasRegister(argv[1], argv[2]); + IConsole::AliasRegister(argv[1], argv[2]); } else { - free(alias->cmdline); - alias->cmdline = stredup(argv[2]); + alias->cmdline = argv[2]; } return true; } @@ -1515,13 +1514,13 @@ DEF_CONSOLE_CMD(ConInfoCmd) if (argc < 2) return false; - const IConsoleCmd *cmd = IConsoleCmdGet(argv[1]); + const IConsoleCmd *cmd = IConsole::CmdGet(argv[1]); if (cmd == nullptr) { IConsoleError("the given command was not found"); return true; } - IConsolePrintF(CC_DEFAULT, "command name: %s", cmd->name); + IConsolePrintF(CC_DEFAULT, "command name: %s", cmd->name.c_str()); IConsolePrintF(CC_DEFAULT, "command proc: %p", cmd->proc); if (cmd->hook != nullptr) IConsoleWarning("command is hooked"); @@ -1580,21 +1579,20 @@ DEF_CONSOLE_CMD(ConHelp) const IConsoleCmd *cmd; const IConsoleAlias *alias; - RemoveUnderscores(argv[1]); - cmd = IConsoleCmdGet(argv[1]); + cmd = IConsole::CmdGet(argv[1]); if (cmd != nullptr) { cmd->proc(0, nullptr); return true; } - alias = IConsoleAliasGet(argv[1]); + alias = IConsole::AliasGet(argv[1]); if (alias != nullptr) { - cmd = IConsoleCmdGet(alias->cmdline); + cmd = IConsole::CmdGet(alias->cmdline); if (cmd != nullptr) { cmd->proc(0, nullptr); return true; } - IConsolePrintF(CC_ERROR, "ERROR: alias is of special type, please see its execution-line: '%s'", alias->cmdline); + IConsolePrintF(CC_ERROR, "ERROR: alias is of special type, please see its execution-line: '%s'", alias->cmdline.c_str()); return true; } @@ -1621,9 +1619,10 @@ DEF_CONSOLE_CMD(ConListCommands) return true; } - for (const IConsoleCmd *cmd = _iconsole_cmds; cmd != nullptr; cmd = cmd->next) { - if (argv[1] == nullptr || strstr(cmd->name, argv[1]) != nullptr) { - if (cmd->hook == nullptr || cmd->hook(false) != CHR_HIDE) IConsolePrintF(CC_DEFAULT, "%s", cmd->name); + for (auto &it : IConsole::Commands()) { + const IConsoleCmd *cmd = &it.second; + if (argv[1] == nullptr || cmd->name.find(argv[1]) != std::string::npos) { + if (cmd->hook == nullptr || cmd->hook(false) != CHR_HIDE) IConsolePrintF(CC_DEFAULT, "%s", cmd->name.c_str()); } } @@ -1637,9 +1636,10 @@ DEF_CONSOLE_CMD(ConListAliases) return true; } - for (const IConsoleAlias *alias = _iconsole_aliases; alias != nullptr; alias = alias->next) { - if (argv[1] == nullptr || strstr(alias->name, argv[1]) != nullptr) { - IConsolePrintF(CC_DEFAULT, "%s => %s", alias->name, alias->cmdline); + for (auto &it : IConsole::Aliases()) { + const IConsoleAlias *alias = &it.second; + if (argv[1] == nullptr || alias->name.find(argv[1]) != std::string::npos) { + IConsolePrintF(CC_DEFAULT, "%s => %s", alias->name.c_str(), alias->cmdline.c_str()); } } @@ -2133,9 +2133,9 @@ DEF_CONSOLE_CMD(ConNewGRFProfile) static void IConsoleDebugLibRegister() { - IConsoleCmdRegister("resettile", ConResetTile); - IConsoleAliasRegister("dbg_echo", "echo %A; echo %B"); - IConsoleAliasRegister("dbg_echo2", "echo %!"); + IConsole::CmdRegister("resettile", ConResetTile); + IConsole::AliasRegister("dbg_echo", "echo %A; echo %B"); + IConsole::AliasRegister("dbg_echo2", "echo %!"); } #endif @@ -2329,137 +2329,137 @@ DEF_CONSOLE_CMD(ConDumpInfo) void IConsoleStdLibRegister() { - IConsoleCmdRegister("debug_level", ConDebugLevel); - IConsoleCmdRegister("echo", ConEcho); - IConsoleCmdRegister("echoc", ConEchoC); - IConsoleCmdRegister("exec", ConExec); - IConsoleCmdRegister("exit", ConExit); - IConsoleCmdRegister("part", ConPart); - IConsoleCmdRegister("help", ConHelp); - IConsoleCmdRegister("info_cmd", ConInfoCmd); - IConsoleCmdRegister("list_cmds", ConListCommands); - IConsoleCmdRegister("list_aliases", ConListAliases); - IConsoleCmdRegister("newgame", ConNewGame); - IConsoleCmdRegister("restart", ConRestart); - IConsoleCmdRegister("reload", ConReload); - IConsoleCmdRegister("getseed", ConGetSeed); - IConsoleCmdRegister("getdate", ConGetDate); - IConsoleCmdRegister("getsysdate", ConGetSysDate); - IConsoleCmdRegister("quit", ConExit); - IConsoleCmdRegister("resetengines", ConResetEngines, ConHookNoNetwork); - IConsoleCmdRegister("reset_enginepool", ConResetEnginePool, ConHookNoNetwork); - IConsoleCmdRegister("return", ConReturn); - IConsoleCmdRegister("screenshot", ConScreenShot); - IConsoleCmdRegister("script", ConScript); - IConsoleCmdRegister("scrollto", ConScrollToTile); - IConsoleCmdRegister("alias", ConAlias); - IConsoleCmdRegister("load", ConLoad); - IConsoleCmdRegister("rm", ConRemove); - IConsoleCmdRegister("save", ConSave); - IConsoleCmdRegister("saveconfig", ConSaveConfig); - IConsoleCmdRegister("ls", ConListFiles); - IConsoleCmdRegister("cd", ConChangeDirectory); - IConsoleCmdRegister("pwd", ConPrintWorkingDirectory); - IConsoleCmdRegister("clear", ConClearBuffer); - IConsoleCmdRegister("setting", ConSetting); - IConsoleCmdRegister("setting_newgame", ConSettingNewgame); - IConsoleCmdRegister("list_settings",ConListSettings); - IConsoleCmdRegister("gamelog", ConGamelogPrint); - IConsoleCmdRegister("rescan_newgrf", ConRescanNewGRF); + IConsole::CmdRegister("debug_level", ConDebugLevel); + IConsole::CmdRegister("echo", ConEcho); + IConsole::CmdRegister("echoc", ConEchoC); + IConsole::CmdRegister("exec", ConExec); + IConsole::CmdRegister("exit", ConExit); + IConsole::CmdRegister("part", ConPart); + IConsole::CmdRegister("help", ConHelp); + IConsole::CmdRegister("info_cmd", ConInfoCmd); + IConsole::CmdRegister("list_cmds", ConListCommands); + IConsole::CmdRegister("list_aliases", ConListAliases); + IConsole::CmdRegister("newgame", ConNewGame); + IConsole::CmdRegister("restart", ConRestart); + IConsole::CmdRegister("reload", ConReload); + IConsole::CmdRegister("getseed", ConGetSeed); + IConsole::CmdRegister("getdate", ConGetDate); + IConsole::CmdRegister("getsysdate", ConGetSysDate); + IConsole::CmdRegister("quit", ConExit); + IConsole::CmdRegister("resetengines", ConResetEngines, ConHookNoNetwork); + IConsole::CmdRegister("reset_enginepool", ConResetEnginePool, ConHookNoNetwork); + IConsole::CmdRegister("return", ConReturn); + IConsole::CmdRegister("screenshot", ConScreenShot); + IConsole::CmdRegister("script", ConScript); + IConsole::CmdRegister("scrollto", ConScrollToTile); + IConsole::CmdRegister("alias", ConAlias); + IConsole::CmdRegister("load", ConLoad); + IConsole::CmdRegister("rm", ConRemove); + IConsole::CmdRegister("save", ConSave); + IConsole::CmdRegister("saveconfig", ConSaveConfig); + IConsole::CmdRegister("ls", ConListFiles); + IConsole::CmdRegister("cd", ConChangeDirectory); + IConsole::CmdRegister("pwd", ConPrintWorkingDirectory); + IConsole::CmdRegister("clear", ConClearBuffer); + IConsole::CmdRegister("setting", ConSetting); + IConsole::CmdRegister("setting_newgame", ConSettingNewgame); + IConsole::CmdRegister("list_settings", ConListSettings); + IConsole::CmdRegister("gamelog", ConGamelogPrint); + IConsole::CmdRegister("rescan_newgrf", ConRescanNewGRF); - IConsoleAliasRegister("dir", "ls"); - IConsoleAliasRegister("del", "rm %+"); - IConsoleAliasRegister("newmap", "newgame"); - IConsoleAliasRegister("patch", "setting %+"); - IConsoleAliasRegister("set", "setting %+"); - IConsoleAliasRegister("set_newgame", "setting_newgame %+"); - IConsoleAliasRegister("list_patches", "list_settings %+"); - IConsoleAliasRegister("developer", "setting developer %+"); + IConsole::AliasRegister("dir", "ls"); + IConsole::AliasRegister("del", "rm %+"); + IConsole::AliasRegister("newmap", "newgame"); + IConsole::AliasRegister("patch", "setting %+"); + IConsole::AliasRegister("set", "setting %+"); + IConsole::AliasRegister("set_newgame", "setting_newgame %+"); + IConsole::AliasRegister("list_patches", "list_settings %+"); + IConsole::AliasRegister("developer", "setting developer %+"); - IConsoleCmdRegister("list_ai_libs", ConListAILibs); - IConsoleCmdRegister("list_ai", ConListAI); - IConsoleCmdRegister("reload_ai", ConReloadAI); - IConsoleCmdRegister("rescan_ai", ConRescanAI); - IConsoleCmdRegister("start_ai", ConStartAI); - IConsoleCmdRegister("stop_ai", ConStopAI); + IConsole::CmdRegister("list_ai_libs", ConListAILibs); + IConsole::CmdRegister("list_ai", ConListAI); + IConsole::CmdRegister("reload_ai", ConReloadAI); + IConsole::CmdRegister("rescan_ai", ConRescanAI); + IConsole::CmdRegister("start_ai", ConStartAI); + IConsole::CmdRegister("stop_ai", ConStopAI); - IConsoleCmdRegister("list_game", ConListGame); - IConsoleCmdRegister("list_game_libs", ConListGameLibs); - IConsoleCmdRegister("rescan_game", ConRescanGame); + IConsole::CmdRegister("list_game", ConListGame); + IConsole::CmdRegister("list_game_libs", ConListGameLibs); + IConsole::CmdRegister("rescan_game", ConRescanGame); - IConsoleCmdRegister("companies", ConCompanies); - IConsoleAliasRegister("players", "companies"); + IConsole::CmdRegister("companies", ConCompanies); + IConsole::AliasRegister("players", "companies"); /* networking functions */ /* Content downloading is only available with ZLIB */ #if defined(WITH_ZLIB) - IConsoleCmdRegister("content", ConContent); + IConsole::CmdRegister("content", ConContent); #endif /* defined(WITH_ZLIB) */ /*** Networking commands ***/ - IConsoleCmdRegister("say", ConSay, ConHookNeedNetwork); - IConsoleCmdRegister("say_company", ConSayCompany, ConHookNeedNetwork); - IConsoleAliasRegister("say_player", "say_company %+"); - IConsoleCmdRegister("say_client", ConSayClient, ConHookNeedNetwork); + IConsole::CmdRegister("say", ConSay, ConHookNeedNetwork); + IConsole::CmdRegister("say_company", ConSayCompany, ConHookNeedNetwork); + IConsole::AliasRegister("say_player", "say_company %+"); + IConsole::CmdRegister("say_client", ConSayClient, ConHookNeedNetwork); - IConsoleCmdRegister("connect", ConNetworkConnect, ConHookClientOnly); - IConsoleCmdRegister("clients", ConNetworkClients, ConHookNeedNetwork); - IConsoleCmdRegister("status", ConStatus, ConHookServerOnly); - IConsoleCmdRegister("server_info", ConServerInfo, ConHookServerOnly); - IConsoleAliasRegister("info", "server_info"); - IConsoleCmdRegister("reconnect", ConNetworkReconnect, ConHookClientOnly); - IConsoleCmdRegister("rcon", ConRcon, ConHookNeedNetwork); + IConsole::CmdRegister("connect", ConNetworkConnect, ConHookClientOnly); + IConsole::CmdRegister("clients", ConNetworkClients, ConHookNeedNetwork); + IConsole::CmdRegister("status", ConStatus, ConHookServerOnly); + IConsole::CmdRegister("server_info", ConServerInfo, ConHookServerOnly); + IConsole::AliasRegister("info", "server_info"); + IConsole::CmdRegister("reconnect", ConNetworkReconnect, ConHookClientOnly); + IConsole::CmdRegister("rcon", ConRcon, ConHookNeedNetwork); - IConsoleCmdRegister("join", ConJoinCompany, ConHookNeedNetwork); - IConsoleAliasRegister("spectate", "join 255"); - IConsoleCmdRegister("move", ConMoveClient, ConHookServerOnly); - IConsoleCmdRegister("reset_company", ConResetCompany, ConHookServerOnly); - IConsoleAliasRegister("clean_company", "reset_company %A"); - IConsoleCmdRegister("client_name", ConClientNickChange, ConHookServerOnly); - IConsoleCmdRegister("kick", ConKick, ConHookServerOnly); - IConsoleCmdRegister("ban", ConBan, ConHookServerOnly); - IConsoleCmdRegister("unban", ConUnBan, ConHookServerOnly); - IConsoleCmdRegister("banlist", ConBanList, ConHookServerOnly); + IConsole::CmdRegister("join", ConJoinCompany, ConHookNeedNetwork); + IConsole::AliasRegister("spectate", "join 255"); + IConsole::CmdRegister("move", ConMoveClient, ConHookServerOnly); + IConsole::CmdRegister("reset_company", ConResetCompany, ConHookServerOnly); + IConsole::AliasRegister("clean_company", "reset_company %A"); + IConsole::CmdRegister("client_name", ConClientNickChange, ConHookServerOnly); + IConsole::CmdRegister("kick", ConKick, ConHookServerOnly); + IConsole::CmdRegister("ban", ConBan, ConHookServerOnly); + IConsole::CmdRegister("unban", ConUnBan, ConHookServerOnly); + IConsole::CmdRegister("banlist", ConBanList, ConHookServerOnly); - IConsoleCmdRegister("pause", ConPauseGame, ConHookServerOnly); - IConsoleCmdRegister("unpause", ConUnpauseGame, ConHookServerOnly); + IConsole::CmdRegister("pause", ConPauseGame, ConHookServerOnly); + IConsole::CmdRegister("unpause", ConUnpauseGame, ConHookServerOnly); - IConsoleCmdRegister("company_pw", ConCompanyPassword, ConHookNeedNetwork); - IConsoleAliasRegister("company_password", "company_pw %+"); + IConsole::CmdRegister("company_pw", ConCompanyPassword, ConHookNeedNetwork); + IConsole::AliasRegister("company_password", "company_pw %+"); - IConsoleAliasRegister("net_frame_freq", "setting frame_freq %+"); - IConsoleAliasRegister("net_sync_freq", "setting sync_freq %+"); - IConsoleAliasRegister("server_pw", "setting server_password %+"); - IConsoleAliasRegister("server_password", "setting server_password %+"); - IConsoleAliasRegister("rcon_pw", "setting rcon_password %+"); - IConsoleAliasRegister("rcon_password", "setting rcon_password %+"); - IConsoleAliasRegister("name", "setting client_name %+"); - IConsoleAliasRegister("server_name", "setting server_name %+"); - IConsoleAliasRegister("server_port", "setting server_port %+"); - IConsoleAliasRegister("server_advertise", "setting server_advertise %+"); - IConsoleAliasRegister("max_clients", "setting max_clients %+"); - IConsoleAliasRegister("max_companies", "setting max_companies %+"); - IConsoleAliasRegister("max_spectators", "setting max_spectators %+"); - IConsoleAliasRegister("max_join_time", "setting max_join_time %+"); - IConsoleAliasRegister("pause_on_join", "setting pause_on_join %+"); - IConsoleAliasRegister("autoclean_companies", "setting autoclean_companies %+"); - IConsoleAliasRegister("autoclean_protected", "setting autoclean_protected %+"); - IConsoleAliasRegister("autoclean_unprotected", "setting autoclean_unprotected %+"); - IConsoleAliasRegister("restart_game_year", "setting restart_game_year %+"); - IConsoleAliasRegister("min_players", "setting min_active_clients %+"); - IConsoleAliasRegister("reload_cfg", "setting reload_cfg %+"); + IConsole::AliasRegister("net_frame_freq", "setting frame_freq %+"); + IConsole::AliasRegister("net_sync_freq", "setting sync_freq %+"); + IConsole::AliasRegister("server_pw", "setting server_password %+"); + IConsole::AliasRegister("server_password", "setting server_password %+"); + IConsole::AliasRegister("rcon_pw", "setting rcon_password %+"); + IConsole::AliasRegister("rcon_password", "setting rcon_password %+"); + IConsole::AliasRegister("name", "setting client_name %+"); + IConsole::AliasRegister("server_name", "setting server_name %+"); + IConsole::AliasRegister("server_port", "setting server_port %+"); + IConsole::AliasRegister("server_advertise", "setting server_advertise %+"); + IConsole::AliasRegister("max_clients", "setting max_clients %+"); + IConsole::AliasRegister("max_companies", "setting max_companies %+"); + IConsole::AliasRegister("max_spectators", "setting max_spectators %+"); + IConsole::AliasRegister("max_join_time", "setting max_join_time %+"); + IConsole::AliasRegister("pause_on_join", "setting pause_on_join %+"); + IConsole::AliasRegister("autoclean_companies", "setting autoclean_companies %+"); + IConsole::AliasRegister("autoclean_protected", "setting autoclean_protected %+"); + IConsole::AliasRegister("autoclean_unprotected", "setting autoclean_unprotected %+"); + IConsole::AliasRegister("restart_game_year", "setting restart_game_year %+"); + IConsole::AliasRegister("min_players", "setting min_active_clients %+"); + IConsole::AliasRegister("reload_cfg", "setting reload_cfg %+"); /* debugging stuff */ #ifdef _DEBUG IConsoleDebugLibRegister(); #endif - IConsoleCmdRegister("fps", ConFramerate); - IConsoleCmdRegister("fps_wnd", ConFramerateWindow); + IConsole::CmdRegister("fps", ConFramerate); + IConsole::CmdRegister("fps_wnd", ConFramerateWindow); /* NewGRF development stuff */ - IConsoleCmdRegister("reload_newgrfs", ConNewGRFReload, ConHookNewGRFDeveloperTool); - IConsoleCmdRegister("newgrf_profile", ConNewGRFProfile, ConHookNewGRFDeveloperTool); + IConsole::CmdRegister("reload_newgrfs", ConNewGRFReload, ConHookNewGRFDeveloperTool); + IConsole::CmdRegister("newgrf_profile", ConNewGRFProfile, ConHookNewGRFDeveloperTool); - IConsoleCmdRegister("dump_info", ConDumpInfo); + IConsole::CmdRegister("dump_info", ConDumpInfo); } diff --git a/src/console_internal.h b/src/console_internal.h index 630f69607b..ca2f92193b 100644 --- a/src/console_internal.h +++ b/src/console_internal.h @@ -11,6 +11,7 @@ #define CONSOLE_INTERNAL_H #include "gfx_type.h" +#include static const uint ICON_CMDLN_SIZE = 1024; ///< maximum length of a typed in command static const uint ICON_MAX_STREAMSIZE = 2048; ///< maximum length of a totally expanded command @@ -33,9 +34,9 @@ enum ConsoleHookResult { typedef bool IConsoleCmdProc(byte argc, char *argv[]); typedef ConsoleHookResult IConsoleHook(bool echo); struct IConsoleCmd { - char *name; ///< name of command - IConsoleCmd *next; ///< next command in list + IConsoleCmd(const std::string &name, IConsoleCmdProc *proc, IConsoleHook *hook) : name(name), proc(proc), hook(hook) {} + std::string name; ///< name of command IConsoleCmdProc *proc; ///< process executed when command is typed IConsoleHook *hook; ///< any special trigger action that needs executing }; @@ -53,25 +54,31 @@ struct IConsoleCmd { * - ";" allows for combining commands (see example 'ng') */ struct IConsoleAlias { - char *name; ///< name of the alias - IConsoleAlias *next; ///< next alias in list + IConsoleAlias(const std::string &name, const std::string &cmdline) : name(name), cmdline(cmdline) {} - char *cmdline; ///< command(s) that is/are being aliased + std::string name; ///< name of the alias + std::string cmdline; ///< command(s) that is/are being aliased }; -/* console parser */ -extern IConsoleCmd *_iconsole_cmds; ///< List of registered commands. -extern IConsoleAlias *_iconsole_aliases; ///< List of registered aliases. +struct IConsole +{ + typedef std::map CommandList; + typedef std::map AliasList; + + /* console parser */ + static CommandList &Commands(); + static AliasList &Aliases(); + + /* Commands */ + static void CmdRegister(const std::string &name, IConsoleCmdProc *proc, IConsoleHook *hook = nullptr); + static IConsoleCmd *CmdGet(const std::string &name); + static void AliasRegister(const std::string &name, const std::string &cmd); + static IConsoleAlias *AliasGet(const std::string &name); +}; /* console functions */ void IConsoleClearBuffer(); -/* Commands */ -void IConsoleCmdRegister(const char *name, IConsoleCmdProc *proc, IConsoleHook *hook = nullptr); -void IConsoleAliasRegister(const char *name, const char *cmd); -IConsoleCmd *IConsoleCmdGet(const char *name); -IConsoleAlias *IConsoleAliasGet(const char *name); - /* console std lib (register ingame commands/aliases) */ void IConsoleStdLibRegister(); @@ -81,6 +88,5 @@ bool GetArgumentInteger(uint32 *value, const char *arg); void IConsoleGUIInit(); void IConsoleGUIFree(); void IConsoleGUIPrint(TextColour colour_code, char *string); -char *RemoveUnderscores(char *name); #endif /* CONSOLE_INTERNAL_H */ diff --git a/src/music/midifile.cpp b/src/music/midifile.cpp index 4bdd3e4e39..dcd2cefd8d 100644 --- a/src/music/midifile.cpp +++ b/src/music/midifile.cpp @@ -1142,7 +1142,7 @@ static void RegisterConsoleMidiCommands() { static bool registered = false; if (!registered) { - IConsoleCmdRegister("dumpsmf", CmdDumpSMF); + IConsole::CmdRegister("dumpsmf", CmdDumpSMF); registered = true; } } From 2e39ac5ba2dc1f2f8b08ceb00898fe919d8f5d3b Mon Sep 17 00:00:00 2001 From: Patric Stout Date: Sat, 24 Apr 2021 17:03:19 +0200 Subject: [PATCH 134/268] Add: [GitHub] use issue templates to make it more clear what we expect from users (#9092) --- .github/ISSUE_TEMPLATE.md | 7 ------- .github/ISSUE_TEMPLATE/bug.md | 17 +++++++++++++++++ .github/ISSUE_TEMPLATE/config.yml | 5 +++++ .github/ISSUE_TEMPLATE/crash.md | 12 ++++++++++++ 4 files changed, 34 insertions(+), 7 deletions(-) delete mode 100644 .github/ISSUE_TEMPLATE.md create mode 100644 .github/ISSUE_TEMPLATE/bug.md create mode 100644 .github/ISSUE_TEMPLATE/config.yml create mode 100644 .github/ISSUE_TEMPLATE/crash.md diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md deleted file mode 100644 index ba365bfe4f..0000000000 --- a/.github/ISSUE_TEMPLATE.md +++ /dev/null @@ -1,7 +0,0 @@ -## Version of OpenTTD - -## Expected result - -## Actual result - -## Steps to reproduce diff --git a/.github/ISSUE_TEMPLATE/bug.md b/.github/ISSUE_TEMPLATE/bug.md new file mode 100644 index 0000000000..0af8a89791 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug.md @@ -0,0 +1,17 @@ +--- +name: Bugs +about: Found a bug in OpenTTD? +title: "Bug Report" +--- + +## Version of OpenTTD + + +## Expected result + + +## Actual result + + +## Steps to reproduce + diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000000..2fd749087a --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,5 @@ +blank_issues_enabled: false +contact_links: +- name: Suggestions and ideas? + url: https://www.tt-forums.net/viewforum.php?f=32 + about: Have a suggestion or an idea for a cool new feature? Post them on our forum! diff --git a/.github/ISSUE_TEMPLATE/crash.md b/.github/ISSUE_TEMPLATE/crash.md new file mode 100644 index 0000000000..f7d127e930 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/crash.md @@ -0,0 +1,12 @@ +--- +name: Crash +about: Did OpenTTD crash? +title: "Crash Report" +--- + + +## Version of OpenTTD + + +## Steps to reproduce + From 3b9b177b36c4ab381b7c78b794fbc7818a9cfe8f Mon Sep 17 00:00:00 2001 From: Leif Linse Date: Sat, 24 Apr 2021 18:49:37 +0200 Subject: [PATCH 135/268] Update: Developer credits (#9091) --- CREDITS.md | 2 +- src/misc_gui.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CREDITS.md b/CREDITS.md index 8c20a4ac18..33f8836265 100644 --- a/CREDITS.md +++ b/CREDITS.md @@ -14,7 +14,6 @@ - Ingo von Borstel (planetmaker) - General coding, Support (since 1.1) - Remko Bijker (Rubidium) - Lead coder and way more (since 0.4.5) - José Soler (Terkhen) - General coding (since 1.0) -- Leif Linse (Zuu) - AI/Game Script (since 1.2) ### Inactive Developers: @@ -28,6 +27,7 @@ - Christoph Mallon (Tron) - Programmer, code correctness police (0.3 - 0.5) - Patric Stout (TrueBrain) - NoProgrammer (0.3 - 1.2), sys op (active) - Thijs Marinussen (Yexo) - AI Framework, General (0.6 - 1.3) +- Leif Linse (Zuu) - AI/Game Script (1.2 - 1.6) ### Retired Developers: diff --git a/src/misc_gui.cpp b/src/misc_gui.cpp index 8ca94b2c3e..9be207ac60 100644 --- a/src/misc_gui.cpp +++ b/src/misc_gui.cpp @@ -445,7 +445,6 @@ static const char * const _credits[] = { u8" Ingo von Borstel (planetmaker) - General, Support (since 1.1)", u8" Remko Bijker (Rubidium) - Lead coder and way more (since 0.4.5)", u8" Jos\u00e9 Soler (Terkhen) - General coding (since 1.0)", - u8" Leif Linse (Zuu) - AI/Game Script (since 1.2)", u8"", u8"Inactive Developers:", u8" Jean-Fran\u00e7ois Claeys (Belugas) - GUI, NewGRF and more (0.4.5 - 1.0)", @@ -458,6 +457,7 @@ static const char * const _credits[] = { u8" Christoph Mallon (Tron) - Programmer, code correctness police (0.3 - 0.5)", u8" Patric Stout (TrueBrain) - NoAI, NoGo, Network (0.3 - 1.2), sys op (active)", u8" Thijs Marinussen (Yexo) - AI Framework, General (0.6 - 1.3)", + u8" Leif Linse (Zuu) - AI/Game Script (1.2 - 1.6)", u8"", u8"Retired Developers:", u8" Tam\u00e1s Farag\u00f3 (Darkvater) - Ex-Lead coder (0.3 - 0.5)", From 470d8b66372ceb5a38ce9823d451bddd2f3eca35 Mon Sep 17 00:00:00 2001 From: translators Date: Sat, 24 Apr 2021 17:53:56 +0000 Subject: [PATCH 136/268] Update: Translations from eints korean: 2 changes by telk5093 russian: 3 changes by Ln-Wolf finnish: 1 change by hpiirai spanish: 1 change by MontyMontana polish: 1 change by pAter-exe hindi: 62 changes by ss141309 --- src/lang/finnish.txt | 1 + src/lang/hindi.txt | 62 ++++++++++++++++++++++++++++++++++++++++++++ src/lang/korean.txt | 3 ++- src/lang/polish.txt | 1 + src/lang/russian.txt | 5 ++-- src/lang/spanish.txt | 1 + 6 files changed, 70 insertions(+), 3 deletions(-) diff --git a/src/lang/finnish.txt b/src/lang/finnish.txt index a3b2a8cb09..820a83b065 100644 --- a/src/lang/finnish.txt +++ b/src/lang/finnish.txt @@ -993,6 +993,7 @@ STR_GAME_OPTIONS_AUTOSAVE_DROPDOWN_EVERY_12_MONTHS :Kerran vuodessa STR_GAME_OPTIONS_LANGUAGE :{BLACK}Kieli STR_GAME_OPTIONS_LANGUAGE_TOOLTIP :{BLACK}Valitse käyttöliittymän kieli +STR_GAME_OPTIONS_LANGUAGE_PERCENTAGE :{STRING} ({NUM}{NBSP}% valmiina) STR_GAME_OPTIONS_FULLSCREEN :{BLACK}Koko näyttö STR_GAME_OPTIONS_FULLSCREEN_TOOLTIP :{BLACK}Valitse tämä pelataksesi kokoruututilassa diff --git a/src/lang/hindi.txt b/src/lang/hindi.txt index b0d67efb33..d8e185057f 100644 --- a/src/lang/hindi.txt +++ b/src/lang/hindi.txt @@ -23,9 +23,12 @@ STR_EMPTY : # Cargo related strings # Plural cargo name STR_CARGO_PLURAL_NOTHING : +STR_CARGO_PLURAL_COAL :कोयला # Singular cargo name STR_CARGO_SINGULAR_NOTHING : +STR_CARGO_SINGULAR_MAIZE :मक्का +STR_CARGO_SINGULAR_SWEETS :मिठाई # Quantity of cargo STR_QUANTITY_NOTHING : @@ -36,6 +39,7 @@ STR_ABBREV_NOTHING : # 'Mode' of transport for cargoes # Colours, do not shuffle +STR_COLOUR_PINK :गुलाबी # Units used in OpenTTD @@ -61,7 +65,12 @@ STR_ABBREV_NOTHING : # These are used in buttons +STR_SORT_BY_CAPTION_DATE :{BLACK}दिनाँक # These are used in dropdowns +STR_SORT_BY_PRODUCTION :उत्पादन +STR_SORT_BY_PROFIT_LAST_YEAR :पिछले वर्ष का लाभ +STR_SORT_BY_TOTAL_PROFIT_THIS_YEAR :इस वर्ष का कुल लाभ +STR_SORT_BY_AVERAGE_PROFIT_LAST_YEAR :पिछले वर्ष का औसत लाभ # Group by options for vehicle list @@ -123,12 +132,15 @@ STR_ABOUT_MENU_SEPARATOR : ############ range ends here ############ range for ordinal numbers used for the place in the highscore window +STR_ORDINAL_NUMBER_2ND :द्वितीय ############ range for ordinal numbers ends ############ range for days starts ############ range for days ends ############ range for months starts +STR_MONTH_ABBREV_JAN :जन +STR_MONTH_ABBREV_NOV :नव ############ range for months ends @@ -147,6 +159,7 @@ STR_GRAPH_CARGO_PAYMENT_CARGO :{TINY_FONT}{BLA # Performance detail window ############ Those following lines need to be in this order!! +STR_PERFORMANCE_DETAIL_VEHICLES :{BLACK}वाहन: ############ End of order list # Music window @@ -195,15 +208,22 @@ STR_NEWS_NEW_VEHICLE_TYPE :{BIG_FONT}{BLAC # Game options window ############ start of currency region +STR_GAME_OPTIONS_CURRENCY_HKD :हाँग काँग डॉलर (एचकेडी) ############ end of currency region +STR_GAME_OPTIONS_ROAD_VEHICLES_DROPDOWN_RIGHT :दाईं ओर वाहन चलाएँ ############ start of townname region +STR_GAME_OPTIONS_TOWN_NAME_FRENCH :फ़्रेंच +STR_GAME_OPTIONS_TOWN_NAME_POLISH :पोलिश +STR_GAME_OPTIONS_TOWN_NAME_TURKISH :तुर्की +STR_GAME_OPTIONS_TOWN_NAME_ITALIAN :इटैलियन ############ end of townname region ############ start of autosave dropdown +STR_GAME_OPTIONS_AUTOSAVE_DROPDOWN_EVERY_1_MONTH :प्रत्येक माह ############ end of autosave dropdown @@ -231,6 +251,7 @@ STR_NEWS_NEW_VEHICLE_TYPE :{BIG_FONT}{BLAC +STR_AI_SPEED_SLOW :धीमा @@ -244,6 +265,7 @@ STR_NEWS_NEW_VEHICLE_TYPE :{BIG_FONT}{BLAC +STR_CONFIG_SETTING_COMPANIES_OFF :बन्द @@ -252,6 +274,7 @@ STR_CONFIG_SETTING_MAP_HEIGHT_LIMIT_VALUE :{NUM} +STR_CONFIG_SETTING_ORDER_REVIEW_OFF :नहीं @@ -260,6 +283,7 @@ STR_CONFIG_SETTING_MAP_HEIGHT_LIMIT_VALUE :{NUM} +STR_CONFIG_SETTING_SOUND_NEWS :समाचार पत्र: {STRING} @@ -273,6 +297,7 @@ STR_CONFIG_SETTING_ENDING_YEAR_VALUE :{NUM} STR_CONFIG_SETTING_SOFT_LIMIT_VALUE :{COMMA} +STR_CONFIG_SETTING_SPRITE_ZOOM_LVL_IN_2X :२x @@ -298,6 +323,7 @@ STR_CONFIG_SETTING_SOFT_LIMIT_VALUE :{COMMA} # Quit window +STR_QUIT_YES :{BLACK}हाँ # Abandon game @@ -312,6 +338,7 @@ STR_CONFIG_SETTING_SOFT_LIMIT_VALUE :{COMMA} # Network server list +STR_NETWORK_SERVER_LIST_MAP_SIZE_SHORT :{BLACK}{COMMA}x{COMMA} @@ -321,11 +348,13 @@ STR_CONFIG_SETTING_SOFT_LIMIT_VALUE :{COMMA} # Start new multiplayer server +STR_NETWORK_START_SERVER_ADVERTISED :हाँ # Network game lobby +STR_NETWORK_GAME_LOBBY_INAUGURATION_YEAR :{SILVER}उद्घाटन: {WHITE}{NUM} @@ -357,6 +386,7 @@ STR_CONFIG_SETTING_SOFT_LIMIT_VALUE :{COMMA} # Network related errors ############ Leave those lines in this order!! +STR_NETWORK_SERVER_MESSAGE_GAME_STILL_PAUSED_3 :खेल अभी भी ठहरा हुआ है ({STRING}, {STRING}, {STRING}) ############ End of leave-in-this-order # Content downloading window @@ -432,6 +462,8 @@ STR_CONFIG_SETTING_SOFT_LIMIT_VALUE :{COMMA} # Town generation window (SE) +STR_FOUND_TOWN_INITIAL_SIZE_SMALL_BUTTON :{BLACK}लघु +STR_FOUND_TOWN_CITY :{BLACK}शहर # Fund new industry window @@ -474,6 +506,7 @@ STR_FRAMERATE_BYTES_BAD :{RED}{BYTES} # World generation # Strings for map borders at game generation +STR_MAPGEN_BORDER_WATER :{BLACK}जल @@ -481,6 +514,7 @@ STR_FRAMERATE_BYTES_BAD :{RED}{BYTES} # Map generation progress +STR_GENERATION_PROGRESS_NUM :{BLACK}{NUM} / {NUM} # NewGRF settings @@ -526,7 +560,9 @@ STR_NEWGRF_ERROR_MSG_INFO :{SILVER}{STRING # Town view window STR_TOWN_VIEW_TOWN_CAPTION :{WHITE}{TOWN} +STR_TOWN_VIEW_LOCAL_AUTHORITY_TOOLTIP :{BLACK}स्थानीय प्राधिकारी के बारे में जानकारी दिखाएँ +STR_TOWN_VIEW_EXPAND_BUTTON :{BLACK}फैलाएँ # Town local authority window @@ -547,6 +583,7 @@ STR_GOALS_PROGRESS_COMPLETE :{GREEN}{STRING} # Story book window STR_STORY_BOOK_TITLE :{YELLOW}{STRING} +STR_STORY_BOOK_NEXT_PAGE_TOOLTIP :{BLACK}अगले पृष्ठ पर जाएँ # Station list window STR_STATION_LIST_STATION :{YELLOW}{STATION} {STATION_FEATURES} @@ -574,10 +611,13 @@ STR_WAYPOINT_VIEW_CAPTION :{WHITE}{WAYPOIN # Finances window STR_FINANCES_YEAR :{WHITE}{NUM} +STR_FINANCES_POSITIVE_INCOME :{BLACK}+{CURRENCY_LONG} STR_FINANCES_TOTAL_CURRENCY :{BLACK}{CURRENCY_LONG} +STR_FINANCES_BORROW_BUTTON :{BLACK}{CURRENCY_LONG} उधार # Company view STR_COMPANY_VIEW_CAPTION :{WHITE}{COMPANY} {BLACK}{COMPANY_NUM} +STR_COMPANY_VIEW_PRESIDENT_MANAGER_TITLE :{WHITE}{PRESIDENT_NAME}{}{GOLD}(प्रबन्धक) @@ -588,6 +628,7 @@ STR_COMPANY_VIEW_CAPTION :{WHITE}{COMPANY # Company infrastructure window # Industry directory +STR_INDUSTRY_DIRECTORY_CAPTION :{WHITE}उद्योग STR_INDUSTRY_DIRECTORY_ITEM_NOPROD :{ORANGE}{INDUSTRY} STR_INDUSTRY_DIRECTORY_ITEM_PROD1 :{ORANGE}{INDUSTRY} {STRING} @@ -658,12 +699,14 @@ STR_DEPOT_VEHICLE_TOOLTIP :{BLACK}{ENGINE} +STR_ENGINE_PREVIEW_SHIP :जहाज # Autoreplace window +STR_REPLACE_VEHICLES_STOP :वाहन प्रतिस्थापित करना बन्द करें @@ -724,6 +767,7 @@ STR_ORDER_TEXT :{STRING} {STRIN # Conditional order variables, must follow order of OrderConditionVariable enum +STR_ORDER_CONDITIONAL_COMPARATOR_EQUALS :के बराबर है @@ -764,9 +808,11 @@ STR_ORDER_GO_TO_STATION :{STRING} {STATI # Date window (for timetable) +STR_DATE_MONTH_TOOLTIP :{BLACK}महीना चुनें # AI debug window +STR_AI_DEBUG_NAME_AND_VERSION :{BLACK}{STRING} (v{NUM}) # AI configuration window @@ -789,6 +835,7 @@ STR_AI_CONFIG_CHANGE_NONE : # Vehicle loading indicators # Income 'floats' +STR_INCOME_FLOAT_COST :{RED}मूल्य: {CURRENCY_LONG} # Saveload messages @@ -804,12 +851,14 @@ STR_AI_CONFIG_CHANGE_NONE : # Error message titles +STR_ERROR_MESSAGE_CAPTION :{YELLOW}सन्देश # Generic construction errors # Local authority errors # Levelling errors +STR_ERROR_TOO_HIGH :{WHITE}... बहुत ऊँचा # Company related errors @@ -858,6 +907,7 @@ STR_AI_CONFIG_CHANGE_NONE : # Generic vehicle errors +STR_ERROR_CAN_T_RENAME_SHIP :{WHITE}जहाज का नामकरण नहीं कर सकते... @@ -888,6 +938,8 @@ STR_AI_CONFIG_CHANGE_NONE : ##id 0x4800 # industry names +STR_INDUSTRY_NAME_POWER_STATION :बिजलीघर +STR_INDUSTRY_NAME_BANK_TROPIC_ARCTIC :बैंक ############ WARNING, using range 0x6000 for strings that are stored in the savegame ############ These strings may never get a new id, or savegames will break! @@ -895,6 +947,7 @@ STR_AI_CONFIG_CHANGE_NONE : STR_SV_EMPTY : STR_SV_STNAME :{STRING} +STR_SV_STNAME_AIRPORT :{STRING} हवाई अड्डा STR_SV_STNAME_BUOY :{STRING} STR_SV_STNAME_WAYPOINT :{STRING} ##id 0x6020 @@ -902,6 +955,15 @@ STR_SV_STNAME_WAYPOINT :{STRING} ##id 0x8000 # Vehicle names +STR_VEHICLE_NAME_TRAIN_WAGON_RAIL_COAL_CAR :कोयला वाहन +STR_VEHICLE_NAME_TRAIN_WAGON_RAIL_FRUIT_TRUCK :फल वाहन +STR_VEHICLE_NAME_TRAIN_WAGON_MAGLEV_BUBBLE_VAN :बबल वैन +STR_VEHICLE_NAME_ROAD_VEHICLE_PLODDYPHUT_MKIII_BUS :प्लॉडीपीहट एमके३ बस +STR_VEHICLE_NAME_AIRCRAFT_BAKEWELL_COTSWALD_LB_3 :बेकवेल कॉट्सवॉल्ड एलबी-३ +STR_VEHICLE_NAME_AIRCRAFT_BAKEWELL_LUCKETT_LB_9 :बेकवेल लकेट एलबी-९ +STR_VEHICLE_NAME_AIRCRAFT_BAKEWELL_LUCKETT_LB80 :बेकवेल लकेट एलबी८० +STR_VEHICLE_NAME_AIRCRAFT_BAKEWELL_LUCKETT_LB_11 :बेकवेल लकेट एलबी-११ +STR_VEHICLE_NAME_AIRCRAFT_DARWIN_600 :डार्विन ‌६०० ##id 0x8800 # Formatting of some strings diff --git a/src/lang/korean.txt b/src/lang/korean.txt index 226d10c89a..3495651e76 100644 --- a/src/lang/korean.txt +++ b/src/lang/korean.txt @@ -994,6 +994,7 @@ STR_GAME_OPTIONS_AUTOSAVE_DROPDOWN_EVERY_12_MONTHS :12개월마다 STR_GAME_OPTIONS_LANGUAGE :{BLACK}언어 STR_GAME_OPTIONS_LANGUAGE_TOOLTIP :{BLACK}사용할 언어를 선택하세요 +STR_GAME_OPTIONS_LANGUAGE_PERCENTAGE :{STRING} ({NUM}% 번역됨) STR_GAME_OPTIONS_FULLSCREEN :{BLACK}전체화면 STR_GAME_OPTIONS_FULLSCREEN_TOOLTIP :{BLACK}OpenTTD를 전체화면으로 플레이하려면 클릭하세요. @@ -1265,7 +1266,7 @@ STR_CONFIG_SETTING_MULTIPINDTOWN_HELPTEXT :일반적으로 STR_CONFIG_SETTING_SIGNALSIDE :신호기 보이기: {STRING} STR_CONFIG_SETTING_SIGNALSIDE_HELPTEXT :선로의 어느 쪽에 신호기를 설치할 지 선택합니다. STR_CONFIG_SETTING_SIGNALSIDE_LEFT :왼쪽에 -STR_CONFIG_SETTING_SIGNALSIDE_DRIVING_SIDE :진행 방향에 +STR_CONFIG_SETTING_SIGNALSIDE_DRIVING_SIDE :자동차 통행 방향에 STR_CONFIG_SETTING_SIGNALSIDE_RIGHT :오른쪽에 STR_CONFIG_SETTING_SHOWFINANCES :연말에 자동으로 재정 창을 띄우기: {STRING} STR_CONFIG_SETTING_SHOWFINANCES_HELPTEXT :이 설정을 켜면. 회사의 재정 상태를 확인하기 쉽도록 매년 말에 재정 창이 자동으로 뜹니다. diff --git a/src/lang/polish.txt b/src/lang/polish.txt index 81a477fbbb..d2d888b189 100644 --- a/src/lang/polish.txt +++ b/src/lang/polish.txt @@ -1373,6 +1373,7 @@ STR_GAME_OPTIONS_AUTOSAVE_DROPDOWN_EVERY_12_MONTHS :Co 12 miesięcy STR_GAME_OPTIONS_LANGUAGE :{BLACK}Język STR_GAME_OPTIONS_LANGUAGE_TOOLTIP :{BLACK}Wybierz język interfejsu +STR_GAME_OPTIONS_LANGUAGE_PERCENTAGE :{STRING} (ukończono {NUM}%) STR_GAME_OPTIONS_FULLSCREEN :{BLACK}Pełny ekran STR_GAME_OPTIONS_FULLSCREEN_TOOLTIP :{BLACK}Zaznacz, jeśli chcesz grać w OpenTTD w trybie pełnoekranowym diff --git a/src/lang/russian.txt b/src/lang/russian.txt index b8c303733e..df060c6a7c 100644 --- a/src/lang/russian.txt +++ b/src/lang/russian.txt @@ -1138,6 +1138,7 @@ STR_GAME_OPTIONS_AUTOSAVE_DROPDOWN_EVERY_12_MONTHS :Каждый г STR_GAME_OPTIONS_LANGUAGE :{BLACK}Язык STR_GAME_OPTIONS_LANGUAGE_TOOLTIP :{BLACK}Язык пользовательского интерфейса +STR_GAME_OPTIONS_LANGUAGE_PERCENTAGE :{STRING} ({NUM}% перевед{P ён ено ено}) STR_GAME_OPTIONS_FULLSCREEN :{BLACK}Полноэкранный режим STR_GAME_OPTIONS_FULLSCREEN_TOOLTIP :{BLACK}Включить/выключить полноэкранный режим @@ -1148,11 +1149,11 @@ STR_GAME_OPTIONS_RESOLUTION_OTHER :Другое STR_GAME_OPTIONS_RESOLUTION_ITEM :{NUM}x{NUM} STR_GAME_OPTIONS_VIDEO_ACCELERATION :{BLACK}Аппаратное ускорение -STR_GAME_OPTIONS_VIDEO_ACCELERATION_TOOLTIP :{BLACK}Нажмите здесь, чтобы включить/выключить аппаратное ускорение в OpenTTD. После этого игру потребуется перезапустить. +STR_GAME_OPTIONS_VIDEO_ACCELERATION_TOOLTIP :{BLACK}Включить/выключить аппаратное ускорение. После этого игру потребуется перезапустить. STR_GAME_OPTIONS_VIDEO_ACCELERATION_RESTART :{WHITE}Эта настройка будет применена только после перезапуска игры STR_GAME_OPTIONS_VIDEO_VSYNC :{BLACK}Вертикальная синхронизация -STR_GAME_OPTIONS_VIDEO_VSYNC_TOOLTIP :{BLACK}Нажмите здесь, чтобы включить/выключить вертикальную синхронизацию. После этого игру потребуется перезапустить. Работает только при включённом аппаратном ускорении. +STR_GAME_OPTIONS_VIDEO_VSYNC_TOOLTIP :{BLACK}Включить/выключить вертикальную синхронизацию. После этого игру потребуется перезапустить. Работает только при включённом аппаратном ускорении. STR_GAME_OPTIONS_GUI_ZOOM_FRAME :{BLACK}Размер элементов интерфейса STR_GAME_OPTIONS_GUI_ZOOM_DROPDOWN_TOOLTIP :{BLACK}Выберите размер элементов интерфейса diff --git a/src/lang/spanish.txt b/src/lang/spanish.txt index 32625a802e..3d1a705163 100644 --- a/src/lang/spanish.txt +++ b/src/lang/spanish.txt @@ -994,6 +994,7 @@ STR_GAME_OPTIONS_AUTOSAVE_DROPDOWN_EVERY_12_MONTHS :Cada 12 meses STR_GAME_OPTIONS_LANGUAGE :{BLACK}Idioma STR_GAME_OPTIONS_LANGUAGE_TOOLTIP :{BLACK}Selecciona el idioma a emplear en interfaz del juego +STR_GAME_OPTIONS_LANGUAGE_PERCENTAGE :{STRING} ({NUM}% completo) STR_GAME_OPTIONS_FULLSCREEN :{BLACK}Pantalla completa STR_GAME_OPTIONS_FULLSCREEN_TOOLTIP :{BLACK}Marca esta opción para jugar OpenTTD a pantalla completa From c545cc9d7039a89e23de160b1c93adc959eabda5 Mon Sep 17 00:00:00 2001 From: Rubidium Date: Sun, 18 Apr 2021 09:01:27 +0200 Subject: [PATCH 137/268] Codechange: move more logic about packet size validity and reading into Packet --- src/network/core/packet.cpp | 25 +++++++++++++++++++++---- src/network/core/packet.h | 3 ++- src/network/core/tcp.cpp | 10 ++++------ src/network/core/udp.cpp | 4 ++-- 4 files changed, 29 insertions(+), 13 deletions(-) diff --git a/src/network/core/packet.cpp b/src/network/core/packet.cpp index 94ffcc5584..4eb0e929ee 100644 --- a/src/network/core/packet.cpp +++ b/src/network/core/packet.cpp @@ -181,13 +181,32 @@ bool Packet::CanReadFromPacket(uint bytes_to_read) } /** - * Reads the packet size from the raw packet and stores it in the packet->size + * Check whether the packet, given the position of the "write" pointer, has read + * enough of the packet to contain its size. + * @return True iff there is enough data in the packet to contain the packet's size. */ -void Packet::ReadRawPacketSize() +bool Packet::HasPacketSizeData() const +{ + return this->pos >= sizeof(PacketSize); +} + +/** + * Reads the packet size from the raw packet and stores it in the packet->size + * @return True iff the packet size seems plausible. + */ +bool Packet::ParsePacketSize() { assert(this->cs != nullptr && this->next == nullptr); this->size = (PacketSize)this->buffer[0]; this->size += (PacketSize)this->buffer[1] << 8; + + /* If the size of the packet is less than the bytes required for the size and type of + * the packet, or more than the allowed limit, then something is wrong with the packet. + * In those cases the packet can generally be regarded as containing garbage data. */ + if (this->size < sizeof(PacketSize) + sizeof(PacketType) || this->size > SEND_MTU) return false; + + this->pos = sizeof(PacketSize); + return true; } /** @@ -195,8 +214,6 @@ void Packet::ReadRawPacketSize() */ void Packet::PrepareToRead() { - this->ReadRawPacketSize(); - /* Put the position on the right place */ this->pos = sizeof(PacketSize); } diff --git a/src/network/core/packet.h b/src/network/core/packet.h index c9be4eeb53..6e5c5509ce 100644 --- a/src/network/core/packet.h +++ b/src/network/core/packet.h @@ -71,7 +71,8 @@ public: void Send_string(const char *data); /* Reading/receiving of packets */ - void ReadRawPacketSize(); + bool HasPacketSizeData() const; + bool ParsePacketSize(); void PrepareToRead(); bool CanReadFromPacket (uint bytes_to_read); diff --git a/src/network/core/tcp.cpp b/src/network/core/tcp.cpp index a51913d843..1461a92981 100644 --- a/src/network/core/tcp.cpp +++ b/src/network/core/tcp.cpp @@ -155,8 +155,8 @@ Packet *NetworkTCPSocketHandler::ReceivePacket() Packet *p = this->packet_recv; /* Read packet size */ - if (p->pos < sizeof(PacketSize)) { - while (p->pos < sizeof(PacketSize)) { + if (!p->HasPacketSizeData()) { + while (!p->HasPacketSizeData()) { /* Read the size of the packet */ res = recv(this->sock, (char*)p->buffer + p->pos, sizeof(PacketSize) - p->pos, 0); if (res == -1) { @@ -178,10 +178,8 @@ Packet *NetworkTCPSocketHandler::ReceivePacket() p->pos += res; } - /* Read the packet size from the received packet */ - p->ReadRawPacketSize(); - - if (p->size > SEND_MTU) { + /* Parse the size in the received packet and if not valid, close the connection. */ + if (!p->ParsePacketSize()) { this->CloseConnection(); return nullptr; } diff --git a/src/network/core/udp.cpp b/src/network/core/udp.cpp index 72fec49e1e..614ae8c3ba 100644 --- a/src/network/core/udp.cpp +++ b/src/network/core/udp.cpp @@ -134,14 +134,14 @@ void NetworkUDPSocketHandler::ReceivePackets() #endif NetworkAddress address(client_addr, client_len); - p.PrepareToRead(); /* If the size does not match the packet must be corrupted. * Otherwise it will be marked as corrupted later on. */ - if (nbytes != p.size) { + if (!p.ParsePacketSize() || nbytes != p.size) { DEBUG(net, 1, "received a packet with mismatching size from %s", address.GetAddressAsString().c_str()); continue; } + p.PrepareToRead(); /* Handle the packet */ this->HandleUDPPacket(&p, &address); From a2051bad503618f37e941aca3e4a5d53af1b0fbe Mon Sep 17 00:00:00 2001 From: Rubidium Date: Sun, 18 Apr 2021 09:26:06 +0200 Subject: [PATCH 138/268] Codechange: move logic whether there is enough space in a packet to write data into the Packet --- src/network/core/packet.cpp | 41 ++++++++++++++++++++++------------ src/network/core/packet.h | 3 ++- src/network/network_admin.cpp | 2 +- src/network/network_client.cpp | 2 +- src/network/network_udp.cpp | 12 +++++----- 5 files changed, 37 insertions(+), 23 deletions(-) diff --git a/src/network/core/packet.cpp b/src/network/core/packet.cpp index 4eb0e929ee..54f5a79e16 100644 --- a/src/network/core/packet.cpp +++ b/src/network/core/packet.cpp @@ -68,6 +68,16 @@ void Packet::PrepareToSend() this->pos = 0; // We start reading from here } +/** + * Is it safe to write to the packet, i.e. didn't we run over the buffer? + * @param bytes_to_write The amount of bytes we want to try to write. + * @return True iff the given amount of bytes can be written to the packet. + */ +bool Packet::CanWriteToPacket(size_t bytes_to_write) +{ + return this->size + bytes_to_write < SEND_MTU; +} + /* * The next couple of functions make sure we can send * uint8, uint16, uint32 and uint64 endian-safe @@ -95,7 +105,7 @@ void Packet::Send_bool(bool data) */ void Packet::Send_uint8(uint8 data) { - assert(this->size < SEND_MTU - sizeof(data)); + assert(this->CanWriteToPacket(sizeof(data))); this->buffer[this->size++] = data; } @@ -105,7 +115,7 @@ void Packet::Send_uint8(uint8 data) */ void Packet::Send_uint16(uint16 data) { - assert(this->size < SEND_MTU - sizeof(data)); + assert(this->CanWriteToPacket(sizeof(data))); this->buffer[this->size++] = GB(data, 0, 8); this->buffer[this->size++] = GB(data, 8, 8); } @@ -116,7 +126,7 @@ void Packet::Send_uint16(uint16 data) */ void Packet::Send_uint32(uint32 data) { - assert(this->size < SEND_MTU - sizeof(data)); + assert(this->CanWriteToPacket(sizeof(data))); this->buffer[this->size++] = GB(data, 0, 8); this->buffer[this->size++] = GB(data, 8, 8); this->buffer[this->size++] = GB(data, 16, 8); @@ -129,7 +139,7 @@ void Packet::Send_uint32(uint32 data) */ void Packet::Send_uint64(uint64 data) { - assert(this->size < SEND_MTU - sizeof(data)); + assert(this->CanWriteToPacket(sizeof(data))); this->buffer[this->size++] = GB(data, 0, 8); this->buffer[this->size++] = GB(data, 8, 8); this->buffer[this->size++] = GB(data, 16, 8); @@ -148,8 +158,8 @@ void Packet::Send_uint64(uint64 data) void Packet::Send_string(const char *data) { assert(data != nullptr); - /* The <= *is* valid due to the fact that we are comparing sizes and not the index. */ - assert(this->size + strlen(data) + 1 <= SEND_MTU); + /* Length of the string + 1 for the '\0' termination. */ + assert(this->CanWriteToPacket(strlen(data) + 1)); while ((this->buffer[this->size++] = *data++) != '\0') {} } @@ -162,18 +172,21 @@ void Packet::Send_string(const char *data) /** - * Is it safe to read from the packet, i.e. didn't we run over the buffer ? - * @param bytes_to_read The amount of bytes we want to try to read. + * Is it safe to read from the packet, i.e. didn't we run over the buffer? + * In case \c close_connection is true, the connection will be closed when one would + * overrun the buffer. When it is false, the connection remains untouched. + * @param bytes_to_read The amount of bytes we want to try to read. + * @param close_connection Whether to close the connection if one cannot read that amount. * @return True if that is safe, otherwise false. */ -bool Packet::CanReadFromPacket(uint bytes_to_read) +bool Packet::CanReadFromPacket(size_t bytes_to_read, bool close_connection) { /* Don't allow reading from a quit client/client who send bad data */ if (this->cs->HasClientQuit()) return false; /* Check if variable is within packet-size */ if (this->pos + bytes_to_read > this->size) { - this->cs->NetworkSocketHandler::CloseConnection(); + if (close_connection) this->cs->NetworkSocketHandler::CloseConnection(); return false; } @@ -235,7 +248,7 @@ uint8 Packet::Recv_uint8() { uint8 n; - if (!this->CanReadFromPacket(sizeof(n))) return 0; + if (!this->CanReadFromPacket(sizeof(n), true)) return 0; n = this->buffer[this->pos++]; return n; @@ -249,7 +262,7 @@ uint16 Packet::Recv_uint16() { uint16 n; - if (!this->CanReadFromPacket(sizeof(n))) return 0; + if (!this->CanReadFromPacket(sizeof(n), true)) return 0; n = (uint16)this->buffer[this->pos++]; n += (uint16)this->buffer[this->pos++] << 8; @@ -264,7 +277,7 @@ uint32 Packet::Recv_uint32() { uint32 n; - if (!this->CanReadFromPacket(sizeof(n))) return 0; + if (!this->CanReadFromPacket(sizeof(n), true)) return 0; n = (uint32)this->buffer[this->pos++]; n += (uint32)this->buffer[this->pos++] << 8; @@ -281,7 +294,7 @@ uint64 Packet::Recv_uint64() { uint64 n; - if (!this->CanReadFromPacket(sizeof(n))) return 0; + if (!this->CanReadFromPacket(sizeof(n), true)) return 0; n = (uint64)this->buffer[this->pos++]; n += (uint64)this->buffer[this->pos++] << 8; diff --git a/src/network/core/packet.h b/src/network/core/packet.h index 6e5c5509ce..901d3f593b 100644 --- a/src/network/core/packet.h +++ b/src/network/core/packet.h @@ -63,6 +63,7 @@ public: /* Sending/writing of packets */ void PrepareToSend(); + bool CanWriteToPacket(size_t bytes_to_write); void Send_bool (bool data); void Send_uint8 (uint8 data); void Send_uint16(uint16 data); @@ -75,7 +76,7 @@ public: bool ParsePacketSize(); void PrepareToRead(); - bool CanReadFromPacket (uint bytes_to_read); + bool CanReadFromPacket(size_t bytes_to_read, bool close_connection = false); bool Recv_bool (); uint8 Recv_uint8 (); uint16 Recv_uint16(); diff --git a/src/network/network_admin.cpp b/src/network/network_admin.cpp index fa97b7e578..057ad59883 100644 --- a/src/network/network_admin.cpp +++ b/src/network/network_admin.cpp @@ -613,7 +613,7 @@ NetworkRecvStatus ServerNetworkAdminSocketHandler::SendCmdNames() /* Should SEND_MTU be exceeded, start a new packet * (magic 5: 1 bool "more data" and one uint16 "command id", one * byte for string '\0' termination and 1 bool "no more data" */ - if (p->size + strlen(cmdname) + 5 >= SEND_MTU) { + if (p->CanWriteToPacket(strlen(cmdname) + 5)) { p->Send_bool(false); this->SendPacket(p); diff --git a/src/network/network_client.cpp b/src/network/network_client.cpp index 72f69f99f7..10b4fd1411 100644 --- a/src/network/network_client.cpp +++ b/src/network/network_client.cpp @@ -933,7 +933,7 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_FRAME(Packet *p } #endif /* Receive the token. */ - if (p->pos != p->size) this->token = p->Recv_uint8(); + if (p->CanReadFromPacket(sizeof(uint8))) this->token = p->Recv_uint8(); DEBUG(net, 5, "Received FRAME %d", _frame_counter_server); diff --git a/src/network/network_udp.cpp b/src/network/network_udp.cpp index 46a21fc87d..aa34515bdd 100644 --- a/src/network/network_udp.cpp +++ b/src/network/network_udp.cpp @@ -220,23 +220,23 @@ void ServerNetworkUDPSocketHandler::Receive_CLIENT_DETAIL_INFO(Packet *p, Networ static const uint MIN_CI_SIZE = 54; uint max_cname_length = NETWORK_COMPANY_NAME_LENGTH; - if (Company::GetNumItems() * (MIN_CI_SIZE + NETWORK_COMPANY_NAME_LENGTH) >= (uint)SEND_MTU - packet.size) { + if (!packet.CanWriteToPacket(Company::GetNumItems() * (MIN_CI_SIZE + NETWORK_COMPANY_NAME_LENGTH))) { /* Assume we can at least put the company information in the packets. */ - assert(Company::GetNumItems() * MIN_CI_SIZE < (uint)SEND_MTU - packet.size); + assert(packet.CanWriteToPacket(Company::GetNumItems() * MIN_CI_SIZE)); /* At this moment the company names might not fit in the * packet. Check whether that is really the case. */ for (;;) { - int free = SEND_MTU - packet.size; + size_t required = 0; for (const Company *company : Company::Iterate()) { char company_name[NETWORK_COMPANY_NAME_LENGTH]; SetDParam(0, company->index); GetString(company_name, STR_COMPANY_NAME, company_name + max_cname_length - 1); - free -= MIN_CI_SIZE; - free -= (int)strlen(company_name); + required += MIN_CI_SIZE; + required += strlen(company_name); } - if (free >= 0) break; + if (packet.CanWriteToPacket(required)) break; /* Try again, with slightly shorter strings. */ assert(max_cname_length > 0); From 98aa561cf759f75971afd5dfc4d42e6921a8ab1a Mon Sep 17 00:00:00 2001 From: Rubidium Date: Sun, 18 Apr 2021 09:55:00 +0200 Subject: [PATCH 139/268] Codechange: encapsulate reading data from sockets into Packets to prevent packet state modifications outside of the Packet --- src/network/core/packet.cpp | 24 +++++++++++++++--- src/network/core/packet.h | 50 ++++++++++++++++++++++++++++++++++++- src/network/core/tcp.cpp | 12 +++------ src/network/core/udp.cpp | 4 +-- 4 files changed, 75 insertions(+), 15 deletions(-) diff --git a/src/network/core/packet.cpp b/src/network/core/packet.cpp index 54f5a79e16..5cb6716bca 100644 --- a/src/network/core/packet.cpp +++ b/src/network/core/packet.cpp @@ -17,17 +17,24 @@ #include "../../safeguards.h" /** - * Create a packet that is used to read from a network socket - * @param cs the socket handler associated with the socket we are reading from + * Create a packet that is used to read from a network socket. + * @param cs The socket handler associated with the socket we are reading from. + * @param initial_read_size The initial amount of data to transfer from the socket into the + * packet. This defaults to just the required bytes to determine the + * packet's size. That default is the wanted for streams such as TCP + * as you do not want to read data of the next packet yet. For UDP + * you need to read the whole packet at once otherwise you might + * loose some the data of the packet, so there you pass the maximum + * size for the packet you expect from the network. */ -Packet::Packet(NetworkSocketHandler *cs) +Packet::Packet(NetworkSocketHandler *cs, size_t initial_read_size) { assert(cs != nullptr); this->cs = cs; this->next = nullptr; this->pos = 0; // We start reading from here - this->size = 0; + this->size = static_cast(initial_read_size); this->buffer = MallocT(SEND_MTU); } @@ -336,3 +343,12 @@ void Packet::Recv_string(char *buffer, size_t size, StringValidationSettings set str_validate(bufp, last, 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. + */ +size_t Packet::RemainingBytesToTransfer() const +{ + return this->size - this->pos; +} diff --git a/src/network/core/packet.h b/src/network/core/packet.h index 901d3f593b..3eee0522ff 100644 --- a/src/network/core/packet.h +++ b/src/network/core/packet.h @@ -12,9 +12,11 @@ #ifndef NETWORK_CORE_PACKET_H #define NETWORK_CORE_PACKET_H +#include "os_abstraction.h" #include "config.h" #include "core.h" #include "../../string_type.h" +#include typedef uint16 PacketSize; ///< Size of the whole packet. typedef uint8 PacketType; ///< Identifier for the packet @@ -56,7 +58,7 @@ private: NetworkSocketHandler *cs; public: - Packet(NetworkSocketHandler *cs); + Packet(NetworkSocketHandler *cs, size_t initial_read_size = sizeof(PacketSize)); Packet(PacketType type); ~Packet(); @@ -83,6 +85,52 @@ public: uint32 Recv_uint32(); uint64 Recv_uint64(); void Recv_string(char *buffer, size_t size, StringValidationSettings settings = SVS_REPLACE_WITH_QUESTION_MARK); + + size_t RemainingBytesToTransfer() const; + + /** + * Transfer data from the given function into the packet. It starts writing at the + * position the last transfer stopped. + * + * Examples of functions that can be used to transfer data into a packet are TCP's + * recv and UDP's recvfrom functions. They will directly write their data into the + * packet without an intermediate buffer. + * Examples of functions that can be used to transfer data from a packet are TCP's + * send and UDP's sendto functions. They will directly read the data from the packet's + * buffer without an intermediate buffer. + * These are functions are special in a sense as even though the packet can send or + * receive an amount of data, those functions can say they only processed a smaller + * amount, so special handling is required to keep the position pointers correct. + * Most of these transfer functions are in the form function(source, buffer, amount, ...), + * so the template of this function will assume that as the base parameter order. + * + * This will attempt to write all the remaining bytes into the packet. It updates the + * position based on how many bytes were actually written by the called transfer_function. + * @param transfer_function The function to pass the buffer as second parameter and the + * amount to read as third parameter. It returns the amount that + * was read or -1 upon errors. + * @param source The first parameter of the transfer function. + * @param args The fourth and further parameters to the transfer function, if any. + * @tparam A The type for the amount to be passed, so it can be cast to the right type. + * @tparam F The type of the transfer_function. + * @tparam S The type of the source. + * @tparam Args The types of the remaining arguments to the function. + * @return The return value of the transfer_function. + */ + template + ssize_t TransferIn(F transfer_function, S source, Args&& ... args) + { + size_t amount = this->RemainingBytesToTransfer(); + if (amount == 0) return 0; + + assert(this->pos < this->buffer.size()); + assert(this->pos + amount <= this->buffer.size()); + /* Making buffer a char means casting a lot in the Recv/Send functions. */ + char *input_buffer = reinterpret_cast(this->buffer + this->pos); + ssize_t bytes = transfer_function(source, input_buffer, static_cast(amount), std::forward(args)...); + if (bytes > 0) this->pos += bytes; + return bytes; + } }; #endif /* NETWORK_CORE_PACKET_H */ diff --git a/src/network/core/tcp.cpp b/src/network/core/tcp.cpp index 1461a92981..aa1e1cbeda 100644 --- a/src/network/core/tcp.cpp +++ b/src/network/core/tcp.cpp @@ -156,9 +156,8 @@ Packet *NetworkTCPSocketHandler::ReceivePacket() /* Read packet size */ if (!p->HasPacketSizeData()) { - while (!p->HasPacketSizeData()) { - /* Read the size of the packet */ - res = recv(this->sock, (char*)p->buffer + p->pos, sizeof(PacketSize) - p->pos, 0); + while (p->RemainingBytesToTransfer() != 0) { + res = p->TransferIn(recv, this->sock, 0); if (res == -1) { int err = GET_LAST_ERROR(); if (err != EWOULDBLOCK) { @@ -175,7 +174,6 @@ Packet *NetworkTCPSocketHandler::ReceivePacket() this->CloseConnection(); return nullptr; } - p->pos += res; } /* Parse the size in the received packet and if not valid, close the connection. */ @@ -186,8 +184,8 @@ Packet *NetworkTCPSocketHandler::ReceivePacket() } /* Read rest of packet */ - while (p->pos < p->size) { - res = recv(this->sock, (char*)p->buffer + p->pos, p->size - p->pos, 0); + while (p->RemainingBytesToTransfer() != 0) { + res = p->TransferIn(recv, this->sock, 0); if (res == -1) { int err = GET_LAST_ERROR(); if (err != EWOULDBLOCK) { @@ -204,8 +202,6 @@ Packet *NetworkTCPSocketHandler::ReceivePacket() this->CloseConnection(); return nullptr; } - - p->pos += res; } /* Prepare for receiving a new packet */ diff --git a/src/network/core/udp.cpp b/src/network/core/udp.cpp index 614ae8c3ba..398f53142b 100644 --- a/src/network/core/udp.cpp +++ b/src/network/core/udp.cpp @@ -119,12 +119,12 @@ void NetworkUDPSocketHandler::ReceivePackets() struct sockaddr_storage client_addr; memset(&client_addr, 0, sizeof(client_addr)); - Packet p(this); + Packet p(this, SEND_MTU); socklen_t client_len = sizeof(client_addr); /* Try to receive anything */ SetNonBlocking(s.second); // Some OSes seem to lose the non-blocking status of the socket - int nbytes = recvfrom(s.second, (char*)p.buffer, SEND_MTU, 0, (struct sockaddr *)&client_addr, &client_len); + ssize_t nbytes = p.TransferIn(recvfrom, s.second, 0, (struct sockaddr *)&client_addr, &client_len); /* Did we get the bytes for the base header of the packet? */ if (nbytes <= 0) break; // No data, i.e. no packet From d4f027c03bfc5f632b7d63c125dfa57a7ba89926 Mon Sep 17 00:00:00 2001 From: Rubidium Date: Sun, 18 Apr 2021 10:23:41 +0200 Subject: [PATCH 140/268] Codechange: encapsulate writing data from Packets into sockets/files/buffers to prevent packet state modifications outside of the Packet --- src/network/core/packet.h | 52 +++++++++++++++++++++++++++++++++ src/network/core/tcp.cpp | 6 ++-- src/network/core/tcp_listen.h | 4 +-- src/network/core/udp.cpp | 2 +- src/network/network_client.cpp | 36 ++++++++++++----------- src/network/network_content.cpp | 16 ++++++++-- 6 files changed, 90 insertions(+), 26 deletions(-) diff --git a/src/network/core/packet.h b/src/network/core/packet.h index 3eee0522ff..b091d8a7ec 100644 --- a/src/network/core/packet.h +++ b/src/network/core/packet.h @@ -88,6 +88,58 @@ public: size_t RemainingBytesToTransfer() const; + /** + * Transfer data from the packet to the given function. It starts reading at the + * position the last transfer stopped. + * See Packet::TransferIn for more information about transferring data to functions. + * @param transfer_function The function to pass the buffer as second parameter and the + * amount to write as third parameter. It returns the amount that + * was written or -1 upon errors. + * @param limit The maximum amount of bytes to transfer. + * @param destination The first parameter of the transfer function. + * @param args The fourth and further parameters to the transfer function, if any. + * @return The return value of the transfer_function. + */ + template < + typename A = size_t, ///< The type for the amount to be passed, so it can be cast to the right type. + typename F, ///< The type of the function. + typename D, ///< The type of the destination. + typename ... Args> ///< The types of the remaining arguments to the function. + ssize_t TransferOutWithLimit(F transfer_function, size_t limit, D destination, Args&& ... args) + { + size_t amount = std::min(this->RemainingBytesToTransfer(), limit); + if (amount == 0) return 0; + + assert(this->pos < this->buffer.size()); + assert(this->pos + amount <= this->buffer.size()); + /* Making buffer a char means casting a lot in the Recv/Send functions. */ + const char *output_buffer = reinterpret_cast(this->buffer + this->pos); + ssize_t bytes = transfer_function(destination, output_buffer, static_cast(amount), std::forward(args)...); + if (bytes > 0) this->pos += bytes; + return bytes; + } + + /** + * Transfer data from the packet to the given function. It starts reading at the + * position the last transfer stopped. + * See Packet::TransferIn for more information about transferring data to functions. + * @param transfer_function The function to pass the buffer as second parameter and the + * amount to write as third parameter. It returns the amount that + * was written or -1 upon errors. + * @param destination The first parameter of the transfer function. + * @param args The fourth and further parameters to the transfer function, if any. + * @tparam A The type for the amount to be passed, so it can be cast to the right type. + * @tparam F The type of the transfer_function. + * @tparam D The type of the destination. + * @tparam Args The types of the remaining arguments to the function. + * @return The return value of the transfer_function. + */ + template + ssize_t TransferOut(F transfer_function, D destination, Args&& ... args) + { + return TransferOutWithLimit(transfer_function, std::numeric_limits::max(), destination, std::forward(args)...); + } + /** * Transfer data from the given function into the packet. It starts writing at the * position the last transfer stopped. diff --git a/src/network/core/tcp.cpp b/src/network/core/tcp.cpp index aa1e1cbeda..ab18f47a87 100644 --- a/src/network/core/tcp.cpp +++ b/src/network/core/tcp.cpp @@ -103,7 +103,7 @@ SendPacketsState NetworkTCPSocketHandler::SendPackets(bool closing_down) p = this->packet_queue; while (p != nullptr) { - res = send(this->sock, (const char*)p->buffer + p->pos, p->size - p->pos, 0); + res = p->TransferOut(send, this->sock, 0); if (res == -1) { int err = GET_LAST_ERROR(); if (err != EWOULDBLOCK) { @@ -122,10 +122,8 @@ SendPacketsState NetworkTCPSocketHandler::SendPackets(bool closing_down) return SPS_CLOSED; } - p->pos += res; - /* Is this packet sent? */ - if (p->pos == p->size) { + if (p->RemainingBytesToTransfer() == 0) { /* Go to the next packet */ this->packet_queue = p->next; delete p; diff --git a/src/network/core/tcp_listen.h b/src/network/core/tcp_listen.h index 1f073aa735..53a3d57cc9 100644 --- a/src/network/core/tcp_listen.h +++ b/src/network/core/tcp_listen.h @@ -63,7 +63,7 @@ public: DEBUG(net, 1, "[%s] Banned ip tried to join (%s), refused", Tsocket::GetName(), entry.c_str()); - if (send(s, (const char*)p.buffer, p.size, 0) < 0) { + if (p.TransferOut(send, s, 0) < 0) { DEBUG(net, 0, "send failed with error %d", GET_LAST_ERROR()); } closesocket(s); @@ -80,7 +80,7 @@ public: Packet p(Tfull_packet); p.PrepareToSend(); - if (send(s, (const char*)p.buffer, p.size, 0) < 0) { + if (p.TransferOut(send, s, 0) < 0) { DEBUG(net, 0, "send failed with error %d", GET_LAST_ERROR()); } closesocket(s); diff --git a/src/network/core/udp.cpp b/src/network/core/udp.cpp index 398f53142b..8e476f4e2b 100644 --- a/src/network/core/udp.cpp +++ b/src/network/core/udp.cpp @@ -99,7 +99,7 @@ void NetworkUDPSocketHandler::SendPacket(Packet *p, NetworkAddress *recv, bool a } /* Send the buffer */ - int res = sendto(s.second, (const char*)p->buffer, p->size, 0, (const struct sockaddr *)send.GetAddress(), send.GetAddressLength()); + ssize_t res = p->TransferOut(sendto, s.second, 0, (const struct sockaddr *)send.GetAddress(), send.GetAddressLength()); DEBUG(net, 7, "[udp] sendto(%s)", send.GetAddressAsString().c_str()); /* Check for any errors, but ignore it otherwise */ diff --git a/src/network/network_client.cpp b/src/network/network_client.cpp index 10b4fd1411..6156dc4863 100644 --- a/src/network/network_client.cpp +++ b/src/network/network_client.cpp @@ -35,7 +35,6 @@ /* This file handles all the client-commands */ - /** Read some packets, and when do use that data as initial load filter. */ struct PacketReader : LoadFilter { static const size_t CHUNK = 32 * 1024; ///< 32 KiB chunks of memory. @@ -59,35 +58,38 @@ struct PacketReader : LoadFilter { } } + /** + * Simple wrapper around fwrite to be able to pass it to Packet's TransferOut. + * @param destination The reader to add the data to. + * @param source The buffer to read data from. + * @param amount The number of bytes to copy. + * @return The number of bytes that were copied. + */ + static inline ssize_t TransferOutMemCopy(PacketReader *destination, const char *source, size_t amount) + { + memcpy(destination->buf, source, amount); + destination->buf += amount; + destination->written_bytes += amount; + return amount; + } + /** * Add a packet to this buffer. * @param p The packet to add. */ - void AddPacket(const Packet *p) + void AddPacket(Packet *p) { assert(this->read_bytes == 0); - - size_t in_packet = p->size - p->pos; - size_t to_write = std::min(this->bufe - this->buf, in_packet); - const byte *pbuf = p->buffer + p->pos; - - this->written_bytes += in_packet; - if (to_write != 0) { - memcpy(this->buf, pbuf, to_write); - this->buf += to_write; - } + p->TransferOutWithLimit(TransferOutMemCopy, this->bufe - this->buf, this); /* Did everything fit in the current chunk, then we're done. */ - if (to_write == in_packet) return; + if (p->RemainingBytesToTransfer() == 0) return; /* Allocate a new chunk and add the remaining data. */ - pbuf += to_write; - to_write = in_packet - to_write; this->blocks.push_back(this->buf = CallocT(CHUNK)); this->bufe = this->buf + CHUNK; - memcpy(this->buf, pbuf, to_write); - this->buf += to_write; + p->TransferOutWithLimit(TransferOutMemCopy, this->bufe - this->buf, this); } size_t Read(byte *rbuf, size_t size) override diff --git a/src/network/network_content.cpp b/src/network/network_content.cpp index 0220f890b3..5292252354 100644 --- a/src/network/network_content.cpp +++ b/src/network/network_content.cpp @@ -459,6 +459,18 @@ static bool GunzipFile(const ContentInfo *ci) #endif /* defined(WITH_ZLIB) */ } +/** + * Simple wrapper around fwrite to be able to pass it to Packet's TransferOut. + * @param file The file to write data to. + * @param buffer The buffer to write to the file. + * @param amount The number of bytes to write. + * @return The number of bytes that were written. + */ +static inline ssize_t TransferOutFWrite(FILE *file, const char *buffer, size_t amount) +{ + return fwrite(buffer, 1, amount, file); +} + bool ClientNetworkContentSocketHandler::Receive_SERVER_CONTENT(Packet *p) { if (this->curFile == nullptr) { @@ -476,8 +488,8 @@ bool ClientNetworkContentSocketHandler::Receive_SERVER_CONTENT(Packet *p) } } else { /* We have a file opened, thus are downloading internal content */ - size_t toRead = (size_t)(p->size - p->pos); - if (fwrite(p->buffer + p->pos, 1, toRead, this->curFile) != toRead) { + size_t toRead = p->RemainingBytesToTransfer(); + if (toRead != 0 && (size_t)p->TransferOut(TransferOutFWrite, this->curFile) != toRead) { DeleteWindowById(WC_NETWORK_STATUS_WINDOW, WN_NETWORK_STATUS_WINDOW_CONTENT_DOWNLOAD); ShowErrorMessage(STR_CONTENT_ERROR_COULD_NOT_DOWNLOAD, STR_CONTENT_ERROR_COULD_NOT_DOWNLOAD_FILE_NOT_WRITABLE, WL_ERROR); this->Close(); From 38d15fc9b788e2c904705d2ba8de4d5f1ff7988d Mon Sep 17 00:00:00 2001 From: Rubidium Date: Sun, 18 Apr 2021 10:27:04 +0200 Subject: [PATCH 141/268] Codechange: move the logic shrinking of the packets into the Packet itself --- src/network/core/packet.cpp | 5 +++++ src/network/core/tcp.cpp | 5 ----- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/network/core/packet.cpp b/src/network/core/packet.cpp index 5cb6716bca..6e6bb51c0a 100644 --- a/src/network/core/packet.cpp +++ b/src/network/core/packet.cpp @@ -73,6 +73,11 @@ void Packet::PrepareToSend() this->buffer[1] = GB(this->size, 8, 8); this->pos = 0; // We start reading from here + + /* Reallocate the packet as in 99+% of the times we send at most 25 bytes and + * keeping the other 1400+ bytes wastes memory, especially when someone tries + * to do a denial of service attack! */ + this->buffer = ReallocT(this->buffer, this->size); } /** diff --git a/src/network/core/tcp.cpp b/src/network/core/tcp.cpp index ab18f47a87..c779beb966 100644 --- a/src/network/core/tcp.cpp +++ b/src/network/core/tcp.cpp @@ -65,11 +65,6 @@ void NetworkTCPSocketHandler::SendPacket(Packet *packet) packet->PrepareToSend(); - /* Reallocate the packet as in 99+% of the times we send at most 25 bytes and - * keeping the other 1400+ bytes wastes memory, especially when someone tries - * to do a denial of service attack! */ - packet->buffer = ReallocT(packet->buffer, packet->size); - /* Locate last packet buffered for the client */ p = this->packet_queue; if (p == nullptr) { From 6f161f655942f2ca0091a75cdab8e3260e31bb5f Mon Sep 17 00:00:00 2001 From: Rubidium Date: Sun, 18 Apr 2021 10:49:12 +0200 Subject: [PATCH 142/268] Codechange: encapsulate the logic about how many bytes can be sent from a buffer in to a Packet --- src/network/core/packet.cpp | 15 +++++++++++++++ src/network/core/packet.h | 15 ++++++++------- src/network/network_server.cpp | 8 +++----- 3 files changed, 26 insertions(+), 12 deletions(-) diff --git a/src/network/core/packet.cpp b/src/network/core/packet.cpp index 6e6bb51c0a..c033aec98a 100644 --- a/src/network/core/packet.cpp +++ b/src/network/core/packet.cpp @@ -175,6 +175,21 @@ void Packet::Send_string(const char *data) while ((this->buffer[this->size++] = *data++) != '\0') {} } +/** + * Send as many of the bytes as possible in the packet. This can mean + * that it is possible that not all bytes are sent. To cope with this + * the function returns the amount of bytes that were actually sent. + * @param begin The begin of the buffer to send. + * @param end The end of the buffer to send. + * @return The number of bytes that were added to this packet. + */ +size_t Packet::Send_bytes(const byte *begin, const byte *end) +{ + size_t amount = std::min(end - begin, SEND_MTU - this->size); + memcpy(this->buffer + this->size, begin, amount); + this->size += static_cast(amount); + return amount; +} /* * Receiving commands diff --git a/src/network/core/packet.h b/src/network/core/packet.h index b091d8a7ec..4eb4703c19 100644 --- a/src/network/core/packet.h +++ b/src/network/core/packet.h @@ -65,13 +65,14 @@ public: /* Sending/writing of packets */ void PrepareToSend(); - bool CanWriteToPacket(size_t bytes_to_write); - void Send_bool (bool data); - void Send_uint8 (uint8 data); - void Send_uint16(uint16 data); - void Send_uint32(uint32 data); - void Send_uint64(uint64 data); - void Send_string(const char *data); + bool CanWriteToPacket(size_t bytes_to_write); + void Send_bool (bool data); + void Send_uint8 (uint8 data); + void Send_uint16(uint16 data); + void Send_uint32(uint32 data); + void Send_uint64(uint64 data); + void Send_string(const char *data); + size_t Send_bytes (const byte *begin, const byte *end); /* Reading/receiving of packets */ bool HasPacketSizeData() const; diff --git a/src/network/network_server.cpp b/src/network/network_server.cpp index fbde713eb4..9b77a57afe 100644 --- a/src/network/network_server.cpp +++ b/src/network/network_server.cpp @@ -174,12 +174,10 @@ struct PacketWriter : SaveFilter { byte *bufe = buf + size; while (buf != bufe) { - size_t to_write = std::min(SEND_MTU - this->current->size, bufe - buf); - memcpy(this->current->buffer + this->current->size, buf, to_write); - this->current->size += (PacketSize)to_write; - buf += to_write; + size_t written = this->current->Send_bytes(buf, bufe); + buf += written; - if (this->current->size == SEND_MTU) { + if (!this->current->CanWriteToPacket(1)) { this->AppendQueue(); if (buf != bufe) this->current = new Packet(PACKET_SERVER_MAP_DATA); } From f71fb0f54af443cee37d3c41a0d4f39a24617741 Mon Sep 17 00:00:00 2001 From: Rubidium Date: Sun, 18 Apr 2021 12:29:34 +0200 Subject: [PATCH 143/268] Codechange: encapsulate reading the size of a Packet --- src/network/core/packet.cpp | 12 ++++++++++++ src/network/core/packet.h | 1 + src/network/core/udp.cpp | 2 +- src/network/network_server.cpp | 4 ++-- src/network/network_server.h | 2 +- 5 files changed, 17 insertions(+), 4 deletions(-) diff --git a/src/network/core/packet.cpp b/src/network/core/packet.cpp index c033aec98a..e32b7fad81 100644 --- a/src/network/core/packet.cpp +++ b/src/network/core/packet.cpp @@ -230,6 +230,18 @@ bool Packet::HasPacketSizeData() const return this->pos >= sizeof(PacketSize); } +/** + * Get the number of bytes in the packet. + * When sending a packet this is the size of the data up to that moment. + * When receiving a packet (before PrepareToRead) this is the allocated size for the data to be read. + * When reading a packet (after PrepareToRead) this is the full size of the packet. + * @return The packet's size. + */ +size_t Packet::Size() const +{ + return this->size; +} + /** * Reads the packet size from the raw packet and stores it in the packet->size * @return True iff the packet size seems plausible. diff --git a/src/network/core/packet.h b/src/network/core/packet.h index 4eb4703c19..b6a7ff5d31 100644 --- a/src/network/core/packet.h +++ b/src/network/core/packet.h @@ -77,6 +77,7 @@ public: /* Reading/receiving of packets */ bool HasPacketSizeData() const; bool ParsePacketSize(); + size_t Size() const; void PrepareToRead(); bool CanReadFromPacket(size_t bytes_to_read, bool close_connection = false); diff --git a/src/network/core/udp.cpp b/src/network/core/udp.cpp index 8e476f4e2b..3bd2151fe1 100644 --- a/src/network/core/udp.cpp +++ b/src/network/core/udp.cpp @@ -137,7 +137,7 @@ void NetworkUDPSocketHandler::ReceivePackets() /* If the size does not match the packet must be corrupted. * Otherwise it will be marked as corrupted later on. */ - if (!p.ParsePacketSize() || nbytes != p.size) { + if (!p.ParsePacketSize() || (size_t)nbytes != p.Size()) { DEBUG(net, 1, "received a packet with mismatching size from %s", address.GetAddressAsString().c_str()); continue; } diff --git a/src/network/network_server.cpp b/src/network/network_server.cpp index 9b77a57afe..5301bd084c 100644 --- a/src/network/network_server.cpp +++ b/src/network/network_server.cpp @@ -247,7 +247,7 @@ Packet *ServerNetworkGameSocketHandler::ReceivePacket() /* We can receive a packet, so try that and if needed account for * the amount of received data. */ Packet *p = this->NetworkTCPSocketHandler::ReceivePacket(); - if (p != nullptr) this->receive_limit -= p->size; + if (p != nullptr) this->receive_limit -= p->Size(); return p; } @@ -1832,7 +1832,7 @@ void NetworkServer_Tick(bool send_frame) for (NetworkClientSocket *cs : NetworkClientSocket::Iterate()) { /* We allow a number of bytes per frame, but only to the burst amount * to be available for packet receiving at any particular time. */ - cs->receive_limit = std::min(cs->receive_limit + _settings_client.network.bytes_per_frame, + cs->receive_limit = std::min(cs->receive_limit + _settings_client.network.bytes_per_frame, _settings_client.network.bytes_per_frame_burst); /* Check if the speed of the client is what we can expect from a client */ diff --git a/src/network/network_server.h b/src/network/network_server.h index 77612fdc8c..4f6033fab1 100644 --- a/src/network/network_server.h +++ b/src/network/network_server.h @@ -67,7 +67,7 @@ public: uint32 last_token_frame; ///< The last frame we received the right token ClientStatus status; ///< Status of this client CommandQueue outgoing_queue; ///< The command-queue awaiting delivery - int receive_limit; ///< Amount of bytes that we can receive at this moment + size_t receive_limit; ///< Amount of bytes that we can receive at this moment struct PacketWriter *savegame; ///< Writer used to write the savegame. NetworkAddress client_address; ///< IP-address of the client (so he can be banned) From 3abefdf56190ef55d8680acb1aeab9f1b2fc8108 Mon Sep 17 00:00:00 2001 From: Rubidium Date: Tue, 20 Apr 2021 18:50:46 +0200 Subject: [PATCH 144/268] Codechange: remove public access to the next pointer in Packet --- src/network/core/packet.cpp | 26 ++++++++++++++++++++++++++ src/network/core/packet.h | 3 +++ src/network/core/tcp.cpp | 24 ++++-------------------- src/network/network_server.cpp | 20 +++++--------------- 4 files changed, 38 insertions(+), 35 deletions(-) diff --git a/src/network/core/packet.cpp b/src/network/core/packet.cpp index e32b7fad81..9e9ce69012 100644 --- a/src/network/core/packet.cpp +++ b/src/network/core/packet.cpp @@ -62,6 +62,32 @@ Packet::~Packet() free(this->buffer); } +/** + * Add the given Packet to the end of the queue of packets. + * @param queue The pointer to the begin of the queue. + * @param packet The packet to append to the queue. + */ +/* static */ void Packet::AddToQueue(Packet **queue, Packet *packet) +{ + while (*queue != nullptr) queue = &(*queue)->next; + *queue = packet; +} + +/** + * Pop the packet from the begin of the queue and set the + * begin of the queue to the second element in the queue. + * @param queue The pointer to the begin of the queue. + * @return The Packet that used to be a the begin of the queue. + */ +/* static */ Packet *Packet::PopFromQueue(Packet **queue) +{ + Packet *p = *queue; + *queue = p->next; + p->next = nullptr; + return p; +} + + /** * Writes the packet size from the raw packet from packet->size */ diff --git a/src/network/core/packet.h b/src/network/core/packet.h index b6a7ff5d31..d7ab7fee63 100644 --- a/src/network/core/packet.h +++ b/src/network/core/packet.h @@ -62,6 +62,9 @@ public: Packet(PacketType type); ~Packet(); + static void AddToQueue(Packet **queue, Packet *packet); + static Packet *PopFromQueue(Packet **queue); + /* Sending/writing of packets */ void PrepareToSend(); diff --git a/src/network/core/tcp.cpp b/src/network/core/tcp.cpp index c779beb966..a749b6195b 100644 --- a/src/network/core/tcp.cpp +++ b/src/network/core/tcp.cpp @@ -42,9 +42,7 @@ NetworkRecvStatus NetworkTCPSocketHandler::CloseConnection(bool error) /* Free all pending and partially received packets */ while (this->packet_queue != nullptr) { - Packet *p = this->packet_queue->next; - delete this->packet_queue; - this->packet_queue = p; + delete Packet::PopFromQueue(&this->packet_queue); } delete this->packet_recv; this->packet_recv = nullptr; @@ -60,21 +58,10 @@ NetworkRecvStatus NetworkTCPSocketHandler::CloseConnection(bool error) */ void NetworkTCPSocketHandler::SendPacket(Packet *packet) { - Packet *p; assert(packet != nullptr); packet->PrepareToSend(); - - /* Locate last packet buffered for the client */ - p = this->packet_queue; - if (p == nullptr) { - /* No packets yet */ - this->packet_queue = packet; - } else { - /* Skip to the last packet */ - while (p->next != nullptr) p = p->next; - p->next = packet; - } + Packet::AddToQueue(&this->packet_queue, packet); } /** @@ -96,8 +83,7 @@ SendPacketsState NetworkTCPSocketHandler::SendPackets(bool closing_down) if (!this->writable) return SPS_NONE_SENT; if (!this->IsConnected()) return SPS_CLOSED; - p = this->packet_queue; - while (p != nullptr) { + while ((p = this->packet_queue) != nullptr) { res = p->TransferOut(send, this->sock, 0); if (res == -1) { int err = GET_LAST_ERROR(); @@ -120,9 +106,7 @@ SendPacketsState NetworkTCPSocketHandler::SendPackets(bool closing_down) /* Is this packet sent? */ if (p->RemainingBytesToTransfer() == 0) { /* Go to the next packet */ - this->packet_queue = p->next; - delete p; - p = this->packet_queue; + delete Packet::PopFromQueue(&this->packet_queue); } else { return SPS_PARTLY_SENT; } diff --git a/src/network/network_server.cpp b/src/network/network_server.cpp index 5301bd084c..80a9c56a0c 100644 --- a/src/network/network_server.cpp +++ b/src/network/network_server.cpp @@ -79,9 +79,7 @@ struct PacketWriter : SaveFilter { /* This must all wait until the Destroy function is called. */ while (this->packets != nullptr) { - Packet *p = this->packets->next; - delete this->packets; - this->packets = p; + delete Packet::PopFromQueue(&this->packets); } delete this->current; @@ -132,11 +130,7 @@ struct PacketWriter : SaveFilter { { std::lock_guard lock(this->mutex); - Packet *p = this->packets; - this->packets = p->next; - p->next = nullptr; - - return p; + return Packet::PopFromQueue(&this->packets); } /** Append the current packet to the queue. */ @@ -144,12 +138,7 @@ struct PacketWriter : SaveFilter { { if (this->current == nullptr) return; - Packet **p = &this->packets; - while (*p != nullptr) { - p = &(*p)->next; - } - *p = this->current; - + Packet::AddToQueue(&this->packets, this->current); this->current = nullptr; } @@ -158,7 +147,8 @@ struct PacketWriter : SaveFilter { { if (this->current == nullptr) return; - this->current->next = this->packets; + /* Reversed from AppendQueue so the queue gets added to the current one. */ + Packet::AddToQueue(&this->current, this->packets); this->packets = this->current; this->current = nullptr; } From 450178d780eb885717c53a2dad62587332efc0f4 Mon Sep 17 00:00:00 2001 From: Rubidium Date: Wed, 21 Apr 2021 07:10:09 +0200 Subject: [PATCH 145/268] Codechange: add accessor for the packet type to Packet and make the internal state of Packet private --- src/network/core/packet.cpp | 10 ++++++++++ src/network/core/packet.h | 4 ++-- src/network/network_server.cpp | 2 +- 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/network/core/packet.cpp b/src/network/core/packet.cpp index 9e9ce69012..d534df4d0d 100644 --- a/src/network/core/packet.cpp +++ b/src/network/core/packet.cpp @@ -296,6 +296,16 @@ void Packet::PrepareToRead() this->pos = sizeof(PacketSize); } +/** + * Get the \c PacketType from this packet. + * @return The packet type. + */ +PacketType Packet::GetPacketType() const +{ + assert(this->Size() >= sizeof(PacketSize) + sizeof(PacketType)); + return static_cast(buffer[sizeof(PacketSize)]); +} + /** * Read a boolean from the packet. * @return The read data. diff --git a/src/network/core/packet.h b/src/network/core/packet.h index d7ab7fee63..1a9c9faea6 100644 --- a/src/network/core/packet.h +++ b/src/network/core/packet.h @@ -40,6 +40,7 @@ typedef uint8 PacketType; ///< Identifier for the packet * (year % 4 == 0) and ((year % 100 != 0) or (year % 400 == 0)) */ struct Packet { +private: /** The next packet. Used for queueing packets before sending. */ Packet *next; /** @@ -52,8 +53,6 @@ struct Packet { PacketSize pos; /** The buffer of this packet, of basically variable length up to SEND_MTU. */ byte *buffer; - -private: /** Socket we're associated with. */ NetworkSocketHandler *cs; @@ -82,6 +81,7 @@ public: bool ParsePacketSize(); size_t Size() const; void PrepareToRead(); + PacketType GetPacketType() const; bool CanReadFromPacket(size_t bytes_to_read, bool close_connection = false); bool Recv_bool (); diff --git a/src/network/network_server.cpp b/src/network/network_server.cpp index 80a9c56a0c..a0d1a00666 100644 --- a/src/network/network_server.cpp +++ b/src/network/network_server.cpp @@ -626,7 +626,7 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::SendMap() for (uint i = 0; (has_packets = this->savegame->HasPackets()) && i < sent_packets; i++) { Packet *p = this->savegame->PopPacket(); - last_packet = p->buffer[2] == PACKET_SERVER_MAP_DONE; + last_packet = p->GetPacketType() == PACKET_SERVER_MAP_DONE; this->SendPacket(p); From 75386873b77df4f3493edd5bbba92b33007bdd21 Mon Sep 17 00:00:00 2001 From: Rubidium Date: Sun, 18 Apr 2021 12:36:19 +0200 Subject: [PATCH 146/268] Codechange: use std::vector instead of a fixed size array for Packets --- src/network/core/packet.cpp | 98 +++++++++++++++---------------------- src/network/core/packet.h | 16 ++---- 2 files changed, 44 insertions(+), 70 deletions(-) diff --git a/src/network/core/packet.cpp b/src/network/core/packet.cpp index d534df4d0d..428bc65ba4 100644 --- a/src/network/core/packet.cpp +++ b/src/network/core/packet.cpp @@ -27,39 +27,23 @@ * loose some the data of the packet, so there you pass the maximum * size for the packet you expect from the network. */ -Packet::Packet(NetworkSocketHandler *cs, size_t initial_read_size) +Packet::Packet(NetworkSocketHandler *cs, size_t initial_read_size) : next(nullptr), pos(0) { assert(cs != nullptr); - this->cs = cs; - this->next = nullptr; - this->pos = 0; // We start reading from here - this->size = static_cast(initial_read_size); - this->buffer = MallocT(SEND_MTU); + this->cs = cs; + this->buffer.resize(initial_read_size); } /** * Creates a packet to send * @param type of the packet to send */ -Packet::Packet(PacketType type) +Packet::Packet(PacketType type) : next(nullptr), pos(0), cs(nullptr) { - this->cs = nullptr; - this->next = nullptr; - - /* Skip the size so we can write that in before sending the packet */ - this->pos = 0; - this->size = sizeof(PacketSize); - this->buffer = MallocT(SEND_MTU); - this->buffer[this->size++] = type; -} - -/** - * Free the buffer of this packet. - */ -Packet::~Packet() -{ - free(this->buffer); + /* Allocate space for the the size so we can write that in just before sending the packet. */ + this->Send_uint16(0); + this->Send_uint8(type); } /** @@ -95,15 +79,11 @@ void Packet::PrepareToSend() { assert(this->cs == nullptr && this->next == nullptr); - this->buffer[0] = GB(this->size, 0, 8); - this->buffer[1] = GB(this->size, 8, 8); + this->buffer[0] = GB(this->Size(), 0, 8); + this->buffer[1] = GB(this->Size(), 8, 8); this->pos = 0; // We start reading from here - - /* Reallocate the packet as in 99+% of the times we send at most 25 bytes and - * keeping the other 1400+ bytes wastes memory, especially when someone tries - * to do a denial of service attack! */ - this->buffer = ReallocT(this->buffer, this->size); + this->buffer.shrink_to_fit(); } /** @@ -113,7 +93,7 @@ void Packet::PrepareToSend() */ bool Packet::CanWriteToPacket(size_t bytes_to_write) { - return this->size + bytes_to_write < SEND_MTU; + return this->Size() + bytes_to_write < SEND_MTU; } /* @@ -144,7 +124,7 @@ void Packet::Send_bool(bool data) void Packet::Send_uint8(uint8 data) { assert(this->CanWriteToPacket(sizeof(data))); - this->buffer[this->size++] = data; + this->buffer.emplace_back(data); } /** @@ -154,8 +134,8 @@ void Packet::Send_uint8(uint8 data) void Packet::Send_uint16(uint16 data) { assert(this->CanWriteToPacket(sizeof(data))); - this->buffer[this->size++] = GB(data, 0, 8); - this->buffer[this->size++] = GB(data, 8, 8); + this->buffer.emplace_back(GB(data, 0, 8)); + this->buffer.emplace_back(GB(data, 8, 8)); } /** @@ -165,10 +145,10 @@ void Packet::Send_uint16(uint16 data) void Packet::Send_uint32(uint32 data) { assert(this->CanWriteToPacket(sizeof(data))); - this->buffer[this->size++] = GB(data, 0, 8); - this->buffer[this->size++] = GB(data, 8, 8); - this->buffer[this->size++] = GB(data, 16, 8); - this->buffer[this->size++] = GB(data, 24, 8); + this->buffer.emplace_back(GB(data, 0, 8)); + this->buffer.emplace_back(GB(data, 8, 8)); + this->buffer.emplace_back(GB(data, 16, 8)); + this->buffer.emplace_back(GB(data, 24, 8)); } /** @@ -178,14 +158,14 @@ void Packet::Send_uint32(uint32 data) void Packet::Send_uint64(uint64 data) { assert(this->CanWriteToPacket(sizeof(data))); - this->buffer[this->size++] = GB(data, 0, 8); - this->buffer[this->size++] = GB(data, 8, 8); - this->buffer[this->size++] = GB(data, 16, 8); - this->buffer[this->size++] = GB(data, 24, 8); - this->buffer[this->size++] = GB(data, 32, 8); - this->buffer[this->size++] = GB(data, 40, 8); - this->buffer[this->size++] = GB(data, 48, 8); - this->buffer[this->size++] = GB(data, 56, 8); + this->buffer.emplace_back(GB(data, 0, 8)); + this->buffer.emplace_back(GB(data, 8, 8)); + this->buffer.emplace_back(GB(data, 16, 8)); + this->buffer.emplace_back(GB(data, 24, 8)); + this->buffer.emplace_back(GB(data, 32, 8)); + this->buffer.emplace_back(GB(data, 40, 8)); + this->buffer.emplace_back(GB(data, 48, 8)); + this->buffer.emplace_back(GB(data, 56, 8)); } /** @@ -198,7 +178,7 @@ void Packet::Send_string(const char *data) assert(data != nullptr); /* Length of the string + 1 for the '\0' termination. */ assert(this->CanWriteToPacket(strlen(data) + 1)); - while ((this->buffer[this->size++] = *data++) != '\0') {} + while (this->buffer.emplace_back(*data++) != '\0') {} } /** @@ -211,9 +191,8 @@ void Packet::Send_string(const char *data) */ size_t Packet::Send_bytes(const byte *begin, const byte *end) { - size_t amount = std::min(end - begin, SEND_MTU - this->size); - memcpy(this->buffer + this->size, begin, amount); - this->size += static_cast(amount); + size_t amount = std::min(end - begin, SEND_MTU - this->Size()); + this->buffer.insert(this->buffer.end(), begin, begin + amount); return amount; } @@ -238,7 +217,7 @@ bool Packet::CanReadFromPacket(size_t bytes_to_read, bool close_connection) if (this->cs->HasClientQuit()) return false; /* Check if variable is within packet-size */ - if (this->pos + bytes_to_read > this->size) { + if (this->pos + bytes_to_read > this->Size()) { if (close_connection) this->cs->NetworkSocketHandler::CloseConnection(); return false; } @@ -265,7 +244,7 @@ bool Packet::HasPacketSizeData() const */ size_t Packet::Size() const { - return this->size; + return this->buffer.size(); } /** @@ -275,14 +254,15 @@ size_t Packet::Size() const bool Packet::ParsePacketSize() { assert(this->cs != nullptr && this->next == nullptr); - this->size = (PacketSize)this->buffer[0]; - this->size += (PacketSize)this->buffer[1] << 8; + size_t size = (size_t)this->buffer[0]; + size += (size_t)this->buffer[1] << 8; /* If the size of the packet is less than the bytes required for the size and type of * the packet, or more than the allowed limit, then something is wrong with the packet. * In those cases the packet can generally be regarded as containing garbage data. */ - if (this->size < sizeof(PacketSize) + sizeof(PacketType) || this->size > SEND_MTU) return false; + if (size < sizeof(PacketSize) + sizeof(PacketType) || size > SEND_MTU) return false; + this->buffer.resize(size); this->pos = sizeof(PacketSize); return true; } @@ -398,13 +378,13 @@ void Packet::Recv_string(char *buffer, size_t size, StringValidationSettings set if (cs->HasClientQuit()) return; pos = this->pos; - while (--size > 0 && pos < this->size && (*buffer++ = this->buffer[pos++]) != '\0') {} + while (--size > 0 && pos < this->Size() && (*buffer++ = this->buffer[pos++]) != '\0') {} - if (size == 0 || pos == this->size) { + if (size == 0 || pos == this->Size()) { *buffer = '\0'; /* If size was sooner to zero then the string in the stream * skip till the \0, so than packet can be read out correctly for the rest */ - while (pos < this->size && this->buffer[pos] != '\0') pos++; + while (pos < this->Size() && this->buffer[pos] != '\0') pos++; pos++; } this->pos = pos; @@ -418,5 +398,5 @@ void Packet::Recv_string(char *buffer, size_t size, StringValidationSettings set */ size_t Packet::RemainingBytesToTransfer() const { - return this->size - this->pos; + return this->Size() - this->pos; } diff --git a/src/network/core/packet.h b/src/network/core/packet.h index 1a9c9faea6..1ac7f18a18 100644 --- a/src/network/core/packet.h +++ b/src/network/core/packet.h @@ -43,23 +43,17 @@ struct Packet { private: /** The next packet. Used for queueing packets before sending. */ Packet *next; - /** - * The size of the whole packet for received packets. For packets - * that will be sent, the value is filled in just before the - * actual transmission. - */ - PacketSize size; /** The current read/write position in the packet */ PacketSize pos; - /** The buffer of this packet, of basically variable length up to SEND_MTU. */ - byte *buffer; + /** The buffer of this packet. */ + std::vector buffer; + /** Socket we're associated with. */ NetworkSocketHandler *cs; public: Packet(NetworkSocketHandler *cs, size_t initial_read_size = sizeof(PacketSize)); Packet(PacketType type); - ~Packet(); static void AddToQueue(Packet **queue, Packet *packet); static Packet *PopFromQueue(Packet **queue); @@ -118,7 +112,7 @@ public: assert(this->pos < this->buffer.size()); assert(this->pos + amount <= this->buffer.size()); /* Making buffer a char means casting a lot in the Recv/Send functions. */ - const char *output_buffer = reinterpret_cast(this->buffer + this->pos); + const char *output_buffer = reinterpret_cast(this->buffer.data() + this->pos); ssize_t bytes = transfer_function(destination, output_buffer, static_cast(amount), std::forward(args)...); if (bytes > 0) this->pos += bytes; return bytes; @@ -183,7 +177,7 @@ public: assert(this->pos < this->buffer.size()); assert(this->pos + amount <= this->buffer.size()); /* Making buffer a char means casting a lot in the Recv/Send functions. */ - char *input_buffer = reinterpret_cast(this->buffer + this->pos); + char *input_buffer = reinterpret_cast(this->buffer.data() + this->pos); ssize_t bytes = transfer_function(source, input_buffer, static_cast(amount), std::forward(args)...); if (bytes > 0) this->pos += bytes; return bytes; From e62ecbff16a1470aac2bb0dc830cdf129d1fb539 Mon Sep 17 00:00:00 2001 From: glx22 Date: Sat, 24 Apr 2021 17:49:41 +0200 Subject: [PATCH 147/268] Fix: [CMake] Incorrect dependency checks for GRF --- cmake/CreateGrfCommand.cmake | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmake/CreateGrfCommand.cmake b/cmake/CreateGrfCommand.cmake index 4ad9734198..107ec09b88 100644 --- a/cmake/CreateGrfCommand.cmake +++ b/cmake/CreateGrfCommand.cmake @@ -35,9 +35,9 @@ function(create_grf_command) -DNFORENUM_EXECUTABLE=${NFORENUM_EXECUTABLE} -DGRFCODEC_EXECUTABLE=${GRFCODEC_EXECUTABLE} -P ${CMAKE_SOURCE_DIR}/cmake/scripts/CreateGRF.cmake - MAIN_DEPENDENCY ${GRF_NFO_SOURCE_FILES} + MAIN_DEPENDENCY ${CMAKE_SOURCE_DIR}/cmake/scripts/CreateGRF.cmake DEPENDS ${GRF_PNG_BINARY_FILES} - ${CMAKE_SOURCE_DIR}/cmake/scripts/CreateGRF.cmake + ${GRF_NFO_SOURCE_FILES} WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} COMMENT "Generating ${GRF_SOURCE_FOLDER_NAME}.grf" ) From aca20092aadd65a764f1ea493c36f0ca507b32ff Mon Sep 17 00:00:00 2001 From: glx22 Date: Sat, 24 Apr 2021 17:52:21 +0200 Subject: [PATCH 148/268] Fix: [CMake] Check nforenum and grfcodec return value --- cmake/scripts/CreateGRF.cmake | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/cmake/scripts/CreateGRF.cmake b/cmake/scripts/CreateGRF.cmake index 8631284f33..d47e39aa0e 100644 --- a/cmake/scripts/CreateGRF.cmake +++ b/cmake/scripts/CreateGRF.cmake @@ -39,6 +39,20 @@ foreach(NFO_LINE IN LISTS NFO_LINES) endif() endforeach() -execute_process(COMMAND ${NFORENUM_EXECUTABLE} -s sprites/${GRF_SOURCE_FOLDER_NAME}.nfo) -execute_process(COMMAND ${GRFCODEC_EXECUTABLE} -n -s -e -p1 ${GRF_SOURCE_FOLDER_NAME}.grf) +execute_process(COMMAND ${NFORENUM_EXECUTABLE} -s sprites/${GRF_SOURCE_FOLDER_NAME}.nfo RESULT_VARIABLE RESULT) +if(RESULT) + if(NOT RESULT MATCHES "^[0-9]*$") + message(FATAL_ERROR "Failed to run NFORenum (${RESULT}), please check NFORENUM_EXECUTABLE variable") + endif() + message(FATAL_ERROR "NFORenum failed") +endif() + +execute_process(COMMAND ${GRFCODEC_EXECUTABLE} -n -s -e -p1 ${GRF_SOURCE_FOLDER_NAME}.grf RESULT_VARIABLE RESULT) +if(RESULT) + if(NOT RESULT MATCHES "^[0-9]*$") + message(FATAL_ERROR "Failed to run GRFCodec (${RESULT}), please check GRFCODEC_EXECUTABLE variable") + endif() + message(FATAL_ERROR "GRFCodec failed") +endif() + execute_process(COMMAND ${CMAKE_COMMAND} -E copy ${GRF_SOURCE_FOLDER_NAME}.grf ${GRF_BINARY_FILE}) From 526635942451479bee66e9eb61c50f91ae48a7dd Mon Sep 17 00:00:00 2001 From: Patric Stout Date: Sun, 18 Apr 2021 09:54:47 +0200 Subject: [PATCH 149/268] Feature: rework in-game Online Players GUI The GUI now more clearly shows some basic information about the server you joined, your client name (and the ability to change it), and what players are in which company. It also contains useful buttons to press to join companies, chat with other people, and for admins to kick/ban people. Additionally, renamed "advertised" to "visibility"; this has to do with future additions, but also because it is more clear in wording. --- media/baseset/openttd.grf | Bin 510358 -> 510525 bytes media/baseset/openttd/openttdgui.nfo | 5 +- media/baseset/openttd/openttdgui.png | Bin 43899 -> 44464 bytes src/company_cmd.cpp | 4 +- src/lang/english.txt | 41 +- src/network/network.cpp | 5 +- src/network/network_client.cpp | 10 +- src/network/network_gui.cpp | 645 +++++++++++++++++++++------ src/network/network_server.cpp | 7 +- src/table/sprites.h | 6 +- src/toolbar_gui.cpp | 14 +- src/widgets/network_widget.h | 11 +- 12 files changed, 573 insertions(+), 175 deletions(-) diff --git a/media/baseset/openttd.grf b/media/baseset/openttd.grf index a63bfc4e295e77a34ac22387778c534dcc1f2c3d..c4511f55ea9f87c7dd7e3e4f73e79e404801d053 100644 GIT binary patch delta 217 zcmbO>S$^*nc@`Fi|92Z%T3HxdS(sW`m|Iy`wz9AqlriqzZd%6LIH$gWfsu=c0R)&C z7#LnKUtoK|!_3IY_=1^{@dh_DKR^EqW=8%Oya%`+@Gjtaq0G$4&-j96f$9m{9Ud=2 zRwNuKz%=0j4--HC4Bi7`52Q9IZBPXnvcPBtZ->+jbw&nMb%7 diff --git a/media/baseset/openttd/openttdgui.nfo b/media/baseset/openttd/openttdgui.nfo index eb1313365e..9a13fa8000 100644 --- a/media/baseset/openttd/openttdgui.nfo +++ b/media/baseset/openttd/openttdgui.nfo @@ -4,7 +4,7 @@ // See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see . // -1 * 0 0C "OpenTTD GUI graphics" - -1 * 3 05 15 \b 186 // OPENTTD_SPRITE_COUNT + -1 * 3 05 15 \b 189 // OPENTTD_SPRITE_COUNT -1 sprites/openttdgui.png 8bpp 66 8 64 31 -31 7 normal -1 sprites/openttdgui.png 8bpp 146 8 64 31 -31 7 normal -1 sprites/openttdgui.png 8bpp 226 8 64 31 -31 7 normal @@ -191,3 +191,6 @@ -1 sprites/openttdgui_convert_tram.png 8bpp 24 0 32 32 0 0 normal -1 sprites/openttdgui.png 8bpp 513 440 10 10 0 0 normal -1 sprites/openttdgui.png 8bpp 526 440 10 10 0 0 normal + -1 sprites/openttdgui.png 8bpp 539 440 12 10 0 0 normal + -1 sprites/openttdgui.png 8bpp 553 440 12 10 0 0 normal + -1 sprites/openttdgui.png 8bpp 567 440 12 10 0 0 normal diff --git a/media/baseset/openttd/openttdgui.png b/media/baseset/openttd/openttdgui.png index 1fc02aa61e30774b4e69fd08b21f51ef69a74a00..dc0976a971647fa6adad1ed1506030aaf2c60bf8 100644 GIT binary patch delta 41971 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 AWB>pF delta 41353 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 z

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 = address; // Save the IP of the client + + InvalidateWindowData(WC_CLIENT_LIST, 0); } /** @@ -713,7 +714,7 @@ void NetworkClientConnectGame(NetworkAddress address, CompanyID join_as, const c static void NetworkInitGameInfo() { if (StrEmpty(_settings_client.network.server_name)) { - seprintf(_settings_client.network.server_name, lastof(_settings_client.network.server_name), "Unnamed Server"); + strecpy(_settings_client.network.server_name, "Unnamed Server", lastof(_settings_client.network.server_name)); } /* The server is a client too */ diff --git a/src/network/network_client.cpp b/src/network/network_client.cpp index 6156dc4863..d5fe64b11d 100644 --- a/src/network/network_client.cpp +++ b/src/network/network_client.cpp @@ -647,7 +647,7 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_CLIENT_INFO(Pac ci->client_playas = playas; strecpy(ci->client_name, name, lastof(ci->client_name)); - SetWindowDirty(WC_CLIENT_LIST, 0); + InvalidateWindowData(WC_CLIENT_LIST, 0); return NETWORK_RECV_STATUS_OKAY; } @@ -666,7 +666,7 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_CLIENT_INFO(Pac strecpy(ci->client_name, name, lastof(ci->client_name)); - SetWindowDirty(WC_CLIENT_LIST, 0); + InvalidateWindowData(WC_CLIENT_LIST, 0); return NETWORK_RECV_STATUS_OKAY; } @@ -1043,7 +1043,7 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_ERROR_QUIT(Pack delete ci; } - SetWindowDirty(WC_CLIENT_LIST, 0); + InvalidateWindowData(WC_CLIENT_LIST, 0); return NETWORK_RECV_STATUS_OKAY; } @@ -1062,7 +1062,7 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_QUIT(Packet *p) DEBUG(net, 0, "Unknown client (%d) is leaving the game", client_id); } - SetWindowDirty(WC_CLIENT_LIST, 0); + InvalidateWindowData(WC_CLIENT_LIST, 0); /* If we come here it means we could not locate the client.. strange :s */ return NETWORK_RECV_STATUS_OKAY; @@ -1079,7 +1079,7 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_JOIN(Packet *p) NetworkTextMessage(NETWORK_ACTION_JOIN, CC_DEFAULT, false, ci->client_name); } - SetWindowDirty(WC_CLIENT_LIST, 0); + InvalidateWindowData(WC_CLIENT_LIST, 0); return NETWORK_RECV_STATUS_OKAY; } diff --git a/src/network/network_gui.cpp b/src/network/network_gui.cpp index dd25ad5bc1..fcfeded5a5 100644 --- a/src/network/network_gui.cpp +++ b/src/network/network_gui.cpp @@ -21,6 +21,7 @@ #include "network_udp.h" #include "../window_func.h" #include "../gfx_func.h" +#include "../widgets/dropdown_type.h" #include "../widgets/dropdown_func.h" #include "../querystring_gui.h" #include "../sortlist_type.h" @@ -30,6 +31,8 @@ #include "../map_type.h" #include "../guitimer_func.h" #include "../zoom_func.h" +#include "../sprite.h" +#include "../settings_internal.h" #include "../widgets/network_widget.h" @@ -38,21 +41,24 @@ #include "../stringfilter_type.h" -#include "../safeguards.h" - #ifdef __EMSCRIPTEN__ # include #endif +#include + +#include "../safeguards.h" + static void ShowNetworkStartServerWindow(); static void ShowNetworkLobbyWindow(NetworkGameList *ngl); /** - * Advertisement options in the start server window + * Visibility of the server. Public servers advertise, where private servers + * do not. */ -static const StringID _connection_types_dropdown[] = { - STR_NETWORK_START_SERVER_UNADVERTISED, - STR_NETWORK_START_SERVER_ADVERTISED, +static const StringID _server_visibility_dropdown[] = { + STR_NETWORK_SERVER_VISIBILITY_PRIVATE, + STR_NETWORK_SERVER_VISIBILITY_PUBLIC, INVALID_STRING_ID }; @@ -985,7 +991,7 @@ struct NetworkStartServerWindow : public Window { { switch (widget) { case WID_NSS_CONNTYPE_BTN: - SetDParam(0, _connection_types_dropdown[_settings_client.network.server_advertise]); + SetDParam(0, _server_visibility_dropdown[_settings_client.network.server_advertise]); break; case WID_NSS_CLIENTS_TXT: @@ -1006,7 +1012,7 @@ struct NetworkStartServerWindow : public Window { { switch (widget) { case WID_NSS_CONNTYPE_BTN: - *size = maxdim(GetStringBoundingBox(_connection_types_dropdown[0]), GetStringBoundingBox(_connection_types_dropdown[1])); + *size = maxdim(GetStringBoundingBox(_server_visibility_dropdown[0]), GetStringBoundingBox(_server_visibility_dropdown[1])); size->width += padding.width; size->height += padding.height; break; @@ -1036,7 +1042,7 @@ struct NetworkStartServerWindow : public Window { break; case WID_NSS_CONNTYPE_BTN: // Connection type - ShowDropDownMenu(this, _connection_types_dropdown, _settings_client.network.server_advertise, WID_NSS_CONNTYPE_BTN, 0, 0); // do it for widget WID_NSS_CONNTYPE_BTN + ShowDropDownMenu(this, _server_visibility_dropdown, _settings_client.network.server_advertise, WID_NSS_CONNTYPE_BTN, 0, 0); // do it for widget WID_NSS_CONNTYPE_BTN break; case WID_NSS_CLIENTS_BTND: case WID_NSS_CLIENTS_BTNU: // Click on up/down button for number of clients @@ -1175,8 +1181,8 @@ static const NWidgetPart _nested_network_start_server_window_widgets[] = { NWidget(NWID_HORIZONTAL, NC_EQUALSIZE), SetPIP(10, 6, 10), NWidget(NWID_VERTICAL), SetPIP(0, 1, 0), - NWidget(WWT_TEXT, COLOUR_LIGHT_BLUE, WID_NSS_CONNTYPE_LABEL), SetFill(1, 0), SetDataTip(STR_NETWORK_START_SERVER_ADVERTISED_LABEL, STR_NULL), - NWidget(WWT_DROPDOWN, COLOUR_LIGHT_BLUE, WID_NSS_CONNTYPE_BTN), SetFill(1, 0), SetDataTip(STR_BLACK_STRING, STR_NETWORK_START_SERVER_ADVERTISED_TOOLTIP), + NWidget(WWT_TEXT, COLOUR_LIGHT_BLUE, WID_NSS_CONNTYPE_LABEL), SetFill(1, 0), SetDataTip(STR_NETWORK_START_SERVER_VISIBILITY_LABEL, STR_NULL), + NWidget(WWT_DROPDOWN, COLOUR_LIGHT_BLUE, WID_NSS_CONNTYPE_BTN), SetFill(1, 0), SetDataTip(STR_BLACK_STRING, STR_NETWORK_START_SERVER_VISIBILITY_TOOLTIP), EndContainer(), NWidget(NWID_VERTICAL), SetPIP(0, 1, 0), NWidget(NWID_SPACER), SetFill(1, 1), @@ -1592,21 +1598,6 @@ static void ClientList_Ban(const NetworkClientInfo *ci) NetworkServerKickOrBanIP(ci->client_id, true, nullptr); } -static void ClientList_SpeakToClient(const NetworkClientInfo *ci) -{ - ShowNetworkChatQueryWindow(DESTTYPE_CLIENT, ci->client_id); -} - -static void ClientList_SpeakToCompany(const NetworkClientInfo *ci) -{ - ShowNetworkChatQueryWindow(DESTTYPE_TEAM, ci->client_playas); -} - -static void ClientList_SpeakToAll(const NetworkClientInfo *ci) -{ - ShowNetworkChatQueryWindow(DESTTYPE_BROADCAST, 0); -} - /** Popup selection window to chose an action to perform */ struct NetworkClientListPopupWindow : Window { /** Container for actions that can be executed. */ @@ -1639,15 +1630,6 @@ struct NetworkClientListPopupWindow : Window { const NetworkClientInfo *ci = NetworkClientInfo::GetByClientID(client_id); - if (_network_own_client_id != ci->client_id) { - this->AddAction(STR_NETWORK_CLIENTLIST_SPEAK_TO_CLIENT, &ClientList_SpeakToClient); - } - - if (Company::IsValidID(ci->client_playas) || ci->client_playas == COMPANY_SPECTATOR) { - this->AddAction(STR_NETWORK_CLIENTLIST_SPEAK_TO_COMPANY, &ClientList_SpeakToCompany); - } - this->AddAction(STR_NETWORK_CLIENTLIST_SPEAK_TO_ALL, &ClientList_SpeakToAll); - /* A server can kick clients (but not himself). */ if (_network_server && _network_own_client_id != ci->client_id) { this->AddAction(STR_NETWORK_CLIENTLIST_KICK, &ClientList_Kick); @@ -1671,7 +1653,7 @@ struct NetworkClientListPopupWindow : Window { } d.height *= (uint)this->actions.size(); - d.width += WD_FRAMERECT_LEFT + WD_FRAMERECT_RIGHT; + d.width += WD_FRAMERECT_LEFT + WD_FRAMERECT_RIGHT + 4 + 4; // Give the list a bit of padding on both sides. d.height += WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM; *size = d; } @@ -1690,7 +1672,7 @@ struct NetworkClientListPopupWindow : Window { colour = TC_BLACK; } - DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, action.name, colour); + DrawString(r.left + WD_FRAMERECT_LEFT + 4, r.right - WD_FRAMERECT_RIGHT - 4, y, action.name, colour); y += FONT_HEIGHT_NORMAL; } } @@ -1731,167 +1713,554 @@ static void PopupClientList(ClientID client_id, int x, int y) static const NWidgetPart _nested_client_list_widgets[] = { NWidget(NWID_HORIZONTAL), NWidget(WWT_CLOSEBOX, COLOUR_GREY), - NWidget(WWT_CAPTION, COLOUR_GREY), SetDataTip(STR_NETWORK_COMPANY_LIST_CLIENT_LIST_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS), + NWidget(WWT_CAPTION, COLOUR_GREY), SetDataTip(STR_NETWORK_CLIENT_LIST_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS), + NWidget(WWT_DEFSIZEBOX, COLOUR_GREY), NWidget(WWT_STICKYBOX, COLOUR_GREY), EndContainer(), - NWidget(WWT_PANEL, COLOUR_GREY, WID_CL_PANEL), SetMinimalSize(250, WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM), SetResize(1, 1), EndContainer(), + NWidget(WWT_PANEL, COLOUR_GREY), + NWidget(NWID_SELECTION, INVALID_COLOUR, WID_CL_SERVER_SELECTOR), + NWidget(WWT_FRAME, COLOUR_GREY), SetDataTip(STR_NETWORK_CLIENT_LIST_SERVER, STR_NULL), SetPadding(4, 4, 0, 4), SetPIP(0, 2, 0), + NWidget(NWID_HORIZONTAL), SetPIP(0, 3, 0), + NWidget(WWT_TEXT, COLOUR_GREY), SetMinimalTextLines(1, 0), SetDataTip(STR_NETWORK_CLIENT_LIST_SERVER_NAME, STR_NULL), + NWidget(NWID_SPACER), SetMinimalSize(20, 0), + NWidget(WWT_TEXT, COLOUR_GREY, WID_CL_SERVER_NAME), SetFill(1, 0), SetMinimalTextLines(1, 0), SetResize(1, 0), SetDataTip(STR_BLACK_RAW_STRING, STR_NETWORK_CLIENT_LIST_SERVER_NAME_TOOLTIP), SetAlignment(SA_VERT_CENTER | SA_RIGHT), + NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_CL_SERVER_NAME_EDIT), SetMinimalSize(12, 14), SetDataTip(SPR_RENAME, STR_NETWORK_CLIENT_LIST_SERVER_NAME_EDIT_TOOLTIP), + EndContainer(), + NWidget(NWID_HORIZONTAL), SetPIP(0, 3, 0), + NWidget(WWT_TEXT, COLOUR_GREY), SetMinimalTextLines(1, 0), SetDataTip(STR_NETWORK_CLIENT_LIST_SERVER_VISIBILITY, STR_NULL), + NWidget(NWID_SPACER), SetMinimalSize(20, 0), SetFill(1, 0), SetResize(1, 0), + NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_CL_SERVER_VISIBILITY), SetDataTip(STR_BLACK_STRING, STR_NETWORK_CLIENT_LIST_SERVER_VISIBILITY_TOOLTIP), + EndContainer(), + EndContainer(), + EndContainer(), + NWidget(WWT_FRAME, COLOUR_GREY), SetDataTip(STR_NETWORK_CLIENT_LIST_PLAYER, STR_NULL), SetPadding(4, 4, 4, 4), SetPIP(0, 2, 0), + NWidget(NWID_HORIZONTAL), SetPIP(0, 3, 0), + NWidget(WWT_TEXT, COLOUR_GREY), SetMinimalTextLines(1, 0), SetDataTip(STR_NETWORK_CLIENT_LIST_PLAYER_NAME, STR_NULL), + NWidget(NWID_SPACER), SetMinimalSize(20, 0), + NWidget(WWT_TEXT, COLOUR_GREY, WID_CL_CLIENT_NAME), SetFill(1, 0), SetMinimalTextLines(1, 0), SetResize(1, 0), SetDataTip(STR_BLACK_RAW_STRING, STR_NETWORK_CLIENT_LIST_PLAYER_NAME_TOOLTIP), SetAlignment(SA_VERT_CENTER | SA_RIGHT), + NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_CL_CLIENT_NAME_EDIT), SetMinimalSize(12, 14), SetDataTip(SPR_RENAME, STR_NETWORK_CLIENT_LIST_PLAYER_NAME_EDIT_TOOLTIP), + EndContainer(), + EndContainer(), + NWidget(NWID_HORIZONTAL), + NWidget(WWT_MATRIX, COLOUR_GREY, WID_CL_MATRIX), SetMinimalSize(180, 0), SetResize(1, 1), SetFill(1, 1), SetMatrixDataTip(1, 0, STR_NULL), SetScrollbar(WID_CL_SCROLLBAR), + NWidget(NWID_VERTICAL), + NWidget(NWID_VSCROLLBAR, COLOUR_GREY, WID_CL_SCROLLBAR), + NWidget(WWT_RESIZEBOX, COLOUR_GREY), + EndContainer(), + EndContainer(), + EndContainer(), }; static WindowDesc _client_list_desc( - WDP_AUTO, "list_clients", 0, 0, + WDP_AUTO, "list_clients", 220, 300, WC_CLIENT_LIST, WC_NONE, 0, _nested_client_list_widgets, lengthof(_nested_client_list_widgets) ); +/** + * Button shown for either a company or client in the client-list. + * + * These buttons are dynamic and strongly depends on which company/client + * what buttons are available. This class allows dynamically creating them + * as the current Widget system does not. + */ +class ButtonCommon { +public: + SpriteID sprite; ///< The sprite to use on the button. + StringID tooltip; ///< The tooltip of the button. + Colours colour; ///< The colour of the button. + bool disabled; ///< Is the button disabled? + uint height; ///< Calculated height of the button. + uint width; ///< Calculated width of the button. + + ButtonCommon(SpriteID sprite, StringID tooltip, Colours colour) : + sprite(sprite), + tooltip(tooltip), + colour(colour), + disabled(false) + { + Dimension d = GetSpriteSize(sprite); + this->height = d.height + ScaleGUITrad(WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM); + this->width = d.width + ScaleGUITrad(WD_FRAMERECT_LEFT + WD_FRAMERECT_RIGHT); + } + virtual ~ButtonCommon() {} + + /** + * OnClick handler for when the button is pressed. + */ + virtual void OnClick(struct NetworkClientListWindow *w, Point pt) = 0; +}; + +/** + * Template version of Button, with callback support. + */ +template +class Button : public ButtonCommon { +private: + typedef void (*ButtonCallback)(struct NetworkClientListWindow *w, Point pt, T id); ///< Callback function to call on click. + T id; ///< ID this button belongs to. + ButtonCallback proc; ///< Callback proc to call when button is pressed. + +public: + Button(SpriteID sprite, StringID tooltip, Colours colour, T id, ButtonCallback proc) : + ButtonCommon(sprite, tooltip, colour), + id(id), + proc(proc) + { + assert(proc != nullptr); + } + + void OnClick(struct NetworkClientListWindow *w, Point pt) override + { + if (this->disabled) return; + + this->proc(w, pt, this->id); + } +}; + +using CompanyButton = Button; +using ClientButton = Button; + /** * Main handle for clientlist */ struct NetworkClientListWindow : Window { - int selected_item; +private: + ClientListWidgets query_widget; ///< During a query this tracks what widget caused the query. + CompanyID join_company; ///< During query for company password, this stores what company we wanted to join. - uint server_client_width; - uint line_height; + Scrollbar *vscroll; ///< Vertical scrollbar of this window. + uint line_height; ///< Current lineheight of each entry in the matrix. + uint line_count; ///< Amount of lines in the matrix. - Dimension icon_size; + std::map>> buttons; ///< Per line which buttons are available. - NetworkClientListWindow(WindowDesc *desc, WindowNumber window_number) : - Window(desc), - selected_item(-1) + static const int CLIENT_OFFSET_LEFT = 12; ///< Offset of client entries compared to company entries. + + /** + * Chat button on a Company is clicked. + * @param w The instance of this window. + * @param pt The point where this button was clicked. + * @param company_id The company this button was assigned to. + */ + static void OnClickCompanyChat(NetworkClientListWindow *w, Point pt, CompanyID company_id) { - this->InitNested(window_number); + ShowNetworkChatQueryWindow(DESTTYPE_TEAM, company_id); } /** - * Finds the amount of clients and set the height correct + * Join button on a Company is clicked. + * @param w The instance of this window. + * @param pt The point where this button was clicked. + * @param company_id The company this button was assigned to. */ - bool CheckClientListHeight() + static void OnClickCompanyJoin(NetworkClientListWindow *w, Point pt, CompanyID company_id) { - int num = 0; + if (_network_server) { + NetworkServerDoMove(CLIENT_ID_SERVER, company_id); + MarkWholeScreenDirty(); + } else if (NetworkCompanyIsPassworded(company_id)) { + w->query_widget = WID_CL_COMPANY_JOIN; + w->join_company = company_id; + ShowQueryString(STR_EMPTY, STR_NETWORK_NEED_COMPANY_PASSWORD_CAPTION, NETWORK_PASSWORD_LENGTH, w, CS_ALPHANUMERAL, QSF_PASSWORD); + } else { + NetworkClientRequestMove(company_id); + } + } - /* Should be replaced with a loop through all clients */ + /** + * Admin button on a Client is clicked. + * @param w The instance of this window. + * @param pt The point where this button was clicked. + * @param client_id The client this button was assigned to. + */ + static void OnClickClientAdmin(NetworkClientListWindow *w, Point pt, ClientID client_id) + { + PopupClientList(client_id, pt.x + w->left, pt.y + w->top); + } + + /** + * Chat button on a Client is clicked. + * @param w The instance of this window. + * @param pt The point where this button was clicked. + * @param client_id The client this button was assigned to. + */ + static void OnClickClientChat(NetworkClientListWindow *w, Point pt, ClientID client_id) + { + ShowNetworkChatQueryWindow(DESTTYPE_CLIENT, client_id); + } + + /** + * Part of RebuildList() to create the information for a single company. + * @param company_id The company to build the list for. + * @param own_ci The NetworkClientInfo of the client itself. + */ + void RebuildListCompany(CompanyID company_id, const NetworkClientInfo *own_ci) + { + ButtonCommon *chat_button = new CompanyButton(SPR_CHAT, company_id == COMPANY_SPECTATOR ? STR_NETWORK_CLIENT_LIST_CHAT_SPECTATOR_TOOLTIP : STR_NETWORK_CLIENT_LIST_CHAT_COMPANY_TOOLTIP, COLOUR_ORANGE, company_id, &NetworkClientListWindow::OnClickCompanyChat); + + this->buttons[line_count].emplace_back(chat_button); + if (own_ci->client_playas != company_id) this->buttons[line_count].emplace_back(new CompanyButton(SPR_JOIN, STR_NETWORK_CLIENT_LIST_JOIN_TOOLTIP, COLOUR_ORANGE, company_id, &NetworkClientListWindow::OnClickCompanyJoin)); + + this->line_count += 1; + + bool has_players = false; for (const NetworkClientInfo *ci : NetworkClientInfo::Iterate()) { - if (ci->client_playas != COMPANY_INACTIVE_CLIENT) num++; + if (ci->client_playas != company_id) continue; + has_players = true; + + if (_network_own_client_id != ci->client_id) this->buttons[line_count].emplace_back(new ClientButton(SPR_CHAT, STR_NETWORK_CLIENT_LIST_CHAT_CLIENT_TOOLTIP, COLOUR_ORANGE, ci->client_id, &NetworkClientListWindow::OnClickClientChat)); + if (_network_server && _network_own_client_id != ci->client_id) this->buttons[line_count].emplace_back(new ClientButton(SPR_ADMIN, STR_NETWORK_CLIENT_LIST_ADMIN_TOOLTIP, COLOUR_RED, ci->client_id, &NetworkClientListWindow::OnClickClientAdmin)); + + this->line_count += 1; } - num *= this->line_height; + chat_button->disabled = !has_players; + } - int diff = (num + WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM) - (this->GetWidget(WID_CL_PANEL)->current_y); - /* If height is changed */ - if (diff != 0) { - ResizeWindow(this, 0, diff, false); - return false; + /** + * Rebuild the list, meaning: calculate the lines needed and what buttons go on which line. + */ + void RebuildList() + { + const NetworkClientInfo *own_ci = NetworkClientInfo::GetByClientID(_network_own_client_id); + + this->buttons.clear(); + this->line_count = 0; + + /* Companies */ + for (const Company *c : Company::Iterate()) { + this->RebuildListCompany(c->index, own_ci); } - return true; + + /* Spectators */ + this->RebuildListCompany(COMPANY_SPECTATOR, own_ci); + + this->vscroll->SetCount(this->line_count); + } + + /** + * Get the button at a specific point on the WID_CL_MATRIX. + * @param pt The point to look for a button. + * @return The button or a nullptr if there was none. + */ + ButtonCommon *GetButtonAtPoint(Point pt) + { + uint index = this->vscroll->GetScrolledRowFromWidget(pt.y, this, WID_CL_MATRIX); + NWidgetBase *widget_matrix = this->GetWidget(WID_CL_MATRIX); + + bool rtl = _current_text_dir == TD_RTL; + uint x = rtl ? (uint)widget_matrix->pos_x + WD_FRAMERECT_LEFT : widget_matrix->current_x - WD_FRAMERECT_RIGHT; + + /* Find the buttons for this row. */ + auto button_find = this->buttons.find(index); + if (button_find == this->buttons.end()) return nullptr; + + /* Check if we want to display a tooltip for any of the buttons. */ + for (auto &button : button_find->second) { + uint left = rtl ? x : x - button->width; + uint right = rtl ? x + button->width : x; + + if (IsInsideMM(pt.x, left, right)) { + return button.get(); + } + + int width = button->width + WD_FRAMERECT_LEFT + WD_FRAMERECT_RIGHT; + x += rtl ? width : -width; + } + + return nullptr; + } + +public: + NetworkClientListWindow(WindowDesc *desc, WindowNumber window_number) : + Window(desc) + { + this->CreateNestedTree(); + this->vscroll = this->GetScrollbar(WID_CL_SCROLLBAR); + this->OnInvalidateData(); + this->FinishInitNested(window_number); + } + + void OnInvalidateData(int data = 0, bool gui_scope = true) override + { + this->RebuildList(); + + /* Currently server information is not sync'd to clients, so we cannot show it on clients. */ + this->GetWidget(WID_CL_SERVER_SELECTOR)->SetDisplayedPlane(_network_server ? 0 : SZSP_HORIZONTAL); } void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize) override { - if (widget != WID_CL_PANEL) return; + switch (widget) { + case WID_CL_SERVER_VISIBILITY: + *size = maxdim(GetStringBoundingBox(_server_visibility_dropdown[0]), GetStringBoundingBox(_server_visibility_dropdown[1])); + size->width += padding.width; + size->height += padding.height; + break; - this->server_client_width = std::max(GetStringBoundingBox(STR_NETWORK_SERVER).width, GetStringBoundingBox(STR_NETWORK_CLIENT).width) + WD_FRAMERECT_RIGHT; - this->icon_size = GetSpriteSize(SPR_COMPANY_ICON); - this->line_height = std::max(this->icon_size.height + 2U, (uint)FONT_HEIGHT_NORMAL); + case WID_CL_MATRIX: { + uint height = std::max({GetSpriteSize(SPR_COMPANY_ICON).height, GetSpriteSize(SPR_JOIN).height, GetSpriteSize(SPR_ADMIN).height, GetSpriteSize(SPR_CHAT).height}); + height += ScaleGUITrad(WD_FRAMERECT_TOP) + ScaleGUITrad(WD_FRAMERECT_BOTTOM); + this->line_height = std::max(height, (uint)FONT_HEIGHT_NORMAL) + ScaleGUITrad(WD_MATRIX_TOP + WD_MATRIX_BOTTOM); - uint width = 100; // Default width - for (const NetworkClientInfo *ci : NetworkClientInfo::Iterate()) { - width = std::max(width, GetStringBoundingBox(ci->client_name).width); + resize->width = 1; + resize->height = this->line_height; + fill->height = this->line_height; + size->height = std::max(size->height, 5 * this->line_height); + break; + } } - - size->width = WD_FRAMERECT_LEFT + this->server_client_width + this->icon_size.width + WD_FRAMERECT_LEFT + width + WD_FRAMERECT_RIGHT; } - void OnPaint() override + void OnResize() override { - /* Check if we need to reset the height */ - if (!this->CheckClientListHeight()) return; - - this->DrawWidgets(); + this->vscroll->SetCapacityFromWidget(this, WID_CL_MATRIX); } - void DrawWidget(const Rect &r, int widget) const override + void SetStringParameters(int widget) const override { - if (widget != WID_CL_PANEL) return; + switch (widget) { + case WID_CL_SERVER_NAME: + SetDParamStr(0, _settings_client.network.server_name); + break; - bool rtl = _current_text_dir == TD_RTL; - int icon_offset = (this->line_height - icon_size.height) / 2; - int text_offset = (this->line_height - FONT_HEIGHT_NORMAL) / 2; + case WID_CL_SERVER_VISIBILITY: + SetDParam(0, _server_visibility_dropdown[_settings_client.network.server_advertise]); + break; - uint y = r.top + WD_FRAMERECT_TOP; - uint left = r.left + WD_FRAMERECT_LEFT; - uint right = r.right - WD_FRAMERECT_RIGHT; - uint type_icon_width = this->server_client_width + this->icon_size.width + WD_FRAMERECT_LEFT; - - - uint type_left = rtl ? right - this->server_client_width : left; - uint type_right = rtl ? right : left + this->server_client_width - 1; - uint icon_left = rtl ? right - type_icon_width + WD_FRAMERECT_LEFT : left + this->server_client_width; - uint name_left = rtl ? left : left + type_icon_width; - uint name_right = rtl ? right - type_icon_width : right; - - int i = 0; - for (const NetworkClientInfo *ci : NetworkClientInfo::Iterate()) { - TextColour colour; - if (this->selected_item == i++) { // Selected item, highlight it - GfxFillRect(r.left + 1, y, r.right - 1, y + this->line_height - 1, PC_BLACK); - colour = TC_WHITE; - } else { - colour = TC_BLACK; - } - - if (ci->client_id == CLIENT_ID_SERVER) { - DrawString(type_left, type_right, y + text_offset, STR_NETWORK_SERVER, colour); - } else { - DrawString(type_left, type_right, y + text_offset, STR_NETWORK_CLIENT, colour); - } - - /* Filter out spectators */ - if (Company::IsValidID(ci->client_playas)) DrawCompanyIcon(ci->client_playas, icon_left, y + icon_offset); - - DrawString(name_left, name_right, y + text_offset, ci->client_name, colour); - - y += line_height; + case WID_CL_CLIENT_NAME: + SetDParamStr(0, _settings_client.network.client_name); + break; } } void OnClick(Point pt, int widget, int click_count) override { - /* Show the popup with option */ - if (this->selected_item != -1) { - int client_no = this->selected_item; - for (NetworkClientInfo *ci : NetworkClientInfo::Iterate()) { - if (client_no == 0) { - PopupClientList(ci->client_id, pt.x + this->left, pt.y + this->top); - break; - } - client_no--; + switch (widget) { + case WID_CL_SERVER_NAME_EDIT: + if (!_network_server) break; + + this->query_widget = WID_CL_SERVER_NAME_EDIT; + SetDParamStr(0, _settings_client.network.server_name); + ShowQueryString(STR_JUST_RAW_STRING, STR_NETWORK_CLIENT_LIST_SERVER_NAME_QUERY_CAPTION, NETWORK_NAME_LENGTH, this, CS_ALPHANUMERAL, QSF_LEN_IN_CHARS); + break; + + case WID_CL_CLIENT_NAME_EDIT: + this->query_widget = WID_CL_CLIENT_NAME_EDIT; + SetDParamStr(0, _settings_client.network.client_name); + ShowQueryString(STR_JUST_RAW_STRING, STR_NETWORK_CLIENT_LIST_PLAYER_NAME_QUERY_CAPTION, NETWORK_CLIENT_NAME_LENGTH, this, CS_ALPHANUMERAL, QSF_LEN_IN_CHARS); + break; + + case WID_CL_SERVER_VISIBILITY: + if (!_network_server) break; + + ShowDropDownMenu(this, _server_visibility_dropdown, _settings_client.network.server_advertise, WID_CL_SERVER_VISIBILITY, 0, 0); + break; + + case WID_CL_MATRIX: { + ButtonCommon *button = this->GetButtonAtPoint(pt); + if (button == nullptr) break; + + button->OnClick(this, pt); + break; } } } - void OnMouseOver(Point pt, int widget) override + bool OnTooltip(Point pt, int widget, TooltipCloseCondition close_cond) override { - /* -1 means we left the current window */ - if (pt.y == -1) { - this->selected_item = -1; - this->SetDirty(); - return; + switch (widget) { + case WID_CL_MATRIX: { + ButtonCommon *button = this->GetButtonAtPoint(pt); + if (button == nullptr) return false; + + GuiShowTooltips(this, button->tooltip, 0, nullptr, close_cond); + return true; + }; } - /* Find the new selected item (if any) */ - pt.y -= this->GetWidget(WID_CL_PANEL)->pos_y; - int item = -1; - if (IsInsideMM(pt.y, WD_FRAMERECT_TOP, this->GetWidget(WID_CL_PANEL)->current_y - WD_FRAMERECT_BOTTOM)) { - item = (pt.y - WD_FRAMERECT_TOP) / this->line_height; + return false; + } + + void OnDropdownSelect(int widget, int index) override + { + switch (widget) { + case WID_CL_SERVER_VISIBILITY: + if (!_network_server) break; + + _settings_client.network.server_advertise = (index != 0); + break; + + default: + NOT_REACHED(); } - /* It did not change.. no update! */ - if (item == this->selected_item) return; - this->selected_item = item; - - /* Repaint */ this->SetDirty(); } + + void OnQueryTextFinished(char *str) override + { + if (str == nullptr) return; + + switch (this->query_widget) { + default: NOT_REACHED(); + + case WID_CL_SERVER_NAME_EDIT: { + if (!_network_server) break; + + uint index; + GetSettingFromName("network.server_name", &index); + SetSettingValue(index, StrEmpty(str) ? "Unnamed Server" : str); + this->InvalidateData(); + break; + } + + case WID_CL_CLIENT_NAME_EDIT: { + if (!NetworkValidateClientName(str)) break; + + uint index; + GetSettingFromName("network.client_name", &index); + SetSettingValue(index, str); + this->InvalidateData(); + break; + } + + case WID_CL_COMPANY_JOIN: + NetworkClientRequestMove(this->join_company, str); + break; + } + } + + /** + * Draw the buttons for a single line in the matrix. + * + * The x-position in RTL is the most left or otherwise the most right pixel + * we can draw the buttons from. + * + * @param x The x-position to start with the buttons. Updated during this function. + * @param y The y-position to start with the buttons. + * @param buttons The buttons to draw. + */ + void DrawButtons(uint &x, uint y, const std::vector> &buttons) const + { + for (auto &button : buttons) { + bool rtl = _current_text_dir == TD_RTL; + + uint left = rtl ? x : x - button->width; + uint right = rtl ? x + button->width : x; + + int offset = std::max(0, ((int)(this->line_height + 1) - (int)button->height) / 2); + + DrawFrameRect(left, y + offset, right, y + offset + button->height, button->colour, FR_NONE); + DrawSprite(button->sprite, PAL_NONE, left + ScaleGUITrad(WD_FRAMERECT_LEFT), y + offset + ScaleGUITrad(WD_FRAMERECT_TOP)); + if (button->disabled) { + GfxFillRect(left + 1, y + offset + 1, right - 1, y + offset + button->height - 1, _colour_gradient[button->colour & 0xF][2], FILLRECT_CHECKER); + } + + int width = button->width + WD_FRAMERECT_LEFT + WD_FRAMERECT_RIGHT; + x += rtl ? width : -width; + } + } + + /** + * Draw a company and its clients on the matrix. + * @param c The company to draw. + * @param left The most left pixel of the line. + * @param right The most right pixel of the line. + * @param top The top of the first line. + * @param line The Nth line we are drawing. Updated during this function. + */ + void DrawCompany(const Company *c, uint left, uint right, uint top, uint &line) const + { + bool rtl = _current_text_dir == TD_RTL; + int text_y_offset = std::max(0, ((int)(this->line_height + 1) - (int)FONT_HEIGHT_NORMAL) / 2) + WD_MATRIX_BOTTOM; + + Dimension d = GetSpriteSize(SPR_COMPANY_ICON); + int offset = std::max(0, ((int)(this->line_height + 1) - (int)d.height) / 2); + + uint text_left = left + (rtl ? (uint)WD_FRAMERECT_LEFT : d.width + 8); + uint text_right = right - (rtl ? d.width + 8 : (uint)WD_FRAMERECT_RIGHT); + + uint line_start = this->vscroll->GetPosition(); + uint line_end = line_start + this->vscroll->GetCapacity(); + + uint y = top + (this->line_height * (line - line_start)); + + /* Draw the company line (if in range of scrollbar). */ + if (IsInsideMM(line, line_start, line_end)) { + uint x = rtl ? text_left : text_right; + + /* If there are buttons for this company, draw them. */ + auto button_find = this->buttons.find(line); + if (button_find != this->buttons.end()) { + this->DrawButtons(x, y, button_find->second); + } + + if (c == nullptr) { + DrawSprite(SPR_COMPANY_ICON, PALETTE_TO_GREY, rtl ? right - d.width - 4 : left + 4, y + offset); + DrawString(rtl ? x : text_left, rtl ? text_right : x, y + text_y_offset, STR_NETWORK_CLIENT_LIST_SPECTATORS, TC_SILVER); + } else { + DrawCompanyIcon(c->index, rtl ? right - d.width - 4 : left + 4, y + offset); + + SetDParam(0, c->index); + SetDParam(1, c->index); + DrawString(rtl ? x : text_left, rtl ? text_right : x, y + text_y_offset, STR_COMPANY_NAME, TC_SILVER); + } + } + + y += this->line_height; + line++; + + for (const NetworkClientInfo *ci : NetworkClientInfo::Iterate()) { + if (c != nullptr && ci->client_playas != c->index) continue; + if (c == nullptr && ci->client_playas != COMPANY_SPECTATOR) continue; + + /* Draw the player line (if in range of scrollbar). */ + if (IsInsideMM(line, line_start, line_end)) { + uint x = rtl ? text_left : text_right; + + /* If there are buttons for this client, draw them. */ + auto button_find = this->buttons.find(line); + if (button_find != this->buttons.end()) { + this->DrawButtons(x, y, button_find->second); + } + + StringID client_string = STR_JUST_RAW_STRING; + + if (ci->client_id == CLIENT_ID_SERVER) { + client_string = STR_NETWORK_CLIENT_LIST_PLAYER_HOST; + } + if (ci->client_id == _network_own_client_id) { + client_string = STR_NETWORK_CLIENT_LIST_PLAYER_SELF; + } + + SetDParamStr(0, ci->client_name); + DrawString(rtl ? x : text_left + CLIENT_OFFSET_LEFT, rtl ? text_right - CLIENT_OFFSET_LEFT : x, y + text_y_offset, client_string, TC_BLACK); + } + + y += this->line_height; + line++; + } + } + + void DrawWidget(const Rect &r, int widget) const override + { + switch (widget) { + case WID_CL_MATRIX: { + uint line = 0; + + for (const Company *c : Company::Iterate()) { + this->DrawCompany(c, r.left, r.right, r.top, line); + } + /* Specators */ + this->DrawCompany(nullptr, r.left, r.right, r.top, line); + + break; + } + } + } }; void ShowClientList() diff --git a/src/network/network_server.cpp b/src/network/network_server.cpp index a0d1a00666..746077abdb 100644 --- a/src/network/network_server.cpp +++ b/src/network/network_server.cpp @@ -288,13 +288,14 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::CloseConnection(NetworkRecvSta _network_clients_connected--; DeleteWindowById(WC_CLIENT_LIST_POPUP, this->client_id); - SetWindowDirty(WC_CLIENT_LIST, 0); this->SendPackets(true); delete this->GetInfo(); delete this; + InvalidateWindowData(WC_CLIENT_LIST, 0); + return status; } @@ -1043,6 +1044,7 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::Receive_CLIENT_MAP_OK(Packet * this->GetClientName(client_name, lastof(client_name)); NetworkTextMessage(NETWORK_ACTION_JOIN, CC_DEFAULT, false, client_name, nullptr, this->client_id); + InvalidateWindowData(WC_CLIENT_LIST, 0); /* Mark the client as pre-active, and wait for an ACK * so we know he is done loading and in sync with us */ @@ -2061,6 +2063,9 @@ void NetworkServerDoMove(ClientID client_id, CompanyID company_id) NetworkAction action = (company_id == COMPANY_SPECTATOR) ? NETWORK_ACTION_COMPANY_SPECTATOR : NETWORK_ACTION_COMPANY_JOIN; NetworkServerSendChat(action, DESTTYPE_BROADCAST, 0, "", client_id, company_id + 1); + + InvalidateWindowClassesData(WC_CLIENT_LIST_POPUP); + InvalidateWindowData(WC_CLIENT_LIST, 0); } /** diff --git a/src/table/sprites.h b/src/table/sprites.h index 9071e61dc6..039d50b9de 100644 --- a/src/table/sprites.h +++ b/src/table/sprites.h @@ -54,7 +54,7 @@ static const SpriteID SPR_LARGE_SMALL_WINDOW = 682; /** Extra graphic spritenumbers */ static const SpriteID SPR_OPENTTD_BASE = 4896; -static const uint16 OPENTTD_SPRITE_COUNT = 186; +static const uint16 OPENTTD_SPRITE_COUNT = 189; /* Halftile-selection sprites */ static const SpriteID SPR_HALFTILE_SELECTION_FLAT = SPR_OPENTTD_BASE; @@ -166,6 +166,10 @@ static const SpriteID SPR_WINDOW_DEFSIZE = SPR_OPENTTD_BASE + 168; static const SpriteID SPR_RENAME = SPR_OPENTTD_BASE + 184; static const SpriteID SPR_GOTO_LOCATION = SPR_OPENTTD_BASE + 185; +static const SpriteID SPR_CHAT = SPR_OPENTTD_BASE + 186; +static const SpriteID SPR_ADMIN = SPR_OPENTTD_BASE + 187; +static const SpriteID SPR_JOIN = SPR_OPENTTD_BASE + 188; + static const SpriteID SPR_IMG_CARGOFLOW = SPR_OPENTTD_BASE + 174; static const SpriteID SPR_SIGNALS_BASE = SPR_OPENTTD_BASE + OPENTTD_SPRITE_COUNT; diff --git a/src/toolbar_gui.cpp b/src/toolbar_gui.cpp index ea50661e4c..6a65382b44 100644 --- a/src/toolbar_gui.cpp +++ b/src/toolbar_gui.cpp @@ -205,8 +205,7 @@ static void PopupMainToolbMenu(Window *w, int widget, StringID string, int count /** Enum for the Company Toolbar's network related buttons */ static const int CTMN_CLIENT_LIST = -1; ///< Show the client list static const int CTMN_NEW_COMPANY = -2; ///< Create a new company -static const int CTMN_SPECTATE = -3; ///< Become spectator -static const int CTMN_SPECTATOR = -4; ///< Show a company window as spectator +static const int CTMN_SPECTATOR = -3; ///< Show a company window as spectator /** * Pop up a generic company list menu. @@ -227,8 +226,6 @@ static void PopupMainCompanyToolbMenu(Window *w, int widget, int grey = 0) if (_local_company == COMPANY_SPECTATOR) { list.emplace_back(new DropDownListStringItem(STR_NETWORK_COMPANY_LIST_NEW_COMPANY, CTMN_NEW_COMPANY, NetworkMaxCompaniesReached())); - } else { - list.emplace_back(new DropDownListStringItem(STR_NETWORK_COMPANY_LIST_SPECTATE, CTMN_SPECTATE, NetworkMaxSpectatorsReached())); } break; @@ -619,15 +616,6 @@ static CallBackFunction MenuClickCompany(int index) NetworkSendCommand(0, CCA_NEW, 0, CMD_COMPANY_CTRL, nullptr, nullptr, _local_company); } return CBF_NONE; - - case CTMN_SPECTATE: - if (_network_server) { - NetworkServerDoMove(CLIENT_ID_SERVER, COMPANY_SPECTATOR); - MarkWholeScreenDirty(); - } else { - NetworkClientRequestMove(COMPANY_SPECTATOR); - } - return CBF_NONE; } } ShowCompany((CompanyID)index); diff --git a/src/widgets/network_widget.h b/src/widgets/network_widget.h index 79d33fb067..a453b085cf 100644 --- a/src/widgets/network_widget.h +++ b/src/widgets/network_widget.h @@ -96,7 +96,16 @@ enum NetworkLobbyWidgets { /** Widgets of the #NetworkClientListWindow class. */ enum ClientListWidgets { - WID_CL_PANEL, ///< Panel of the window. + WID_CL_PANEL, ///< Panel of the window. + WID_CL_SERVER_SELECTOR, ///< Selector to hide the server frame. + WID_CL_SERVER_NAME, ///< Server name. + WID_CL_SERVER_NAME_EDIT, ///< Edit button for server name. + WID_CL_SERVER_VISIBILITY, ///< Server visibility. + WID_CL_CLIENT_NAME, ///< Client name. + WID_CL_CLIENT_NAME_EDIT, ///< Edit button for client name. + WID_CL_MATRIX, ///< Company/client list. + WID_CL_SCROLLBAR, ///< Scrollbar for company/client list. + WID_CL_COMPANY_JOIN, ///< Used for QueryWindow when a company has a password. }; /** Widgets of the #NetworkClientListPopupWindow class. */ From ff708c2c659477da04fab108ca8e46a60c0d60b0 Mon Sep 17 00:00:00 2001 From: Patric Stout Date: Fri, 23 Apr 2021 01:52:57 +0200 Subject: [PATCH 150/268] Add: admin menu for companies in multiplayer games You can now easily do: - a password reset (unlock) - remove an empty company (reset company) --- src/company_cmd.cpp | 3 +- src/lang/english.txt | 12 +- src/network/network_gui.cpp | 234 +++++++++++++-------------------- src/network/network_server.cpp | 3 - src/widgets/network_widget.h | 5 - src/window_type.h | 6 - 6 files changed, 97 insertions(+), 166 deletions(-) diff --git a/src/company_cmd.cpp b/src/company_cmd.cpp index c3483a9bfe..063d32d3f3 100644 --- a/src/company_cmd.cpp +++ b/src/company_cmd.cpp @@ -571,7 +571,6 @@ Company *DoStartupNewCompany(bool is_ai, CompanyID company = INVALID_COMPANY) GeneratePresidentName(c); SetWindowDirty(WC_GRAPH_LEGEND, 0); - InvalidateWindowClassesData(WC_CLIENT_LIST_POPUP); InvalidateWindowData(WC_CLIENT_LIST, 0); InvalidateWindowData(WC_LINKGRAPH_LEGEND, 0); BuildOwnerLegend(); @@ -909,6 +908,8 @@ CommandCost CmdCompanyCtrl(TileIndex tile, DoCommandFlag flags, uint32 p1, uint3 CompanyAdminRemove(c_index, (CompanyRemoveReason)reason); if (StoryPage::GetNumItems() == 0 || Goal::GetNumItems() == 0) InvalidateWindowData(WC_MAIN_TOOLBAR, 0); + InvalidateWindowData(WC_CLIENT_LIST, 0); + break; } diff --git a/src/lang/english.txt b/src/lang/english.txt index 13cde55d95..a6d70e26d6 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -2124,10 +2124,6 @@ STR_NETWORK_NEED_COMPANY_PASSWORD_CAPTION :{WHITE}Company STR_NETWORK_COMPANY_LIST_CLIENT_LIST :Online players STR_NETWORK_COMPANY_LIST_NEW_COMPANY :New company -# Network client list popup for clients -STR_NETWORK_CLIENTLIST_KICK :Kick -STR_NETWORK_CLIENTLIST_BAN :Ban - # Network client list STR_NETWORK_CLIENT_LIST_CAPTION :{WHITE}Multiplayer STR_NETWORK_CLIENT_LIST_SERVER :{BLACK}Server @@ -2144,13 +2140,19 @@ STR_NETWORK_CLIENT_LIST_PLAYER_NAME_EDIT_TOOLTIP :{BLACK}Edit you STR_NETWORK_CLIENT_LIST_PLAYER_NAME_QUERY_CAPTION :Your player name STR_NETWORK_CLIENT_LIST_PLAYER_HOST :{WHITE}(host) {BLACK}{RAW_STRING} STR_NETWORK_CLIENT_LIST_PLAYER_SELF :{WHITE}(you) {BLACK}{RAW_STRING} -STR_NETWORK_CLIENT_LIST_ADMIN_TOOLTIP :{BLACK}Administrative actions to perform for this client +STR_NETWORK_CLIENT_LIST_ADMIN_CLIENT_TOOLTIP :{BLACK}Administrative actions to perform for this client +STR_NETWORK_CLIENT_LIST_ADMIN_COMPANY_TOOLTIP :{BLACK}Administrative actions to perform for this company STR_NETWORK_CLIENT_LIST_JOIN_TOOLTIP :{BLACK}Join this company STR_NETWORK_CLIENT_LIST_CHAT_CLIENT_TOOLTIP :{BLACK}Send a message to this player STR_NETWORK_CLIENT_LIST_CHAT_COMPANY_TOOLTIP :{BLACK}Send a message to all players of this company STR_NETWORK_CLIENT_LIST_CHAT_SPECTATOR_TOOLTIP :{BLACK}Send a message to all spectators STR_NETWORK_CLIENT_LIST_SPECTATORS :Spectators +STR_NETWORK_CLIENT_LIST_ADMIN_CLIENT_KICK :Kick +STR_NETWORK_CLIENT_LIST_ADMIN_CLIENT_BAN :Ban +STR_NETWORK_CLIENT_LIST_ADMIN_COMPANY_RESET :Delete +STR_NETWORK_CLIENT_LIST_ADMIN_COMPANY_UNLOCK :Password unlock + STR_NETWORK_SERVER :Server STR_NETWORK_CLIENT :Client STR_NETWORK_SPECTATORS :Spectators diff --git a/src/network/network_gui.cpp b/src/network/network_gui.cpp index fcfeded5a5..ceeca633e4 100644 --- a/src/network/network_gui.cpp +++ b/src/network/network_gui.cpp @@ -17,6 +17,7 @@ #include "network.h" #include "network_base.h" #include "network_content.h" +#include "network_server.h" #include "../gui.h" #include "network_udp.h" #include "../window_func.h" @@ -26,6 +27,7 @@ #include "../querystring_gui.h" #include "../sortlist_type.h" #include "../company_func.h" +#include "../command_func.h" #include "../core/geometry_func.hpp" #include "../genworld.h" #include "../map_type.h" @@ -1570,146 +1572,6 @@ NetworkCompanyInfo *GetLobbyCompanyInfo(CompanyID company) extern void DrawCompanyIcon(CompanyID cid, int x, int y); -/** - * Prototype for ClientList actions. - * @param ci The information about the current client. - */ -typedef void ClientList_Action_Proc(const NetworkClientInfo *ci); - -static const NWidgetPart _nested_client_list_popup_widgets[] = { - NWidget(WWT_PANEL, COLOUR_GREY, WID_CLP_PANEL), EndContainer(), -}; - -static WindowDesc _client_list_popup_desc( - WDP_AUTO, nullptr, 0, 0, - WC_CLIENT_LIST_POPUP, WC_CLIENT_LIST, - 0, - _nested_client_list_popup_widgets, lengthof(_nested_client_list_popup_widgets) -); - -/* Here we start to define the options out of the menu */ -static void ClientList_Kick(const NetworkClientInfo *ci) -{ - NetworkServerKickClient(ci->client_id, nullptr); -} - -static void ClientList_Ban(const NetworkClientInfo *ci) -{ - NetworkServerKickOrBanIP(ci->client_id, true, nullptr); -} - -/** Popup selection window to chose an action to perform */ -struct NetworkClientListPopupWindow : Window { - /** Container for actions that can be executed. */ - struct ClientListAction { - StringID name; ///< Name of the action to execute - ClientList_Action_Proc *proc; ///< Action to execute - }; - - uint sel_index; - ClientID client_id; - Point desired_location; - std::vector actions; ///< Actions to execute - - /** - * Add an action to the list of actions to execute. - * @param name the name of the action - * @param proc the procedure to execute for the action - */ - inline void AddAction(StringID name, ClientList_Action_Proc *proc) - { - this->actions.push_back({name, proc}); - } - - NetworkClientListPopupWindow(WindowDesc *desc, int x, int y, ClientID client_id) : - Window(desc), - sel_index(0), client_id(client_id) - { - this->desired_location.x = x; - this->desired_location.y = y; - - const NetworkClientInfo *ci = NetworkClientInfo::GetByClientID(client_id); - - /* A server can kick clients (but not himself). */ - if (_network_server && _network_own_client_id != ci->client_id) { - this->AddAction(STR_NETWORK_CLIENTLIST_KICK, &ClientList_Kick); - this->AddAction(STR_NETWORK_CLIENTLIST_BAN, &ClientList_Ban); - } - - this->InitNested(client_id); - CLRBITS(this->flags, WF_WHITE_BORDER); - } - - Point OnInitialPosition(int16 sm_width, int16 sm_height, int window_number) override - { - return this->desired_location; - } - - void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize) override - { - Dimension d = *size; - for (const ClientListAction &action : this->actions) { - d = maxdim(GetStringBoundingBox(action.name), d); - } - - d.height *= (uint)this->actions.size(); - d.width += WD_FRAMERECT_LEFT + WD_FRAMERECT_RIGHT + 4 + 4; // Give the list a bit of padding on both sides. - d.height += WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM; - *size = d; - } - - void DrawWidget(const Rect &r, int widget) const override - { - /* Draw the actions */ - int sel = this->sel_index; - int y = r.top + WD_FRAMERECT_TOP; - for (const ClientListAction &action : this->actions) { - TextColour colour; - if (sel-- == 0) { // Selected item, highlight it - GfxFillRect(r.left + 1, y, r.right - 1, y + FONT_HEIGHT_NORMAL - 1, PC_BLACK); - colour = TC_WHITE; - } else { - colour = TC_BLACK; - } - - DrawString(r.left + WD_FRAMERECT_LEFT + 4, r.right - WD_FRAMERECT_RIGHT - 4, y, action.name, colour); - y += FONT_HEIGHT_NORMAL; - } - } - - void OnMouseLoop() override - { - /* We selected an action */ - uint index = (_cursor.pos.y - this->top - WD_FRAMERECT_TOP) / FONT_HEIGHT_NORMAL; - - if (_left_button_down) { - if (index == this->sel_index || index >= this->actions.size()) return; - - this->sel_index = index; - this->SetDirty(); - } else { - if (index < this->actions.size() && _cursor.pos.y >= this->top) { - const NetworkClientInfo *ci = NetworkClientInfo::GetByClientID(this->client_id); - if (ci != nullptr) this->actions[index].proc(ci); - } - - DeleteWindowByClass(WC_CLIENT_LIST_POPUP); - } - } -}; - -/** - * Show the popup (action list) - */ -static void PopupClientList(ClientID client_id, int x, int y) -{ - DeleteWindowByClass(WC_CLIENT_LIST_POPUP); - - if (NetworkClientInfo::GetByClientID(client_id) == nullptr) return; - - new NetworkClientListPopupWindow(&_client_list_popup_desc, x, y, client_id); -} - static const NWidgetPart _nested_client_list_widgets[] = { NWidget(NWID_HORIZONTAL), NWidget(WWT_CLOSEBOX, COLOUR_GREY), @@ -1758,6 +1620,17 @@ static WindowDesc _client_list_desc( _nested_client_list_widgets, lengthof(_nested_client_list_widgets) ); +/** + * The possibly entries in a DropDown for an admin. + * Client and companies are mixed; they just have to be unique. + */ +enum DropDownAdmin { + DD_CLIENT_ADMIN_KICK, + DD_CLIENT_ADMIN_BAN, + DD_COMPANY_ADMIN_RESET, + DD_COMPANY_ADMIN_UNLOCK, +}; + /** * Button shown for either a company or client in the client-list. * @@ -1774,11 +1647,11 @@ public: uint height; ///< Calculated height of the button. uint width; ///< Calculated width of the button. - ButtonCommon(SpriteID sprite, StringID tooltip, Colours colour) : + ButtonCommon(SpriteID sprite, StringID tooltip, Colours colour, bool disabled = false) : sprite(sprite), tooltip(tooltip), colour(colour), - disabled(false) + disabled(disabled) { Dimension d = GetSpriteSize(sprite); this->height = d.height + ScaleGUITrad(WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM); @@ -1803,8 +1676,8 @@ private: ButtonCallback proc; ///< Callback proc to call when button is pressed. public: - Button(SpriteID sprite, StringID tooltip, Colours colour, T id, ButtonCallback proc) : - ButtonCommon(sprite, tooltip, colour), + Button(SpriteID sprite, StringID tooltip, Colours colour, T id, ButtonCallback proc, bool disabled = false) : + ButtonCommon(sprite, tooltip, colour, disabled), id(id), proc(proc) { @@ -1830,6 +1703,9 @@ private: ClientListWidgets query_widget; ///< During a query this tracks what widget caused the query. CompanyID join_company; ///< During query for company password, this stores what company we wanted to join. + ClientID dd_client_id; ///< During admin dropdown, track which client this was for. + CompanyID dd_company_id; ///< During admin dropdown, track which company this was for. + Scrollbar *vscroll; ///< Vertical scrollbar of this window. uint line_height; ///< Current lineheight of each entry in the matrix. uint line_count; ///< Amount of lines in the matrix. @@ -1877,9 +1753,41 @@ private: */ static void OnClickClientAdmin(NetworkClientListWindow *w, Point pt, ClientID client_id) { - PopupClientList(client_id, pt.x + w->left, pt.y + w->top); + DropDownList list; + list.emplace_back(new DropDownListStringItem(STR_NETWORK_CLIENT_LIST_ADMIN_CLIENT_KICK, DD_CLIENT_ADMIN_KICK, false)); + list.emplace_back(new DropDownListStringItem(STR_NETWORK_CLIENT_LIST_ADMIN_CLIENT_BAN, DD_CLIENT_ADMIN_BAN, false)); + + Rect wi_rect; + wi_rect.left = pt.x; + wi_rect.right = pt.x; + wi_rect.top = pt.y; + wi_rect.bottom = pt.y; + + w->dd_client_id = client_id; + ShowDropDownListAt(w, std::move(list), -1, WID_CL_MATRIX, wi_rect, COLOUR_GREY, true, true); } + /** + * Admin button on a Company is clicked. + * @param w The instance of this window. + * @param pt The point where this button was clicked. + * @param company_id The company this button was assigned to. + */ + static void OnClickCompanyAdmin(NetworkClientListWindow *w, Point pt, CompanyID company_id) + { + DropDownList list; + list.emplace_back(new DropDownListStringItem(STR_NETWORK_CLIENT_LIST_ADMIN_COMPANY_RESET, DD_COMPANY_ADMIN_RESET, NetworkCompanyHasClients(company_id))); + list.emplace_back(new DropDownListStringItem(STR_NETWORK_CLIENT_LIST_ADMIN_COMPANY_UNLOCK, DD_COMPANY_ADMIN_UNLOCK, !NetworkCompanyIsPassworded(company_id))); + + Rect wi_rect; + wi_rect.left = pt.x; + wi_rect.right = pt.x; + wi_rect.top = pt.y; + wi_rect.bottom = pt.y; + + w->dd_company_id = company_id; + ShowDropDownListAt(w, std::move(list), -1, WID_CL_MATRIX, wi_rect, COLOUR_GREY, true, true); + } /** * Chat button on a Client is clicked. * @param w The instance of this window. @@ -1900,6 +1808,7 @@ private: { ButtonCommon *chat_button = new CompanyButton(SPR_CHAT, company_id == COMPANY_SPECTATOR ? STR_NETWORK_CLIENT_LIST_CHAT_SPECTATOR_TOOLTIP : STR_NETWORK_CLIENT_LIST_CHAT_COMPANY_TOOLTIP, COLOUR_ORANGE, company_id, &NetworkClientListWindow::OnClickCompanyChat); + if (_network_server) this->buttons[line_count].emplace_back(new CompanyButton(SPR_ADMIN, STR_NETWORK_CLIENT_LIST_ADMIN_COMPANY_TOOLTIP, COLOUR_RED, company_id, &NetworkClientListWindow::OnClickCompanyAdmin)); this->buttons[line_count].emplace_back(chat_button); if (own_ci->client_playas != company_id) this->buttons[line_count].emplace_back(new CompanyButton(SPR_JOIN, STR_NETWORK_CLIENT_LIST_JOIN_TOOLTIP, COLOUR_ORANGE, company_id, &NetworkClientListWindow::OnClickCompanyJoin)); @@ -1910,12 +1819,13 @@ private: if (ci->client_playas != company_id) continue; has_players = true; + if (_network_server) this->buttons[line_count].emplace_back(new ClientButton(SPR_ADMIN, STR_NETWORK_CLIENT_LIST_ADMIN_CLIENT_TOOLTIP, COLOUR_RED, ci->client_id, &NetworkClientListWindow::OnClickClientAdmin, _network_own_client_id == ci->client_id)); if (_network_own_client_id != ci->client_id) this->buttons[line_count].emplace_back(new ClientButton(SPR_CHAT, STR_NETWORK_CLIENT_LIST_CHAT_CLIENT_TOOLTIP, COLOUR_ORANGE, ci->client_id, &NetworkClientListWindow::OnClickClientChat)); - if (_network_server && _network_own_client_id != ci->client_id) this->buttons[line_count].emplace_back(new ClientButton(SPR_ADMIN, STR_NETWORK_CLIENT_LIST_ADMIN_TOOLTIP, COLOUR_RED, ci->client_id, &NetworkClientListWindow::OnClickClientAdmin)); this->line_count += 1; } + /* Disable the chat button when there are players in this company. */ chat_button->disabled = !has_players; } @@ -2084,6 +1994,14 @@ public: return false; } + void OnDropdownClose(Point pt, int widget, int index, bool instant_close) override + { + /* If you close the dropdown outside the list, don't take any action. */ + if (widget == WID_CL_MATRIX) return; + + Window::OnDropdownClose(pt, widget, index, instant_close); + } + void OnDropdownSelect(int widget, int index) override { switch (widget) { @@ -2093,6 +2011,30 @@ public: _settings_client.network.server_advertise = (index != 0); break; + case WID_CL_MATRIX: + switch (index) { + case DD_CLIENT_ADMIN_KICK: + NetworkServerKickClient(this->dd_client_id, nullptr); + break; + + case DD_CLIENT_ADMIN_BAN: + NetworkServerKickOrBanIP(this->dd_client_id, true, nullptr); + break; + + case DD_COMPANY_ADMIN_RESET: + if (NetworkCompanyHasClients(this->dd_company_id)) break; + DoCommandP(0, CCA_DELETE | this->dd_company_id << 16 | CRR_MANUAL << 24, 0, CMD_COMPANY_CTRL); + break; + + case DD_COMPANY_ADMIN_UNLOCK: + NetworkServerSetCompanyPassword(this->dd_company_id, "", false); + break; + + default: + NOT_REACHED(); + } + break; + default: NOT_REACHED(); } diff --git a/src/network/network_server.cpp b/src/network/network_server.cpp index 746077abdb..f6b3e1192e 100644 --- a/src/network/network_server.cpp +++ b/src/network/network_server.cpp @@ -287,8 +287,6 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::CloseConnection(NetworkRecvSta extern byte _network_clients_connected; _network_clients_connected--; - DeleteWindowById(WC_CLIENT_LIST_POPUP, this->client_id); - this->SendPackets(true); delete this->GetInfo(); @@ -2064,7 +2062,6 @@ void NetworkServerDoMove(ClientID client_id, CompanyID company_id) NetworkAction action = (company_id == COMPANY_SPECTATOR) ? NETWORK_ACTION_COMPANY_SPECTATOR : NETWORK_ACTION_COMPANY_JOIN; NetworkServerSendChat(action, DESTTYPE_BROADCAST, 0, "", client_id, company_id + 1); - InvalidateWindowClassesData(WC_CLIENT_LIST_POPUP); InvalidateWindowData(WC_CLIENT_LIST, 0); } diff --git a/src/widgets/network_widget.h b/src/widgets/network_widget.h index a453b085cf..2ed94d2849 100644 --- a/src/widgets/network_widget.h +++ b/src/widgets/network_widget.h @@ -108,11 +108,6 @@ enum ClientListWidgets { WID_CL_COMPANY_JOIN, ///< Used for QueryWindow when a company has a password. }; -/** Widgets of the #NetworkClientListPopupWindow class. */ -enum ClientListPopupWidgets { - WID_CLP_PANEL, ///< Panel of the window. -}; - /** Widgets of the #NetworkJoinStatusWindow class. */ enum NetworkJoinStatusWidgets { WID_NJS_BACKGROUND, ///< Background of the window. diff --git a/src/window_type.h b/src/window_type.h index e4b08e6e5d..2b486fbdf6 100644 --- a/src/window_type.h +++ b/src/window_type.h @@ -471,12 +471,6 @@ enum WindowClass { */ WC_CLIENT_LIST, - /** - * Popup for the client list; %Window numbers: - * - #ClientID = #ClientListPopupWidgets - */ - WC_CLIENT_LIST_POPUP, - /** * Network status window; %Window numbers: * - #WN_NETWORK_STATUS_WINDOW_JOIN = #NetworkJoinStatusWidgets From 54f69deb0c627b602da949e8c3fb5ed40daaeaa0 Mon Sep 17 00:00:00 2001 From: Patric Stout Date: Fri, 23 Apr 2021 12:55:09 +0200 Subject: [PATCH 151/268] Add: ask for confirmation on admin actions in network games --- src/lang/english.txt | 6 +++ src/network/network_gui.cpp | 80 ++++++++++++++++++++++++++++++++++--- 2 files changed, 80 insertions(+), 6 deletions(-) diff --git a/src/lang/english.txt b/src/lang/english.txt index a6d70e26d6..f22e9f7de4 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -2153,6 +2153,12 @@ STR_NETWORK_CLIENT_LIST_ADMIN_CLIENT_BAN :Ban STR_NETWORK_CLIENT_LIST_ADMIN_COMPANY_RESET :Delete STR_NETWORK_CLIENT_LIST_ADMIN_COMPANY_UNLOCK :Password unlock +STR_NETWORK_CLIENT_LIST_ASK_CAPTION :{WHITE}Admin action +STR_NETWORK_CLIENT_LIST_ASK_CLIENT_KICK :{YELLOW}Are you sure you want to kick player '{RAW_STRING}'? +STR_NETWORK_CLIENT_LIST_ASK_CLIENT_BAN :{YELLOW}Are you sure you want to ban player '{RAW_STRING}'? +STR_NETWORK_CLIENT_LIST_ASK_COMPANY_RESET :{YELLOW}Are you sure you want to delete company '{COMPANY}'? +STR_NETWORK_CLIENT_LIST_ASK_COMPANY_UNLOCK :{YELLOW}Are you sure you want to reset the password of company '{COMPANY}'? + STR_NETWORK_SERVER :Server STR_NETWORK_CLIENT :Client STR_NETWORK_SPECTATORS :Spectators diff --git a/src/network/network_gui.cpp b/src/network/network_gui.cpp index ceeca633e4..7aaabb3c06 100644 --- a/src/network/network_gui.cpp +++ b/src/network/network_gui.cpp @@ -54,6 +54,9 @@ static void ShowNetworkStartServerWindow(); static void ShowNetworkLobbyWindow(NetworkGameList *ngl); +static ClientID _admin_client_id = INVALID_CLIENT_ID; ///< For what client a confirmation window is open. +static CompanyID _admin_company_id = INVALID_COMPANY; ///< For what company a confirmation window is open. + /** * Visibility of the server. Public servers advertise, where private servers * do not. @@ -1631,6 +1634,49 @@ enum DropDownAdmin { DD_COMPANY_ADMIN_UNLOCK, }; +/** + * Callback function for admin command to kick client. + * @param w The window which initiated the confirmation dialog. + * @param confirmed Iff the user pressed Yes. + */ +static void AdminClientKickCallback(Window *w, bool confirmed) +{ + if (confirmed) NetworkServerKickClient(_admin_client_id, nullptr); +} + +/** + * Callback function for admin command to ban client. + * @param w The window which initiated the confirmation dialog. + * @param confirmed Iff the user pressed Yes. + */ +static void AdminClientBanCallback(Window *w, bool confirmed) +{ + if (confirmed) NetworkServerKickOrBanIP(_admin_client_id, true, nullptr); +} + +/** + * Callback function for admin command to reset company. + * @param w The window which initiated the confirmation dialog. + * @param confirmed Iff the user pressed Yes. + */ +static void AdminCompanyResetCallback(Window *w, bool confirmed) +{ + if (confirmed) { + if (NetworkCompanyHasClients(_admin_company_id)) return; + DoCommandP(0, CCA_DELETE | _admin_company_id << 16 | CRR_MANUAL << 24, 0, CMD_COMPANY_CTRL); + } +} + +/** + * Callback function for admin command to unlock company. + * @param w The window which initiated the confirmation dialog. + * @param confirmed Iff the user pressed Yes. + */ +static void AdminCompanyUnlockCallback(Window *w, bool confirmed) +{ + if (confirmed) NetworkServerSetCompanyPassword(_admin_company_id, "", false); +} + /** * Button shown for either a company or client in the client-list. * @@ -2011,29 +2057,51 @@ public: _settings_client.network.server_advertise = (index != 0); break; - case WID_CL_MATRIX: + case WID_CL_MATRIX: { + StringID text = STR_NULL; + QueryCallbackProc *callback = nullptr; + switch (index) { case DD_CLIENT_ADMIN_KICK: - NetworkServerKickClient(this->dd_client_id, nullptr); + _admin_client_id = this->dd_client_id; + text = STR_NETWORK_CLIENT_LIST_ASK_CLIENT_KICK; + callback = AdminClientKickCallback; + SetDParamStr(0, NetworkClientInfo::GetByClientID(_admin_client_id)->client_name); break; case DD_CLIENT_ADMIN_BAN: - NetworkServerKickOrBanIP(this->dd_client_id, true, nullptr); + _admin_client_id = this->dd_client_id; + text = STR_NETWORK_CLIENT_LIST_ASK_CLIENT_BAN; + callback = AdminClientBanCallback; + SetDParamStr(0, NetworkClientInfo::GetByClientID(_admin_client_id)->client_name); break; case DD_COMPANY_ADMIN_RESET: - if (NetworkCompanyHasClients(this->dd_company_id)) break; - DoCommandP(0, CCA_DELETE | this->dd_company_id << 16 | CRR_MANUAL << 24, 0, CMD_COMPANY_CTRL); + _admin_company_id = this->dd_company_id; + text = STR_NETWORK_CLIENT_LIST_ASK_COMPANY_RESET; + callback = AdminCompanyResetCallback; + SetDParam(0, _admin_company_id); break; case DD_COMPANY_ADMIN_UNLOCK: - NetworkServerSetCompanyPassword(this->dd_company_id, "", false); + _admin_company_id = this->dd_company_id; + text = STR_NETWORK_CLIENT_LIST_ASK_COMPANY_UNLOCK; + callback = AdminCompanyUnlockCallback; + SetDParam(0, _admin_company_id); break; default: NOT_REACHED(); } + + assert(text != STR_NULL); + assert(callback != nullptr); + + /* Always ask confirmation for all admin actions. */ + ShowQuery(STR_NETWORK_CLIENT_LIST_ASK_CAPTION, text, this, callback); + break; + } default: NOT_REACHED(); From c2e116a3d3240daf6f1f27a0847efe10a0489adc Mon Sep 17 00:00:00 2001 From: Patric Stout Date: Fri, 23 Apr 2021 15:01:02 +0200 Subject: [PATCH 152/268] Change: track hover position on Online Players GUI Especially if there are many players online, trying to chat with the right one can be a visual challenge. This can be solved by highlighting the row you are on. This visual cue is often enough for humans to find the right row. --- src/network/network_gui.cpp | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/network/network_gui.cpp b/src/network/network_gui.cpp index 7aaabb3c06..6eac3e8d7e 100644 --- a/src/network/network_gui.cpp +++ b/src/network/network_gui.cpp @@ -1755,6 +1755,7 @@ private: Scrollbar *vscroll; ///< Vertical scrollbar of this window. uint line_height; ///< Current lineheight of each entry in the matrix. uint line_count; ///< Amount of lines in the matrix. + int hover_index; ///< Index of the current line we are hovering over, or -1 if none. std::map>> buttons; ///< Per line which buttons are available. @@ -2261,6 +2262,11 @@ public: case WID_CL_MATRIX: { uint line = 0; + if (this->hover_index >= 0) { + uint offset = this->hover_index * this->line_height; + GfxFillRect(r.left + 2, r.top + offset, r.right - 1, r.top + offset + this->line_height - 1, GREY_SCALE(9)); + } + for (const Company *c : Company::Iterate()) { this->DrawCompany(c, r.left, r.right, r.top, line); } @@ -2271,6 +2277,24 @@ public: } } } + + virtual void OnMouseLoop() override + { + if (GetWidgetFromPos(this, _cursor.pos.x - this->left, _cursor.pos.y - this->top) != WID_CL_MATRIX) { + this->hover_index = -1; + this->SetDirty(); + return; + } + + NWidgetBase *nwi = this->GetWidget(WID_CL_MATRIX); + int y = _cursor.pos.y - this->top - nwi->pos_y - 2; + int index = y / this->line_height; + + if (index != this->hover_index) { + this->hover_index = index; + this->SetDirty(); + } + } }; void ShowClientList() From a924b2ff81844c8d43ad1eaa25dea9642815738c Mon Sep 17 00:00:00 2001 From: Patric Stout Date: Fri, 23 Apr 2021 16:22:01 +0200 Subject: [PATCH 153/268] Add: move "New Company" inside the Online Players GUI --- src/lang/english.txt | 3 +- src/network/network_gui.cpp | 62 ++++++++++++++++++++++++++++++------- src/toolbar_gui.cpp | 15 +-------- 3 files changed, 54 insertions(+), 26 deletions(-) diff --git a/src/lang/english.txt b/src/lang/english.txt index f22e9f7de4..33ccca2a41 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -2122,7 +2122,6 @@ STR_NETWORK_NEED_COMPANY_PASSWORD_CAPTION :{WHITE}Company # Network company list added strings STR_NETWORK_COMPANY_LIST_CLIENT_LIST :Online players -STR_NETWORK_COMPANY_LIST_NEW_COMPANY :New company # Network client list STR_NETWORK_CLIENT_LIST_CAPTION :{WHITE}Multiplayer @@ -2147,6 +2146,8 @@ STR_NETWORK_CLIENT_LIST_CHAT_CLIENT_TOOLTIP :{BLACK}Send a m STR_NETWORK_CLIENT_LIST_CHAT_COMPANY_TOOLTIP :{BLACK}Send a message to all players of this company STR_NETWORK_CLIENT_LIST_CHAT_SPECTATOR_TOOLTIP :{BLACK}Send a message to all spectators STR_NETWORK_CLIENT_LIST_SPECTATORS :Spectators +STR_NETWORK_CLIENT_LIST_NEW_COMPANY :(New company) +STR_NETWORK_CLIENT_LIST_NEW_COMPANY_TOOLTIP :{BLACK}Create a new company and join it STR_NETWORK_CLIENT_LIST_ADMIN_CLIENT_KICK :Kick STR_NETWORK_CLIENT_LIST_ADMIN_CLIENT_BAN :Ban diff --git a/src/network/network_gui.cpp b/src/network/network_gui.cpp index 6eac3e8d7e..e76500f091 100644 --- a/src/network/network_gui.cpp +++ b/src/network/network_gui.cpp @@ -1792,6 +1792,21 @@ private: } } + /** + * Crete new company button is clicked. + * @param w The instance of this window. + * @param pt The point where this button was clicked. + * @param company_id The company this button was assigned to. + */ + static void OnClickCompanyNew(NetworkClientListWindow *w, Point pt, CompanyID company_id) + { + if (_network_server) { + DoCommandP(0, CCA_NEW, _network_own_client_id, CMD_COMPANY_CTRL); + } else { + NetworkSendCommand(0, CCA_NEW, 0, CMD_COMPANY_CTRL, nullptr, nullptr, _local_company); + } + } + /** * Admin button on a Client is clicked. * @param w The instance of this window. @@ -1886,8 +1901,20 @@ private: this->buttons.clear(); this->line_count = 0; + /* As spectator, show a line to create a new company. */ + if (own_ci->client_playas == COMPANY_SPECTATOR && !NetworkMaxCompaniesReached()) { + this->buttons[line_count].emplace_back(new CompanyButton(SPR_JOIN, STR_NETWORK_CLIENT_LIST_NEW_COMPANY_TOOLTIP, COLOUR_ORANGE, COMPANY_SPECTATOR, &NetworkClientListWindow::OnClickCompanyNew)); + this->line_count += 1; + } + + if (own_ci->client_playas != COMPANY_SPECTATOR) { + this->RebuildListCompany(own_ci->client_playas, own_ci); + } + /* Companies */ for (const Company *c : Company::Iterate()) { + if (c->index == own_ci->client_playas) continue; + this->RebuildListCompany(c->index, own_ci); } @@ -2177,13 +2204,13 @@ public: /** * Draw a company and its clients on the matrix. - * @param c The company to draw. + * @param company_id The company to draw. * @param left The most left pixel of the line. * @param right The most right pixel of the line. * @param top The top of the first line. * @param line The Nth line we are drawing. Updated during this function. */ - void DrawCompany(const Company *c, uint left, uint right, uint top, uint &line) const + void DrawCompany(CompanyID company_id, uint left, uint right, uint top, uint &line) const { bool rtl = _current_text_dir == TD_RTL; int text_y_offset = std::max(0, ((int)(this->line_height + 1) - (int)FONT_HEIGHT_NORMAL) / 2) + WD_MATRIX_BOTTOM; @@ -2209,14 +2236,17 @@ public: this->DrawButtons(x, y, button_find->second); } - if (c == nullptr) { + if (company_id == COMPANY_SPECTATOR) { DrawSprite(SPR_COMPANY_ICON, PALETTE_TO_GREY, rtl ? right - d.width - 4 : left + 4, y + offset); DrawString(rtl ? x : text_left, rtl ? text_right : x, y + text_y_offset, STR_NETWORK_CLIENT_LIST_SPECTATORS, TC_SILVER); + } else if (company_id == COMPANY_NEW_COMPANY) { + DrawSprite(SPR_COMPANY_ICON, PALETTE_TO_GREY, rtl ? right - d.width - 4 : left + 4, y + offset); + DrawString(rtl ? x : text_left, rtl ? text_right : x, y + text_y_offset, STR_NETWORK_CLIENT_LIST_NEW_COMPANY, TC_WHITE); } else { - DrawCompanyIcon(c->index, rtl ? right - d.width - 4 : left + 4, y + offset); + DrawCompanyIcon(company_id, rtl ? right - d.width - 4 : left + 4, y + offset); - SetDParam(0, c->index); - SetDParam(1, c->index); + SetDParam(0, company_id); + SetDParam(1, company_id); DrawString(rtl ? x : text_left, rtl ? text_right : x, y + text_y_offset, STR_COMPANY_NAME, TC_SILVER); } } @@ -2225,8 +2255,7 @@ public: line++; for (const NetworkClientInfo *ci : NetworkClientInfo::Iterate()) { - if (c != nullptr && ci->client_playas != c->index) continue; - if (c == nullptr && ci->client_playas != COMPANY_SPECTATOR) continue; + if (ci->client_playas != company_id) continue; /* Draw the player line (if in range of scrollbar). */ if (IsInsideMM(line, line_start, line_end)) { @@ -2267,11 +2296,22 @@ public: GfxFillRect(r.left + 2, r.top + offset, r.right - 1, r.top + offset + this->line_height - 1, GREY_SCALE(9)); } - for (const Company *c : Company::Iterate()) { - this->DrawCompany(c, r.left, r.right, r.top, line); + NetworkClientInfo *own_ci = NetworkClientInfo::GetByClientID(_network_own_client_id); + if (own_ci->client_playas == COMPANY_SPECTATOR && !NetworkMaxCompaniesReached()) { + this->DrawCompany(COMPANY_NEW_COMPANY, r.left, r.right, r.top, line); } + + if (own_ci->client_playas != COMPANY_SPECTATOR) { + this->DrawCompany(own_ci->client_playas, r.left, r.right, r.top, line); + } + + for (const Company *c : Company::Iterate()) { + if (own_ci->client_playas == c->index) continue; + this->DrawCompany(c->index, r.left, r.right, r.top, line); + } + /* Specators */ - this->DrawCompany(nullptr, r.left, r.right, r.top, line); + this->DrawCompany(COMPANY_SPECTATOR, r.left, r.right, r.top, line); break; } diff --git a/src/toolbar_gui.cpp b/src/toolbar_gui.cpp index 6a65382b44..fc8b183470 100644 --- a/src/toolbar_gui.cpp +++ b/src/toolbar_gui.cpp @@ -204,8 +204,7 @@ static void PopupMainToolbMenu(Window *w, int widget, StringID string, int count /** Enum for the Company Toolbar's network related buttons */ static const int CTMN_CLIENT_LIST = -1; ///< Show the client list -static const int CTMN_NEW_COMPANY = -2; ///< Create a new company -static const int CTMN_SPECTATOR = -3; ///< Show a company window as spectator +static const int CTMN_SPECTATOR = -2; ///< Show a company window as spectator /** * Pop up a generic company list menu. @@ -223,10 +222,6 @@ static void PopupMainCompanyToolbMenu(Window *w, int widget, int grey = 0) /* Add the client list button for the companies menu */ list.emplace_back(new DropDownListStringItem(STR_NETWORK_COMPANY_LIST_CLIENT_LIST, CTMN_CLIENT_LIST, false)); - - if (_local_company == COMPANY_SPECTATOR) { - list.emplace_back(new DropDownListStringItem(STR_NETWORK_COMPANY_LIST_NEW_COMPANY, CTMN_NEW_COMPANY, NetworkMaxCompaniesReached())); - } break; case WID_TN_STORY: @@ -608,14 +603,6 @@ static CallBackFunction MenuClickCompany(int index) case CTMN_CLIENT_LIST: ShowClientList(); return CBF_NONE; - - case CTMN_NEW_COMPANY: - if (_network_server) { - DoCommandP(0, CCA_NEW, _network_own_client_id, CMD_COMPANY_CTRL); - } else { - NetworkSendCommand(0, CCA_NEW, 0, CMD_COMPANY_CTRL, nullptr, nullptr, _local_company); - } - return CBF_NONE; } } ShowCompany((CompanyID)index); From fda1a590f6978f906ed21b6732f80c9aefa02b47 Mon Sep 17 00:00:00 2001 From: Patric Stout Date: Sat, 24 Apr 2021 13:06:59 +0200 Subject: [PATCH 154/268] Change: use icons to indicate you/host in Online Players GUI --- media/baseset/openttd.grf | Bin 510525 -> 510635 bytes media/baseset/openttd/openttdgui.nfo | 4 +- media/baseset/openttd/openttdgui.png | Bin 44464 -> 44530 bytes src/lang/english.txt | 4 +- src/network/network_gui.cpp | 60 +++++++++++++++++++++++---- src/table/sprites.h | 4 +- 6 files changed, 59 insertions(+), 13 deletions(-) diff --git a/media/baseset/openttd.grf b/media/baseset/openttd.grf index c4511f55ea9f87c7dd7e3e4f73e79e404801d053..837e4b12d2553699331ad6ba878d38afda6aa109 100644 GIT binary patch delta 176 zcmdlxMSk^Ec@`Fi|MweNT3HxdS(sW`m|Iy`wz9Aqlriq#Zd%5wF}FU4fsu=g0R)&C z7#LnKUtnWpU|?iqJi*D#&(D8>djr=7kq(Umh8K)Ca2*iARK0-bgajkw1C|>cjEpas zAF$kDXJX`kz{<$*f}M$<{{_nft{0pScs6j|(0HK3$SBD0pT{w?s35. // -1 * 0 0C "OpenTTD GUI graphics" - -1 * 3 05 15 \b 189 // OPENTTD_SPRITE_COUNT + -1 * 3 05 15 \b 191 // OPENTTD_SPRITE_COUNT -1 sprites/openttdgui.png 8bpp 66 8 64 31 -31 7 normal -1 sprites/openttdgui.png 8bpp 146 8 64 31 -31 7 normal -1 sprites/openttdgui.png 8bpp 226 8 64 31 -31 7 normal @@ -194,3 +194,5 @@ -1 sprites/openttdgui.png 8bpp 539 440 12 10 0 0 normal -1 sprites/openttdgui.png 8bpp 553 440 12 10 0 0 normal -1 sprites/openttdgui.png 8bpp 567 440 12 10 0 0 normal + -1 sprites/openttdgui.png 8bpp 581 440 10 10 0 0 normal + -1 sprites/openttdgui.png 8bpp 593 440 10 10 0 0 normal diff --git a/media/baseset/openttd/openttdgui.png b/media/baseset/openttd/openttdgui.png index dc0976a971647fa6adad1ed1506030aaf2c60bf8..5b80c3326047f00565f11a27cc8cad879228a9f1 100644 GIT binary patch delta 19226 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 W+82l{QLL#xKcA+>vHA^Ht&5n))fV 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 diff --git a/src/lang/english.txt b/src/lang/english.txt index 33ccca2a41..dbf0a82d0f 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -2137,8 +2137,6 @@ STR_NETWORK_CLIENT_LIST_PLAYER_NAME :{BLACK}Name STR_NETWORK_CLIENT_LIST_PLAYER_NAME_TOOLTIP :{BLACK}Your player name STR_NETWORK_CLIENT_LIST_PLAYER_NAME_EDIT_TOOLTIP :{BLACK}Edit your player name STR_NETWORK_CLIENT_LIST_PLAYER_NAME_QUERY_CAPTION :Your player name -STR_NETWORK_CLIENT_LIST_PLAYER_HOST :{WHITE}(host) {BLACK}{RAW_STRING} -STR_NETWORK_CLIENT_LIST_PLAYER_SELF :{WHITE}(you) {BLACK}{RAW_STRING} STR_NETWORK_CLIENT_LIST_ADMIN_CLIENT_TOOLTIP :{BLACK}Administrative actions to perform for this client STR_NETWORK_CLIENT_LIST_ADMIN_COMPANY_TOOLTIP :{BLACK}Administrative actions to perform for this company STR_NETWORK_CLIENT_LIST_JOIN_TOOLTIP :{BLACK}Join this company @@ -2148,6 +2146,8 @@ STR_NETWORK_CLIENT_LIST_CHAT_SPECTATOR_TOOLTIP :{BLACK}Send a m STR_NETWORK_CLIENT_LIST_SPECTATORS :Spectators STR_NETWORK_CLIENT_LIST_NEW_COMPANY :(New company) STR_NETWORK_CLIENT_LIST_NEW_COMPANY_TOOLTIP :{BLACK}Create a new company and join it +STR_NETWORK_CLIENT_LIST_PLAYER_ICON_SELF_TOOLTIP :{BLACK}This is you +STR_NETWORK_CLIENT_LIST_PLAYER_ICON_HOST_TOOLTIP :{BLACK}This is the host of the game STR_NETWORK_CLIENT_LIST_ADMIN_CLIENT_KICK :Kick STR_NETWORK_CLIENT_LIST_ADMIN_CLIENT_BAN :Ban diff --git a/src/network/network_gui.cpp b/src/network/network_gui.cpp index e76500f091..68208ee3fc 100644 --- a/src/network/network_gui.cpp +++ b/src/network/network_gui.cpp @@ -1756,6 +1756,8 @@ private: uint line_height; ///< Current lineheight of each entry in the matrix. uint line_count; ///< Amount of lines in the matrix. int hover_index; ///< Index of the current line we are hovering over, or -1 if none. + int player_self_index; ///< The line the current player is on. + int player_host_index; ///< The line the host is on. std::map>> buttons; ///< Per line which buttons are available. @@ -1870,7 +1872,7 @@ private: { ButtonCommon *chat_button = new CompanyButton(SPR_CHAT, company_id == COMPANY_SPECTATOR ? STR_NETWORK_CLIENT_LIST_CHAT_SPECTATOR_TOOLTIP : STR_NETWORK_CLIENT_LIST_CHAT_COMPANY_TOOLTIP, COLOUR_ORANGE, company_id, &NetworkClientListWindow::OnClickCompanyChat); - if (_network_server) this->buttons[line_count].emplace_back(new CompanyButton(SPR_ADMIN, STR_NETWORK_CLIENT_LIST_ADMIN_COMPANY_TOOLTIP, COLOUR_RED, company_id, &NetworkClientListWindow::OnClickCompanyAdmin)); + if (_network_server) this->buttons[line_count].emplace_back(new CompanyButton(SPR_ADMIN, STR_NETWORK_CLIENT_LIST_ADMIN_COMPANY_TOOLTIP, COLOUR_RED, company_id, &NetworkClientListWindow::OnClickCompanyAdmin, company_id == COMPANY_SPECTATOR)); this->buttons[line_count].emplace_back(chat_button); if (own_ci->client_playas != company_id) this->buttons[line_count].emplace_back(new CompanyButton(SPR_JOIN, STR_NETWORK_CLIENT_LIST_JOIN_TOOLTIP, COLOUR_ORANGE, company_id, &NetworkClientListWindow::OnClickCompanyJoin)); @@ -1884,6 +1886,12 @@ private: if (_network_server) this->buttons[line_count].emplace_back(new ClientButton(SPR_ADMIN, STR_NETWORK_CLIENT_LIST_ADMIN_CLIENT_TOOLTIP, COLOUR_RED, ci->client_id, &NetworkClientListWindow::OnClickClientAdmin, _network_own_client_id == ci->client_id)); if (_network_own_client_id != ci->client_id) this->buttons[line_count].emplace_back(new ClientButton(SPR_CHAT, STR_NETWORK_CLIENT_LIST_CHAT_CLIENT_TOOLTIP, COLOUR_ORANGE, ci->client_id, &NetworkClientListWindow::OnClickClientChat)); + if (ci->client_id == _network_own_client_id) { + this->player_self_index = this->line_count; + } else if (ci->client_id == CLIENT_ID_SERVER) { + this->player_host_index = this->line_count; + } + this->line_count += 1; } @@ -1900,6 +1908,8 @@ private: this->buttons.clear(); this->line_count = 0; + this->player_host_index = -1; + this->player_self_index = -1; /* As spectator, show a line to create a new company. */ if (own_ci->client_playas == COMPANY_SPECTATOR && !NetworkMaxCompaniesReached()) { @@ -1959,7 +1969,10 @@ private: public: NetworkClientListWindow(WindowDesc *desc, WindowNumber window_number) : - Window(desc) + Window(desc), + hover_index(-1), + player_self_index(-1), + player_host_index(-1) { this->CreateNestedTree(); this->vscroll = this->GetScrollbar(WID_CL_SCROLLBAR); @@ -2057,6 +2070,30 @@ public: { switch (widget) { case WID_CL_MATRIX: { + int index = this->vscroll->GetScrolledRowFromWidget(pt.y, this, WID_CL_MATRIX); + + bool rtl = _current_text_dir == TD_RTL; + NWidgetBase *widget_matrix = this->GetWidget(WID_CL_MATRIX); + + Dimension d = GetSpriteSize(SPR_COMPANY_ICON); + uint text_left = widget_matrix->pos_x + (rtl ? (uint)WD_FRAMERECT_LEFT : d.width + 8); + uint text_right = widget_matrix->pos_x + widget_matrix->current_x - (rtl ? d.width + 8 : (uint)WD_FRAMERECT_RIGHT); + + Dimension d2 = GetSpriteSize(SPR_PLAYER_SELF); + uint offset_x = CLIENT_OFFSET_LEFT - d2.width - 3; + + uint player_icon_x = rtl ? text_right - offset_x - d2.width : text_left + offset_x; + + if (IsInsideMM(pt.x, player_icon_x, player_icon_x + d2.width)) { + if (index == this->player_self_index) { + GuiShowTooltips(this, STR_NETWORK_CLIENT_LIST_PLAYER_ICON_SELF_TOOLTIP, 0, nullptr, close_cond); + return true; + } else if (index == this->player_host_index) { + GuiShowTooltips(this, STR_NETWORK_CLIENT_LIST_PLAYER_ICON_HOST_TOOLTIP, 0, nullptr, close_cond); + return true; + } + } + ButtonCommon *button = this->GetButtonAtPoint(pt); if (button == nullptr) return false; @@ -2267,17 +2304,22 @@ public: this->DrawButtons(x, y, button_find->second); } - StringID client_string = STR_JUST_RAW_STRING; - - if (ci->client_id == CLIENT_ID_SERVER) { - client_string = STR_NETWORK_CLIENT_LIST_PLAYER_HOST; - } + SpriteID player_icon = 0; if (ci->client_id == _network_own_client_id) { - client_string = STR_NETWORK_CLIENT_LIST_PLAYER_SELF; + player_icon = SPR_PLAYER_SELF; + } else if (ci->client_id == CLIENT_ID_SERVER) { + player_icon = SPR_PLAYER_HOST; + } + + if (player_icon != 0) { + Dimension d2 = GetSpriteSize(player_icon); + uint offset_x = CLIENT_OFFSET_LEFT - 3; + int offset_y = std::max(0, ((int)(this->line_height + 1) - (int)d2.height) / 2); + DrawSprite(player_icon, PALETTE_TO_GREY, rtl ? text_right - offset_x : text_left + offset_x - d2.width, y + offset_y); } SetDParamStr(0, ci->client_name); - DrawString(rtl ? x : text_left + CLIENT_OFFSET_LEFT, rtl ? text_right - CLIENT_OFFSET_LEFT : x, y + text_y_offset, client_string, TC_BLACK); + DrawString(rtl ? x : text_left + CLIENT_OFFSET_LEFT, rtl ? text_right - CLIENT_OFFSET_LEFT : x, y + text_y_offset, STR_JUST_RAW_STRING, TC_BLACK); } y += this->line_height; diff --git a/src/table/sprites.h b/src/table/sprites.h index 039d50b9de..b7bb910203 100644 --- a/src/table/sprites.h +++ b/src/table/sprites.h @@ -54,7 +54,7 @@ static const SpriteID SPR_LARGE_SMALL_WINDOW = 682; /** Extra graphic spritenumbers */ static const SpriteID SPR_OPENTTD_BASE = 4896; -static const uint16 OPENTTD_SPRITE_COUNT = 189; +static const uint16 OPENTTD_SPRITE_COUNT = 191; /* Halftile-selection sprites */ static const SpriteID SPR_HALFTILE_SELECTION_FLAT = SPR_OPENTTD_BASE; @@ -169,6 +169,8 @@ static const SpriteID SPR_GOTO_LOCATION = SPR_OPENTTD_BASE + 185; static const SpriteID SPR_CHAT = SPR_OPENTTD_BASE + 186; static const SpriteID SPR_ADMIN = SPR_OPENTTD_BASE + 187; static const SpriteID SPR_JOIN = SPR_OPENTTD_BASE + 188; +static const SpriteID SPR_PLAYER_SELF = SPR_OPENTTD_BASE + 189; +static const SpriteID SPR_PLAYER_HOST = SPR_OPENTTD_BASE + 190; static const SpriteID SPR_IMG_CARGOFLOW = SPR_OPENTTD_BASE + 174; From fbc232569c149518911ca66fa8d71e2ef2183439 Mon Sep 17 00:00:00 2001 From: PeterN Date: Sat, 24 Apr 2021 22:27:47 +0100 Subject: [PATCH 155/268] Fix #9097: Upper 16 bits of cargo base payment rate were discarded. (#9098) NewGRF spec says that base payment rate is 32 bits, but it was loaded into a 16 bit variable. This value is loaded into Money variable after inflation is applied. --- src/cargotype.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cargotype.h b/src/cargotype.h index 9645bf7c59..f6b3eaead5 100644 --- a/src/cargotype.h +++ b/src/cargotype.h @@ -59,7 +59,7 @@ struct CargoSpec { uint8 rating_colour; uint8 weight; ///< Weight of a single unit of this cargo type in 1/16 ton (62.5 kg). uint16 multiplier; ///< Capacity multiplier for vehicles. (8 fractional bits) - uint16 initial_payment; + uint32 initial_payment; ///< Initial payment rate before inflation is applied. uint8 transit_days[2]; bool is_freight; ///< Cargo type is considered to be freight (affects train freight multiplier). From f4d5c8d99e9071efc54483f44b74a6d3c2a05214 Mon Sep 17 00:00:00 2001 From: Michael Lutz Date: Sun, 25 Apr 2021 00:43:38 +0200 Subject: [PATCH 156/268] Fix: [OpenGL] Main loop expects to start with the video buffer unmapped. (#9100) --- src/video/opengl.cpp | 1 + src/video/sdl2_opengl_v.cpp | 2 ++ src/video/win32_v.cpp | 2 ++ 3 files changed, 5 insertions(+) diff --git a/src/video/opengl.cpp b/src/video/opengl.cpp index 1eab9b10e3..ecafeb6138 100644 --- a/src/video/opengl.cpp +++ b/src/video/opengl.cpp @@ -1154,6 +1154,7 @@ void *OpenGLBackend::GetVideoBuffer() #endif if (!this->persistent_mapping_supported) { + assert(this->vid_buffer == nullptr); _glBindBuffer(GL_PIXEL_UNPACK_BUFFER, this->vid_pbo); this->vid_buffer = _glMapBuffer(GL_PIXEL_UNPACK_BUFFER, GL_READ_WRITE); } else if (this->vid_buffer == nullptr) { diff --git a/src/video/sdl2_opengl_v.cpp b/src/video/sdl2_opengl_v.cpp index 9c84c36b1c..003e194380 100644 --- a/src/video/sdl2_opengl_v.cpp +++ b/src/video/sdl2_opengl_v.cpp @@ -74,6 +74,8 @@ const char *VideoDriver_SDL_OpenGL::Start(const StringList ¶m) this->Stop(); return "Can't get pointer to screen buffer"; } + /* Main loop expects to start with the buffer unmapped. */ + this->ReleaseVideoPointer(); return nullptr; } diff --git a/src/video/win32_v.cpp b/src/video/win32_v.cpp index 1dc1db8cef..f0291db714 100644 --- a/src/video/win32_v.cpp +++ b/src/video/win32_v.cpp @@ -1311,6 +1311,8 @@ const char *VideoDriver_Win32OpenGL::Start(const StringList ¶m) _cur_resolution = old_res; return "Can't get pointer to screen buffer"; } + /* Main loop expects to start with the buffer unmapped. */ + this->ReleaseVideoPointer(); MarkWholeScreenDirty(); From 9d6ff1c780462bed9b1441571919c262f40df3a4 Mon Sep 17 00:00:00 2001 From: 2TallTyler Date: Fri, 9 Apr 2021 21:45:44 -0400 Subject: [PATCH 157/268] Fix: Missing 'Town names:' colon in map gen GUI --- src/lang/english.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lang/english.txt b/src/lang/english.txt index dbf0a82d0f..46a8492c9f 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -953,7 +953,7 @@ STR_GAME_OPTIONS_CURRENCY_MYR :Malaysian Ringg STR_GAME_OPTIONS_ROAD_VEHICLES_DROPDOWN_LEFT :Drive on left STR_GAME_OPTIONS_ROAD_VEHICLES_DROPDOWN_RIGHT :Drive on right -STR_GAME_OPTIONS_TOWN_NAMES_FRAME :{BLACK}Town names +STR_GAME_OPTIONS_TOWN_NAMES_FRAME :{BLACK}Town names: STR_GAME_OPTIONS_TOWN_NAMES_DROPDOWN_TOOLTIP :{BLACK}Select style of town names ############ start of townname region From f158957a4e9d7fab39e35b5dc3b5e31882d4da52 Mon Sep 17 00:00:00 2001 From: PeterN Date: Sun, 25 Apr 2021 17:51:03 +0100 Subject: [PATCH 158/268] Fix: Use width of tiny arrow string instead of scaled pixels in ship/aircraft list. (#9102) --- src/vehicle_gui.cpp | 21 +++++++++++++-------- src/vehicle_gui_base.h | 3 +++ 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/src/vehicle_gui.cpp b/src/vehicle_gui.cpp index aa28938248..f4b5185865 100644 --- a/src/vehicle_gui.cpp +++ b/src/vehicle_gui.cpp @@ -240,6 +240,11 @@ Dimension BaseVehicleListWindow::GetActionDropdownSize(bool show_autoreplace, bo return d; } +void BaseVehicleListWindow::OnInit() +{ + this->order_arrow_width = GetStringBoundingBox(STR_TINY_RIGHT_ARROW).width; +} + /** * Display the Action dropdown window. * @param show_autoreplace If true include the autoreplace item. @@ -1406,14 +1411,14 @@ static const NWidgetPart _nested_vehicle_list[] = { EndContainer(), }; -static void DrawSmallOrderList(const Vehicle *v, int left, int right, int y, VehicleOrderID start = 0) +static void DrawSmallOrderList(const Vehicle *v, int left, int right, int y, uint order_arrow_width, VehicleOrderID start) { const Order *order = v->GetOrder(start); if (order == nullptr) return; bool rtl = _current_text_dir == TD_RTL; - int l_offset = rtl ? 0 : ScaleGUITrad(6); - int r_offset = rtl ? ScaleGUITrad(6) : 0; + int l_offset = rtl ? 0 : order_arrow_width; + int r_offset = rtl ? order_arrow_width : 0; int i = 0; VehicleOrderID oid = start; @@ -1438,11 +1443,11 @@ static void DrawSmallOrderList(const Vehicle *v, int left, int right, int y, Veh } /** Draw small order list in the vehicle GUI, but without the little black arrow. This is used for shared order groups. */ -static void DrawSmallOrderList(const Order *order, int left, int right, int y) +static void DrawSmallOrderList(const Order *order, int left, int right, int y, uint order_arrow_width) { bool rtl = _current_text_dir == TD_RTL; - int l_offset = rtl ? 0 : ScaleGUITrad(6); - int r_offset = rtl ? ScaleGUITrad(6) : 0; + int l_offset = rtl ? 0 : order_arrow_width; + int r_offset = rtl ? order_arrow_width : 0; int i = 0; while (order != nullptr) { if (order->IsType(OT_GOTO_STATION)) { @@ -1550,7 +1555,7 @@ void BaseVehicleListWindow::DrawVehicleListItems(VehicleID selected_vehicle, int DrawString(text_left, text_right, y, STR_TINY_GROUP, TC_BLACK); } - if (show_orderlist) DrawSmallOrderList(v, orderlist_left, orderlist_right, y, v->cur_real_order_index); + if (show_orderlist) DrawSmallOrderList(v, orderlist_left, orderlist_right, y, this->order_arrow_width, v->cur_real_order_index); StringID str; if (v->IsChainInDepot()) { @@ -1572,7 +1577,7 @@ void BaseVehicleListWindow::DrawVehicleListItems(VehicleID selected_vehicle, int DrawVehicleImage(vehgroup.vehicles_begin[i], image_left + 8 * i, image_right, y + FONT_HEIGHT_SMALL - 1, selected_vehicle, EIT_IN_LIST, 0); } - if (show_orderlist) DrawSmallOrderList((vehgroup.vehicles_begin[0])->GetFirstOrder(), orderlist_left, orderlist_right, y); + if (show_orderlist) DrawSmallOrderList((vehgroup.vehicles_begin[0])->GetFirstOrder(), orderlist_left, orderlist_right, y, this->order_arrow_width); SetDParam(0, vehgroup.NumVehicles()); DrawString(left, right, y + 2, STR_BLACK_COMMA); diff --git a/src/vehicle_gui_base.h b/src/vehicle_gui_base.h index 4bbfbf2a80..d724bc3433 100644 --- a/src/vehicle_gui_base.h +++ b/src/vehicle_gui_base.h @@ -83,6 +83,7 @@ struct BaseVehicleListWindow : public Window { byte unitnumber_digits; ///< The number of digits of the highest unit number. Scrollbar *vscroll; VehicleListIdentifier vli; ///< Identifier of the vehicle list we want to currently show. + uint order_arrow_width; ///< Width of the arrow in the small order list. typedef GUIVehicleGroupList::SortFunction VehicleGroupSortFunction; typedef GUIVehicleList::SortFunction VehicleIndividualSortFunction; @@ -104,6 +105,8 @@ struct BaseVehicleListWindow : public Window { BaseVehicleListWindow(WindowDesc *desc, WindowNumber wno); + void OnInit() override; + void UpdateSortingFromGrouping(); void DrawVehicleListItems(VehicleID selected_vehicle, int line_height, const Rect &r) const; From 65818db1f4332fecb9395df2e41f2068bb86feeb Mon Sep 17 00:00:00 2001 From: rubidium42 Date: Sun, 25 Apr 2021 17:53:24 +0200 Subject: [PATCH 159/268] Fix: [Network] Prevent stalling save game transfer when compression is slow --- src/network/network_server.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/network/network_server.cpp b/src/network/network_server.cpp index f6b3e1192e..5489db8480 100644 --- a/src/network/network_server.cpp +++ b/src/network/network_server.cpp @@ -592,7 +592,7 @@ void ServerNetworkGameSocketHandler::CheckNextClientToSendMap(NetworkClientSocke /** This sends the map to the client */ NetworkRecvStatus ServerNetworkGameSocketHandler::SendMap() { - static uint sent_packets; // How many packets we did send successfully last time + static uint16 sent_packets; // How many packets we did send successfully last time if (this->status < STATUS_AUTHORIZED) { /* Illegal call, return error and ignore the packet */ @@ -652,8 +652,10 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::SendMap() return NETWORK_RECV_STATUS_CONN_LOST; case SPS_ALL_SENT: - /* All are sent, increase the sent_packets */ - if (has_packets) sent_packets *= 2; + /* All are sent, increase the sent_packets but do not overflow! */ + if (has_packets && sent_packets < std::numeric_limits::max() / 2) { + sent_packets *= 2; + } break; case SPS_PARTLY_SENT: From b721787c7fafbff040fbcaa188a9d5dd7557e26f Mon Sep 17 00:00:00 2001 From: translators Date: Sun, 25 Apr 2021 18:04:52 +0000 Subject: [PATCH 160/268] Update: Translations from eints norwegian (bokmal): 4 changes by Anolitt spanish (mexican): 3 changes by absay japanese: 60 changes by scabtert, 38 changes by Azusa257 english (us): 3 changes by 2TallTyler korean: 3 changes by telk5093 russian: 4 changes by Ln-Wolf finnish: 3 changes by hpiirai slovak: 20 changes by FuryPapaya --- src/lang/afrikaans.txt | 15 +--- src/lang/arabic_egypt.txt | 10 +-- src/lang/basque.txt | 12 +--- src/lang/belarusian.txt | 15 +--- src/lang/brazilian_portuguese.txt | 15 +--- src/lang/bulgarian.txt | 14 +--- src/lang/catalan.txt | 15 +--- src/lang/chuvash.txt | 3 + src/lang/croatian.txt | 15 +--- src/lang/czech.txt | 15 +--- src/lang/danish.txt | 15 +--- src/lang/dutch.txt | 15 +--- src/lang/english_AU.txt | 14 +--- src/lang/english_US.txt | 18 ++--- src/lang/esperanto.txt | 10 +-- src/lang/estonian.txt | 15 +--- src/lang/faroese.txt | 12 +--- src/lang/finnish.txt | 18 ++--- src/lang/french.txt | 15 +--- src/lang/frisian.txt | 13 +--- src/lang/gaelic.txt | 14 +--- src/lang/galician.txt | 15 +--- src/lang/german.txt | 15 +--- src/lang/greek.txt | 15 +--- src/lang/hebrew.txt | 14 +--- src/lang/hindi.txt | 4 +- src/lang/hungarian.txt | 15 +--- src/lang/icelandic.txt | 10 +-- src/lang/ido.txt | 3 + src/lang/indonesian.txt | 15 +--- src/lang/irish.txt | 14 +--- src/lang/italian.txt | 15 +--- src/lang/japanese.txt | 115 ++++++++++++++++++++++++++---- src/lang/korean.txt | 18 ++--- src/lang/latin.txt | 15 +--- src/lang/latvian.txt | 15 +--- src/lang/lithuanian.txt | 15 +--- src/lang/luxembourgish.txt | 15 +--- src/lang/macedonian.txt | 3 + src/lang/malay.txt | 11 +-- src/lang/maltese.txt | 3 + src/lang/marathi.txt | 3 + src/lang/norwegian_bokmal.txt | 19 ++--- src/lang/norwegian_nynorsk.txt | 14 +--- src/lang/persian.txt | 13 +--- src/lang/polish.txt | 15 +--- src/lang/portuguese.txt | 15 +--- src/lang/romanian.txt | 14 +--- src/lang/russian.txt | 20 ++---- src/lang/serbian.txt | 15 +--- src/lang/simplified_chinese.txt | 15 +--- src/lang/slovak.txt | 55 ++++++-------- src/lang/slovenian.txt | 14 +--- src/lang/spanish.txt | 15 +--- src/lang/spanish_MX.txt | 18 ++--- src/lang/swedish.txt | 15 +--- src/lang/tamil.txt | 14 +--- src/lang/thai.txt | 14 +--- src/lang/traditional_chinese.txt | 14 +--- src/lang/turkish.txt | 15 +--- src/lang/ukrainian.txt | 15 +--- src/lang/urdu.txt | 10 +-- src/lang/vietnamese.txt | 15 +--- src/lang/welsh.txt | 14 +--- 64 files changed, 330 insertions(+), 674 deletions(-) diff --git a/src/lang/afrikaans.txt b/src/lang/afrikaans.txt index cf3d05321e..4390a75a0d 100644 --- a/src/lang/afrikaans.txt +++ b/src/lang/afrikaans.txt @@ -1915,6 +1915,7 @@ STR_FACE_TIE :Das: STR_FACE_EARRING :Oorbel: STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}Verander das of oorbel + # Network server list STR_NETWORK_SERVER_LIST_CAPTION :{WHITE}Multispeler STR_NETWORK_SERVER_LIST_PLAYER_NAME :{BLACK}Speler naam: @@ -1973,10 +1974,6 @@ STR_NETWORK_START_SERVER_NEW_GAME_NAME_TOOLTIP :{BLACK}Die spel STR_NETWORK_START_SERVER_SET_PASSWORD :{BLACK}Stel wagwoord STR_NETWORK_START_SERVER_PASSWORD_TOOLTIP :{BLACK}Beskerm jou spel met 'n wagwoord as jy wil dit nie publieke toepassing laat wees nie -STR_NETWORK_START_SERVER_ADVERTISED_LABEL :{BLACK}Openbaar -STR_NETWORK_START_SERVER_ADVERTISED_TOOLTIP :{BLACK}Kies tussen 'n openbare (internet) of 'n plaaslike (LAN) spel -STR_NETWORK_START_SERVER_UNADVERTISED :Nee -STR_NETWORK_START_SERVER_ADVERTISED :Ja STR_NETWORK_START_SERVER_CLIENTS_SELECT :{BLACK}{NUM} Klient{P "" e} STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS :{BLACK}Maksimum aantal kliënte: STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS_TOOLTIP :{BLACK}Kies die maksimum aantal kliënte. Alle posisies hoef nie vol te wees nie @@ -2038,19 +2035,13 @@ STR_NETWORK_CONNECTION_DISCONNECT :{BLACK}Loskoppe STR_NETWORK_NEED_GAME_PASSWORD_CAPTION :{WHITE}Verskaffer is beskerm. Voer wagwoord in STR_NETWORK_NEED_COMPANY_PASSWORD_CAPTION :{WHITE}Maatskappy is beskerm. Voer wagwoord in -STR_NETWORK_COMPANY_LIST_CLIENT_LIST_CAPTION :{WHITE}Kliëntelys # Network company list added strings STR_NETWORK_COMPANY_LIST_CLIENT_LIST :Klient Lys -STR_NETWORK_COMPANY_LIST_SPECTATE :Toeskou -STR_NETWORK_COMPANY_LIST_NEW_COMPANY :Nuwe maatskapy # Network client list -STR_NETWORK_CLIENTLIST_KICK :Skop -STR_NETWORK_CLIENTLIST_BAN :Verbod -STR_NETWORK_CLIENTLIST_SPEAK_TO_ALL :Praat met almal -STR_NETWORK_CLIENTLIST_SPEAK_TO_COMPANY :Praat met maatskappy -STR_NETWORK_CLIENTLIST_SPEAK_TO_CLIENT :Privaate boodskap + + STR_NETWORK_SERVER :Verskaffer STR_NETWORK_CLIENT :Klient diff --git a/src/lang/arabic_egypt.txt b/src/lang/arabic_egypt.txt index 61bb91a186..eb9abb14ea 100644 --- a/src/lang/arabic_egypt.txt +++ b/src/lang/arabic_egypt.txt @@ -1611,6 +1611,7 @@ STR_FACE_TIE :الربطة: STR_FACE_EARRING :أقراط الأذان: STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}غير الربطة أو أقراط الأذن + # Network server list STR_NETWORK_SERVER_LIST_CAPTION :{WHITE}تعدد اللاعبين STR_NETWORK_SERVER_LIST_PLAYER_NAME :{BLACK}أسم اللاعب @@ -1733,15 +1734,10 @@ STR_NETWORK_NEED_COMPANY_PASSWORD_CAPTION :{WHITE}الشر # Network company list added strings STR_NETWORK_COMPANY_LIST_CLIENT_LIST :قائمة العملاء -STR_NETWORK_COMPANY_LIST_SPECTATE :شاهد -STR_NETWORK_COMPANY_LIST_NEW_COMPANY :شركة جديدة # Network client list -STR_NETWORK_CLIENTLIST_KICK :اطرد -STR_NETWORK_CLIENTLIST_BAN :بان -STR_NETWORK_CLIENTLIST_SPEAK_TO_ALL :تحدث مع الكل -STR_NETWORK_CLIENTLIST_SPEAK_TO_COMPANY :تحدث لشركة -STR_NETWORK_CLIENTLIST_SPEAK_TO_CLIENT :رسالة خاصة + + STR_NETWORK_SERVER :خادم STR_NETWORK_CLIENT :عميل diff --git a/src/lang/basque.txt b/src/lang/basque.txt index 66b4a38eff..4223e9f9cc 100644 --- a/src/lang/basque.txt +++ b/src/lang/basque.txt @@ -1790,6 +1790,7 @@ STR_FACE_TIE :Korbata: STR_FACE_EARRING :Belarritakoak: STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}Aldatu korbata eta belarritakoak + # Network server list STR_NETWORK_SERVER_LIST_CAPTION :{WHITE}Multijokalaria STR_NETWORK_SERVER_LIST_PLAYER_NAME :{BLACK}Jokalariaren izena: @@ -1848,8 +1849,6 @@ STR_NETWORK_START_SERVER_NEW_GAME_NAME_TOOLTIP :{BLACK}Jokoaren STR_NETWORK_START_SERVER_SET_PASSWORD :{BLACK}Pasahitza ezarri STR_NETWORK_START_SERVER_PASSWORD_TOOLTIP :{BLACK}Babestu zure jokoa pasahitz batekin ez baduzu nahi publikoa izatea -STR_NETWORK_START_SERVER_UNADVERTISED :Ez -STR_NETWORK_START_SERVER_ADVERTISED :Bai STR_NETWORK_START_SERVER_CLIENTS_SELECT :{BLACK}{NUM} Bezero STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS :{BLACK}Gehienezko bezeroak: STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS_TOOLTIP :{BLACK}Gehienezko bezero kopurua aukeratu. Ez da beharrezkoa guztia betetzea @@ -1914,15 +1913,10 @@ STR_NETWORK_NEED_COMPANY_PASSWORD_CAPTION :{WHITE}Konpaini # Network company list added strings STR_NETWORK_COMPANY_LIST_CLIENT_LIST :Bezero zerrenda -STR_NETWORK_COMPANY_LIST_SPECTATE :Ikusle -STR_NETWORK_COMPANY_LIST_NEW_COMPANY :Konpainia berria # Network client list -STR_NETWORK_CLIENTLIST_KICK :Kanporatu -STR_NETWORK_CLIENTLIST_BAN :Debekatu -STR_NETWORK_CLIENTLIST_SPEAK_TO_ALL :Guztiei hitz egin -STR_NETWORK_CLIENTLIST_SPEAK_TO_COMPANY :Konpainiari hitz egin -STR_NETWORK_CLIENTLIST_SPEAK_TO_CLIENT :Mezu pribatua + + STR_NETWORK_SERVER :Zerbitzaria STR_NETWORK_CLIENT :Bezeroa diff --git a/src/lang/belarusian.txt b/src/lang/belarusian.txt index 21f4e795c2..519a4bf547 100644 --- a/src/lang/belarusian.txt +++ b/src/lang/belarusian.txt @@ -2225,6 +2225,7 @@ STR_FACE_TIE :Гальшту STR_FACE_EARRING :Завушніца: STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}Зьмяніць гальштук або завушніцу + # Network server list STR_NETWORK_SERVER_LIST_CAPTION :{WHITE}Сеткавая гульня STR_NETWORK_SERVER_LIST_PLAYER_NAME :{BLACK}Імя гульца: @@ -2283,10 +2284,6 @@ STR_NETWORK_START_SERVER_NEW_GAME_NAME_TOOLTIP :{BLACK}Назв STR_NETWORK_START_SERVER_SET_PASSWORD :{BLACK}Усталяваць пароль STR_NETWORK_START_SERVER_PASSWORD_TOOLTIP :{BLACK}Абараніце вашу гульню паролем, калі ня хочаце рабіць яе публічна даступнай -STR_NETWORK_START_SERVER_ADVERTISED_LABEL :{BLACK}Інтэрнэт -STR_NETWORK_START_SERVER_ADVERTISED_TOOLTIP :{BLACK}Выберыце паміж гульнёй праз Інтэрнэт або ў лакальнай сетцы -STR_NETWORK_START_SERVER_UNADVERTISED :Не -STR_NETWORK_START_SERVER_ADVERTISED :Так STR_NETWORK_START_SERVER_CLIENTS_SELECT :{BLACK}{NUM} клiент{P "" ы аў} STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS :{BLACK}Макс. колькасьць клiентаў: STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS_TOOLTIP :{BLACK}Выбар максымальнай колькасьці кліентаў. Ня ўсе месцы павінны быць занятыя @@ -2348,19 +2345,13 @@ STR_NETWORK_CONNECTION_DISCONNECT :{BLACK}Адлу STR_NETWORK_NEED_GAME_PASSWORD_CAPTION :{WHITE}Сэрвэр абаронены. Увядзіце пароль STR_NETWORK_NEED_COMPANY_PASSWORD_CAPTION :{WHITE}Кампанія абароненая. Увядзіце пароль -STR_NETWORK_COMPANY_LIST_CLIENT_LIST_CAPTION :{WHITE}Сьпіс кліентаў # Network company list added strings STR_NETWORK_COMPANY_LIST_CLIENT_LIST :Сьпіс кліентаў -STR_NETWORK_COMPANY_LIST_SPECTATE :Назіраць -STR_NETWORK_COMPANY_LIST_NEW_COMPANY :Новая кампанія # Network client list -STR_NETWORK_CLIENTLIST_KICK :Выкінуць гульца -STR_NETWORK_CLIENTLIST_BAN :Бан -STR_NETWORK_CLIENTLIST_SPEAK_TO_ALL :Пагутарыць з усімі -STR_NETWORK_CLIENTLIST_SPEAK_TO_COMPANY :Пагутарыць з кампаніяй -STR_NETWORK_CLIENTLIST_SPEAK_TO_CLIENT :Прыватнае паведамленьне + + STR_NETWORK_SERVER :Сэрвэр STR_NETWORK_CLIENT :Кліент diff --git a/src/lang/brazilian_portuguese.txt b/src/lang/brazilian_portuguese.txt index 6ea84d97b2..92c6929b82 100644 --- a/src/lang/brazilian_portuguese.txt +++ b/src/lang/brazilian_portuguese.txt @@ -1991,6 +1991,7 @@ STR_FACE_TIE :Gravata: STR_FACE_EARRING :Brinco: STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}Alterar gravata ou brinco + # Network server list STR_NETWORK_SERVER_LIST_CAPTION :{WHITE}Multi-jogador STR_NETWORK_SERVER_LIST_PLAYER_NAME :{BLACK}Nome: @@ -2053,10 +2054,6 @@ STR_NETWORK_START_SERVER_NEW_GAME_NAME_TOOLTIP :{BLACK}O nome d STR_NETWORK_START_SERVER_SET_PASSWORD :{BLACK}Definir senha STR_NETWORK_START_SERVER_PASSWORD_TOOLTIP :{BLACK}Proteja o jogo com uma senha se não desejar que seja publicamente acessível -STR_NETWORK_START_SERVER_ADVERTISED_LABEL :[BLACK}Publicado -STR_NETWORK_START_SERVER_ADVERTISED_TOOLTIP :{BLACK}Selecione entre um jogo publicado (internet) ou não publicado (Rede de Área Local, LAN) -STR_NETWORK_START_SERVER_UNADVERTISED :Não -STR_NETWORK_START_SERVER_ADVERTISED :Sim STR_NETWORK_START_SERVER_CLIENTS_SELECT :{BLACK}{NUM} cliente{P "" s} STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS :{BLACK}Num máx de clientes: STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS_TOOLTIP :{BLACK}Escolha o número máximo de clientes. Não é necessário estarem todos preenchidos @@ -2118,19 +2115,13 @@ STR_NETWORK_CONNECTION_DISCONNECT :{BLACK}Desconec STR_NETWORK_NEED_GAME_PASSWORD_CAPTION :{WHITE}Servidor está protegido. Digite a senha STR_NETWORK_NEED_COMPANY_PASSWORD_CAPTION :{WHITE}Empresa está protegida. Digite a senha -STR_NETWORK_COMPANY_LIST_CLIENT_LIST_CAPTION :{WHITE}Lista de clientes # Network company list added strings STR_NETWORK_COMPANY_LIST_CLIENT_LIST :Lista de clientes -STR_NETWORK_COMPANY_LIST_SPECTATE :Assistir -STR_NETWORK_COMPANY_LIST_NEW_COMPANY :Nova Companhia # Network client list -STR_NETWORK_CLIENTLIST_KICK :Banir -STR_NETWORK_CLIENTLIST_BAN :Banir -STR_NETWORK_CLIENTLIST_SPEAK_TO_ALL :Falar com todos -STR_NETWORK_CLIENTLIST_SPEAK_TO_COMPANY :Falar com a empresa -STR_NETWORK_CLIENTLIST_SPEAK_TO_CLIENT :Mensagem privada + + STR_NETWORK_SERVER :Servidor STR_NETWORK_CLIENT :Cliente diff --git a/src/lang/bulgarian.txt b/src/lang/bulgarian.txt index aa11c5df73..9ddda2dc28 100644 --- a/src/lang/bulgarian.txt +++ b/src/lang/bulgarian.txt @@ -1836,6 +1836,7 @@ STR_FACE_TIE :Вратовр STR_FACE_EARRING :Oбица: STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}Cмени вратовръзкатa или oбицатa + # Network server list STR_NETWORK_SERVER_LIST_CAPTION :{WHITE}Онлайн играчи STR_NETWORK_SERVER_LIST_PLAYER_NAME :{BLACK}Име на играч: @@ -1894,10 +1895,6 @@ STR_NETWORK_START_SERVER_NEW_GAME_NAME_TOOLTIP :{BLACK}Имет STR_NETWORK_START_SERVER_SET_PASSWORD :{BLACK}Поставяне на парола STR_NETWORK_START_SERVER_PASSWORD_TOOLTIP :{BLACK}Защитаване на вашата игра с парола за да не е публично достъпна -STR_NETWORK_START_SERVER_ADVERTISED_LABEL :{BLACK}Рекламирана -STR_NETWORK_START_SERVER_ADVERTISED_TOOLTIP :{BLACK}Избери игра измежду рекламирана през интернет или нерекламирана през Локален интернет хост или ЛАН -STR_NETWORK_START_SERVER_UNADVERTISED :Не -STR_NETWORK_START_SERVER_ADVERTISED :Да STR_NETWORK_START_SERVER_CLIENTS_SELECT :{BLACK}{NUM} клиент{P "" s} STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS :{BLACK}Макс. брой играчи: STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS_TOOLTIP :{BLACK}Избор на максималния брой клиенти. Не всички слотове трябва да се попълнят @@ -1962,15 +1959,10 @@ STR_NETWORK_NEED_COMPANY_PASSWORD_CAPTION :{WHITE}Комп # Network company list added strings STR_NETWORK_COMPANY_LIST_CLIENT_LIST :Списък с играчите -STR_NETWORK_COMPANY_LIST_SPECTATE :Наблюдавай -STR_NETWORK_COMPANY_LIST_NEW_COMPANY :Нова фирма # Network client list -STR_NETWORK_CLIENTLIST_KICK :Изгони -STR_NETWORK_CLIENTLIST_BAN :Бан -STR_NETWORK_CLIENTLIST_SPEAK_TO_ALL :Кажи на всички -STR_NETWORK_CLIENTLIST_SPEAK_TO_COMPANY :Кажи на компания -STR_NETWORK_CLIENTLIST_SPEAK_TO_CLIENT :Лично съобщение + + STR_NETWORK_SERVER :Сървър STR_NETWORK_CLIENT :Клиент diff --git a/src/lang/catalan.txt b/src/lang/catalan.txt index 6df8a5e4b7..5ad9f92378 100644 --- a/src/lang/catalan.txt +++ b/src/lang/catalan.txt @@ -1991,6 +1991,7 @@ STR_FACE_TIE :Corbata: STR_FACE_EARRING :Arracades: STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}Canvia la corbata o les arracades + # Network server list STR_NETWORK_SERVER_LIST_CAPTION :{WHITE}Multijugador STR_NETWORK_SERVER_LIST_PLAYER_NAME :{BLACK}Nom del jugador: @@ -2053,10 +2054,6 @@ STR_NETWORK_START_SERVER_NEW_GAME_NAME_TOOLTIP :{BLACK}El nom d STR_NETWORK_START_SERVER_SET_PASSWORD :{BLACK}Posa una contrasenya STR_NETWORK_START_SERVER_PASSWORD_TOOLTIP :{BLACK}Protegeix la teva partida amb una contrasenya si no vols que sigui accessible a desconeguts -STR_NETWORK_START_SERVER_ADVERTISED_LABEL :{BLACK}Anunciat -STR_NETWORK_START_SERVER_ADVERTISED_TOOLTIP :{BLACK}Escull entre una partida anunciada (internet) i una partida no anunciada (xarxa d'àrea local, LAN) -STR_NETWORK_START_SERVER_UNADVERTISED :No -STR_NETWORK_START_SERVER_ADVERTISED :Sí STR_NETWORK_START_SERVER_CLIENTS_SELECT :{BLACK}{NUM} client{P "" s} STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS :{BLACK}Màxim nombre de clients: STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS_TOOLTIP :{BLACK}Tria el nombre màxim de clients. No és necessari omplir tots els llocs. @@ -2118,19 +2115,13 @@ STR_NETWORK_CONNECTION_DISCONNECT :{BLACK}Desconne STR_NETWORK_NEED_GAME_PASSWORD_CAPTION :{WHITE}Servidor protegit: escriviu-ne la contrasenya STR_NETWORK_NEED_COMPANY_PASSWORD_CAPTION :{WHITE}Companyia protegida: escriviu-ne la contrasenya -STR_NETWORK_COMPANY_LIST_CLIENT_LIST_CAPTION :{WHITE}Llista de clients # Network company list added strings STR_NETWORK_COMPANY_LIST_CLIENT_LIST :Llista de clients -STR_NETWORK_COMPANY_LIST_SPECTATE :Espectador -STR_NETWORK_COMPANY_LIST_NEW_COMPANY :Nova companyia # Network client list -STR_NETWORK_CLIENTLIST_KICK :Breu -STR_NETWORK_CLIENTLIST_BAN :Prohibit -STR_NETWORK_CLIENTLIST_SPEAK_TO_ALL :Parla a tothom -STR_NETWORK_CLIENTLIST_SPEAK_TO_COMPANY :Parla amb la companyia -STR_NETWORK_CLIENTLIST_SPEAK_TO_CLIENT :Missatge Privat + + STR_NETWORK_SERVER :Servidor STR_NETWORK_CLIENT :Client diff --git a/src/lang/chuvash.txt b/src/lang/chuvash.txt index c5e617e841..37567cefc0 100644 --- a/src/lang/chuvash.txt +++ b/src/lang/chuvash.txt @@ -726,6 +726,7 @@ STR_FACE_COLLAR :Ҫуха: STR_FACE_TIE :Галстук: STR_FACE_EARRING :Алка: + # Network server list STR_NETWORK_SERVER_LIST_PLAYER_NAME :{BLACK}Вӑйӑҫӑ ят: @@ -788,6 +789,8 @@ STR_NETWORK_START_SERVER_SET_PASSWORD :{BLACK}Вырн # Network client list + + # Network set password # Network company info join/password diff --git a/src/lang/croatian.txt b/src/lang/croatian.txt index b28690c848..ab7f63301a 100644 --- a/src/lang/croatian.txt +++ b/src/lang/croatian.txt @@ -2020,6 +2020,7 @@ STR_FACE_TIE :Kravata: STR_FACE_EARRING :Naušnica: STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}Promijeni kravatu ili naušnicu + # Network server list STR_NETWORK_SERVER_LIST_CAPTION :{WHITE}Više igrača STR_NETWORK_SERVER_LIST_PLAYER_NAME :{BLACK}Ime igrača: @@ -2078,10 +2079,6 @@ STR_NETWORK_START_SERVER_NEW_GAME_NAME_TOOLTIP :{BLACK}Ime igre STR_NETWORK_START_SERVER_SET_PASSWORD :{BLACK}Postavi zaporku STR_NETWORK_START_SERVER_PASSWORD_TOOLTIP :{BLACK}Zaštiti svoju igru pomoću zaporke ukoliko ne želiš da bude javno dostupna -STR_NETWORK_START_SERVER_ADVERTISED_LABEL :{BLACK}Sa oglasima -STR_NETWORK_START_SERVER_ADVERTISED_TOOLTIP :{BLACK}Odaberi između igre s oglasima (internet) i bez oglasa (Local Area Network, LAN) -STR_NETWORK_START_SERVER_UNADVERTISED :Ne -STR_NETWORK_START_SERVER_ADVERTISED :Da STR_NETWORK_START_SERVER_CLIENTS_SELECT :{BLACK}{NUM} klijen{P t ta ata} STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS :{BLACK}Najveći broj klijenata: STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS_TOOLTIP :{BLACK}Odaberi najveći broj klijenata. Ne moraju sva mjesta biti popunjena. @@ -2143,19 +2140,13 @@ STR_NETWORK_CONNECTION_DISCONNECT :{BLACK}Odspoji STR_NETWORK_NEED_GAME_PASSWORD_CAPTION :{WHITE}Poslužitelj je zaštićen. Unesite zaporku STR_NETWORK_NEED_COMPANY_PASSWORD_CAPTION :{WHITE}Tvrtka je zaštićena. Unesite zaporku -STR_NETWORK_COMPANY_LIST_CLIENT_LIST_CAPTION :{WHITE}Popis klijenata # Network company list added strings STR_NETWORK_COMPANY_LIST_CLIENT_LIST :Popis klijenata -STR_NETWORK_COMPANY_LIST_SPECTATE :Promatraj -STR_NETWORK_COMPANY_LIST_NEW_COMPANY :Nova tvrtka # Network client list -STR_NETWORK_CLIENTLIST_KICK :Izbaci -STR_NETWORK_CLIENTLIST_BAN :Zabrana -STR_NETWORK_CLIENTLIST_SPEAK_TO_ALL :Razgovaraj sa svima -STR_NETWORK_CLIENTLIST_SPEAK_TO_COMPANY :Razgovaraj s tvrtkom -STR_NETWORK_CLIENTLIST_SPEAK_TO_CLIENT :Privatna poruka + + STR_NETWORK_SERVER :Poslužitelj STR_NETWORK_CLIENT :Klijent diff --git a/src/lang/czech.txt b/src/lang/czech.txt index 444bdc7dfa..8650bf7f3c 100644 --- a/src/lang/czech.txt +++ b/src/lang/czech.txt @@ -2072,6 +2072,7 @@ STR_FACE_TIE :Kravata: STR_FACE_EARRING :Náušnice: STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}Změnit kravatu nebo náušnice + # Network server list STR_NETWORK_SERVER_LIST_CAPTION :{WHITE}Multiplayer STR_NETWORK_SERVER_LIST_PLAYER_NAME :{BLACK}Jméno hráče: @@ -2134,10 +2135,6 @@ STR_NETWORK_START_SERVER_NEW_GAME_NAME_TOOLTIP :{BLACK}Jméno h STR_NETWORK_START_SERVER_SET_PASSWORD :{BLACK}Nastavit heslo STR_NETWORK_START_SERVER_PASSWORD_TOOLTIP :{BLACK}Svoji hru si můžeš ochránit heslem, když nechceš, aby se ti do ni hlásili jiní lidé -STR_NETWORK_START_SERVER_ADVERTISED_LABEL :{BLACK}Vypsané -STR_NETWORK_START_SERVER_ADVERTISED_TOOLTIP :{BLACK}Vyber mezi propagovanou (internet) a nepropagovanou (Místní síť, LAN) hrou -STR_NETWORK_START_SERVER_UNADVERTISED :Ne -STR_NETWORK_START_SERVER_ADVERTISED :Ano STR_NETWORK_START_SERVER_CLIENTS_SELECT :{BLACK}{NUM} klient{P "" i ů} STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS :{BLACK}Nejvyšší počet hráčů: STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS_TOOLTIP :{BLACK}Zvol nejvyšší počet hráčů. Může se jich připojit i méně @@ -2199,19 +2196,13 @@ STR_NETWORK_CONNECTION_DISCONNECT :{BLACK}Odpojit STR_NETWORK_NEED_GAME_PASSWORD_CAPTION :{WHITE}Server je chráněný. Napiš heslo STR_NETWORK_NEED_COMPANY_PASSWORD_CAPTION :{WHITE}Společnost je chráněná. Napiš heslo -STR_NETWORK_COMPANY_LIST_CLIENT_LIST_CAPTION :Seznam klientů # Network company list added strings STR_NETWORK_COMPANY_LIST_CLIENT_LIST :Seznam hráčů -STR_NETWORK_COMPANY_LIST_SPECTATE :Pozorovat -STR_NETWORK_COMPANY_LIST_NEW_COMPANY :Nová společnost # Network client list -STR_NETWORK_CLIENTLIST_KICK :Vyhodit -STR_NETWORK_CLIENTLIST_BAN :Ban -STR_NETWORK_CLIENTLIST_SPEAK_TO_ALL :Napsat všem -STR_NETWORK_CLIENTLIST_SPEAK_TO_COMPANY :Napsat společnosti -STR_NETWORK_CLIENTLIST_SPEAK_TO_CLIENT :Soukromá zpráva + + STR_NETWORK_SERVER :Server STR_NETWORK_CLIENT :Klient diff --git a/src/lang/danish.txt b/src/lang/danish.txt index cff944dd92..8a34c16c57 100644 --- a/src/lang/danish.txt +++ b/src/lang/danish.txt @@ -1929,6 +1929,7 @@ STR_FACE_TIE :Slips: STR_FACE_EARRING :Ørering: STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}Ændre slips eller ørering + # Network server list STR_NETWORK_SERVER_LIST_CAPTION :{WHITE}Netværksspil STR_NETWORK_SERVER_LIST_PLAYER_NAME :{BLACK}Spiller navn: @@ -1987,10 +1988,6 @@ STR_NETWORK_START_SERVER_NEW_GAME_NAME_TOOLTIP :{BLACK}Navnet v STR_NETWORK_START_SERVER_SET_PASSWORD :{BLACK}Sæt kodeord STR_NETWORK_START_SERVER_PASSWORD_TOOLTIP :{BLACK}Beskyt dit spil med et kodeord hvis du ikke vil have fremmede med -STR_NETWORK_START_SERVER_ADVERTISED_LABEL :{BLACK}Offentlig -STR_NETWORK_START_SERVER_ADVERTISED_TOOLTIP :{BLACK}Vælg mellem et offentligt (internet) og et ikke offentligt (lokalnetværk, LAN) spil -STR_NETWORK_START_SERVER_UNADVERTISED :Nej -STR_NETWORK_START_SERVER_ADVERTISED :Ja STR_NETWORK_START_SERVER_CLIENTS_SELECT :{BLACK}{NUM} klient{P "" er} STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS :{BLACK}Maksimalt antal tilladte klienter: STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS_TOOLTIP :{BLACK}Vælg det maksimale antal klienter. Det er ikke nødvendigt at fylde dem alle @@ -2052,19 +2049,13 @@ STR_NETWORK_CONNECTION_DISCONNECT :{BLACK}Afbryd f STR_NETWORK_NEED_GAME_PASSWORD_CAPTION :{WHITE}Serveren er beskyttet. Indtast kodeord STR_NETWORK_NEED_COMPANY_PASSWORD_CAPTION :{WHITE}Selskabet er beskyttet. Indtast kodeord -STR_NETWORK_COMPANY_LIST_CLIENT_LIST_CAPTION :{WHITE}Klientliste # Network company list added strings STR_NETWORK_COMPANY_LIST_CLIENT_LIST :Klient liste -STR_NETWORK_COMPANY_LIST_SPECTATE :Tilslut som tilskuer -STR_NETWORK_COMPANY_LIST_NEW_COMPANY :Nyt firma # Network client list -STR_NETWORK_CLIENTLIST_KICK :Smid ud -STR_NETWORK_CLIENTLIST_BAN :Ban (Forvis spiller) -STR_NETWORK_CLIENTLIST_SPEAK_TO_ALL :Tal til alle -STR_NETWORK_CLIENTLIST_SPEAK_TO_COMPANY :Tal til selskab -STR_NETWORK_CLIENTLIST_SPEAK_TO_CLIENT :Privat besked + + STR_NETWORK_SERVER :Server STR_NETWORK_CLIENT :Klient diff --git a/src/lang/dutch.txt b/src/lang/dutch.txt index 63ed51c351..ae1af9673a 100644 --- a/src/lang/dutch.txt +++ b/src/lang/dutch.txt @@ -1990,6 +1990,7 @@ STR_FACE_TIE :Stropdas: STR_FACE_EARRING :Oorbel: STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}Verander das of oorbel + # Network server list STR_NETWORK_SERVER_LIST_CAPTION :{WHITE}Netwerkspel STR_NETWORK_SERVER_LIST_PLAYER_NAME :{BLACK}Spelersnaam: @@ -2052,10 +2053,6 @@ STR_NETWORK_START_SERVER_NEW_GAME_NAME_TOOLTIP :{BLACK}De speln STR_NETWORK_START_SERVER_SET_PASSWORD :{BLACK}Wachtwoord instellen STR_NETWORK_START_SERVER_PASSWORD_TOOLTIP :{BLACK}Beveilig je spel met een wachtwoord als je niet wilt dat dit algemeen toegankelijk is -STR_NETWORK_START_SERVER_ADVERTISED_LABEL :{BLACK}Openbaar -STR_NETWORK_START_SERVER_ADVERTISED_TOOLTIP :{BLACK}Kies tussen een openbaar (internet) en een niet-openbaar (Local Area Network, LAN) spel -STR_NETWORK_START_SERVER_UNADVERTISED :Nee -STR_NETWORK_START_SERVER_ADVERTISED :Ja STR_NETWORK_START_SERVER_CLIENTS_SELECT :{BLACK}{NUM} speler{P "" s} STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS :{BLACK}Maximumaantal spelers: STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS_TOOLTIP :{BLACK}Kies het maximaal aantal toegestane spelers. Niet alle posities hoeven gebruikt te worden. @@ -2117,19 +2114,13 @@ STR_NETWORK_CONNECTION_DISCONNECT :{BLACK}Verbindi STR_NETWORK_NEED_GAME_PASSWORD_CAPTION :{WHITE}Server is beveiligd. Voer wachtwoord in STR_NETWORK_NEED_COMPANY_PASSWORD_CAPTION :{WHITE}Bedrijf is beveiligd. Voer wachtwoord in -STR_NETWORK_COMPANY_LIST_CLIENT_LIST_CAPTION :{WHITE}Spelerslijst # Network company list added strings STR_NETWORK_COMPANY_LIST_CLIENT_LIST :Spelerslijst -STR_NETWORK_COMPANY_LIST_SPECTATE :Toekijken -STR_NETWORK_COMPANY_LIST_NEW_COMPANY :Nieuw bedrijf # Network client list -STR_NETWORK_CLIENTLIST_KICK :Uit het spel schoppen -STR_NETWORK_CLIENTLIST_BAN :Verbannen -STR_NETWORK_CLIENTLIST_SPEAK_TO_ALL :Met iedereen praten -STR_NETWORK_CLIENTLIST_SPEAK_TO_COMPANY :Met bedrijf praten -STR_NETWORK_CLIENTLIST_SPEAK_TO_CLIENT :Privébericht + + STR_NETWORK_SERVER :Server STR_NETWORK_CLIENT :Speler diff --git a/src/lang/english_AU.txt b/src/lang/english_AU.txt index 1fd94cbd87..085e982fa3 100644 --- a/src/lang/english_AU.txt +++ b/src/lang/english_AU.txt @@ -1846,6 +1846,7 @@ STR_FACE_TIE :Tie: STR_FACE_EARRING :Earring: STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}Change tie or earring + # Network server list STR_NETWORK_SERVER_LIST_CAPTION :{WHITE}Multiplayer STR_NETWORK_SERVER_LIST_PLAYER_NAME :{BLACK}Player name: @@ -1904,10 +1905,6 @@ STR_NETWORK_START_SERVER_NEW_GAME_NAME_TOOLTIP :{BLACK}The game STR_NETWORK_START_SERVER_SET_PASSWORD :{BLACK}Set password STR_NETWORK_START_SERVER_PASSWORD_TOOLTIP :{BLACK}Protect your game with a password if you don't want it to be publicly accessible -STR_NETWORK_START_SERVER_ADVERTISED_LABEL :{BLACK}Advertised -STR_NETWORK_START_SERVER_ADVERTISED_TOOLTIP :{BLACK}Choose between an advertised (internet) and a not advertised (Local Area Network, LAN) game -STR_NETWORK_START_SERVER_UNADVERTISED :No -STR_NETWORK_START_SERVER_ADVERTISED :Yes STR_NETWORK_START_SERVER_CLIENTS_SELECT :{BLACK}{NUM} client{P "" s} STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS :{BLACK}Maximum number of clients: STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS_TOOLTIP :{BLACK}Choose the maximum number of clients. Not all slots need to be filled @@ -1972,15 +1969,10 @@ STR_NETWORK_NEED_COMPANY_PASSWORD_CAPTION :{WHITE}Company # Network company list added strings STR_NETWORK_COMPANY_LIST_CLIENT_LIST :Client list -STR_NETWORK_COMPANY_LIST_SPECTATE :Spectate -STR_NETWORK_COMPANY_LIST_NEW_COMPANY :New company # Network client list -STR_NETWORK_CLIENTLIST_KICK :Kick -STR_NETWORK_CLIENTLIST_BAN :Ban -STR_NETWORK_CLIENTLIST_SPEAK_TO_ALL :Speak to all -STR_NETWORK_CLIENTLIST_SPEAK_TO_COMPANY :Speak to company -STR_NETWORK_CLIENTLIST_SPEAK_TO_CLIENT :Private message + + STR_NETWORK_SERVER :Server STR_NETWORK_CLIENT :Client diff --git a/src/lang/english_US.txt b/src/lang/english_US.txt index 9573e2f9dd..6b8b77bacb 100644 --- a/src/lang/english_US.txt +++ b/src/lang/english_US.txt @@ -1991,6 +1991,7 @@ STR_FACE_TIE :Tie: STR_FACE_EARRING :Earring: STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}Change tie or earring + # Network server list STR_NETWORK_SERVER_LIST_CAPTION :{WHITE}Multiplayer STR_NETWORK_SERVER_LIST_PLAYER_NAME :{BLACK}Player name: @@ -2053,10 +2054,6 @@ STR_NETWORK_START_SERVER_NEW_GAME_NAME_TOOLTIP :{BLACK}The game STR_NETWORK_START_SERVER_SET_PASSWORD :{BLACK}Set password STR_NETWORK_START_SERVER_PASSWORD_TOOLTIP :{BLACK}Protect your game with a password if you don't want it to be publicly accessible -STR_NETWORK_START_SERVER_ADVERTISED_LABEL :{BLACK}Advertised -STR_NETWORK_START_SERVER_ADVERTISED_TOOLTIP :{BLACK}Choose between an advertised (internet) and a not advertised (Local Area Network, LAN) game -STR_NETWORK_START_SERVER_UNADVERTISED :No -STR_NETWORK_START_SERVER_ADVERTISED :Yes STR_NETWORK_START_SERVER_CLIENTS_SELECT :{BLACK}{NUM} client{P "" s} STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS :{BLACK}Maximum number of clients: STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS_TOOLTIP :{BLACK}Choose the maximum number of clients. Not all slots need to be filled @@ -2118,19 +2115,13 @@ STR_NETWORK_CONNECTION_DISCONNECT :{BLACK}Disconne STR_NETWORK_NEED_GAME_PASSWORD_CAPTION :{WHITE}Server is protected. Enter password STR_NETWORK_NEED_COMPANY_PASSWORD_CAPTION :{WHITE}Company is protected. Enter password -STR_NETWORK_COMPANY_LIST_CLIENT_LIST_CAPTION :{WHITE}Client list # Network company list added strings STR_NETWORK_COMPANY_LIST_CLIENT_LIST :Client list -STR_NETWORK_COMPANY_LIST_SPECTATE :Spectate -STR_NETWORK_COMPANY_LIST_NEW_COMPANY :New company # Network client list -STR_NETWORK_CLIENTLIST_KICK :Kick -STR_NETWORK_CLIENTLIST_BAN :Ban -STR_NETWORK_CLIENTLIST_SPEAK_TO_ALL :Speak to all -STR_NETWORK_CLIENTLIST_SPEAK_TO_COMPANY :Speak to company -STR_NETWORK_CLIENTLIST_SPEAK_TO_CLIENT :Private message + + STR_NETWORK_SERVER :Server STR_NETWORK_CLIENT :Client @@ -2175,6 +2166,7 @@ STR_NETWORK_ERROR_SERVER_START :{WHITE}Could no STR_NETWORK_ERROR_CLIENT_START :{WHITE}Could not connect STR_NETWORK_ERROR_TIMEOUT :{WHITE}Connection #{NUM} timed out STR_NETWORK_ERROR_SERVER_ERROR :{WHITE}A protocol error was detected and the connection was closed +STR_NETWORK_ERROR_BAD_PLAYER_NAME :{WHITE}Your player name has not been set. The name can be set at the top of the Multiplayer window STR_NETWORK_ERROR_WRONG_REVISION :{WHITE}The revision of this client does not match the server's revision STR_NETWORK_ERROR_WRONG_PASSWORD :{WHITE}Wrong password STR_NETWORK_ERROR_SERVER_FULL :{WHITE}The server is full @@ -2187,6 +2179,7 @@ STR_NETWORK_ERROR_TIMEOUT_PASSWORD :{WHITE}You took STR_NETWORK_ERROR_TIMEOUT_COMPUTER :{WHITE}Your computer is too slow to keep up with the server 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 ############ Leave those lines in this order!! STR_NETWORK_ERROR_CLIENT_GENERAL :general error @@ -2209,6 +2202,7 @@ STR_NETWORK_ERROR_CLIENT_TIMEOUT_PASSWORD :received no pas STR_NETWORK_ERROR_CLIENT_TIMEOUT_COMPUTER :general timeout STR_NETWORK_ERROR_CLIENT_TIMEOUT_MAP :downloading map took too long STR_NETWORK_ERROR_CLIENT_TIMEOUT_JOIN :processing map took too long +STR_NETWORK_ERROR_CLIENT_INVALID_CLIENT_NAME :invalid client name ############ End of leave-in-this-order STR_NETWORK_ERROR_CLIENT_GUI_LOST_CONNECTION_CAPTION :{WHITE}Possible connection loss diff --git a/src/lang/esperanto.txt b/src/lang/esperanto.txt index 97f1e625f4..99473e3e4e 100644 --- a/src/lang/esperanto.txt +++ b/src/lang/esperanto.txt @@ -1530,6 +1530,7 @@ STR_FACE_TIE :Kravato: STR_FACE_EARRING :Orelringo: STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}Ŝanĝi kravaton aŭ orelringon. + # Network server list STR_NETWORK_SERVER_LIST_CAPTION :{WHITE}Pluraj ludantoj STR_NETWORK_SERVER_LIST_PLAYER_NAME :{BLACK}Ludantnomo: @@ -1652,15 +1653,10 @@ STR_NETWORK_NEED_COMPANY_PASSWORD_CAPTION :{WHITE}Kompanio # Network company list added strings STR_NETWORK_COMPANY_LIST_CLIENT_LIST :Klientlisto -STR_NETWORK_COMPANY_LIST_SPECTATE :Spekti -STR_NETWORK_COMPANY_LIST_NEW_COMPANY :Nova kompanio # Network client list -STR_NETWORK_CLIENTLIST_KICK :Forbatu -STR_NETWORK_CLIENTLIST_BAN :Bari -STR_NETWORK_CLIENTLIST_SPEAK_TO_ALL :Parolu al ĉiuj -STR_NETWORK_CLIENTLIST_SPEAK_TO_COMPANY :Parolu al kompanio -STR_NETWORK_CLIENTLIST_SPEAK_TO_CLIENT :Privata mesaĝo + + STR_NETWORK_SERVER :Servilo STR_NETWORK_CLIENT :Kliento diff --git a/src/lang/estonian.txt b/src/lang/estonian.txt index fdf2958841..d4b7d94dbe 100644 --- a/src/lang/estonian.txt +++ b/src/lang/estonian.txt @@ -2042,6 +2042,7 @@ STR_FACE_TIE :Lips: STR_FACE_EARRING :Kõrvarõngas: STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}Vaheta kraed või kõrvarõngast + # Network server list STR_NETWORK_SERVER_LIST_CAPTION :{WHITE}Mitmikmäng STR_NETWORK_SERVER_LIST_PLAYER_NAME :{BLACK}Mängija nimi: @@ -2104,10 +2105,6 @@ 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_ADVERTISED_LABEL :{BLACK}Reklaami -STR_NETWORK_START_SERVER_ADVERTISED_TOOLTIP :{BLACK}Valib reklaamitava (internet) või mittereklaamitava (kohtvõrk, LAN) mängu -STR_NETWORK_START_SERVER_UNADVERTISED :Ei -STR_NETWORK_START_SERVER_ADVERTISED :Jah 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 @@ -2170,19 +2167,13 @@ STR_NETWORK_CONNECTION_DISCONNECT :{BLACK}Katkesta STR_NETWORK_NEED_GAME_PASSWORD_CAPTION :{WHITE}Server on kaitstud. Sisesta salasõna STR_NETWORK_NEED_COMPANY_PASSWORD_CAPTION :{WHITE}Ettevõte on kaitstud. Sisesta salasõna -STR_NETWORK_COMPANY_LIST_CLIENT_LIST_CAPTION :{WHITE}Klientide nimekiri # Network company list added strings STR_NETWORK_COMPANY_LIST_CLIENT_LIST :Klientide nimekiri -STR_NETWORK_COMPANY_LIST_SPECTATE :Jälgi -STR_NETWORK_COMPANY_LIST_NEW_COMPANY :Uus ettevõte # Network client list -STR_NETWORK_CLIENTLIST_KICK :Viska välja -STR_NETWORK_CLIENTLIST_BAN :Bänn -STR_NETWORK_CLIENTLIST_SPEAK_TO_ALL :Räägi kõigiga -STR_NETWORK_CLIENTLIST_SPEAK_TO_COMPANY :Räägi ettevõttega -STR_NETWORK_CLIENTLIST_SPEAK_TO_CLIENT :Privaatne sõnum + + STR_NETWORK_SERVER :Server STR_NETWORK_CLIENT :Klient diff --git a/src/lang/faroese.txt b/src/lang/faroese.txt index a0c6ed76bb..5820f62ead 100644 --- a/src/lang/faroese.txt +++ b/src/lang/faroese.txt @@ -1696,6 +1696,7 @@ STR_FACE_TIE :Slips: STR_FACE_EARRING :Oyraringur: STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}Broyt slips ella oyraring + # Network server list STR_NETWORK_SERVER_LIST_CAPTION :{WHITE}Hópspæl STR_NETWORK_SERVER_LIST_PLAYER_NAME :{BLACK}Spælara navn: @@ -1754,8 +1755,6 @@ STR_NETWORK_START_SERVER_NEW_GAME_NAME_TOOLTIP :{BLACK}Aðrir s STR_NETWORK_START_SERVER_SET_PASSWORD :{BLACK}Áset loyniorð STR_NETWORK_START_SERVER_PASSWORD_TOOLTIP :{BLACK}Verj títt spæl við einum loyniorðið um tú ikki vil at ta skal verða opi fyri almenninginum -STR_NETWORK_START_SERVER_UNADVERTISED :Nei -STR_NETWORK_START_SERVER_ADVERTISED :Ja STR_NETWORK_START_SERVER_CLIENTS_SELECT :{BLACK}{NUM} klient{P "" ar} STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS :{BLACK}Mest loyvdir klientar: STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS_TOOLTIP :{BLACK}Vel mest loyvda tali av klientum. Ta er ikki neyðugt at fylla øll plássini @@ -1820,15 +1819,10 @@ STR_NETWORK_NEED_COMPANY_PASSWORD_CAPTION :{WHITE}Fyritøk # Network company list added strings STR_NETWORK_COMPANY_LIST_CLIENT_LIST :Listi yvir klientar -STR_NETWORK_COMPANY_LIST_SPECTATE :Eygleið -STR_NETWORK_COMPANY_LIST_NEW_COMPANY :Nýggja fyritøku # Network client list -STR_NETWORK_CLIENTLIST_KICK :Sparka -STR_NETWORK_CLIENTLIST_BAN :Bannað -STR_NETWORK_CLIENTLIST_SPEAK_TO_ALL :Tosa við øll -STR_NETWORK_CLIENTLIST_SPEAK_TO_COMPANY :Tosa við fyritøku -STR_NETWORK_CLIENTLIST_SPEAK_TO_CLIENT :Persónligt boð + + STR_NETWORK_SERVER :Servari STR_NETWORK_CLIENT :Klient diff --git a/src/lang/finnish.txt b/src/lang/finnish.txt index 820a83b065..e4896cd45e 100644 --- a/src/lang/finnish.txt +++ b/src/lang/finnish.txt @@ -1991,6 +1991,7 @@ STR_FACE_TIE :Solmio: STR_FACE_EARRING :Korvakoru: STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}Vaihda solmio tai korvakoru + # Network server list STR_NETWORK_SERVER_LIST_CAPTION :{WHITE}Moninpeli STR_NETWORK_SERVER_LIST_PLAYER_NAME :{BLACK}Pelaajan nimi @@ -2053,10 +2054,6 @@ STR_NETWORK_START_SERVER_NEW_GAME_NAME_TOOLTIP :{BLACK}Nimi nä STR_NETWORK_START_SERVER_SET_PASSWORD :{BLACK}Aseta salasana STR_NETWORK_START_SERVER_PASSWORD_TOOLTIP :{BLACK}Jos peliin ei halua ulkopuolisia, voi sen suojata salasanalla -STR_NETWORK_START_SERVER_ADVERTISED_LABEL :{BLACK}Mainostettu -STR_NETWORK_START_SERVER_ADVERTISED_TOOLTIP :{BLACK}Valitse mainostettu (internet) tai ei-mainostettu (paikallisverkko, LAN) peli -STR_NETWORK_START_SERVER_UNADVERTISED :Ei -STR_NETWORK_START_SERVER_ADVERTISED :Kyllä STR_NETWORK_START_SERVER_CLIENTS_SELECT :{BLACK}{NUM} asiakas{P "" ta} STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS :{BLACK}Pelaajien enimmäismäärä: STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS_TOOLTIP :{BLACK}Pelaajien enimmäismäärä. Pelissä voi olla myös vähemmän pelaajia @@ -2118,19 +2115,13 @@ STR_NETWORK_CONNECTION_DISCONNECT :{BLACK}Pura yht STR_NETWORK_NEED_GAME_PASSWORD_CAPTION :{WHITE}Palvelin on suojattu. Anna salasana STR_NETWORK_NEED_COMPANY_PASSWORD_CAPTION :{WHITE}Yhtiö on suojattu. Anna salasana -STR_NETWORK_COMPANY_LIST_CLIENT_LIST_CAPTION :{WHITE}Asiakaslista # Network company list added strings STR_NETWORK_COMPANY_LIST_CLIENT_LIST :Asiakaslista -STR_NETWORK_COMPANY_LIST_SPECTATE :Katsele -STR_NETWORK_COMPANY_LIST_NEW_COMPANY :Uusi yhtiö # Network client list -STR_NETWORK_CLIENTLIST_KICK :Potkaise -STR_NETWORK_CLIENTLIST_BAN :Kiellä -STR_NETWORK_CLIENTLIST_SPEAK_TO_ALL :Puhu kaikille -STR_NETWORK_CLIENTLIST_SPEAK_TO_COMPANY :Puhu yhtiölle -STR_NETWORK_CLIENTLIST_SPEAK_TO_CLIENT :Yksityinen viesti + + STR_NETWORK_SERVER :Palvelin STR_NETWORK_CLIENT :Pelaaja @@ -2175,6 +2166,7 @@ STR_NETWORK_ERROR_SERVER_START :{WHITE}Palvelin STR_NETWORK_ERROR_CLIENT_START :{WHITE}Yhdistäminen ei onnistunut STR_NETWORK_ERROR_TIMEOUT :{WHITE}Yhteys nro {NUM} aikakatkaistiin STR_NETWORK_ERROR_SERVER_ERROR :{WHITE}Protokollavirhe tapahtui ja yhteys suljettiin +STR_NETWORK_ERROR_BAD_PLAYER_NAME :{WHITE}Et ole asettanut pelaajanimeäsi. Nimen voi asettaa moninpeli-ikkunan ylälaidassa. 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ä @@ -2187,6 +2179,7 @@ STR_NETWORK_ERROR_TIMEOUT_PASSWORD :{WHITE}Käytit STR_NETWORK_ERROR_TIMEOUT_COMPUTER :{WHITE}Tietokoneesi on liian hidas pysyäkseen palvelimen tahdissa 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 ############ Leave those lines in this order!! STR_NETWORK_ERROR_CLIENT_GENERAL :yleinen virhe @@ -2209,6 +2202,7 @@ STR_NETWORK_ERROR_CLIENT_TIMEOUT_PASSWORD :salasanaa ei va STR_NETWORK_ERROR_CLIENT_TIMEOUT_COMPUTER :yleinen aikakatkaisu STR_NETWORK_ERROR_CLIENT_TIMEOUT_MAP :kartan lataaminen kesti liian kauan STR_NETWORK_ERROR_CLIENT_TIMEOUT_JOIN :kartan käsittely kesti liian kauan +STR_NETWORK_ERROR_CLIENT_INVALID_CLIENT_NAME :Epäkelpo asiakasnimi ############ End of leave-in-this-order STR_NETWORK_ERROR_CLIENT_GUI_LOST_CONNECTION_CAPTION :{WHITE}Mahdollinen yhteyden menetys diff --git a/src/lang/french.txt b/src/lang/french.txt index bfad801ea5..a578ea2739 100644 --- a/src/lang/french.txt +++ b/src/lang/french.txt @@ -1991,6 +1991,7 @@ STR_FACE_TIE :Cravate{NBSP}: STR_FACE_EARRING :Boucle d'oreille{NBSP}: STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}Modifier la cravate ou la boucle d'oreille + # Network server list STR_NETWORK_SERVER_LIST_CAPTION :{WHITE}Multijoueurs STR_NETWORK_SERVER_LIST_PLAYER_NAME :{BLACK}Nom du joueur{NBSP}: @@ -2053,10 +2054,6 @@ STR_NETWORK_START_SERVER_NEW_GAME_NAME_TOOLTIP :{BLACK}Les autr STR_NETWORK_START_SERVER_SET_PASSWORD :{BLACK}Choisir le mot de passe STR_NETWORK_START_SERVER_PASSWORD_TOOLTIP :{BLACK}Protégez votre partie avec un mot de passe si vous ne souhaitez pas que d'autres l'utilisent -STR_NETWORK_START_SERVER_ADVERTISED_LABEL :{BLACK}Publiée -STR_NETWORK_START_SERVER_ADVERTISED_TOOLTIP :{BLACK}Choisir entre une partie publiée (internet) et une partie non publiée (Réseau local, LAN) -STR_NETWORK_START_SERVER_UNADVERTISED :Non -STR_NETWORK_START_SERVER_ADVERTISED :Oui STR_NETWORK_START_SERVER_CLIENTS_SELECT :{BLACK}{NUM} client{P "" s} STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS :{BLACK}Nombre de clients maximum{NBSP}: STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS_TOOLTIP :{BLACK}Choisir un nombre maximum de clients. Tous les emplacements n'auront pas besoin d'être remplis @@ -2118,19 +2115,13 @@ STR_NETWORK_CONNECTION_DISCONNECT :{BLACK}Déconne STR_NETWORK_NEED_GAME_PASSWORD_CAPTION :{WHITE}Le serveur est protégé. Entrez le mot de passe STR_NETWORK_NEED_COMPANY_PASSWORD_CAPTION :{WHITE}La compagnie est protégée. Entrez le mot de passe -STR_NETWORK_COMPANY_LIST_CLIENT_LIST_CAPTION :{WHITE}Liste des clients # Network company list added strings STR_NETWORK_COMPANY_LIST_CLIENT_LIST :Liste des clients -STR_NETWORK_COMPANY_LIST_SPECTATE :Spectateur -STR_NETWORK_COMPANY_LIST_NEW_COMPANY :Nouvelle compagnie # Network client list -STR_NETWORK_CLIENTLIST_KICK :Exclure -STR_NETWORK_CLIENTLIST_BAN :Bannir -STR_NETWORK_CLIENTLIST_SPEAK_TO_ALL :Parler à tous -STR_NETWORK_CLIENTLIST_SPEAK_TO_COMPANY :Parler à la compagnie -STR_NETWORK_CLIENTLIST_SPEAK_TO_CLIENT :Message privé + + STR_NETWORK_SERVER :Serveur STR_NETWORK_CLIENT :Client diff --git a/src/lang/frisian.txt b/src/lang/frisian.txt index 4a7178629e..6bceed5a1a 100644 --- a/src/lang/frisian.txt +++ b/src/lang/frisian.txt @@ -1795,6 +1795,7 @@ STR_FACE_TIE :Strik: STR_FACE_EARRING :Earbel: STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}Feroarje strik of earbel + # Network server list STR_NETWORK_SERVER_LIST_CAPTION :{WHITE}Multiplayer STR_NETWORK_SERVER_LIST_PLAYER_NAME :{BLACK}Namme fan spieler: @@ -1841,10 +1842,6 @@ STR_NETWORK_START_SERVER_CAPTION :{WHITE}Start in STR_NETWORK_START_SERVER_NEW_GAME_NAME :{BLACK}Spulnamme: STR_NETWORK_START_SERVER_SET_PASSWORD :{BLACK}Wachtwurd ynstelle -STR_NETWORK_START_SERVER_ADVERTISED_LABEL :{BLACK}Advertearre -STR_NETWORK_START_SERVER_ADVERTISED_TOOLTIP :{BLACK}Kies tusken in advertearre (ynternet) of in net-advertearre (LAN) spul -STR_NETWORK_START_SERVER_UNADVERTISED :Nee -STR_NETWORK_START_SERVER_ADVERTISED :Ja STR_NETWORK_START_SERVER_CLIENTS_SELECT :{BLACK}{NUM} kliïnt{P "" en} STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS :{BLACK}Maksimaal oantal kliïnten: STR_NETWORK_START_SERVER_COMPANIES_SELECT :{BLACK}{NUM} bedriuw{P "" en} @@ -1898,14 +1895,10 @@ STR_NETWORK_NEED_COMPANY_PASSWORD_CAPTION :{WHITE}Bedriuw # Network company list added strings STR_NETWORK_COMPANY_LIST_CLIENT_LIST :{WHITE}Client lyst -STR_NETWORK_COMPANY_LIST_SPECTATE :{WHITE}Taskôgje -STR_NETWORK_COMPANY_LIST_NEW_COMPANY :{WHITE}Nei Bedriuw # Network client list -STR_NETWORK_CLIENTLIST_KICK :Skoppe -STR_NETWORK_CLIENTLIST_SPEAK_TO_ALL :Tsjin elkenien prate -STR_NETWORK_CLIENTLIST_SPEAK_TO_COMPANY :Praat mei bedriuw -STR_NETWORK_CLIENTLIST_SPEAK_TO_CLIENT :Priveeberjocht + + STR_NETWORK_SERVER :Tsjinner STR_NETWORK_CLIENT :Kliïnt diff --git a/src/lang/gaelic.txt b/src/lang/gaelic.txt index c38ac7e026..d6609eb0d6 100644 --- a/src/lang/gaelic.txt +++ b/src/lang/gaelic.txt @@ -2081,6 +2081,7 @@ STR_FACE_TIE :Tàidh: STR_FACE_EARRING :Fàinne-chluaise: STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}Atharraich an tàidh no an fhàinne-chluaise + # Network server list STR_NETWORK_SERVER_LIST_CAPTION :{WHITE}Ioma-chluicheadair STR_NETWORK_SERVER_LIST_PLAYER_NAME :{BLACK}Ainm cluicheadair: @@ -2139,10 +2140,6 @@ STR_NETWORK_START_SERVER_NEW_GAME_NAME_TOOLTIP :{BLACK}Thèid a STR_NETWORK_START_SERVER_SET_PASSWORD :{BLACK}Suidhich facal-faire STR_NETWORK_START_SERVER_PASSWORD_TOOLTIP :{BLACK}Dìon an geama agad le facal-faire ach nach eil e ri fhaighinn gu poblach -STR_NETWORK_START_SERVER_ADVERTISED_LABEL :{BLACK}Sanasaichte -STR_NETWORK_START_SERVER_ADVERTISED_TOOLTIP :{BLACK}Dèan taghadh eadar geama sanasaichte (eadar-lìon) no gun sanasachadh (lìonra ionadail, LAN) -STR_NETWORK_START_SERVER_UNADVERTISED :Chan eil -STR_NETWORK_START_SERVER_ADVERTISED :Tha STR_NETWORK_START_SERVER_CLIENTS_SELECT :{BLACK}{NUM} {P chliant chliant cliantan cliant} STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS :{BLACK}Cliantan air a char as motha: STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS_TOOLTIP :{BLACK}Tagh an àireamh as motha dhe chliantan. Cha leig thu leas a h-uile slot a lìonadh @@ -2207,15 +2204,10 @@ STR_NETWORK_NEED_COMPANY_PASSWORD_CAPTION :{WHITE}Tha a' c # Network company list added strings STR_NETWORK_COMPANY_LIST_CLIENT_LIST :Liosta nan cliant -STR_NETWORK_COMPANY_LIST_SPECTATE :Coimhead air -STR_NETWORK_COMPANY_LIST_NEW_COMPANY :Companaidh ùr # Network client list -STR_NETWORK_CLIENTLIST_KICK :Thoir a bhròg dha -STR_NETWORK_CLIENTLIST_BAN :Toirmisg -STR_NETWORK_CLIENTLIST_SPEAK_TO_ALL :Bruidhinn ris a h-uile duine -STR_NETWORK_CLIENTLIST_SPEAK_TO_COMPANY :Bruidhinn ris a' chompanaidh -STR_NETWORK_CLIENTLIST_SPEAK_TO_CLIENT :Teachdaireachd phrìobhaideach + + STR_NETWORK_SERVER :Frithealaiche STR_NETWORK_CLIENT :Cliant diff --git a/src/lang/galician.txt b/src/lang/galician.txt index 255d5e8838..864bcdb043 100644 --- a/src/lang/galician.txt +++ b/src/lang/galician.txt @@ -1917,6 +1917,7 @@ STR_FACE_TIE :Garavata: STR_FACE_EARRING :Pendentes: STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}Cambia-la garavata ou os pendentes + # Network server list STR_NETWORK_SERVER_LIST_CAPTION :{WHITE}Multixogador STR_NETWORK_SERVER_LIST_PLAYER_NAME :{BLACK}Nome do xogador: @@ -1975,10 +1976,6 @@ STR_NETWORK_START_SERVER_NEW_GAME_NAME_TOOLTIP :{BLACK}O nome d STR_NETWORK_START_SERVER_SET_PASSWORD :{BLACK}Establecer contrasinal STR_NETWORK_START_SERVER_PASSWORD_TOOLTIP :{BLACK}Protexe a túa partida cun contrasinal se non queres ser accesible públicamente -STR_NETWORK_START_SERVER_ADVERTISED_LABEL :{BLACK}Anunciado -STR_NETWORK_START_SERVER_ADVERTISED_TOOLTIP :{BLACK}Escolle entre unha partida con anuncios (internet) ou sen eles (LAN) -STR_NETWORK_START_SERVER_UNADVERTISED :Non -STR_NETWORK_START_SERVER_ADVERTISED :Si STR_NETWORK_START_SERVER_CLIENTS_SELECT :{BLACK}{NUM} cliente{P "" s} STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS :{BLACK}Máximo de clientes: STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS_TOOLTIP :{BLACK}Escolle o máximo número de clientes. Non tódolos ocos teñen que estar cubertos @@ -2040,19 +2037,13 @@ STR_NETWORK_CONNECTION_DISCONNECT :{BLACK}Desconec STR_NETWORK_NEED_GAME_PASSWORD_CAPTION :{WHITE}O servidor está protexido. Introduce o contrasinal STR_NETWORK_NEED_COMPANY_PASSWORD_CAPTION :{WHITE}A compañía está protexida. Introduce o contrasinal -STR_NETWORK_COMPANY_LIST_CLIENT_LIST_CAPTION :{WHITE}Lista de clientes # Network company list added strings STR_NETWORK_COMPANY_LIST_CLIENT_LIST :Lista de clientes -STR_NETWORK_COMPANY_LIST_SPECTATE :Observar -STR_NETWORK_COMPANY_LIST_NEW_COMPANY :Nova compañía # Network client list -STR_NETWORK_CLIENTLIST_KICK :Chimpar -STR_NETWORK_CLIENTLIST_BAN :Bloquear -STR_NETWORK_CLIENTLIST_SPEAK_TO_ALL :Falar a todos -STR_NETWORK_CLIENTLIST_SPEAK_TO_COMPANY :Falar con compañía -STR_NETWORK_CLIENTLIST_SPEAK_TO_CLIENT :Mensaxe privada + + STR_NETWORK_SERVER :Servidor STR_NETWORK_CLIENT :Cliente diff --git a/src/lang/german.txt b/src/lang/german.txt index d0f89a9265..3ac9293998 100644 --- a/src/lang/german.txt +++ b/src/lang/german.txt @@ -1991,6 +1991,7 @@ STR_FACE_TIE :Krawatte: STR_FACE_EARRING :Ohrring: STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}Krawatte oder Ohrring ändern + # Network server list STR_NETWORK_SERVER_LIST_CAPTION :{WHITE}Mehrspieler STR_NETWORK_SERVER_LIST_PLAYER_NAME :{BLACK}Spielername: @@ -2053,10 +2054,6 @@ STR_NETWORK_START_SERVER_NEW_GAME_NAME_TOOLTIP :{BLACK}Der Name STR_NETWORK_START_SERVER_SET_PASSWORD :{BLACK}Passwort setzen STR_NETWORK_START_SERVER_PASSWORD_TOOLTIP :{BLACK}Ein Passwort verhindert, dass unbefugte Leute beitreten -STR_NETWORK_START_SERVER_ADVERTISED_LABEL :{BLACK}Angekündigt -STR_NETWORK_START_SERVER_ADVERTISED_TOOLTIP :{BLACK}Zwischen öffentlich angekündigtem (Internet) und nicht angekündigtem (Local Area Network, LAN) Spiel wählen -STR_NETWORK_START_SERVER_UNADVERTISED :Nein -STR_NETWORK_START_SERVER_ADVERTISED :Ja STR_NETWORK_START_SERVER_CLIENTS_SELECT :{BLACK}{NUM} Teilnehmer STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS :{BLACK}Maximale Teilnehmeranzahl: STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS_TOOLTIP :{BLACK}Auswahl der maximal erlaubten Anzahl von Teilnehmern. Nicht alle Slots müssen belegt werden @@ -2118,19 +2115,13 @@ STR_NETWORK_CONNECTION_DISCONNECT :{BLACK}Trennen STR_NETWORK_NEED_GAME_PASSWORD_CAPTION :{WHITE}Server ist geschützt. Passwort eingeben: STR_NETWORK_NEED_COMPANY_PASSWORD_CAPTION :{WHITE}Firma ist geschützt. Passwort eingeben: -STR_NETWORK_COMPANY_LIST_CLIENT_LIST_CAPTION :{WHITE}Teilnehmerliste # Network company list added strings STR_NETWORK_COMPANY_LIST_CLIENT_LIST :Teilnehmerliste -STR_NETWORK_COMPANY_LIST_SPECTATE :Zuschauen -STR_NETWORK_COMPANY_LIST_NEW_COMPANY :Neue Firma # Network client list -STR_NETWORK_CLIENTLIST_KICK :Hinauswerfen -STR_NETWORK_CLIENTLIST_BAN :Sperren -STR_NETWORK_CLIENTLIST_SPEAK_TO_ALL :Mit allen sprechen -STR_NETWORK_CLIENTLIST_SPEAK_TO_COMPANY :Mit Firma sprechen -STR_NETWORK_CLIENTLIST_SPEAK_TO_CLIENT :Private Nachricht + + STR_NETWORK_SERVER :Server STR_NETWORK_CLIENT :Teilnehmer diff --git a/src/lang/greek.txt b/src/lang/greek.txt index fc55858dc8..15c9734628 100644 --- a/src/lang/greek.txt +++ b/src/lang/greek.txt @@ -2037,6 +2037,7 @@ STR_FACE_TIE :Γραβάτα: STR_FACE_EARRING :Σκουλαρίκι: STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}Αλλαγή γραβάτας ή σκουλαρικιού + # Network server list STR_NETWORK_SERVER_LIST_CAPTION :{WHITE}Παιχνίδι πολλών παικτών STR_NETWORK_SERVER_LIST_PLAYER_NAME :{BLACK}Όνομα παίκτη: @@ -2096,10 +2097,6 @@ STR_NETWORK_START_SERVER_NEW_GAME_NAME_TOOLTIP :{BLACK}Το ό STR_NETWORK_START_SERVER_SET_PASSWORD :{BLACK}Θέση κωδικού STR_NETWORK_START_SERVER_PASSWORD_TOOLTIP :{BLACK}Προστατέψτε το παιχνίδι με έναν κωδικό εάν δε θέλετε να είναι δημοσίως προσβάσιμο -STR_NETWORK_START_SERVER_ADVERTISED_LABEL :{BLACK}Διαφημιζόμενο -STR_NETWORK_START_SERVER_ADVERTISED_TOOLTIP :{BLACK}Επιλογή ανάμεσα σε ένα διαφημιζόμενο (διαδίκτυο) ή μη διαφημιζόμενο (Τοπικό Δίκτυο/LAN) παιχνίδι -STR_NETWORK_START_SERVER_UNADVERTISED :Όχι -STR_NETWORK_START_SERVER_ADVERTISED :Ναι STR_NETWORK_START_SERVER_CLIENTS_SELECT :{BLACK}{NUM} συμπαίκτ{P ης ες} STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS :{BLACK}Μέγιστος αριθμός συμμετεχόντων: STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS_TOOLTIP :{BLACK}Επιλέξτε τον μέγιστο αριθμό συμμετεχόντων. Δεν είναι ανάγκη να γεμίσουν όλες οι θέσεις @@ -2161,19 +2158,13 @@ STR_NETWORK_CONNECTION_DISCONNECT :{BLACK}Αποσ STR_NETWORK_NEED_GAME_PASSWORD_CAPTION :{WHITE}Η πρόσβαση στον διακομιστή προστατεύεται. Εισάγετε τον κωδικό STR_NETWORK_NEED_COMPANY_PASSWORD_CAPTION :{WHITE}Η εταιρεία προστατεύεται από κωδικό. Εισάγετε κωδικό -STR_NETWORK_COMPANY_LIST_CLIENT_LIST_CAPTION :{WHITE}Λίστα συμμετεχόντων # Network company list added strings STR_NETWORK_COMPANY_LIST_CLIENT_LIST :Λίστα συμμετεχόντων -STR_NETWORK_COMPANY_LIST_SPECTATE :Παρακολούθηση -STR_NETWORK_COMPANY_LIST_NEW_COMPANY :Νέα εταιρεία # Network client list -STR_NETWORK_CLIENTLIST_KICK :Εκδίωξη -STR_NETWORK_CLIENTLIST_BAN :Απαγόρευση -STR_NETWORK_CLIENTLIST_SPEAK_TO_ALL :Μιλήστε σε όλους -STR_NETWORK_CLIENTLIST_SPEAK_TO_COMPANY :Μιλήστε στην εταιρεία -STR_NETWORK_CLIENTLIST_SPEAK_TO_CLIENT :Προσωπικό μήνυμα + + STR_NETWORK_SERVER :Διακομιστής STR_NETWORK_CLIENT :Πελάτης diff --git a/src/lang/hebrew.txt b/src/lang/hebrew.txt index c36119dd48..eee6f1e00f 100644 --- a/src/lang/hebrew.txt +++ b/src/lang/hebrew.txt @@ -1895,6 +1895,7 @@ STR_FACE_TIE ::עניבה STR_FACE_EARRING ::עגילים STR_FACE_TIE_EARRING_TOOLTIP :{BLACK} שנה עניבה/עגילים + # Network server list STR_NETWORK_SERVER_LIST_CAPTION :{WHITE}משחק רשת STR_NETWORK_SERVER_LIST_PLAYER_NAME :{BLACK} :שם השחקן @@ -1953,10 +1954,6 @@ STR_NETWORK_START_SERVER_NEW_GAME_NAME_TOOLTIP :{BLACK}השם STR_NETWORK_START_SERVER_SET_PASSWORD :{BLACK}קבע סיסמה STR_NETWORK_START_SERVER_PASSWORD_TOOLTIP :{BLACK}הגן על המשחק שלך עם סיסמה אם אתה לא רוצה שהוא יהיה זמין לכולם -STR_NETWORK_START_SERVER_ADVERTISED_LABEL :{BLACK}מפורסם -STR_NETWORK_START_SERVER_ADVERTISED_TOOLTIP :{BLACK}בחר בין משחק מפורסם (אינטרנטי) ומשחק לא מפורסם (רשת מקומית, LAN) -STR_NETWORK_START_SERVER_UNADVERTISED :לא -STR_NETWORK_START_SERVER_ADVERTISED :כן STR_NETWORK_START_SERVER_CLIENTS_SELECT :{BLACK}{NUM} לקוח{P "" "ות"} STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS :{BLACK}מספר מקסימלי של לקוחות: STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS_TOOLTIP :{BLACK} :מספר משתתפים מירבי @@ -2021,15 +2018,10 @@ STR_NETWORK_NEED_COMPANY_PASSWORD_CAPTION :{WHITE}חברה # Network company list added strings STR_NETWORK_COMPANY_LIST_CLIENT_LIST :רשימת משתתפים -STR_NETWORK_COMPANY_LIST_SPECTATE :צפה -STR_NETWORK_COMPANY_LIST_NEW_COMPANY :חברה חדשה # Network client list -STR_NETWORK_CLIENTLIST_KICK :בעט -STR_NETWORK_CLIENTLIST_BAN :חסימה -STR_NETWORK_CLIENTLIST_SPEAK_TO_ALL :דבר לכולם -STR_NETWORK_CLIENTLIST_SPEAK_TO_COMPANY :דבר לחברה -STR_NETWORK_CLIENTLIST_SPEAK_TO_CLIENT :הודעה פרטית + + STR_NETWORK_SERVER :שרת STR_NETWORK_CLIENT :לקוח diff --git a/src/lang/hindi.txt b/src/lang/hindi.txt index d8e185057f..220e584b4c 100644 --- a/src/lang/hindi.txt +++ b/src/lang/hindi.txt @@ -336,6 +336,7 @@ STR_QUIT_YES :{BLACK}हा # Face selection window + # Network server list STR_NETWORK_SERVER_LIST_MAP_SIZE_SHORT :{BLACK}{COMMA}x{COMMA} @@ -348,7 +349,6 @@ STR_NETWORK_SERVER_LIST_MAP_SIZE_SHORT :{BLACK}{COMMA}x # Start new multiplayer server -STR_NETWORK_START_SERVER_ADVERTISED :हाँ # Network game lobby @@ -371,6 +371,8 @@ STR_NETWORK_GAME_LOBBY_INAUGURATION_YEAR :{SILVER}उद # Network client list + + # Network set password # Network company info join/password diff --git a/src/lang/hungarian.txt b/src/lang/hungarian.txt index cba2a6aef0..5aa64101a3 100644 --- a/src/lang/hungarian.txt +++ b/src/lang/hungarian.txt @@ -2054,6 +2054,7 @@ STR_FACE_TIE :Nyakkendő: STR_FACE_EARRING :Fülbevaló: STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}Nyakkendő vagy fülbevaló cseréje + # Network server list STR_NETWORK_SERVER_LIST_CAPTION :{WHITE}Hálózati játék STR_NETWORK_SERVER_LIST_PLAYER_NAME :{BLACK}Játékos neve: @@ -2116,10 +2117,6 @@ 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_ADVERTISED_LABEL :{BLACK}Hírdetett -STR_NETWORK_START_SERVER_ADVERTISED_TOOLTIP :{BLACK}Válassz hírdetett (internet) és nem hírdetett (helyi hálózat, LAN) játék közül. -STR_NETWORK_START_SERVER_UNADVERTISED :Nem -STR_NETWORK_START_SERVER_ADVERTISED :Igen 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 @@ -2181,19 +2178,13 @@ STR_NETWORK_CONNECTION_DISCONNECT :{BLACK}Megszak STR_NETWORK_NEED_GAME_PASSWORD_CAPTION :{WHITE}A szerver jelszóval van védve. Írd be STR_NETWORK_NEED_COMPANY_PASSWORD_CAPTION :{WHITE}A vállalat jelszóval van védve. Írd be -STR_NETWORK_COMPANY_LIST_CLIENT_LIST_CAPTION :{WHITE}Kliens lista # Network company list added strings STR_NETWORK_COMPANY_LIST_CLIENT_LIST :Kliens lista -STR_NETWORK_COMPANY_LIST_SPECTATE :Megfigyelés -STR_NETWORK_COMPANY_LIST_NEW_COMPANY :Új vállalat # Network client list -STR_NETWORK_CLIENTLIST_KICK :Kirúgás -STR_NETWORK_CLIENTLIST_BAN :Kitiltás -STR_NETWORK_CLIENTLIST_SPEAK_TO_ALL :Üzenet mindenkinek -STR_NETWORK_CLIENTLIST_SPEAK_TO_COMPANY :Üzenet a vállalatnak -STR_NETWORK_CLIENTLIST_SPEAK_TO_CLIENT :Privát üzenet + + STR_NETWORK_SERVER :Szerver STR_NETWORK_CLIENT :Kliens diff --git a/src/lang/icelandic.txt b/src/lang/icelandic.txt index 47a4d268bc..f22328c20f 100644 --- a/src/lang/icelandic.txt +++ b/src/lang/icelandic.txt @@ -1734,6 +1734,7 @@ STR_FACE_TIE :Bindi: STR_FACE_EARRING :Eyrnalokkur: STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}Breyta bindi eða eyrnalokk + # Network server list STR_NETWORK_SERVER_LIST_CAPTION :{WHITE}Fjölspilun STR_NETWORK_SERVER_LIST_PLAYER_NAME :{BLACK}Nafn leikmanns: @@ -1856,15 +1857,10 @@ STR_NETWORK_NEED_COMPANY_PASSWORD_CAPTION :{WHITE}Fyrirtæ # Network company list added strings STR_NETWORK_COMPANY_LIST_CLIENT_LIST :Listi yfir leikmenn -STR_NETWORK_COMPANY_LIST_SPECTATE :Fylgjast með -STR_NETWORK_COMPANY_LIST_NEW_COMPANY :Nýtt fyrirtæki # Network client list -STR_NETWORK_CLIENTLIST_KICK :Sparka -STR_NETWORK_CLIENTLIST_BAN :Banna -STR_NETWORK_CLIENTLIST_SPEAK_TO_ALL :Tala við alla -STR_NETWORK_CLIENTLIST_SPEAK_TO_COMPANY :Tala við fyrirtæki -STR_NETWORK_CLIENTLIST_SPEAK_TO_CLIENT :Einkaskilaboð + + STR_NETWORK_SERVER :Þjónn STR_NETWORK_CLIENT :Notandi diff --git a/src/lang/ido.txt b/src/lang/ido.txt index ba04de0dee..aad91f8978 100644 --- a/src/lang/ido.txt +++ b/src/lang/ido.txt @@ -602,6 +602,7 @@ STR_QUIT_NO :{BLACK}Ne # Face selection window + # Network server list STR_NETWORK_SERVER_LIST_GENERAL_ONLINE :{BLACK}{COMMA}/{COMMA} - {COMMA}/{COMMA} @@ -636,6 +637,8 @@ STR_NETWORK_SERVER_LIST_MAP_SIZE_SHORT :{BLACK}{COMMA}x # Network client list + + # Network set password # Network company info join/password diff --git a/src/lang/indonesian.txt b/src/lang/indonesian.txt index a6401981a2..878cb24d8e 100644 --- a/src/lang/indonesian.txt +++ b/src/lang/indonesian.txt @@ -1967,6 +1967,7 @@ STR_FACE_TIE :Dasi: STR_FACE_EARRING :Anting-anting: STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}Ubah dasi atau anting-anting + # Network server list STR_NETWORK_SERVER_LIST_CAPTION :{WHITE}Bermain bersama STR_NETWORK_SERVER_LIST_PLAYER_NAME :{BLACK}Nama pemain: @@ -2029,10 +2030,6 @@ STR_NETWORK_START_SERVER_NEW_GAME_NAME_TOOLTIP :{BLACK}Nama per STR_NETWORK_START_SERVER_SET_PASSWORD :{BLACK}Atur kata sandi STR_NETWORK_START_SERVER_PASSWORD_TOOLTIP :{BLACK}Lindungi permainan ini dengan kata kunci jika anda tidak ingin membiarkannya terbuka untuk umum -STR_NETWORK_START_SERVER_ADVERTISED_LABEL :{BLACK}Diiklankan -STR_NETWORK_START_SERVER_ADVERTISED_TOOLTIP :{BLACK}Pilih antara permainan diiklankan (internet) dan tidak diiklankan (Jaringan wilayah lokal, LAN) -STR_NETWORK_START_SERVER_UNADVERTISED :Tidak -STR_NETWORK_START_SERVER_ADVERTISED :Ya STR_NETWORK_START_SERVER_CLIENTS_SELECT :{BLACK}{NUM} klien STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS :{BLACK}Maksimum jumlah klien: STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS_TOOLTIP :{BLACK}Pilih jumlah klien maksimal. Tidak semua slot harus diisi @@ -2094,19 +2091,13 @@ STR_NETWORK_CONNECTION_DISCONNECT :{BLACK}Putuskan STR_NETWORK_NEED_GAME_PASSWORD_CAPTION :{WHITE}Server terkunci, masukkan kata kunci STR_NETWORK_NEED_COMPANY_PASSWORD_CAPTION :{WHITE}Perusahaan terkunci, masukkan kata kunci -STR_NETWORK_COMPANY_LIST_CLIENT_LIST_CAPTION :{WHITE}Daftar klien # Network company list added strings STR_NETWORK_COMPANY_LIST_CLIENT_LIST :Daftar klien -STR_NETWORK_COMPANY_LIST_SPECTATE :Menonton -STR_NETWORK_COMPANY_LIST_NEW_COMPANY :Buat Perusahaan # Network client list -STR_NETWORK_CLIENTLIST_KICK :Usir -STR_NETWORK_CLIENTLIST_BAN :Larangan -STR_NETWORK_CLIENTLIST_SPEAK_TO_ALL :Bicara ke semua -STR_NETWORK_CLIENTLIST_SPEAK_TO_COMPANY :Bicara ke perusahaan -STR_NETWORK_CLIENTLIST_SPEAK_TO_CLIENT :Pesan pribadi + + STR_NETWORK_SERVER :Server STR_NETWORK_CLIENT :Klien diff --git a/src/lang/irish.txt b/src/lang/irish.txt index 18e4bc2df2..da17c26811 100644 --- a/src/lang/irish.txt +++ b/src/lang/irish.txt @@ -1869,6 +1869,7 @@ STR_FACE_TIE :Carbhat: STR_FACE_EARRING :Fáinne cluaise: STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}Athraigh carbhat nó fáinne cluaise + # Network server list STR_NETWORK_SERVER_LIST_CAPTION :{WHITE}Ilimreoirí STR_NETWORK_SERVER_LIST_PLAYER_NAME :{BLACK}Ainm imreora: @@ -1927,10 +1928,6 @@ STR_NETWORK_START_SERVER_NEW_GAME_NAME_TOOLTIP :{BLACK}Taispeá STR_NETWORK_START_SERVER_SET_PASSWORD :{BLACK}Socraigh pasfhocal STR_NETWORK_START_SERVER_PASSWORD_TOOLTIP :{BLACK}Cosain do chluiche le pasfhocal más mian leat nach mbeidh rochtain phoiblí air -STR_NETWORK_START_SERVER_ADVERTISED_LABEL :{BLACK}Fógartha -STR_NETWORK_START_SERVER_ADVERTISED_TOOLTIP :{BLACK}Roghnaigh idir cluiche fógartha (idirlín) agus cluiche neamhfhógartha (Líonra Achair Logánta, LAN) -STR_NETWORK_START_SERVER_UNADVERTISED :Níl -STR_NETWORK_START_SERVER_ADVERTISED :Tá STR_NETWORK_START_SERVER_CLIENTS_SELECT :{BLACK}{NUM} {P ch ch ch gc c}lia{P "" "" "" "" i}nt STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS :{BLACK}Cliaint uasta: STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS_TOOLTIP :{BLACK}Roghnaigh uaslíon na gcliant. Ní gá gach áit a líonadh @@ -1995,15 +1992,10 @@ STR_NETWORK_NEED_COMPANY_PASSWORD_CAPTION :{WHITE}Tá an c # Network company list added strings STR_NETWORK_COMPANY_LIST_CLIENT_LIST :Liosta na gcliant -STR_NETWORK_COMPANY_LIST_SPECTATE :Féach air -STR_NETWORK_COMPANY_LIST_NEW_COMPANY :Cuideachta nua # Network client list -STR_NETWORK_CLIENTLIST_KICK :Ciceáil -STR_NETWORK_CLIENTLIST_BAN :Toirmisc -STR_NETWORK_CLIENTLIST_SPEAK_TO_ALL :Labhair le cách -STR_NETWORK_CLIENTLIST_SPEAK_TO_COMPANY :Labhair le cuideachta -STR_NETWORK_CLIENTLIST_SPEAK_TO_CLIENT :Teacht. phríobháideach + + STR_NETWORK_SERVER :Freastalaí STR_NETWORK_CLIENT :Cliant diff --git a/src/lang/italian.txt b/src/lang/italian.txt index 9cad946207..8841f3e711 100644 --- a/src/lang/italian.txt +++ b/src/lang/italian.txt @@ -2005,6 +2005,7 @@ STR_FACE_TIE :Cravatta: STR_FACE_EARRING :Orecchino: STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}Cambia la cravatta o l'orecchino + # Network server list STR_NETWORK_SERVER_LIST_CAPTION :{WHITE}Multigiocatore STR_NETWORK_SERVER_LIST_PLAYER_NAME :{BLACK}Nome giocatore: @@ -2067,10 +2068,6 @@ STR_NETWORK_START_SERVER_NEW_GAME_NAME_TOOLTIP :{BLACK}Il nome STR_NETWORK_START_SERVER_SET_PASSWORD :{BLACK}Imposta password STR_NETWORK_START_SERVER_PASSWORD_TOOLTIP :{BLACK}Protegge la partita con una password in modo che non sia accessibile pubblicamente -STR_NETWORK_START_SERVER_ADVERTISED_LABEL :{BLACK}Pubblico -STR_NETWORK_START_SERVER_ADVERTISED_TOOLTIP :{BLACK}Sceglie fra partita pubblica (su Internet) o privata (su rete locale, LAN) -STR_NETWORK_START_SERVER_UNADVERTISED :No -STR_NETWORK_START_SERVER_ADVERTISED :Sì STR_NETWORK_START_SERVER_CLIENTS_SELECT :{BLACK}{NUM} client STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS :{BLACK}Limite client: STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS_TOOLTIP :{BLACK}Imposta il numero massimo di client. Non tutti i posti dovranno essere occupati @@ -2132,19 +2129,13 @@ STR_NETWORK_CONNECTION_DISCONNECT :{BLACK}Disconne STR_NETWORK_NEED_GAME_PASSWORD_CAPTION :{WHITE}Server protetto. Inserire la password STR_NETWORK_NEED_COMPANY_PASSWORD_CAPTION :{WHITE}Compagnia protetta. Inserire la password -STR_NETWORK_COMPANY_LIST_CLIENT_LIST_CAPTION :{WHITE}Elenco dei client # Network company list added strings STR_NETWORK_COMPANY_LIST_CLIENT_LIST :Elenco dei client -STR_NETWORK_COMPANY_LIST_SPECTATE :Diventa spettatore -STR_NETWORK_COMPANY_LIST_NEW_COMPANY :Nuova compagnia # Network client list -STR_NETWORK_CLIENTLIST_KICK :Espelli -STR_NETWORK_CLIENTLIST_BAN :Bandisci -STR_NETWORK_CLIENTLIST_SPEAK_TO_ALL :Parla a tutti -STR_NETWORK_CLIENTLIST_SPEAK_TO_COMPANY :Parla alla compagnia -STR_NETWORK_CLIENTLIST_SPEAK_TO_CLIENT :Messaggio privato + + STR_NETWORK_SERVER :Server STR_NETWORK_CLIENT :Client diff --git a/src/lang/japanese.txt b/src/lang/japanese.txt index d9c676f1e2..f4cc22bf0a 100644 --- a/src/lang/japanese.txt +++ b/src/lang/japanese.txt @@ -32,7 +32,7 @@ STR_CARGO_PLURAL_OIL :石油 STR_CARGO_PLURAL_LIVESTOCK :家畜 STR_CARGO_PLURAL_GOODS :商品 STR_CARGO_PLURAL_GRAIN :穀物 -STR_CARGO_PLURAL_WOOD :木材 +STR_CARGO_PLURAL_WOOD :原木 STR_CARGO_PLURAL_IRON_ORE :鉄鉱石 STR_CARGO_PLURAL_STEEL :鋼鉄 STR_CARGO_PLURAL_VALUABLES :貴重品 @@ -66,7 +66,7 @@ STR_CARGO_SINGULAR_OIL :石油 STR_CARGO_SINGULAR_LIVESTOCK :家畜 STR_CARGO_SINGULAR_GOODS :商品 STR_CARGO_SINGULAR_GRAIN :穀物 -STR_CARGO_SINGULAR_WOOD :木材 +STR_CARGO_SINGULAR_WOOD :原木 STR_CARGO_SINGULAR_IRON_ORE :鉄鉱石 STR_CARGO_SINGULAR_STEEL :鋼鉄 STR_CARGO_SINGULAR_VALUABLES :貴重品 @@ -100,7 +100,7 @@ STR_QUANTITY_OIL :石油{VOLUME_L STR_QUANTITY_LIVESTOCK :家畜{COMMA}頭 STR_QUANTITY_GOODS :商品{COMMA}箱 STR_QUANTITY_GRAIN :穀物{WEIGHT_LONG} -STR_QUANTITY_WOOD :木材{WEIGHT_LONG} +STR_QUANTITY_WOOD :原木{WEIGHT_LONG} STR_QUANTITY_IRON_ORE :鉄鉱石{WEIGHT_LONG} STR_QUANTITY_STEEL :鋼鉄{WEIGHT_LONG} STR_QUANTITY_VALUABLES :貴重品{COMMA}袋 @@ -187,6 +187,7 @@ STR_COLOUR_ORANGE :橙 STR_COLOUR_BROWN :茶 STR_COLOUR_GREY :灰 STR_COLOUR_WHITE :白 +STR_COLOUR_RANDOM :ランダム # Units used in OpenTTD STR_UNITS_VELOCITY_IMPERIAL :{COMMA}mph @@ -312,6 +313,7 @@ STR_SORT_BY_RATING :レーティン STR_SORT_BY_NUM_VEHICLES :車両数 STR_SORT_BY_TOTAL_PROFIT_LAST_YEAR :昨年の総利益 STR_SORT_BY_TOTAL_PROFIT_THIS_YEAR :今年の総利益 +STR_SORT_BY_AVERAGE_PROFIT_LAST_YEAR :前年度の平均利益 # Group by options for vehicle list STR_GROUP_BY_SHARED_ORDERS :共有注文 @@ -360,6 +362,7 @@ STR_SCENEDIT_TOOLBAR_LANDSCAPE_GENERATION :{BLACK}地形 STR_SCENEDIT_TOOLBAR_TOWN_GENERATION :{BLACK}町を生成します STR_SCENEDIT_TOOLBAR_INDUSTRY_GENERATION :{BLACK}産業を生成します STR_SCENEDIT_TOOLBAR_ROAD_CONSTRUCTION :{BLACK}道路を建設します +STR_SCENEDIT_TOOLBAR_TRAM_CONSTRUCTION :{BLACK}路面電車建設 STR_SCENEDIT_TOOLBAR_PLANT_TREES :{BLACK}木を植えます。Shiftキーを押しながら決定すると費用を見積もります STR_SCENEDIT_TOOLBAR_PLACE_SIGN :{BLACK}標識を設置します STR_SCENEDIT_TOOLBAR_PLACE_OBJECT :{BLACK}オブジェクトを設置します。Shiftを押しながら決定すると費用を見積もります @@ -648,6 +651,7 @@ STR_MUSIC_EFFECTS_VOLUME :{TINY_FONT}{BLA STR_MUSIC_TRACK_NONE :{TINY_FONT}{DKGREEN}-- STR_MUSIC_TRACK_DIGIT :{TINY_FONT}{DKGREEN}{ZEROFILL_NUM} STR_MUSIC_TITLE_NONE :{TINY_FONT}{DKGREEN}------ +STR_MUSIC_TITLE_NOMUSIC :{TINY_FONT}{DKGREEN}音楽がありません STR_MUSIC_TITLE_NAME :{TINY_FONT}{DKGREEN}"{STRING}" STR_MUSIC_TRACK :{TINY_FONT}{BLACK}トラック STR_MUSIC_XTITLE :{TINY_FONT}{BLACK}タイトル @@ -673,7 +677,9 @@ STR_PLAYLIST_TRACK_NAME :{TINY_FONT}{LTB STR_PLAYLIST_TRACK_INDEX :{TINY_FONT}{BLACK}楽曲索引 STR_PLAYLIST_PROGRAM :{TINY_FONT}{BLACK}プレイリスト - '{STRING}' STR_PLAYLIST_CLEAR :{TINY_FONT}{BLACK}消去 +STR_PLAYLIST_CHANGE_SET :{BLACK}セットの変更 STR_PLAYLIST_TOOLTIP_CLEAR_CURRENT_PROGRAM_CUSTOM1 :{BLACK}選択したプレイリストの内容を消去します。(カスタム1/2 のみ) +STR_PLAYLIST_TOOLTIP_CHANGE_SET :{BLACK}音楽の選択を別のインストール済みセットに変更する STR_PLAYLIST_TOOLTIP_CLICK_TO_ADD_TRACK :{BLACK}クリックすると、その曲を選択したプレイリストに追加します。(カスタム1/2 のみ) STR_PLAYLIST_TOOLTIP_CLICK_TO_REMOVE_TRACK :{BLACK}クリックすると、その曲を選択したプレイリストから削除します。(カスタム1/2 のみ) @@ -733,6 +739,7 @@ STR_SMALLMAP_LEGENDA_DOCK :{TINY_FONT}{BLA STR_SMALLMAP_LEGENDA_ROUGH_LAND :{TINY_FONT}{BLACK}荒地 STR_SMALLMAP_LEGENDA_GRASS_LAND :{TINY_FONT}{BLACK}草地 STR_SMALLMAP_LEGENDA_BARE_LAND :{TINY_FONT}{BLACK}露地 +STR_SMALLMAP_LEGENDA_RAINFOREST :{TINY_FONT} {BLACK}熱帯雨林 STR_SMALLMAP_LEGENDA_FIELDS :{TINY_FONT}{BLACK}耕作地 STR_SMALLMAP_LEGENDA_TREES :{TINY_FONT}{BLACK}樹林 STR_SMALLMAP_LEGENDA_ROCKS :{TINY_FONT}{BLACK}岩石 @@ -764,6 +771,7 @@ STR_SMALLMAP_TOOLTIP_ENABLE_ALL_CARGOS :{BLACK}全貨 STR_STATUSBAR_TOOLTIP_SHOW_LAST_NEWS :{BLACK}最新のメッセージ/ニュースを表示します STR_STATUSBAR_COMPANY_NAME :{SILVER}- - {COMPANY} - - STR_STATUSBAR_PAUSED :{YELLOW}* * ポーズ中 * * +STR_STATUSBAR_PAUSED_LINK_GRAPH :{ORANGE}* * 一時停止中 (リンクグラフの更新を待っています) * * STR_STATUSBAR_AUTOSAVE :{RED}オートセーブ STR_STATUSBAR_SAVING_GAME :{RED}* * ゲームセーブ中 * * @@ -923,8 +931,12 @@ STR_GAME_OPTIONS_CURRENCY_ZAR :南アフリカ STR_GAME_OPTIONS_CURRENCY_CUSTOM :カスタム… STR_GAME_OPTIONS_CURRENCY_GEL :グルジア ラリー(GEL) STR_GAME_OPTIONS_CURRENCY_IRR :イラン リアル(IRR) +STR_GAME_OPTIONS_CURRENCY_RUB :新ロシアルーブル(RUB) STR_GAME_OPTIONS_CURRENCY_MXN :メキシコペソ(MXN) STR_GAME_OPTIONS_CURRENCY_NTD :新台湾ドル(ntd) +STR_GAME_OPTIONS_CURRENCY_CNY :人民元(CNY) +STR_GAME_OPTIONS_CURRENCY_HKD :香港ドル(HKD) +STR_GAME_OPTIONS_CURRENCY_INR :インドルピー(INR) STR_GAME_OPTIONS_CURRENCY_IDR :インドネシアルピア(IDR) ############ end of currency region @@ -982,6 +994,7 @@ STR_GAME_OPTIONS_RESOLUTION_ITEM :{NUM}x{NUM} STR_GAME_OPTIONS_VIDEO_ACCELERATION :{BLACK}ハードウェアアクセラレーション +STR_GAME_OPTIONS_VIDEO_VSYNC :{BLACK} 垂直同期 STR_GAME_OPTIONS_GUI_ZOOM_FRAME :{BLACK}インターフェイスのサイズ STR_GAME_OPTIONS_GUI_ZOOM_DROPDOWN_TOOLTIP :{BLACK}インターフェイス上の単位サイズを指定します @@ -992,15 +1005,19 @@ STR_GAME_OPTIONS_GUI_ZOOM_DROPDOWN_2X_ZOOM :2倍 STR_GAME_OPTIONS_GUI_ZOOM_DROPDOWN_4X_ZOOM :4倍 STR_GAME_OPTIONS_FONT_ZOOM :{BLACK}フォントサイズ +STR_GAME_OPTIONS_FONT_ZOOM_DROPDOWN_TOOLTIP :{BLACK}使用するインターフェースのフォントサイズを選択します STR_GAME_OPTIONS_FONT_ZOOM_DROPDOWN_AUTO :(自動検出) STR_GAME_OPTIONS_FONT_ZOOM_DROPDOWN_NORMAL :ノーマル +STR_GAME_OPTIONS_FONT_ZOOM_DROPDOWN_2X_ZOOM :ダブルサイズ +STR_GAME_OPTIONS_FONT_ZOOM_DROPDOWN_4X_ZOOM :4倍 STR_GAME_OPTIONS_GRAPHICS :{BLACK}グラフィクス STR_GAME_OPTIONS_REFRESH_RATE :{BLACK}画面リフレッシュレート STR_GAME_OPTIONS_REFRESH_RATE_TOOLTIP :{BLACK}使用する画面のリフレッシュレートを選択します STR_GAME_OPTIONS_REFRESH_RATE_OTHER :その他 +STR_GAME_OPTIONS_REFRESH_RATE_WARNING :{WHITE}60Hzを超えるリフレッシュレートはパフォーマンスに影響を与える可能性があります。 STR_GAME_OPTIONS_BASE_GRF :{BLACK}基本グラフィックセット STR_GAME_OPTIONS_BASE_GRF_TOOLTIP :{BLACK}使用するグラフィックセットを選択します @@ -1096,6 +1113,7 @@ STR_TERRAIN_TYPE_FLAT :平地 STR_TERRAIN_TYPE_HILLY :丘陵地 STR_TERRAIN_TYPE_MOUNTAINOUS :山岳地 STR_TERRAIN_TYPE_ALPINIST :山脈地帯 +STR_TERRAIN_TYPE_CUSTOM :カスタム高度 STR_CITY_APPROVAL_PERMISSIVE :寛大 STR_CITY_APPROVAL_TOLERANT :寛容 @@ -1116,6 +1134,7 @@ STR_CONFIG_SETTING_TYPE_GAME_MENU :ゲーム設定 STR_CONFIG_SETTING_TYPE_GAME_INGAME :ゲーム設定(現在のゲームにのみ影響) STR_CONFIG_SETTING_TYPE_COMPANY_MENU :会社設定(新規ゲームにのみ影響) STR_CONFIG_SETTING_TYPE_COMPANY_INGAME :会社設定(現在の会社のみに影響) +STR_CONFIG_SETTING_RESET_ALL_CONFIRMATION_DIALOG_CAPTION :{WHITE}注意! STR_CONFIG_SETTING_RESTRICT_CATEGORY :{BLACK}カテゴリ: STR_CONFIG_SETTING_RESTRICT_TYPE :{BLACK}種類: @@ -1177,11 +1196,13 @@ STR_CONFIG_SETTING_DISASTERS_HELPTEXT :設定を有効 STR_CONFIG_SETTING_CITY_APPROVAL :地域の再編に対する町の姿勢: {STRING} STR_CONFIG_SETTING_CITY_APPROVAL_HELPTEXT :会社が街域で引き起こした騒音(主に空港)や環境破壊がどの程度、街での評価や同じ地域での更なる建設行為に影響するかを設定します +STR_CONFIG_SETTING_MAP_HEIGHT_LIMIT :マップ高さ限界: {STRING} STR_CONFIG_SETTING_TOO_HIGH_MOUNTAIN :{WHITE}マップの最高高さをこの値には設定出来ません。少なくとも1箇所以上この値より高い山があります。 STR_CONFIG_SETTING_AUTOSLOPE :建物/路線の自動地形追従: {STRING} STR_CONFIG_SETTING_AUTOSLOPE_HELPTEXT :撤去を行わないで建物や路線がある土地の地形を変更することを可能にします。建物/路線は変更された地形に自動で追従します。 STR_CONFIG_SETTING_CATCHMENT :現実的な受入範囲: {STRING} STR_CONFIG_SETTING_CATCHMENT_HELPTEXT :駅や空港の種類の違いによって受入範囲が変動するようになります +STR_CONFIG_SETTING_SERVE_NEUTRAL_INDUSTRIES_HELPTEXT :有効にすると、駅が接続されている産業(石油掘削装置など)にも、近くに建設された会社が所有している駅がサービスを提供する場合があります。無効になっている場合、これらの産業は、接続されているステーションによってのみサービスを受けることができます。近くの会社のステーションはそれらにサービスを提供できず、接続されたステーションは業界以外のものにサービスを提供しません STR_CONFIG_SETTING_EXTRADYNAMITE :街有道路・橋・トンネルの撤去容認: {STRING} STR_CONFIG_SETTING_EXTRADYNAMITE_HELPTEXT :街有の交通インフラや建物の撤去をより容易にします STR_CONFIG_SETTING_TRAIN_LENGTH :列車の最大長: {STRING} @@ -1319,6 +1340,7 @@ STR_CONFIG_SETTING_OIL_REF_EDGE_DISTANCE :石油精製所 STR_CONFIG_SETTING_OIL_REF_EDGE_DISTANCE_HELPTEXT :石油精製所はマップの外周付近にのみ建設されます。つまり、外周が海のマップでは海岸沿いに建設されるということです STR_CONFIG_SETTING_SNOWLINE_HEIGHT :雪線の位置: {STRING} STR_CONFIG_SETTING_SNOWLINE_HEIGHT_HELPTEXT :亜寒帯気候での雪線の高さを設定します。雪は産業と街の成長に影響があります +STR_CONFIG_SETTING_SNOW_COVERAGE_VALUE :{NUM}% STR_CONFIG_SETTING_ROUGHNESS_OF_TERRAIN :地形の起伏: {STRING} STR_CONFIG_SETTING_ROUGHNESS_OF_TERRAIN_HELPTEXT :(TerraGenesisのみ) 地形の起伏度を設定します。なだらかな地形では丘陵の数は減り、裾野が長くなります。起伏が多い地形では丘陵が多くなりますが、似たり寄ったりな地形の繰り返しに見えることがあります STR_CONFIG_SETTING_ROUGHNESS_OF_TERRAIN_VERY_SMOOTH :特になだらか @@ -1357,6 +1379,7 @@ STR_CONFIG_SETTING_SMALLMAP_LAND_COLOUR_VIOLET :青紫 STR_CONFIG_SETTING_SCROLLMODE_HELPTEXT :スクロール時の動き STR_CONFIG_SETTING_SCROLLMODE_DEFAULT :右クリックでビューポートを移動し、マウスの位置をロックします STR_CONFIG_SETTING_SCROLLMODE_RMB_LOCKED :右クリックで地図を移動し、マウスの位置をロックします +STR_CONFIG_SETTING_SCROLLMODE_RMB :マップを右マウスボタンで動かす STR_CONFIG_SETTING_SMOOTH_SCROLLING :画面のスムーズスクロール: {STRING} STR_CONFIG_SETTING_SMOOTH_SCROLLING_HELPTEXT :ミニマップでの移動や「現在位置に移動」などのコマンドを使用した際にメイン画面がどのように移動するかを設定します。有効にした場合はスムーズにスクロールして移動します。無効の場合は目的地に直接ジャンプします STR_CONFIG_SETTING_MEASURE_TOOLTIP :測定ツールチップ表示: {STRING} @@ -1428,6 +1451,7 @@ STR_CONFIG_SETTING_PERSISTENT_BUILDINGTOOLS_HELPTEXT :橋やトンネ STR_CONFIG_SETTING_EXPENSES_LAYOUT :財政ウィンドウのグループ分け: {STRING} STR_CONFIG_SETTING_EXPENSES_LAYOUT_HELPTEXT :財政ウィンドウのレイアウトを収入部門・支出部門でグループ分けするかどうかを設定します STR_CONFIG_SETTING_FAST_FORWARD_SPEED_LIMIT_VAL :{NUM}%通常のゲーム速度 +STR_CONFIG_SETTING_FAST_FORWARD_SPEED_LIMIT_ZERO :制限なし(コンピューターが許す限り高速) STR_CONFIG_SETTING_SOUND_TICKER :ニュース表示: {STRING} STR_CONFIG_SETTING_SOUND_TICKER_HELPTEXT :ステータスバーにニュースが流れたとき効果音を鳴らすかどうかを設定します @@ -1476,6 +1500,7 @@ STR_CONFIG_SETTING_AI_IN_MULTIPLAYER :マルチプレ STR_CONFIG_SETTING_AI_IN_MULTIPLAYER_HELPTEXT :マルチプレイヤーゲームでもAIのライバル企業が登場するかを設定します STR_CONFIG_SETTING_SCRIPT_MAX_OPCODES :命令コード処理上限: {STRING} STR_CONFIG_SETTING_SCRIPT_MAX_OPCODES_HELPTEXT :AIやゲームスクリプトが一つの「詰め込み指令」を処理する際に、一度に演算できる命令コード数を設定します。一般に値を小さくした場合、ゲームへの負荷が軽減されます +STR_CONFIG_SETTING_SCRIPT_MAX_MEMORY_VALUE :{COMMA} MiB STR_CONFIG_SETTING_SERVINT_ISPERCENT :最大信頼度を点検要件化: {STRING} STR_CONFIG_SETTING_SERVINT_ISPERCENT_HELPTEXT :次の点検が必要と判断される条件を設定します。無効の場合は、前の点検から指定の期間が経過した際に点検が必要と判断されます。有効にすると、輸送機器の最大信頼度が指定の値より落ち込んだ場合に次の点検が必要と判断されます @@ -1535,7 +1560,9 @@ STR_CONFIG_SETTING_COLOURED_NEWS_YEAR :カラー新聞 STR_CONFIG_SETTING_COLOURED_NEWS_YEAR_HELPTEXT :新聞がカラー版になる境目の年を設定します STR_CONFIG_SETTING_STARTING_YEAR :開始年: {STRING} STR_CONFIG_SETTING_ENDING_YEAR_HELPTEXT :スコアリングの目的でゲームが終了する年。 今年の終わりには、会社のスコアが記録され、ハイスコア画面が表示されますが、プレーヤーはその後もプレイを続けることができます。{}これが開始年より前の場合、ハイスコア画面は表示されません。 +STR_CONFIG_SETTING_ENDING_YEAR_VALUE :{NUM} STR_CONFIG_SETTING_ECONOMY_TYPE :エコノミータイプ:{STRING} +STR_CONFIG_SETTING_ECONOMY_TYPE_ORIGINAL :オリジナル STR_CONFIG_SETTING_ECONOMY_TYPE_SMOOTH :なだらか STR_CONFIG_SETTING_ALLOW_SHARES :他社株の取引許容: {STRING} STR_CONFIG_SETTING_ALLOW_SHARES_HELPTEXT :有効にすると、ライバル会社の株式を取引できるようになります。この設定を有効にしても、目的の社が設立から丸5年経過していない場合は取引できません @@ -1586,6 +1613,7 @@ STR_CONFIG_SETTING_TOWN_CARGOGENMODE_HELPTEXT :町の総人口 STR_CONFIG_SETTING_EXTRA_TREE_PLACEMENT :樹木の自然成長: {STRING} STR_CONFIG_SETTING_EXTRA_TREE_PLACEMENT_HELPTEXT :ゲーム中、ランダムに生えてくる樹種を設定します。設定によっては樹木の生育に依存する産業に悪影響が生じる可能性があります(「不可」にした場合、亜熱帯地域の伐採所を機能させ続けるためには手動で植林し続ける必要があります) STR_CONFIG_SETTING_EXTRA_TREE_PLACEMENT_SPREAD_RAINFOREST :成長するが、熱帯雨林にのみ広がる +STR_CONFIG_SETTING_EXTRA_TREE_PLACEMENT_SPREAD_ALL :成長し、どこにでも広がる STR_CONFIG_SETTING_TOOLBAR_POS :メインツールバーの位置: {STRING} STR_CONFIG_SETTING_TOOLBAR_POS_HELPTEXT :画面上のメインツールバーの位置を決めます @@ -1746,6 +1774,7 @@ STR_CONFIG_ERROR_OUT_OF_MEMORY :{WHITE}メモ STR_CONFIG_ERROR_SPRITECACHE_TOO_BIG :{WHITE}スプライトキャッシュ中、{BYTES}の割り当てに失敗しました。スプライトキャッシュは{BYTES}に減ったため、OpenTTDの処理速度が低下する恐れがあります。必要メモリ量を減らすには32bitグラフィックを無効にするか、最大ズームイン・ズームアウトのレベルを下げてください # Video initalization errors +STR_VIDEO_DRIVER_ERROR :{WHITE}ビデオ設定にエラーがあります... # Intro window STR_INTRO_CAPTION :{WHITE}OpenTTD {REV} @@ -1910,6 +1939,7 @@ STR_FACE_TIE :ネクタイ: STR_FACE_EARRING :イヤリング: STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}ネクタイ/イヤリングを変更します + # Network server list STR_NETWORK_SERVER_LIST_CAPTION :{WHITE}マルチプレイヤーゲーム STR_NETWORK_SERVER_LIST_PLAYER_NAME :{BLACK}プレイヤー名: @@ -1953,6 +1983,7 @@ STR_NETWORK_SERVER_LIST_REFRESH :{BLACK}サー STR_NETWORK_SERVER_LIST_REFRESH_TOOLTIP :{BLACK}サーバー情報を更新します STR_NETWORK_SERVER_LIST_SEARCH_SERVER_INTERNET :{BLACK}インターネットを検索 +STR_NETWORK_SERVER_LIST_SEARCH_SERVER_INTERNET_TOOLTIP :{BLACK}インターネットで公開されているサーバーを探す STR_NETWORK_SERVER_LIST_SEARCH_SERVER_LAN :{BLACK}LANで探す STR_NETWORK_SERVER_LIST_SEARCH_SERVER_LAN_TOOLTIP :{BLACK}ローカルエリアネットワークでサーバーを検索する STR_NETWORK_SERVER_LIST_ADD_SERVER :{BLACK}サーバーを追加 @@ -1971,10 +2002,6 @@ STR_NETWORK_START_SERVER_NEW_GAME_NAME_TOOLTIP :{BLACK}ゲー STR_NETWORK_START_SERVER_SET_PASSWORD :{BLACK}パスワードを設定 STR_NETWORK_START_SERVER_PASSWORD_TOOLTIP :{BLACK}ゲームをパスワードで保護することができます。一般から公然とアクセスされたくない場合等に設定します -STR_NETWORK_START_SERVER_ADVERTISED_LABEL :{BLACK}ゲーム公示 -STR_NETWORK_START_SERVER_ADVERTISED_TOOLTIP :{BLACK}ゲームを公示(インターネット)するか、非公示(LAN)にするかを選びます -STR_NETWORK_START_SERVER_UNADVERTISED :非公示 -STR_NETWORK_START_SERVER_ADVERTISED :公示 STR_NETWORK_START_SERVER_CLIENTS_SELECT :{BLACK}接続者数: {NUM} STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS :{BLACK}最大接続数: STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS_TOOLTIP :{BLACK}接続できるクライアントの最大数を指定します。必ずしも全スロットを埋める必要はありません @@ -2039,15 +2066,10 @@ STR_NETWORK_NEED_COMPANY_PASSWORD_CAPTION :{WHITE}この # Network company list added strings STR_NETWORK_COMPANY_LIST_CLIENT_LIST :クライアントリスト -STR_NETWORK_COMPANY_LIST_SPECTATE :観覧モード -STR_NETWORK_COMPANY_LIST_NEW_COMPANY :新会社 # Network client list -STR_NETWORK_CLIENTLIST_KICK :追放 -STR_NETWORK_CLIENTLIST_BAN :参入禁止 -STR_NETWORK_CLIENTLIST_SPEAK_TO_ALL :全員へ発言 -STR_NETWORK_CLIENTLIST_SPEAK_TO_COMPANY :チームへ発言 -STR_NETWORK_CLIENTLIST_SPEAK_TO_CLIENT :個人的なメッセージ + + STR_NETWORK_SERVER :サーバー STR_NETWORK_CLIENT :クライアント @@ -2103,6 +2125,7 @@ STR_NETWORK_ERROR_TIMEOUT_PASSWORD :{WHITE}パス STR_NETWORK_ERROR_TIMEOUT_COMPUTER :{WHITE}サーバーとの接続を維持できる十分な処理能力がありません STR_NETWORK_ERROR_TIMEOUT_MAP :{WHITE}マップのダウンロード時間が規定を超過しました STR_NETWORK_ERROR_TIMEOUT_JOIN :{WHITE}タイムアウトによりサーバーへの接続を確立できません +STR_NETWORK_ERROR_INVALID_CLIENT_NAME :{WHITE}プレイヤー名が無効です ############ Leave those lines in this order!! STR_NETWORK_ERROR_CLIENT_GENERAL :一般エラー @@ -2138,6 +2161,7 @@ STR_NETWORK_SERVER_MESSAGE_GAME_STILL_PAUSED_1 :ゲームはま STR_NETWORK_SERVER_MESSAGE_GAME_STILL_PAUSED_2 :ゲームはまだポーズされています。({STRING}、{STRING}) STR_NETWORK_SERVER_MESSAGE_GAME_STILL_PAUSED_3 :ゲームはまだポーズされています。 ({STRING}、{STRING}、{STRING}) STR_NETWORK_SERVER_MESSAGE_GAME_STILL_PAUSED_4 :ゲームはまだポーズされています。 ({STRING}、{STRING}、{STRING}、{STRING}) +STR_NETWORK_SERVER_MESSAGE_GAME_STILL_PAUSED_5 :ゲームはまだポーズされています。 ({STRING}, {STRING}, {STRING}, {STRING}, {STRING}) STR_NETWORK_SERVER_MESSAGE_GAME_UNPAUSED :ゲームのポーズが解除されました。({STRING}) STR_NETWORK_SERVER_MESSAGE_GAME_REASON_NOT_ENOUGH_PLAYERS :プレーヤー数不足 STR_NETWORK_SERVER_MESSAGE_GAME_REASON_CONNECTING_CLIENTS :クライアントに接続中 @@ -2155,6 +2179,7 @@ STR_NETWORK_MESSAGE_CLIENT_LEFT :*** {STRING} STR_NETWORK_MESSAGE_NAME_CHANGE :*** {STRING} は名前を {STRING} に変更しました STR_NETWORK_MESSAGE_SERVER_SHUTDOWN :{WHITE}サーバがセッションを終了しました STR_NETWORK_MESSAGE_SERVER_REBOOT :{WHITE}このサーバーは再起動中です…{}しばらくお待ちください… +STR_NETWORK_MESSAGE_KICKED :*** {STRING}がキックされました。理由: ({STRING}) # Content downloading window STR_CONTENT_TITLE :{WHITE}コンテンツをダウンロード中 @@ -2230,6 +2255,7 @@ STR_MISSING_GRAPHICS_YES_DOWNLOAD :{BLACK}はい STR_MISSING_GRAPHICS_NO_QUIT :{BLACK}いいえ、OpenTTDを終了します STR_MISSING_GRAPHICS_ERROR_TITLE :{WHITE}ダウンロードに失敗しました +STR_MISSING_GRAPHICS_ERROR :{BLACK}グラフィックのダウンロードに失敗しました。{}手動でダウンロードしてください。 # Transparency settings window STR_TRANSPARENCY_CAPTION :{WHITE}透過表示設定 @@ -2249,6 +2275,7 @@ STR_LINKGRAPH_LEGEND_CAPTION :{BLACK}貨物 STR_LINKGRAPH_LEGEND_ALL :{BLACK}全て STR_LINKGRAPH_LEGEND_NONE :{BLACK}なし STR_LINKGRAPH_LEGEND_SELECT_COMPANIES :{BLACK}表示する会社を選択 +STR_LINKGRAPH_LEGEND_COMPANY_TOOLTIP :{BLACK}{STRING}{}{COMPANY} # Linkgraph legend window and linkgraph legend in smallmap STR_LINKGRAPH_LEGEND_UNUSED :{TINY_FONT}{BLACK}未使用(運送過多) @@ -2375,6 +2402,7 @@ STR_ROAD_TOOLBAR_TOOLTIP_BUILD_ROAD_TUNNEL :{BLACK}道路 STR_ROAD_TOOLBAR_TOOLTIP_BUILD_TRAMWAY_TUNNEL :{BLACK}軌道用トンネルを建設します。Shiftを押しながら決定すると費用の見積が出ます STR_ROAD_TOOLBAR_TOOLTIP_TOGGLE_BUILD_REMOVE_FOR_ROAD :{BLACK}道路の建設/撤去を切り替えます STR_ROAD_TOOLBAR_TOOLTIP_TOGGLE_BUILD_REMOVE_FOR_TRAMWAYS :{BLACK}軌道の建設/撤去を切り替えます +STR_ROAD_TOOLBAR_TOOLTIP_CONVERT_ROAD :{BLACK}道路の種類を変更/アップグレードします.Shiftは、コスト見積もりの​​作成/表示を切り替えます STR_ROAD_NAME_TRAM :トラムウェイ @@ -2465,6 +2493,7 @@ STR_TREES_RANDOM_TYPE_TOOLTIP :{BLACK}ラン STR_TREES_RANDOM_TREES_BUTTON :{BLACK}ランダムに広域植林 STR_TREES_RANDOM_TREES_TOOLTIP :{BLACK}地表全体にランダムに植林します STR_TREES_MODE_FOREST_SM_BUTTON :{BLACK}グローブ +STR_TREES_MODE_FOREST_SM_TOOLTIP :{BLACK}風景をドラッグして小さな森を植えます STR_TREES_MODE_FOREST_LG_TOOLTIP :{BLACK}風景の上をドラッグして、大きな森を植えます。 # Land generation window (SE) @@ -2524,6 +2553,7 @@ STR_FUND_INDUSTRY_INDUSTRY_BUILD_COST :{BLACK}費用: STR_FUND_INDUSTRY_PROSPECT_NEW_INDUSTRY :{BLACK}調査/探鉱 STR_FUND_INDUSTRY_BUILD_NEW_INDUSTRY :{BLACK}建設 STR_FUND_INDUSTRY_FUND_NEW_INDUSTRY :{BLACK}出資 +STR_FUND_INDUSTRY_REMOVE_ALL_INDUSTRIES_QUERY :{YELLOW}すべての産業を削除してもよろしいですか? # Industry cargoes window STR_INDUSTRY_CARGOES_INDUSTRY_CAPTION :{WHITE}産業:{STRING}に関わる産業チェーン @@ -2670,22 +2700,36 @@ STR_ABOUT_COPYRIGHT_OPENTTD :{BLACK}OpenTTD # Framerate display window STR_FRAMERATE_CAPTION :{WHITE}フレームレート STR_FRAMERATE_RATE_BLITTER :{BLACK}グラフィックフレームレート:{STRING} +STR_FRAMERATE_SPEED_FACTOR :{BLACK}現在のゲーム速度:{DECIMAL} x +STR_FRAMERATE_CURRENT :{WHITE}現在 +STR_FRAMERATE_AVERAGE :{WHITE}平均 +STR_FRAMERATE_MEMORYUSE :{WHITE}メモリ STR_FRAMERATE_MS_GOOD :{LTBLUE}{DECIMAL} ms STR_FRAMERATE_MS_WARN :{YELLOW} {DECIMAL} ms STR_FRAMERATE_FPS_GOOD :{LTBLUE}{DECIMAL} フレーム/秒 STR_FRAMERATE_FPS_WARN :{YELLOW} {DECIMAL}フレーム/秒 +STR_FRAMERATE_FPS_BAD :{RED} {DECIMAL}FPS STR_FRAMERATE_BYTES_GOOD :{LTBLUE}{BYTES} ############ Leave those lines in this order!! STR_FRAMERATE_GAMELOOP :{BLACK}ゲームループの合計: STR_FRAMERATE_GL_ECONOMY :{BLACK}貨物の取り扱い: +STR_FRAMERATE_GL_TRAINS :{BLACK} 鉄道車両のティック: +STR_FRAMERATE_GL_ROADVEHS :{BLACK} 自動車ティック: +STR_FRAMERATE_GL_AIRCRAFT :{BLACK}航空機ティック: +STR_FRAMERATE_DRAWING :{BLACK}グラフィックレンダリング: STR_FRAMERATE_VIDEO :{BLACK}ビデオ出力: +STR_FRAMERATE_SOUND :{BLACK}サウンドミキサー: ############ End of leave-in-this-order ############ Leave those lines in this order!! STR_FRAMETIME_CAPTION_GAMELOOP :ゲームループ STR_FRAMETIME_CAPTION_GL_ECONOMY :貨物の取り扱い STR_FRAMETIME_CAPTION_GL_TRAINS :切符 +STR_FRAMETIME_CAPTION_GL_LANDSCAPE :ワールドティック STR_FRAMETIME_CAPTION_GL_LINKGRAPH :リンクグラフの遅延 +STR_FRAMETIME_CAPTION_DRAWING :グラフィックレンダリング STR_FRAMETIME_CAPTION_DRAWING_VIEWPORTS :ワールドビューポートレンダリング +STR_FRAMETIME_CAPTION_VIDEO :ビデオ出力 +STR_FRAMETIME_CAPTION_AI :AI {NUM} {STRING} ############ End of leave-in-this-order @@ -2711,8 +2755,10 @@ STR_SAVELOAD_DETAIL_CAPTION :{BLACK}ゲー STR_SAVELOAD_DETAIL_NOT_AVAILABLE :{BLACK}― 情報なし ― STR_SAVELOAD_DETAIL_COMPANY_INDEX :{SILVER}{COMMA}: {WHITE}{STRING} STR_SAVELOAD_DETAIL_GRFSTATUS :{SILVER}NewGRF: {WHITE}{STRING} +STR_SAVELOAD_FILTER_TITLE :{BLACK}フィルター: STR_SAVELOAD_OVERWRITE_TITLE :{WHITE}ファイルを上書きする STR_SAVELOAD_OVERWRITE_WARNING :{YELLOW}既存のファイルを上書きしてもよろしいですか? +STR_SAVELOAD_PARENT_DIRECTORY :{STRING} (Parent directory) STR_SAVELOAD_OSKTITLE :{BLACK}保存名を入力 @@ -2724,6 +2770,14 @@ STR_MAPGEN_BY :{BLACK}× STR_MAPGEN_NUMBER_OF_TOWNS :{BLACK}街数: STR_MAPGEN_DATE :{BLACK}日付: STR_MAPGEN_NUMBER_OF_INDUSTRIES :{BLACK}産業数: +STR_MAPGEN_HEIGHTMAP_HEIGHT :{BLACK}最高峰: +STR_MAPGEN_HEIGHTMAP_HEIGHT_DOWN :{BLACK}マップの最高峰の最大の高さを1減らします。 +STR_MAPGEN_SNOW_COVERAGE :{BLACK}降雪量: +STR_MAPGEN_SNOW_COVERAGE_DOWN :{BLACK}積雪量を10%減らします +STR_MAPGEN_SNOW_COVERAGE_TEXT :{BLACK} {NUM}% +STR_MAPGEN_DESERT_COVERAGE :{BLACK}砂漠の範囲: +STR_MAPGEN_DESERT_COVERAGE_UP :{BLACK}砂漠の割合を10%増やします +STR_MAPGEN_DESERT_COVERAGE_DOWN :{BLACK}砂漠の範囲を10%減らします STR_MAPGEN_DESERT_COVERAGE_TEXT :{BLACK}{NUM}% STR_MAPGEN_LAND_GENERATOR :{BLACK}地形作成: STR_MAPGEN_TERRAIN_TYPE :{BLACK}地形種類: @@ -2750,6 +2804,8 @@ STR_MAPGEN_HEIGHTMAP_NAME :{BLACK}ハイ STR_MAPGEN_HEIGHTMAP_SIZE_LABEL :{BLACK}サイズ: STR_MAPGEN_HEIGHTMAP_SIZE :{ORANGE}{NUM} × {NUM} +STR_MAPGEN_SNOW_COVERAGE_QUERY_CAPT :{WHITE}積雪量(%) +STR_MAPGEN_DESERT_COVERAGE_QUERY_CAPT :{WHITE}砂漠領域(%) STR_MAPGEN_START_DATE_QUERY_CAPT :{WHITE}開始年の変更 # SE Map generation @@ -3004,6 +3060,7 @@ STR_TOWN_VIEW_RENAME_TOWN_BUTTON :街名を変更 # Town local authority window STR_LOCAL_AUTHORITY_CAPTION :{WHITE}{TOWN} 地方自治体 STR_LOCAL_AUTHORITY_ZONE :ゾーン +STR_LOCAL_AUTHORITY_ZONE_TOOLTIP :{BLACK}地方自治体の境界を見る STR_LOCAL_AUTHORITY_COMPANY_RATINGS :{BLACK}社の評判: STR_LOCAL_AUTHORITY_COMPANY_RATING :{YELLOW}{COMPANY} {COMPANY_NUM}: {ORANGE}{STRING} STR_LOCAL_AUTHORITY_ACTIONS_TITLE :{BLACK}可能な活動: @@ -3032,6 +3089,7 @@ STR_LOCAL_AUTHORITY_ACTION_TOOLTIP_BRIBE :{YELLOW}買収 # Goal window STR_GOALS_CAPTION :{WHITE}{COMPANY} 目標 STR_GOALS_SPECTATOR_CAPTION :{WHITE}大目標 +STR_GOALS_GLOBAL_BUTTON_HELPTEXT :{BLACK}グローバルな目標を表示する STR_GOALS_COMPANY_BUTTON :{BLACK}会社 STR_GOALS_COMPANY_BUTTON_HELPTEXT :{BLACK}会社の目標を見る STR_GOALS_TEXT :{ORANGE}{STRING} @@ -3234,6 +3292,7 @@ STR_COMPANY_VIEW_RELOCATE_HQ :{BLACK}本社 STR_COMPANY_VIEW_RELOCATE_COMPANY_HEADQUARTERS :{BLACK}本社ビルを移転します(費用は社の総資産の1%になります)。Shift+クリックで費用を見積もります STR_COMPANY_VIEW_INFRASTRUCTURE_BUTTON :{BLACK}詳細 STR_COMPANY_VIEW_INFRASTRUCTURE_TOOLTIP :{BLACK}インフラ設備の詳細な個数・タイル数を表示します +STR_COMPANY_VIEW_GIVE_MONEY_TOOLTIP :{BLACK}この会社にお金を渡す STR_COMPANY_VIEW_NEW_FACE_BUTTON :{BLACK}顔の変更 STR_COMPANY_VIEW_NEW_FACE_TOOLTIP :{BLACK}社長の顔を変更します @@ -3251,6 +3310,7 @@ STR_COMPANY_VIEW_SELL_SHARE_TOOLTIP :{BLACK}この STR_COMPANY_VIEW_COMPANY_NAME_QUERY_CAPTION :会社名 STR_COMPANY_VIEW_PRESIDENT_S_NAME_QUERY_CAPTION :社長名 +STR_COMPANY_VIEW_GIVE_MONEY_QUERY_CAPTION :寄付したい金額を入力してください STR_BUY_COMPANY_MESSAGE :{WHITE}現在、当{COMPANY}は業績悪化に伴い、債務の肩代わりを条件に社の全資産をお譲り致そうと考えております。{}{}債務{CURRENCY_LONG}を一括代済し、この会社を吸収合併しますか? @@ -3259,6 +3319,7 @@ STR_COMPANY_INFRASTRUCTURE_VIEW_CAPTION :{WHITE}{COMPANY STR_COMPANY_INFRASTRUCTURE_VIEW_RAIL_SECT :{GOLD}線路長: STR_COMPANY_INFRASTRUCTURE_VIEW_SIGNALS :{WHITE}信号 STR_COMPANY_INFRASTRUCTURE_VIEW_ROAD_SECT :{GOLD}道路長(含軌道): +STR_COMPANY_INFRASTRUCTURE_VIEW_TRAM_SECT :{GOLD}路面電車軌道: STR_COMPANY_INFRASTRUCTURE_VIEW_WATER_SECT :{GOLD}水運長: STR_COMPANY_INFRASTRUCTURE_VIEW_CANALS :{WHITE}運河 STR_COMPANY_INFRASTRUCTURE_VIEW_STATION_SECT :{GOLD}停留施設数: @@ -3269,8 +3330,10 @@ STR_COMPANY_INFRASTRUCTURE_VIEW_TOTAL :{WHITE}{CURRENC # Industry directory STR_INDUSTRY_DIRECTORY_CAPTION :{WHITE}産業 STR_INDUSTRY_DIRECTORY_NONE :{ORANGE}- なし - +STR_INDUSTRY_DIRECTORY_ITEM_INFO :{BLACK}{CARGO_LONG}{STRING}{YELLOW} ({COMMA}% 輸送済み){BLACK} STR_INDUSTRY_DIRECTORY_ITEM_NOPROD :{ORANGE}{INDUSTRY} STR_INDUSTRY_DIRECTORY_LIST_CAPTION :{BLACK}産業の名前です - 名前をクリックするとこの産業拠点の場所にメイン画面を移動します。Ctrl+クリックでこの産業拠点の場所を新たなビューポートに表示します +STR_INDUSTRY_DIRECTORY_ACCEPTED_CARGO_FILTER :{BLACK}受け取った貨物: {SILVER}{STRING} STR_INDUSTRY_DIRECTORY_FILTER_ALL_TYPES :すべての貨物タイプ STR_INDUSTRY_DIRECTORY_FILTER_NONE :なし @@ -3283,6 +3346,7 @@ STR_INDUSTRY_VIEW_PRODUCTION_LEVEL :{BLACK}生産 STR_INDUSTRY_VIEW_INDUSTRY_ANNOUNCED_CLOSURE :{YELLOW}この産業拠点は間もなく閉鎖されます! STR_INDUSTRY_VIEW_REQUIRES_N_CARGO :{BLACK}必要条件:{YELLOW} {STRING} {STRING} +STR_INDUSTRY_VIEW_CARGO_LIST_EXTENSION :、{STRING} {STRING} STR_INDUSTRY_VIEW_REQUIRES : {BLACK}必要物資: STR_INDUSTRY_VIEW_ACCEPT_CARGO_AMOUNT :{YELLOW} {STRING} {BLACK}:{CARGO_SHORT}待機中{STRING} @@ -3353,6 +3417,7 @@ STR_GROUP_RENAME_CAPTION :{BLACK}グル STR_GROUP_PROFIT_THIS_YEAR :今年の利益: STR_GROUP_PROFIT_LAST_YEAR :昨年の利益: +STR_GROUP_OCCUPANCY :選択中: # Build vehicle window STR_BUY_VEHICLE_TRAIN_RAIL_CAPTION :新規機関車(非電化) @@ -3361,6 +3426,7 @@ STR_BUY_VEHICLE_TRAIN_MONORAIL_CAPTION :新規モノレ STR_BUY_VEHICLE_TRAIN_MAGLEV_CAPTION :新規リニア車両 STR_BUY_VEHICLE_ROAD_VEHICLE_CAPTION :新規車両 +STR_BUY_VEHICLE_TRAM_VEHICLE_CAPTION :新しい路面電車車両 ############ range for vehicle availability starts STR_BUY_VEHICLE_TRAIN_ALL_CAPTION :新規列車 @@ -3369,6 +3435,7 @@ STR_BUY_VEHICLE_AIRCRAFT_CAPTION :新規航空機 ############ range for vehicle availability ends STR_PURCHASE_INFO_COST_WEIGHT :{BLACK}価格: {GOLD}{CURRENCY_LONG}{BLACK} 重量: {GOLD}{WEIGHT_SHORT} +STR_PURCHASE_INFO_COST_REFIT_WEIGHT :{BLACK}コスト:{GOLD} {CURRENCY_LONG} {BLACK}(修理コスト:{GOLD} {CURRENCY_LONG} {BLACK})重量:{GOLD} {WEIGHT_SHORT} STR_PURCHASE_INFO_SPEED_POWER :{BLACK}最高速度: {GOLD}{VELOCITY}{BLACK} 出力: {GOLD}{POWER} STR_PURCHASE_INFO_SPEED :{BLACK}最高速度: {GOLD}{VELOCITY} STR_PURCHASE_INFO_SPEED_OCEAN :{BLACK}外洋/湖での航行速度: {GOLD}{VELOCITY} @@ -3392,6 +3459,7 @@ STR_PURCHASE_INFO_ENGINES_ONLY :エンジンの STR_PURCHASE_INFO_ALL_BUT :{CARGO_LIST}を除いてすべて STR_PURCHASE_INFO_MAX_TE :{BLACK}最大牽引力: {GOLD}{FORCE} STR_PURCHASE_INFO_AIRCRAFT_RANGE :{BLACK}航続距離: {GOLD}{COMMA} タイル +STR_PURCHASE_INFO_AIRCRAFT_TYPE :{BLACK}航空機の機種: {GOLD}{STRING} STR_BUY_VEHICLE_TRAIN_LIST_TOOLTIP :{BLACK}列車リスト - 個々の情報を見るには列車をクリックします。Ctrl+クリックでその列車種の表示/非表示を切り替えます STR_BUY_VEHICLE_ROAD_VEHICLE_LIST_TOOLTIP :{BLACK}車両リスト - 個々の情報を見るには車両をクリックします。Ctrl+クリックでその車両種の表示/非表示を切り替えます @@ -3403,12 +3471,15 @@ STR_BUY_VEHICLE_ROAD_VEHICLE_BUY_VEHICLE_BUTTON :{BLACK}車両 STR_BUY_VEHICLE_SHIP_BUY_VEHICLE_BUTTON :{BLACK}船舶を購入 STR_BUY_VEHICLE_AIRCRAFT_BUY_VEHICLE_BUTTON :{BLACK}航空機を購入 +STR_BUY_VEHICLE_TRAIN_BUY_REFIT_VEHICLE_BUTTON :{BLACK}購入し改造する +STR_BUY_VEHICLE_SHIP_BUY_REFIT_VEHICLE_BUTTON :{BLACK}船の購入と修理 STR_BUY_VEHICLE_TRAIN_BUY_VEHICLE_TOOLTIP :{BLACK}選択した列車を購入します。Shift+クリックで購入費を見積もります STR_BUY_VEHICLE_ROAD_VEHICLE_BUY_VEHICLE_TOOLTIP :{BLACK}選択した車両を購入します。Shift+クリックで購入費を見積もります STR_BUY_VEHICLE_SHIP_BUY_VEHICLE_TOOLTIP :{BLACK}選択した船舶を購入します。Shift+クリックで購入費を見積もります STR_BUY_VEHICLE_AIRCRAFT_BUY_VEHICLE_TOOLTIP :{BLACK}選択した航空機を購入します。Shift+クリックで購入費を見積もります +STR_BUY_VEHICLE_SHIP_BUY_REFIT_VEHICLE_TOOLTIP :{BLACK}ハイライトされた船を購入して修理します。Shift +クリックすると、購入なしの推定コストが表示されます STR_BUY_VEHICLE_TRAIN_RENAME_BUTTON :{BLACK}名称を変更 STR_BUY_VEHICLE_ROAD_VEHICLE_RENAME_BUTTON :{BLACK}名称を変更 @@ -3524,6 +3595,7 @@ STR_ENGINE_PREVIEW_MONORAIL_LOCOMOTIVE :モノレール STR_ENGINE_PREVIEW_MAGLEV_LOCOMOTIVE :リニア列車 STR_ENGINE_PREVIEW_ROAD_VEHICLE :車両 +STR_ENGINE_PREVIEW_TRAM_VEHICLE :路面電車車両 STR_ENGINE_PREVIEW_AIRCRAFT :航空機 STR_ENGINE_PREVIEW_SHIP :船舶 @@ -3571,6 +3643,7 @@ STR_REPLACE_ELRAIL_VEHICLES :機関車(電 STR_REPLACE_MONORAIL_VEHICLES :モノレール車両 STR_REPLACE_MAGLEV_VEHICLES :リニア車両 +STR_REPLACE_ROAD_VEHICLES :道路車両 STR_REPLACE_TRAM_VEHICLES :路面電車の車両 STR_REPLACE_REMOVE_WAGON :{BLACK}列車の短縮: {ORANGE}{STRING} @@ -3611,6 +3684,8 @@ STR_VEHICLE_VIEW_ROAD_VEHICLE_SHOW_DETAILS_TOOLTIP :{BLACK}車両 STR_VEHICLE_VIEW_SHIP_SHOW_DETAILS_TOOLTIP :{BLACK}船舶の情報を表示 STR_VEHICLE_VIEW_AIRCRAFT_SHOW_DETAILS_TOOLTIP :{BLACK}航空機の情報を表示します +STR_VEHICLE_VIEW_TRAIN_STATUS_START_STOP_TOOLTIP :{BLACK}現在の列車の動作-クリックして列車を停止/開始します +STR_VEHICLE_VIEW_ROAD_VEHICLE_STATUS_START_STOP_TOOLTIP :{BLACK}現在の車両の動作-クリックして車両を停止/開始します STR_VEHICLE_VIEW_SHIP_STATE_STATUS_STOP_TOOLTIP :{BLACK}現在の船のアクション-クリックして船を停止/開始します STR_VEHICLE_VIEW_AIRCRAFT_STATUS_START_STOP_TOOLTIP :{BLACK}現在の航空機のアクション-クリックして航空機を停止/開始します @@ -3653,6 +3728,8 @@ STR_VEHICLE_INFO_AGE :{COMMA}年({COM STR_VEHICLE_INFO_AGE_RED :{RED}{COMMA}年({COMMA}年) STR_VEHICLE_INFO_MAX_SPEED :{BLACK}最高速度: {LTBLUE}{VELOCITY} +STR_VEHICLE_INFO_MAX_SPEED_TYPE :{BLACK}最高速度: {LTBLUE}{VELOCITY} {BLACK}航空機のタイプ: {LTBLUE}{STRING} +STR_VEHICLE_INFO_MAX_SPEED_TYPE_RANGE :{BLACK}最高速度: {LTBLUE}{VELOCITY} {BLACK}航空機のタイプ: {LTBLUE}{STRING} {BLACK}範囲: {LTBLUE}{COMMA}タイル STR_VEHICLE_INFO_WEIGHT_POWER_MAX_SPEED :{BLACK}重量: {LTBLUE}{WEIGHT_SHORT} {BLACK}出力: {LTBLUE}{POWER}{BLACK} 最高速度: {LTBLUE}{VELOCITY} STR_VEHICLE_INFO_WEIGHT_POWER_MAX_SPEED_MAX_TE :{BLACK}重量: {LTBLUE}{WEIGHT_SHORT} {BLACK}出力: {LTBLUE}{POWER}{BLACK} 最高速度: {LTBLUE}{VELOCITY} {BLACK}最大牽引力: {LTBLUE}{FORCE} @@ -3838,6 +3915,7 @@ STR_ORDER_REFIT_STOP_ORDER :({STRING}に改 STR_ORDER_STOP_ORDER :(運用停止) STR_ORDER_GO_TO_STATION :{1:STATION}へ{0:STRING} {2:STRING} +STR_ORDER_GO_TO_STATION_CAN_T_USE_STATION :{PUSH_COLOUR}{RED}(駅を使用できません){POP_COLOUR} {STRING} {STATION} {STRING} STR_ORDER_IMPLICIT :(自動) @@ -4016,7 +4094,9 @@ STR_AI_LIST_ACCEPT_TOOLTIP :{BLACK}選択 STR_AI_LIST_CANCEL :{BLACK}キャンセル STR_AI_LIST_CANCEL_TOOLTIP :{BLACK}スクリプトを変更しません +STR_SCREENSHOT_ZOOMIN_SCREENSHOT :{BLACK}スクリーンショットを完全に拡大 STR_SCREENSHOT_WORLD_SCREENSHOT :{BLACK}地図全体のスクリーンショット +STR_SCREENSHOT_HEIGHTMAP_SCREENSHOT :{BLACK}ハイトマップスクリーンショット # AI Parameters STR_AI_SETTINGS_CAPTION :{WHITE}{STRING} パラメータ @@ -4076,6 +4156,7 @@ STR_GAME_SAVELOAD_ERROR_TOO_NEW_SAVEGAME :このファイ STR_GAME_SAVELOAD_ERROR_FILE_NOT_READABLE :ファイルを読み込むことができません STR_GAME_SAVELOAD_ERROR_FILE_NOT_WRITEABLE :ファイルに書き込むことができません STR_GAME_SAVELOAD_ERROR_DATA_INTEGRITY_CHECK_FAILED :データ保全性チェック失敗 +STR_GAME_SAVELOAD_ERROR_PATCHPACK :安定版のセーブデータです。 STR_GAME_SAVELOAD_NOT_AVAILABLE :<使用不能> STR_WARNING_LOADGAME_REMOVED_TRAMS :{WHITE}このゲームは路面電車に対応していないバージョンで保存されましたので、すべての路面電車が削除されました。 @@ -4283,6 +4364,7 @@ STR_ERROR_TRAIN_TOO_LONG_AFTER_REPLACEMENT :{WHITE}{VEHICLE STR_ERROR_AUTOREPLACE_NOTHING_TO_DO :{WHITE}この輸送機器の自動置換/更新は行われませんでした STR_ERROR_AUTOREPLACE_MONEY_LIMIT :(最低資金が確保できていません) STR_ERROR_AUTOREPLACE_INCOMPATIBLE_CARGO :{WHITE}新しい車両は{STRING}を運ぶことができません +STR_ERROR_AUTOREPLACE_INCOMPATIBLE_REFIT :{WHITE}新しい車両は順番に修理できません{NUM} # Rail construction errors STR_ERROR_IMPOSSIBLE_TRACK_COMBINATION :{WHITE}不可能な線路の組み合わせです @@ -4291,6 +4373,7 @@ STR_ERROR_NO_SUITABLE_RAILROAD_TRACK :{WHITE}適当 STR_ERROR_MUST_REMOVE_RAILROAD_TRACK :{WHITE}先に線路を撤去しなければなりません STR_ERROR_CROSSING_ON_ONEWAY_ROAD :{WHITE}道路は一方通行または進入禁止です STR_ERROR_CROSSING_DISALLOWED_RAIL :{WHITE}このレール種別との平面交差はできません +STR_ERROR_CROSSING_DISALLOWED_ROAD :{WHITE}この道路では踏切は作れません STR_ERROR_CAN_T_BUILD_SIGNALS_HERE :{WHITE}ここには信号を設置できません STR_ERROR_CAN_T_BUILD_RAILROAD_TRACK :{WHITE}ここには線路を建設できません STR_ERROR_CAN_T_REMOVE_RAILROAD_TRACK :{WHITE}ここから線路を撤去できません @@ -4311,6 +4394,9 @@ STR_ERROR_CAN_T_REMOVE_TRAMWAY_FROM :{WHITE}ここ STR_ERROR_THERE_IS_NO_ROAD :{WHITE}道路がありません STR_ERROR_THERE_IS_NO_TRAMWAY :{WHITE}軌道がありません STR_ERROR_CAN_T_CONVERT_ROAD :{WHITE}この道路のタイプは変更できません +STR_ERROR_CAN_T_CONVERT_TRAMWAY :{WHITE}ここで路面電車の種類を変更できません... +STR_ERROR_NO_SUITABLE_TRAMWAY :{WHITE}有効な路面電車がありません +STR_ERROR_INCOMPATIBLE_TRAMWAY :{WHITE}軌道に互換性がありません # Waterway construction errors STR_ERROR_CAN_T_BUILD_CANALS :{WHITE}ここには運河を建設できません @@ -4862,6 +4948,7 @@ STR_FORMAT_BUOY_NAME :{TOWN} ブイ STR_FORMAT_BUOY_NAME_SERIAL :{TOWN} 第{COMMA}ブイ STR_FORMAT_COMPANY_NUM :(会社{COMMA}) STR_FORMAT_GROUP_NAME :グループ {COMMA} +STR_FORMAT_GROUP_VEHICLE_NAME :{GROUP} #{COMMA} STR_FORMAT_INDUSTRY_NAME :{TOWN}{STRING} STR_FORMAT_WAYPOINT_NAME :{TOWN}中継駅 STR_FORMAT_WAYPOINT_NAME_SERIAL :第{1:COMMA} {0:TOWN}中継駅 diff --git a/src/lang/korean.txt b/src/lang/korean.txt index 3495651e76..471de45fc3 100644 --- a/src/lang/korean.txt +++ b/src/lang/korean.txt @@ -1992,6 +1992,7 @@ STR_FACE_TIE :넥타이: STR_FACE_EARRING :귀걸이: STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}넥타이/귀걸이 변경 + # Network server list STR_NETWORK_SERVER_LIST_CAPTION :{WHITE}멀티 플레이 STR_NETWORK_SERVER_LIST_PLAYER_NAME :{BLACK}플레이어 이름: @@ -2054,10 +2055,6 @@ STR_NETWORK_START_SERVER_NEW_GAME_NAME_TOOLTIP :{BLACK}멀티 STR_NETWORK_START_SERVER_SET_PASSWORD :{BLACK}비밀번호 설정 STR_NETWORK_START_SERVER_PASSWORD_TOOLTIP :{BLACK}서버에 공개적으로 접근하는 것을 막고 싶을 때 비밀번호를 걸어 보호합니다. -STR_NETWORK_START_SERVER_ADVERTISED_LABEL :{BLACK}공개 여부 -STR_NETWORK_START_SERVER_ADVERTISED_TOOLTIP :{BLACK}공개된 게임(인터넷)과 비공개된 게임(LAN) 중에서 선택하세요 -STR_NETWORK_START_SERVER_UNADVERTISED :아니요 -STR_NETWORK_START_SERVER_ADVERTISED :예 STR_NETWORK_START_SERVER_CLIENTS_SELECT :{BLACK}{NUM}명 STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS :{BLACK}최대 접속자 수: STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS_TOOLTIP :{BLACK}최대 접속자 수를 선택합니다. 모든 자리가 다 차 있을 필요는 없습니다 @@ -2119,19 +2116,13 @@ STR_NETWORK_CONNECTION_DISCONNECT :{BLACK}접속 STR_NETWORK_NEED_GAME_PASSWORD_CAPTION :{WHITE}서버 암호가 걸려있습니다. 암호를 입력하세요 STR_NETWORK_NEED_COMPANY_PASSWORD_CAPTION :{WHITE}회사 암호가 걸려있습니다. 암호를 입력하세요 -STR_NETWORK_COMPANY_LIST_CLIENT_LIST_CAPTION :{WHITE}접속자 목록 # Network company list added strings STR_NETWORK_COMPANY_LIST_CLIENT_LIST :접속자 목록 -STR_NETWORK_COMPANY_LIST_SPECTATE :관전 -STR_NETWORK_COMPANY_LIST_NEW_COMPANY :새 회사 # Network client list -STR_NETWORK_CLIENTLIST_KICK :추방 -STR_NETWORK_CLIENTLIST_BAN :차단 -STR_NETWORK_CLIENTLIST_SPEAK_TO_ALL :모두에게 말하기 -STR_NETWORK_CLIENTLIST_SPEAK_TO_COMPANY :이 회사에게 말하기 -STR_NETWORK_CLIENTLIST_SPEAK_TO_CLIENT :귓속말하기 + + STR_NETWORK_SERVER :서버 STR_NETWORK_CLIENT :접속자 @@ -2176,6 +2167,7 @@ STR_NETWORK_ERROR_SERVER_START :{WHITE}서버 STR_NETWORK_ERROR_CLIENT_START :{WHITE}접속할 수 없습니다 STR_NETWORK_ERROR_TIMEOUT :{WHITE}접속자 #{NUM}의 입력 시간이 초과되었습니다 STR_NETWORK_ERROR_SERVER_ERROR :{WHITE}프로토콜 오류가 발생되어 연결이 끊어졌습니다 +STR_NETWORK_ERROR_BAD_PLAYER_NAME :{WHITE}플레이에 사용할 이름을 설정하지 않았습니다. 이름은 멀티플레이 창의 상단에서 설정할 수 있습니다. STR_NETWORK_ERROR_WRONG_REVISION :{WHITE}이 접속자의 게임 버전이 서버의 버전과 일치하지 않습니다 STR_NETWORK_ERROR_WRONG_PASSWORD :{WHITE}잘못된 비밀번호입니다 STR_NETWORK_ERROR_SERVER_FULL :{WHITE}서버에 인원이 가득 찼습니다 @@ -2188,6 +2180,7 @@ STR_NETWORK_ERROR_TIMEOUT_PASSWORD :{WHITE}비밀 STR_NETWORK_ERROR_TIMEOUT_COMPUTER :{WHITE}사용자의 컴퓨터가 서버와 연결을 유지할 수 있을 만큼 빠르지 않습니다 STR_NETWORK_ERROR_TIMEOUT_MAP :{WHITE}지도 다운로드 시간을 초과하였습니다 STR_NETWORK_ERROR_TIMEOUT_JOIN :{WHITE}서버 접속 시간을 초과하였습니다 +STR_NETWORK_ERROR_INVALID_CLIENT_NAME :{WHITE}사용할 수 없는 이름입니다 ############ Leave those lines in this order!! STR_NETWORK_ERROR_CLIENT_GENERAL :일반 오류 @@ -2210,6 +2203,7 @@ STR_NETWORK_ERROR_CLIENT_TIMEOUT_PASSWORD :제 시간에 STR_NETWORK_ERROR_CLIENT_TIMEOUT_COMPUTER :반응 시간 초과 STR_NETWORK_ERROR_CLIENT_TIMEOUT_MAP :지도 다운로드 시간 초과 STR_NETWORK_ERROR_CLIENT_TIMEOUT_JOIN :지도 생성 / 입장 시간 초과 +STR_NETWORK_ERROR_CLIENT_INVALID_CLIENT_NAME :유효하지 않은 클라이언트 이름 ############ End of leave-in-this-order STR_NETWORK_ERROR_CLIENT_GUI_LOST_CONNECTION_CAPTION :{WHITE}가능한 연결 손실 diff --git a/src/lang/latin.txt b/src/lang/latin.txt index a6eeb3cca2..4d7f6a2980 100644 --- a/src/lang/latin.txt +++ b/src/lang/latin.txt @@ -2088,6 +2088,7 @@ STR_FACE_TIE :Focale: STR_FACE_EARRING :Inauris: STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}Mutare focale vel inaurem + # Network server list STR_NETWORK_SERVER_LIST_CAPTION :{WHITE}Modus Plurium Lusorum STR_NETWORK_SERVER_LIST_PLAYER_NAME :{BLACK}Nomen lusoris: @@ -2146,10 +2147,6 @@ STR_NETWORK_START_SERVER_NEW_GAME_NAME_TOOLTIP :{BLACK}Hoc ludi STR_NETWORK_START_SERVER_SET_PASSWORD :{BLACK}Elige tesseram STR_NETWORK_START_SERVER_PASSWORD_TOOLTIP :{BLACK}Custodire tessera ludum tuum, si non vis publicos iungere -STR_NETWORK_START_SERVER_ADVERTISED_LABEL :{BLACK}Ostensum -STR_NETWORK_START_SERVER_ADVERTISED_TOOLTIP :{BLACK}Eligere servatrum ostensum (interretis) aut non ostensum (LAN) -STR_NETWORK_START_SERVER_UNADVERTISED :Non -STR_NETWORK_START_SERVER_ADVERTISED :Ita STR_NETWORK_START_SERVER_CLIENTS_SELECT :{BLACK}{NUM} clien{P s tes} STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS :{BLACK}Clientes maximi: STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS_TOOLTIP :{BLACK}Eligere numerum maximum clientorum. Necesse non est omnes loci pleni esse @@ -2211,19 +2208,13 @@ STR_NETWORK_CONNECTION_DISCONNECT :{BLACK}Disiunge STR_NETWORK_NEED_GAME_PASSWORD_CAPTION :{WHITE}Servatrum tutum est. Tesseram inscribe STR_NETWORK_NEED_COMPANY_PASSWORD_CAPTION :{WHITE}Societas tuta est. Tesseram inscribe -STR_NETWORK_COMPANY_LIST_CLIENT_LIST_CAPTION :{WHITE}Index clientum # Network company list added strings STR_NETWORK_COMPANY_LIST_CLIENT_LIST :Index clientum -STR_NETWORK_COMPANY_LIST_SPECTATE :Spectare -STR_NETWORK_COMPANY_LIST_NEW_COMPANY :Societas Nova # Network client list -STR_NETWORK_CLIENTLIST_KICK :Dimittere -STR_NETWORK_CLIENTLIST_BAN :Expellere -STR_NETWORK_CLIENTLIST_SPEAK_TO_ALL :Ad omnes loqui -STR_NETWORK_CLIENTLIST_SPEAK_TO_COMPANY :Ad societatem loqui -STR_NETWORK_CLIENTLIST_SPEAK_TO_CLIENT :Ad clientem loqui + + STR_NETWORK_SERVER :Servatrum STR_NETWORK_CLIENT :Cliens diff --git a/src/lang/latvian.txt b/src/lang/latvian.txt index f1a2888642..5fbf0616c0 100644 --- a/src/lang/latvian.txt +++ b/src/lang/latvian.txt @@ -1960,6 +1960,7 @@ STR_FACE_TIE :Kaklasaite: STR_FACE_EARRING :Auskars: STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}Mainīt kaklasaiti vai auskarus + # Network server list STR_NETWORK_SERVER_LIST_CAPTION :{WHITE}Vairākspēlētāju spēle STR_NETWORK_SERVER_LIST_PLAYER_NAME :{BLACK}Spēlētāja vārds: @@ -2022,10 +2023,6 @@ STR_NETWORK_START_SERVER_NEW_GAME_NAME_TOOLTIP :{BLACK}Spēles STR_NETWORK_START_SERVER_SET_PASSWORD :{BLACK}Uzstādīt paroli STR_NETWORK_START_SERVER_PASSWORD_TOOLTIP :{BLACK}Aizsargā jūsu spēli ar paroli, ja nevēlaties lai tā būtu publiski pieejama -STR_NETWORK_START_SERVER_ADVERTISED_LABEL :{BLACK}Izsludināt -STR_NETWORK_START_SERVER_ADVERTISED_TOOLTIP :{BLACK}Izvēlieties starp reklamēto (interneta) un nereklamēto (lokālā tīkla, LAN) spēli -STR_NETWORK_START_SERVER_UNADVERTISED :Nē -STR_NETWORK_START_SERVER_ADVERTISED :Jā STR_NETWORK_START_SERVER_CLIENTS_SELECT :{BLACK}{NUM} spēlētāj{P s i u} STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS :{BLACK}Maksimālais spēlētāju skaits: STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS_TOOLTIP :{BLACK}Izvēlēties maksimālo spēlētāju skaitu. Ne visiem slotiem ir jābūt aizpildītiem @@ -2087,19 +2084,13 @@ STR_NETWORK_CONNECTION_DISCONNECT :{BLACK}Atvienot STR_NETWORK_NEED_GAME_PASSWORD_CAPTION :{WHITE}Serveris ir aizsargāts. Ievadiet paroli STR_NETWORK_NEED_COMPANY_PASSWORD_CAPTION :{WHITE}Uzņēmums ir aizsargāts. Ievadiet paroli -STR_NETWORK_COMPANY_LIST_CLIENT_LIST_CAPTION :{WHITE}Klientu saraksts # Network company list added strings STR_NETWORK_COMPANY_LIST_CLIENT_LIST :Spēlētāju saraksts -STR_NETWORK_COMPANY_LIST_SPECTATE :Skatīt -STR_NETWORK_COMPANY_LIST_NEW_COMPANY :Jauns uzņēmums # Network client list -STR_NETWORK_CLIENTLIST_KICK :Izmest -STR_NETWORK_CLIENTLIST_BAN :Aizliegt -STR_NETWORK_CLIENTLIST_SPEAK_TO_ALL :Runāt ar visiem -STR_NETWORK_CLIENTLIST_SPEAK_TO_COMPANY :Runāt ar uzņēmumu -STR_NETWORK_CLIENTLIST_SPEAK_TO_CLIENT :Privāts ziņojums + + STR_NETWORK_SERVER :Serveris STR_NETWORK_CLIENT :Spēlētājs diff --git a/src/lang/lithuanian.txt b/src/lang/lithuanian.txt index e5816348c2..b90ee4e996 100644 --- a/src/lang/lithuanian.txt +++ b/src/lang/lithuanian.txt @@ -2204,6 +2204,7 @@ STR_FACE_TIE :Kaklaraištis: STR_FACE_EARRING :Auskaras: STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}Pakeisti kaklaraištį arba auskarą + # Network server list STR_NETWORK_SERVER_LIST_CAPTION :{WHITE}Žaidimas tinkle STR_NETWORK_SERVER_LIST_PLAYER_NAME :{BLACK}Žaidėjo vardas: @@ -2266,10 +2267,6 @@ STR_NETWORK_START_SERVER_NEW_GAME_NAME_TOOLTIP :{BLACK}Žaidimo STR_NETWORK_START_SERVER_SET_PASSWORD :{BLACK}Nustatyti slaptažodi STR_NETWORK_START_SERVER_PASSWORD_TOOLTIP :{BLACK}Apsaugokite savo žaidimą slaptažodžiu, jei nenorite, kad jis būtų viešai prieinamas -STR_NETWORK_START_SERVER_ADVERTISED_LABEL :{BLACK}Reklamuoti -STR_NETWORK_START_SERVER_ADVERTISED_TOOLTIP :{BLACK}Pasirinkite tarp reklamuojamo (internetas) ir nereklamuojamo (Vietinis Tinklas, LAN) žaidimo -STR_NETWORK_START_SERVER_UNADVERTISED :Ne -STR_NETWORK_START_SERVER_ADVERTISED :Taip STR_NETWORK_START_SERVER_CLIENTS_SELECT :{BLACK}{NUM} klient{P as ai ų} STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS :{BLACK}Didžiausias klientų skaičius: STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS_TOOLTIP :{BLACK}Pasirinkite didžiausią klientų skaičių. Nebūtinai visos jungtys turi būti užpildytos @@ -2331,19 +2328,13 @@ STR_NETWORK_CONNECTION_DISCONNECT :{BLACK}Atsijung STR_NETWORK_NEED_GAME_PASSWORD_CAPTION :{WHITE}Serveris apsaugotas. Įvesk slaptažodį STR_NETWORK_NEED_COMPANY_PASSWORD_CAPTION :{WHITE}Kompanija apsaugota. Įvesk slaptažodį -STR_NETWORK_COMPANY_LIST_CLIENT_LIST_CAPTION :{WHITE}Žaidėjų sąrašas # Network company list added strings STR_NETWORK_COMPANY_LIST_CLIENT_LIST :Žaidėjų sąrašas -STR_NETWORK_COMPANY_LIST_SPECTATE :Stebėti -STR_NETWORK_COMPANY_LIST_NEW_COMPANY :Nauja įmonė # Network client list -STR_NETWORK_CLIENTLIST_KICK :Išmesti (Kick) -STR_NETWORK_CLIENTLIST_BAN :Užblokuoti -STR_NETWORK_CLIENTLIST_SPEAK_TO_ALL :Sakyti visiems -STR_NETWORK_CLIENTLIST_SPEAK_TO_COMPANY :Sakyti kompanijai -STR_NETWORK_CLIENTLIST_SPEAK_TO_CLIENT :Privati žinutė + + STR_NETWORK_SERVER :Serveris STR_NETWORK_CLIENT :Žaidėjas diff --git a/src/lang/luxembourgish.txt b/src/lang/luxembourgish.txt index a5c21ae4a0..1730014d4a 100644 --- a/src/lang/luxembourgish.txt +++ b/src/lang/luxembourgish.txt @@ -1985,6 +1985,7 @@ STR_FACE_TIE :Krawatt: STR_FACE_EARRING :Ouerréng: STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}Krawatt oder Ouerréng änneren + # Network server list STR_NETWORK_SERVER_LIST_CAPTION :{WHITE}Multiplayer STR_NETWORK_SERVER_LIST_PLAYER_NAME :{BLACK}Spillernumm: @@ -2047,10 +2048,6 @@ STR_NETWORK_START_SERVER_NEW_GAME_NAME_TOOLTIP :{BLACK}Den Numm STR_NETWORK_START_SERVER_SET_PASSWORD :{BLACK}Passwuert setzen STR_NETWORK_START_SERVER_PASSWORD_TOOLTIP :{BLACK}En Passwuert fir d'Spill setzen, dass et net Public accessibel ass -STR_NETWORK_START_SERVER_ADVERTISED_LABEL :{BLACK}Ugekënnegt -STR_NETWORK_START_SERVER_ADVERTISED_TOOLTIP :{BLACK}Wiel tëschent engem ugekënnegten (Internet) an net ugekënnegten (LAN) Spill -STR_NETWORK_START_SERVER_UNADVERTISED :Nee -STR_NETWORK_START_SERVER_ADVERTISED :Jo STR_NETWORK_START_SERVER_CLIENTS_SELECT :{BLACK}{NUM} Spiller STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS :{BLACK}Maximal Spiller: STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS_TOOLTIP :{BLACK}Maximal Unzuel vun de Clients. Et muss net all Slot gefëllt sinn. @@ -2112,19 +2109,13 @@ STR_NETWORK_CONNECTION_DISCONNECT :{BLACK}Verbindu STR_NETWORK_NEED_GAME_PASSWORD_CAPTION :{WHITE}Server ass geschützt. Passwuert aginn STR_NETWORK_NEED_COMPANY_PASSWORD_CAPTION :{WHITE}Firma ass geschützt. Passwuert aginn -STR_NETWORK_COMPANY_LIST_CLIENT_LIST_CAPTION :{WHITE}Spillerlëscht # Network company list added strings STR_NETWORK_COMPANY_LIST_CLIENT_LIST :Spillerlëscht -STR_NETWORK_COMPANY_LIST_SPECTATE :Nokucken -STR_NETWORK_COMPANY_LIST_NEW_COMPANY :Nei Firma # Network client list -STR_NETWORK_CLIENTLIST_KICK :Kicken -STR_NETWORK_CLIENTLIST_BAN :Bannen -STR_NETWORK_CLIENTLIST_SPEAK_TO_ALL :Mat alle schwetzen -STR_NETWORK_CLIENTLIST_SPEAK_TO_COMPANY :Mat der Firma schwetzen -STR_NETWORK_CLIENTLIST_SPEAK_TO_CLIENT :Privatmessage + + STR_NETWORK_SERVER :Server STR_NETWORK_CLIENT :Spiller diff --git a/src/lang/macedonian.txt b/src/lang/macedonian.txt index 064d0b8e91..844bfd0a36 100644 --- a/src/lang/macedonian.txt +++ b/src/lang/macedonian.txt @@ -960,6 +960,7 @@ STR_ABANDON_GAME_QUERY :{YELLOW}Дал # Face selection window + # Network server list STR_NETWORK_SERVER_LIST_GENERAL_ONLINE :{BLACK}{COMMA}/{COMMA} - {COMMA}/{COMMA} @@ -994,6 +995,8 @@ STR_NETWORK_SERVER_LIST_MAP_SIZE_SHORT :{BLACK}{COMMA}x # Network client list + + # Network set password # Network company info join/password diff --git a/src/lang/malay.txt b/src/lang/malay.txt index 76a99e9b06..1507365150 100644 --- a/src/lang/malay.txt +++ b/src/lang/malay.txt @@ -1631,6 +1631,7 @@ STR_FACE_TIE :Tali leher: STR_FACE_EARRING :Anting-anting: STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}Tukar tali leher atau anting-anting + # Network server list STR_NETWORK_SERVER_LIST_CAPTION :{WHITE}Pemain berbilang STR_NETWORK_SERVER_LIST_PLAYER_NAME :{BLACK}Nama pemain: @@ -1689,7 +1690,6 @@ STR_NETWORK_START_SERVER_NEW_GAME_NAME_TOOLTIP :{BLACK}Nama per STR_NETWORK_START_SERVER_SET_PASSWORD :{BLACK}Tetapkan kata laluan STR_NETWORK_START_SERVER_PASSWORD_TOOLTIP :{BLACK}Lindungi permainan anda dengan kata laluan jika anda tidak mahu ianya diakses awam -STR_NETWORK_START_SERVER_UNADVERTISED :Tidak STR_NETWORK_START_SERVER_CLIENTS_SELECT :{BLACK}{NUM} pelanggan STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS :{BLACK}Amaun maksimum pemain: STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS_TOOLTIP :{BLACK}Tentukan bilangan klien maks. Tidak perlu semua slot diisi @@ -1754,15 +1754,10 @@ STR_NETWORK_NEED_COMPANY_PASSWORD_CAPTION :{WHITE}Syarikat # Network company list added strings STR_NETWORK_COMPANY_LIST_CLIENT_LIST :Senarai klien -STR_NETWORK_COMPANY_LIST_SPECTATE :Saksi -STR_NETWORK_COMPANY_LIST_NEW_COMPANY :Syarikat baru # Network client list -STR_NETWORK_CLIENTLIST_KICK :Tendang -STR_NETWORK_CLIENTLIST_BAN :Larang -STR_NETWORK_CLIENTLIST_SPEAK_TO_ALL :Cakap kepada semua -STR_NETWORK_CLIENTLIST_SPEAK_TO_COMPANY :Cakap kepada syarikat -STR_NETWORK_CLIENTLIST_SPEAK_TO_CLIENT :Pesanan peribadi + + STR_NETWORK_SERVER :Pelayan STR_NETWORK_CLIENT :Klien diff --git a/src/lang/maltese.txt b/src/lang/maltese.txt index ea5b7518d8..58f13463c4 100644 --- a/src/lang/maltese.txt +++ b/src/lang/maltese.txt @@ -528,6 +528,7 @@ STR_CHEAT_CHANGE_DATE_QUERY_CAPT :{WHITE}Ibdel is # Face selection window + # Network server list STR_NETWORK_SERVER_LIST_GENERAL_ONLINE :{BLACK}{COMMA}/{COMMA} - {COMMA}/{COMMA} @@ -564,6 +565,8 @@ STR_NETWORK_CONNECTING_DOWNLOADING_2 :{BLACK}{BYTES} # Network client list + + # Network set password # Network company info join/password diff --git a/src/lang/marathi.txt b/src/lang/marathi.txt index e7ac24eac5..8f4c7fbac3 100644 --- a/src/lang/marathi.txt +++ b/src/lang/marathi.txt @@ -892,6 +892,7 @@ STR_FACE_LIPS_MOUSTACHE_TOOLTIP :{BLACK}ओठ STR_FACE_CHIN :हनुवटी: STR_FACE_CHIN_TOOLTIP :{BLACK}हनुवटी बदला + # Network server list STR_NETWORK_SERVER_LIST_GAME_NAME :{BLACK}नाव @@ -934,6 +935,8 @@ STR_NETWORK_CONNECTING_DOWNLOADING_2 :{BLACK}{BYTES} # Network client list + + # Network set password # Network company info join/password diff --git a/src/lang/norwegian_bokmal.txt b/src/lang/norwegian_bokmal.txt index 9e65d9502e..623c346ac1 100644 --- a/src/lang/norwegian_bokmal.txt +++ b/src/lang/norwegian_bokmal.txt @@ -995,6 +995,7 @@ STR_GAME_OPTIONS_AUTOSAVE_DROPDOWN_EVERY_12_MONTHS :Hver 12. måned STR_GAME_OPTIONS_LANGUAGE :{BLACK}Språk STR_GAME_OPTIONS_LANGUAGE_TOOLTIP :{BLACK}Velg språk som skal brukes +STR_GAME_OPTIONS_LANGUAGE_PERCENTAGE :{STRING} ({NUM}% fullført) STR_GAME_OPTIONS_FULLSCREEN :{BLACK}Fullskjerm STR_GAME_OPTIONS_FULLSCREEN_TOOLTIP :{BLACK}Kryss av denne knappen for å spille OpenTTD i fullskjermmodus @@ -1993,6 +1994,7 @@ STR_FACE_TIE :Slips: STR_FACE_EARRING :Ørering: STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}Endre slips eller ørering + # Network server list STR_NETWORK_SERVER_LIST_CAPTION :{WHITE}Flerspiller STR_NETWORK_SERVER_LIST_PLAYER_NAME :{BLACK}Spillernavn: @@ -2055,10 +2057,6 @@ STR_NETWORK_START_SERVER_NEW_GAME_NAME_TOOLTIP :{BLACK}Spillnav STR_NETWORK_START_SERVER_SET_PASSWORD :{BLACK}Sett passord STR_NETWORK_START_SERVER_PASSWORD_TOOLTIP :{BLACK}Beskytt ditt spill med et passord hvis du ikke vil at hvem som helst skal bli med på det -STR_NETWORK_START_SERVER_ADVERTISED_LABEL :{BLACK}Annonsert -STR_NETWORK_START_SERVER_ADVERTISED_TOOLTIP :{BLACK}Velg mellom et utlyst (internett) og et ikke utlyst (Lokalnettverk, LAN) spill -STR_NETWORK_START_SERVER_UNADVERTISED :Nei -STR_NETWORK_START_SERVER_ADVERTISED :Ja STR_NETWORK_START_SERVER_CLIENTS_SELECT :{BLACK}{NUM} klient{P "" er} STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS :{BLACK}Maks antall klienter: STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS.small :dra og slipp @@ -2121,19 +2119,13 @@ STR_NETWORK_CONNECTION_DISCONNECT :{BLACK}Koble fr STR_NETWORK_NEED_GAME_PASSWORD_CAPTION :{WHITE}Tjeneren er beskyttet. Skriv inn passord STR_NETWORK_NEED_COMPANY_PASSWORD_CAPTION :{WHITE}Firmaet er beskyttet. Skriv inn passord -STR_NETWORK_COMPANY_LIST_CLIENT_LIST_CAPTION :{WHITE}Klientliste # Network company list added strings STR_NETWORK_COMPANY_LIST_CLIENT_LIST :Liste over klienter -STR_NETWORK_COMPANY_LIST_SPECTATE :Vær tilskuer -STR_NETWORK_COMPANY_LIST_NEW_COMPANY :Nytt firma # Network client list -STR_NETWORK_CLIENTLIST_KICK :Kast ut -STR_NETWORK_CLIENTLIST_BAN :Bannlys -STR_NETWORK_CLIENTLIST_SPEAK_TO_ALL :Snakk til alle -STR_NETWORK_CLIENTLIST_SPEAK_TO_COMPANY :Snakk til firma -STR_NETWORK_CLIENTLIST_SPEAK_TO_CLIENT :Privat melding + + STR_NETWORK_SERVER :Tjener STR_NETWORK_CLIENT :Klient @@ -2178,6 +2170,7 @@ STR_NETWORK_ERROR_SERVER_START :{WHITE}Kunne ik STR_NETWORK_ERROR_CLIENT_START :{WHITE}Kunne ikke opprette forbindelse STR_NETWORK_ERROR_TIMEOUT :{WHITE}Tilkobling #{NUM} ble tidsavbrutt STR_NETWORK_ERROR_SERVER_ERROR :{WHITE}Forbindelsen ble brutt pga. en protokollfeil +STR_NETWORK_ERROR_BAD_PLAYER_NAME :{WHITE}Spillernavnet ditt er ikke angitt. Navnet kan angis øverst i flerspillervinduet STR_NETWORK_ERROR_WRONG_REVISION :{WHITE}Klientversjonen er ikke den samme som tjenerversjonen STR_NETWORK_ERROR_WRONG_PASSWORD :{WHITE}Feil passord STR_NETWORK_ERROR_SERVER_FULL :{WHITE}Tjeneren er full @@ -2190,6 +2183,7 @@ STR_NETWORK_ERROR_TIMEOUT_PASSWORD :{WHITE}Du brukt STR_NETWORK_ERROR_TIMEOUT_COMPUTER :{WHITE}Din datamaskin er ikke rask nok til å holde følge med tjeneren STR_NETWORK_ERROR_TIMEOUT_MAP :{WHITE}Din datamaskin brukte for lang tid på å laste ned kartet STR_NETWORK_ERROR_TIMEOUT_JOIN :{WHITE}Din datamaskin brukte for lang tid på å koble til tjeneren +STR_NETWORK_ERROR_INVALID_CLIENT_NAME :{WHITE}Spillernavnet ditt er ugyldig ############ Leave those lines in this order!! STR_NETWORK_ERROR_CLIENT_GENERAL :generell feil @@ -2212,6 +2206,7 @@ STR_NETWORK_ERROR_CLIENT_TIMEOUT_PASSWORD :mottok ikke pas STR_NETWORK_ERROR_CLIENT_TIMEOUT_COMPUTER :generelt tidsavbrudd STR_NETWORK_ERROR_CLIENT_TIMEOUT_MAP :nedlasting av kart tok for lang tid STR_NETWORK_ERROR_CLIENT_TIMEOUT_JOIN :behandling av kartet tok for lang tid +STR_NETWORK_ERROR_CLIENT_INVALID_CLIENT_NAME :ugyldig klientnavn ############ End of leave-in-this-order STR_NETWORK_ERROR_CLIENT_GUI_LOST_CONNECTION_CAPTION :{WHITE}Mulig tap av tilkobling diff --git a/src/lang/norwegian_nynorsk.txt b/src/lang/norwegian_nynorsk.txt index 9488472931..515ffbed97 100644 --- a/src/lang/norwegian_nynorsk.txt +++ b/src/lang/norwegian_nynorsk.txt @@ -1793,6 +1793,7 @@ STR_FACE_TIE :Slips: STR_FACE_EARRING :Øyrering: STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}Byt slips eller øyrering + # Network server list STR_NETWORK_SERVER_LIST_CAPTION :{WHITE}Fleirspelar STR_NETWORK_SERVER_LIST_PLAYER_NAME :{BLACK}Spelarnamn: @@ -1851,10 +1852,6 @@ STR_NETWORK_START_SERVER_NEW_GAME_NAME_TOOLTIP :{BLACK}Spelnamn STR_NETWORK_START_SERVER_SET_PASSWORD :{BLACK}Set passord STR_NETWORK_START_SERVER_PASSWORD_TOOLTIP :{BLACK}Beskytt spelet ditt med passord så ikkje kven som helst kan verte med på det -STR_NETWORK_START_SERVER_ADVERTISED_LABEL :{BLACK}Kunngjort -STR_NETWORK_START_SERVER_ADVERTISED_TOOLTIP :{BLACK}Vel mellom spel på internett (kunngjort) og spel på lokalt nett (ikkje kunngjort) -STR_NETWORK_START_SERVER_UNADVERTISED :Nei -STR_NETWORK_START_SERVER_ADVERTISED :Ja STR_NETWORK_START_SERVER_CLIENTS_SELECT :{BLACK}{NUM} klient{P "" er} STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS :{BLACK}Maks. antal tilletne klientar: STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS_TOOLTIP :{BLACK}Vel maks. antal klientar. Alle plassane treng ikkje å verte tekne @@ -1919,15 +1916,10 @@ STR_NETWORK_NEED_COMPANY_PASSWORD_CAPTION :{WHITE}Firmaet # Network company list added strings STR_NETWORK_COMPANY_LIST_CLIENT_LIST :Liste over klientar -STR_NETWORK_COMPANY_LIST_SPECTATE :Vær tilskodar -STR_NETWORK_COMPANY_LIST_NEW_COMPANY :Nytt firma # Network client list -STR_NETWORK_CLIENTLIST_KICK :Kast ut -STR_NETWORK_CLIENTLIST_BAN :Svartelist -STR_NETWORK_CLIENTLIST_SPEAK_TO_ALL :Snakk til alle -STR_NETWORK_CLIENTLIST_SPEAK_TO_COMPANY :Snakk til firma -STR_NETWORK_CLIENTLIST_SPEAK_TO_CLIENT :Privat melding + + STR_NETWORK_SERVER :Tenar STR_NETWORK_CLIENT :Klient diff --git a/src/lang/persian.txt b/src/lang/persian.txt index ac5c6ae40c..a6c6a03bcd 100644 --- a/src/lang/persian.txt +++ b/src/lang/persian.txt @@ -1591,6 +1591,7 @@ STR_FACE_TIE :کراوات: STR_FACE_EARRING :گوشواره: STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}تغییر کراوات یا گوشواره + # Network server list STR_NETWORK_SERVER_LIST_CAPTION :{WHITE}چندنفره STR_NETWORK_SERVER_LIST_PLAYER_NAME :{BLACK}نام بازیگر: @@ -1649,9 +1650,6 @@ STR_NETWORK_START_SERVER_NEW_GAME_NAME_TOOLTIP :{BLACK}بازی STR_NETWORK_START_SERVER_SET_PASSWORD :{BLACK}ثبت رمز STR_NETWORK_START_SERVER_PASSWORD_TOOLTIP :{BLACK}اگر بازی برای استفاده همگان نیست، بازی خود را با رمز محافظت کنید -STR_NETWORK_START_SERVER_ADVERTISED_LABEL :{BLACK}تبلیغ دار -STR_NETWORK_START_SERVER_UNADVERTISED :خیر -STR_NETWORK_START_SERVER_ADVERTISED :بله STR_NETWORK_START_SERVER_CLIENTS_SELECT :{BLACK}{NUM} سرویس گیرنده STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS :{BLACK}بیشترین تعداد سرویس گیرنده: STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS_TOOLTIP :{BLACK}بیشترین تعداد سرویس گیرنده را مشخص کنید.لازم نیست دقیقا به تعداد نفرات باشد @@ -1716,15 +1714,10 @@ STR_NETWORK_NEED_COMPANY_PASSWORD_CAPTION :{WHITE}شرکت # Network company list added strings STR_NETWORK_COMPANY_LIST_CLIENT_LIST :{WHITE}لیست سرویس گیرنده ها -STR_NETWORK_COMPANY_LIST_SPECTATE :{WHITE}تماشا -STR_NETWORK_COMPANY_LIST_NEW_COMPANY :{WHITE}شرکت جدید # Network client list -STR_NETWORK_CLIENTLIST_KICK :اخراج -STR_NETWORK_CLIENTLIST_BAN :قدغن کردن -STR_NETWORK_CLIENTLIST_SPEAK_TO_ALL :گفتگو با همه -STR_NETWORK_CLIENTLIST_SPEAK_TO_COMPANY :گفتگو با شرکت -STR_NETWORK_CLIENTLIST_SPEAK_TO_CLIENT :پیام خصوصی + + STR_NETWORK_SERVER :سرویس دهنده STR_NETWORK_CLIENT :سرویس گیرنده diff --git a/src/lang/polish.txt b/src/lang/polish.txt index d2d888b189..d6af6dc64d 100644 --- a/src/lang/polish.txt +++ b/src/lang/polish.txt @@ -2371,6 +2371,7 @@ STR_FACE_TIE :Krawat: STR_FACE_EARRING :Kolczyk: STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}Zmień krawat lub kolczyk + # Network server list STR_NETWORK_SERVER_LIST_CAPTION :{WHITE}Gra wieloosobowa STR_NETWORK_SERVER_LIST_PLAYER_NAME :{BLACK}Nazwa gracza: @@ -2433,10 +2434,6 @@ STR_NETWORK_START_SERVER_NEW_GAME_NAME_TOOLTIP :{BLACK}Nazwa gr STR_NETWORK_START_SERVER_SET_PASSWORD :{BLACK}Ustaw hasło STR_NETWORK_START_SERVER_PASSWORD_TOOLTIP :{BLACK}Zabezpiecz grę hasłem jeśli nie chcesz, by była publicznie dostępna -STR_NETWORK_START_SERVER_ADVERTISED_LABEL :{BLACK}Publiczny -STR_NETWORK_START_SERVER_ADVERTISED_TOOLTIP :{BLACK}Wybierz między rozgrywkami reklamowanymi (internetowymi) a niereklamowanymi (w sieci lokalnej LAN) -STR_NETWORK_START_SERVER_UNADVERTISED :Nie -STR_NETWORK_START_SERVER_ADVERTISED :Tak STR_NETWORK_START_SERVER_CLIENTS_SELECT :{BLACK}{NUM} klient{P "" ów ów} STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS :{BLACK}Dopuszczalna liczba klientów: STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS_TOOLTIP :{BLACK}Ustaw maksymalną liczbę klientów. Nie wszystkie pola muszą być wypełnione. @@ -2498,19 +2495,13 @@ STR_NETWORK_CONNECTION_DISCONNECT :{BLACK}Rozłąc STR_NETWORK_NEED_GAME_PASSWORD_CAPTION :{WHITE}Serwer jest chroniony. Wprowadź hasło STR_NETWORK_NEED_COMPANY_PASSWORD_CAPTION :{WHITE}Firma jest chroniona. Wprowadź hasło -STR_NETWORK_COMPANY_LIST_CLIENT_LIST_CAPTION :{WHITE}Lista klientów # Network company list added strings STR_NETWORK_COMPANY_LIST_CLIENT_LIST :Lista klientów -STR_NETWORK_COMPANY_LIST_SPECTATE :Obserwuj -STR_NETWORK_COMPANY_LIST_NEW_COMPANY :Nowa firma # Network client list -STR_NETWORK_CLIENTLIST_KICK :Wyrzuć -STR_NETWORK_CLIENTLIST_BAN :Banuj -STR_NETWORK_CLIENTLIST_SPEAK_TO_ALL :Mów do wszystkich -STR_NETWORK_CLIENTLIST_SPEAK_TO_COMPANY :Mów do firmy -STR_NETWORK_CLIENTLIST_SPEAK_TO_CLIENT :Prywatna wiadomość + + STR_NETWORK_SERVER :Serwer STR_NETWORK_CLIENT :Klient diff --git a/src/lang/portuguese.txt b/src/lang/portuguese.txt index 36742f76fe..fadf6b9f90 100644 --- a/src/lang/portuguese.txt +++ b/src/lang/portuguese.txt @@ -1991,6 +1991,7 @@ STR_FACE_TIE :Gravata: STR_FACE_EARRING :Brinco: STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}Mudar gravata ou brinco + # Network server list STR_NETWORK_SERVER_LIST_CAPTION :{WHITE}Multi-jogador STR_NETWORK_SERVER_LIST_PLAYER_NAME :{BLACK}Nome do jogador: @@ -2053,10 +2054,6 @@ STR_NETWORK_START_SERVER_NEW_GAME_NAME_TOOLTIP :{BLACK}O nome d STR_NETWORK_START_SERVER_SET_PASSWORD :{BLACK}Definir palavra-chave STR_NETWORK_START_SERVER_PASSWORD_TOOLTIP :{BLACK}Proteja o jogo com uma senha se não desejar que pessoas indesejadas se juntem -STR_NETWORK_START_SERVER_ADVERTISED_LABEL :{BLACK}Anunciado -STR_NETWORK_START_SERVER_ADVERTISED_TOOLTIP :{BLACK}Escolher entre jogo público (internet) e privado (Área de Rede Local, LAN) -STR_NETWORK_START_SERVER_UNADVERTISED :Não -STR_NETWORK_START_SERVER_ADVERTISED :Sim STR_NETWORK_START_SERVER_CLIENTS_SELECT :{BLACK}{NUM} cliente{P "" s} STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS :{BLACK}Máximo de clientes: STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS_TOOLTIP :{BLACK}Escolha o número máximo de clientes. Não necessitam estar todos presentes. @@ -2118,19 +2115,13 @@ STR_NETWORK_CONNECTION_DISCONNECT :{BLACK}Desligar STR_NETWORK_NEED_GAME_PASSWORD_CAPTION :{WHITE}Servidor protegido. Introduza palavra-chave STR_NETWORK_NEED_COMPANY_PASSWORD_CAPTION :{WHITE}Empresa protegida. Introduza palavra-chave -STR_NETWORK_COMPANY_LIST_CLIENT_LIST_CAPTION :{WHITE}Lista de clientes # Network company list added strings STR_NETWORK_COMPANY_LIST_CLIENT_LIST :Lista de clientes -STR_NETWORK_COMPANY_LIST_SPECTATE :Assistir -STR_NETWORK_COMPANY_LIST_NEW_COMPANY :Nova empresa # Network client list -STR_NETWORK_CLIENTLIST_KICK :Expulsar -STR_NETWORK_CLIENTLIST_BAN :Banir -STR_NETWORK_CLIENTLIST_SPEAK_TO_ALL :Falar com todos -STR_NETWORK_CLIENTLIST_SPEAK_TO_COMPANY :Falar com a empresa -STR_NETWORK_CLIENTLIST_SPEAK_TO_CLIENT :Mensagem privada + + STR_NETWORK_SERVER :Servidor STR_NETWORK_CLIENT :Cliente diff --git a/src/lang/romanian.txt b/src/lang/romanian.txt index 847b41debf..60cb7a616e 100644 --- a/src/lang/romanian.txt +++ b/src/lang/romanian.txt @@ -1910,6 +1910,7 @@ STR_FACE_TIE :Cravată: STR_FACE_EARRING :Cercei: STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}Schimbă cravata sau cerceii + # Network server list STR_NETWORK_SERVER_LIST_CAPTION :{WHITE}Multiplayer STR_NETWORK_SERVER_LIST_PLAYER_NAME :{BLACK}Numele jucătorului: @@ -1972,10 +1973,6 @@ STR_NETWORK_START_SERVER_NEW_GAME_NAME_TOOLTIP :{BLACK}Numele j STR_NETWORK_START_SERVER_SET_PASSWORD :{BLACK}Pune parolă STR_NETWORK_START_SERVER_PASSWORD_TOOLTIP :{BLACK}Protejează-ţi jocul cu o parolă dacă nu vrei să intre jucători neautorizaţi -STR_NETWORK_START_SERVER_ADVERTISED_LABEL :{BLACK}Publicat -STR_NETWORK_START_SERVER_ADVERTISED_TOOLTIP :{BLACK}Alege între un joc publicat (prin Internet) și unul privat (reț) game -STR_NETWORK_START_SERVER_UNADVERTISED :Nu -STR_NETWORK_START_SERVER_ADVERTISED :Da STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS :{BLACK}Număr maxim de clienţi: STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS_TOOLTIP :{BLACK}Alege un număr maxim de clienţi. Nu trebuie ocupate toate locurile. STR_NETWORK_START_SERVER_NUMBER_OF_COMPANIES :{BLACK}Companii maxim: @@ -2036,15 +2033,10 @@ STR_NETWORK_NEED_COMPANY_PASSWORD_CAPTION :{WHITE}Companie # Network company list added strings STR_NETWORK_COMPANY_LIST_CLIENT_LIST :Lista de clienţi -STR_NETWORK_COMPANY_LIST_SPECTATE :Observă -STR_NETWORK_COMPANY_LIST_NEW_COMPANY :Companie nouă # Network client list -STR_NETWORK_CLIENTLIST_KICK :Dă afară -STR_NETWORK_CLIENTLIST_BAN :Interzice acces -STR_NETWORK_CLIENTLIST_SPEAK_TO_ALL :Vorbeşte către toţi -STR_NETWORK_CLIENTLIST_SPEAK_TO_COMPANY :Vorbeşte către companie -STR_NETWORK_CLIENTLIST_SPEAK_TO_CLIENT :Mesaj privat + + STR_NETWORK_SERVER :Server STR_NETWORK_CLIENT :Client diff --git a/src/lang/russian.txt b/src/lang/russian.txt index df060c6a7c..de8c4f84cf 100644 --- a/src/lang/russian.txt +++ b/src/lang/russian.txt @@ -2142,6 +2142,7 @@ STR_FACE_TIE :Галстук: STR_FACE_EARRING :Серьга: STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}Изменить галстук или серьгу + # Network server list STR_NETWORK_SERVER_LIST_CAPTION :{WHITE}Сетевая игра STR_NETWORK_SERVER_LIST_PLAYER_NAME :{BLACK}Имя игрока: @@ -2204,10 +2205,6 @@ STR_NETWORK_START_SERVER_NEW_GAME_NAME_TOOLTIP :{BLACK}Назв STR_NETWORK_START_SERVER_SET_PASSWORD :{BLACK}Установить пароль STR_NETWORK_START_SERVER_PASSWORD_TOOLTIP :{BLACK}Защитите вашу игру паролем, если не хотите, чтобы к ней могли подключиться посторонние. -STR_NETWORK_START_SERVER_ADVERTISED_LABEL :{BLACK}Интернет -STR_NETWORK_START_SERVER_ADVERTISED_TOOLTIP :{BLACK}Выберите между игрой через Интернет, либо в локальной сети -STR_NETWORK_START_SERVER_UNADVERTISED :Нет -STR_NETWORK_START_SERVER_ADVERTISED :Да STR_NETWORK_START_SERVER_CLIENTS_SELECT :{BLACK}{NUM} клиент{P "" а ов} STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS :{BLACK}Макс. количество клиентов: STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS_TOOLTIP :{BLACK}Выбор максимального числа клиентов. Не все места должны быть заняты @@ -2269,19 +2266,13 @@ STR_NETWORK_CONNECTION_DISCONNECT :{BLACK}Откл STR_NETWORK_NEED_GAME_PASSWORD_CAPTION :{WHITE}Сервер защищён. Введите пароль. STR_NETWORK_NEED_COMPANY_PASSWORD_CAPTION :{WHITE}Компания защищена. Введите пароль. -STR_NETWORK_COMPANY_LIST_CLIENT_LIST_CAPTION :{WHITE}Список клиентов # Network company list added strings STR_NETWORK_COMPANY_LIST_CLIENT_LIST :Список клиентов -STR_NETWORK_COMPANY_LIST_SPECTATE :Наблюдать -STR_NETWORK_COMPANY_LIST_NEW_COMPANY :Новая компания # Network client list -STR_NETWORK_CLIENTLIST_KICK :Отключить -STR_NETWORK_CLIENTLIST_BAN :Бан -STR_NETWORK_CLIENTLIST_SPEAK_TO_ALL :Сказать всем -STR_NETWORK_CLIENTLIST_SPEAK_TO_COMPANY :Говорить с компанией -STR_NETWORK_CLIENTLIST_SPEAK_TO_CLIENT :Личное сообщение + + STR_NETWORK_SERVER :Сервер STR_NETWORK_CLIENT :Клиент @@ -2326,6 +2317,7 @@ STR_NETWORK_ERROR_SERVER_START :{WHITE}Не у STR_NETWORK_ERROR_CLIENT_START :{WHITE}Не удалось соединиться STR_NETWORK_ERROR_TIMEOUT :{WHITE}Истекло время ожидания соединения #{NUM} STR_NETWORK_ERROR_SERVER_ERROR :{WHITE}Произошла ошибка протокола, и соединение было закрыто +STR_NETWORK_ERROR_BAD_PLAYER_NAME :{WHITE}Не указано имя игрока. Его можно ввести в верхней части окна «Сетевая игра». STR_NETWORK_ERROR_WRONG_REVISION :{WHITE}Версия этого клиента не совместима с версией сервера STR_NETWORK_ERROR_WRONG_PASSWORD :{WHITE}Неверный пароль STR_NETWORK_ERROR_SERVER_FULL :{WHITE}Сервер переполнен @@ -2338,6 +2330,7 @@ STR_NETWORK_ERROR_TIMEOUT_PASSWORD :{WHITE}Вы н STR_NETWORK_ERROR_TIMEOUT_COMPUTER :{WHITE}Ваш компьютер работает слишком медленно и не поспевает за сервером STR_NETWORK_ERROR_TIMEOUT_MAP :{WHITE}Ваш компьютер тратит много времени на загрузку карты STR_NETWORK_ERROR_TIMEOUT_JOIN :{WHITE}Ваш компьютер тратит много времени на подключение к серверу +STR_NETWORK_ERROR_INVALID_CLIENT_NAME :{WHITE}Неверно указано имя игрока ############ Leave those lines in this order!! STR_NETWORK_ERROR_CLIENT_GENERAL :общая ошибка @@ -2360,6 +2353,7 @@ STR_NETWORK_ERROR_CLIENT_TIMEOUT_PASSWORD :пароль н STR_NETWORK_ERROR_CLIENT_TIMEOUT_COMPUTER :время вышло STR_NETWORK_ERROR_CLIENT_TIMEOUT_MAP :загрузка карты заняла слишком много времени STR_NETWORK_ERROR_CLIENT_TIMEOUT_JOIN :обработка карты заняла слишком много времени +STR_NETWORK_ERROR_CLIENT_INVALID_CLIENT_NAME :имя клиента некорректно ############ End of leave-in-this-order STR_NETWORK_ERROR_CLIENT_GUI_LOST_CONNECTION_CAPTION :{WHITE}Подключение утеряно @@ -3269,7 +3263,7 @@ STR_NEWGRF_BROKEN :{WHITE}Файл STR_NEWGRF_BROKEN_POWERED_WAGON :{WHITE}Меняется состояние локомотива «{1:ENGINE}», находящегося вне депо STR_NEWGRF_BROKEN_VEHICLE_LENGTH :{WHITE}Меняется длина транспорта «{1:ENGINE}», находящегося вне депо STR_NEWGRF_BROKEN_CAPACITY :{WHITE}Он изменил ёмкость ТС «{1:ENGINE}» за пределами депо или без задания на переоборудование -STR_BROKEN_VEHICLE_LENGTH :{WHITE}Поезд «{VEHICLE}», принадлежащий «{COMPANY}», имеет неправильную длину. Вероятно, это вызвано проблемами в файле новой графики (NewGRF). Игра может рассинхронизироваться или вылететь. +STR_BROKEN_VEHICLE_LENGTH :{WHITE}Поезд «{VEHICLE}», принадлежащий «{COMPANY}», имеет неправильную длину. Вероятно, это вызвано ошибками в одном из модулей NewGRF. Игра может рассинхронизироваться или вылететь. STR_NEWGRF_BUGGY :{WHITE}NewGRF «{0:STRING}» предоставляет неверную информацию. STR_NEWGRF_BUGGY_ARTICULATED_CARGO :{WHITE}Информация о вместимости/переоборудовании для локомотива «{1:ENGINE}» после постройки отличается от сведений в списке покупки, что может помешать функции автообновления/автозамены корректно произвести переоборудование. diff --git a/src/lang/serbian.txt b/src/lang/serbian.txt index 78fb879283..5079e879b7 100644 --- a/src/lang/serbian.txt +++ b/src/lang/serbian.txt @@ -2180,6 +2180,7 @@ STR_FACE_TIE :Kravata: STR_FACE_EARRING :Minđuše: STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}Promena kravate ili minđuša + # Network server list STR_NETWORK_SERVER_LIST_CAPTION :{WHITE}Mrežna partija STR_NETWORK_SERVER_LIST_PLAYER_NAME :{BLACK}Ime igrača: @@ -2242,10 +2243,6 @@ STR_NETWORK_START_SERVER_NEW_GAME_NAME_TOOLTIP :{BLACK}Naziv pa STR_NETWORK_START_SERVER_SET_PASSWORD :{BLACK}Postavi lozinku STR_NETWORK_START_SERVER_PASSWORD_TOOLTIP :{BLACK}Ukoliko ne želite da bude javno dostupna zaštitite Vašu partiju lozinkom -STR_NETWORK_START_SERVER_ADVERTISED_LABEL :{BLACK} Sa reklamama -STR_NETWORK_START_SERVER_ADVERTISED_TOOLTIP :{BLACK}Odaberite između igre s oglasima (internet) i bez oglasa (Local Area Network, LAN) -STR_NETWORK_START_SERVER_UNADVERTISED :Ne -STR_NETWORK_START_SERVER_ADVERTISED :Da STR_NETWORK_START_SERVER_CLIENTS_SELECT :{BLACK}{NUM} igrač{P "" a a} STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS :{BLACK}Najviše igrača: STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS_TOOLTIP :{BLACK}Odaberite koliko najviše može da ima igrača. Nemoraju sva mesta da budu popunjena @@ -2307,19 +2304,13 @@ STR_NETWORK_CONNECTION_DISCONNECT :{BLACK}Isključ STR_NETWORK_NEED_GAME_PASSWORD_CAPTION :{WHITE}Server je zaštićen. Unesite lozinku STR_NETWORK_NEED_COMPANY_PASSWORD_CAPTION :{WHITE}Preduzeće je zaštićeno. Unesite lozinku -STR_NETWORK_COMPANY_LIST_CLIENT_LIST_CAPTION :{WHITE}Spisak klijenata # Network company list added strings STR_NETWORK_COMPANY_LIST_CLIENT_LIST :Spisak klijenata -STR_NETWORK_COMPANY_LIST_SPECTATE :Praćenje -STR_NETWORK_COMPANY_LIST_NEW_COMPANY :Novo preduzeće # Network client list -STR_NETWORK_CLIENTLIST_KICK :Izbaci -STR_NETWORK_CLIENTLIST_BAN :Zabrani pristup -STR_NETWORK_CLIENTLIST_SPEAK_TO_ALL :Razgovaraj sa svima -STR_NETWORK_CLIENTLIST_SPEAK_TO_COMPANY :Razgovaraj sa ekipom -STR_NETWORK_CLIENTLIST_SPEAK_TO_CLIENT :Privatna poruka + + STR_NETWORK_SERVER :Server STR_NETWORK_CLIENT :Igrač diff --git a/src/lang/simplified_chinese.txt b/src/lang/simplified_chinese.txt index 8b568b51dd..993736cc8f 100644 --- a/src/lang/simplified_chinese.txt +++ b/src/lang/simplified_chinese.txt @@ -1990,6 +1990,7 @@ STR_FACE_TIE :领带 STR_FACE_EARRING :耳环 STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}选择领带或是耳环 + # Network server list STR_NETWORK_SERVER_LIST_CAPTION :{WHITE}联机游戏 STR_NETWORK_SERVER_LIST_PLAYER_NAME :{BLACK}玩家名称: @@ -2052,10 +2053,6 @@ STR_NETWORK_START_SERVER_NEW_GAME_NAME_TOOLTIP :{BLACK}游戏 STR_NETWORK_START_SERVER_SET_PASSWORD :{BLACK}设置密码: STR_NETWORK_START_SERVER_PASSWORD_TOOLTIP :{BLACK}如果不希望你的游戏被外人加入,请设置一个密码 -STR_NETWORK_START_SERVER_ADVERTISED_LABEL :{BLACK}发布到互联网 -STR_NETWORK_START_SERVER_ADVERTISED_TOOLTIP :{BLACK}选择是否将游戏发布到官方服务器列表。发布的话,可以让互联网上的玩家找到你的服务器并加入游戏,否则进行局域网内的游戏。 -STR_NETWORK_START_SERVER_UNADVERTISED :否 -STR_NETWORK_START_SERVER_ADVERTISED :是 STR_NETWORK_START_SERVER_CLIENTS_SELECT :{BLACK}{NUM} 客户端 STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS :{BLACK}玩家数目: STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS_TOOLTIP :{BLACK}选择可以参加的玩家上限(达不到此数量仍然可以开始游戏) @@ -2117,19 +2114,13 @@ STR_NETWORK_CONNECTION_DISCONNECT :{BLACK}断开 STR_NETWORK_NEED_GAME_PASSWORD_CAPTION :{WHITE}服务器需要密码: STR_NETWORK_NEED_COMPANY_PASSWORD_CAPTION :{WHITE}公司需要密码: -STR_NETWORK_COMPANY_LIST_CLIENT_LIST_CAPTION :{WHITE}客户端列表 # Network company list added strings STR_NETWORK_COMPANY_LIST_CLIENT_LIST :客户端列表 -STR_NETWORK_COMPANY_LIST_SPECTATE :旁观 -STR_NETWORK_COMPANY_LIST_NEW_COMPANY :新公司 # Network client list -STR_NETWORK_CLIENTLIST_KICK :踢出 -STR_NETWORK_CLIENTLIST_BAN :禁止 -STR_NETWORK_CLIENTLIST_SPEAK_TO_ALL :向全体人广播 -STR_NETWORK_CLIENTLIST_SPEAK_TO_COMPANY :向本公司广播 -STR_NETWORK_CLIENTLIST_SPEAK_TO_CLIENT :私聊 + + STR_NETWORK_SERVER :服务器 STR_NETWORK_CLIENT :客户端 diff --git a/src/lang/slovak.txt b/src/lang/slovak.txt index c3e9dee60e..563fcaa41d 100644 --- a/src/lang/slovak.txt +++ b/src/lang/slovak.txt @@ -2053,6 +2053,7 @@ STR_FACE_TIE :Kravata: STR_FACE_EARRING :Náušnica: STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}Zmeniť kravatu alebo náušnicu + # Network server list STR_NETWORK_SERVER_LIST_CAPTION :{WHITE}Hra pre viac hráčov STR_NETWORK_SERVER_LIST_PLAYER_NAME :{BLACK}Meno hráča: @@ -2115,10 +2116,6 @@ STR_NETWORK_START_SERVER_NEW_GAME_NAME_TOOLTIP :{BLACK}Názov h STR_NETWORK_START_SERVER_SET_PASSWORD :{BLACK}Nastaviť heslo STR_NETWORK_START_SERVER_PASSWORD_TOOLTIP :{BLACK}Zabezpeč hru heslom, ak nechceš povoliť verejný prístup -STR_NETWORK_START_SERVER_ADVERTISED_LABEL :{BLACK}Zverejnené -STR_NETWORK_START_SERVER_ADVERTISED_TOOLTIP :{BLACK}Vyberte medzi zverejnenou (internet) alebo nezverejnenou (lokálna sieť, LAN) hrou -STR_NETWORK_START_SERVER_UNADVERTISED :Nie -STR_NETWORK_START_SERVER_ADVERTISED :Áno STR_NETWORK_START_SERVER_CLIENTS_SELECT :{BLACK}{NUM} klient{P "" i ov} STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS :{BLACK}Maximálny počet klientov: STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS_TOOLTIP :{BLACK}Zvoľ maximálny počet klientov. Môže sa ich pripojiť aj menej. @@ -2180,19 +2177,13 @@ STR_NETWORK_CONNECTION_DISCONNECT :{BLACK}Odpojiť STR_NETWORK_NEED_GAME_PASSWORD_CAPTION :{WHITE}Server je chránený. Zadaj heslo STR_NETWORK_NEED_COMPANY_PASSWORD_CAPTION :{WHITE}Spoločnosť je chránená. Zadaj heslo -STR_NETWORK_COMPANY_LIST_CLIENT_LIST_CAPTION :{WHITE}Zoznam klientov # Network company list added strings STR_NETWORK_COMPANY_LIST_CLIENT_LIST :Zoznam klientov -STR_NETWORK_COMPANY_LIST_SPECTATE :Pozorovať -STR_NETWORK_COMPANY_LIST_NEW_COMPANY :Nová spoločnosť # Network client list -STR_NETWORK_CLIENTLIST_KICK :Vyhodit -STR_NETWORK_CLIENTLIST_BAN :Ban -STR_NETWORK_CLIENTLIST_SPEAK_TO_ALL :Napísať správu všetkým -STR_NETWORK_CLIENTLIST_SPEAK_TO_COMPANY :Napísať spoločnosti správu -STR_NETWORK_CLIENTLIST_SPEAK_TO_CLIENT :Súkromná správa + + STR_NETWORK_SERVER :Server STR_NETWORK_CLIENT :Klient @@ -4436,14 +4427,14 @@ STR_ERROR_CAN_T_CHANGE_COMPANY_NAME :{WHITE}Názov s STR_ERROR_CAN_T_CHANGE_PRESIDENT :{WHITE}Meno prezidenta sa nedá zmeniť... STR_ERROR_MAXIMUM_PERMITTED_LOAN :{WHITE}... úverovy limit je {CURRENCY_LONG} -STR_ERROR_CAN_T_BORROW_ANY_MORE_MONEY :{WHITE}Nemôžeš si požičať viac peňazí... +STR_ERROR_CAN_T_BORROW_ANY_MORE_MONEY :{WHITE}Nemôžete si požičať viac peňazí... STR_ERROR_LOAN_ALREADY_REPAYED :{WHITE}... úver už bol splatený STR_ERROR_CURRENCY_REQUIRED :{WHITE}... {CURRENCY_LONG} potrebuješ STR_ERROR_CAN_T_REPAY_LOAN :{WHITE}Úver sa nedá splatiť... STR_ERROR_INSUFFICIENT_FUNDS :{WHITE}Nie je možné presunúť peniaze, ktoré sú požičané z banky... STR_ERROR_CAN_T_GIVE_MONEY :{WHITE}Nie je možné presunúť peniaze tejto spoločnosti... STR_ERROR_CAN_T_BUY_COMPANY :{WHITE}Spoločnosť nie je možné kúpiť ... -STR_ERROR_CAN_T_BUILD_COMPANY_HEADQUARTERS :{WHITE}Nemôžeš postaviť sídlo spoločnosti... +STR_ERROR_CAN_T_BUILD_COMPANY_HEADQUARTERS :{WHITE}Nemôžete tu postaviť sídlo spoločnosti... STR_ERROR_CAN_T_BUY_25_SHARE_IN_THIS :{WHITE}Nemôžeš kúpiť 25% podiel v tejto spoločnosti... STR_ERROR_CAN_T_SELL_25_SHARE_IN :{WHITE}25% podiel v tejto spoločnosti sa nedá predať ... STR_ERROR_PROTECTED :{WHITE}S akciami tejto spoločnosti nie je zatiaľ možné obchodovať... @@ -4466,10 +4457,10 @@ STR_ERROR_STATUE_NO_SUITABLE_PLACE :{WHITE}... v ce STR_ERROR_TOO_MANY_INDUSTRIES :{WHITE}... príliš veľa priemyslu STR_ERROR_CAN_T_GENERATE_INDUSTRIES :{WHITE}Nemožno vygenerovať priemysel... STR_ERROR_CAN_T_BUILD_HERE :{WHITE}{STRING} sa tu nedá postaviť... -STR_ERROR_CAN_T_CONSTRUCT_THIS_INDUSTRY :{WHITE}Nemôžeš tu postaviť tento typ priemyslu... +STR_ERROR_CAN_T_CONSTRUCT_THIS_INDUSTRY :{WHITE}Nemôžete tu postaviť tento typ priemyslu... STR_ERROR_INDUSTRY_TOO_CLOSE :{WHITE}... príliš blízko iného priemyslu STR_ERROR_MUST_FOUND_TOWN_FIRST :{WHITE}... najskôr je potrebné založiť mesto -STR_ERROR_ONLY_ONE_ALLOWED_PER_TOWN :{WHITE}... povolené len jedno pre 1mesto +STR_ERROR_ONLY_ONE_ALLOWED_PER_TOWN :{WHITE}... povolené len jedno pre každé mesto STR_ERROR_CAN_ONLY_BE_BUILT_IN_TOWNS_WITH_POPULATION_OF_1200 :{WHITE}... môže byť postavené v mestách z populáciou väčšou ako 1200 STR_ERROR_CAN_ONLY_BE_BUILT_IN_RAINFOREST :{WHITE}... môže byť postavené len v dažďovom pralese STR_ERROR_CAN_ONLY_BE_BUILT_IN_DESERT :{WHITE}... môže byť postavené len v púšti @@ -4485,13 +4476,13 @@ STR_ERROR_NO_SUITABLE_PLACES_FOR_INDUSTRIES :{WHITE}Nenašli STR_ERROR_NO_SUITABLE_PLACES_FOR_INDUSTRIES_EXPLANATION :{WHITE}Pre získanie lepšej mapy zmeňte parametre jej generovania # Station construction related errors -STR_ERROR_CAN_T_BUILD_RAILROAD_STATION :{WHITE}Nemôžeš tu postaviť železničnú stanicu... -STR_ERROR_CAN_T_BUILD_BUS_STATION :{WHITE}Nemôžeš tu postaviť autobusovú zastávku... +STR_ERROR_CAN_T_BUILD_RAILROAD_STATION :{WHITE}Nemôžete tu postaviť železničnú stanicu... +STR_ERROR_CAN_T_BUILD_BUS_STATION :{WHITE}Nemôžete tu postaviť autobusovú zastávku... STR_ERROR_CAN_T_BUILD_TRUCK_STATION :{WHITE}Nemôžeš tu postaviť vykládku... -STR_ERROR_CAN_T_BUILD_PASSENGER_TRAM_STATION :{WHITE}Nemôžeš postaviť električkovú osobnú stanicu... -STR_ERROR_CAN_T_BUILD_CARGO_TRAM_STATION :{WHITE}Nemôžeš postaviť električkovú nákladnú stanicu... -STR_ERROR_CAN_T_BUILD_DOCK_HERE :{WHITE}Nemôžeš tu postaviť prístav... -STR_ERROR_CAN_T_BUILD_AIRPORT_HERE :{WHITE}Nemôžeš tu postaviť letisko... +STR_ERROR_CAN_T_BUILD_PASSENGER_TRAM_STATION :{WHITE}Nemôžete tu postaviť električkovú zastávku... +STR_ERROR_CAN_T_BUILD_CARGO_TRAM_STATION :{WHITE}Nemôžete tu postaviť električkovú vykládku... +STR_ERROR_CAN_T_BUILD_DOCK_HERE :{WHITE}Nemôžete tu postaviť prístav... +STR_ERROR_CAN_T_BUILD_AIRPORT_HERE :{WHITE}Nemôžete tu postaviť letisko... STR_ERROR_ADJOINS_MORE_THAN_ONE_EXISTING :{WHITE}Susedí z viacerými stanicami/výkladkami STR_ERROR_STATION_TOO_SPREAD_OUT :{WHITE}... stanica je príliš veľká @@ -4539,8 +4530,8 @@ STR_ERROR_BUOY_IN_THE_WAY :{WHITE}... bój STR_ERROR_BUOY_IS_IN_USE :{WHITE}... bóju používa iná spoločnosť! # Depot related errors -STR_ERROR_CAN_T_BUILD_TRAIN_DEPOT :{WHITE}Nemôžeš tu postaviť vlakové depo... -STR_ERROR_CAN_T_BUILD_ROAD_DEPOT :{WHITE}Nemôžeš tu postaviť garáž... +STR_ERROR_CAN_T_BUILD_TRAIN_DEPOT :{WHITE}Nemôžete tu postaviť vlakové depo... +STR_ERROR_CAN_T_BUILD_ROAD_DEPOT :{WHITE}Nemôžete tu postaviť garáž... STR_ERROR_CAN_T_BUILD_TRAM_DEPOT :{WHITE}Nemôžeš tu postaviť električkové depo... STR_ERROR_CAN_T_BUILD_SHIP_DEPOT :{WHITE}Nemôžeš tu postaviť lodenicu... @@ -4580,7 +4571,7 @@ STR_ERROR_CROSSING_ON_ONEWAY_ROAD :{WHITE}Cesta je STR_ERROR_CROSSING_DISALLOWED_RAIL :{WHITE}Nie sú povolené priecestia pre tento typ železnice STR_ERROR_CROSSING_DISALLOWED_ROAD :{WHITE}Nie sú povolené priecestia pre tento typ cesty STR_ERROR_CAN_T_BUILD_SIGNALS_HERE :{WHITE}Nemôžeš tu umiestniť návestidlá... -STR_ERROR_CAN_T_BUILD_RAILROAD_TRACK :{WHITE}Nemôžeš tu stavať železničné koľaje... +STR_ERROR_CAN_T_BUILD_RAILROAD_TRACK :{WHITE}Nemôžete tu postaviť železničnú trať... STR_ERROR_CAN_T_REMOVE_RAILROAD_TRACK :{WHITE}Nemôžeš tu odstrániť železničné koľaje... STR_ERROR_CAN_T_REMOVE_SIGNALS_FROM :{WHITE}Nemôžeš tu odstrániť návestidlá... STR_ERROR_SIGNAL_CAN_T_CONVERT_SIGNALS_HERE :{WHITE}Tu nie je možné zameniť návestidlá... @@ -4592,7 +4583,7 @@ STR_ERROR_CAN_T_CONVERT_RAIL :{WHITE}Nemôže # Road construction errors STR_ERROR_MUST_REMOVE_ROAD_FIRST :{WHITE}Najprv treba odstrániť cestu STR_ERROR_ONEWAY_ROADS_CAN_T_HAVE_JUNCTION :{WHITE}... na jednosmerných cestách nie sú dovolené križovatky -STR_ERROR_CAN_T_BUILD_ROAD_HERE :{WHITE}Nemôžeš tu postaviť cestu... +STR_ERROR_CAN_T_BUILD_ROAD_HERE :{WHITE}Nemôžete tu postaviť cestu... STR_ERROR_CAN_T_BUILD_TRAMWAY_HERE :{WHITE}Nemôžeš tu stavať električkovú trať... STR_ERROR_CAN_T_REMOVE_ROAD_FROM :{WHITE}Nemôžeš tu odstrániť cestu... STR_ERROR_CAN_T_REMOVE_TRAMWAY_FROM :{WHITE}Nemôžeš tu odstrániť električkovú trať... @@ -4606,15 +4597,15 @@ STR_ERROR_INCOMPATIBLE_TRAMWAY :{WHITE}... neko # Waterway construction errors STR_ERROR_CAN_T_BUILD_CANALS :{WHITE}Tu sa nedá postaviť vodný kanál... -STR_ERROR_CAN_T_BUILD_LOCKS :{WHITE}Tu sa nedá postaviť stavidlo... +STR_ERROR_CAN_T_BUILD_LOCKS :{WHITE}Nemôžete tu postaviť stavidlá... STR_ERROR_CAN_T_PLACE_RIVERS :{WHITE}Tu nie je možné umiestniť rieku... STR_ERROR_MUST_BE_BUILT_ON_WATER :{WHITE}... musíš postaviť na vode -STR_ERROR_CAN_T_BUILD_ON_WATER :{WHITE}... nemôžeš postaviť na vode -STR_ERROR_CAN_T_BUILD_ON_SEA :{WHITE}... nemôžeš postaviť na otvorenom mori +STR_ERROR_CAN_T_BUILD_ON_WATER :{WHITE}... nemôže byť postavené na vode +STR_ERROR_CAN_T_BUILD_ON_SEA :{WHITE}... nemôže byť postavené na otvorenom mori STR_ERROR_CAN_T_BUILD_ON_CANAL :{WHITE}... nemôže byť postavené na vodnom kanále STR_ERROR_CAN_T_BUILD_ON_RIVER :{WHITE}... nemôže byť postavené na rieke STR_ERROR_MUST_DEMOLISH_CANAL_FIRST :{WHITE}Najprv sa musí zbúrať vodný kanál -STR_ERROR_CAN_T_BUILD_AQUEDUCT_HERE :{WHITE}Tu nie je možné postaviť akvadukt... +STR_ERROR_CAN_T_BUILD_AQUEDUCT_HERE :{WHITE}Nemôžete tu postaviť akvadukt... # Tree related errors STR_ERROR_TREE_ALREADY_HERE :{WHITE}... strom tu už je @@ -4622,7 +4613,7 @@ STR_ERROR_TREE_WRONG_TERRAIN_FOR_TREE_TYPE :{WHITE}... zlý STR_ERROR_CAN_T_PLANT_TREE_HERE :{WHITE}Nemôžeš tu zasadiť strom... # Bridge related errors -STR_ERROR_CAN_T_BUILD_BRIDGE_HERE :{WHITE}Nemôžeš tu postaviť most... +STR_ERROR_CAN_T_BUILD_BRIDGE_HERE :{WHITE}Nemôžete tu postaviť most... STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST :{WHITE}Most musíš najskôr zbúrať STR_ERROR_CAN_T_START_AND_END_ON :{WHITE}Nemožno začať a skončiť na tom istom mieste STR_ERROR_BRIDGEHEADS_NOT_SAME_HEIGHT :{WHITE}Konce mosta nie sú v rovnakej výške @@ -4740,7 +4731,7 @@ STR_ERROR_CAN_T_ADD_ORDER_SHARED :{WHITE}... vozi STR_ERROR_CAN_T_SHARE_ORDER_LIST :{WHITE}Nemožno zdieľať zoznam príkazov ... STR_ERROR_CAN_T_STOP_SHARING_ORDER_LIST :{WHITE}Nie je možné prestať zdieľať zoznam príkazov... -STR_ERROR_CAN_T_COPY_ORDER_LIST :{WHITE}Nemozno kopirovat zoznam prikazov ... +STR_ERROR_CAN_T_COPY_ORDER_LIST :{WHITE}Nemožno kopírovať zoznam príkazov ... STR_ERROR_TOO_FAR_FROM_PREVIOUS_DESTINATION :{WHITE}... príliš ďaleko z predchádzajúceho cieľa STR_ERROR_AIRCRAFT_NOT_ENOUGH_RANGE :{WHITE}... lietadlo nemá dostatočný dosah diff --git a/src/lang/slovenian.txt b/src/lang/slovenian.txt index d1614d12eb..c75a000e0b 100644 --- a/src/lang/slovenian.txt +++ b/src/lang/slovenian.txt @@ -2025,6 +2025,7 @@ STR_FACE_TIE :Kravata: STR_FACE_EARRING :Uhani: STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}Spremeni kravato ali uhane + # Network server list STR_NETWORK_SERVER_LIST_CAPTION :{WHITE}Več igralcev STR_NETWORK_SERVER_LIST_PLAYER_NAME :{BLACK}Ime igralca: @@ -2083,10 +2084,6 @@ STR_NETWORK_START_SERVER_NEW_GAME_NAME_TOOLTIP :{BLACK}Ime bo p STR_NETWORK_START_SERVER_SET_PASSWORD :{BLACK}Nastavi geslo STR_NETWORK_START_SERVER_PASSWORD_TOOLTIP :{BLACK}Zaščiti igro z geslom, če želiš preprečiti dostop nepovabljenim -STR_NETWORK_START_SERVER_ADVERTISED_LABEL :{BLACK}Oglaševano -STR_NETWORK_START_SERVER_ADVERTISED_TOOLTIP :{BLACK}Izberi med oglaševano (internet) in neoglaševano (Local Area Network, LAN) igro -STR_NETWORK_START_SERVER_UNADVERTISED :Ne -STR_NETWORK_START_SERVER_ADVERTISED :Da STR_NETWORK_START_SERVER_CLIENTS_SELECT :{BLACK}{NUM} gost{P "" a i ov} STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS :{BLACK}Dovoljeno število gostov: STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS_TOOLTIP :{BLACK}Izberite največje število gostov. Ni potrebno, da so vsi prisotni @@ -2151,15 +2148,10 @@ STR_NETWORK_NEED_COMPANY_PASSWORD_CAPTION :{WHITE}Podjetje # Network company list added strings STR_NETWORK_COMPANY_LIST_CLIENT_LIST :Seznam gostov -STR_NETWORK_COMPANY_LIST_SPECTATE :Opazuj -STR_NETWORK_COMPANY_LIST_NEW_COMPANY :Novo podjetje # Network client list -STR_NETWORK_CLIENTLIST_KICK :Brcni -STR_NETWORK_CLIENTLIST_BAN :Blokiraj -STR_NETWORK_CLIENTLIST_SPEAK_TO_ALL :Pogovor vsem -STR_NETWORK_CLIENTLIST_SPEAK_TO_COMPANY :Pogovor podjetju -STR_NETWORK_CLIENTLIST_SPEAK_TO_CLIENT :Zasebno sporočilo + + STR_NETWORK_SERVER :Strežnik STR_NETWORK_CLIENT :Gost diff --git a/src/lang/spanish.txt b/src/lang/spanish.txt index 3d1a705163..1af0a7fc4f 100644 --- a/src/lang/spanish.txt +++ b/src/lang/spanish.txt @@ -1992,6 +1992,7 @@ STR_FACE_TIE :Corbata: STR_FACE_EARRING :Pendientes: STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}Cambiar corbata o pendientes + # Network server list STR_NETWORK_SERVER_LIST_CAPTION :{WHITE}Multijugador STR_NETWORK_SERVER_LIST_PLAYER_NAME :{BLACK}Nombre del jugador: @@ -2054,10 +2055,6 @@ STR_NETWORK_START_SERVER_NEW_GAME_NAME_TOOLTIP :{BLACK}La parti STR_NETWORK_START_SERVER_SET_PASSWORD :{BLACK}Establecer contraseña STR_NETWORK_START_SERVER_PASSWORD_TOOLTIP :{BLACK}Protege tu partida con una contraseña si no quieres que sea universalmente accesible -STR_NETWORK_START_SERVER_ADVERTISED_LABEL :{BLACK}Público -STR_NETWORK_START_SERVER_ADVERTISED_TOOLTIP :{BLACK}Permite escoger entre una partida pública (internet) y una partida privada (LAN) -STR_NETWORK_START_SERVER_UNADVERTISED :No -STR_NETWORK_START_SERVER_ADVERTISED :Sí STR_NETWORK_START_SERVER_CLIENTS_SELECT :{BLACK}{NUM} cliente{P "" s} STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS :{BLACK}Número máximo de clientes: STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS_TOOLTIP :{BLACK}Selecciona el número máximo de clientes. No es necesario ocupar todos los espacios @@ -2119,19 +2116,13 @@ STR_NETWORK_CONNECTION_DISCONNECT :{BLACK}Desconec STR_NETWORK_NEED_GAME_PASSWORD_CAPTION :{WHITE}Servidor protegido. Introduce la contraseña STR_NETWORK_NEED_COMPANY_PASSWORD_CAPTION :{WHITE}Empresa protegida. Introduce la contraseña -STR_NETWORK_COMPANY_LIST_CLIENT_LIST_CAPTION :{WHITE}Lista de Clientes # Network company list added strings STR_NETWORK_COMPANY_LIST_CLIENT_LIST :Lista de clientes -STR_NETWORK_COMPANY_LIST_SPECTATE :Observar -STR_NETWORK_COMPANY_LIST_NEW_COMPANY :Nueva empresa # Network client list -STR_NETWORK_CLIENTLIST_KICK :Expulsar (Kick) -STR_NETWORK_CLIENTLIST_BAN :Prohibir (Ban) -STR_NETWORK_CLIENTLIST_SPEAK_TO_ALL :Hablar a todos -STR_NETWORK_CLIENTLIST_SPEAK_TO_COMPANY :Hablar a empresa -STR_NETWORK_CLIENTLIST_SPEAK_TO_CLIENT :Mensaje privado + + STR_NETWORK_SERVER :Servidor STR_NETWORK_CLIENT :Cliente diff --git a/src/lang/spanish_MX.txt b/src/lang/spanish_MX.txt index 0c81885e17..74b8b10cff 100644 --- a/src/lang/spanish_MX.txt +++ b/src/lang/spanish_MX.txt @@ -1992,6 +1992,7 @@ STR_FACE_TIE :Corbata: STR_FACE_EARRING :Aretes: STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}Cambiar corbata o aretes + # Network server list STR_NETWORK_SERVER_LIST_CAPTION :{WHITE}Multijugador STR_NETWORK_SERVER_LIST_PLAYER_NAME :{BLACK}Nombre del jugador: @@ -2054,10 +2055,6 @@ STR_NETWORK_START_SERVER_NEW_GAME_NAME_TOOLTIP :{BLACK}La parti STR_NETWORK_START_SERVER_SET_PASSWORD :{BLACK}Establecer contraseña STR_NETWORK_START_SERVER_PASSWORD_TOOLTIP :{BLACK}Proteger la partida con una contraseña para prevenir el acceso a otras personas -STR_NETWORK_START_SERVER_ADVERTISED_LABEL :{BLACK}Público -STR_NETWORK_START_SERVER_ADVERTISED_TOOLTIP :{BLACK}Permitir escoger entre una partida pública (Internet) y una partida privada (LAN) -STR_NETWORK_START_SERVER_UNADVERTISED :No -STR_NETWORK_START_SERVER_ADVERTISED :Sí STR_NETWORK_START_SERVER_CLIENTS_SELECT :{BLACK}{NUM} cliente{P "" s} STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS :{BLACK}Número máximo de clientes: STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS_TOOLTIP :{BLACK}Elegir el número máximo de clientes. No es necesario que se conecten todos @@ -2119,19 +2116,13 @@ STR_NETWORK_CONNECTION_DISCONNECT :{BLACK}Desconec STR_NETWORK_NEED_GAME_PASSWORD_CAPTION :{WHITE}Servidor protegido. Introducir contraseña STR_NETWORK_NEED_COMPANY_PASSWORD_CAPTION :{WHITE}Empresa protegida. Introducir contraseña -STR_NETWORK_COMPANY_LIST_CLIENT_LIST_CAPTION :{WHITE}Lista de clientes # Network company list added strings STR_NETWORK_COMPANY_LIST_CLIENT_LIST :Lista de clientes -STR_NETWORK_COMPANY_LIST_SPECTATE :Observar -STR_NETWORK_COMPANY_LIST_NEW_COMPANY :Nueva empresa # Network client list -STR_NETWORK_CLIENTLIST_KICK :Expulsar -STR_NETWORK_CLIENTLIST_BAN :Prohibir acceso -STR_NETWORK_CLIENTLIST_SPEAK_TO_ALL :Hablar a todos -STR_NETWORK_CLIENTLIST_SPEAK_TO_COMPANY :Hablar a una empresa -STR_NETWORK_CLIENTLIST_SPEAK_TO_CLIENT :Mensaje privado + + STR_NETWORK_SERVER :Servidor STR_NETWORK_CLIENT :Cliente @@ -2176,6 +2167,7 @@ STR_NETWORK_ERROR_SERVER_START :{WHITE}No se pu STR_NETWORK_ERROR_CLIENT_START :{WHITE}No se pudo conectar STR_NETWORK_ERROR_TIMEOUT :{WHITE}Tiempo de espera agotado en conexión #{NUM} STR_NETWORK_ERROR_SERVER_ERROR :{WHITE}Se produjo un error de protocolo y la conexión fue cerrada +STR_NETWORK_ERROR_BAD_PLAYER_NAME :{WHITE}Aún no tienes nombre de jugador, pero lo puedes poner en la parte superior de la ventana de Multijugador. STR_NETWORK_ERROR_WRONG_REVISION :{WHITE}La versión de este cliente no corresponde con la versión del servidor STR_NETWORK_ERROR_WRONG_PASSWORD :{WHITE}Contraseña incorrecta STR_NETWORK_ERROR_SERVER_FULL :{WHITE}El servidor está completo @@ -2188,6 +2180,7 @@ STR_NETWORK_ERROR_TIMEOUT_PASSWORD :{WHITE}Tardaste STR_NETWORK_ERROR_TIMEOUT_COMPUTER :{WHITE}Tu computadora es demasiado lenta para seguir la velocidad del servidor STR_NETWORK_ERROR_TIMEOUT_MAP :{WHITE}Tu computadora tardó demasiado en descargar el mapa STR_NETWORK_ERROR_TIMEOUT_JOIN :{WHITE}Tu computadora tardó demasiado en conectarse al servidor +STR_NETWORK_ERROR_INVALID_CLIENT_NAME :{WHITE}Tu nombre de jugador no es válido ############ Leave those lines in this order!! STR_NETWORK_ERROR_CLIENT_GENERAL :error general @@ -2210,6 +2203,7 @@ STR_NETWORK_ERROR_CLIENT_TIMEOUT_PASSWORD :no se recibió STR_NETWORK_ERROR_CLIENT_TIMEOUT_COMPUTER :tiempo agotado en general STR_NETWORK_ERROR_CLIENT_TIMEOUT_MAP :la descarga del mapa tardó demasiado STR_NETWORK_ERROR_CLIENT_TIMEOUT_JOIN :el procesado del mapa tardó demasiado +STR_NETWORK_ERROR_CLIENT_INVALID_CLIENT_NAME :nombre de cliente no válido ############ End of leave-in-this-order STR_NETWORK_ERROR_CLIENT_GUI_LOST_CONNECTION_CAPTION :{WHITE}Posible pérdida de conexión diff --git a/src/lang/swedish.txt b/src/lang/swedish.txt index 54bfa40661..d28125b9b1 100644 --- a/src/lang/swedish.txt +++ b/src/lang/swedish.txt @@ -1990,6 +1990,7 @@ STR_FACE_TIE :Slips: STR_FACE_EARRING :Örhänge: STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}Ändra slips eller örhänge + # Network server list STR_NETWORK_SERVER_LIST_CAPTION :{WHITE}Flera spelare STR_NETWORK_SERVER_LIST_PLAYER_NAME :{BLACK}Spelarnamn: @@ -2052,10 +2053,6 @@ STR_NETWORK_START_SERVER_NEW_GAME_NAME_TOOLTIP :{BLACK}Namnet p STR_NETWORK_START_SERVER_SET_PASSWORD :{BLACK}Bestäm lösenord STR_NETWORK_START_SERVER_PASSWORD_TOOLTIP :{BLACK}Skydda spelet med ett lösenord så att inte andra än dem som har lösenordet kan gå med i spelet -STR_NETWORK_START_SERVER_ADVERTISED_LABEL :{BLACK}Publikt -STR_NETWORK_START_SERVER_ADVERTISED_TOOLTIP :{BLACK}Välj mellan ett publikt (internet) eller icke publikt (Local Area Network. LAN) spel -STR_NETWORK_START_SERVER_UNADVERTISED :Nej -STR_NETWORK_START_SERVER_ADVERTISED :Ja STR_NETWORK_START_SERVER_CLIENTS_SELECT :{BLACK}{NUM} klient{P "" er} STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS :{BLACK}Max antal tillåtna klienter: STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS_TOOLTIP :{BLACK}Välj max antal tillåtna klienter. Alla platser måste inte fyllas. @@ -2117,19 +2114,13 @@ STR_NETWORK_CONNECTION_DISCONNECT :{BLACK}Koppla i STR_NETWORK_NEED_GAME_PASSWORD_CAPTION :{WHITE}Servern är skyddad. Ange lösenord STR_NETWORK_NEED_COMPANY_PASSWORD_CAPTION :{WHITE}Företaget är skyddat. Ange lösenord -STR_NETWORK_COMPANY_LIST_CLIENT_LIST_CAPTION :{WHITE}Klientlista # Network company list added strings STR_NETWORK_COMPANY_LIST_CLIENT_LIST :Klientlista -STR_NETWORK_COMPANY_LIST_SPECTATE :Åskåda -STR_NETWORK_COMPANY_LIST_NEW_COMPANY :Nytt företag # Network client list -STR_NETWORK_CLIENTLIST_KICK :Kasta ut -STR_NETWORK_CLIENTLIST_BAN :Bannlys -STR_NETWORK_CLIENTLIST_SPEAK_TO_ALL :Prata med alla -STR_NETWORK_CLIENTLIST_SPEAK_TO_COMPANY :Prata med alla i företaget -STR_NETWORK_CLIENTLIST_SPEAK_TO_CLIENT :Privat meddelande + + STR_NETWORK_SERVER :Server STR_NETWORK_CLIENT :Klient diff --git a/src/lang/tamil.txt b/src/lang/tamil.txt index 0f3b782e84..bfdd69caa2 100644 --- a/src/lang/tamil.txt +++ b/src/lang/tamil.txt @@ -1734,6 +1734,7 @@ STR_FACE_TIE :Tie: STR_FACE_EARRING :கம்மல்: STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}Tie அல்லது காதணியை மாற்றவும் + # Network server list STR_NETWORK_SERVER_LIST_CAPTION :{WHITE}பல்வீரர் STR_NETWORK_SERVER_LIST_PLAYER_NAME :{BLACK}விளையாடுபவரின் பெயர்: @@ -1789,9 +1790,6 @@ STR_NETWORK_START_SERVER_CAPTION :{WHITE}பு STR_NETWORK_START_SERVER_NEW_GAME_NAME :{BLACK}விளையாட்டு பெயர்: STR_NETWORK_START_SERVER_SET_PASSWORD :{BLACK}கடவுச்சொல்லினை அமைக்கவும் -STR_NETWORK_START_SERVER_ADVERTISED_LABEL :{BLACK}விளம்பரப்படுத்தப்பட்ட -STR_NETWORK_START_SERVER_UNADVERTISED :இல்லை -STR_NETWORK_START_SERVER_ADVERTISED :ஆம் STR_NETWORK_START_SERVER_CLIENTS_SELECT :{BLACK}{NUM} விளையாடுபவர்{P "" கள்} STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS :{BLACK}அதிகபட்ச வீரர்கள்: STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS_TOOLTIP :{BLACK}வாடிக்கையாளர்களின் அதிகபட்ச எண்ணிக்கையைத் தேர்வுசெய்க. எல்லா இடங்களும் நிரப்பப்பட வேண்டியதில்லை @@ -1850,19 +1848,13 @@ STR_NETWORK_CONNECTION_DISCONNECT :{BLACK}இண STR_NETWORK_NEED_GAME_PASSWORD_CAPTION :{WHITE}சர்வர் காக்கப்பட்டுள்ளது. கடவுச்சொல்லினை இடவும் STR_NETWORK_NEED_COMPANY_PASSWORD_CAPTION :{WHITE}நிற்வனம் காக்கப்பட்டுள்ளது. கடவுச்சொல்லினை இடவும் -STR_NETWORK_COMPANY_LIST_CLIENT_LIST_CAPTION :{WHITE}விளையாடுவோர் பட்டியல் # Network company list added strings STR_NETWORK_COMPANY_LIST_CLIENT_LIST :விளையாடுவோர் பட்டியல் -STR_NETWORK_COMPANY_LIST_SPECTATE :கவனி -STR_NETWORK_COMPANY_LIST_NEW_COMPANY :புதிய நிறுவனம் # Network client list -STR_NETWORK_CLIENTLIST_KICK :உதை -STR_NETWORK_CLIENTLIST_BAN :அனுமதிக்காதே -STR_NETWORK_CLIENTLIST_SPEAK_TO_ALL :அனைவருடனும் பேசு -STR_NETWORK_CLIENTLIST_SPEAK_TO_COMPANY :நிறுவனத்திடம் பேசு -STR_NETWORK_CLIENTLIST_SPEAK_TO_CLIENT :இரகசிய தகவல் + + STR_NETWORK_SERVER :சர்வர் STR_NETWORK_CLIENT :Client diff --git a/src/lang/thai.txt b/src/lang/thai.txt index 2a3b5aed10..9bc03a74b3 100644 --- a/src/lang/thai.txt +++ b/src/lang/thai.txt @@ -1820,6 +1820,7 @@ STR_FACE_TIE :เนคไท STR_FACE_EARRING :ต่างหู: STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}เปลี่ยนเนคไทหรือต่างหู + # Network server list STR_NETWORK_SERVER_LIST_CAPTION :{WHITE}เล่นหลายคน STR_NETWORK_SERVER_LIST_PLAYER_NAME :{BLACK}ชื่อผู้เล่น: @@ -1878,10 +1879,6 @@ STR_NETWORK_START_SERVER_NEW_GAME_NAME_TOOLTIP :{BLACK}ชื STR_NETWORK_START_SERVER_SET_PASSWORD :{BLACK}กำหนดรหัสผ่าน STR_NETWORK_START_SERVER_PASSWORD_TOOLTIP :{BLACK}ป้องกันเกมด้วยรหัสผ่านหากไม่ต้องการให้มีการเข้าร่วมจากบุคคลทั่วไป -STR_NETWORK_START_SERVER_ADVERTISED_LABEL :{BLACK}การประกาศ -STR_NETWORK_START_SERVER_ADVERTISED_TOOLTIP :{BLACK}เลือกระหว่างการโฆษณา (internet) หรือไม่โฆษณา (Local Area Network, LAN) เกม -STR_NETWORK_START_SERVER_UNADVERTISED :ไม่ใช่ -STR_NETWORK_START_SERVER_ADVERTISED :ใช่ STR_NETWORK_START_SERVER_CLIENTS_SELECT :{BLACK}{NUM} ลูกข่าย STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS :{BLACK}ลูกข่ายสูงสุด: STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS_TOOLTIP :{BLACK}เลือกจำนวนมากสุดของลูกข่าย (ไม่จำเป็นต้องครบตามจำนวนนี้) @@ -1946,15 +1943,10 @@ STR_NETWORK_NEED_COMPANY_PASSWORD_CAPTION :{WHITE}บร # Network company list added strings STR_NETWORK_COMPANY_LIST_CLIENT_LIST :รายการลูกข่าย -STR_NETWORK_COMPANY_LIST_SPECTATE :ผู้ชม -STR_NETWORK_COMPANY_LIST_NEW_COMPANY :บริษัทใหม่ # Network client list -STR_NETWORK_CLIENTLIST_KICK :เตะ -STR_NETWORK_CLIENTLIST_BAN :แบน -STR_NETWORK_CLIENTLIST_SPEAK_TO_ALL :พูดกับผู้เล่นทั้งหมด -STR_NETWORK_CLIENTLIST_SPEAK_TO_COMPANY :พูดกับผู้เล่นในบริษัทเดียวกัน -STR_NETWORK_CLIENTLIST_SPEAK_TO_CLIENT :ข้อความส่วนตัว + + STR_NETWORK_SERVER :เซิฟเวอร์ STR_NETWORK_CLIENT :ลูกข่าย diff --git a/src/lang/traditional_chinese.txt b/src/lang/traditional_chinese.txt index 99b446d669..7384d0a45b 100644 --- a/src/lang/traditional_chinese.txt +++ b/src/lang/traditional_chinese.txt @@ -1879,6 +1879,7 @@ STR_FACE_TIE :領帶: STR_FACE_EARRING :耳飾: STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}改變領帶或耳飾 + # Network server list STR_NETWORK_SERVER_LIST_CAPTION :{WHITE}多人遊戲 STR_NETWORK_SERVER_LIST_PLAYER_NAME :{BLACK}玩家名稱: @@ -1939,10 +1940,6 @@ STR_NETWORK_START_SERVER_NEW_GAME_NAME_TOOLTIP :{BLACK}遊戲 STR_NETWORK_START_SERVER_SET_PASSWORD :{BLACK}設定密碼 STR_NETWORK_START_SERVER_PASSWORD_TOOLTIP :{BLACK}如果您不想讓大眾連線的話,可以用密碼保護遊戲 -STR_NETWORK_START_SERVER_ADVERTISED_LABEL :{BLACK}已啟用宣傳 -STR_NETWORK_START_SERVER_ADVERTISED_TOOLTIP :{BLACK}選擇是否在互聯網上宣傳此遊戲 -STR_NETWORK_START_SERVER_UNADVERTISED :否 -STR_NETWORK_START_SERVER_ADVERTISED :是 STR_NETWORK_START_SERVER_CLIENTS_SELECT :{BLACK}{NUM} 玩家 STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS :{BLACK}用戶端上限: STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS_TOOLTIP :{BLACK}選擇連線人數上限,但不是每個名額都要有人連線 @@ -2007,15 +2004,10 @@ STR_NETWORK_NEED_COMPANY_PASSWORD_CAPTION :{WHITE}公司 # Network company list added strings STR_NETWORK_COMPANY_LIST_CLIENT_LIST :用戶端清單 -STR_NETWORK_COMPANY_LIST_SPECTATE :旁觀者 -STR_NETWORK_COMPANY_LIST_NEW_COMPANY :新公司 # Network client list -STR_NETWORK_CLIENTLIST_KICK :踢除 -STR_NETWORK_CLIENTLIST_BAN :封鎖 -STR_NETWORK_CLIENTLIST_SPEAK_TO_ALL :對全員說話 -STR_NETWORK_CLIENTLIST_SPEAK_TO_COMPANY :對公司說話 -STR_NETWORK_CLIENTLIST_SPEAK_TO_CLIENT :私人訊息 + + STR_NETWORK_SERVER :伺服器 STR_NETWORK_CLIENT :用戶端 diff --git a/src/lang/turkish.txt b/src/lang/turkish.txt index fb378c4c9f..cbedd61fae 100644 --- a/src/lang/turkish.txt +++ b/src/lang/turkish.txt @@ -1928,6 +1928,7 @@ STR_FACE_TIE :Kravat: STR_FACE_EARRING :Küpe: STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}Kravatı veya küpeyi değiştir + # Network server list STR_NETWORK_SERVER_LIST_CAPTION :{WHITE}Çok Oyunculu STR_NETWORK_SERVER_LIST_PLAYER_NAME :{BLACK}Oyuncu adı: @@ -1986,10 +1987,6 @@ STR_NETWORK_START_SERVER_NEW_GAME_NAME_TOOLTIP :{BLACK}Bu oyun STR_NETWORK_START_SERVER_SET_PASSWORD :{BLACK}Parola koy STR_NETWORK_START_SERVER_PASSWORD_TOOLTIP :{BLACK}Erişimi kısıtlamak için oyuna parola koy -STR_NETWORK_START_SERVER_ADVERTISED_LABEL :{BLACK}Duyurulan -STR_NETWORK_START_SERVER_ADVERTISED_TOOLTIP :{BLACK}Duyurulan (internet) ve duyurulmayan (yerel ağ, LAN) oyun türlerinden birini seçin -STR_NETWORK_START_SERVER_UNADVERTISED :Hayır -STR_NETWORK_START_SERVER_ADVERTISED :Evet STR_NETWORK_START_SERVER_CLIENTS_SELECT :{BLACK}{NUM} istemci STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS :{BLACK}Azami istemci sayısı: STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS_TOOLTIP :{BLACK}İzin verilen en fazla oyuncu sayısını seç. Her yerin dolması gerekmez @@ -2051,19 +2048,13 @@ STR_NETWORK_CONNECTION_DISCONNECT :{BLACK}Bağlant STR_NETWORK_NEED_GAME_PASSWORD_CAPTION :{WHITE}Sunucu korumalı. Parola girin STR_NETWORK_NEED_COMPANY_PASSWORD_CAPTION :{WHITE}Şirket korumalı. Parola girin -STR_NETWORK_COMPANY_LIST_CLIENT_LIST_CAPTION :{WHITE}İstemci listesi # Network company list added strings STR_NETWORK_COMPANY_LIST_CLIENT_LIST :Oyuncu listesi -STR_NETWORK_COMPANY_LIST_SPECTATE :Gözlemle -STR_NETWORK_COMPANY_LIST_NEW_COMPANY :Yeni şirket # Network client list -STR_NETWORK_CLIENTLIST_KICK :At -STR_NETWORK_CLIENTLIST_BAN :Yasakla -STR_NETWORK_CLIENTLIST_SPEAK_TO_ALL :Herkesle konuş -STR_NETWORK_CLIENTLIST_SPEAK_TO_COMPANY :Şirketle konuş -STR_NETWORK_CLIENTLIST_SPEAK_TO_CLIENT :Özel mesaj + + STR_NETWORK_SERVER :Sunucu STR_NETWORK_CLIENT :İstemci diff --git a/src/lang/ukrainian.txt b/src/lang/ukrainian.txt index 0d26d41cc3..4ce593db54 100644 --- a/src/lang/ukrainian.txt +++ b/src/lang/ukrainian.txt @@ -2113,6 +2113,7 @@ STR_FACE_TIE :Краватк STR_FACE_EARRING :Сережки: STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}Змінити комір або сережки + # Network server list STR_NETWORK_SERVER_LIST_CAPTION :{WHITE}Мережева гра STR_NETWORK_SERVER_LIST_PLAYER_NAME :{BLACK}Ім'я гравця: @@ -2175,10 +2176,6 @@ STR_NETWORK_START_SERVER_NEW_GAME_NAME_TOOLTIP :{BLACK}Назв STR_NETWORK_START_SERVER_SET_PASSWORD :{BLACK}Встановити пароль STR_NETWORK_START_SERVER_PASSWORD_TOOLTIP :{BLACK}Захистіть вашу гру паролем, якщо не бажаєте загального доступу -STR_NETWORK_START_SERVER_ADVERTISED_LABEL :{BLACK}Інтернет -STR_NETWORK_START_SERVER_ADVERTISED_TOOLTIP :{BLACK}Вибір між рекламованою (інтернет) і не рекламованою (локальна мережа, LAN) грою -STR_NETWORK_START_SERVER_UNADVERTISED :Ні -STR_NETWORK_START_SERVER_ADVERTISED :Так STR_NETWORK_START_SERVER_CLIENTS_SELECT :{BLACK}{NUM} клієнт{P "" а ів} STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS :{BLACK}Макс. клієнтів: STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS_TOOLTIP :{BLACK}Виберіть максимальну кількість клієнтів. Не всі слоти мають бути зайняті @@ -2240,19 +2237,13 @@ STR_NETWORK_CONNECTION_DISCONNECT :{BLACK}Роз' STR_NETWORK_NEED_GAME_PASSWORD_CAPTION :{WHITE}Сервер захищено паролем. Введіть пароль STR_NETWORK_NEED_COMPANY_PASSWORD_CAPTION :{WHITE}Компанія захищена паролем. Введіть пароль -STR_NETWORK_COMPANY_LIST_CLIENT_LIST_CAPTION :{WHITE}Список клієнтів # Network company list added strings STR_NETWORK_COMPANY_LIST_CLIENT_LIST :Список клієнтів -STR_NETWORK_COMPANY_LIST_SPECTATE :Спостерігати -STR_NETWORK_COMPANY_LIST_NEW_COMPANY :Нова компанія # Network client list -STR_NETWORK_CLIENTLIST_KICK :Відключити гравця -STR_NETWORK_CLIENTLIST_BAN :Бан -STR_NETWORK_CLIENTLIST_SPEAK_TO_ALL :Повідомлення всім -STR_NETWORK_CLIENTLIST_SPEAK_TO_COMPANY :Повідомлення компанії -STR_NETWORK_CLIENTLIST_SPEAK_TO_CLIENT :Приватне повідомлення + + STR_NETWORK_SERVER :Сервер STR_NETWORK_CLIENT :Клієнт diff --git a/src/lang/urdu.txt b/src/lang/urdu.txt index 70cb48636b..330cf32824 100644 --- a/src/lang/urdu.txt +++ b/src/lang/urdu.txt @@ -1482,6 +1482,7 @@ STR_FACE_TIE :ٹائی: STR_FACE_EARRING :بالیاں: STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}ٹائی یا بالیاں بدلیں + # Network server list STR_NETWORK_SERVER_LIST_CAPTION :{WHITE}زیادہ کھلاڑی STR_NETWORK_SERVER_LIST_PLAYER_NAME :{BLACK}کھلاڑی کا نام: @@ -1604,15 +1605,10 @@ STR_NETWORK_NEED_COMPANY_PASSWORD_CAPTION :{WHITE}کمپن # Network company list added strings STR_NETWORK_COMPANY_LIST_CLIENT_LIST :{WHITE}کلائینٹس کی فہرست -STR_NETWORK_COMPANY_LIST_SPECTATE :{WHITE}نظارہ کریں -STR_NETWORK_COMPANY_LIST_NEW_COMPANY :{WHITE}نئی کمپنی # Network client list -STR_NETWORK_CLIENTLIST_KICK :ٹھڈا -STR_NETWORK_CLIENTLIST_BAN :پابندی -STR_NETWORK_CLIENTLIST_SPEAK_TO_ALL :سب سے بات کریں -STR_NETWORK_CLIENTLIST_SPEAK_TO_COMPANY :کمپنی سے بات کریں -STR_NETWORK_CLIENTLIST_SPEAK_TO_CLIENT :پرائیویٹ پیغام + + STR_NETWORK_SERVER :سرور STR_NETWORK_CLIENT :کلائینٹ diff --git a/src/lang/vietnamese.txt b/src/lang/vietnamese.txt index da4bf597b5..389a8f4a19 100644 --- a/src/lang/vietnamese.txt +++ b/src/lang/vietnamese.txt @@ -1988,6 +1988,7 @@ STR_FACE_TIE :Cà vạt: STR_FACE_EARRING :Bông tai: STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}Thay đổi cà vạt hoặc bông tai + # Network server list STR_NETWORK_SERVER_LIST_CAPTION :{WHITE}Ván Chơi Mạng STR_NETWORK_SERVER_LIST_PLAYER_NAME :{BLACK}Tên người chơi: @@ -2050,10 +2051,6 @@ STR_NETWORK_START_SERVER_NEW_GAME_NAME_TOOLTIP :{BLACK}Tên c STR_NETWORK_START_SERVER_SET_PASSWORD :{BLACK}Đặt mật khẩu STR_NETWORK_START_SERVER_PASSWORD_TOOLTIP :{BLACK}Bảo vệ game của bạn bằng mật khẩu nếu bạn không muốn người khác vào tùy tiện -STR_NETWORK_START_SERVER_ADVERTISED_LABEL :{BLACK}Đã quảng bá -STR_NETWORK_START_SERVER_ADVERTISED_TOOLTIP :{BLACK}Lựa chọn ván chơi quảng bá (ra ngoài internet) và không quảng bá (Local Area Network, LAN) -STR_NETWORK_START_SERVER_UNADVERTISED :Không -STR_NETWORK_START_SERVER_ADVERTISED :Có STR_NETWORK_START_SERVER_CLIENTS_SELECT :{BLACK}{NUM} máy trạm STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS :{BLACK}Số máy trạm tối đa: STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS_TOOLTIP :{BLACK}Chọn số lượng máy trạm tối đa. Không nhất thiết phải chọn đầy các dòng @@ -2115,19 +2112,13 @@ STR_NETWORK_CONNECTION_DISCONNECT :{BLACK}Ngắt k STR_NETWORK_NEED_GAME_PASSWORD_CAPTION :{WHITE}Server yêu cầu xác thực. Nhập mật khẩu STR_NETWORK_NEED_COMPANY_PASSWORD_CAPTION :{WHITE}Công ty yêu cầu xác thực. Nhập mật khẩu -STR_NETWORK_COMPANY_LIST_CLIENT_LIST_CAPTION :{WHITE}Danh sách máy trạm # Network company list added strings STR_NETWORK_COMPANY_LIST_CLIENT_LIST :Danh sách máy trạm -STR_NETWORK_COMPANY_LIST_SPECTATE :Xem -STR_NETWORK_COMPANY_LIST_NEW_COMPANY :Công ty mới # Network client list -STR_NETWORK_CLIENTLIST_KICK :Đá -STR_NETWORK_CLIENTLIST_BAN :Cấm -STR_NETWORK_CLIENTLIST_SPEAK_TO_ALL :Nói với tất cả -STR_NETWORK_CLIENTLIST_SPEAK_TO_COMPANY :Nói cho công ty -STR_NETWORK_CLIENTLIST_SPEAK_TO_CLIENT :Thông điệp riêng + + STR_NETWORK_SERVER :Server STR_NETWORK_CLIENT :Máy trạm diff --git a/src/lang/welsh.txt b/src/lang/welsh.txt index f08bd8ff12..cff7088277 100644 --- a/src/lang/welsh.txt +++ b/src/lang/welsh.txt @@ -1877,6 +1877,7 @@ STR_FACE_TIE :Tei: STR_FACE_EARRING :Clustlws: STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}Newid tei neu glustlws + # Network server list STR_NETWORK_SERVER_LIST_CAPTION :{WHITE}Amlchwaraewr STR_NETWORK_SERVER_LIST_PLAYER_NAME :{BLACK}Enw chwaraewr: @@ -1935,10 +1936,6 @@ STR_NETWORK_START_SERVER_NEW_GAME_NAME_TOOLTIP :{BLACK}Bydd y g STR_NETWORK_START_SERVER_SET_PASSWORD :{BLACK}Gosod cyfrinair STR_NETWORK_START_SERVER_PASSWORD_TOOLTIP :{BLACK}Diogelwch eich gêm â chyfrinair os nad ydych am i fynediad ato fod yn gyhoeddus -STR_NETWORK_START_SERVER_ADVERTISED_LABEL :{BLACK}Hysbys -STR_NETWORK_START_SERVER_ADVERTISED_TOOLTIP :{BLACK}Dewis rhwyd gêm a hysbysebir (rhyngrwyd) neu un anhysbys (rhwydwaith leol) -STR_NETWORK_START_SERVER_UNADVERTISED :Na -STR_NETWORK_START_SERVER_ADVERTISED :Ia STR_NETWORK_START_SERVER_CLIENTS_SELECT :{BLACK}{NUM} gwestai STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS :{BLACK}Uchafswm nifer gwesteion: STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS_TOOLTIP :{BLACK}Dewiswch uchafswm y gwesteion. Does dim rhaid llanw pob slot @@ -2003,15 +2000,10 @@ STR_NETWORK_NEED_COMPANY_PASSWORD_CAPTION :{WHITE}Mae'r cw # Network company list added strings STR_NETWORK_COMPANY_LIST_CLIENT_LIST :Rhestr Cleientiaid -STR_NETWORK_COMPANY_LIST_SPECTATE :Gwylio -STR_NETWORK_COMPANY_LIST_NEW_COMPANY :Cwmni newydd # Network client list -STR_NETWORK_CLIENTLIST_KICK :Cicio -STR_NETWORK_CLIENTLIST_BAN :Gwahardd -STR_NETWORK_CLIENTLIST_SPEAK_TO_ALL :Siarad â phawb -STR_NETWORK_CLIENTLIST_SPEAK_TO_COMPANY :Siarad a'r cwmni -STR_NETWORK_CLIENTLIST_SPEAK_TO_CLIENT :Neges breifat + + STR_NETWORK_SERVER :Gweinydd STR_NETWORK_CLIENT :Gwestai From 27031b396f4a41842e597abb2867bbe2832d315c Mon Sep 17 00:00:00 2001 From: Tyler Trahan Date: Sun, 25 Apr 2021 14:27:30 -0400 Subject: [PATCH 161/268] Change: [Actions] Reword bug/crash report instructions (#9104) --- .github/ISSUE_TEMPLATE/bug.md | 4 ++-- .github/ISSUE_TEMPLATE/crash.md | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug.md b/.github/ISSUE_TEMPLATE/bug.md index 0af8a89791..4801435901 100644 --- a/.github/ISSUE_TEMPLATE/bug.md +++ b/.github/ISSUE_TEMPLATE/bug.md @@ -5,13 +5,13 @@ title: "Bug Report" --- ## Version of OpenTTD - + ## Expected result ## Actual result - + ## Steps to reproduce diff --git a/.github/ISSUE_TEMPLATE/crash.md b/.github/ISSUE_TEMPLATE/crash.md index f7d127e930..00f62f05c9 100644 --- a/.github/ISSUE_TEMPLATE/crash.md +++ b/.github/ISSUE_TEMPLATE/crash.md @@ -6,7 +6,7 @@ title: "Crash Report" ## Version of OpenTTD - + ## Steps to reproduce From 224625942c44ec33519010ae320b3821d933229a Mon Sep 17 00:00:00 2001 From: PeterN Date: Sun, 25 Apr 2021 19:34:16 +0100 Subject: [PATCH 162/268] Fix: Count engine details text in lines rather than pixels. (#9107) This allows the details panel to scale correctly for different zoom levels. --- src/autoreplace_gui.cpp | 8 ++++---- src/build_vehicle_gui.cpp | 10 +++++----- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/autoreplace_gui.cpp b/src/autoreplace_gui.cpp index d16e0916e1..e04eba302a 100644 --- a/src/autoreplace_gui.cpp +++ b/src/autoreplace_gui.cpp @@ -81,7 +81,7 @@ class ReplaceVehicleWindow : public Window { bool replace_engines; ///< If \c true, engines are replaced, if \c false, wagons are replaced (only for trains). bool reset_sel_engine; ///< Also reset #sel_engine while updating left and/or right and no valid engine selected. GroupID sel_group; ///< Group selected to replace. - int details_height; ///< Minimal needed height of the details panels (found so far). + int details_height; ///< Minimal needed height of the details panels, in text lines (found so far). byte sort_criteria; ///< Criteria of sorting vehicles. bool descending_sort_order; ///< Order of sorting vehicles. bool show_hidden_engines; ///< Whether to show the hidden engines. @@ -229,7 +229,7 @@ public: this->engines[0].ForceRebuild(); this->engines[1].ForceRebuild(); this->reset_sel_engine = true; - this->details_height = ((vehicletype == VEH_TRAIN) ? 10 : 9) * FONT_HEIGHT_NORMAL + WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM; + this->details_height = ((vehicletype == VEH_TRAIN) ? 10 : 9); this->sel_engine[0] = INVALID_ENGINE; this->sel_engine[1] = INVALID_ENGINE; this->show_hidden_engines = _engine_sort_show_hidden_engines[vehicletype]; @@ -274,7 +274,7 @@ public: case WID_RV_LEFT_DETAILS: case WID_RV_RIGHT_DETAILS: - size->height = this->details_height; + size->height = FONT_HEIGHT_NORMAL * this->details_height + padding.height; break; case WID_RV_TRAIN_WAGONREMOVE_TOGGLE: { @@ -482,7 +482,7 @@ public: NWidgetBase *nwi = this->GetWidget(side == 0 ? WID_RV_LEFT_DETAILS : WID_RV_RIGHT_DETAILS); int text_end = DrawVehiclePurchaseInfo(nwi->pos_x + WD_FRAMETEXT_LEFT, nwi->pos_x + nwi->current_x - WD_FRAMETEXT_RIGHT, nwi->pos_y + WD_FRAMERECT_TOP, this->sel_engine[side], ted); - needed_height = std::max(needed_height, text_end - (int)nwi->pos_y + WD_FRAMERECT_BOTTOM); + needed_height = std::max(needed_height, (text_end - (int)nwi->pos_y - WD_FRAMERECT_TOP) / FONT_HEIGHT_NORMAL); } } if (needed_height != this->details_height) { // Details window are not high enough, enlarge them. diff --git a/src/build_vehicle_gui.cpp b/src/build_vehicle_gui.cpp index a7a7d80428..8ac0494558 100644 --- a/src/build_vehicle_gui.cpp +++ b/src/build_vehicle_gui.cpp @@ -1055,7 +1055,7 @@ struct BuildVehicleWindow : Window { CargoID cargo_filter[NUM_CARGO + 3]; ///< Available cargo filters; CargoID or CF_ANY or CF_NONE or CF_ENGINES StringID cargo_filter_texts[NUM_CARGO + 4]; ///< Texts for filter_cargo, terminated by INVALID_STRING_ID byte cargo_filter_criteria; ///< Selected cargo filter - int details_height; ///< Minimal needed height of the details panels (found so far). + int details_height; ///< Minimal needed height of the details panels, in text lines (found so far). Scrollbar *vscroll; TestedEngineDetails te; ///< Tested cost and capacity after refit. @@ -1115,7 +1115,7 @@ struct BuildVehicleWindow : Window { widget->tool_tip = STR_SHOW_HIDDEN_ENGINES_VEHICLE_TRAIN_TOOLTIP + type; widget->SetLowered(this->show_hidden_engines); - this->details_height = ((this->vehicle_type == VEH_TRAIN) ? 10 : 9) * FONT_HEIGHT_NORMAL + WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM; + this->details_height = ((this->vehicle_type == VEH_TRAIN) ? 10 : 9); this->FinishInitNested(tile == INVALID_TILE ? (int)type : tile); @@ -1553,7 +1553,7 @@ struct BuildVehicleWindow : Window { break; case WID_BV_PANEL: - size->height = this->details_height; + size->height = FONT_HEIGHT_NORMAL * this->details_height + padding.height; break; case WID_BV_SORT_ASCENDING_DESCENDING: { @@ -1620,12 +1620,12 @@ struct BuildVehicleWindow : Window { NWidgetBase *nwi = this->GetWidget(WID_BV_PANEL); int text_end = DrawVehiclePurchaseInfo(nwi->pos_x + WD_FRAMETEXT_LEFT, nwi->pos_x + nwi->current_x - WD_FRAMETEXT_RIGHT, nwi->pos_y + WD_FRAMERECT_TOP, this->sel_engine, this->te); - needed_height = std::max(needed_height, text_end - (int)nwi->pos_y + WD_FRAMERECT_BOTTOM); + needed_height = std::max(needed_height, (text_end - (int)nwi->pos_y - WD_FRAMERECT_TOP) / FONT_HEIGHT_NORMAL); } if (needed_height != this->details_height) { // Details window are not high enough, enlarge them. int resize = needed_height - this->details_height; this->details_height = needed_height; - this->ReInit(0, resize); + this->ReInit(0, resize * FONT_HEIGHT_NORMAL); return; } } From 97288bc286bf4b4b92cf29bcd051c49b2299624b Mon Sep 17 00:00:00 2001 From: Charles Pigott Date: Sun, 25 Apr 2021 20:11:59 +0100 Subject: [PATCH 163/268] Fix #9109: Malformed STR_ROAD_TOOLBAR_TOOLTIP_CONVERT_ROAD in Japanese lang (#9110) --- src/lang/japanese.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lang/japanese.txt b/src/lang/japanese.txt index f4cc22bf0a..867f929d1f 100644 --- a/src/lang/japanese.txt +++ b/src/lang/japanese.txt @@ -2402,7 +2402,7 @@ STR_ROAD_TOOLBAR_TOOLTIP_BUILD_ROAD_TUNNEL :{BLACK}道路 STR_ROAD_TOOLBAR_TOOLTIP_BUILD_TRAMWAY_TUNNEL :{BLACK}軌道用トンネルを建設します。Shiftを押しながら決定すると費用の見積が出ます STR_ROAD_TOOLBAR_TOOLTIP_TOGGLE_BUILD_REMOVE_FOR_ROAD :{BLACK}道路の建設/撤去を切り替えます STR_ROAD_TOOLBAR_TOOLTIP_TOGGLE_BUILD_REMOVE_FOR_TRAMWAYS :{BLACK}軌道の建設/撤去を切り替えます -STR_ROAD_TOOLBAR_TOOLTIP_CONVERT_ROAD :{BLACK}道路の種類を変更/アップグレードします.Shiftは、コスト見積もりの​​作成/表示を切り替えます +STR_ROAD_TOOLBAR_TOOLTIP_CONVERT_ROAD :{BLACK}道路の種類を変更/アップグレードします.Shiftは、コスト見積もりの作成/表示を切り替えます STR_ROAD_NAME_TRAM :トラムウェイ From 8b302761d4ca51f41eaff4097c9afa4f3aec5ec5 Mon Sep 17 00:00:00 2001 From: Rubidium Date: Sun, 18 Apr 2021 14:42:06 +0200 Subject: [PATCH 164/268] Codechange: allow different limits in packet sizes --- src/network/core/packet.cpp | 17 +++++++++++------ src/network/core/packet.h | 15 +++++++++------ src/network/core/tcp.cpp | 2 +- src/network/core/udp.cpp | 3 ++- 4 files changed, 23 insertions(+), 14 deletions(-) diff --git a/src/network/core/packet.cpp b/src/network/core/packet.cpp index 428bc65ba4..39531c0a40 100644 --- a/src/network/core/packet.cpp +++ b/src/network/core/packet.cpp @@ -19,6 +19,7 @@ /** * Create a packet that is used to read from a network socket. * @param cs The socket handler associated with the socket we are reading from. + * @param limit The maximum size of packets to accept. * @param initial_read_size The initial amount of data to transfer from the socket into the * packet. This defaults to just the required bytes to determine the * packet's size. That default is the wanted for streams such as TCP @@ -27,7 +28,7 @@ * loose some the data of the packet, so there you pass the maximum * size for the packet you expect from the network. */ -Packet::Packet(NetworkSocketHandler *cs, size_t initial_read_size) : next(nullptr), pos(0) +Packet::Packet(NetworkSocketHandler *cs, size_t limit, size_t initial_read_size) : next(nullptr), pos(0), limit(limit) { assert(cs != nullptr); @@ -37,9 +38,13 @@ Packet::Packet(NetworkSocketHandler *cs, size_t initial_read_size) : next(nullpt /** * Creates a packet to send - * @param type of the packet to send + * @param type The type of the packet to send + * @param limit The maximum number of bytes the packet may have. Default is SEND_MTU. + * Be careful of compatibility with older clients/servers when changing + * the limit as it might break things if the other side is not expecting + * much larger packets than what they support. */ -Packet::Packet(PacketType type) : next(nullptr), pos(0), cs(nullptr) +Packet::Packet(PacketType type, size_t limit) : next(nullptr), pos(0), cs(nullptr) { /* Allocate space for the the size so we can write that in just before sending the packet. */ this->Send_uint16(0); @@ -93,7 +98,7 @@ void Packet::PrepareToSend() */ bool Packet::CanWriteToPacket(size_t bytes_to_write) { - return this->Size() + bytes_to_write < SEND_MTU; + return this->Size() + bytes_to_write < this->limit; } /* @@ -191,7 +196,7 @@ void Packet::Send_string(const char *data) */ size_t Packet::Send_bytes(const byte *begin, const byte *end) { - size_t amount = std::min(end - begin, SEND_MTU - this->Size()); + size_t amount = std::min(end - begin, this->limit - this->Size()); this->buffer.insert(this->buffer.end(), begin, begin + amount); return amount; } @@ -260,7 +265,7 @@ bool Packet::ParsePacketSize() /* If the size of the packet is less than the bytes required for the size and type of * the packet, or more than the allowed limit, then something is wrong with the packet. * In those cases the packet can generally be regarded as containing garbage data. */ - if (size < sizeof(PacketSize) + sizeof(PacketType) || size > SEND_MTU) return false; + if (size < sizeof(PacketSize) + sizeof(PacketType) || size > this->limit) return false; this->buffer.resize(size); this->pos = sizeof(PacketSize); diff --git a/src/network/core/packet.h b/src/network/core/packet.h index 1ac7f18a18..db051005c5 100644 --- a/src/network/core/packet.h +++ b/src/network/core/packet.h @@ -25,10 +25,11 @@ typedef uint8 PacketType; ///< Identifier for the packet * Internal entity of a packet. As everything is sent as a packet, * all network communication will need to call the functions that * populate the packet. - * Every packet can be at most SEND_MTU bytes. Overflowing this - * limit will give an assertion when sending (i.e. writing) the - * packet. Reading past the size of the packet when receiving - * will return all 0 values and "" in case of the string. + * Every packet can be at most a limited number bytes set in the + * constructor. Overflowing this limit will give an assertion when + * sending (i.e. writing) the packet. Reading past the size of the + * packet when receiving will return all 0 values and "" in case of + * the string. * * --- Points of attention --- * - all > 1 byte integral values are written in little endian, @@ -47,13 +48,15 @@ private: PacketSize pos; /** The buffer of this packet. */ std::vector buffer; + /** The limit for the packet size. */ + size_t limit; /** Socket we're associated with. */ NetworkSocketHandler *cs; public: - Packet(NetworkSocketHandler *cs, size_t initial_read_size = sizeof(PacketSize)); - Packet(PacketType type); + Packet(NetworkSocketHandler *cs, size_t limit, size_t initial_read_size = sizeof(PacketSize)); + Packet(PacketType type, size_t limit = SEND_MTU); static void AddToQueue(Packet **queue, Packet *packet); static Packet *PopFromQueue(Packet **queue); diff --git a/src/network/core/tcp.cpp b/src/network/core/tcp.cpp index a749b6195b..be70efdd2f 100644 --- a/src/network/core/tcp.cpp +++ b/src/network/core/tcp.cpp @@ -126,7 +126,7 @@ Packet *NetworkTCPSocketHandler::ReceivePacket() if (!this->IsConnected()) return nullptr; if (this->packet_recv == nullptr) { - this->packet_recv = new Packet(this); + this->packet_recv = new Packet(this, SEND_MTU); } Packet *p = this->packet_recv; diff --git a/src/network/core/udp.cpp b/src/network/core/udp.cpp index 3bd2151fe1..8872fa295a 100644 --- a/src/network/core/udp.cpp +++ b/src/network/core/udp.cpp @@ -119,7 +119,8 @@ void NetworkUDPSocketHandler::ReceivePackets() struct sockaddr_storage client_addr; memset(&client_addr, 0, sizeof(client_addr)); - Packet p(this, SEND_MTU); + /* The limit is SEND_MTU, but also allocate that much as we need to read the whole packet in one go. */ + Packet p(this, SEND_MTU, SEND_MTU); socklen_t client_len = sizeof(client_addr); /* Try to receive anything */ From d6000c2ec5f61c599d8859b981f2dac6a92e0755 Mon Sep 17 00:00:00 2001 From: Rubidium Date: Sun, 18 Apr 2021 14:49:39 +0200 Subject: [PATCH 165/268] Codechange: differentiate between UDP, TCP and compatibility MTU values --- src/network/core/config.h | 8 +++++--- src/network/core/packet.cpp | 2 +- src/network/core/packet.h | 2 +- src/network/core/tcp.cpp | 2 +- src/network/core/udp.cpp | 4 ++-- src/network/network_admin.cpp | 6 +++--- src/network/network_content.cpp | 8 ++++---- src/network/network_udp.cpp | 6 +++--- 8 files changed, 20 insertions(+), 18 deletions(-) diff --git a/src/network/core/config.h b/src/network/core/config.h index 1483419ea5..572b4720db 100644 --- a/src/network/core/config.h +++ b/src/network/core/config.h @@ -30,7 +30,9 @@ static const uint16 NETWORK_DEFAULT_PORT = 3979; ///< The defau static const uint16 NETWORK_ADMIN_PORT = 3977; ///< The default port for admin network static const uint16 NETWORK_DEFAULT_DEBUGLOG_PORT = 3982; ///< The default port debug-log is sent to (TCP) -static const uint16 SEND_MTU = 1460; ///< Number of bytes we can pack in a single packet +static const uint16 UDP_MTU = 1460; ///< Number of bytes we can pack in a single UDP packet +static const uint16 TCP_MTU = 1460; ///< Number of bytes we can pack in a single TCP packet +static const uint16 COMPAT_MTU = 1460; ///< Number of bytes we can pack in a single packet for backward compatibility static const byte NETWORK_GAME_ADMIN_VERSION = 1; ///< What version of the admin network do we use? static const byte NETWORK_GAME_INFO_VERSION = 4; ///< What version of game-info do we use? @@ -46,14 +48,14 @@ static const uint NETWORK_PASSWORD_LENGTH = 33; ///< The maxim static const uint NETWORK_CLIENTS_LENGTH = 200; ///< The maximum length for the list of clients that controls a company, in bytes including '\0' static const uint NETWORK_CLIENT_NAME_LENGTH = 25; ///< The maximum length of a client's name, in bytes including '\0' static const uint NETWORK_RCONCOMMAND_LENGTH = 500; ///< The maximum length of a rconsole command, in bytes including '\0' -static const uint NETWORK_GAMESCRIPT_JSON_LENGTH = SEND_MTU - 3; ///< The maximum length of a gamescript json string, in bytes including '\0'. Must not be longer than SEND_MTU including header (3 bytes) +static const uint NETWORK_GAMESCRIPT_JSON_LENGTH = COMPAT_MTU-3; ///< The maximum length of a gamescript json string, in bytes including '\0'. Must not be longer than COMPAT_MTU including header (3 bytes) static const uint NETWORK_CHAT_LENGTH = 900; ///< The maximum length of a chat message, in bytes including '\0' static const uint NETWORK_GRF_NAME_LENGTH = 80; ///< Maximum length of the name of a GRF /** * Maximum number of GRFs that can be sent. - * This limit is reached when PACKET_UDP_SERVER_RESPONSE reaches the maximum size of SEND_MTU bytes. + * This limit is reached when PACKET_UDP_SERVER_RESPONSE reaches the maximum size of UDP_MTU bytes. */ static const uint NETWORK_MAX_GRF_COUNT = 62; diff --git a/src/network/core/packet.cpp b/src/network/core/packet.cpp index 39531c0a40..40e6640550 100644 --- a/src/network/core/packet.cpp +++ b/src/network/core/packet.cpp @@ -39,7 +39,7 @@ Packet::Packet(NetworkSocketHandler *cs, size_t limit, size_t initial_read_size) /** * Creates a packet to send * @param type The type of the packet to send - * @param limit The maximum number of bytes the packet may have. Default is SEND_MTU. + * @param limit The maximum number of bytes the packet may have. Default is COMPAT_MTU. * Be careful of compatibility with older clients/servers when changing * the limit as it might break things if the other side is not expecting * much larger packets than what they support. diff --git a/src/network/core/packet.h b/src/network/core/packet.h index db051005c5..d55dad316a 100644 --- a/src/network/core/packet.h +++ b/src/network/core/packet.h @@ -56,7 +56,7 @@ private: public: Packet(NetworkSocketHandler *cs, size_t limit, size_t initial_read_size = sizeof(PacketSize)); - Packet(PacketType type, size_t limit = SEND_MTU); + Packet(PacketType type, size_t limit = COMPAT_MTU); static void AddToQueue(Packet **queue, Packet *packet); static Packet *PopFromQueue(Packet **queue); diff --git a/src/network/core/tcp.cpp b/src/network/core/tcp.cpp index be70efdd2f..72e66a0b55 100644 --- a/src/network/core/tcp.cpp +++ b/src/network/core/tcp.cpp @@ -126,7 +126,7 @@ Packet *NetworkTCPSocketHandler::ReceivePacket() if (!this->IsConnected()) return nullptr; if (this->packet_recv == nullptr) { - this->packet_recv = new Packet(this, SEND_MTU); + this->packet_recv = new Packet(this, TCP_MTU); } Packet *p = this->packet_recv; diff --git a/src/network/core/udp.cpp b/src/network/core/udp.cpp index 8872fa295a..e8299f7b62 100644 --- a/src/network/core/udp.cpp +++ b/src/network/core/udp.cpp @@ -119,8 +119,8 @@ void NetworkUDPSocketHandler::ReceivePackets() struct sockaddr_storage client_addr; memset(&client_addr, 0, sizeof(client_addr)); - /* The limit is SEND_MTU, but also allocate that much as we need to read the whole packet in one go. */ - Packet p(this, SEND_MTU, SEND_MTU); + /* The limit is UDP_MTU, but also allocate that much as we need to read the whole packet in one go. */ + Packet p(this, UDP_MTU, UDP_MTU); socklen_t client_len = sizeof(client_addr); /* Try to receive anything */ diff --git a/src/network/network_admin.cpp b/src/network/network_admin.cpp index 057ad59883..1b86c3e561 100644 --- a/src/network/network_admin.cpp +++ b/src/network/network_admin.cpp @@ -560,8 +560,8 @@ NetworkRecvStatus ServerNetworkAdminSocketHandler::SendConsole(const char *origi /* If the length of both strings, plus the 2 '\0' terminations and 3 bytes of the packet * are bigger than the MTU, just ignore the message. Better safe than sorry. It should * never occur though as the longest strings are chat messages, which are still 30% - * smaller than SEND_MTU. */ - if (strlen(origin) + strlen(string) + 2 + 3 >= SEND_MTU) return NETWORK_RECV_STATUS_OKAY; + * smaller than COMPAT_MTU. */ + if (strlen(origin) + strlen(string) + 2 + 3 >= COMPAT_MTU) return NETWORK_RECV_STATUS_OKAY; Packet *p = new Packet(ADMIN_PACKET_SERVER_CONSOLE); @@ -610,7 +610,7 @@ NetworkRecvStatus ServerNetworkAdminSocketHandler::SendCmdNames() for (uint i = 0; i < CMD_END; i++) { const char *cmdname = GetCommandName(i); - /* Should SEND_MTU be exceeded, start a new packet + /* Should COMPAT_MTU be exceeded, start a new packet * (magic 5: 1 bool "more data" and one uint16 "command id", one * byte for string '\0' termination and 1 bool "no more data" */ if (p->CanWriteToPacket(strlen(cmdname) + 5)) { diff --git a/src/network/network_content.cpp b/src/network/network_content.cpp index 5292252354..fb95ff72b4 100644 --- a/src/network/network_content.cpp +++ b/src/network/network_content.cpp @@ -221,9 +221,9 @@ void ClientNetworkContentSocketHandler::RequestContentList(uint count, const Con * A packet begins with the packet size and a byte for the type. * Then this packet adds a uint16 for the count in this packet. * The rest of the packet can be used for the IDs. */ - uint p_count = std::min(count, (SEND_MTU - sizeof(PacketSize) - sizeof(byte) - sizeof(uint16)) / sizeof(uint32)); + uint p_count = std::min(count, (COMPAT_MTU - sizeof(PacketSize) - sizeof(byte) - sizeof(uint16)) / sizeof(uint32)); - Packet *p = new Packet(PACKET_CONTENT_CLIENT_INFO_ID); + Packet *p = new Packet(PACKET_CONTENT_CLIENT_INFO_ID, COMPAT_MTU); p->Send_uint16(p_count); for (uint i = 0; i < p_count; i++) { @@ -248,7 +248,7 @@ void ClientNetworkContentSocketHandler::RequestContentList(ContentVector *cv, bo this->Connect(); assert(cv->size() < 255); - assert(cv->size() < (SEND_MTU - sizeof(PacketSize) - sizeof(byte) - sizeof(uint8)) / + assert(cv->size() < (COMPAT_MTU - sizeof(PacketSize) - sizeof(byte) - sizeof(uint8)) / (sizeof(uint8) + sizeof(uint32) + (send_md5sum ? /*sizeof(ContentInfo::md5sum)*/16 : 0))); Packet *p = new Packet(send_md5sum ? PACKET_CONTENT_CLIENT_INFO_EXTID_MD5 : PACKET_CONTENT_CLIENT_INFO_EXTID); @@ -363,7 +363,7 @@ void ClientNetworkContentSocketHandler::DownloadSelectedContentFallback(const Co * A packet begins with the packet size and a byte for the type. * Then this packet adds a uint16 for the count in this packet. * The rest of the packet can be used for the IDs. */ - uint p_count = std::min(count, (SEND_MTU - sizeof(PacketSize) - sizeof(byte) - sizeof(uint16)) / sizeof(uint32)); + uint p_count = std::min(count, (COMPAT_MTU - sizeof(PacketSize) - sizeof(byte) - sizeof(uint16)) / sizeof(uint32)); Packet *p = new Packet(PACKET_CONTENT_CLIENT_CONTENT); p->Send_uint16(p_count); diff --git a/src/network/network_udp.cpp b/src/network/network_udp.cpp index aa34515bdd..f7db689d4b 100644 --- a/src/network/network_udp.cpp +++ b/src/network/network_udp.cpp @@ -256,10 +256,10 @@ void ServerNetworkUDPSocketHandler::Receive_CLIENT_DETAIL_INFO(Packet *p, Networ /** * A client has requested the names of some NewGRFs. * - * Replying this can be tricky as we have a limit of SEND_MTU bytes + * Replying this can be tricky as we have a limit of UDP_MTU bytes * in the reply packet and we can send up to 100 bytes per NewGRF * (GRF ID, MD5sum and NETWORK_GRF_NAME_LENGTH bytes for the name). - * As SEND_MTU is _much_ less than 100 * NETWORK_MAX_GRF_COUNT, it + * As UDP_MTU is _much_ less than 100 * NETWORK_MAX_GRF_COUNT, it * could be that a packet overflows. To stop this we only reply * with the first N NewGRFs so that if the first N + 1 NewGRFs * would be sent, the packet overflows. @@ -295,7 +295,7 @@ void ServerNetworkUDPSocketHandler::Receive_CLIENT_GET_NEWGRFS(Packet *p, Networ * The name could be an empty string, if so take the filename. */ packet_len += sizeof(c.grfid) + sizeof(c.md5sum) + std::min(strlen(f->GetName()) + 1, (size_t)NETWORK_GRF_NAME_LENGTH); - if (packet_len > SEND_MTU - 4) { // 4 is 3 byte header + grf count in reply + if (packet_len > UDP_MTU - 4) { // 4 is 3 byte header + grf count in reply break; } in_reply[in_reply_count] = f; From 21f58ab437992761caefd11f931c45086cc3d216 Mon Sep 17 00:00:00 2001 From: Rubidium Date: Sun, 18 Apr 2021 14:56:25 +0200 Subject: [PATCH 166/268] Change: use 32 KiB packets to transfer the savegame --- src/network/core/config.h | 16 +++++++++++++++- src/network/network_server.cpp | 4 ++-- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/src/network/core/config.h b/src/network/core/config.h index 572b4720db..866d1791d4 100644 --- a/src/network/core/config.h +++ b/src/network/core/config.h @@ -31,7 +31,21 @@ static const uint16 NETWORK_ADMIN_PORT = 3977; ///< The defau static const uint16 NETWORK_DEFAULT_DEBUGLOG_PORT = 3982; ///< The default port debug-log is sent to (TCP) static const uint16 UDP_MTU = 1460; ///< Number of bytes we can pack in a single UDP packet -static const uint16 TCP_MTU = 1460; ///< Number of bytes we can pack in a single TCP packet +/* + * Technically a TCP packet could become 64kiB, however the high bit is kept so it becomes possible in the future + * to go to (significantly) larger packets if needed. This would entail a strategy such as employed for UTF-8. + * + * Packets up to 32 KiB have the high bit not set: + * 00000000 00000000 0bbbbbbb aaaaaaaa -> aaaaaaaa 0bbbbbbb + * Send_uint16(GB(size, 0, 15) + * + * Packets up to 1 GiB, first uint16 has high bit set so it knows to read a + * next uint16 for the remaining bits of the size. + * 00dddddd cccccccc bbbbbbbb aaaaaaaa -> cccccccc 10dddddd aaaaaaaa bbbbbbbb + * Send_uint16(GB(size, 16, 14) | 0b10 << 14) + * Send_uint16(GB(size, 0, 16)) + */ +static const uint16 TCP_MTU = 32767; ///< Number of bytes we can pack in a single TCP packet static const uint16 COMPAT_MTU = 1460; ///< Number of bytes we can pack in a single packet for backward compatibility static const byte NETWORK_GAME_ADMIN_VERSION = 1; ///< What version of the admin network do we use? diff --git a/src/network/network_server.cpp b/src/network/network_server.cpp index 5489db8480..7d10f04fe3 100644 --- a/src/network/network_server.cpp +++ b/src/network/network_server.cpp @@ -158,7 +158,7 @@ struct PacketWriter : SaveFilter { /* We want to abort the saving when the socket is closed. */ if (this->cs == nullptr) SlError(STR_NETWORK_ERROR_LOSTCONNECTION); - if (this->current == nullptr) this->current = new Packet(PACKET_SERVER_MAP_DATA); + if (this->current == nullptr) this->current = new Packet(PACKET_SERVER_MAP_DATA, TCP_MTU); std::lock_guard lock(this->mutex); @@ -169,7 +169,7 @@ struct PacketWriter : SaveFilter { if (!this->current->CanWriteToPacket(1)) { this->AppendQueue(); - if (buf != bufe) this->current = new Packet(PACKET_SERVER_MAP_DATA); + if (buf != bufe) this->current = new Packet(PACKET_SERVER_MAP_DATA, TCP_MTU); } } From a3c9eca722e73b823c5c47f49da2fb0ba754ee5b Mon Sep 17 00:00:00 2001 From: Rubidium Date: Sun, 18 Apr 2021 14:59:23 +0200 Subject: [PATCH 167/268] Change: use 32 KiB packets to send requests to the content server --- src/network/network_content.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/network/network_content.cpp b/src/network/network_content.cpp index fb95ff72b4..a208f3c6e2 100644 --- a/src/network/network_content.cpp +++ b/src/network/network_content.cpp @@ -221,9 +221,9 @@ void ClientNetworkContentSocketHandler::RequestContentList(uint count, const Con * A packet begins with the packet size and a byte for the type. * Then this packet adds a uint16 for the count in this packet. * The rest of the packet can be used for the IDs. */ - uint p_count = std::min(count, (COMPAT_MTU - sizeof(PacketSize) - sizeof(byte) - sizeof(uint16)) / sizeof(uint32)); + uint p_count = std::min(count, (TCP_MTU - sizeof(PacketSize) - sizeof(byte) - sizeof(uint16)) / sizeof(uint32)); - Packet *p = new Packet(PACKET_CONTENT_CLIENT_INFO_ID, COMPAT_MTU); + Packet *p = new Packet(PACKET_CONTENT_CLIENT_INFO_ID, TCP_MTU); p->Send_uint16(p_count); for (uint i = 0; i < p_count; i++) { @@ -248,10 +248,10 @@ void ClientNetworkContentSocketHandler::RequestContentList(ContentVector *cv, bo this->Connect(); assert(cv->size() < 255); - assert(cv->size() < (COMPAT_MTU - sizeof(PacketSize) - sizeof(byte) - sizeof(uint8)) / + assert(cv->size() < (TCP_MTU - sizeof(PacketSize) - sizeof(byte) - sizeof(uint8)) / (sizeof(uint8) + sizeof(uint32) + (send_md5sum ? /*sizeof(ContentInfo::md5sum)*/16 : 0))); - Packet *p = new Packet(send_md5sum ? PACKET_CONTENT_CLIENT_INFO_EXTID_MD5 : PACKET_CONTENT_CLIENT_INFO_EXTID); + Packet *p = new Packet(send_md5sum ? PACKET_CONTENT_CLIENT_INFO_EXTID_MD5 : PACKET_CONTENT_CLIENT_INFO_EXTID, TCP_MTU); p->Send_uint8((uint8)cv->size()); for (const ContentInfo *ci : *cv) { @@ -363,9 +363,9 @@ void ClientNetworkContentSocketHandler::DownloadSelectedContentFallback(const Co * A packet begins with the packet size and a byte for the type. * Then this packet adds a uint16 for the count in this packet. * The rest of the packet can be used for the IDs. */ - uint p_count = std::min(count, (COMPAT_MTU - sizeof(PacketSize) - sizeof(byte) - sizeof(uint16)) / sizeof(uint32)); + uint p_count = std::min(count, (TCP_MTU - sizeof(PacketSize) - sizeof(byte) - sizeof(uint16)) / sizeof(uint32)); - Packet *p = new Packet(PACKET_CONTENT_CLIENT_CONTENT); + Packet *p = new Packet(PACKET_CONTENT_CLIENT_CONTENT, TCP_MTU); p->Send_uint16(p_count); for (uint i = 0; i < p_count; i++) { From 5afb09008286759fce9fcf109881eabc078b7301 Mon Sep 17 00:00:00 2001 From: rubidium42 Date: Sun, 25 Apr 2021 19:59:19 +0200 Subject: [PATCH 168/268] Change: [Network] Remove now defunct savegame transfer packet limiter --- src/network/network_server.cpp | 71 ++++++++-------------------------- 1 file changed, 17 insertions(+), 54 deletions(-) diff --git a/src/network/network_server.cpp b/src/network/network_server.cpp index 7d10f04fe3..67952bca69 100644 --- a/src/network/network_server.cpp +++ b/src/network/network_server.cpp @@ -112,25 +112,27 @@ struct PacketWriter : SaveFilter { } /** - * Checks whether there are packets. - * It's not 100% threading safe, but this is only asked for when checking - * whether there still is something to send. Then another call will be made - * to actually get the Packet, which will be the only one popping packets - * and thus eventually setting this on false. + * Transfer all packets from here to the network's queue while holding + * the lock on our mutex. + * @param socket The network socket to write to. + * @return True iff the last packet of the map has been sent. */ - bool HasPackets() + bool TransferToNetworkQueue(ServerNetworkGameSocketHandler *socket) { - return this->packets != nullptr; - } + /* Unsafe check for the queue being empty or not. */ + if (this->packets == nullptr) return false; - /** - * Pop a single created packet from the queue with packets. - */ - Packet *PopPacket() - { std::lock_guard lock(this->mutex); - return Packet::PopFromQueue(&this->packets); + while (this->packets != nullptr) { + Packet *p = Packet::PopFromQueue(&this->packets); + bool last_packet = p->GetPacketType() == PACKET_SERVER_MAP_DONE; + socket->SendPacket(p); + + if (last_packet) return true; + } + + return false; } /** Append the current packet to the queue. */ @@ -592,8 +594,6 @@ void ServerNetworkGameSocketHandler::CheckNextClientToSendMap(NetworkClientSocke /** This sends the map to the client */ NetworkRecvStatus ServerNetworkGameSocketHandler::SendMap() { - static uint16 sent_packets; // How many packets we did send successfully last time - if (this->status < STATUS_AUTHORIZED) { /* Illegal call, return error and ignore the packet */ return this->SendError(NETWORK_ERROR_NOT_AUTHORIZED); @@ -613,28 +613,12 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::SendMap() this->last_frame = _frame_counter; this->last_frame_server = _frame_counter; - sent_packets = 4; // We start with trying 4 packets - /* Make a dump of the current game */ if (SaveWithFilter(this->savegame, true) != SL_OK) usererror("network savedump failed"); } if (this->status == STATUS_MAP) { - bool last_packet = false; - bool has_packets = false; - - for (uint i = 0; (has_packets = this->savegame->HasPackets()) && i < sent_packets; i++) { - Packet *p = this->savegame->PopPacket(); - last_packet = p->GetPacketType() == PACKET_SERVER_MAP_DONE; - - this->SendPacket(p); - - if (last_packet) { - /* There is no more data, so break the for */ - break; - } - } - + bool last_packet = this->savegame->TransferToNetworkQueue(this); if (last_packet) { /* Done reading, make sure saving is done as well */ this->savegame->Destroy(); @@ -646,27 +630,6 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::SendMap() this->CheckNextClientToSendMap(); } - - switch (this->SendPackets()) { - case SPS_CLOSED: - return NETWORK_RECV_STATUS_CONN_LOST; - - case SPS_ALL_SENT: - /* All are sent, increase the sent_packets but do not overflow! */ - if (has_packets && sent_packets < std::numeric_limits::max() / 2) { - sent_packets *= 2; - } - break; - - case SPS_PARTLY_SENT: - /* Only a part is sent; leave the transmission state. */ - break; - - case SPS_NONE_SENT: - /* Not everything is sent, decrease the sent_packets */ - if (sent_packets > 1) sent_packets /= 2; - break; - } } return NETWORK_RECV_STATUS_OKAY; } From 43b6f6915b5bfb1685c70cc1898e55cbe2cec03a Mon Sep 17 00:00:00 2001 From: Charles Pigott Date: Sun, 25 Apr 2021 23:30:32 +0100 Subject: [PATCH 169/268] Fix 8a95fee4: Missing initialiser in Packet constructor --- src/network/core/packet.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/network/core/packet.cpp b/src/network/core/packet.cpp index 40e6640550..644490e0fb 100644 --- a/src/network/core/packet.cpp +++ b/src/network/core/packet.cpp @@ -44,7 +44,7 @@ Packet::Packet(NetworkSocketHandler *cs, size_t limit, size_t initial_read_size) * the limit as it might break things if the other side is not expecting * much larger packets than what they support. */ -Packet::Packet(PacketType type, size_t limit) : next(nullptr), pos(0), cs(nullptr) +Packet::Packet(PacketType type, size_t limit) : next(nullptr), pos(0), limit(limit), cs(nullptr) { /* Allocate space for the the size so we can write that in just before sending the packet. */ this->Send_uint16(0); From 8ea06128be7b33749546500c9f2159b9d43f07a1 Mon Sep 17 00:00:00 2001 From: translators Date: Mon, 26 Apr 2021 17:53:20 +0000 Subject: [PATCH 170/268] Update: Translations from eints english (us): 39 changes by 2TallTyler korean: 44 changes by telk5093 german: 43 changes by danidoedel russian: 7 changes by Ln-Wolf finnish: 39 changes by hpiirai --- src/lang/english_US.txt | 41 +++++++++++++++++++++++++++++++-- src/lang/finnish.txt | 41 +++++++++++++++++++++++++++++++-- src/lang/german.txt | 45 ++++++++++++++++++++++++++++++++++-- src/lang/korean.txt | 51 +++++++++++++++++++++++++++++++++++------ src/lang/russian.txt | 8 ++++++- 5 files changed, 172 insertions(+), 14 deletions(-) diff --git a/src/lang/english_US.txt b/src/lang/english_US.txt index 6b8b77bacb..444e46462f 100644 --- a/src/lang/english_US.txt +++ b/src/lang/english_US.txt @@ -953,7 +953,7 @@ STR_GAME_OPTIONS_CURRENCY_MYR :Malaysian Ringg STR_GAME_OPTIONS_ROAD_VEHICLES_DROPDOWN_LEFT :Drive on left STR_GAME_OPTIONS_ROAD_VEHICLES_DROPDOWN_RIGHT :Drive on right -STR_GAME_OPTIONS_TOWN_NAMES_FRAME :{BLACK}Town names +STR_GAME_OPTIONS_TOWN_NAMES_FRAME :{BLACK}Town names: STR_GAME_OPTIONS_TOWN_NAMES_DROPDOWN_TOOLTIP :{BLACK}Select style of town names ############ start of townname region @@ -1991,6 +1991,8 @@ STR_FACE_TIE :Tie: STR_FACE_EARRING :Earring: STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}Change tie or earring +STR_NETWORK_SERVER_VISIBILITY_PRIVATE :Private +STR_NETWORK_SERVER_VISIBILITY_PUBLIC :Public # Network server list STR_NETWORK_SERVER_LIST_CAPTION :{WHITE}Multiplayer @@ -2054,6 +2056,8 @@ STR_NETWORK_START_SERVER_NEW_GAME_NAME_TOOLTIP :{BLACK}The game STR_NETWORK_START_SERVER_SET_PASSWORD :{BLACK}Set password STR_NETWORK_START_SERVER_PASSWORD_TOOLTIP :{BLACK}Protect your game with a password if you don't want it to be publicly accessible +STR_NETWORK_START_SERVER_VISIBILITY_LABEL :{BLACK}Visibility +STR_NETWORK_START_SERVER_VISIBILITY_TOOLTIP :{BLACK}Whether other people can see your server in the public listing STR_NETWORK_START_SERVER_CLIENTS_SELECT :{BLACK}{NUM} client{P "" s} STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS :{BLACK}Maximum number of clients: STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS_TOOLTIP :{BLACK}Choose the maximum number of clients. Not all slots need to be filled @@ -2117,11 +2121,44 @@ STR_NETWORK_NEED_GAME_PASSWORD_CAPTION :{WHITE}Server i STR_NETWORK_NEED_COMPANY_PASSWORD_CAPTION :{WHITE}Company is protected. Enter password # Network company list added strings -STR_NETWORK_COMPANY_LIST_CLIENT_LIST :Client list +STR_NETWORK_COMPANY_LIST_CLIENT_LIST :Online players # Network client list +STR_NETWORK_CLIENT_LIST_CAPTION :{WHITE}Multiplayer +STR_NETWORK_CLIENT_LIST_SERVER :{BLACK}Server +STR_NETWORK_CLIENT_LIST_SERVER_NAME :{BLACK}Name +STR_NETWORK_CLIENT_LIST_SERVER_NAME_TOOLTIP :{BLACK}Name of the server you are playing on +STR_NETWORK_CLIENT_LIST_SERVER_NAME_EDIT_TOOLTIP :{BLACK}Edit the name of your server +STR_NETWORK_CLIENT_LIST_SERVER_NAME_QUERY_CAPTION :Name of the server +STR_NETWORK_CLIENT_LIST_SERVER_VISIBILITY :{BLACK}Visibility +STR_NETWORK_CLIENT_LIST_SERVER_VISIBILITY_TOOLTIP :{BLACK}Whether other people can see your server in the public listing +STR_NETWORK_CLIENT_LIST_PLAYER :{BLACK}Player +STR_NETWORK_CLIENT_LIST_PLAYER_NAME :{BLACK}Name +STR_NETWORK_CLIENT_LIST_PLAYER_NAME_TOOLTIP :{BLACK}Your player name +STR_NETWORK_CLIENT_LIST_PLAYER_NAME_EDIT_TOOLTIP :{BLACK}Edit your player name +STR_NETWORK_CLIENT_LIST_PLAYER_NAME_QUERY_CAPTION :Your player name +STR_NETWORK_CLIENT_LIST_ADMIN_CLIENT_TOOLTIP :{BLACK}Administrative actions to perform for this client +STR_NETWORK_CLIENT_LIST_ADMIN_COMPANY_TOOLTIP :{BLACK}Administrative actions to perform for this company +STR_NETWORK_CLIENT_LIST_JOIN_TOOLTIP :{BLACK}Join this company +STR_NETWORK_CLIENT_LIST_CHAT_CLIENT_TOOLTIP :{BLACK}Send a message to this player +STR_NETWORK_CLIENT_LIST_CHAT_COMPANY_TOOLTIP :{BLACK}Send a message to all players of this company +STR_NETWORK_CLIENT_LIST_CHAT_SPECTATOR_TOOLTIP :{BLACK}Send a message to all spectators +STR_NETWORK_CLIENT_LIST_SPECTATORS :Spectators +STR_NETWORK_CLIENT_LIST_NEW_COMPANY :(New company) +STR_NETWORK_CLIENT_LIST_NEW_COMPANY_TOOLTIP :{BLACK}Create a new company and join it +STR_NETWORK_CLIENT_LIST_PLAYER_ICON_SELF_TOOLTIP :{BLACK}This is you +STR_NETWORK_CLIENT_LIST_PLAYER_ICON_HOST_TOOLTIP :{BLACK}This is the host of the game +STR_NETWORK_CLIENT_LIST_ADMIN_CLIENT_KICK :Kick +STR_NETWORK_CLIENT_LIST_ADMIN_CLIENT_BAN :Ban +STR_NETWORK_CLIENT_LIST_ADMIN_COMPANY_RESET :Delete +STR_NETWORK_CLIENT_LIST_ADMIN_COMPANY_UNLOCK :Password unlock +STR_NETWORK_CLIENT_LIST_ASK_CAPTION :{WHITE}Admin action +STR_NETWORK_CLIENT_LIST_ASK_CLIENT_KICK :{YELLOW}Are you sure you want to kick player '{STRING}'? +STR_NETWORK_CLIENT_LIST_ASK_CLIENT_BAN :{YELLOW}Are you sure you want to ban player '{STRING}'? +STR_NETWORK_CLIENT_LIST_ASK_COMPANY_RESET :{YELLOW}Are you sure you want to delete company '{COMPANY}'? +STR_NETWORK_CLIENT_LIST_ASK_COMPANY_UNLOCK :{YELLOW}Are you sure you want to reset the password of company '{COMPANY}'? STR_NETWORK_SERVER :Server STR_NETWORK_CLIENT :Client diff --git a/src/lang/finnish.txt b/src/lang/finnish.txt index e4896cd45e..4e795f5949 100644 --- a/src/lang/finnish.txt +++ b/src/lang/finnish.txt @@ -953,7 +953,7 @@ STR_GAME_OPTIONS_CURRENCY_MYR :Malesian ringgi STR_GAME_OPTIONS_ROAD_VEHICLES_DROPDOWN_LEFT :Vasemmanpuolinen liikenne STR_GAME_OPTIONS_ROAD_VEHICLES_DROPDOWN_RIGHT :Oikeanpuolinen liikenne -STR_GAME_OPTIONS_TOWN_NAMES_FRAME :{BLACK}Kuntien nimet +STR_GAME_OPTIONS_TOWN_NAMES_FRAME :{BLACK}Kuntien nimet: STR_GAME_OPTIONS_TOWN_NAMES_DROPDOWN_TOOLTIP :{BLACK}Valitse kuntien nimien tyyli ############ start of townname region @@ -1991,6 +1991,8 @@ STR_FACE_TIE :Solmio: STR_FACE_EARRING :Korvakoru: STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}Vaihda solmio tai korvakoru +STR_NETWORK_SERVER_VISIBILITY_PRIVATE :Yksityinen +STR_NETWORK_SERVER_VISIBILITY_PUBLIC :Julkinen # Network server list STR_NETWORK_SERVER_LIST_CAPTION :{WHITE}Moninpeli @@ -2054,6 +2056,8 @@ STR_NETWORK_START_SERVER_NEW_GAME_NAME_TOOLTIP :{BLACK}Nimi nä STR_NETWORK_START_SERVER_SET_PASSWORD :{BLACK}Aseta salasana STR_NETWORK_START_SERVER_PASSWORD_TOOLTIP :{BLACK}Jos peliin ei halua ulkopuolisia, voi sen suojata salasanalla +STR_NETWORK_START_SERVER_VISIBILITY_LABEL :{BLACK}Näkyvyys +STR_NETWORK_START_SERVER_VISIBILITY_TOOLTIP :{BLACK}Näkyykö palvelimesi muille julkisessa listauksessa STR_NETWORK_START_SERVER_CLIENTS_SELECT :{BLACK}{NUM} asiakas{P "" ta} STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS :{BLACK}Pelaajien enimmäismäärä: STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS_TOOLTIP :{BLACK}Pelaajien enimmäismäärä. Pelissä voi olla myös vähemmän pelaajia @@ -2117,11 +2121,44 @@ STR_NETWORK_NEED_GAME_PASSWORD_CAPTION :{WHITE}Palvelin STR_NETWORK_NEED_COMPANY_PASSWORD_CAPTION :{WHITE}Yhtiö on suojattu. Anna salasana # Network company list added strings -STR_NETWORK_COMPANY_LIST_CLIENT_LIST :Asiakaslista +STR_NETWORK_COMPANY_LIST_CLIENT_LIST :Kytkeytyneet pelaajat # Network client list +STR_NETWORK_CLIENT_LIST_CAPTION :{WHITE}Moninpeli +STR_NETWORK_CLIENT_LIST_SERVER :{BLACK}Palvelin +STR_NETWORK_CLIENT_LIST_SERVER_NAME :{BLACK}Nimi +STR_NETWORK_CLIENT_LIST_SERVER_NAME_TOOLTIP :{BLACK}Sen palvelimen nimi, jolla pelaat +STR_NETWORK_CLIENT_LIST_SERVER_NAME_EDIT_TOOLTIP :{BLACK}Muokkaa palvelimesi nimeä +STR_NETWORK_CLIENT_LIST_SERVER_NAME_QUERY_CAPTION :Palvelimen nimi +STR_NETWORK_CLIENT_LIST_SERVER_VISIBILITY :{BLACK}Näkyvyys +STR_NETWORK_CLIENT_LIST_SERVER_VISIBILITY_TOOLTIP :{BLACK}Näkyykö palvelimesi muille julkisessa listauksessa +STR_NETWORK_CLIENT_LIST_PLAYER :{BLACK}Pelaaja +STR_NETWORK_CLIENT_LIST_PLAYER_NAME :{BLACK}Nimi +STR_NETWORK_CLIENT_LIST_PLAYER_NAME_TOOLTIP :{BLACK}Pelaajanimesi +STR_NETWORK_CLIENT_LIST_PLAYER_NAME_EDIT_TOOLTIP :{BLACK}Muokkaa pelaajanimeäsi +STR_NETWORK_CLIENT_LIST_PLAYER_NAME_QUERY_CAPTION :Pelaajanimesi +STR_NETWORK_CLIENT_LIST_ADMIN_CLIENT_TOOLTIP :{BLACK}Tähän asiakkaaseen kohdistettavat ylläpitotoiminnot +STR_NETWORK_CLIENT_LIST_ADMIN_COMPANY_TOOLTIP :{BLACK}Tähän yhtiöön kohdistettavat ylläpitotoiminnot +STR_NETWORK_CLIENT_LIST_JOIN_TOOLTIP :{BLACK}Liity tähän yhtiöön +STR_NETWORK_CLIENT_LIST_CHAT_CLIENT_TOOLTIP :{BLACK}Lähetä tälle pelaajalle viesti +STR_NETWORK_CLIENT_LIST_CHAT_COMPANY_TOOLTIP :{BLACK}Lähetä viesti kaikille tämän yhtiön pelaajille +STR_NETWORK_CLIENT_LIST_CHAT_SPECTATOR_TOOLTIP :{BLACK}Lähetä viesti kaikille katsojille +STR_NETWORK_CLIENT_LIST_SPECTATORS :Katsojat +STR_NETWORK_CLIENT_LIST_NEW_COMPANY :(Uusi yhtiö) +STR_NETWORK_CLIENT_LIST_NEW_COMPANY_TOOLTIP :{BLACK}Perusta uusi yhtiö ja liity siihen +STR_NETWORK_CLIENT_LIST_PLAYER_ICON_SELF_TOOLTIP :{BLACK}Tämä olet sinä +STR_NETWORK_CLIENT_LIST_PLAYER_ICON_HOST_TOOLTIP :{BLACK}Tämä on pelin ylläpitäjä +STR_NETWORK_CLIENT_LIST_ADMIN_CLIENT_KICK :Potki +STR_NETWORK_CLIENT_LIST_ADMIN_CLIENT_BAN :Estä +STR_NETWORK_CLIENT_LIST_ADMIN_COMPANY_RESET :Poista +STR_NETWORK_CLIENT_LIST_ADMIN_COMPANY_UNLOCK :Poista salasanalukitus +STR_NETWORK_CLIENT_LIST_ASK_CAPTION :{WHITE}Ylläpitäjän toiminta +STR_NETWORK_CLIENT_LIST_ASK_CLIENT_KICK :{YELLOW}Haluatko varmasti potkia pelaajan ”{STRING}” pelistä? +STR_NETWORK_CLIENT_LIST_ASK_CLIENT_BAN :{YELLOW}Haluatko varmasti estää pelaajan ”{STRING}”? +STR_NETWORK_CLIENT_LIST_ASK_COMPANY_RESET :{YELLOW}Haluatko varmasti poistaa yhtiön ”{COMPANY}”? +STR_NETWORK_CLIENT_LIST_ASK_COMPANY_UNLOCK :{YELLOW}Haluatko varmasti nollata yhtiön ”{COMPANY}” salasanan? STR_NETWORK_SERVER :Palvelin STR_NETWORK_CLIENT :Pelaaja diff --git a/src/lang/german.txt b/src/lang/german.txt index 3ac9293998..2c30fea557 100644 --- a/src/lang/german.txt +++ b/src/lang/german.txt @@ -954,7 +954,7 @@ STR_GAME_OPTIONS_CURRENCY_MYR :Malaysische Rin STR_GAME_OPTIONS_ROAD_VEHICLES_DROPDOWN_LEFT :Linksverkehr STR_GAME_OPTIONS_ROAD_VEHICLES_DROPDOWN_RIGHT :Rechtsverkehr -STR_GAME_OPTIONS_TOWN_NAMES_FRAME :{BLACK}Städtenamen +STR_GAME_OPTIONS_TOWN_NAMES_FRAME :{BLACK}Städtenamen: STR_GAME_OPTIONS_TOWN_NAMES_DROPDOWN_TOOLTIP :{BLACK}Wähle die Sprache für die Städtenamen aus ############ start of townname region @@ -994,6 +994,7 @@ STR_GAME_OPTIONS_AUTOSAVE_DROPDOWN_EVERY_12_MONTHS :Alle 12 Monate STR_GAME_OPTIONS_LANGUAGE :{BLACK}Sprache STR_GAME_OPTIONS_LANGUAGE_TOOLTIP :{BLACK}Sprache für die Spieloberfläche auswählen +STR_GAME_OPTIONS_LANGUAGE_PERCENTAGE :{STRING} ({NUM}% abgeschlossen) STR_GAME_OPTIONS_FULLSCREEN :{BLACK}Vollbild STR_GAME_OPTIONS_FULLSCREEN_TOOLTIP :{BLACK}OpenTTD im Vollbildmodus spielen @@ -1991,6 +1992,8 @@ STR_FACE_TIE :Krawatte: STR_FACE_EARRING :Ohrring: STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}Krawatte oder Ohrring ändern +STR_NETWORK_SERVER_VISIBILITY_PRIVATE :Privat +STR_NETWORK_SERVER_VISIBILITY_PUBLIC :Öffentlich # Network server list STR_NETWORK_SERVER_LIST_CAPTION :{WHITE}Mehrspieler @@ -2054,6 +2057,8 @@ STR_NETWORK_START_SERVER_NEW_GAME_NAME_TOOLTIP :{BLACK}Der Name STR_NETWORK_START_SERVER_SET_PASSWORD :{BLACK}Passwort setzen STR_NETWORK_START_SERVER_PASSWORD_TOOLTIP :{BLACK}Ein Passwort verhindert, dass unbefugte Leute beitreten +STR_NETWORK_START_SERVER_VISIBILITY_LABEL :{BLACK}Sichtbarkeit +STR_NETWORK_START_SERVER_VISIBILITY_TOOLTIP :{BLACK}Ob andere Personen Ihren Server in der öffentlichen Liste sehen können STR_NETWORK_START_SERVER_CLIENTS_SELECT :{BLACK}{NUM} Teilnehmer STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS :{BLACK}Maximale Teilnehmeranzahl: STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS_TOOLTIP :{BLACK}Auswahl der maximal erlaubten Anzahl von Teilnehmern. Nicht alle Slots müssen belegt werden @@ -2117,11 +2122,44 @@ STR_NETWORK_NEED_GAME_PASSWORD_CAPTION :{WHITE}Server i STR_NETWORK_NEED_COMPANY_PASSWORD_CAPTION :{WHITE}Firma ist geschützt. Passwort eingeben: # Network company list added strings -STR_NETWORK_COMPANY_LIST_CLIENT_LIST :Teilnehmerliste +STR_NETWORK_COMPANY_LIST_CLIENT_LIST :Online-Spieler # Network client list +STR_NETWORK_CLIENT_LIST_CAPTION :{WHITE}Mehrspieler +STR_NETWORK_CLIENT_LIST_SERVER :{BLACK}Server +STR_NETWORK_CLIENT_LIST_SERVER_NAME :{BLACK}Name +STR_NETWORK_CLIENT_LIST_SERVER_NAME_TOOLTIP :{BLACK}Name des Servers auf dem Sie spielen +STR_NETWORK_CLIENT_LIST_SERVER_NAME_EDIT_TOOLTIP :{BLACK}Ihren Servernamen bearbeiten +STR_NETWORK_CLIENT_LIST_SERVER_NAME_QUERY_CAPTION :Name des Servers +STR_NETWORK_CLIENT_LIST_SERVER_VISIBILITY :{BLACK}Sichtbarkeit +STR_NETWORK_CLIENT_LIST_SERVER_VISIBILITY_TOOLTIP :{BLACK}Ob andere Personen Ihren Server in der öffentlichen Liste sehen können +STR_NETWORK_CLIENT_LIST_PLAYER :{BLACK}Spieler +STR_NETWORK_CLIENT_LIST_PLAYER_NAME :{BLACK}Name +STR_NETWORK_CLIENT_LIST_PLAYER_NAME_TOOLTIP :{BLACK}Ihr Spielername +STR_NETWORK_CLIENT_LIST_PLAYER_NAME_EDIT_TOOLTIP :{BLACK}Ihren Spielernamen bearbeiten +STR_NETWORK_CLIENT_LIST_PLAYER_NAME_QUERY_CAPTION :Ihr Spielername +STR_NETWORK_CLIENT_LIST_ADMIN_CLIENT_TOOLTIP :{BLACK}Auszuübende administrative Aktionen für diesen Teilnehmer +STR_NETWORK_CLIENT_LIST_ADMIN_COMPANY_TOOLTIP :{BLACK}Auszuübende administrative Aktionen für diese Firma +STR_NETWORK_CLIENT_LIST_JOIN_TOOLTIP :{BLACK}Dieser Firma beitreten +STR_NETWORK_CLIENT_LIST_CHAT_CLIENT_TOOLTIP :{BLACK}Eine Nachricht an diesen Spieler schicken +STR_NETWORK_CLIENT_LIST_CHAT_COMPANY_TOOLTIP :{BLACK}Eine Nachricht an alle Spieler dieser Firma schicken +STR_NETWORK_CLIENT_LIST_CHAT_SPECTATOR_TOOLTIP :{BLACK}Eine Nachricht an alle Zuschauer schicken +STR_NETWORK_CLIENT_LIST_SPECTATORS :Zuschauer +STR_NETWORK_CLIENT_LIST_NEW_COMPANY :(Neue Firma) +STR_NETWORK_CLIENT_LIST_NEW_COMPANY_TOOLTIP :{BLACK}Eine neue Firma gründen und beitreten +STR_NETWORK_CLIENT_LIST_PLAYER_ICON_SELF_TOOLTIP :{BLACK}Das sind Sie +STR_NETWORK_CLIENT_LIST_PLAYER_ICON_HOST_TOOLTIP :{BLACK}Dies ist der Host des Spiels +STR_NETWORK_CLIENT_LIST_ADMIN_CLIENT_KICK :Hinauswerfen +STR_NETWORK_CLIENT_LIST_ADMIN_CLIENT_BAN :Bannen +STR_NETWORK_CLIENT_LIST_ADMIN_COMPANY_RESET :Löschen +STR_NETWORK_CLIENT_LIST_ADMIN_COMPANY_UNLOCK :Passwort entsperren +STR_NETWORK_CLIENT_LIST_ASK_CAPTION :{WHITE}Admin-Aktion +STR_NETWORK_CLIENT_LIST_ASK_CLIENT_KICK :{YELLOW}Sind Sie sicher, dass Sie Spieler '{STRING}' hinauswerfen möchten? +STR_NETWORK_CLIENT_LIST_ASK_CLIENT_BAN :{YELLOW}Sind Sie sicher, dass Sie Spieler '{STRING}' bannen möchten? +STR_NETWORK_CLIENT_LIST_ASK_COMPANY_RESET :{YELLOW}Sind Sie sicher, dass Sie die Firma '{COMPANY}' löschen möchten? +STR_NETWORK_CLIENT_LIST_ASK_COMPANY_UNLOCK :{YELLOW}Sind Sie sicher, dass Sie das Passwort der Firma '{COMPANY}' zurücksetzen möchten? STR_NETWORK_SERVER :Server STR_NETWORK_CLIENT :Teilnehmer @@ -2166,6 +2204,7 @@ STR_NETWORK_ERROR_SERVER_START :{WHITE}Server k STR_NETWORK_ERROR_CLIENT_START :{WHITE}Verbindung konnte nicht hergestellt werden STR_NETWORK_ERROR_TIMEOUT :{WHITE}Verbindung #{NUM} hat das Zeitlimit überschritten STR_NETWORK_ERROR_SERVER_ERROR :{WHITE}Protokollfehler: Die Verbindung musste getrennt werden +STR_NETWORK_ERROR_BAD_PLAYER_NAME :{WHITE}Ihr Spielername wurde nicht gesetzt. Der Name kann im Mehrspielerfenster oben gesetzt werden STR_NETWORK_ERROR_WRONG_REVISION :{WHITE}Diese Version des Spiels entspricht nicht der des Servers STR_NETWORK_ERROR_WRONG_PASSWORD :{WHITE}Falsches Passwort STR_NETWORK_ERROR_SERVER_FULL :{WHITE}Der Server ist voll @@ -2178,6 +2217,7 @@ STR_NETWORK_ERROR_TIMEOUT_PASSWORD :{WHITE}Zeit fü STR_NETWORK_ERROR_TIMEOUT_COMPUTER :{WHITE}Dieser Computer ist sehr langsam, sodass er nicht mit dem Server mithalten kann STR_NETWORK_ERROR_TIMEOUT_MAP :{WHITE}Das Herunterladen der Karte dauerte zu lange STR_NETWORK_ERROR_TIMEOUT_JOIN :{WHITE}Der Beitritt zum Server dauerte zu lange +STR_NETWORK_ERROR_INVALID_CLIENT_NAME :{WHITE}Ihr Spielername ist ungültig ############ Leave those lines in this order!! STR_NETWORK_ERROR_CLIENT_GENERAL :Allgemeiner Fehler @@ -2200,6 +2240,7 @@ STR_NETWORK_ERROR_CLIENT_TIMEOUT_PASSWORD :Zeitüberschrei STR_NETWORK_ERROR_CLIENT_TIMEOUT_COMPUTER :Allgemeine Zeitüberschreitung STR_NETWORK_ERROR_CLIENT_TIMEOUT_MAP :das Herunterladen der Karte dauerte zu lange STR_NETWORK_ERROR_CLIENT_TIMEOUT_JOIN :Verarbeitung der Karte dauerte zu lange +STR_NETWORK_ERROR_CLIENT_INVALID_CLIENT_NAME :Ungültiger Teilnehmername ############ End of leave-in-this-order STR_NETWORK_ERROR_CLIENT_GUI_LOST_CONNECTION_CAPTION :{WHITE}Möglicher Verbindungsabbruch diff --git a/src/lang/korean.txt b/src/lang/korean.txt index 471de45fc3..95efeeead7 100644 --- a/src/lang/korean.txt +++ b/src/lang/korean.txt @@ -1533,7 +1533,7 @@ STR_CONFIG_SETTING_AI_PROFILE_EASY :쉬움 STR_CONFIG_SETTING_AI_PROFILE_MEDIUM :중간 STR_CONFIG_SETTING_AI_PROFILE_HARD :어려움 -STR_CONFIG_SETTING_AI_IN_MULTIPLAYER :멀티플레이에서 컴퓨터 플레이어의 참여 허용: {STRING} +STR_CONFIG_SETTING_AI_IN_MULTIPLAYER :멀티 플레이에서 컴퓨터 플레이어의 참여 허용: {STRING} STR_CONFIG_SETTING_AI_IN_MULTIPLAYER_HELPTEXT :멀티 플레이 게임에서 인공지능 컴퓨터 플레이어가 참여하는 것을 허용합니다. STR_CONFIG_SETTING_SCRIPT_MAX_OPCODES :게임 스크립트가 중지되기 직전에 계산할 수 있는 최대 횟수: {STRING} STR_CONFIG_SETTING_SCRIPT_MAX_OPCODES_HELPTEXT :게임 스크립트가 한 단계에서 계산할 수 있는 최대 계산 횟수를 설정합니다. @@ -1992,6 +1992,8 @@ STR_FACE_TIE :넥타이: STR_FACE_EARRING :귀걸이: STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}넥타이/귀걸이 변경 +STR_NETWORK_SERVER_VISIBILITY_PRIVATE :비공개 +STR_NETWORK_SERVER_VISIBILITY_PUBLIC :공개 # Network server list STR_NETWORK_SERVER_LIST_CAPTION :{WHITE}멀티 플레이 @@ -2055,6 +2057,8 @@ STR_NETWORK_START_SERVER_NEW_GAME_NAME_TOOLTIP :{BLACK}멀티 STR_NETWORK_START_SERVER_SET_PASSWORD :{BLACK}비밀번호 설정 STR_NETWORK_START_SERVER_PASSWORD_TOOLTIP :{BLACK}서버에 공개적으로 접근하는 것을 막고 싶을 때 비밀번호를 걸어 보호합니다. +STR_NETWORK_START_SERVER_VISIBILITY_LABEL :{BLACK}공개 여부 +STR_NETWORK_START_SERVER_VISIBILITY_TOOLTIP :{BLACK}서버 목록에 이 서버를 공개할 지 여부를 설정합니다 STR_NETWORK_START_SERVER_CLIENTS_SELECT :{BLACK}{NUM}명 STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS :{BLACK}최대 접속자 수: STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS_TOOLTIP :{BLACK}최대 접속자 수를 선택합니다. 모든 자리가 다 차 있을 필요는 없습니다 @@ -2073,7 +2077,7 @@ STR_NETWORK_START_SERVER_NEW_GAME_NAME_OSKTITLE :{BLACK}네트 STR_NETWORK_GAME_LOBBY_CAPTION :{WHITE}멀티플레이 게임 대기실 STR_NETWORK_GAME_LOBBY_PREPARE_TO_JOIN :{BLACK}참가 준비중: {ORANGE}{STRING} -STR_NETWORK_GAME_LOBBY_COMPANY_LIST_TOOLTIP :{BLACK}이 게임에 있는 회사의 목록입니다. 다른 회사에 같이 참가하거나, 빈 자리가 있을 경우 새로운 회사로 시작할 수 있습니다 +STR_NETWORK_GAME_LOBBY_COMPANY_LIST_TOOLTIP :{BLACK}이 게임에 있는 회사의 목록입니다. 다른 회사에 같이 참여하거나, 빈 자리가 있을 경우 새로운 회사를 세워서 시작할 수 있습니다 STR_NETWORK_GAME_LOBBY_COMPANY_INFO :{SILVER}회사 정보 STR_NETWORK_GAME_LOBBY_COMPANY_NAME :{SILVER}회사 이름: {WHITE}{STRING} @@ -2121,8 +2125,41 @@ STR_NETWORK_NEED_COMPANY_PASSWORD_CAPTION :{WHITE}회사 STR_NETWORK_COMPANY_LIST_CLIENT_LIST :접속자 목록 # Network client list +STR_NETWORK_CLIENT_LIST_CAPTION :{WHITE}멀티 플레이 +STR_NETWORK_CLIENT_LIST_SERVER :{BLACK}서버 +STR_NETWORK_CLIENT_LIST_SERVER_NAME :{BLACK}이름 +STR_NETWORK_CLIENT_LIST_SERVER_NAME_TOOLTIP :{BLACK}현재 플레이하고 있는 서버의 이름입니다 +STR_NETWORK_CLIENT_LIST_SERVER_NAME_EDIT_TOOLTIP :{BLACK}서버의 이름을 수정합니다 +STR_NETWORK_CLIENT_LIST_SERVER_NAME_QUERY_CAPTION :서버 이름 +STR_NETWORK_CLIENT_LIST_SERVER_VISIBILITY :{BLACK}공개 여부 +STR_NETWORK_CLIENT_LIST_SERVER_VISIBILITY_TOOLTIP :{BLACK}서버 목록에 이 서버를 공개할 지 여부를 설정합니다 +STR_NETWORK_CLIENT_LIST_PLAYER :{BLACK}플레이어 +STR_NETWORK_CLIENT_LIST_PLAYER_NAME :{BLACK}이름 +STR_NETWORK_CLIENT_LIST_PLAYER_NAME_TOOLTIP :{BLACK}당신의 접속자 이름입니다 +STR_NETWORK_CLIENT_LIST_PLAYER_NAME_EDIT_TOOLTIP :{BLACK}접속자 이름을 수정합니다 +STR_NETWORK_CLIENT_LIST_PLAYER_NAME_QUERY_CAPTION :접속자 이름 +STR_NETWORK_CLIENT_LIST_ADMIN_CLIENT_TOOLTIP :{BLACK}관리자가 이 접속자에게 할 수 있는 기능입니다 +STR_NETWORK_CLIENT_LIST_ADMIN_COMPANY_TOOLTIP :{BLACK}관리자가 이 접속자에게 할 수 있는 기능입니다 +STR_NETWORK_CLIENT_LIST_JOIN_TOOLTIP :{BLACK}여기에 참여합니다 +STR_NETWORK_CLIENT_LIST_CHAT_CLIENT_TOOLTIP :{BLACK}이 플레이어에게 메시지를 보냅니다 +STR_NETWORK_CLIENT_LIST_CHAT_COMPANY_TOOLTIP :{BLACK}이 회사에 참여 중인 모든 플레이어에게 메시지를 보냅니다 +STR_NETWORK_CLIENT_LIST_CHAT_SPECTATOR_TOOLTIP :{BLACK}모든 관전자에게 메시지를 보냅니다 +STR_NETWORK_CLIENT_LIST_SPECTATORS :관전 +STR_NETWORK_CLIENT_LIST_NEW_COMPANY :(새 회사) +STR_NETWORK_CLIENT_LIST_NEW_COMPANY_TOOLTIP :{BLACK}새 회사를 만듭니다 +STR_NETWORK_CLIENT_LIST_PLAYER_ICON_SELF_TOOLTIP :{BLACK}당신입니다 +STR_NETWORK_CLIENT_LIST_PLAYER_ICON_HOST_TOOLTIP :{BLACK}이 게임을 연 사람입니다 +STR_NETWORK_CLIENT_LIST_ADMIN_CLIENT_KICK :추방 +STR_NETWORK_CLIENT_LIST_ADMIN_CLIENT_BAN :차단 +STR_NETWORK_CLIENT_LIST_ADMIN_COMPANY_RESET :삭제 +STR_NETWORK_CLIENT_LIST_ADMIN_COMPANY_UNLOCK :비밀번호 초기화 +STR_NETWORK_CLIENT_LIST_ASK_CAPTION :{WHITE}관리자 기능 +STR_NETWORK_CLIENT_LIST_ASK_CLIENT_KICK :{YELLOW}'{STRING}' 플레이어를 정말로 추방하시겠습니까? +STR_NETWORK_CLIENT_LIST_ASK_CLIENT_BAN :{YELLOW}'{STRING}' 플레이어를 정말로 차단하시겠습니까? +STR_NETWORK_CLIENT_LIST_ASK_COMPANY_RESET :{YELLOW}'{COMPANY}' 회사를 정말로 삭제하시겠습니까? +STR_NETWORK_CLIENT_LIST_ASK_COMPANY_UNLOCK :{YELLOW}'{COMPANY}' 회사의 비밀번호를 정말로 초기화하시겠습니까? STR_NETWORK_SERVER :서버 STR_NETWORK_CLIENT :접속자 @@ -2133,11 +2170,11 @@ STR_COMPANY_PASSWORD_CANCEL :{BLACK}입력 STR_COMPANY_PASSWORD_OK :{BLACK}이 회사에 새 비밀번호 부여 STR_COMPANY_PASSWORD_CAPTION :{WHITE}회사 비밀번호 STR_COMPANY_PASSWORD_MAKE_DEFAULT :{BLACK}회사 비밀번호 기본값으로 설정 -STR_COMPANY_PASSWORD_MAKE_DEFAULT_TOOLTIP :{BLACK}이 회사 비밀번호를 새 회사의 비밀번호 기본값으로 사용 +STR_COMPANY_PASSWORD_MAKE_DEFAULT_TOOLTIP :{BLACK}이 비밀번호를 새 회사의 비밀번호 기본값으로 사용합니다 # Network company info join/password STR_COMPANY_VIEW_JOIN :{BLACK}참여 -STR_COMPANY_VIEW_JOIN_TOOLTIP :{BLACK}이 회사로 참가해서 플레이합니다 +STR_COMPANY_VIEW_JOIN_TOOLTIP :{BLACK}이 회사에 참여해서 플레이합니다 STR_COMPANY_VIEW_PASSWORD :{BLACK}비밀번호 STR_COMPANY_VIEW_PASSWORD_TOOLTIP :{BLACK}다른 참가자가 이 회사에 참여하여 플레이하지 못 하도록 암호로 보호합니다 STR_COMPANY_VIEW_SET_PASSWORD :{BLACK}회사 비밀번호 설정 @@ -2167,7 +2204,7 @@ STR_NETWORK_ERROR_SERVER_START :{WHITE}서버 STR_NETWORK_ERROR_CLIENT_START :{WHITE}접속할 수 없습니다 STR_NETWORK_ERROR_TIMEOUT :{WHITE}접속자 #{NUM}의 입력 시간이 초과되었습니다 STR_NETWORK_ERROR_SERVER_ERROR :{WHITE}프로토콜 오류가 발생되어 연결이 끊어졌습니다 -STR_NETWORK_ERROR_BAD_PLAYER_NAME :{WHITE}플레이에 사용할 이름을 설정하지 않았습니다. 이름은 멀티플레이 창의 상단에서 설정할 수 있습니다. +STR_NETWORK_ERROR_BAD_PLAYER_NAME :{WHITE}접속자 이름을 설정하지 않았습니다. 이름은 멀티플레이 창의 상단에서 설정할 수 있습니다. STR_NETWORK_ERROR_WRONG_REVISION :{WHITE}이 접속자의 게임 버전이 서버의 버전과 일치하지 않습니다 STR_NETWORK_ERROR_WRONG_PASSWORD :{WHITE}잘못된 비밀번호입니다 STR_NETWORK_ERROR_SERVER_FULL :{WHITE}서버에 인원이 가득 찼습니다 @@ -2232,7 +2269,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 :*** {0:STRING} 님이 {1:STRING}에게 {2:CURRENCY_LONG}만큼의 돈을 보내셨습니다 STR_NETWORK_MESSAGE_SERVER_SHUTDOWN :{WHITE}서버가 게임을 종료하였습니다 STR_NETWORK_MESSAGE_SERVER_REBOOT :{WHITE}서버가 재시작되고 있습니다...{}기다려주세요... @@ -3086,7 +3123,7 @@ STR_NEWGRF_LIST_MISSING :{RED}파일 없 # NewGRF 'it's broken' warnings STR_NEWGRF_BROKEN :{WHITE}'{0:STRING}' NewGRF가 적용되는 과정에서 비동기화나 충돌이 일어날 수 있습니다 -STR_NEWGRF_BROKEN_POWERED_WAGON :{WHITE}차고지 안에 있지 않은 '{1:ENGINE}'에 대한 동력 차량 상태가 바뀌었습니다 +STR_NEWGRF_BROKEN_POWERED_WAGON :{WHITE}차고지 안에 있지 않은 '{1:ENGINE}' 동력 차량의 상태가 바뀌었습니다 STR_NEWGRF_BROKEN_VEHICLE_LENGTH :{WHITE}'{1:ENGINE}'{G 1 "이" "가"} 차고지 안에 있지 않으면 차량 길이가 바뀝니다 STR_NEWGRF_BROKEN_CAPACITY :{WHITE}차량이 기지 안에 있지 않거나 개조가 불가능한 상태에서 '{1:ENGINE}'의 수송량이 변경되었습니다 STR_BROKEN_VEHICLE_LENGTH :{WHITE}'{1:COMPANY}'에 속한 열차 '{0:VEHICLE}'의 길이가 잘못된 값을 가지고 있습니다. NewGRF에 의한 문제로 보입니다. 게임이 비동기화 또는 충돌을 일으킬 수 있습니다. diff --git a/src/lang/russian.txt b/src/lang/russian.txt index de8c4f84cf..1a8d94e9d1 100644 --- a/src/lang/russian.txt +++ b/src/lang/russian.txt @@ -1098,7 +1098,7 @@ STR_GAME_OPTIONS_CURRENCY_MYR :Малайзи STR_GAME_OPTIONS_ROAD_VEHICLES_DROPDOWN_LEFT :Левостороннее STR_GAME_OPTIONS_ROAD_VEHICLES_DROPDOWN_RIGHT :Правостороннее -STR_GAME_OPTIONS_TOWN_NAMES_FRAME :{BLACK}Названия городов +STR_GAME_OPTIONS_TOWN_NAMES_FRAME :{BLACK}Названия городов: STR_GAME_OPTIONS_TOWN_NAMES_DROPDOWN_TOOLTIP :{BLACK}Язык, который будет использоваться для выбора названий населённых пунктов ############ start of townname region @@ -2271,8 +2271,14 @@ STR_NETWORK_NEED_COMPANY_PASSWORD_CAPTION :{WHITE}Комп STR_NETWORK_COMPANY_LIST_CLIENT_LIST :Список клиентов # Network client list +STR_NETWORK_CLIENT_LIST_ADMIN_CLIENT_TOOLTIP :{BLACK}Действия администратора, применимые к этому клиенту +STR_NETWORK_CLIENT_LIST_ADMIN_CLIENT_KICK :Отключить +STR_NETWORK_CLIENT_LIST_ADMIN_CLIENT_BAN :Заблокировать +STR_NETWORK_CLIENT_LIST_ASK_CAPTION :{WHITE}Подтверждение действия +STR_NETWORK_CLIENT_LIST_ASK_CLIENT_KICK :{YELLOW}Отключить игрока «{STRING}»? +STR_NETWORK_CLIENT_LIST_ASK_CLIENT_BAN :{YELLOW}Заблокировать игрока «{STRING}»? STR_NETWORK_SERVER :Сервер STR_NETWORK_CLIENT :Клиент From a934dfe0be9621b784ccd1dca81d2ef8214fc4e0 Mon Sep 17 00:00:00 2001 From: Chris Date: Tue, 27 Apr 2021 18:57:53 +0200 Subject: [PATCH 171/268] Fix: [NewGRF] Errors with severity ERROR also display a pop-up window (#9119) --- src/lang/english.txt | 1 + src/newgrf_gui.cpp | 10 +++++++--- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/lang/english.txt b/src/lang/english.txt index 46a8492c9f..3037bf90fe 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -3079,6 +3079,7 @@ STR_NEWGRF_ERROR_MSG_WARNING :{RED}Warning: { STR_NEWGRF_ERROR_MSG_ERROR :{RED}Error: {SILVER}{RAW_STRING} STR_NEWGRF_ERROR_MSG_FATAL :{RED}Fatal: {SILVER}{RAW_STRING} STR_NEWGRF_ERROR_FATAL_POPUP :{WHITE}A fatal NewGRF error has occurred: {}{STRING5} +STR_NEWGRF_ERROR_POPUP :{WHITE}A NewGRF error has occurred: {}{STRING5} STR_NEWGRF_ERROR_VERSION_NUMBER :{1:RAW_STRING} will not work with the TTDPatch version reported by OpenTTD STR_NEWGRF_ERROR_DOS_OR_WINDOWS :{1:RAW_STRING} is for the {RAW_STRING} version of TTD STR_NEWGRF_ERROR_UNSET_SWITCH :{1:RAW_STRING} is designed to be used with {RAW_STRING} diff --git a/src/newgrf_gui.cpp b/src/newgrf_gui.cpp index fe2510f74b..3465a94379 100644 --- a/src/newgrf_gui.cpp +++ b/src/newgrf_gui.cpp @@ -47,8 +47,8 @@ void ShowNewGRFError() if (_game_mode == GM_MENU) return; for (const GRFConfig *c = _grfconfig; c != nullptr; c = c->next) { - /* We only want to show fatal errors */ - if (c->error == nullptr || c->error->severity != STR_NEWGRF_ERROR_MSG_FATAL) continue; + /* Only show Fatal and Error level messages */ + 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()); @@ -57,7 +57,11 @@ void ShowNewGRFError() for (uint i = 0; i < lengthof(c->error->param_value); i++) { SetDParam(4 + i, c->error->param_value[i]); } - ShowErrorMessage(STR_NEWGRF_ERROR_FATAL_POPUP, INVALID_STRING_ID, WL_CRITICAL); + if (c->error->severity == STR_NEWGRF_ERROR_MSG_FATAL) { + ShowErrorMessage(STR_NEWGRF_ERROR_FATAL_POPUP, INVALID_STRING_ID, WL_CRITICAL); + } else { + ShowErrorMessage(STR_NEWGRF_ERROR_POPUP, INVALID_STRING_ID, WL_ERROR); + } break; } } From 65c5a647191a9a10a532bb9d67da6938f5eace1b Mon Sep 17 00:00:00 2001 From: rubidium42 Date: Tue, 27 Apr 2021 10:50:28 +0200 Subject: [PATCH 172/268] Fix: [Network] errno and strerror do not handle network errors on Windows --- src/network/core/address.cpp | 14 +++++++------- src/network/core/core.cpp | 17 +++++++++++++++++ src/network/core/os_abstraction.h | 25 ++++++++++++++++++++++--- src/network/core/tcp.cpp | 6 +++--- src/network/core/tcp_http.cpp | 2 +- src/network/core/tcp_listen.h | 4 ++-- src/network/core/udp.cpp | 4 ++-- 7 files changed, 54 insertions(+), 18 deletions(-) diff --git a/src/network/core/address.cpp b/src/network/core/address.cpp index 8c69094385..fc19439e00 100644 --- a/src/network/core/address.cpp +++ b/src/network/core/address.cpp @@ -316,7 +316,7 @@ static SOCKET ConnectLoopProc(addrinfo *runp) SOCKET sock = socket(runp->ai_family, runp->ai_socktype, runp->ai_protocol); if (sock == INVALID_SOCKET) { - DEBUG(net, 1, "[%s] could not create %s socket: %s", type, family, strerror(errno)); + DEBUG(net, 1, "[%s] could not create %s socket: %s", type, family, NetworkGetLastErrorString()); return INVALID_SOCKET; } @@ -331,7 +331,7 @@ static SOCKET ConnectLoopProc(addrinfo *runp) if (err != 0) #endif { - DEBUG(net, 1, "[%s] could not connect %s socket: %s", type, family, strerror(errno)); + DEBUG(net, 1, "[%s] could not connect %s socket: %s", type, family, NetworkGetLastErrorString()); closesocket(sock); return INVALID_SOCKET; } @@ -369,7 +369,7 @@ static SOCKET ListenLoopProc(addrinfo *runp) SOCKET sock = socket(runp->ai_family, runp->ai_socktype, runp->ai_protocol); if (sock == INVALID_SOCKET) { - DEBUG(net, 0, "[%s] could not create %s socket on port %s: %s", type, family, address, strerror(errno)); + DEBUG(net, 0, "[%s] could not create %s socket on port %s: %s", type, family, address, NetworkGetLastErrorString()); return INVALID_SOCKET; } @@ -380,24 +380,24 @@ static SOCKET ListenLoopProc(addrinfo *runp) int on = 1; /* The (const char*) cast is needed for windows!! */ if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (const char*)&on, sizeof(on)) == -1) { - DEBUG(net, 3, "[%s] could not set reusable %s sockets for port %s: %s", type, family, address, strerror(errno)); + DEBUG(net, 3, "[%s] could not set reusable %s sockets for port %s: %s", type, family, address, NetworkGetLastErrorString()); } #ifndef __OS2__ if (runp->ai_family == AF_INET6 && setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, (const char*)&on, sizeof(on)) == -1) { - DEBUG(net, 3, "[%s] could not disable IPv4 over IPv6 on port %s: %s", type, address, strerror(errno)); + DEBUG(net, 3, "[%s] could not disable IPv4 over IPv6 on port %s: %s", type, address, NetworkGetLastErrorString()); } #endif if (bind(sock, runp->ai_addr, (int)runp->ai_addrlen) != 0) { - DEBUG(net, 1, "[%s] could not bind on %s port %s: %s", type, family, address, strerror(errno)); + DEBUG(net, 1, "[%s] could not bind on %s port %s: %s", type, family, address, NetworkGetLastErrorString()); closesocket(sock); return INVALID_SOCKET; } if (runp->ai_socktype != SOCK_DGRAM && listen(sock, 1) != 0) { - DEBUG(net, 1, "[%s] could not listen at %s port %s: %s", type, family, address, strerror(errno)); + DEBUG(net, 1, "[%s] could not listen at %s port %s: %s", type, family, address, NetworkGetLastErrorString()); closesocket(sock); return INVALID_SOCKET; } diff --git a/src/network/core/core.cpp b/src/network/core/core.cpp index 0aeb9c65ce..8c5c5c2292 100644 --- a/src/network/core/core.cpp +++ b/src/network/core/core.cpp @@ -13,6 +13,7 @@ #include "../../debug.h" #include "os_abstraction.h" #include "packet.h" +#include "../../string_func.h" #include "../../safeguards.h" @@ -48,6 +49,22 @@ void NetworkCoreShutdown() #endif } +#if defined(_WIN32) +/** + * Return the string representation of the given error from the OS's network functions. + * @param error The error number (from \c NetworkGetLastError()). + * @return The error message, potentially an empty string but never \c nullptr. + */ +const char *NetworkGetErrorString(int error) +{ + static char buffer[512]; + if (FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, error, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), buffer, sizeof(buffer), NULL) == 0) { + seprintf(buffer, lastof(buffer), "Unknown error %d", error); + } + return buffer; +} +#endif /* defined(_WIN32) */ /** * Serializes the GRFIdentifier (GRF ID and MD5 checksum) to the packet diff --git a/src/network/core/os_abstraction.h b/src/network/core/os_abstraction.h index 836cfeae8f..a015c4374f 100644 --- a/src/network/core/os_abstraction.h +++ b/src/network/core/os_abstraction.h @@ -23,9 +23,17 @@ #include #include -#define GET_LAST_ERROR() WSAGetLastError() +/** + * Get the last error code from any of the OS's network functions. + * What it returns and when it is reset, is implementation defined. + * @return The last error code. + */ +#define NetworkGetLastError() WSAGetLastError() #undef EWOULDBLOCK #define EWOULDBLOCK WSAEWOULDBLOCK + +const char *NetworkGetErrorString(int error); + /* Windows has some different names for some types */ typedef unsigned long in_addr_t; @@ -51,7 +59,8 @@ typedef unsigned long in_addr_t; # define INVALID_SOCKET -1 # define ioctlsocket ioctl # define closesocket close -# define GET_LAST_ERROR() (errno) +# define NetworkGetLastError() (errno) +# define NetworkGetErrorString(error) (strerror(error)) /* Need this for FIONREAD on solaris */ # define BSD_COMP @@ -101,7 +110,8 @@ typedef unsigned long in_addr_t; # define INVALID_SOCKET -1 # define ioctlsocket ioctl # define closesocket close -# define GET_LAST_ERROR() (sock_errno()) +# define NetworkGetLastError() (sock_errno()) +# define NetworkGetErrorString(error) (strerror(error)) /* Includes needed for OS/2 systems */ # include @@ -173,6 +183,15 @@ static inline socklen_t FixAddrLenForEmscripten(struct sockaddr_storage &address } #endif +/** + * Return the string representation of the last error from the OS's network functions. + * @return The error message, potentially an empty string but never \c nullptr. + */ +static inline const char *NetworkGetLastErrorString() +{ + return NetworkGetErrorString(NetworkGetLastError()); +} + /** * Try to set the socket into non-blocking mode. * @param d The socket to set the non-blocking more for. diff --git a/src/network/core/tcp.cpp b/src/network/core/tcp.cpp index 72e66a0b55..b9ba33f00e 100644 --- a/src/network/core/tcp.cpp +++ b/src/network/core/tcp.cpp @@ -86,7 +86,7 @@ SendPacketsState NetworkTCPSocketHandler::SendPackets(bool closing_down) while ((p = this->packet_queue) != nullptr) { res = p->TransferOut(send, this->sock, 0); if (res == -1) { - int err = GET_LAST_ERROR(); + int err = NetworkGetLastError(); if (err != EWOULDBLOCK) { /* Something went wrong.. close client! */ if (!closing_down) { @@ -136,7 +136,7 @@ Packet *NetworkTCPSocketHandler::ReceivePacket() while (p->RemainingBytesToTransfer() != 0) { res = p->TransferIn(recv, this->sock, 0); if (res == -1) { - int err = GET_LAST_ERROR(); + int err = NetworkGetLastError(); if (err != EWOULDBLOCK) { /* Something went wrong... (104 is connection reset by peer) */ if (err != 104) DEBUG(net, 0, "recv failed with error %d", err); @@ -164,7 +164,7 @@ Packet *NetworkTCPSocketHandler::ReceivePacket() while (p->RemainingBytesToTransfer() != 0) { res = p->TransferIn(recv, this->sock, 0); if (res == -1) { - int err = GET_LAST_ERROR(); + int err = NetworkGetLastError(); if (err != EWOULDBLOCK) { /* Something went wrong... (104 is connection reset by peer) */ if (err != 104) DEBUG(net, 0, "recv failed with error %d", err); diff --git a/src/network/core/tcp_http.cpp b/src/network/core/tcp_http.cpp index d88ea711d3..04bc6a03a4 100644 --- a/src/network/core/tcp_http.cpp +++ b/src/network/core/tcp_http.cpp @@ -228,7 +228,7 @@ int NetworkHTTPSocketHandler::Receive() for (;;) { ssize_t res = recv(this->sock, (char *)this->recv_buffer + this->recv_pos, lengthof(this->recv_buffer) - this->recv_pos, 0); if (res == -1) { - int err = GET_LAST_ERROR(); + int err = NetworkGetLastError(); if (err != EWOULDBLOCK) { /* Something went wrong... (104 is connection reset by peer) */ if (err != 104) DEBUG(net, 0, "recv failed with error %d", err); diff --git a/src/network/core/tcp_listen.h b/src/network/core/tcp_listen.h index 53a3d57cc9..c11727ba71 100644 --- a/src/network/core/tcp_listen.h +++ b/src/network/core/tcp_listen.h @@ -64,7 +64,7 @@ public: DEBUG(net, 1, "[%s] Banned ip tried to join (%s), refused", Tsocket::GetName(), entry.c_str()); if (p.TransferOut(send, s, 0) < 0) { - DEBUG(net, 0, "send failed with error %d", GET_LAST_ERROR()); + DEBUG(net, 0, "send failed with error %d", NetworkGetLastError()); } closesocket(s); break; @@ -81,7 +81,7 @@ public: p.PrepareToSend(); if (p.TransferOut(send, s, 0) < 0) { - DEBUG(net, 0, "send failed with error %d", GET_LAST_ERROR()); + DEBUG(net, 0, "send failed with error %d", NetworkGetLastError()); } closesocket(s); diff --git a/src/network/core/udp.cpp b/src/network/core/udp.cpp index e8299f7b62..abbb1bbdb5 100644 --- a/src/network/core/udp.cpp +++ b/src/network/core/udp.cpp @@ -94,7 +94,7 @@ void NetworkUDPSocketHandler::SendPacket(Packet *p, NetworkAddress *recv, bool a /* Enable broadcast */ unsigned long val = 1; if (setsockopt(s.second, SOL_SOCKET, SO_BROADCAST, (char *) &val, sizeof(val)) < 0) { - DEBUG(net, 1, "[udp] setting broadcast failed with: %i", GET_LAST_ERROR()); + DEBUG(net, 1, "[udp] setting broadcast failed with: %i", NetworkGetLastError()); } } @@ -103,7 +103,7 @@ void NetworkUDPSocketHandler::SendPacket(Packet *p, NetworkAddress *recv, bool a DEBUG(net, 7, "[udp] sendto(%s)", send.GetAddressAsString().c_str()); /* Check for any errors, but ignore it otherwise */ - if (res == -1) DEBUG(net, 1, "[udp] sendto(%s) failed with: %i", send.GetAddressAsString().c_str(), GET_LAST_ERROR()); + if (res == -1) DEBUG(net, 1, "[udp] sendto(%s) failed with: %i", send.GetAddressAsString().c_str(), NetworkGetLastError()); if (!all) break; } From cf8c1aa860e05e696f9829e3b031a22ca82781ef Mon Sep 17 00:00:00 2001 From: rubidium42 Date: Tue, 27 Apr 2021 12:13:06 +0200 Subject: [PATCH 173/268] Change: [Network] Use string error messages instead of numeric error numbers that need to be looked up --- src/network/core/os_abstraction.h | 2 ++ src/network/core/tcp.cpp | 10 +++++----- src/network/core/tcp_http.cpp | 4 ++-- src/network/core/tcp_listen.h | 4 ++-- src/network/core/udp.cpp | 4 ++-- 5 files changed, 13 insertions(+), 11 deletions(-) diff --git a/src/network/core/os_abstraction.h b/src/network/core/os_abstraction.h index a015c4374f..7af3fd163e 100644 --- a/src/network/core/os_abstraction.h +++ b/src/network/core/os_abstraction.h @@ -31,6 +31,8 @@ #define NetworkGetLastError() WSAGetLastError() #undef EWOULDBLOCK #define EWOULDBLOCK WSAEWOULDBLOCK +#undef ECONNRESET +#define ECONNRESET WSAECONNRESET const char *NetworkGetErrorString(int error); diff --git a/src/network/core/tcp.cpp b/src/network/core/tcp.cpp index b9ba33f00e..f23b202c8b 100644 --- a/src/network/core/tcp.cpp +++ b/src/network/core/tcp.cpp @@ -90,7 +90,7 @@ SendPacketsState NetworkTCPSocketHandler::SendPackets(bool closing_down) if (err != EWOULDBLOCK) { /* Something went wrong.. close client! */ if (!closing_down) { - DEBUG(net, 0, "send failed with error %d", err); + DEBUG(net, 0, "send failed with error %s", NetworkGetErrorString(err)); this->CloseConnection(); } return SPS_CLOSED; @@ -138,8 +138,8 @@ Packet *NetworkTCPSocketHandler::ReceivePacket() if (res == -1) { int err = NetworkGetLastError(); if (err != EWOULDBLOCK) { - /* Something went wrong... (104 is connection reset by peer) */ - if (err != 104) DEBUG(net, 0, "recv failed with error %d", err); + /* Something went wrong... (ECONNRESET is connection reset by peer) */ + if (err != ECONNRESET) DEBUG(net, 0, "recv failed with error %s", NetworkGetErrorString(err)); this->CloseConnection(); return nullptr; } @@ -166,8 +166,8 @@ Packet *NetworkTCPSocketHandler::ReceivePacket() if (res == -1) { int err = NetworkGetLastError(); if (err != EWOULDBLOCK) { - /* Something went wrong... (104 is connection reset by peer) */ - if (err != 104) DEBUG(net, 0, "recv failed with error %d", err); + /* Something went wrong... (ECONNRESET is connection reset by peer) */ + if (err != ECONNRESET) DEBUG(net, 0, "recv failed with error %s", NetworkGetErrorString(err)); this->CloseConnection(); return nullptr; } diff --git a/src/network/core/tcp_http.cpp b/src/network/core/tcp_http.cpp index 04bc6a03a4..d57f4eceb7 100644 --- a/src/network/core/tcp_http.cpp +++ b/src/network/core/tcp_http.cpp @@ -230,8 +230,8 @@ int NetworkHTTPSocketHandler::Receive() if (res == -1) { int err = NetworkGetLastError(); if (err != EWOULDBLOCK) { - /* Something went wrong... (104 is connection reset by peer) */ - if (err != 104) DEBUG(net, 0, "recv failed with error %d", err); + /* Something went wrong... (ECONNRESET is connection reset by peer) */ + if (err != ECONNRESET) DEBUG(net, 0, "recv failed with error %s", NetworkGetErrorString(err)); return -1; } /* Connection would block, so stop for now */ diff --git a/src/network/core/tcp_listen.h b/src/network/core/tcp_listen.h index c11727ba71..168f49f947 100644 --- a/src/network/core/tcp_listen.h +++ b/src/network/core/tcp_listen.h @@ -64,7 +64,7 @@ public: DEBUG(net, 1, "[%s] Banned ip tried to join (%s), refused", Tsocket::GetName(), entry.c_str()); if (p.TransferOut(send, s, 0) < 0) { - DEBUG(net, 0, "send failed with error %d", NetworkGetLastError()); + DEBUG(net, 0, "send failed with error %s", NetworkGetLastErrorString()); } closesocket(s); break; @@ -81,7 +81,7 @@ public: p.PrepareToSend(); if (p.TransferOut(send, s, 0) < 0) { - DEBUG(net, 0, "send failed with error %d", NetworkGetLastError()); + DEBUG(net, 0, "send failed with error %s", NetworkGetLastErrorString()); } closesocket(s); diff --git a/src/network/core/udp.cpp b/src/network/core/udp.cpp index abbb1bbdb5..df5140e2b5 100644 --- a/src/network/core/udp.cpp +++ b/src/network/core/udp.cpp @@ -94,7 +94,7 @@ void NetworkUDPSocketHandler::SendPacket(Packet *p, NetworkAddress *recv, bool a /* Enable broadcast */ unsigned long val = 1; if (setsockopt(s.second, SOL_SOCKET, SO_BROADCAST, (char *) &val, sizeof(val)) < 0) { - DEBUG(net, 1, "[udp] setting broadcast failed with: %i", NetworkGetLastError()); + DEBUG(net, 1, "[udp] setting broadcast failed with: %s", NetworkGetLastErrorString()); } } @@ -103,7 +103,7 @@ void NetworkUDPSocketHandler::SendPacket(Packet *p, NetworkAddress *recv, bool a DEBUG(net, 7, "[udp] sendto(%s)", send.GetAddressAsString().c_str()); /* Check for any errors, but ignore it otherwise */ - if (res == -1) DEBUG(net, 1, "[udp] sendto(%s) failed with: %i", send.GetAddressAsString().c_str(), NetworkGetLastError()); + if (res == -1) DEBUG(net, 1, "[udp] sendto(%s) failed with: %s", send.GetAddressAsString().c_str(), NetworkGetLastErrorString()); if (!all) break; } From cbad518bf3c7e6d05b62d8c1d063b89d974e5f33 Mon Sep 17 00:00:00 2001 From: rubidium42 Date: Tue, 27 Apr 2021 10:56:26 +0200 Subject: [PATCH 174/268] Codechange: [Network] Do not leak os_abstraction.h via network_func --- src/console_cmds.cpp | 4 ++-- src/network/network.cpp | 15 ++++++++------- src/network/network_func.h | 5 ++--- src/network/network_gui.cpp | 6 +++--- src/openttd.cpp | 6 +++--- 5 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/console_cmds.cpp b/src/console_cmds.cpp index f8d9eb1b0c..048b8b3e93 100644 --- a/src/console_cmds.cpp +++ b/src/console_cmds.cpp @@ -900,7 +900,7 @@ DEF_CONSOLE_CMD(ConNetworkReconnect) /* Don't resolve the address first, just print it directly as it comes from the config file. */ IConsolePrintF(CC_DEFAULT, "Reconnecting to %s:%d...", _settings_client.network.last_host, _settings_client.network.last_port); - NetworkClientConnectGame(NetworkAddress(_settings_client.network.last_host, _settings_client.network.last_port), playas); + NetworkClientConnectGame(_settings_client.network.last_host, _settings_client.network.last_port, playas); return true; } @@ -942,7 +942,7 @@ DEF_CONSOLE_CMD(ConNetworkConnect) IConsolePrintF(CC_DEFAULT, " port: %s", port); } - NetworkClientConnectGame(NetworkAddress(ip, rport), join_as); + NetworkClientConnectGame(ip, rport, join_as); return true; } diff --git a/src/network/network.cpp b/src/network/network.cpp index 1e0838684e..812456b4e2 100644 --- a/src/network/network.cpp +++ b/src/network/network.cpp @@ -688,16 +688,16 @@ public: /* Used by clients, to connect to a server */ -void NetworkClientConnectGame(NetworkAddress address, CompanyID join_as, const char *join_server_password, const char *join_company_password) +void NetworkClientConnectGame(const char *hostname, uint16 port, CompanyID join_as, const char *join_server_password, const char *join_company_password) { if (!_network_available) return; - if (address.GetPort() == 0) return; + if (port == 0) return; if (!NetworkValidateClientName()) return; - strecpy(_settings_client.network.last_host, address.GetHostname(), lastof(_settings_client.network.last_host)); - _settings_client.network.last_port = address.GetPort(); + strecpy(_settings_client.network.last_host, hostname, lastof(_settings_client.network.last_host)); + _settings_client.network.last_port = port; _network_join_as = join_as; _network_join_server_password = join_server_password; _network_join_company_password = join_company_password; @@ -708,7 +708,7 @@ void NetworkClientConnectGame(NetworkAddress address, CompanyID join_as, const c _network_join_status = NETWORK_JOIN_STATUS_CONNECTING; ShowJoinStatusWindow(); - new TCPClientConnecter(address); + new TCPClientConnecter(NetworkAddress(hostname, port)); } static void NetworkInitGameInfo() @@ -1059,12 +1059,13 @@ static void NetworkGenerateServerId() seprintf(_settings_client.network.network_id, lastof(_settings_client.network.network_id), "%s", hex_output); } -void NetworkStartDebugLog(NetworkAddress address) +void NetworkStartDebugLog(const char *hostname, uint16 port) { extern SOCKET _debug_socket; // Comes from debug.c - DEBUG(net, 0, "Redirecting DEBUG() to %s:%d", address.GetHostname(), address.GetPort()); + DEBUG(net, 0, "Redirecting DEBUG() to %s:%d", hostname, port); + NetworkAddress address(hostname, port); SOCKET s = address.Connect(); if (s == INVALID_SOCKET) { DEBUG(net, 0, "Failed to open socket for redirection DEBUG()"); diff --git a/src/network/network_func.h b/src/network/network_func.h index c1271f69c6..66719b2166 100644 --- a/src/network/network_func.h +++ b/src/network/network_func.h @@ -17,7 +17,6 @@ // #define DEBUG_DUMP_COMMANDS // #define DEBUG_FAILED_DUMP_COMMANDS -#include "core/address.h" #include "network_type.h" #include "../console_type.h" #include "../gfx_type.h" @@ -48,12 +47,12 @@ void NetworkGameLoop(); void NetworkBackgroundLoop(); void ParseConnectionString(const char **port, char *connection_string); void ParseGameConnectionString(const char **company, const char **port, char *connection_string); -void NetworkStartDebugLog(NetworkAddress address); +void NetworkStartDebugLog(const char *hostname, uint16 port); void NetworkPopulateCompanyStats(NetworkCompanyStats *stats); void NetworkUpdateClientInfo(ClientID client_id); void NetworkClientsToSpectators(CompanyID cid); -void NetworkClientConnectGame(NetworkAddress address, CompanyID join_as, const char *join_server_password = nullptr, const char *join_company_password = nullptr); +void NetworkClientConnectGame(const char *hostname, uint16 port, CompanyID join_as, const char *join_server_password = nullptr, const char *join_company_password = nullptr); void NetworkClientRequestMove(CompanyID company, const char *pass = ""); void NetworkClientSendRcon(const char *password, const char *command); void NetworkClientSendChat(NetworkAction action, DestType type, int dest, const char *msg, int64 data = 0); diff --git a/src/network/network_gui.cpp b/src/network/network_gui.cpp index 68208ee3fc..ebba47d851 100644 --- a/src/network/network_gui.cpp +++ b/src/network/network_gui.cpp @@ -1470,15 +1470,15 @@ struct NetworkLobbyWindow : public Window { case WID_NL_JOIN: // Join company /* Button can be clicked only when it is enabled. */ - NetworkClientConnectGame(NetworkAddress(_settings_client.network.last_host, _settings_client.network.last_port), this->company); + NetworkClientConnectGame(_settings_client.network.last_host, _settings_client.network.last_port, this->company); break; case WID_NL_NEW: // New company - NetworkClientConnectGame(NetworkAddress(_settings_client.network.last_host, _settings_client.network.last_port), COMPANY_NEW_COMPANY); + NetworkClientConnectGame(_settings_client.network.last_host, _settings_client.network.last_port, COMPANY_NEW_COMPANY); break; case WID_NL_SPECTATE: // Spectate game - NetworkClientConnectGame(NetworkAddress(_settings_client.network.last_host, _settings_client.network.last_port), COMPANY_SPECTATOR); + NetworkClientConnectGame(_settings_client.network.last_host, _settings_client.network.last_port, COMPANY_SPECTATOR); break; case WID_NL_REFRESH: // Refresh diff --git a/src/openttd.cpp b/src/openttd.cpp index b204ca5a2f..016d482873 100644 --- a/src/openttd.cpp +++ b/src/openttd.cpp @@ -495,7 +495,7 @@ struct AfterNewGRFScan : NewGRFScanCallback { LoadIntroGame(); _switch_mode = SM_NONE; - NetworkClientConnectGame(NetworkAddress(network_conn, rport), join_as, join_server_password, join_company_password); + NetworkClientConnectGame(network_conn, rport, join_as, join_server_password, join_company_password); } /* After the scan we're not used anymore. */ @@ -779,7 +779,7 @@ int openttd_main(int argc, char *argv[]) ParseConnectionString(&port, debuglog_conn); if (port != nullptr) rport = atoi(port); - NetworkStartDebugLog(NetworkAddress(debuglog_conn, rport)); + NetworkStartDebugLog(debuglog_conn, rport); } if (!HandleBootstrap()) { @@ -1491,7 +1491,7 @@ void GameLoop() if (_network_reconnect > 0 && --_network_reconnect == 0) { /* This means that we want to reconnect to the last host * We do this here, because it means that the network is really closed */ - NetworkClientConnectGame(NetworkAddress(_settings_client.network.last_host, _settings_client.network.last_port), COMPANY_SPECTATOR); + NetworkClientConnectGame(_settings_client.network.last_host, _settings_client.network.last_port, COMPANY_SPECTATOR); } /* Singleplayer */ StateGameLoop(); From 84985c1223757766bad17b917a5579f84e1dec64 Mon Sep 17 00:00:00 2001 From: rubidium42 Date: Tue, 27 Apr 2021 11:17:03 +0200 Subject: [PATCH 175/268] Codechange: [Network] Do not leak os_abstraction.h via fios.h --- src/fios.h | 2 +- src/network/core/CMakeLists.txt | 1 + src/network/core/tcp_content.h | 76 +----------------------- src/network/core/tcp_content_type.h | 90 +++++++++++++++++++++++++++++ src/newgrf_config.cpp | 1 + 5 files changed, 94 insertions(+), 76 deletions(-) create mode 100644 src/network/core/tcp_content_type.h diff --git a/src/fios.h b/src/fios.h index 8d8faac07b..3a16b6426a 100644 --- a/src/fios.h +++ b/src/fios.h @@ -13,7 +13,7 @@ #include "gfx_type.h" #include "company_base.h" #include "newgrf_config.h" -#include "network/core/tcp_content.h" +#include "network/core/tcp_content_type.h" /** Special values for save-load window for the data parameter of #InvalidateWindowData. */ diff --git a/src/network/core/CMakeLists.txt b/src/network/core/CMakeLists.txt index 777d15d841..c9368a5b46 100644 --- a/src/network/core/CMakeLists.txt +++ b/src/network/core/CMakeLists.txt @@ -17,6 +17,7 @@ add_files( tcp_connect.cpp tcp_content.cpp tcp_content.h + tcp_content_type.h tcp_game.cpp tcp_game.h tcp_http.cpp diff --git a/src/network/core/tcp_content.h b/src/network/core/tcp_content.h index ef8ae3a10a..f927021f4d 100644 --- a/src/network/core/tcp_content.h +++ b/src/network/core/tcp_content.h @@ -16,81 +16,7 @@ #include "tcp.h" #include "packet.h" #include "../../debug.h" - -/** The values in the enum are important; they are used as database 'keys' */ -enum ContentType { - CONTENT_TYPE_BEGIN = 1, ///< Helper to mark the begin of the types - CONTENT_TYPE_BASE_GRAPHICS = 1, ///< The content consists of base graphics - CONTENT_TYPE_NEWGRF = 2, ///< The content consists of a NewGRF - CONTENT_TYPE_AI = 3, ///< The content consists of an AI - CONTENT_TYPE_AI_LIBRARY = 4, ///< The content consists of an AI library - CONTENT_TYPE_SCENARIO = 5, ///< The content consists of a scenario - CONTENT_TYPE_HEIGHTMAP = 6, ///< The content consists of a heightmap - CONTENT_TYPE_BASE_SOUNDS = 7, ///< The content consists of base sounds - CONTENT_TYPE_BASE_MUSIC = 8, ///< The content consists of base music - CONTENT_TYPE_GAME = 9, ///< The content consists of a game script - CONTENT_TYPE_GAME_LIBRARY = 10, ///< The content consists of a GS library - CONTENT_TYPE_END, ///< Helper to mark the end of the types -}; - -/** Enum with all types of TCP content packets. The order MUST not be changed **/ -enum PacketContentType { - PACKET_CONTENT_CLIENT_INFO_LIST, ///< Queries the content server for a list of info of a given content type - PACKET_CONTENT_CLIENT_INFO_ID, ///< Queries the content server for information about a list of internal IDs - PACKET_CONTENT_CLIENT_INFO_EXTID, ///< Queries the content server for information about a list of external IDs - PACKET_CONTENT_CLIENT_INFO_EXTID_MD5, ///< Queries the content server for information about a list of external IDs and MD5 - PACKET_CONTENT_SERVER_INFO, ///< Reply of content server with information about content - PACKET_CONTENT_CLIENT_CONTENT, ///< Request a content file given an internal ID - PACKET_CONTENT_SERVER_CONTENT, ///< Reply with the content of the given ID - PACKET_CONTENT_END, ///< Must ALWAYS be on the end of this list!! (period) -}; - -/** Unique identifier for the content. */ -enum ContentID { - INVALID_CONTENT_ID = UINT32_MAX, ///< Sentinel for invalid content. -}; - -/** Container for all important information about a piece of content. */ -struct ContentInfo { - /** The state the content can be in. */ - enum State { - UNSELECTED, ///< The content has not been selected - SELECTED, ///< The content has been manually selected - AUTOSELECTED, ///< The content has been selected as dependency - ALREADY_HERE, ///< The content is already at the client side - DOES_NOT_EXIST, ///< The content does not exist in the content system - INVALID, ///< The content's invalid - }; - - ContentType type; ///< Type of content - ContentID id; ///< Unique (server side) ID for the content - uint32 filesize; ///< Size of the file - char filename[48]; ///< Filename (for the .tar.gz; only valid on download) - char name[32]; ///< Name of the content - char version[16]; ///< Version of the content - char url[96]; ///< URL related to the content - char description[512]; ///< Description of the content - uint32 unique_id; ///< Unique ID; either GRF ID or shortname - byte md5sum[16]; ///< The MD5 checksum - uint8 dependency_count; ///< Number of dependencies - ContentID *dependencies; ///< Malloced array of dependencies (unique server side ids) - uint8 tag_count; ///< Number of tags - char (*tags)[32]; ///< Malloced array of tags (strings) - State state; ///< Whether the content info is selected (for download) - bool upgrade; ///< This item is an upgrade - - ContentInfo(); - ~ContentInfo(); - - void TransferFrom(ContentInfo *other); - - size_t Size() const; - bool IsSelected() const; - bool IsValid() const; -#ifndef OPENTTD_MSU - const char *GetTextfile(TextfileType type) const; -#endif /* OPENTTD_MSU */ -}; +#include "tcp_content_type.h" /** Base socket handler for all Content TCP sockets */ class NetworkContentSocketHandler : public NetworkTCPSocketHandler { diff --git a/src/network/core/tcp_content_type.h b/src/network/core/tcp_content_type.h new file mode 100644 index 0000000000..f4dbc0c6ee --- /dev/null +++ b/src/network/core/tcp_content_type.h @@ -0,0 +1,90 @@ +/* + * This file is part of OpenTTD. + * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. + * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see . + */ + +/** + * @file tcp_content_type.h Basic types related to the content on the content server. + */ + +#ifndef NETWORK_CORE_TCP_CONTENT_TYPE_H +#define NETWORK_CORE_TCP_CONTENT_TYPE_H + +/** The values in the enum are important; they are used as database 'keys' */ +enum ContentType { + CONTENT_TYPE_BEGIN = 1, ///< Helper to mark the begin of the types + CONTENT_TYPE_BASE_GRAPHICS = 1, ///< The content consists of base graphics + CONTENT_TYPE_NEWGRF = 2, ///< The content consists of a NewGRF + CONTENT_TYPE_AI = 3, ///< The content consists of an AI + CONTENT_TYPE_AI_LIBRARY = 4, ///< The content consists of an AI library + CONTENT_TYPE_SCENARIO = 5, ///< The content consists of a scenario + CONTENT_TYPE_HEIGHTMAP = 6, ///< The content consists of a heightmap + CONTENT_TYPE_BASE_SOUNDS = 7, ///< The content consists of base sounds + CONTENT_TYPE_BASE_MUSIC = 8, ///< The content consists of base music + CONTENT_TYPE_GAME = 9, ///< The content consists of a game script + CONTENT_TYPE_GAME_LIBRARY = 10, ///< The content consists of a GS library + CONTENT_TYPE_END, ///< Helper to mark the end of the types +}; + +/** Enum with all types of TCP content packets. The order MUST not be changed **/ +enum PacketContentType { + PACKET_CONTENT_CLIENT_INFO_LIST, ///< Queries the content server for a list of info of a given content type + PACKET_CONTENT_CLIENT_INFO_ID, ///< Queries the content server for information about a list of internal IDs + PACKET_CONTENT_CLIENT_INFO_EXTID, ///< Queries the content server for information about a list of external IDs + PACKET_CONTENT_CLIENT_INFO_EXTID_MD5, ///< Queries the content server for information about a list of external IDs and MD5 + PACKET_CONTENT_SERVER_INFO, ///< Reply of content server with information about content + PACKET_CONTENT_CLIENT_CONTENT, ///< Request a content file given an internal ID + PACKET_CONTENT_SERVER_CONTENT, ///< Reply with the content of the given ID + PACKET_CONTENT_END, ///< Must ALWAYS be on the end of this list!! (period) +}; + +/** Unique identifier for the content. */ +enum ContentID { + INVALID_CONTENT_ID = UINT32_MAX, ///< Sentinel for invalid content. +}; + +/** Container for all important information about a piece of content. */ +struct ContentInfo { + /** The state the content can be in. */ + enum State { + UNSELECTED, ///< The content has not been selected + SELECTED, ///< The content has been manually selected + AUTOSELECTED, ///< The content has been selected as dependency + ALREADY_HERE, ///< The content is already at the client side + DOES_NOT_EXIST, ///< The content does not exist in the content system + INVALID, ///< The content's invalid + }; + + ContentType type; ///< Type of content + ContentID id; ///< Unique (server side) ID for the content + uint32 filesize; ///< Size of the file + char filename[48]; ///< Filename (for the .tar.gz; only valid on download) + char name[32]; ///< Name of the content + char version[16]; ///< Version of the content + char url[96]; ///< URL related to the content + char description[512]; ///< Description of the content + uint32 unique_id; ///< Unique ID; either GRF ID or shortname + byte md5sum[16]; ///< The MD5 checksum + uint8 dependency_count; ///< Number of dependencies + ContentID *dependencies; ///< Malloced array of dependencies (unique server side ids) + uint8 tag_count; ///< Number of tags + char (*tags)[32]; ///< Malloced array of tags (strings) + State state; ///< Whether the content info is selected (for download) + bool upgrade; ///< This item is an upgrade + + ContentInfo(); + ~ContentInfo(); + + void TransferFrom(ContentInfo *other); + + size_t Size() const; + bool IsSelected() const; + bool IsValid() const; +#ifndef OPENTTD_MSU + const char *GetTextfile(TextfileType type) const; +#endif /* OPENTTD_MSU */ +}; + +#endif /* NETWORK_CORE_TCP_CONTENT_TYPE_H */ diff --git a/src/newgrf_config.cpp b/src/newgrf_config.cpp index a0e60ef755..d7919e32ae 100644 --- a/src/newgrf_config.cpp +++ b/src/newgrf_config.cpp @@ -17,6 +17,7 @@ #include "window_func.h" #include "progress.h" #include "video/video_driver.hpp" +#include "string_func.h" #include "strings_func.h" #include "textfile_gui.h" #include "thread.h" From 8c2e3a004ee362b59bc9f918aa4fb6d4d7baedba Mon Sep 17 00:00:00 2001 From: rubidium42 Date: Tue, 27 Apr 2021 11:20:27 +0200 Subject: [PATCH 176/268] Codechange: [Network] Do not leak os_abstraction.h via base_media_func.h --- src/base_media_func.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/base_media_func.h b/src/base_media_func.h index 8491f0e510..f5a5995f17 100644 --- a/src/base_media_func.h +++ b/src/base_media_func.h @@ -274,7 +274,7 @@ template return p; } -#include "network/network_content.h" +#include "network/core/tcp_content_type.h" template const char *TryGetBaseSetFile(const ContentInfo *ci, bool md5sum, const Tbase_set *s) { From 4880ec29e4649655e775c36cd6bd8c0927d13ee1 Mon Sep 17 00:00:00 2001 From: rubidium42 Date: Tue, 27 Apr 2021 11:30:56 +0200 Subject: [PATCH 177/268] Change: [Network] Safeguard from using errno/strerror for handling network errors They are likely not working as expected on Windows, so prevent their usage. Winsock does not set errno and strerror does not return anything useful for Winsock error numbers. --- src/safeguards.h | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/safeguards.h b/src/safeguards.h index 5351116ecb..e3d6c4a3e4 100644 --- a/src/safeguards.h +++ b/src/safeguards.h @@ -69,4 +69,17 @@ #undef abs #endif +#if defined(NETWORK_CORE_OS_ABSTRACTION_H) && defined(_WIN32) +/* Use NetworkGetLastError() instead of errno, or do not (indirectly) include network/core/os_abstraction.h. + * Winsock does not set errno, but one should rather call WSAGetLastError. NetworkGetLastError abstracts that away. */ +#ifdef errno +#undef errno +#endif +#define errno SAFEGUARD_DO_NOT_USE_THIS_METHOD + +/* Use NetworkGetLastErrorString() instead of strerror, or do not (indirectly) include network/core/os_abstraction.h. + * Winsock errors are not handled by strerror, but one should rather call FormatMessage. NetworkGetLastErrorString abstracts that away. */ +#define strerror SAFEGUARD_DO_NOT_USE_THIS_METHOD +#endif /* defined(NETWORK_CORE_OS_ABSTRACTION_H) && defined(_WIN32) */ + #endif /* SAFEGUARDS_H */ From b54d8a49fb8d635545b2251bfdf41ebee60edc0c Mon Sep 17 00:00:00 2001 From: rubidium42 Date: Tue, 27 Apr 2021 18:14:44 +0200 Subject: [PATCH 178/268] Feature: allow non-ASCII currency separators --- src/settings.cpp | 3 --- src/settings_gui.cpp | 6 +++--- src/table/currency_settings.ini | 4 ++-- src/table/settings.h.preamble | 3 --- 4 files changed, 5 insertions(+), 11 deletions(-) diff --git a/src/settings.cpp b/src/settings.cpp index fdb26368cf..4a04784bca 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -563,8 +563,6 @@ static void IniLoadSettings(IniFile *ini, const SettingDesc *sd, const char *grp *(char**)ptr = p == nullptr ? nullptr : stredup((const char*)p); break; - case SLE_VAR_CHAR: if (p != nullptr) *(char *)ptr = *(const char *)p; break; - default: NOT_REACHED(); } break; @@ -716,7 +714,6 @@ static void IniSaveSettings(IniFile *ini, const SettingDesc *sd, const char *grp } break; - case SLE_VAR_CHAR: buf[0] = *(char*)ptr; buf[1] = '\0'; break; default: NOT_REACHED(); } break; diff --git a/src/settings_gui.cpp b/src/settings_gui.cpp index f5872c648f..52d900e6e8 100644 --- a/src/settings_gui.cpp +++ b/src/settings_gui.cpp @@ -2676,7 +2676,7 @@ struct CustomCurrencyWindow : Window { case WID_CC_SEPARATOR: SetDParamStr(0, _custom_currency.separator); str = STR_JUST_RAW_STRING; - len = 1; + len = sizeof(_custom_currency.separator) - 1; // Number of characters excluding '\0' termination line = WID_CC_SEPARATOR; break; @@ -2684,7 +2684,7 @@ struct CustomCurrencyWindow : Window { case WID_CC_PREFIX: SetDParamStr(0, _custom_currency.prefix); str = STR_JUST_RAW_STRING; - len = 12; + len = sizeof(_custom_currency.prefix) - 1; // Number of characters excluding '\0' termination line = WID_CC_PREFIX; break; @@ -2692,7 +2692,7 @@ struct CustomCurrencyWindow : Window { case WID_CC_SUFFIX: SetDParamStr(0, _custom_currency.suffix); str = STR_JUST_RAW_STRING; - len = 12; + len = sizeof(_custom_currency.suffix) - 1; // Number of characters excluding '\0' termination line = WID_CC_SUFFIX; break; diff --git a/src/table/currency_settings.ini b/src/table/currency_settings.ini index c242c83a87..3e51d0240a 100644 --- a/src/table/currency_settings.ini +++ b/src/table/currency_settings.ini @@ -10,7 +10,6 @@ static const SettingDesc _currency_settings[] = { }; [templates] SDT_VAR = SDT_VAR($base, $var, $type, $flags, $guiflags, $def, $min, $max, $interval, $str, $strhelp, $strval, $proc, $from, $to, $cat, $extra, $startup), -SDT_CHR = SDT_CHR($base, $var, $flags, $guiflags, $def, $str, $strhelp, $strval, $proc, $from, $to, $cat, $extra, $startup), SDT_STR = SDT_STR($base, $var, $type, $flags, $guiflags, $def, $str, $strhelp, $strval, $proc, $from, $to, $cat, $extra, $startup), SDT_END = SDT_END() @@ -42,9 +41,10 @@ def = 1 min = 0 max = UINT16_MAX -[SDT_CHR] +[SDT_STR] base = CurrencySpec var = separator +type = SLE_STRBQ def = ""."" cat = SC_BASIC diff --git a/src/table/settings.h.preamble b/src/table/settings.h.preamble index d7084d7247..c3e0678b7b 100644 --- a/src/table/settings.h.preamble +++ b/src/table/settings.h.preamble @@ -107,9 +107,6 @@ static size_t ConvertLandscape(const char *value); #define SDT_STR(base, var, type, flags, guiflags, def, str, strhelp, strval, proc, from, to, cat, extra, startup)\ SDT_GENERAL(#var, SDT_STRING, SL_STR, type, flags, guiflags, base, var, sizeof(((base*)8)->var), def, 0, 0, 0, nullptr, str, strhelp, strval, proc, nullptr, from, to, cat, extra, startup) -#define SDT_CHR(base, var, flags, guiflags, def, str, strhelp, strval, proc, from, to, cat, extra, startup)\ - SDT_GENERAL(#var, SDT_STRING, SL_VAR, SLE_CHAR, flags, guiflags, base, var, 1, def, 0, 0, 0, nullptr, str, strhelp, strval, proc, nullptr, from, to, cat, extra, startup) - #define SDT_OMANY(base, var, type, flags, guiflags, def, max, full, str, strhelp, strval, proc, from, to, load, cat, extra, startup)\ SDT_GENERAL(#var, SDT_ONEOFMANY, SL_VAR, type, flags, guiflags, base, var, 1, def, 0, max, 0, full, str, strhelp, strval, proc, load, from, to, cat, extra, startup) From 0e449f20dcec4e9a04b218205453b40deb947382 Mon Sep 17 00:00:00 2001 From: rubidium42 Date: Tue, 27 Apr 2021 18:24:33 +0200 Subject: [PATCH 179/268] Codechange: writing and string validation to its own functions --- src/settings.cpp | 91 ++++++++++++++++++++++++++++++------------------ 1 file changed, 57 insertions(+), 34 deletions(-) diff --git a/src/settings.cpp b/src/settings.cpp index 4a04784bca..3095c6079c 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -495,6 +495,54 @@ static void Write_ValidateSetting(void *ptr, const SettingDesc *sd, int32 val) WriteValue(ptr, sd->save.conv, (int64)val); } +/** + * Set the string value of a setting. + * @param ptr Pointer to the storage location (might be a pointer to a pointer). + * @param sld Pointer to the information for the conversions and limitations to apply. + * @param p The string to save. + */ +static void Write_ValidateString(void *ptr, const SaveLoad *sld, const char *p) +{ + switch (GetVarMemType(sld->conv)) { + case SLE_VAR_STRB: + case SLE_VAR_STRBQ: + if (p != nullptr) strecpy((char*)ptr, (const char*)p, (char*)ptr + sld->length - 1); + break; + + case SLE_VAR_STR: + case SLE_VAR_STRQ: + free(*(char**)ptr); + *(char**)ptr = p == nullptr ? nullptr : stredup(p); + break; + + default: NOT_REACHED(); + } +} + +/** + * Set the string value of a setting. + * @param ptr Pointer to the std::string. + * @param sld Pointer to the information for the conversions and limitations to apply. + * @param p The string to save. + */ +static void Write_ValidateStdString(void *ptr, const SaveLoad *sld, const char *p) +{ + std::string *dst = reinterpret_cast(ptr); + + switch (GetVarMemType(sld->conv)) { + case SLE_VAR_STR: + case SLE_VAR_STRQ: + if (p != nullptr) { + dst->assign(p); + } else { + dst->clear(); + } + break; + + default: NOT_REACHED(); + } +} + /** * Load values from a group of an IniFile structure into the internal representation * @param ini pointer to IniFile structure that holds administrative information @@ -551,36 +599,11 @@ static void IniLoadSettings(IniFile *ini, const SettingDesc *sd, const char *grp break; case SDT_STRING: - switch (GetVarMemType(sld->conv)) { - case SLE_VAR_STRB: - case SLE_VAR_STRBQ: - if (p != nullptr) strecpy((char*)ptr, (const char*)p, (char*)ptr + sld->length - 1); - break; - - case SLE_VAR_STR: - case SLE_VAR_STRQ: - free(*(char**)ptr); - *(char**)ptr = p == nullptr ? nullptr : stredup((const char*)p); - break; - - default: NOT_REACHED(); - } + Write_ValidateString(ptr, sld, (const char *)p); break; case SDT_STDSTRING: - switch (GetVarMemType(sld->conv)) { - case SLE_VAR_STR: - case SLE_VAR_STRQ: - if (p != nullptr) { - reinterpret_cast(ptr)->assign((const char *)p); - } else { - reinterpret_cast(ptr)->clear(); - } - break; - - default: NOT_REACHED(); - } - + Write_ValidateStdString(ptr, sld, (const char *)p); break; case SDT_INTLIST: { @@ -2082,13 +2105,13 @@ bool SetSettingValue(uint index, const char *value, bool force_newgame) const SettingDesc *sd = &_settings[index]; assert(sd->save.conv & SLF_NO_NETWORK_SYNC); - if (GetVarMemType(sd->save.conv) == SLE_VAR_STRQ) { - char **var = (char**)GetVariableAddress((_game_mode == GM_MENU || force_newgame) ? &_settings_newgame : &_settings_game, &sd->save); - free(*var); - *var = strcmp(value, "(null)") == 0 ? nullptr : stredup(value); - } else { - char *var = (char*)GetVariableAddress(nullptr, &sd->save); - strecpy(var, value, &var[sd->save.length - 1]); + if (GetVarMemType(sd->save.conv) == SLE_VAR_STRQ && strcmp(value, "(null)") == 0) { + value = nullptr; + } + + void *ptr = GetVariableAddress((_game_mode == GM_MENU || force_newgame) ? &_settings_newgame : &_settings_game, &sd->save); + if (sd->desc.cmd == SDT_STRING) { + Write_ValidateString(ptr, &sd->save, value); } if (sd->desc.proc != nullptr) sd->desc.proc(0); From 31c87ba90813a38222002d717d7f03dcf30ebced Mon Sep 17 00:00:00 2001 From: rubidium42 Date: Tue, 27 Apr 2021 18:25:53 +0200 Subject: [PATCH 180/268] Fix: truncating strings in settings could leave invalid Utf8 characters --- src/settings.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/settings.cpp b/src/settings.cpp index 3095c6079c..9b97107a34 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -506,7 +506,12 @@ static void Write_ValidateString(void *ptr, const SaveLoad *sld, const char *p) switch (GetVarMemType(sld->conv)) { case SLE_VAR_STRB: case SLE_VAR_STRBQ: - if (p != nullptr) strecpy((char*)ptr, (const char*)p, (char*)ptr + sld->length - 1); + if (p != nullptr) { + char *begin = (char*)ptr; + char *end = begin + sld->length - 1; + strecpy(begin, p, end); + str_validate(begin, end, SVS_NONE); + } break; case SLE_VAR_STR: From b89dba7e4e60c3387f74690687174080bac911cb Mon Sep 17 00:00:00 2001 From: translators Date: Tue, 27 Apr 2021 17:53:43 +0000 Subject: [PATCH 181/268] Update: Translations from eints japanese: 26 changes by scabtert catalan: 43 changes by J0anJosep --- src/lang/catalan.txt | 45 +++++++++++++++++++++++++++++++++++++++++-- src/lang/japanese.txt | 26 +++++++++++++++++++++++++ 2 files changed, 69 insertions(+), 2 deletions(-) diff --git a/src/lang/catalan.txt b/src/lang/catalan.txt index 5ad9f92378..6dd49f586b 100644 --- a/src/lang/catalan.txt +++ b/src/lang/catalan.txt @@ -954,7 +954,7 @@ STR_GAME_OPTIONS_CURRENCY_MYR :Ringgit (MYR) STR_GAME_OPTIONS_ROAD_VEHICLES_DROPDOWN_LEFT :Conducció per l'esquerra STR_GAME_OPTIONS_ROAD_VEHICLES_DROPDOWN_RIGHT :Conducció per la dreta -STR_GAME_OPTIONS_TOWN_NAMES_FRAME :{BLACK}Estil dels noms de poblacions +STR_GAME_OPTIONS_TOWN_NAMES_FRAME :{BLACK}Estil dels noms de les poblacions: STR_GAME_OPTIONS_TOWN_NAMES_DROPDOWN_TOOLTIP :{BLACK}Selecciona l'estil dels noms de poblacions ############ start of townname region @@ -994,6 +994,7 @@ STR_GAME_OPTIONS_AUTOSAVE_DROPDOWN_EVERY_12_MONTHS :Cada 12 mesos STR_GAME_OPTIONS_LANGUAGE :{BLACK}Idioma STR_GAME_OPTIONS_LANGUAGE_TOOLTIP :{BLACK}Selecciona l'idioma de la interfície +STR_GAME_OPTIONS_LANGUAGE_PERCENTAGE :{STRING} ({NUM}{NBSP}% completed) STR_GAME_OPTIONS_FULLSCREEN :{BLACK}Pantalla completa STR_GAME_OPTIONS_FULLSCREEN_TOOLTIP :{BLACK}Marqueu la casella per mostrar l'OpenTTD a pantalla completa. @@ -1991,6 +1992,8 @@ STR_FACE_TIE :Corbata: STR_FACE_EARRING :Arracades: STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}Canvia la corbata o les arracades +STR_NETWORK_SERVER_VISIBILITY_PRIVATE :Privada +STR_NETWORK_SERVER_VISIBILITY_PUBLIC :Pública # Network server list STR_NETWORK_SERVER_LIST_CAPTION :{WHITE}Multijugador @@ -2054,6 +2057,8 @@ STR_NETWORK_START_SERVER_NEW_GAME_NAME_TOOLTIP :{BLACK}El nom d STR_NETWORK_START_SERVER_SET_PASSWORD :{BLACK}Posa una contrasenya STR_NETWORK_START_SERVER_PASSWORD_TOOLTIP :{BLACK}Protegeix la teva partida amb una contrasenya si no vols que sigui accessible a desconeguts +STR_NETWORK_START_SERVER_VISIBILITY_LABEL :{BLACK}Visibilitat +STR_NETWORK_START_SERVER_VISIBILITY_TOOLTIP :{BLACK}Permet establir si altres persones poden veure el vostre servidor a la llista pública. STR_NETWORK_START_SERVER_CLIENTS_SELECT :{BLACK}{NUM} client{P "" s} STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS :{BLACK}Màxim nombre de clients: STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS_TOOLTIP :{BLACK}Tria el nombre màxim de clients. No és necessari omplir tots els llocs. @@ -2117,11 +2122,44 @@ STR_NETWORK_NEED_GAME_PASSWORD_CAPTION :{WHITE}Servidor STR_NETWORK_NEED_COMPANY_PASSWORD_CAPTION :{WHITE}Companyia protegida: escriviu-ne la contrasenya # Network company list added strings -STR_NETWORK_COMPANY_LIST_CLIENT_LIST :Llista de clients +STR_NETWORK_COMPANY_LIST_CLIENT_LIST :Jugadors en línia # Network client list +STR_NETWORK_CLIENT_LIST_CAPTION :{WHITE}Multijugador +STR_NETWORK_CLIENT_LIST_SERVER :{BLACK}Servidor +STR_NETWORK_CLIENT_LIST_SERVER_NAME :{BLACK}Nom +STR_NETWORK_CLIENT_LIST_SERVER_NAME_TOOLTIP :{BLACK}Nom del servidor on esteu jugant +STR_NETWORK_CLIENT_LIST_SERVER_NAME_EDIT_TOOLTIP :{BLACK}Editeu el nom del vostre servidor. +STR_NETWORK_CLIENT_LIST_SERVER_NAME_QUERY_CAPTION :Nom del servidor +STR_NETWORK_CLIENT_LIST_SERVER_VISIBILITY :{BLACK}Visibilitat +STR_NETWORK_CLIENT_LIST_SERVER_VISIBILITY_TOOLTIP :{BLACK}Permet establir si altres persones poden veure el vostre servidor a la llista pública. +STR_NETWORK_CLIENT_LIST_PLAYER :{BLACK}Jugador +STR_NETWORK_CLIENT_LIST_PLAYER_NAME :{BLACK}Nom +STR_NETWORK_CLIENT_LIST_PLAYER_NAME_TOOLTIP :{BLACK}El vostre nom de jugador +STR_NETWORK_CLIENT_LIST_PLAYER_NAME_EDIT_TOOLTIP :{BLACK}Editeu el vostre nom de jugador. +STR_NETWORK_CLIENT_LIST_PLAYER_NAME_QUERY_CAPTION :El vostre nom de jugador +STR_NETWORK_CLIENT_LIST_ADMIN_CLIENT_TOOLTIP :{BLACK}Accions d'administració que s'han de realitzar per a aquest client. +STR_NETWORK_CLIENT_LIST_ADMIN_COMPANY_TOOLTIP :{BLACK}Accions d'administració que s'han de realitzar per a aquesta companyia. +STR_NETWORK_CLIENT_LIST_JOIN_TOOLTIP :{BLACK}Uniu-vos a aquesta companyia. +STR_NETWORK_CLIENT_LIST_CHAT_CLIENT_TOOLTIP :{BLACK}Envia un missatge a aquest jugador. +STR_NETWORK_CLIENT_LIST_CHAT_COMPANY_TOOLTIP :{BLACK}Envia un missatge a tots els jugadors de la companyia. +STR_NETWORK_CLIENT_LIST_CHAT_SPECTATOR_TOOLTIP :{BLACK}Envieu un missatge a tots els espectadors. +STR_NETWORK_CLIENT_LIST_SPECTATORS :Espectadors +STR_NETWORK_CLIENT_LIST_NEW_COMPANY :(Companyia nova) +STR_NETWORK_CLIENT_LIST_NEW_COMPANY_TOOLTIP :{BLACK}Crea una companyia nova i uniu-vos. +STR_NETWORK_CLIENT_LIST_PLAYER_ICON_SELF_TOOLTIP :{BLACK}Aquest ets tu. +STR_NETWORK_CLIENT_LIST_PLAYER_ICON_HOST_TOOLTIP :{BLACK}Aquest és l'hoste de la partida. +STR_NETWORK_CLIENT_LIST_ADMIN_CLIENT_KICK :Treu +STR_NETWORK_CLIENT_LIST_ADMIN_CLIENT_BAN :Expulsa +STR_NETWORK_CLIENT_LIST_ADMIN_COMPANY_RESET :Esborra +STR_NETWORK_CLIENT_LIST_ADMIN_COMPANY_UNLOCK :Desbloca la contrasenya +STR_NETWORK_CLIENT_LIST_ASK_CAPTION :{WHITE}Acció de l'administrador +STR_NETWORK_CLIENT_LIST_ASK_CLIENT_KICK :{YELLOW}Esteu segur que voleu treure el jugador «{STRING}»? +STR_NETWORK_CLIENT_LIST_ASK_CLIENT_BAN :{YELLOW}Esteu segur que voleu expulsar el jugador «{STRING}»? +STR_NETWORK_CLIENT_LIST_ASK_COMPANY_RESET :{YELLOW}Esteu segur que voleu esborrar la companyia «{COMPANY}»? +STR_NETWORK_CLIENT_LIST_ASK_COMPANY_UNLOCK :{YELLOW}Esteu segur que voleu restablir la contrasenya de la companyia «{COMPANY}»? STR_NETWORK_SERVER :Servidor STR_NETWORK_CLIENT :Client @@ -2166,6 +2204,7 @@ STR_NETWORK_ERROR_SERVER_START :{WHITE}No s'ha STR_NETWORK_ERROR_CLIENT_START :{WHITE}No s'ha pogut connectar STR_NETWORK_ERROR_TIMEOUT :{WHITE}La connexió #{NUM} ha esgotat el temps d'espera STR_NETWORK_ERROR_SERVER_ERROR :{WHITE}S'ha obtingut un error de protocol i s'ha tancat la connexió +STR_NETWORK_ERROR_BAD_PLAYER_NAME :{WHITE}No s'ha escollit un nom per al vostre jugador. El nom es pot establir a la part superior de la finestra de mode multijugador. STR_NETWORK_ERROR_WRONG_REVISION :{WHITE}La revisió d'aquest client no concorda amb la revisió del servidor STR_NETWORK_ERROR_WRONG_PASSWORD :{WHITE}Contrasenya incorrecta STR_NETWORK_ERROR_SERVER_FULL :{WHITE}El servidor està ple @@ -2178,6 +2217,7 @@ STR_NETWORK_ERROR_TIMEOUT_PASSWORD :{WHITE}Has tard STR_NETWORK_ERROR_TIMEOUT_COMPUTER :{WHITE}El teu ordinador és massa lent per mantenir-se connectat al servidor STR_NETWORK_ERROR_TIMEOUT_MAP :{WHITE}El teu ordinador ha tardat massa a descarregar el mapa STR_NETWORK_ERROR_TIMEOUT_JOIN :{WHITE}El teu ordinador ha tardat massa a unir-se al servidor +STR_NETWORK_ERROR_INVALID_CLIENT_NAME :{WHITE}El vostre nom de jugador no és vàlid. ############ Leave those lines in this order!! STR_NETWORK_ERROR_CLIENT_GENERAL :error general @@ -2200,6 +2240,7 @@ STR_NETWORK_ERROR_CLIENT_TIMEOUT_PASSWORD :no s'ha rebut l STR_NETWORK_ERROR_CLIENT_TIMEOUT_COMPUTER :temps d'espera general esgotat STR_NETWORK_ERROR_CLIENT_TIMEOUT_MAP :la descàrrega del mapa ha tardat massa STR_NETWORK_ERROR_CLIENT_TIMEOUT_JOIN :el processat del mapa ha tardat massa +STR_NETWORK_ERROR_CLIENT_INVALID_CLIENT_NAME :nom de client no vàlid ############ End of leave-in-this-order STR_NETWORK_ERROR_CLIENT_GUI_LOST_CONNECTION_CAPTION :{WHITE}Possible pèrdua de connexió diff --git a/src/lang/japanese.txt b/src/lang/japanese.txt index 867f929d1f..b066e74c37 100644 --- a/src/lang/japanese.txt +++ b/src/lang/japanese.txt @@ -1114,6 +1114,7 @@ STR_TERRAIN_TYPE_HILLY :丘陵地 STR_TERRAIN_TYPE_MOUNTAINOUS :山岳地 STR_TERRAIN_TYPE_ALPINIST :山脈地帯 STR_TERRAIN_TYPE_CUSTOM :カスタム高度 +STR_TERRAIN_TYPE_CUSTOM_VALUE :カスタム高度 ({NUM}) STR_CITY_APPROVAL_PERMISSIVE :寛大 STR_CITY_APPROVAL_TOLERANT :寛容 @@ -1197,6 +1198,7 @@ STR_CONFIG_SETTING_CITY_APPROVAL :地域の再編 STR_CONFIG_SETTING_CITY_APPROVAL_HELPTEXT :会社が街域で引き起こした騒音(主に空港)や環境破壊がどの程度、街での評価や同じ地域での更なる建設行為に影響するかを設定します STR_CONFIG_SETTING_MAP_HEIGHT_LIMIT :マップ高さ限界: {STRING} +STR_CONFIG_SETTING_MAP_HEIGHT_LIMIT_AUTO :(自動) STR_CONFIG_SETTING_TOO_HIGH_MOUNTAIN :{WHITE}マップの最高高さをこの値には設定出来ません。少なくとも1箇所以上この値より高い山があります。 STR_CONFIG_SETTING_AUTOSLOPE :建物/路線の自動地形追従: {STRING} STR_CONFIG_SETTING_AUTOSLOPE_HELPTEXT :撤去を行わないで建物や路線がある土地の地形を変更することを可能にします。建物/路線は変更された地形に自動で追従します。 @@ -1340,6 +1342,7 @@ STR_CONFIG_SETTING_OIL_REF_EDGE_DISTANCE :石油精製所 STR_CONFIG_SETTING_OIL_REF_EDGE_DISTANCE_HELPTEXT :石油精製所はマップの外周付近にのみ建設されます。つまり、外周が海のマップでは海岸沿いに建設されるということです STR_CONFIG_SETTING_SNOWLINE_HEIGHT :雪線の位置: {STRING} STR_CONFIG_SETTING_SNOWLINE_HEIGHT_HELPTEXT :亜寒帯気候での雪線の高さを設定します。雪は産業と街の成長に影響があります +STR_CONFIG_SETTING_SNOW_COVERAGE_HELPTEXT :亜寒帯の風景のおおよその雪の量を制御します。雪はまた、産業の生成と町の成長要件にも影響を及ぼします。マップの生成中にのみ使用されます。海抜のすぐ上の土地は常に雪がありません STR_CONFIG_SETTING_SNOW_COVERAGE_VALUE :{NUM}% STR_CONFIG_SETTING_ROUGHNESS_OF_TERRAIN :地形の起伏: {STRING} STR_CONFIG_SETTING_ROUGHNESS_OF_TERRAIN_HELPTEXT :(TerraGenesisのみ) 地形の起伏度を設定します。なだらかな地形では丘陵の数は減り、裾野が長くなります。起伏が多い地形では丘陵が多くなりますが、似たり寄ったりな地形の繰り返しに見えることがあります @@ -1450,6 +1453,7 @@ STR_CONFIG_SETTING_PERSISTENT_BUILDINGTOOLS :建設ツール STR_CONFIG_SETTING_PERSISTENT_BUILDINGTOOLS_HELPTEXT :橋やトンネルなどを建設した後もツールバーを開いたままにします STR_CONFIG_SETTING_EXPENSES_LAYOUT :財政ウィンドウのグループ分け: {STRING} STR_CONFIG_SETTING_EXPENSES_LAYOUT_HELPTEXT :財政ウィンドウのレイアウトを収入部門・支出部門でグループ分けするかどうかを設定します +STR_CONFIG_SETTING_FAST_FORWARD_SPEED_LIMIT_HELPTEXT :早送りが有効になっている場合のゲームの進行速度を制限します。0 =制限なし(コンピューターが許す限り高速)。100%未満の値は、ゲームの速度を低下させます。上限はコンピュータの仕様によって異なり、ゲームによって異なる場合があります。 STR_CONFIG_SETTING_FAST_FORWARD_SPEED_LIMIT_VAL :{NUM}%通常のゲーム速度 STR_CONFIG_SETTING_FAST_FORWARD_SPEED_LIMIT_ZERO :制限なし(コンピューターが許す限り高速) @@ -1500,6 +1504,7 @@ STR_CONFIG_SETTING_AI_IN_MULTIPLAYER :マルチプレ STR_CONFIG_SETTING_AI_IN_MULTIPLAYER_HELPTEXT :マルチプレイヤーゲームでもAIのライバル企業が登場するかを設定します STR_CONFIG_SETTING_SCRIPT_MAX_OPCODES :命令コード処理上限: {STRING} STR_CONFIG_SETTING_SCRIPT_MAX_OPCODES_HELPTEXT :AIやゲームスクリプトが一つの「詰め込み指令」を処理する際に、一度に演算できる命令コード数を設定します。一般に値を小さくした場合、ゲームへの負荷が軽減されます +STR_CONFIG_SETTING_SCRIPT_MAX_MEMORY :スクリプトあたりの最大メモリ使用量:{STRING} STR_CONFIG_SETTING_SCRIPT_MAX_MEMORY_VALUE :{COMMA} MiB STR_CONFIG_SETTING_SERVINT_ISPERCENT :最大信頼度を点検要件化: {STRING} @@ -1631,6 +1636,7 @@ STR_CONFIG_SETTING_ZOOM_MIN :最大ズーム STR_CONFIG_SETTING_ZOOM_MIN_HELPTEXT :ズームインの最大倍率を設定します。倍率を高くすればするほどメモリー使用量が増えます STR_CONFIG_SETTING_ZOOM_MAX :最大ズームアウトレベル:{STRING} STR_CONFIG_SETTING_ZOOM_MAX_HELPTEXT :ズームアウトの最大倍率を設定します。ズームアウトの倍率が大きいと、処理遅延が発生する可能性があります +STR_CONFIG_SETTING_SPRITE_ZOOM_MIN :使用する最高解像度のスプライト:{STRING} STR_CONFIG_SETTING_SPRITE_ZOOM_MIN_HELPTEXT :スプライトに使用する最大解像度を制限します。 スプライトの解像度を制限すると、使用可能な場合でも高解像度のグラフィックを使用できなくなります。 これにより、高解像度のグラフィックを使用する場合と使用しない場合のGRFファイルを組み合わせて使用する場合に、ゲームの外観を統一することができます。 STR_CONFIG_SETTING_ZOOM_LVL_MIN :4倍 STR_CONFIG_SETTING_ZOOM_LVL_IN_2X :2倍 @@ -2002,6 +2008,7 @@ STR_NETWORK_START_SERVER_NEW_GAME_NAME_TOOLTIP :{BLACK}ゲー STR_NETWORK_START_SERVER_SET_PASSWORD :{BLACK}パスワードを設定 STR_NETWORK_START_SERVER_PASSWORD_TOOLTIP :{BLACK}ゲームをパスワードで保護することができます。一般から公然とアクセスされたくない場合等に設定します +STR_NETWORK_START_SERVER_VISIBILITY_LABEL :{BLACK}可視性 STR_NETWORK_START_SERVER_CLIENTS_SELECT :{BLACK}接続者数: {NUM} STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS :{BLACK}最大接続数: STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS_TOOLTIP :{BLACK}接続できるクライアントの最大数を指定します。必ずしも全スロットを埋める必要はありません @@ -2068,8 +2075,13 @@ STR_NETWORK_NEED_COMPANY_PASSWORD_CAPTION :{WHITE}この STR_NETWORK_COMPANY_LIST_CLIENT_LIST :クライアントリスト # Network client list +STR_NETWORK_CLIENT_LIST_SERVER_VISIBILITY_TOOLTIP :{BLACK}他の人があなたのサーバーを公開リストで見ることができるかどうか +STR_NETWORK_CLIENT_LIST_CHAT_SPECTATOR_TOOLTIP :{BLACK}すべての観客にメッセージを送る +STR_NETWORK_CLIENT_LIST_PLAYER_ICON_HOST_TOOLTIP :{BLACK}ゲームのホストです +STR_NETWORK_CLIENT_LIST_ASK_CLIENT_BAN :{YELLOW}プレーヤー「{STRING}」を追放してもよろしいですか? +STR_NETWORK_CLIENT_LIST_ASK_COMPANY_UNLOCK :{YELLOW}会社 '{COMPANY}'のパスワードをリセットしてもよろしいですか? STR_NETWORK_SERVER :サーバー STR_NETWORK_CLIENT :クライアント @@ -2119,6 +2131,7 @@ STR_NETWORK_ERROR_WRONG_PASSWORD :{WHITE}不正 STR_NETWORK_ERROR_SERVER_FULL :{WHITE}サーバが満員です STR_NETWORK_ERROR_SERVER_BANNED :{WHITE}サーバー側であなたの参加が禁止されています STR_NETWORK_ERROR_KICKED :{WHITE}ゲームから追放されました +STR_NETWORK_ERROR_KICK_MESSAGE :{WHITE}理由:{STRING} STR_NETWORK_ERROR_CHEATER :{WHITE}このサーバーではチート行為は許可されていません STR_NETWORK_ERROR_TOO_MANY_COMMANDS :{WHITE}サーバーに送ったコマンド数が過剰です STR_NETWORK_ERROR_TIMEOUT_PASSWORD :{WHITE}パスワード入力時間切れです @@ -2256,6 +2269,7 @@ STR_MISSING_GRAPHICS_NO_QUIT :{BLACK}いい STR_MISSING_GRAPHICS_ERROR_TITLE :{WHITE}ダウンロードに失敗しました STR_MISSING_GRAPHICS_ERROR :{BLACK}グラフィックのダウンロードに失敗しました。{}手動でダウンロードしてください。 +STR_MISSING_GRAPHICS_ERROR_QUIT :{BLACK}OpenTTDをやめる # Transparency settings window STR_TRANSPARENCY_CAPTION :{WHITE}透過表示設定 @@ -2404,6 +2418,7 @@ STR_ROAD_TOOLBAR_TOOLTIP_TOGGLE_BUILD_REMOVE_FOR_ROAD :{BLACK}道路 STR_ROAD_TOOLBAR_TOOLTIP_TOGGLE_BUILD_REMOVE_FOR_TRAMWAYS :{BLACK}軌道の建設/撤去を切り替えます STR_ROAD_TOOLBAR_TOOLTIP_CONVERT_ROAD :{BLACK}道路の種類を変更/アップグレードします.Shiftは、コスト見積もりの作成/表示を切り替えます +STR_ROAD_NAME_ROAD :道路 STR_ROAD_NAME_TRAM :トラムウェイ # Road depot construction window @@ -2492,6 +2507,7 @@ STR_TREES_RANDOM_TYPE :{BLACK}ラン STR_TREES_RANDOM_TYPE_TOOLTIP :{BLACK}ランダムな樹類で植林します。Shift+クリックで費用を見積もります STR_TREES_RANDOM_TREES_BUTTON :{BLACK}ランダムに広域植林 STR_TREES_RANDOM_TREES_TOOLTIP :{BLACK}地表全体にランダムに植林します +STR_TREES_MODE_NORMAL_TOOLTIP :{BLACK}風景の上をドラッグして、単一の木を植えます。 STR_TREES_MODE_FOREST_SM_BUTTON :{BLACK}グローブ STR_TREES_MODE_FOREST_SM_TOOLTIP :{BLACK}風景をドラッグして小さな森を植えます STR_TREES_MODE_FOREST_LG_TOOLTIP :{BLACK}風景の上をドラッグして、大きな森を植えます。 @@ -2710,6 +2726,7 @@ STR_FRAMERATE_FPS_GOOD :{LTBLUE}{DECIMA STR_FRAMERATE_FPS_WARN :{YELLOW} {DECIMAL}フレーム/秒 STR_FRAMERATE_FPS_BAD :{RED} {DECIMAL}FPS STR_FRAMERATE_BYTES_GOOD :{LTBLUE}{BYTES} +STR_FRAMERATE_BYTES_WARN :{YELLOW} {BYTES} ############ Leave those lines in this order!! STR_FRAMERATE_GAMELOOP :{BLACK}ゲームループの合計: STR_FRAMERATE_GL_ECONOMY :{BLACK}貨物の取り扱い: @@ -2724,6 +2741,8 @@ STR_FRAMERATE_SOUND :{BLACK}サウ STR_FRAMETIME_CAPTION_GAMELOOP :ゲームループ STR_FRAMETIME_CAPTION_GL_ECONOMY :貨物の取り扱い STR_FRAMETIME_CAPTION_GL_TRAINS :切符 +STR_FRAMETIME_CAPTION_GL_SHIPS :船のティック +STR_FRAMETIME_CAPTION_GL_AIRCRAFT :航空機ティック STR_FRAMETIME_CAPTION_GL_LANDSCAPE :ワールドティック STR_FRAMETIME_CAPTION_GL_LINKGRAPH :リンクグラフの遅延 STR_FRAMETIME_CAPTION_DRAWING :グラフィックレンダリング @@ -2804,6 +2823,7 @@ STR_MAPGEN_HEIGHTMAP_NAME :{BLACK}ハイ STR_MAPGEN_HEIGHTMAP_SIZE_LABEL :{BLACK}サイズ: STR_MAPGEN_HEIGHTMAP_SIZE :{ORANGE}{NUM} × {NUM} +STR_MAPGEN_TERRAIN_TYPE_QUERY_CAPT :{WHITE}ターゲットの最高の高さ STR_MAPGEN_SNOW_COVERAGE_QUERY_CAPT :{WHITE}積雪量(%) STR_MAPGEN_DESERT_COVERAGE_QUERY_CAPT :{WHITE}砂漠領域(%) STR_MAPGEN_START_DATE_QUERY_CAPT :{WHITE}開始年の変更 @@ -2880,6 +2900,7 @@ STR_NEWGRF_SETTINGS_MD5SUM :{BLACK}MD5sum: STR_NEWGRF_SETTINGS_PALETTE :{BLACK}パレット: {SILVER}{STRING} STR_NEWGRF_SETTINGS_PALETTE_DEFAULT :デフォルト(D) STR_NEWGRF_SETTINGS_PALETTE_DEFAULT_32BPP :デフォルト(D)/ 32 bpp +STR_NEWGRF_SETTINGS_PALETTE_LEGACY :レガシー(W) STR_NEWGRF_SETTINGS_PALETTE_LEGACY_32BPP :レガシー(W)/ 32 bpp STR_NEWGRF_SETTINGS_PARAMETER :{BLACK}設定: {SILVER}{STRING} STR_NEWGRF_SETTINGS_PARAMETER_NONE :なし @@ -3334,6 +3355,7 @@ STR_INDUSTRY_DIRECTORY_ITEM_INFO :{BLACK}{CARGO_L STR_INDUSTRY_DIRECTORY_ITEM_NOPROD :{ORANGE}{INDUSTRY} STR_INDUSTRY_DIRECTORY_LIST_CAPTION :{BLACK}産業の名前です - 名前をクリックするとこの産業拠点の場所にメイン画面を移動します。Ctrl+クリックでこの産業拠点の場所を新たなビューポートに表示します STR_INDUSTRY_DIRECTORY_ACCEPTED_CARGO_FILTER :{BLACK}受け取った貨物: {SILVER}{STRING} +STR_INDUSTRY_DIRECTORY_PRODUCED_CARGO_FILTER :{BLACK}生産された貨物:{SILVER} {STRING} STR_INDUSTRY_DIRECTORY_FILTER_ALL_TYPES :すべての貨物タイプ STR_INDUSTRY_DIRECTORY_FILTER_NONE :なし @@ -3653,6 +3675,7 @@ STR_REPLACE_REMOVE_WAGON_HELP :{BLACK}機関 STR_VEHICLE_VIEW_CAPTION :{WHITE}{VEHICLE} STR_VEHICLE_VIEW_TRAIN_CENTER_TOOLTIP :{BLACK}メイン画面を列車に中心します。ダブルクリックで列車をメイン画面で追従します。Ctrl+クリックで列車の場所で新しいビューポートでを開きます。 +STR_VEHICLE_VIEW_AIRCRAFT_CENTER_TOOLTIP :{BLACK}航空機の位置に関する中央のメインビュー。ダブルクリックすると、メインビューで航空機が表示されます。Ctrl +クリックすると、航空機の位置に新しいビューポートが開きます STR_VEHICLE_VIEW_TRAIN_SEND_TO_DEPOT_TOOLTIP :{BLACK}列車を列車庫へ回送します。Ctrl+クリックすると点検後、再出庫します STR_VEHICLE_VIEW_ROAD_VEHICLE_SEND_TO_DEPOT_TOOLTIP :{BLACK}車両を車庫へ回送します。Ctrl+クリックすると点検後、再出庫します @@ -4094,9 +4117,11 @@ STR_AI_LIST_ACCEPT_TOOLTIP :{BLACK}選択 STR_AI_LIST_CANCEL :{BLACK}キャンセル STR_AI_LIST_CANCEL_TOOLTIP :{BLACK}スクリプトを変更しません +STR_SCREENSHOT_CAPTION :{WHITE}スクリーンショットを撮る STR_SCREENSHOT_ZOOMIN_SCREENSHOT :{BLACK}スクリーンショットを完全に拡大 STR_SCREENSHOT_WORLD_SCREENSHOT :{BLACK}地図全体のスクリーンショット STR_SCREENSHOT_HEIGHTMAP_SCREENSHOT :{BLACK}ハイトマップスクリーンショット +STR_SCREENSHOT_MINIMAP_SCREENSHOT :{BLACK}ミニマップのスクリーンショット # AI Parameters STR_AI_SETTINGS_CAPTION :{WHITE}{STRING} パラメータ @@ -4237,6 +4262,7 @@ STR_ERROR_LOAN_ALREADY_REPAYED :{WHITE}全額 STR_ERROR_CURRENCY_REQUIRED :{WHITE}{CURRENCY_LONG}が必要です STR_ERROR_CAN_T_REPAY_LOAN :{WHITE}借入金を返済できません STR_ERROR_INSUFFICIENT_FUNDS :{WHITE}借入金を送金することはできません +STR_ERROR_CAN_T_GIVE_MONEY :{WHITE}この会社にお金を渡すことはできません... STR_ERROR_CAN_T_BUY_COMPANY :{WHITE}会社を買収できません STR_ERROR_CAN_T_BUILD_COMPANY_HEADQUARTERS :{WHITE}会社の本社ビルを建設できません STR_ERROR_CAN_T_BUY_25_SHARE_IN_THIS :{WHITE}この会社の株を25%購入できません From 015e3b412ebe709e1596179e86fde364cf19f52a Mon Sep 17 00:00:00 2001 From: rubidium42 Date: Tue, 27 Apr 2021 19:32:51 +0200 Subject: [PATCH 182/268] Cleanup: remove #ifdefs for compiling the old content server --- src/network/core/tcp_content.cpp | 6 ------ src/network/core/tcp_content.h | 2 -- src/network/core/tcp_content_type.h | 2 -- 3 files changed, 10 deletions(-) diff --git a/src/network/core/tcp_content.cpp b/src/network/core/tcp_content.cpp index 55319e430d..488be50003 100644 --- a/src/network/core/tcp_content.cpp +++ b/src/network/core/tcp_content.cpp @@ -10,14 +10,12 @@ */ #include "../../stdafx.h" -#ifndef OPENTTD_MSU #include "../../textfile_gui.h" #include "../../newgrf_config.h" #include "../../base_media_base.h" #include "../../ai/ai.hpp" #include "../../game/game.hpp" #include "../../fios.h" -#endif /* OPENTTD_MSU */ #include "tcp_content.h" #include "../../safeguards.h" @@ -92,7 +90,6 @@ bool ContentInfo::IsValid() const return this->state < ContentInfo::INVALID && this->type >= CONTENT_TYPE_BEGIN && this->type < CONTENT_TYPE_END; } -#ifndef OPENTTD_MSU /** * Search a textfile file next to this file in the content list. * @param type The type of the textfile to search for. @@ -139,7 +136,6 @@ const char *ContentInfo::GetTextfile(TextfileType type) const if (tmp == nullptr) return nullptr; return ::GetTextfile(type, GetContentInfoSubDir(this->type), tmp); } -#endif /* OPENTTD_MSU */ void NetworkContentSocketHandler::Close() { @@ -236,7 +232,6 @@ bool NetworkContentSocketHandler::Receive_SERVER_INFO(Packet *p) { return this-> bool NetworkContentSocketHandler::Receive_CLIENT_CONTENT(Packet *p) { return this->ReceiveInvalidPacket(PACKET_CONTENT_CLIENT_CONTENT); } bool NetworkContentSocketHandler::Receive_SERVER_CONTENT(Packet *p) { return this->ReceiveInvalidPacket(PACKET_CONTENT_SERVER_CONTENT); } -#ifndef OPENTTD_MSU /** * Helper to get the subdirectory a #ContentInfo is located in. * @param type The type of content. @@ -261,4 +256,3 @@ Subdirectory GetContentInfoSubDir(ContentType type) case CONTENT_TYPE_HEIGHTMAP: return HEIGHTMAP_DIR; } } -#endif /* OPENTTD_MSU */ diff --git a/src/network/core/tcp_content.h b/src/network/core/tcp_content.h index f927021f4d..52cae1e0ed 100644 --- a/src/network/core/tcp_content.h +++ b/src/network/core/tcp_content.h @@ -129,8 +129,6 @@ public: bool ReceivePackets(); }; -#ifndef OPENTTD_MSU Subdirectory GetContentInfoSubDir(ContentType type); -#endif /* OPENTTD_MSU */ #endif /* NETWORK_CORE_TCP_CONTENT_H */ diff --git a/src/network/core/tcp_content_type.h b/src/network/core/tcp_content_type.h index f4dbc0c6ee..4dc20f46bb 100644 --- a/src/network/core/tcp_content_type.h +++ b/src/network/core/tcp_content_type.h @@ -82,9 +82,7 @@ struct ContentInfo { size_t Size() const; bool IsSelected() const; bool IsValid() const; -#ifndef OPENTTD_MSU const char *GetTextfile(TextfileType type) const; -#endif /* OPENTTD_MSU */ }; #endif /* NETWORK_CORE_TCP_CONTENT_TYPE_H */ From 8fa53f543a5929bdbb12c8776ae9577594f9eba7 Mon Sep 17 00:00:00 2001 From: Patric Stout Date: Tue, 27 Apr 2021 20:18:43 +0200 Subject: [PATCH 183/268] Change: [Network] lower TCP connect() timeout to 3s (#9112) Currently we use default OS timeout for TCP connections, which is around 30s. 99% of the users will never notice this, but there are a few cases where this is an issue: - If you have a broken IPv6 connection, using Content Service is first tried over IPv6. Only after 30s it times out and tries IPv4. Nobody is waiting for that 30s. - Upcoming STUN support has several methods of establishing a connection between client and server. This requires feedback from connect() to know if any method worked (they have to be tried one by one). With 30s, this would take a very long time. What is good to mention, is that there is no good value here. Any value will have edge-cases where the experience is suboptimal. But with 3s we support most of the stable connections, and if it fails, the user can just retry. On the other side of the spectrum, with 30s, it means the user has no possibility to use the service. So worst case we annoy a few users with them having the retry vs annoying a few users which have no means of resolving the situation. --- src/network/core/address.cpp | 48 ++++++++++++++++++++++++------- src/network/core/os_abstraction.h | 16 +++++++++++ 2 files changed, 53 insertions(+), 11 deletions(-) diff --git a/src/network/core/address.cpp b/src/network/core/address.cpp index fc19439e00..e53566c0bb 100644 --- a/src/network/core/address.cpp +++ b/src/network/core/address.cpp @@ -14,6 +14,8 @@ #include "../../safeguards.h" +static const int DEFAULT_CONNECT_TIMEOUT_SECONDS = 3; ///< Allow connect() three seconds to connect. + /** * Get the hostname; in case it wasn't given the * IPv4 dotted representation is given. @@ -322,23 +324,47 @@ static SOCKET ConnectLoopProc(addrinfo *runp) if (!SetNoDelay(sock)) DEBUG(net, 1, "[%s] setting TCP_NODELAY failed", type); + if (!SetNonBlocking(sock)) DEBUG(net, 0, "[%s] setting non-blocking mode failed", type); + int err = connect(sock, runp->ai_addr, (int)runp->ai_addrlen); -#ifdef __EMSCRIPTEN__ - /* Emscripten is asynchronous, and as such a connect() is still in - * progress by the time the call returns. */ - if (err != 0 && errno != EINPROGRESS) -#else - if (err != 0) -#endif - { - DEBUG(net, 1, "[%s] could not connect %s socket: %s", type, family, NetworkGetLastErrorString()); + if (err != 0 && NetworkGetLastError() != EINPROGRESS) { + DEBUG(net, 1, "[%s] could not connect to %s over %s: %s", type, address, family, NetworkGetLastErrorString()); closesocket(sock); return INVALID_SOCKET; } - /* Connection succeeded */ - if (!SetNonBlocking(sock)) DEBUG(net, 0, "[%s] setting non-blocking mode failed", type); + fd_set write_fd; + struct timeval tv; + FD_ZERO(&write_fd); + FD_SET(sock, &write_fd); + + /* Wait for connect() to either connect, timeout or fail. */ + tv.tv_usec = 0; + tv.tv_sec = DEFAULT_CONNECT_TIMEOUT_SECONDS; + int n = select(FD_SETSIZE, NULL, &write_fd, NULL, &tv); + if (n < 0) { + DEBUG(net, 1, "[%s] could not connect to %s: %s", type, address, NetworkGetLastErrorString()); + closesocket(sock); + return INVALID_SOCKET; + } + + /* If no fd is selected, the timeout has been reached. */ + if (n == 0) { + DEBUG(net, 1, "[%s] timed out while connecting to %s", type, address); + closesocket(sock); + return INVALID_SOCKET; + } + + /* Retrieve last error, if any, on the socket. */ + err = GetSocketError(sock); + if (err != 0) { + DEBUG(net, 1, "[%s] could not connect to %s: %s", type, address, NetworkGetErrorString(err)); + closesocket(sock); + return INVALID_SOCKET; + } + + /* Connection succeeded. */ DEBUG(net, 1, "[%s] connected to %s", type, address); return sock; diff --git a/src/network/core/os_abstraction.h b/src/network/core/os_abstraction.h index 7af3fd163e..9bd0e321f7 100644 --- a/src/network/core/os_abstraction.h +++ b/src/network/core/os_abstraction.h @@ -33,6 +33,8 @@ #define EWOULDBLOCK WSAEWOULDBLOCK #undef ECONNRESET #define ECONNRESET WSAECONNRESET +#undef EINPROGRESS +#define EINPROGRESS WSAEWOULDBLOCK const char *NetworkGetErrorString(int error); @@ -230,6 +232,20 @@ static inline bool SetNoDelay(SOCKET d) #endif } +/** + * Get the error from a socket, if any. + * @param d The socket to get the error from. + * @return The errno on the socket. + */ +static inline int GetSocketError(SOCKET d) +{ + int err; + socklen_t len = sizeof(err); + getsockopt(d, SOL_SOCKET, SO_ERROR, (char *)&err, &len); + + return err; +} + /* Make sure these structures have the size we expect them to be */ static_assert(sizeof(in_addr) == 4); ///< IPv4 addresses should be 4 bytes. static_assert(sizeof(in6_addr) == 16); ///< IPv6 addresses should be 16 bytes. From cb2ef1ea4b74c3d73ba86d978c001784d398bf27 Mon Sep 17 00:00:00 2001 From: Patric Stout Date: Mon, 26 Apr 2021 15:18:10 +0200 Subject: [PATCH 184/268] Codechange: move all NetworkGameInfo related functions to a single file It currently was a bit scattered over the place. Part of NetworkGameInfo is also the GRF Identifiers that goes with it. --- src/console_cmds.cpp | 1 + src/network/core/CMakeLists.txt | 3 +- src/network/core/core.cpp | 28 -- src/network/core/core.h | 2 - src/network/core/game_info.cpp | 323 +++++++++++++++++++++++ src/network/core/{game.h => game_info.h} | 62 ++++- src/network/core/tcp_http.cpp | 1 + src/network/core/udp.cpp | 137 +--------- src/network/core/udp.h | 48 +--- src/network/network.cpp | 81 +----- src/network/network_admin.cpp | 1 + src/network/network_base.h | 1 + src/network/network_client.cpp | 3 +- src/network/network_func.h | 2 +- src/network/network_gamelist.h | 1 + src/network/network_gui.h | 1 + src/network/network_internal.h | 2 - src/network/network_server.cpp | 3 +- src/network/network_type.h | 2 +- src/network/network_udp.cpp | 54 +--- 20 files changed, 407 insertions(+), 349 deletions(-) create mode 100644 src/network/core/game_info.cpp rename src/network/core/{game.h => game_info.h} (51%) diff --git a/src/console_cmds.cpp b/src/console_cmds.cpp index 048b8b3e93..8cdf9c664e 100644 --- a/src/console_cmds.cpp +++ b/src/console_cmds.cpp @@ -13,6 +13,7 @@ #include "engine_func.h" #include "landscape.h" #include "saveload/saveload.h" +#include "network/core/game_info.h" #include "network/network.h" #include "network/network_func.h" #include "network/network_base.h" diff --git a/src/network/core/CMakeLists.txt b/src/network/core/CMakeLists.txt index c9368a5b46..37cc3e1954 100644 --- a/src/network/core/CMakeLists.txt +++ b/src/network/core/CMakeLists.txt @@ -4,7 +4,8 @@ add_files( config.h core.cpp core.h - game.h + game_info.cpp + game_info.h host.cpp host.h os_abstraction.h diff --git a/src/network/core/core.cpp b/src/network/core/core.cpp index 8c5c5c2292..5c12cb2242 100644 --- a/src/network/core/core.cpp +++ b/src/network/core/core.cpp @@ -65,31 +65,3 @@ const char *NetworkGetErrorString(int error) return buffer; } #endif /* defined(_WIN32) */ - -/** - * Serializes the GRFIdentifier (GRF ID and MD5 checksum) to the packet - * @param p the packet to write the data to - * @param grf the GRFIdentifier to serialize - */ -void NetworkSocketHandler::SendGRFIdentifier(Packet *p, const GRFIdentifier *grf) -{ - uint j; - p->Send_uint32(grf->grfid); - for (j = 0; j < sizeof(grf->md5sum); j++) { - p->Send_uint8 (grf->md5sum[j]); - } -} - -/** - * Deserializes the GRFIdentifier (GRF ID and MD5 checksum) from the packet - * @param p the packet to read the data from - * @param grf the GRFIdentifier to deserialize - */ -void NetworkSocketHandler::ReceiveGRFIdentifier(Packet *p, GRFIdentifier *grf) -{ - uint j; - grf->grfid = p->Recv_uint32(); - for (j = 0; j < sizeof(grf->md5sum); j++) { - grf->md5sum[j] = p->Recv_uint8(); - } -} diff --git a/src/network/core/core.h b/src/network/core/core.h index bf83adc72c..aac36080ff 100644 --- a/src/network/core/core.h +++ b/src/network/core/core.h @@ -71,8 +71,6 @@ public: */ void Reopen() { this->has_quit = false; } - void SendGRFIdentifier(Packet *p, const GRFIdentifier *grf); - void ReceiveGRFIdentifier(Packet *p, GRFIdentifier *grf); void SendCompanyInformation(Packet *p, const struct Company *c, const struct NetworkCompanyStats *stats, uint max_len = NETWORK_COMPANY_NAME_LENGTH); }; diff --git a/src/network/core/game_info.cpp b/src/network/core/game_info.cpp new file mode 100644 index 0000000000..bea687af80 --- /dev/null +++ b/src/network/core/game_info.cpp @@ -0,0 +1,323 @@ +/* + * This file is part of OpenTTD. + * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. + * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see . + */ + +/** + * @file game_info.cpp Functions to convert NetworkGameInfo to Packet and back. + */ + +#include "../../stdafx.h" +#include "game_info.h" +#include "../../core/bitmath_func.hpp" +#include "../../company_base.h" +#include "../../date_func.h" +#include "../../debug.h" +#include "../../map_func.h" +#include "../../settings_type.h" +#include "../../string_func.h" +#include "../../rev.h" +#include "../network_func.h" +#include "../network.h" +#include "packet.h" + +#include "../../safeguards.h" + + +/** + * How many hex digits of the git hash to include in network revision string. + * Determined as 10 hex digits + 2 characters for -g/-u/-m prefix. + */ +static const uint GITHASH_SUFFIX_LEN = 12; + +NetworkServerGameInfo _network_game_info; ///< Information about our game. + +/** + * Get the network version string used by this build. + * The returned string is guaranteed to be at most NETWORK_REVISON_LENGTH bytes. + */ +const char *GetNetworkRevisionString() +{ + /* This will be allocated on heap and never free'd, but only once so not a "real" leak. */ + static char *network_revision = nullptr; + + if (!network_revision) { + /* Start by taking a chance on the full revision string. */ + network_revision = stredup(_openttd_revision); + /* Ensure it's not longer than the packet buffer length. */ + if (strlen(network_revision) >= NETWORK_REVISION_LENGTH) network_revision[NETWORK_REVISION_LENGTH - 1] = '\0'; + + /* Tag names are not mangled further. */ + if (_openttd_revision_tagged) { + DEBUG(net, 1, "Network revision name is '%s'", network_revision); + return network_revision; + } + + /* Prepare a prefix of the git hash. + * Size is length + 1 for terminator, +2 for -g prefix. */ + assert(_openttd_revision_modified < 3); + char githash_suffix[GITHASH_SUFFIX_LEN + 1] = "-"; + githash_suffix[1] = "gum"[_openttd_revision_modified]; + for (uint i = 2; i < GITHASH_SUFFIX_LEN; i++) { + githash_suffix[i] = _openttd_revision_hash[i-2]; + } + + /* Where did the hash start in the original string? + * Overwrite from that position, unless that would go past end of packet buffer length. */ + ptrdiff_t hashofs = strrchr(_openttd_revision, '-') - _openttd_revision; + if (hashofs + strlen(githash_suffix) + 1 > NETWORK_REVISION_LENGTH) hashofs = strlen(network_revision) - strlen(githash_suffix); + /* Replace the git hash in revision string. */ + strecpy(network_revision + hashofs, githash_suffix, network_revision + NETWORK_REVISION_LENGTH); + assert(strlen(network_revision) < NETWORK_REVISION_LENGTH); // strlen does not include terminator, constant does, hence strictly less than + DEBUG(net, 1, "Network revision name is '%s'", network_revision); + } + + return network_revision; +} + +/** + * Extract the git hash from the revision string. + * @param revstr The revision string (formatted as DATE-BRANCH-GITHASH). + * @return The git has part of the revision. + */ +static const char *ExtractNetworkRevisionHash(const char *revstr) +{ + return strrchr(revstr, '-'); +} + +/** + * Checks whether the given version string is compatible with our version. + * First tries to match the full string, if that fails, attempts to compare just git hashes. + * @param other the version string to compare to + */ +bool IsNetworkCompatibleVersion(const char *other) +{ + if (strncmp(GetNetworkRevisionString(), other, NETWORK_REVISION_LENGTH - 1) == 0) return true; + + /* If this version is tagged, then the revision string must be a complete match, + * since there is no git hash suffix in it. + * This is needed to avoid situations like "1.9.0-beta1" comparing equal to "2.0.0-beta1". */ + if (_openttd_revision_tagged) return false; + + const char *hash1 = ExtractNetworkRevisionHash(GetNetworkRevisionString()); + const char *hash2 = ExtractNetworkRevisionHash(other); + return hash1 && hash2 && (strncmp(hash1, hash2, GITHASH_SUFFIX_LEN) == 0); +} + +/** + * Fill a NetworkGameInfo structure with the latest information of the server. + * @param ngi the NetworkGameInfo struct to fill with data. + */ +void FillNetworkGameInfo(NetworkGameInfo &ngi) +{ + /* 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)); +} + +/** + * Function that is called for every GRFConfig that is read when receiving + * a NetworkGameInfo. Only grfid and md5sum are set, the rest is zero. This + * function must set all appropriate fields. This GRF is later appended to + * the grfconfig list of the NetworkGameInfo. + * @param config the GRF to handle. + */ +static void HandleIncomingNetworkGameInfoGRFConfig(GRFConfig *config) +{ + /* Find the matching GRF file */ + const GRFConfig *f = FindGRFConfig(config->ident.grfid, FGCM_EXACT, config->ident.md5sum); + if (f == nullptr) { + /* Don't know the GRF, so mark game incompatible and the (possibly) + * already resolved name for this GRF (another server has sent the + * name of the GRF already */ + config->name = FindUnknownGRFName(config->ident.grfid, config->ident.md5sum, true); + config->status = GCS_NOT_FOUND; + } else { + config->filename = f->filename; + config->name = f->name; + config->info = f->info; + config->url = f->url; + } + SetBit(config->flags, GCF_COPY); +} + +/** + * Serializes the NetworkGameInfo struct to the packet. + * @param p the packet to write the data to. + * @param info the NetworkGameInfo struct to serialize from. + */ +void SerializeNetworkGameInfo(Packet *p, const NetworkGameInfo *info) +{ + p->Send_uint8 (NETWORK_GAME_INFO_VERSION); + + /* + * Please observe the order. + * The parts must be read in the same order as they are sent! + */ + + /* Update the documentation in game_info.h on changes + * to the NetworkGameInfo wire-protocol! */ + + /* NETWORK_GAME_INFO_VERSION = 4 */ + { + /* Only send the GRF Identification (GRF_ID and MD5 checksum) of + * the GRFs that are needed, i.e. the ones that the server has + * selected in the NewGRF GUI and not the ones that are used due + * to the fact that they are in [newgrf-static] in openttd.cfg */ + const GRFConfig *c; + uint count = 0; + + /* Count number of GRFs to send information about */ + for (c = info->grfconfig; c != nullptr; c = c->next) { + if (!HasBit(c->flags, GCF_STATIC)) count++; + } + p->Send_uint8 (count); // Send number of GRFs + + /* Send actual GRF Identifications */ + for (c = info->grfconfig; c != nullptr; c = c->next) { + if (!HasBit(c->flags, GCF_STATIC)) SerializeGRFIdentifier(p, &c->ident); + } + } + + /* NETWORK_GAME_INFO_VERSION = 3 */ + p->Send_uint32(info->game_date); + p->Send_uint32(info->start_date); + + /* NETWORK_GAME_INFO_VERSION = 2 */ + p->Send_uint8 (info->companies_max); + p->Send_uint8 (info->companies_on); + p->Send_uint8 (info->spectators_max); + + /* NETWORK_GAME_INFO_VERSION = 1 */ + p->Send_string(info->server_name); + p->Send_string(info->server_revision); + p->Send_uint8 (0); // Used to be server-lang. + p->Send_bool (info->use_password); + p->Send_uint8 (info->clients_max); + p->Send_uint8 (info->clients_on); + p->Send_uint8 (info->spectators_on); + 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_bool (info->dedicated); +} + +/** + * Deserializes the NetworkGameInfo struct from the packet. + * @param p the packet to read the data from. + * @param info the NetworkGameInfo to deserialize into. + */ +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(); + + /* + * Please observe the order. + * The parts must be read in the same order as they are sent! + */ + + /* Update the documentation in game_info.h on changes + * to the NetworkGameInfo wire-protocol! */ + + switch (info->game_info_version) { + case 4: { + GRFConfig **dst = &info->grfconfig; + uint i; + uint num_grfs = p->Recv_uint8(); + + /* Broken/bad data. It cannot have that many NewGRFs. */ + if (num_grfs > NETWORK_MAX_GRF_COUNT) return; + + for (i = 0; i < num_grfs; i++) { + GRFConfig *c = new GRFConfig(); + DeserializeGRFIdentifier(p, &c->ident); + HandleIncomingNetworkGameInfoGRFConfig(c); + + /* Append GRFConfig to the list */ + *dst = c; + dst = &c->next; + } + FALLTHROUGH; + } + + case 3: + info->game_date = Clamp(p->Recv_uint32(), 0, MAX_DATE); + info->start_date = Clamp(p->Recv_uint32(), 0, MAX_DATE); + FALLTHROUGH; + + case 2: + info->companies_max = p->Recv_uint8 (); + info->companies_on = p->Recv_uint8 (); + info->spectators_max = p->Recv_uint8 (); + FALLTHROUGH; + + case 1: + p->Recv_string(info->server_name, sizeof(info->server_name)); + p->Recv_string(info->server_revision, sizeof(info->server_revision)); + 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 + 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->dedicated = p->Recv_bool (); + + if (info->map_set >= NETWORK_NUM_LANDSCAPES) info->map_set = 0; + } +} + +/** + * Serializes the GRFIdentifier (GRF ID and MD5 checksum) to the packet + * @param p the packet to write the data to. + * @param grf the GRFIdentifier to serialize. + */ +void SerializeGRFIdentifier(Packet *p, const GRFIdentifier *grf) +{ + uint j; + p->Send_uint32(grf->grfid); + for (j = 0; j < sizeof(grf->md5sum); j++) { + p->Send_uint8(grf->md5sum[j]); + } +} + +/** + * Deserializes the GRFIdentifier (GRF ID and MD5 checksum) from the packet + * @param p the packet to read the data from. + * @param grf the GRFIdentifier to deserialize. + */ +void DeserializeGRFIdentifier(Packet *p, GRFIdentifier *grf) +{ + uint j; + grf->grfid = p->Recv_uint32(); + for (j = 0; j < sizeof(grf->md5sum); j++) { + grf->md5sum[j] = p->Recv_uint8(); + } +} diff --git a/src/network/core/game.h b/src/network/core/game_info.h similarity index 51% rename from src/network/core/game.h rename to src/network/core/game_info.h index 29eca418f0..7767005117 100644 --- a/src/network/core/game.h +++ b/src/network/core/game_info.h @@ -6,17 +6,56 @@ */ /** - * @file game.h Information about a game that is sent between a - * game server, game client and masterserver. + * @file game_info.h Convert NetworkGameInfo to Packet and back. */ -#ifndef NETWORK_CORE_GAME_H -#define NETWORK_CORE_GAME_H +#ifndef NETWORK_CORE_GAME_INFO_H +#define NETWORK_CORE_GAME_INFO_H #include "config.h" +#include "core.h" #include "../../newgrf_config.h" #include "../../date_type.h" +/* + * NetworkGameInfo has several revisions which we still need to support on the + * wire. The table below shows the version and size for each field of the + * serialized NetworkGameInfo. + * + * Version: Bytes: Description: + * all 1 the version of this packet's structure + * + * 4+ 1 number of GRFs attached (n) + * 4+ n * 20 unique identifier for GRF files. Consists of: + * - one 4 byte variable with the GRF ID + * - 16 bytes (sent sequentially) for the MD5 checksum + * of the GRF + * + * 3+ 4 current game date in days since 1-1-0 (DMY) + * 3+ 4 game introduction date in days since 1-1-0 (DMY) + * + * 2+ 1 maximum number of companies allowed on the server + * 2+ 1 number of companies on the server + * 2+ 1 maximum number of spectators allowed on the server + * + * 1+ var string with the name of the server + * 1+ var string with the revision of the server + * 1+ 1 the language run on the server + * (0 = any, 1 = English, 2 = German, 3 = French) + * 1+ 1 whether the server uses a password (0 = no, 1 = yes) + * 1+ 1 maximum number of clients allowed on the server + * 1+ 1 number of clients on the server + * 1+ 1 number of spectators on the server + * 1 & 2 2 current game date in days since 1-1-1920 (DMY) + * 1 & 2 2 game introduction date in days since 1-1-1920 (DMY) + * 1+ var string with the name of the map + * 1+ 2 width of the map in tiles + * 1+ 2 height of the map in tiles + * 1+ 1 type of map: + * (0 = temperate, 1 = arctic, 2 = desert, 3 = toyland) + * 1+ 1 whether the server is dedicated (0 = no, 1 = yes) + */ + /** * The game information that is not generated on-the-fly and has to * be sent to the clients. @@ -50,6 +89,17 @@ struct NetworkGameInfo : NetworkServerGameInfo { byte map_set; ///< Graphical set }; -const char * GetNetworkRevisionString(); +extern NetworkServerGameInfo _network_game_info; -#endif /* NETWORK_CORE_GAME_H */ +const char *GetNetworkRevisionString(); +bool IsNetworkCompatibleVersion(const char *other); + +void FillNetworkGameInfo(NetworkGameInfo &ngi); + +void DeserializeGRFIdentifier(Packet *p, GRFIdentifier *grf); +void SerializeGRFIdentifier(Packet *p, const GRFIdentifier *grf); + +void DeserializeNetworkGameInfo(Packet *p, NetworkGameInfo *info); +void SerializeNetworkGameInfo(Packet *p, const NetworkGameInfo *info); + +#endif /* NETWORK_CORE_GAME_INFO_H */ diff --git a/src/network/core/tcp_http.cpp b/src/network/core/tcp_http.cpp index d57f4eceb7..ee74c45070 100644 --- a/src/network/core/tcp_http.cpp +++ b/src/network/core/tcp_http.cpp @@ -13,6 +13,7 @@ #include "../../debug.h" #include "../../rev.h" #include "../network_func.h" +#include "game_info.h" #include "tcp_http.h" diff --git a/src/network/core/udp.cpp b/src/network/core/udp.cpp index df5140e2b5..ffc86d825f 100644 --- a/src/network/core/udp.cpp +++ b/src/network/core/udp.cpp @@ -12,6 +12,7 @@ #include "../../stdafx.h" #include "../../date_func.h" #include "../../debug.h" +#include "game_info.h" #include "udp.h" #include "../../safeguards.h" @@ -150,142 +151,6 @@ void NetworkUDPSocketHandler::ReceivePackets() } } - -/** - * Serializes the NetworkGameInfo struct to the packet - * @param p the packet to write the data to - * @param info the NetworkGameInfo struct to serialize - */ -void NetworkUDPSocketHandler::SendNetworkGameInfo(Packet *p, const NetworkGameInfo *info) -{ - p->Send_uint8 (NETWORK_GAME_INFO_VERSION); - - /* - * Please observe the order. - * The parts must be read in the same order as they are sent! - */ - - /* Update the documentation in udp.h on changes - * to the NetworkGameInfo wire-protocol! */ - - /* NETWORK_GAME_INFO_VERSION = 4 */ - { - /* Only send the GRF Identification (GRF_ID and MD5 checksum) of - * the GRFs that are needed, i.e. the ones that the server has - * selected in the NewGRF GUI and not the ones that are used due - * to the fact that they are in [newgrf-static] in openttd.cfg */ - const GRFConfig *c; - uint count = 0; - - /* Count number of GRFs to send information about */ - for (c = info->grfconfig; c != nullptr; c = c->next) { - if (!HasBit(c->flags, GCF_STATIC)) count++; - } - p->Send_uint8 (count); // Send number of GRFs - - /* Send actual GRF Identifications */ - for (c = info->grfconfig; c != nullptr; c = c->next) { - if (!HasBit(c->flags, GCF_STATIC)) this->SendGRFIdentifier(p, &c->ident); - } - } - - /* NETWORK_GAME_INFO_VERSION = 3 */ - p->Send_uint32(info->game_date); - p->Send_uint32(info->start_date); - - /* NETWORK_GAME_INFO_VERSION = 2 */ - p->Send_uint8 (info->companies_max); - p->Send_uint8 (info->companies_on); - p->Send_uint8 (info->spectators_max); - - /* NETWORK_GAME_INFO_VERSION = 1 */ - p->Send_string(info->server_name); - p->Send_string(info->server_revision); - p->Send_uint8 (0); // Used to be server-lang. - p->Send_bool (info->use_password); - p->Send_uint8 (info->clients_max); - p->Send_uint8 (info->clients_on); - p->Send_uint8 (info->spectators_on); - 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_bool (info->dedicated); -} - -/** - * Deserializes the NetworkGameInfo struct from the packet - * @param p the packet to read the data from - * @param info the NetworkGameInfo to deserialize into - */ -void NetworkUDPSocketHandler::ReceiveNetworkGameInfo(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(); - - /* - * Please observe the order. - * The parts must be read in the same order as they are sent! - */ - - /* Update the documentation in udp.h on changes - * to the NetworkGameInfo wire-protocol! */ - - switch (info->game_info_version) { - case 4: { - GRFConfig **dst = &info->grfconfig; - uint i; - uint num_grfs = p->Recv_uint8(); - - /* Broken/bad data. It cannot have that many NewGRFs. */ - if (num_grfs > NETWORK_MAX_GRF_COUNT) return; - - for (i = 0; i < num_grfs; i++) { - GRFConfig *c = new GRFConfig(); - this->ReceiveGRFIdentifier(p, &c->ident); - this->HandleIncomingNetworkGameInfoGRFConfig(c); - - /* Append GRFConfig to the list */ - *dst = c; - dst = &c->next; - } - FALLTHROUGH; - } - - case 3: - info->game_date = Clamp(p->Recv_uint32(), 0, MAX_DATE); - info->start_date = Clamp(p->Recv_uint32(), 0, MAX_DATE); - FALLTHROUGH; - - case 2: - info->companies_max = p->Recv_uint8 (); - info->companies_on = p->Recv_uint8 (); - info->spectators_max = p->Recv_uint8 (); - FALLTHROUGH; - - case 1: - p->Recv_string(info->server_name, sizeof(info->server_name)); - p->Recv_string(info->server_revision, sizeof(info->server_revision)); - 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 - 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->dedicated = p->Recv_bool (); - - if (info->map_set >= NETWORK_NUM_LANDSCAPES) info->map_set = 0; - } -} - /** * Handle an incoming packets by sending it to the correct function. * @param p the received packet diff --git a/src/network/core/udp.h b/src/network/core/udp.h index ddbbd4515f..881fb0a612 100644 --- a/src/network/core/udp.h +++ b/src/network/core/udp.h @@ -13,7 +13,6 @@ #define NETWORK_CORE_UDP_H #include "address.h" -#include "game.h" #include "packet.h" /** Enum with all types of UDP packets. The order MUST not be changed **/ @@ -63,40 +62,7 @@ protected: /** * Return of server information to the client. - * This packet has several legacy versions, so we list the version and size of each "field": - * - * Version: Bytes: Description: - * all 1 the version of this packet's structure - * - * 4+ 1 number of GRFs attached (n) - * 4+ n * 20 unique identifier for GRF files. Consists of: - * - one 4 byte variable with the GRF ID - * - 16 bytes (sent sequentially) for the MD5 checksum - * of the GRF - * - * 3+ 4 current game date in days since 1-1-0 (DMY) - * 3+ 4 game introduction date in days since 1-1-0 (DMY) - * - * 2+ 1 maximum number of companies allowed on the server - * 2+ 1 number of companies on the server - * 2+ 1 maximum number of spectators allowed on the server - * - * 1+ var string with the name of the server - * 1+ var string with the revision of the server - * 1+ 1 the language run on the server - * (0 = any, 1 = English, 2 = German, 3 = French) - * 1+ 1 whether the server uses a password (0 = no, 1 = yes) - * 1+ 1 maximum number of clients allowed on the server - * 1+ 1 number of clients on the server - * 1+ 1 number of spectators on the server - * 1 & 2 2 current game date in days since 1-1-1920 (DMY) - * 1 & 2 2 game introduction date in days since 1-1-1920 (DMY) - * 1+ var string with the name of the map - * 1+ 2 width of the map in tiles - * 1+ 2 height of the map in tiles - * 1+ 1 type of map: - * (0 = temperate, 1 = arctic, 2 = desert, 3 = toyland) - * 1+ 1 whether the server is dedicated (0 = no, 1 = yes) + * Serialized NetworkGameInfo. See game_info.h for details. * @param p The received packet. * @param client_addr The origin of the packet. */ @@ -217,15 +183,6 @@ protected: virtual void Receive_MASTER_SESSION_KEY(Packet *p, NetworkAddress *client_addr); void HandleUDPPacket(Packet *p, NetworkAddress *client_addr); - - /** - * Function that is called for every GRFConfig that is read when receiving - * a NetworkGameInfo. Only grfid and md5sum are set, the rest is zero. This - * function must set all appropriate fields. This GRF is later appended to - * the grfconfig list of the NetworkGameInfo. - * @param config the GRF to handle - */ - virtual void HandleIncomingNetworkGameInfoGRFConfig(GRFConfig *config) { NOT_REACHED(); } public: NetworkUDPSocketHandler(NetworkAddressList *bind = nullptr); @@ -237,9 +194,6 @@ public: void SendPacket(Packet *p, NetworkAddress *recv, bool all = false, bool broadcast = false); void ReceivePackets(); - - void SendNetworkGameInfo(Packet *p, const NetworkGameInfo *info); - void ReceiveNetworkGameInfo(Packet *p, NetworkGameInfo *info); }; #endif /* NETWORK_CORE_UDP_H */ diff --git a/src/network/network.cpp b/src/network/network.cpp index 812456b4e2..9a45ce06e1 100644 --- a/src/network/network.cpp +++ b/src/network/network.cpp @@ -54,7 +54,6 @@ bool _network_server; ///< network-server is active bool _network_available; ///< is network mode available? bool _network_dedicated; ///< are we a dedicated server? bool _is_network_server; ///< Does this client wants to be a network-server? -NetworkServerGameInfo _network_game_info; ///< Information about our game. NetworkCompanyState *_network_company_states = nullptr; ///< Statistics about some companies. ClientID _network_own_client_id; ///< Our client identifier. ClientID _redirect_console_to_client; ///< If not invalid, redirect the console output to a client. @@ -603,9 +602,10 @@ public: } }; -/* Query a server to fetch his game-info - * If game_info is true, only the gameinfo is fetched, - * else only the client_info is fetched */ +/** + * Query a server to fetch his game-info. + * @param address the address to query. + */ void NetworkTCPQueryServer(NetworkAddress address) { if (!_network_available) return; @@ -1110,79 +1110,6 @@ void NetworkShutDown() NetworkCoreShutdown(); } -/** - * How many hex digits of the git hash to include in network revision string. - * Determined as 10 hex digits + 2 characters for -g/-u/-m prefix. - */ -static const uint GITHASH_SUFFIX_LEN = 12; - -/** - * Get the network version string used by this build. - * The returned string is guaranteed to be at most NETWORK_REVISON_LENGTH bytes. - */ -const char * GetNetworkRevisionString() -{ - /* This will be allocated on heap and never free'd, but only once so not a "real" leak. */ - static char *network_revision = nullptr; - - if (!network_revision) { - /* Start by taking a chance on the full revision string. */ - network_revision = stredup(_openttd_revision); - /* Ensure it's not longer than the packet buffer length. */ - if (strlen(network_revision) >= NETWORK_REVISION_LENGTH) network_revision[NETWORK_REVISION_LENGTH - 1] = '\0'; - - /* Tag names are not mangled further. */ - if (_openttd_revision_tagged) { - DEBUG(net, 1, "Network revision name is '%s'", network_revision); - return network_revision; - } - - /* Prepare a prefix of the git hash. - * Size is length + 1 for terminator, +2 for -g prefix. */ - assert(_openttd_revision_modified < 3); - char githash_suffix[GITHASH_SUFFIX_LEN + 1] = "-"; - githash_suffix[1] = "gum"[_openttd_revision_modified]; - for (uint i = 2; i < GITHASH_SUFFIX_LEN; i++) { - githash_suffix[i] = _openttd_revision_hash[i-2]; - } - - /* Where did the hash start in the original string? - * Overwrite from that position, unless that would go past end of packet buffer length. */ - ptrdiff_t hashofs = strrchr(_openttd_revision, '-') - _openttd_revision; - if (hashofs + strlen(githash_suffix) + 1 > NETWORK_REVISION_LENGTH) hashofs = strlen(network_revision) - strlen(githash_suffix); - /* Replace the git hash in revision string. */ - strecpy(network_revision + hashofs, githash_suffix, network_revision + NETWORK_REVISION_LENGTH); - assert(strlen(network_revision) < NETWORK_REVISION_LENGTH); // strlen does not include terminator, constant does, hence strictly less than - DEBUG(net, 1, "Network revision name is '%s'", network_revision); - } - - return network_revision; -} - -static const char *ExtractNetworkRevisionHash(const char *revstr) -{ - return strrchr(revstr, '-'); -} - -/** - * Checks whether the given version string is compatible with our version. - * First tries to match the full string, if that fails, attempts to compare just git hashes. - * @param other the version string to compare to - */ -bool IsNetworkCompatibleVersion(const char *other) -{ - if (strncmp(GetNetworkRevisionString(), other, NETWORK_REVISION_LENGTH - 1) == 0) return true; - - /* If this version is tagged, then the revision string must be a complete match, - * since there is no git hash suffix in it. - * This is needed to avoid situations like "1.9.0-beta1" comparing equal to "2.0.0-beta1". */ - if (_openttd_revision_tagged) return false; - - const char *hash1 = ExtractNetworkRevisionHash(GetNetworkRevisionString()); - const char *hash2 = ExtractNetworkRevisionHash(other); - return hash1 && hash2 && (strncmp(hash1, hash2, GITHASH_SUFFIX_LEN) == 0); -} - #ifdef __EMSCRIPTEN__ extern "C" { diff --git a/src/network/network_admin.cpp b/src/network/network_admin.cpp index 1b86c3e561..7b49754515 100644 --- a/src/network/network_admin.cpp +++ b/src/network/network_admin.cpp @@ -10,6 +10,7 @@ #include "../stdafx.h" #include "../strings_func.h" #include "../date_func.h" +#include "core/game_info.h" #include "network_admin.h" #include "network_base.h" #include "network_server.h" diff --git a/src/network/network_base.h b/src/network/network_base.h index 15f410dbc4..9ad0300629 100644 --- a/src/network/network_base.h +++ b/src/network/network_base.h @@ -14,6 +14,7 @@ #include "core/address.h" #include "../core/pool_type.hpp" #include "../company_type.h" +#include "../date_type.h" /** Type for the pool with client information. */ typedef Pool NetworkClientInfoPool; diff --git a/src/network/network_client.cpp b/src/network/network_client.cpp index d5fe64b11d..f496923076 100644 --- a/src/network/network_client.cpp +++ b/src/network/network_client.cpp @@ -23,6 +23,7 @@ #include "../gfx_func.h" #include "../error.h" #include "../rev.h" +#include "core/game_info.h" #include "network.h" #include "network_base.h" #include "network_client.h" @@ -730,7 +731,7 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_CHECK_NEWGRFS(P /* Check all GRFs */ for (; grf_count > 0; grf_count--) { GRFIdentifier c; - this->ReceiveGRFIdentifier(p, &c); + DeserializeGRFIdentifier(p, &c); /* Check whether we know this GRF */ const GRFConfig *f = FindGRFConfig(c.grfid, FGCM_EXACT, c.md5sum); diff --git a/src/network/network_func.h b/src/network/network_func.h index 66719b2166..a0f163345e 100644 --- a/src/network/network_func.h +++ b/src/network/network_func.h @@ -22,8 +22,8 @@ #include "../gfx_type.h" #include "../openttd.h" #include "../company_type.h" +#include "../string_type.h" -extern NetworkServerGameInfo _network_game_info; extern NetworkCompanyState *_network_company_states; extern ClientID _network_own_client_id; diff --git a/src/network/network_gamelist.h b/src/network/network_gamelist.h index 8e61f3a93b..ce35c01d8b 100644 --- a/src/network/network_gamelist.h +++ b/src/network/network_gamelist.h @@ -11,6 +11,7 @@ #define NETWORK_GAMELIST_H #include "core/address.h" +#include "core/game_info.h" #include "network_type.h" /** Structure with information shown in the game list (GUI) */ diff --git a/src/network/network_gui.h b/src/network/network_gui.h index 5124955626..b8ed68225b 100644 --- a/src/network/network_gui.h +++ b/src/network/network_gui.h @@ -11,6 +11,7 @@ #define NETWORK_GUI_H #include "../company_type.h" +#include "../date_type.h" #include "../economy_type.h" #include "../window_type.h" #include "network_type.h" diff --git a/src/network/network_internal.h b/src/network/network_internal.h index 352416320d..c64eac7954 100644 --- a/src/network/network_internal.h +++ b/src/network/network_internal.h @@ -94,8 +94,6 @@ void NetworkAddServer(const char *b); void NetworkRebuildHostList(); void UpdateNetworkGameWindow(); -bool IsNetworkCompatibleVersion(const char *version); - /* From network_command.cpp */ /** * Everything we need to know about a command to be able to execute it. diff --git a/src/network/network_server.cpp b/src/network/network_server.cpp index 67952bca69..6bc6f82916 100644 --- a/src/network/network_server.cpp +++ b/src/network/network_server.cpp @@ -10,6 +10,7 @@ #include "../stdafx.h" #include "../strings_func.h" #include "../date_func.h" +#include "core/game_info.h" #include "network_admin.h" #include "network_server.h" #include "network_udp.h" @@ -477,7 +478,7 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::SendNewGRFCheck() p->Send_uint8 (grf_count); for (c = _grfconfig; c != nullptr; c = c->next) { - if (!HasBit(c->flags, GCF_STATIC)) this->SendGRFIdentifier(p, &c->ident); + if (!HasBit(c->flags, GCF_STATIC)) SerializeGRFIdentifier(p, &c->ident); } this->SendPacket(p); diff --git a/src/network/network_type.h b/src/network/network_type.h index 4c75346da6..4dcdd8c03d 100644 --- a/src/network/network_type.h +++ b/src/network/network_type.h @@ -10,7 +10,7 @@ #ifndef NETWORK_TYPE_H #define NETWORK_TYPE_H -#include "core/game.h" +#include "core/config.h" /** How many clients can we have */ static const uint MAX_CLIENTS = 255; diff --git a/src/network/network_udp.cpp b/src/network/network_udp.cpp index f7db689d4b..8e661e9393 100644 --- a/src/network/network_udp.cpp +++ b/src/network/network_udp.cpp @@ -16,6 +16,7 @@ #include "../date_func.h" #include "../map_func.h" #include "../debug.h" +#include "core/game_info.h" #include "network_gamelist.h" #include "network_internal.h" #include "network_udp.h" @@ -171,29 +172,10 @@ void ServerNetworkUDPSocketHandler::Receive_CLIENT_FIND_SERVER(Packet *p, Networ } NetworkGameInfo ngi; - - /* 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)); + FillNetworkGameInfo(ngi); Packet packet(PACKET_UDP_SERVER_RESPONSE); - this->SendNetworkGameInfo(&packet, &ngi); + SerializeNetworkGameInfo(&packet, &ngi); /* Let the client know that we are here */ this->SendPacket(&packet, client_addr); @@ -284,7 +266,7 @@ void ServerNetworkUDPSocketHandler::Receive_CLIENT_GET_NEWGRFS(Packet *p, Networ GRFIdentifier c; const GRFConfig *f; - this->ReceiveGRFIdentifier(p, &c); + DeserializeGRFIdentifier(p, &c); /* Find the matching GRF file */ f = FindGRFConfig(c.grfid, FGCM_EXACT, c.md5sum); @@ -311,7 +293,7 @@ void ServerNetworkUDPSocketHandler::Receive_CLIENT_GET_NEWGRFS(Packet *p, Networ /* The name could be an empty string, if so take the filename */ strecpy(name, in_reply[i]->GetName(), lastof(name)); - this->SendGRFIdentifier(&packet, &in_reply[i]->ident); + SerializeGRFIdentifier(&packet, &in_reply[i]->ident); packet.Send_string(name); } @@ -326,7 +308,6 @@ protected: void Receive_SERVER_RESPONSE(Packet *p, NetworkAddress *client_addr) override; void Receive_MASTER_RESPONSE_LIST(Packet *p, NetworkAddress *client_addr) override; void Receive_SERVER_NEWGRFS(Packet *p, NetworkAddress *client_addr) override; - void HandleIncomingNetworkGameInfoGRFConfig(GRFConfig *config) override; public: virtual ~ClientNetworkUDPSocketHandler() {} }; @@ -344,7 +325,7 @@ void ClientNetworkUDPSocketHandler::Receive_SERVER_RESPONSE(Packet *p, NetworkAd item = NetworkGameListAddItem(*client_addr); ClearGRFConfigList(&item->info.grfconfig); - this->ReceiveNetworkGameInfo(p, &item->info); + DeserializeNetworkGameInfo(p, &item->info); item->info.compatible = true; { @@ -373,7 +354,7 @@ void ClientNetworkUDPSocketHandler::Receive_SERVER_RESPONSE(Packet *p, NetworkAd packet.Send_uint8(in_request_count); for (i = 0; i < in_request_count; i++) { - this->SendGRFIdentifier(&packet, &in_request[i]->ident); + SerializeGRFIdentifier(&packet, &in_request[i]->ident); } this->SendPacket(&packet, &item->address); @@ -447,7 +428,7 @@ void ClientNetworkUDPSocketHandler::Receive_SERVER_NEWGRFS(Packet *p, NetworkAdd char name[NETWORK_GRF_NAME_LENGTH]; GRFIdentifier c; - this->ReceiveGRFIdentifier(p, &c); + DeserializeGRFIdentifier(p, &c); p->Recv_string(name, sizeof(name)); /* An empty name is not possible under normal circumstances @@ -464,25 +445,6 @@ void ClientNetworkUDPSocketHandler::Receive_SERVER_NEWGRFS(Packet *p, NetworkAdd } } -void ClientNetworkUDPSocketHandler::HandleIncomingNetworkGameInfoGRFConfig(GRFConfig *config) -{ - /* Find the matching GRF file */ - const GRFConfig *f = FindGRFConfig(config->ident.grfid, FGCM_EXACT, config->ident.md5sum); - if (f == nullptr) { - /* Don't know the GRF, so mark game incompatible and the (possibly) - * already resolved name for this GRF (another server has sent the - * name of the GRF already */ - config->name = FindUnknownGRFName(config->ident.grfid, config->ident.md5sum, true); - config->status = GCS_NOT_FOUND; - } else { - config->filename = f->filename; - config->name = f->name; - config->info = f->info; - config->url = f->url; - } - SetBit(config->flags, GCF_COPY); -} - /** Broadcast to all ips */ static void NetworkUDPBroadCast(NetworkUDPSocketHandler *socket) { From 84c75a7b9a8aeb60506454260f9c98454ffb8ab5 Mon Sep 17 00:00:00 2001 From: Patric Stout Date: Tue, 27 Apr 2021 19:58:32 +0200 Subject: [PATCH 185/268] Codechange: be explicit in pointer comparisons --- src/network/core/game_info.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/network/core/game_info.cpp b/src/network/core/game_info.cpp index bea687af80..b664bb580a 100644 --- a/src/network/core/game_info.cpp +++ b/src/network/core/game_info.cpp @@ -103,7 +103,7 @@ bool IsNetworkCompatibleVersion(const char *other) const char *hash1 = ExtractNetworkRevisionHash(GetNetworkRevisionString()); const char *hash2 = ExtractNetworkRevisionHash(other); - return hash1 && hash2 && (strncmp(hash1, hash2, GITHASH_SUFFIX_LEN) == 0); + return hash1 != nullptr && hash2 != nullptr && strncmp(hash1, hash2, GITHASH_SUFFIX_LEN) == 0; } /** From b3003dd163ab6b7902da33fa6cfaa29e45556db1 Mon Sep 17 00:00:00 2001 From: Patric Stout Date: Mon, 26 Apr 2021 20:02:27 +0200 Subject: [PATCH 186/268] Add: ability to retrieve game info from server over TCP --- src/network/core/tcp_game.cpp | 4 ++++ src/network/core/tcp_game.h | 19 ++++++++++++++++++- src/network/network_server.cpp | 19 +++++++++++++++++++ src/network/network_server.h | 2 ++ 4 files changed, 43 insertions(+), 1 deletion(-) diff --git a/src/network/core/tcp_game.cpp b/src/network/core/tcp_game.cpp index 06e56e2291..eb53db5acd 100644 --- a/src/network/core/tcp_game.cpp +++ b/src/network/core/tcp_game.cpp @@ -71,6 +71,8 @@ NetworkRecvStatus NetworkGameSocketHandler::HandlePacket(Packet *p) case PACKET_SERVER_BANNED: return this->Receive_SERVER_BANNED(p); case PACKET_CLIENT_JOIN: return this->Receive_CLIENT_JOIN(p); case PACKET_SERVER_ERROR: return this->Receive_SERVER_ERROR(p); + case PACKET_CLIENT_GAME_INFO: return this->Receive_CLIENT_GAME_INFO(p); + case PACKET_SERVER_GAME_INFO: return this->Receive_SERVER_GAME_INFO(p); case PACKET_CLIENT_COMPANY_INFO: return this->Receive_CLIENT_COMPANY_INFO(p); case PACKET_SERVER_COMPANY_INFO: return this->Receive_SERVER_COMPANY_INFO(p); case PACKET_SERVER_CLIENT_INFO: return this->Receive_SERVER_CLIENT_INFO(p); @@ -157,6 +159,8 @@ NetworkRecvStatus NetworkGameSocketHandler::Receive_SERVER_FULL(Packet *p) { ret NetworkRecvStatus NetworkGameSocketHandler::Receive_SERVER_BANNED(Packet *p) { return this->ReceiveInvalidPacket(PACKET_SERVER_BANNED); } NetworkRecvStatus NetworkGameSocketHandler::Receive_CLIENT_JOIN(Packet *p) { return this->ReceiveInvalidPacket(PACKET_CLIENT_JOIN); } NetworkRecvStatus NetworkGameSocketHandler::Receive_SERVER_ERROR(Packet *p) { return this->ReceiveInvalidPacket(PACKET_SERVER_ERROR); } +NetworkRecvStatus NetworkGameSocketHandler::Receive_CLIENT_GAME_INFO(Packet *p) { return this->ReceiveInvalidPacket(PACKET_CLIENT_GAME_INFO); } +NetworkRecvStatus NetworkGameSocketHandler::Receive_SERVER_GAME_INFO(Packet *p) { return this->ReceiveInvalidPacket(PACKET_SERVER_GAME_INFO); } NetworkRecvStatus NetworkGameSocketHandler::Receive_CLIENT_COMPANY_INFO(Packet *p) { return this->ReceiveInvalidPacket(PACKET_CLIENT_COMPANY_INFO); } NetworkRecvStatus NetworkGameSocketHandler::Receive_SERVER_COMPANY_INFO(Packet *p) { return this->ReceiveInvalidPacket(PACKET_SERVER_COMPANY_INFO); } NetworkRecvStatus NetworkGameSocketHandler::Receive_SERVER_CLIENT_INFO(Packet *p) { return this->ReceiveInvalidPacket(PACKET_SERVER_CLIENT_INFO); } diff --git a/src/network/core/tcp_game.h b/src/network/core/tcp_game.h index 95b5f8c6c7..6dcc0bd01f 100644 --- a/src/network/core/tcp_game.h +++ b/src/network/core/tcp_game.h @@ -24,7 +24,7 @@ */ enum PacketGameType { /* - * These first three pair of packets (thus six in + * These first four pair of packets (thus eight in * total) must remain in this order for backward * and forward compatibility between clients that * are trying to join directly. @@ -42,6 +42,10 @@ enum PacketGameType { PACKET_CLIENT_COMPANY_INFO, ///< Request information about all companies. PACKET_SERVER_COMPANY_INFO, ///< Information about a single company. + /* Packets used to get the game info. */ + PACKET_CLIENT_GAME_INFO, ///< Request information about the server. + PACKET_SERVER_GAME_INFO, ///< Information about the server. + /* * Packets after here assume that the client * and server are running the same version. As @@ -183,6 +187,19 @@ protected: */ virtual NetworkRecvStatus Receive_SERVER_ERROR(Packet *p); + /** + * Request game information. + * @param p The packet that was just received. + */ + virtual NetworkRecvStatus Receive_CLIENT_GAME_INFO(Packet *p); + + /** + * Sends information about the game. + * Serialized NetworkGameInfo. See game_info.h for details. + * @param p The packet that was just received. + */ + virtual NetworkRecvStatus Receive_SERVER_GAME_INFO(Packet *p); + /** * Request company information (in detail). * @param p The packet that was just received. diff --git a/src/network/network_server.cpp b/src/network/network_server.cpp index 6bc6f82916..fd69a8d37a 100644 --- a/src/network/network_server.cpp +++ b/src/network/network_server.cpp @@ -353,6 +353,20 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::SendClientInfo(NetworkClientIn return NETWORK_RECV_STATUS_OKAY; } +/** Send the client information about the server. */ +NetworkRecvStatus ServerNetworkGameSocketHandler::SendGameInfo() +{ + NetworkGameInfo ngi; + FillNetworkGameInfo(ngi); + + Packet *p = new Packet(PACKET_SERVER_GAME_INFO); + SerializeNetworkGameInfo(p, &ngi); + + this->SendPacket(p); + + return NETWORK_RECV_STATUS_OKAY; +} + /** Send the client information about the companies. */ NetworkRecvStatus ServerNetworkGameSocketHandler::SendCompanyInfo() { @@ -828,6 +842,11 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::SendConfigUpdate() * DEF_SERVER_RECEIVE_COMMAND has parameter: NetworkClientSocket *cs, Packet *p ************/ +NetworkRecvStatus ServerNetworkGameSocketHandler::Receive_CLIENT_GAME_INFO(Packet *p) +{ + return this->SendGameInfo(); +} + NetworkRecvStatus ServerNetworkGameSocketHandler::Receive_CLIENT_COMPANY_INFO(Packet *p) { return this->SendCompanyInfo(); diff --git a/src/network/network_server.h b/src/network/network_server.h index 4f6033fab1..e72377e824 100644 --- a/src/network/network_server.h +++ b/src/network/network_server.h @@ -24,6 +24,7 @@ extern NetworkClientSocketPool _networkclientsocket_pool; class ServerNetworkGameSocketHandler : public NetworkClientSocketPool::PoolItem<&_networkclientsocket_pool>, public NetworkGameSocketHandler, public TCPListenHandler { protected: NetworkRecvStatus Receive_CLIENT_JOIN(Packet *p) override; + NetworkRecvStatus Receive_CLIENT_GAME_INFO(Packet *p) override; NetworkRecvStatus Receive_CLIENT_COMPANY_INFO(Packet *p) override; NetworkRecvStatus Receive_CLIENT_GAME_PASSWORD(Packet *p) override; NetworkRecvStatus Receive_CLIENT_COMPANY_PASSWORD(Packet *p) override; @@ -40,6 +41,7 @@ protected: NetworkRecvStatus Receive_CLIENT_NEWGRFS_CHECKED(Packet *p) override; NetworkRecvStatus Receive_CLIENT_MOVE(Packet *p) override; + NetworkRecvStatus SendGameInfo(); NetworkRecvStatus SendCompanyInfo(); NetworkRecvStatus SendNewGRFCheck(); NetworkRecvStatus SendWelcome(); From b57d845e55f733514d235e24f64b09a8744f3abc Mon Sep 17 00:00:00 2001 From: Patric Stout Date: Mon, 26 Apr 2021 20:02:58 +0200 Subject: [PATCH 187/268] Codechange: refactor CheckGameCompatibility() from existing function Later commits use this function in other places too. --- src/network/core/game_info.cpp | 15 +++++++++++++++ src/network/core/game_info.h | 1 + src/network/network_udp.cpp | 14 ++++++-------- 3 files changed, 22 insertions(+), 8 deletions(-) diff --git a/src/network/core/game_info.cpp b/src/network/core/game_info.cpp index b664bb580a..55b5d1749a 100644 --- a/src/network/core/game_info.cpp +++ b/src/network/core/game_info.cpp @@ -106,6 +106,21 @@ bool IsNetworkCompatibleVersion(const char *other) return hash1 != nullptr && hash2 != nullptr && strncmp(hash1, hash2, GITHASH_SUFFIX_LEN) == 0; } +/** + * Check if an game entry is compatible with our client. + */ +void CheckGameCompatibility(NetworkGameInfo &ngi) +{ + /* Check if we are allowed on this server based on the revision-check. */ + ngi.version_compatible = IsNetworkCompatibleVersion(ngi.server_revision); + ngi.compatible = ngi.version_compatible; + + /* Check if we have all the GRFs on the client-system too. */ + for (const GRFConfig *c = ngi.grfconfig; c != nullptr; c = c->next) { + if (c->status == GCS_NOT_FOUND) ngi.compatible = false; + } +} + /** * Fill a NetworkGameInfo structure with the latest information of the server. * @param ngi the NetworkGameInfo struct to fill with data. diff --git a/src/network/core/game_info.h b/src/network/core/game_info.h index 7767005117..28aea75073 100644 --- a/src/network/core/game_info.h +++ b/src/network/core/game_info.h @@ -93,6 +93,7 @@ extern NetworkServerGameInfo _network_game_info; const char *GetNetworkRevisionString(); bool IsNetworkCompatibleVersion(const char *other); +void CheckGameCompatibility(NetworkGameInfo &ngi); void FillNetworkGameInfo(NetworkGameInfo &ngi); diff --git a/src/network/network_udp.cpp b/src/network/network_udp.cpp index 8e661e9393..d62998794e 100644 --- a/src/network/network_udp.cpp +++ b/src/network/network_udp.cpp @@ -324,10 +324,15 @@ void ClientNetworkUDPSocketHandler::Receive_SERVER_RESPONSE(Packet *p, NetworkAd /* Find next item */ item = NetworkGameListAddItem(*client_addr); + /* Clear any existing GRFConfig chain. */ ClearGRFConfigList(&item->info.grfconfig); + /* Retrieve the NetworkGameInfo from the packet. */ DeserializeNetworkGameInfo(p, &item->info); + /* Check for compatability with the client. */ + CheckGameCompatibility(item->info); + /* Ensure we consider the server online. */ + item->online = true; - item->info.compatible = true; { /* Checks whether there needs to be a request for names of GRFs and makes * the request if necessary. GRFs that need to be requested are the GRFs @@ -341,7 +346,6 @@ void ClientNetworkUDPSocketHandler::Receive_SERVER_RESPONSE(Packet *p, NetworkAd uint in_request_count = 0; for (c = item->info.grfconfig; c != nullptr; c = c->next) { - if (c->status == GCS_NOT_FOUND) item->info.compatible = false; if (c->status != GCS_NOT_FOUND || strcmp(c->GetName(), UNKNOWN_GRF_NAME_PLACEHOLDER) != 0) continue; in_request[in_request_count] = c; in_request_count++; @@ -369,12 +373,6 @@ void ClientNetworkUDPSocketHandler::Receive_SERVER_RESPONSE(Packet *p, NetworkAd strecat(item->info.server_name, " (IPv6)", lastof(item->info.server_name)); } - /* Check if we are allowed on this server based on the revision-match */ - item->info.version_compatible = IsNetworkCompatibleVersion(item->info.server_revision); - item->info.compatible &= item->info.version_compatible; // Already contains match for GRFs - - item->online = true; - UpdateNetworkGameWindow(); } From 31f1db2d3ab1614f00a08a7f44b518a04aeadd3e Mon Sep 17 00:00:00 2001 From: Patric Stout Date: Mon, 26 Apr 2021 20:04:02 +0200 Subject: [PATCH 188/268] Change: no longer use UDP when entering the lobby of a server The lobby of a server requested some parts via UDP and some via TCP. This is strictly seen fine, but for future extensions it is a lot easier if just one protocol is used. --- src/network/network.cpp | 2 +- src/network/network_client.cpp | 34 ++++++++++++++++++++++++++++++---- src/network/network_client.h | 3 ++- src/network/network_gui.cpp | 17 +++++++++++++---- src/network/network_gui.h | 2 ++ 5 files changed, 48 insertions(+), 10 deletions(-) diff --git a/src/network/network.cpp b/src/network/network.cpp index 9a45ce06e1..c0b2e06fd4 100644 --- a/src/network/network.cpp +++ b/src/network/network.cpp @@ -598,7 +598,7 @@ public: { _networking = true; new ClientNetworkGameSocketHandler(s); - MyClient::SendCompanyInformationQuery(); + MyClient::SendInformationQuery(); } }; diff --git a/src/network/network_client.cpp b/src/network/network_client.cpp index f496923076..af7073e558 100644 --- a/src/network/network_client.cpp +++ b/src/network/network_client.cpp @@ -27,6 +27,7 @@ #include "network.h" #include "network_base.h" #include "network_client.h" +#include "network_gamelist.h" #include "../core/backup_type.hpp" #include "../thread.h" @@ -341,15 +342,18 @@ static_assert(NETWORK_SERVER_ID_LENGTH == 16 * 2 + 1); * DEF_CLIENT_SEND_COMMAND has no parameters ************/ -/** Query the server for company information. */ -NetworkRecvStatus ClientNetworkGameSocketHandler::SendCompanyInformationQuery() +/** + * Query the server for server information. + */ +NetworkRecvStatus ClientNetworkGameSocketHandler::SendInformationQuery() { my_client->status = STATUS_COMPANY_INFO; _network_join_status = NETWORK_JOIN_STATUS_GETTING_COMPANY_INFO; SetWindowDirty(WC_NETWORK_STATUS_WINDOW, WN_NETWORK_STATUS_WINDOW_JOIN); - Packet *p = new Packet(PACKET_CLIENT_COMPANY_INFO); - my_client->SendPacket(p); + my_client->SendPacket(new Packet(PACKET_CLIENT_GAME_INFO)); + my_client->SendPacket(new Packet(PACKET_CLIENT_COMPANY_INFO)); + return NETWORK_RECV_STATUS_OKAY; } @@ -571,6 +575,28 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_BANNED(Packet * return NETWORK_RECV_STATUS_SERVER_BANNED; } +NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_GAME_INFO(Packet *p) +{ + if (this->status != STATUS_COMPANY_INFO && this->status != STATUS_INACTIVE) return NETWORK_RECV_STATUS_MALFORMED_PACKET; + + NetworkGameList *item = GetLobbyGameInfo(); + + /* Clear any existing GRFConfig chain. */ + ClearGRFConfigList(&item->info.grfconfig); + /* Retrieve the NetworkGameInfo from the packet. */ + DeserializeNetworkGameInfo(p, &item->info); + /* Check for compatability with the client. */ + CheckGameCompatibility(item->info); + /* Ensure we consider the server online. */ + item->online = true; + + SetWindowDirty(WC_NETWORK_WINDOW, WN_NETWORK_WINDOW_LOBBY); + + /* We will receive company info next, so keep connection open. */ + if (this->status == STATUS_COMPANY_INFO) return NETWORK_RECV_STATUS_OKAY; + return NETWORK_RECV_STATUS_CLOSE_QUERY; +} + NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_COMPANY_INFO(Packet *p) { if (this->status != STATUS_COMPANY_INFO) return NETWORK_RECV_STATUS_MALFORMED_PACKET; diff --git a/src/network/network_client.h b/src/network/network_client.h index d1ffe1c1d1..66cdb8ba57 100644 --- a/src/network/network_client.h +++ b/src/network/network_client.h @@ -43,6 +43,7 @@ protected: NetworkRecvStatus Receive_SERVER_FULL(Packet *p) override; NetworkRecvStatus Receive_SERVER_BANNED(Packet *p) override; NetworkRecvStatus Receive_SERVER_ERROR(Packet *p) override; + NetworkRecvStatus Receive_SERVER_GAME_INFO(Packet *p) override; NetworkRecvStatus Receive_SERVER_COMPANY_INFO(Packet *p) override; NetworkRecvStatus Receive_SERVER_CLIENT_INFO(Packet *p) override; NetworkRecvStatus Receive_SERVER_NEED_GAME_PASSWORD(Packet *p) override; @@ -79,7 +80,7 @@ public: NetworkRecvStatus CloseConnection(NetworkRecvStatus status) override; void ClientError(NetworkRecvStatus res); - static NetworkRecvStatus SendCompanyInformationQuery(); + static NetworkRecvStatus SendInformationQuery(); static NetworkRecvStatus SendJoin(); static NetworkRecvStatus SendCommand(const CommandPacket *cp); diff --git a/src/network/network_gui.cpp b/src/network/network_gui.cpp index ebba47d851..57127aee81 100644 --- a/src/network/network_gui.cpp +++ b/src/network/network_gui.cpp @@ -1482,10 +1482,10 @@ struct NetworkLobbyWindow : public Window { break; case WID_NL_REFRESH: // Refresh - NetworkTCPQueryServer(NetworkAddress(_settings_client.network.last_host, _settings_client.network.last_port)); // company info - NetworkUDPQueryServer(NetworkAddress(_settings_client.network.last_host, _settings_client.network.last_port)); // general data /* Clear the information so removed companies don't remain */ for (auto &company : this->company_info) company = {}; + + NetworkTCPQueryServer(NetworkAddress(_settings_client.network.last_host, _settings_client.network.last_port)); break; } } @@ -1553,8 +1553,7 @@ static void ShowNetworkLobbyWindow(NetworkGameList *ngl) DeleteWindowById(WC_NETWORK_WINDOW, WN_NETWORK_WINDOW_START); DeleteWindowById(WC_NETWORK_WINDOW, WN_NETWORK_WINDOW_GAME); - NetworkTCPQueryServer(NetworkAddress(_settings_client.network.last_host, _settings_client.network.last_port)); // company info - NetworkUDPQueryServer(NetworkAddress(_settings_client.network.last_host, _settings_client.network.last_port)); // general data + NetworkTCPQueryServer(NetworkAddress(_settings_client.network.last_host, _settings_client.network.last_port)); new NetworkLobbyWindow(&_network_lobby_window_desc, ngl); } @@ -1570,6 +1569,16 @@ NetworkCompanyInfo *GetLobbyCompanyInfo(CompanyID company) return (lobby != nullptr && company < MAX_COMPANIES) ? &lobby->company_info[company] : nullptr; } +/** + * Get the game information for the lobby. + * @return the game info struct to write the (downloaded) data to. + */ +NetworkGameList *GetLobbyGameInfo() +{ + NetworkLobbyWindow *lobby = dynamic_cast(FindWindowById(WC_NETWORK_WINDOW, WN_NETWORK_WINDOW_LOBBY)); + return lobby != nullptr ? lobby->server : nullptr; +} + /* The window below gives information about the connected clients * and also makes able to kick them (if server) and stuff like that. */ diff --git a/src/network/network_gui.h b/src/network/network_gui.h index b8ed68225b..e4ccf4e783 100644 --- a/src/network/network_gui.h +++ b/src/network/network_gui.h @@ -15,6 +15,7 @@ #include "../economy_type.h" #include "../window_type.h" #include "network_type.h" +#include "network_gamelist.h" void ShowNetworkNeedPassword(NetworkPasswordType npt); void ShowNetworkChatQueryWindow(DestType type, int dest); @@ -37,5 +38,6 @@ struct NetworkCompanyInfo : NetworkCompanyStats { }; NetworkCompanyInfo *GetLobbyCompanyInfo(CompanyID company); +NetworkGameList *GetLobbyGameInfo(); #endif /* NETWORK_GUI_H */ From a341852cd56d6a3f83dfdb293426b1c38786da0b Mon Sep 17 00:00:00 2001 From: Milek7 Date: Wed, 28 Apr 2021 00:22:03 +0200 Subject: [PATCH 189/268] Fix: missing include in network/core/packet.h (#9123) --- src/network/core/packet.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/network/core/packet.h b/src/network/core/packet.h index d55dad316a..bc7bab6c4f 100644 --- a/src/network/core/packet.h +++ b/src/network/core/packet.h @@ -17,6 +17,7 @@ #include "core.h" #include "../../string_type.h" #include +#include typedef uint16 PacketSize; ///< Size of the whole packet. typedef uint8 PacketType; ///< Identifier for the packet From 267703c14be579b168125e2b3fbab2e407638ced Mon Sep 17 00:00:00 2001 From: translators Date: Wed, 28 Apr 2021 17:53:26 +0000 Subject: [PATCH 190/268] Update: Translations from eints spanish (mexican): 40 changes by absay english (us): 1 change by 2TallTyler korean: 3 changes by telk5093 german: 1 change by danidoedel finnish: 1 change by hpiirai catalan: 1 change by J0anJosep portuguese: 45 changes by azulcosta portuguese (brazilian): 44 changes by Vimerum --- src/lang/brazilian_portuguese.txt | 46 +++++++++++++++++++++++++++-- src/lang/catalan.txt | 1 + src/lang/english_US.txt | 1 + src/lang/finnish.txt | 1 + src/lang/german.txt | 1 + src/lang/korean.txt | 5 ++-- src/lang/portuguese.txt | 48 +++++++++++++++++++++++++++++-- src/lang/spanish_MX.txt | 42 +++++++++++++++++++++++++-- 8 files changed, 136 insertions(+), 9 deletions(-) diff --git a/src/lang/brazilian_portuguese.txt b/src/lang/brazilian_portuguese.txt index 92c6929b82..96e9fbbd1c 100644 --- a/src/lang/brazilian_portuguese.txt +++ b/src/lang/brazilian_portuguese.txt @@ -954,7 +954,7 @@ STR_GAME_OPTIONS_CURRENCY_MYR :Ringgit Malaio STR_GAME_OPTIONS_ROAD_VEHICLES_DROPDOWN_LEFT :Dirigem na esquerda STR_GAME_OPTIONS_ROAD_VEHICLES_DROPDOWN_RIGHT :Dirigem na direita -STR_GAME_OPTIONS_TOWN_NAMES_FRAME :{BLACK}Nomes das cidades +STR_GAME_OPTIONS_TOWN_NAMES_FRAME :{BLACK}Nome das cidades: STR_GAME_OPTIONS_TOWN_NAMES_DROPDOWN_TOOLTIP :{BLACK}Selecionar o estilo dos nomes das cidades ############ start of townname region @@ -994,6 +994,7 @@ STR_GAME_OPTIONS_AUTOSAVE_DROPDOWN_EVERY_12_MONTHS :A cada 12 meses STR_GAME_OPTIONS_LANGUAGE :{BLACK}Idioma STR_GAME_OPTIONS_LANGUAGE_TOOLTIP :{BLACK}Selecionar o idioma da interface do jogo +STR_GAME_OPTIONS_LANGUAGE_PERCENTAGE :{STRING} ({NUM}% concluído) STR_GAME_OPTIONS_FULLSCREEN :{BLACK}Tela cheia STR_GAME_OPTIONS_FULLSCREEN_TOOLTIP :{BLACK}Marcar esta caixa para jogar OpenTTD modo de tela cheia @@ -1991,6 +1992,8 @@ STR_FACE_TIE :Gravata: STR_FACE_EARRING :Brinco: STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}Alterar gravata ou brinco +STR_NETWORK_SERVER_VISIBILITY_PRIVATE :Privado +STR_NETWORK_SERVER_VISIBILITY_PUBLIC :Público # Network server list STR_NETWORK_SERVER_LIST_CAPTION :{WHITE}Multi-jogador @@ -2054,6 +2057,8 @@ STR_NETWORK_START_SERVER_NEW_GAME_NAME_TOOLTIP :{BLACK}O nome d STR_NETWORK_START_SERVER_SET_PASSWORD :{BLACK}Definir senha STR_NETWORK_START_SERVER_PASSWORD_TOOLTIP :{BLACK}Proteja o jogo com uma senha se não desejar que seja publicamente acessível +STR_NETWORK_START_SERVER_VISIBILITY_LABEL :{BLACK}Visibilidade +STR_NETWORK_START_SERVER_VISIBILITY_TOOLTIP :{BLACK}Se outras pessoas podem ver seu servidor na lista pública STR_NETWORK_START_SERVER_CLIENTS_SELECT :{BLACK}{NUM} cliente{P "" s} STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS :{BLACK}Num máx de clientes: STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS_TOOLTIP :{BLACK}Escolha o número máximo de clientes. Não é necessário estarem todos preenchidos @@ -2117,11 +2122,44 @@ STR_NETWORK_NEED_GAME_PASSWORD_CAPTION :{WHITE}Servidor STR_NETWORK_NEED_COMPANY_PASSWORD_CAPTION :{WHITE}Empresa está protegida. Digite a senha # Network company list added strings -STR_NETWORK_COMPANY_LIST_CLIENT_LIST :Lista de clientes +STR_NETWORK_COMPANY_LIST_CLIENT_LIST :Jogadores online # Network client list +STR_NETWORK_CLIENT_LIST_CAPTION :{WHITE}Multijogador +STR_NETWORK_CLIENT_LIST_SERVER :{BLACK}Servidor +STR_NETWORK_CLIENT_LIST_SERVER_NAME :{BLACK}Nome +STR_NETWORK_CLIENT_LIST_SERVER_NAME_TOOLTIP :{BLACK}Nome do servidor que você está jogando +STR_NETWORK_CLIENT_LIST_SERVER_NAME_EDIT_TOOLTIP :{BLACK}Edita o nome do seu servidor +STR_NETWORK_CLIENT_LIST_SERVER_NAME_QUERY_CAPTION :Nome do servidor +STR_NETWORK_CLIENT_LIST_SERVER_VISIBILITY :{BLACK}Visibilidade +STR_NETWORK_CLIENT_LIST_SERVER_VISIBILITY_TOOLTIP :{BLACK}Se outras pessoas podem ver seu servidor na lista pública +STR_NETWORK_CLIENT_LIST_PLAYER :{BLACK}Jogador +STR_NETWORK_CLIENT_LIST_PLAYER_NAME :{BLACK}Nome +STR_NETWORK_CLIENT_LIST_PLAYER_NAME_TOOLTIP :{BLACK}Seu nome de jogador +STR_NETWORK_CLIENT_LIST_PLAYER_NAME_EDIT_TOOLTIP :{BLACK}Edita seu nome de jogador +STR_NETWORK_CLIENT_LIST_PLAYER_NAME_QUERY_CAPTION :Seu nome de jogador +STR_NETWORK_CLIENT_LIST_ADMIN_CLIENT_TOOLTIP :{BLACK}Ações administrativas a serem executadas para esse cliente +STR_NETWORK_CLIENT_LIST_ADMIN_COMPANY_TOOLTIP :{BLACK}Ações administrativas a serem executadas para essa empresa +STR_NETWORK_CLIENT_LIST_JOIN_TOOLTIP :{BLACK}Junta-se a essa empresa +STR_NETWORK_CLIENT_LIST_CHAT_CLIENT_TOOLTIP :{BLACK}Envia uma mensagem a esse jogador +STR_NETWORK_CLIENT_LIST_CHAT_COMPANY_TOOLTIP :{BLACK}Envia uma mensagem a todos os jogadores dessa empresa +STR_NETWORK_CLIENT_LIST_CHAT_SPECTATOR_TOOLTIP :{BLACK}Envia uma mensagem a todos os espectadores +STR_NETWORK_CLIENT_LIST_SPECTATORS :Espectadores +STR_NETWORK_CLIENT_LIST_NEW_COMPANY :(Nova empresa) +STR_NETWORK_CLIENT_LIST_NEW_COMPANY_TOOLTIP :{BLACK}Cria uma nova empresa e se une a ela +STR_NETWORK_CLIENT_LIST_PLAYER_ICON_SELF_TOOLTIP :{BLACK}Esse é você +STR_NETWORK_CLIENT_LIST_PLAYER_ICON_HOST_TOOLTIP :{BLACK}Esse é o hospedeiro do jogo +STR_NETWORK_CLIENT_LIST_ADMIN_CLIENT_KICK :Expulsar +STR_NETWORK_CLIENT_LIST_ADMIN_CLIENT_BAN :Banir +STR_NETWORK_CLIENT_LIST_ADMIN_COMPANY_RESET :Excluir +STR_NETWORK_CLIENT_LIST_ADMIN_COMPANY_UNLOCK :Desbloqueio com senha +STR_NETWORK_CLIENT_LIST_ASK_CAPTION :{WHITE}Ação de administrador +STR_NETWORK_CLIENT_LIST_ASK_CLIENT_KICK :{YELLOW}Você tem certeza que quer expulsar o jogador '{STRING}'? +STR_NETWORK_CLIENT_LIST_ASK_CLIENT_BAN :{YELLOW}Você tem certeza que quer banir o jogador '{STRING}'? +STR_NETWORK_CLIENT_LIST_ASK_COMPANY_RESET :{YELLOW}Você tem certeza que quer excluir a empresa '{COMPANY}'? +STR_NETWORK_CLIENT_LIST_ASK_COMPANY_UNLOCK :{YELLOW}Você tem certeza que quer restaurar a senha da empresa '{COMPANY}'? STR_NETWORK_SERVER :Servidor STR_NETWORK_CLIENT :Cliente @@ -2166,6 +2204,7 @@ STR_NETWORK_ERROR_SERVER_START :{WHITE}Não foi STR_NETWORK_ERROR_CLIENT_START :{WHITE}Não foi possível estabelecer conexão STR_NETWORK_ERROR_TIMEOUT :{WHITE}Tempo de espera esgotado na conexão #{NUM} STR_NETWORK_ERROR_SERVER_ERROR :{WHITE}Ocorreu um erro de protocolo e a conexão foi encerrada +STR_NETWORK_ERROR_BAD_PLAYER_NAME :{WHITE}Seu nome de jogador não foi definido. O nome pode ser definido no topo da janela de Multijogador STR_NETWORK_ERROR_WRONG_REVISION :{WHITE}A versão deste cliente não condiz com a versão do servidor STR_NETWORK_ERROR_WRONG_PASSWORD :{WHITE}Senha incorreta STR_NETWORK_ERROR_SERVER_FULL :{WHITE}O servidor está cheio @@ -2178,6 +2217,7 @@ STR_NETWORK_ERROR_TIMEOUT_PASSWORD :{WHITE}Você de STR_NETWORK_ERROR_TIMEOUT_COMPUTER :{WHITE}Seu computador é lento demais para acompanhar o servidor STR_NETWORK_ERROR_TIMEOUT_MAP :{WHITE}Seu computador demorou demais para baixar o mapa STR_NETWORK_ERROR_TIMEOUT_JOIN :{WHITE}Seu computador demorou demais para entrar no servidor +STR_NETWORK_ERROR_INVALID_CLIENT_NAME :{WHITE}Seu nome de jogador não é válido ############ Leave those lines in this order!! STR_NETWORK_ERROR_CLIENT_GENERAL :erro geral @@ -2200,6 +2240,7 @@ STR_NETWORK_ERROR_CLIENT_TIMEOUT_PASSWORD :não recebeu se STR_NETWORK_ERROR_CLIENT_TIMEOUT_COMPUTER :tempo esgotado STR_NETWORK_ERROR_CLIENT_TIMEOUT_MAP :a baixa do mapa demorou demais STR_NETWORK_ERROR_CLIENT_TIMEOUT_JOIN :o processamento do mapa demorou demais +STR_NETWORK_ERROR_CLIENT_INVALID_CLIENT_NAME :nome de cliente inválido ############ End of leave-in-this-order STR_NETWORK_ERROR_CLIENT_GUI_LOST_CONNECTION_CAPTION :{WHITE}Possível perda de conexão @@ -3039,6 +3080,7 @@ STR_NEWGRF_ERROR_MSG_WARNING :{RED}Atenção: STR_NEWGRF_ERROR_MSG_ERROR :{RED}Erro: {SILVER}{STRING} STR_NEWGRF_ERROR_MSG_FATAL :{RED}Erro Fatal: {SILVER}{STRING} STR_NEWGRF_ERROR_FATAL_POPUP :{WHITE}Um erro de NewGRF fatal ocorreu:{}{STRING} +STR_NEWGRF_ERROR_POPUP :{WHITE}Um erro NewGRF ocorreu:{}{STRING} STR_NEWGRF_ERROR_VERSION_NUMBER :{1:STRING} não irá funcionar com a versão do TTDPatch encontrada pelo OpenTTD STR_NEWGRF_ERROR_DOS_OR_WINDOWS :{1:STRING} funciona na versão {STRING} de TTD STR_NEWGRF_ERROR_UNSET_SWITCH :{1:STRING} é projetado para ser usado com {STRING} diff --git a/src/lang/catalan.txt b/src/lang/catalan.txt index 6dd49f586b..34b6cfb0b4 100644 --- a/src/lang/catalan.txt +++ b/src/lang/catalan.txt @@ -3080,6 +3080,7 @@ STR_NEWGRF_ERROR_MSG_WARNING :{RED}Alerta: {S STR_NEWGRF_ERROR_MSG_ERROR :{RED}Error: {SILVER}{STRING} STR_NEWGRF_ERROR_MSG_FATAL :{RED}Fatal: {SILVER}{STRING} STR_NEWGRF_ERROR_FATAL_POPUP :{WHITE}S'ha produït un error fatal de NewGRF:{}{STRING} +STR_NEWGRF_ERROR_POPUP :{WHITE}S'ha produït un error relacionat amb els NewGRF:{}{STRING} STR_NEWGRF_ERROR_VERSION_NUMBER :{1:STRING} no funcionarà amb la versió TTDPatch informada per l'OpenTTD STR_NEWGRF_ERROR_DOS_OR_WINDOWS :{1:STRING} és per la versió {STRING} de TTD STR_NEWGRF_ERROR_UNSET_SWITCH :{1:STRING} està dissenyat per ser utilitzat amb {STRING} diff --git a/src/lang/english_US.txt b/src/lang/english_US.txt index 444e46462f..dd81df78e3 100644 --- a/src/lang/english_US.txt +++ b/src/lang/english_US.txt @@ -3079,6 +3079,7 @@ STR_NEWGRF_ERROR_MSG_WARNING :{RED}Warning: { STR_NEWGRF_ERROR_MSG_ERROR :{RED}Error: {SILVER}{STRING} STR_NEWGRF_ERROR_MSG_FATAL :{RED}Fatal: {SILVER}{STRING} STR_NEWGRF_ERROR_FATAL_POPUP :{WHITE}A fatal NewGRF error has occurred:{}{STRING} +STR_NEWGRF_ERROR_POPUP :{WHITE}A NewGRF error has occurred:{}{STRING} STR_NEWGRF_ERROR_VERSION_NUMBER :{1:STRING} will not work with the TTDPatch version reported by OpenTTD STR_NEWGRF_ERROR_DOS_OR_WINDOWS :{1:STRING} is for the {STRING} version of TTD STR_NEWGRF_ERROR_UNSET_SWITCH :{1:STRING} is designed to be used with {STRING} diff --git a/src/lang/finnish.txt b/src/lang/finnish.txt index 4e795f5949..4b5765492b 100644 --- a/src/lang/finnish.txt +++ b/src/lang/finnish.txt @@ -3079,6 +3079,7 @@ STR_NEWGRF_ERROR_MSG_WARNING :{RED}Varoitus: STR_NEWGRF_ERROR_MSG_ERROR :{RED}Virhe: {SILVER}{STRING} STR_NEWGRF_ERROR_MSG_FATAL :{RED}Virhe: {SILVER}{STRING} STR_NEWGRF_ERROR_FATAL_POPUP :{WHITE}Vakava NewGRF-virhe on tapahtunut:{}{STRING} +STR_NEWGRF_ERROR_POPUP :{WHITE}NewGRF-virhe on tapahtunut:{}{STRING} STR_NEWGRF_ERROR_VERSION_NUMBER :{1:STRING} ei toimi OpenTTD:n ilmoittaman TTDPatch-version kanssa STR_NEWGRF_ERROR_DOS_OR_WINDOWS :{1:STRING} on TTD:n {STRING}-versiota varten STR_NEWGRF_ERROR_UNSET_SWITCH :{1:STRING} ja {STRING} on suunniteltu toimimaan yhdessä diff --git a/src/lang/german.txt b/src/lang/german.txt index 2c30fea557..79df8af49c 100644 --- a/src/lang/german.txt +++ b/src/lang/german.txt @@ -3080,6 +3080,7 @@ STR_NEWGRF_ERROR_MSG_WARNING :{RED}Warnung: { STR_NEWGRF_ERROR_MSG_ERROR :{RED}Fehler: {SILVER}{STRING} STR_NEWGRF_ERROR_MSG_FATAL :{RED}Schwerer Fehler: {SILVER}{STRING} STR_NEWGRF_ERROR_FATAL_POPUP :{WHITE}Ein schwerer NewGRF-Fehler ist aufgetreten:{}{STRING} +STR_NEWGRF_ERROR_POPUP :{WHITE}Ein NewGRF-Fehler ist aufgetreten:{}{STRING} STR_NEWGRF_ERROR_VERSION_NUMBER :{1:STRING} funktioniert nicht im Zusammenhang mit der von OpenTTD ermittelten TTDPatch-Version STR_NEWGRF_ERROR_DOS_OR_WINDOWS :{1:STRING} ist für die {STRING}-Version von TTD STR_NEWGRF_ERROR_UNSET_SWITCH :{1:STRING} ist für die Nutzung mit {STRING} vorgesehen diff --git a/src/lang/korean.txt b/src/lang/korean.txt index 95efeeead7..2a51f3dfcc 100644 --- a/src/lang/korean.txt +++ b/src/lang/korean.txt @@ -1808,8 +1808,8 @@ STR_CONFIG_ERROR :{WHITE}설정 STR_CONFIG_ERROR_ARRAY :{WHITE}... 배열 '{STRING}'에서 오류 발생 STR_CONFIG_ERROR_INVALID_VALUE :{WHITE}... '{1:STRING}'에 잘못된 값('{0:STRING}')이 지정되었습니다. STR_CONFIG_ERROR_TRAILING_CHARACTERS :{WHITE}... '{STRING}' 설정의 끝에 후행 문자가 있습니다. -STR_CONFIG_ERROR_DUPLICATE_GRFID :{WHITE}... NewGRF '{STRING}' 무시중: '{STRING}'{G 1 "과" "와"} GRF ID가 겹침 -STR_CONFIG_ERROR_INVALID_GRF :{WHITE}... 유효하지 않은 NewGRF '{STRING}' 무시중: {STRING} +STR_CONFIG_ERROR_DUPLICATE_GRFID :{WHITE}... '{STRING}' NewGRF를 무시합니다: '{STRING}'{G 1 "과" "와"} GRF ID가 겹침 +STR_CONFIG_ERROR_INVALID_GRF :{WHITE}... 유효하지 않은 '{STRING}' NewGRF를 무시합니다: {STRING} STR_CONFIG_ERROR_INVALID_GRF_NOT_FOUND :찾을 수 없음 STR_CONFIG_ERROR_INVALID_GRF_UNSAFE :사용하기에 불안함 STR_CONFIG_ERROR_INVALID_GRF_SYSTEM :NewGRF 시스템 @@ -3080,6 +3080,7 @@ STR_NEWGRF_ERROR_MSG_WARNING :{RED}경고: {S STR_NEWGRF_ERROR_MSG_ERROR :{RED}오류: {SILVER}{STRING} STR_NEWGRF_ERROR_MSG_FATAL :{RED}치명적 오류: {SILVER}{STRING} STR_NEWGRF_ERROR_FATAL_POPUP :{WHITE}치명적인 NewGRF 오류가 발생했습니다:{}{STRING} +STR_NEWGRF_ERROR_POPUP :{WHITE}NewGRF 관련 오류가 발생했습니다:{}{STRING} STR_NEWGRF_ERROR_VERSION_NUMBER :{1:STRING}{G 1 "은" "는"} OpenTTD에서 보고된 TTDPatch 버전에서 작동하지 않을 것입니다 STR_NEWGRF_ERROR_DOS_OR_WINDOWS :{1:STRING}{G 1 "은" "는"} {STRING} 버전의 TTD를 위한 것입니다 STR_NEWGRF_ERROR_UNSET_SWITCH :{1:STRING}{G 1 "은" "는"} {STRING}{G 1 "와" "과"} 같이 사용해야 합니다 diff --git a/src/lang/portuguese.txt b/src/lang/portuguese.txt index fadf6b9f90..d97e52569a 100644 --- a/src/lang/portuguese.txt +++ b/src/lang/portuguese.txt @@ -954,7 +954,7 @@ STR_GAME_OPTIONS_CURRENCY_MYR :Ringgit da Mal STR_GAME_OPTIONS_ROAD_VEHICLES_DROPDOWN_LEFT :Circular pela esquerda STR_GAME_OPTIONS_ROAD_VEHICLES_DROPDOWN_RIGHT :Circular pela direita -STR_GAME_OPTIONS_TOWN_NAMES_FRAME :{BLACK}Nomes das localidades +STR_GAME_OPTIONS_TOWN_NAMES_FRAME :{BLACK}Nomes das localidades: STR_GAME_OPTIONS_TOWN_NAMES_DROPDOWN_TOOLTIP :{BLACK}Seleccionar o estilo dos nomes das localidades ############ start of townname region @@ -994,6 +994,7 @@ STR_GAME_OPTIONS_AUTOSAVE_DROPDOWN_EVERY_12_MONTHS :Cada 12 meses STR_GAME_OPTIONS_LANGUAGE :{BLACK}Idioma STR_GAME_OPTIONS_LANGUAGE_TOOLTIP :{BLACK}Seleccionar o idioma da interface do jogo +STR_GAME_OPTIONS_LANGUAGE_PERCENTAGE :{STRING} ({NUM}% completado) STR_GAME_OPTIONS_FULLSCREEN :{BLACK}Ecrã Inteiro STR_GAME_OPTIONS_FULLSCREEN_TOOLTIP :{BLACK}Seleccione esta opção para jogar o OpenTTD em modo de ecrã inteiro @@ -1991,6 +1992,8 @@ STR_FACE_TIE :Gravata: STR_FACE_EARRING :Brinco: STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}Mudar gravata ou brinco +STR_NETWORK_SERVER_VISIBILITY_PRIVATE :Privado +STR_NETWORK_SERVER_VISIBILITY_PUBLIC :Público # Network server list STR_NETWORK_SERVER_LIST_CAPTION :{WHITE}Multi-jogador @@ -2043,7 +2046,7 @@ STR_NETWORK_SERVER_LIST_ADD_SERVER_TOOLTIP :{BLACK}Adiciona STR_NETWORK_SERVER_LIST_START_SERVER :{BLACK}Iniciar servidor STR_NETWORK_SERVER_LIST_START_SERVER_TOOLTIP :{BLACK}Iniciar um servidor próprio -STR_NETWORK_SERVER_LIST_PLAYER_NAME_OSKTITLE :{BLACK}Digite teu nome +STR_NETWORK_SERVER_LIST_PLAYER_NAME_OSKTITLE :{BLACK}Introduza o seu nome STR_NETWORK_SERVER_LIST_ENTER_IP :{BLACK}Introduza o endereço IP do servidor # Start new multiplayer server @@ -2054,6 +2057,8 @@ STR_NETWORK_START_SERVER_NEW_GAME_NAME_TOOLTIP :{BLACK}O nome d STR_NETWORK_START_SERVER_SET_PASSWORD :{BLACK}Definir palavra-chave STR_NETWORK_START_SERVER_PASSWORD_TOOLTIP :{BLACK}Proteja o jogo com uma senha se não desejar que pessoas indesejadas se juntem +STR_NETWORK_START_SERVER_VISIBILITY_LABEL :{BLACK}Visibilidade +STR_NETWORK_START_SERVER_VISIBILITY_TOOLTIP :{BLACK}Se as outras pessoas podem ver o seu servidor na lista pública STR_NETWORK_START_SERVER_CLIENTS_SELECT :{BLACK}{NUM} cliente{P "" s} STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS :{BLACK}Máximo de clientes: STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS_TOOLTIP :{BLACK}Escolha o número máximo de clientes. Não necessitam estar todos presentes. @@ -2117,11 +2122,44 @@ STR_NETWORK_NEED_GAME_PASSWORD_CAPTION :{WHITE}Servidor STR_NETWORK_NEED_COMPANY_PASSWORD_CAPTION :{WHITE}Empresa protegida. Introduza palavra-chave # Network company list added strings -STR_NETWORK_COMPANY_LIST_CLIENT_LIST :Lista de clientes +STR_NETWORK_COMPANY_LIST_CLIENT_LIST :Jogadores "online" # Network client list +STR_NETWORK_CLIENT_LIST_CAPTION :{WHITE}Multi-jogador +STR_NETWORK_CLIENT_LIST_SERVER :{BLACK}Servidor +STR_NETWORK_CLIENT_LIST_SERVER_NAME :{BLACK}Nome +STR_NETWORK_CLIENT_LIST_SERVER_NAME_TOOLTIP :{BLACK}Nome do servidor onde está a jogar +STR_NETWORK_CLIENT_LIST_SERVER_NAME_EDIT_TOOLTIP :{BLACK}Editar o nome do seu servidor +STR_NETWORK_CLIENT_LIST_SERVER_NAME_QUERY_CAPTION :Nome do servidor +STR_NETWORK_CLIENT_LIST_SERVER_VISIBILITY :{BLACK}Visibilidade +STR_NETWORK_CLIENT_LIST_SERVER_VISIBILITY_TOOLTIP :{BLACK}Se as outras pessoas podem ver o seu servidor na lista pública +STR_NETWORK_CLIENT_LIST_PLAYER :{BLACK}Jogador +STR_NETWORK_CLIENT_LIST_PLAYER_NAME :{BLACK}Nome +STR_NETWORK_CLIENT_LIST_PLAYER_NAME_TOOLTIP :{BLACK}O seu nome de jogador +STR_NETWORK_CLIENT_LIST_PLAYER_NAME_EDIT_TOOLTIP :{BLACK}Editar o seu nome de jogador +STR_NETWORK_CLIENT_LIST_PLAYER_NAME_QUERY_CAPTION :O seu nome de jogador +STR_NETWORK_CLIENT_LIST_ADMIN_CLIENT_TOOLTIP :{BLACK}Ações administrativas para executar a este cliente +STR_NETWORK_CLIENT_LIST_ADMIN_COMPANY_TOOLTIP :{BLACK}Ações administrativas para executar a esta empresa +STR_NETWORK_CLIENT_LIST_JOIN_TOOLTIP :{BLACK}Juntar-se a esta empresa +STR_NETWORK_CLIENT_LIST_CHAT_CLIENT_TOOLTIP :{BLACK}Enviar uma mensagem a este jogador +STR_NETWORK_CLIENT_LIST_CHAT_COMPANY_TOOLTIP :{BLACK}Enviar uma mensagem a todos os jogadores desta empresa +STR_NETWORK_CLIENT_LIST_CHAT_SPECTATOR_TOOLTIP :{BLACK}Enviar uma mensagem a todos os espectadores +STR_NETWORK_CLIENT_LIST_SPECTATORS :Espectadores +STR_NETWORK_CLIENT_LIST_NEW_COMPANY :(Nova empresa) +STR_NETWORK_CLIENT_LIST_NEW_COMPANY_TOOLTIP :{BLACK}Criar uma nova empresa e juntar-se a ela +STR_NETWORK_CLIENT_LIST_PLAYER_ICON_SELF_TOOLTIP :{BLACK}Este é você +STR_NETWORK_CLIENT_LIST_PLAYER_ICON_HOST_TOOLTIP :{BLACK}Este é o anfitrião do jogo +STR_NETWORK_CLIENT_LIST_ADMIN_CLIENT_KICK :Expulsar +STR_NETWORK_CLIENT_LIST_ADMIN_CLIENT_BAN :Banir +STR_NETWORK_CLIENT_LIST_ADMIN_COMPANY_RESET :Apagar +STR_NETWORK_CLIENT_LIST_ADMIN_COMPANY_UNLOCK :desbloquear com palavra-passe +STR_NETWORK_CLIENT_LIST_ASK_CAPTION :{WHITE}Ação administrativa +STR_NETWORK_CLIENT_LIST_ASK_CLIENT_KICK :{YELLOW}Tem a certeza que quer expulsar o jogador '{STRING}'? +STR_NETWORK_CLIENT_LIST_ASK_CLIENT_BAN :{YELLOW}Tem a certeza que quer banir o jogador '{STRING}'? +STR_NETWORK_CLIENT_LIST_ASK_COMPANY_RESET :{YELLOW}Tem a certeza que quer apagar a empresa '{COMPANY}'? +STR_NETWORK_CLIENT_LIST_ASK_COMPANY_UNLOCK :{YELLOW}Tem a certeza que quer restabelecer a palavra-chave da empresa '{COMPANY}'? STR_NETWORK_SERVER :Servidor STR_NETWORK_CLIENT :Cliente @@ -2166,6 +2204,7 @@ STR_NETWORK_ERROR_SERVER_START :{WHITE}Não foi STR_NETWORK_ERROR_CLIENT_START :{WHITE}Não foi possível estabelecer ligação STR_NETWORK_ERROR_TIMEOUT :{WHITE}Tempo de espera esgotado na conexão #{NUM}. STR_NETWORK_ERROR_SERVER_ERROR :{WHITE}Ocorreu um erro de protocolo e a ligação foi encerrada +STR_NETWORK_ERROR_BAD_PLAYER_NAME :{WHITE}O seu nome de jogador não foi definido. O nome pode ser definido no topo da janela de Multi-jogador STR_NETWORK_ERROR_WRONG_REVISION :{WHITE}A revisão deste cliente não condiz com a revisão do servidor STR_NETWORK_ERROR_WRONG_PASSWORD :{WHITE}Palavra-chave incorrecta STR_NETWORK_ERROR_SERVER_FULL :{WHITE}Servidor cheio @@ -2178,6 +2217,7 @@ STR_NETWORK_ERROR_TIMEOUT_PASSWORD :{WHITE}Demorou STR_NETWORK_ERROR_TIMEOUT_COMPUTER :{WHITE}O seu computador é demasiado lento para acompanhar com o servidor STR_NETWORK_ERROR_TIMEOUT_MAP :{WHITE}O seu computador demorou demasiado a transferir o mapa STR_NETWORK_ERROR_TIMEOUT_JOIN :{WHITE}O seu computador demorou demasiado a ligar ao servidor +STR_NETWORK_ERROR_INVALID_CLIENT_NAME :{WHITE}O seu nome de jogador não é válido ############ Leave those lines in this order!! STR_NETWORK_ERROR_CLIENT_GENERAL :erro geral @@ -2200,6 +2240,7 @@ STR_NETWORK_ERROR_CLIENT_TIMEOUT_PASSWORD :não foi recebi STR_NETWORK_ERROR_CLIENT_TIMEOUT_COMPUTER :demasiado tempo (geral) STR_NETWORK_ERROR_CLIENT_TIMEOUT_MAP :demorou demasiado a transferir o mapa STR_NETWORK_ERROR_CLIENT_TIMEOUT_JOIN :demorou demasiado a processar o mapa +STR_NETWORK_ERROR_CLIENT_INVALID_CLIENT_NAME :nome de cliente inválido ############ End of leave-in-this-order STR_NETWORK_ERROR_CLIENT_GUI_LOST_CONNECTION_CAPTION :{WHITE}Possível perda de conexão @@ -3039,6 +3080,7 @@ STR_NEWGRF_ERROR_MSG_WARNING :{RED}Aviso: {SI STR_NEWGRF_ERROR_MSG_ERROR :{RED}Erro: {SILVER}{STRING} STR_NEWGRF_ERROR_MSG_FATAL :{RED}Fatal: {SILVER}{STRING} STR_NEWGRF_ERROR_FATAL_POPUP :{WHITE}Ocorreu um erro fatal num NewGRF:{}{STRING} +STR_NEWGRF_ERROR_POPUP :{WHITE}Ocorreu um erro de NewGRF:{}{STRING} STR_NEWGRF_ERROR_VERSION_NUMBER :{1:STRING} não funciona com a versão do TTDPatch reportada por OpenTTD. STR_NEWGRF_ERROR_DOS_OR_WINDOWS :{1:STRING} é para a versão {STRING} do TTD. STR_NEWGRF_ERROR_UNSET_SWITCH :{1:STRING} foi concebido para ser usado com {STRING} diff --git a/src/lang/spanish_MX.txt b/src/lang/spanish_MX.txt index 74b8b10cff..08d5bdea6b 100644 --- a/src/lang/spanish_MX.txt +++ b/src/lang/spanish_MX.txt @@ -954,7 +954,7 @@ STR_GAME_OPTIONS_CURRENCY_MYR :Ringgit malasio STR_GAME_OPTIONS_ROAD_VEHICLES_DROPDOWN_LEFT :Manejar por la izquierda STR_GAME_OPTIONS_ROAD_VEHICLES_DROPDOWN_RIGHT :Manejar por la derecha -STR_GAME_OPTIONS_TOWN_NAMES_FRAME :{BLACK}Nombres de pueblos +STR_GAME_OPTIONS_TOWN_NAMES_FRAME :{BLACK}Nombres de pueblos: STR_GAME_OPTIONS_TOWN_NAMES_DROPDOWN_TOOLTIP :{BLACK}Elegir el estilo de nombres para pueblos ############ start of townname region @@ -1992,6 +1992,8 @@ STR_FACE_TIE :Corbata: STR_FACE_EARRING :Aretes: STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}Cambiar corbata o aretes +STR_NETWORK_SERVER_VISIBILITY_PRIVATE :Privado +STR_NETWORK_SERVER_VISIBILITY_PUBLIC :Público # Network server list STR_NETWORK_SERVER_LIST_CAPTION :{WHITE}Multijugador @@ -2055,6 +2057,8 @@ STR_NETWORK_START_SERVER_NEW_GAME_NAME_TOOLTIP :{BLACK}La parti STR_NETWORK_START_SERVER_SET_PASSWORD :{BLACK}Establecer contraseña STR_NETWORK_START_SERVER_PASSWORD_TOOLTIP :{BLACK}Proteger la partida con una contraseña para prevenir el acceso a otras personas +STR_NETWORK_START_SERVER_VISIBILITY_LABEL :{BLACK}Visibilidad +STR_NETWORK_START_SERVER_VISIBILITY_TOOLTIP :{BLACK}Quién puede ver tu servidor en la lista pública STR_NETWORK_START_SERVER_CLIENTS_SELECT :{BLACK}{NUM} cliente{P "" s} STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS :{BLACK}Número máximo de clientes: STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS_TOOLTIP :{BLACK}Elegir el número máximo de clientes. No es necesario que se conecten todos @@ -2118,11 +2122,44 @@ STR_NETWORK_NEED_GAME_PASSWORD_CAPTION :{WHITE}Servidor STR_NETWORK_NEED_COMPANY_PASSWORD_CAPTION :{WHITE}Empresa protegida. Introducir contraseña # Network company list added strings -STR_NETWORK_COMPANY_LIST_CLIENT_LIST :Lista de clientes +STR_NETWORK_COMPANY_LIST_CLIENT_LIST :Jugadores conectados # Network client list +STR_NETWORK_CLIENT_LIST_CAPTION :{WHITE}Multijugador +STR_NETWORK_CLIENT_LIST_SERVER :{BLACK}Servidor +STR_NETWORK_CLIENT_LIST_SERVER_NAME :{BLACK}Nombre +STR_NETWORK_CLIENT_LIST_SERVER_NAME_TOOLTIP :{BLACK}Nombre del servidor en el que estás jugando +STR_NETWORK_CLIENT_LIST_SERVER_NAME_EDIT_TOOLTIP :{BLACK}Modificar nombre del servidor +STR_NETWORK_CLIENT_LIST_SERVER_NAME_QUERY_CAPTION :Nombre del servidor +STR_NETWORK_CLIENT_LIST_SERVER_VISIBILITY :{BLACK}Visibilidad +STR_NETWORK_CLIENT_LIST_SERVER_VISIBILITY_TOOLTIP :{BLACK}Quién puede ver tu servidor en la lista pública +STR_NETWORK_CLIENT_LIST_PLAYER :{BLACK}Jugador +STR_NETWORK_CLIENT_LIST_PLAYER_NAME :{BLACK}Nombre +STR_NETWORK_CLIENT_LIST_PLAYER_NAME_TOOLTIP :{BLACK}Tu nombre de jugador +STR_NETWORK_CLIENT_LIST_PLAYER_NAME_EDIT_TOOLTIP :{BLACK}Modificar nombre de jugador +STR_NETWORK_CLIENT_LIST_PLAYER_NAME_QUERY_CAPTION :Tu nombre de jugador +STR_NETWORK_CLIENT_LIST_ADMIN_CLIENT_TOOLTIP :{BLACK}Acciones administrativas para este cliente +STR_NETWORK_CLIENT_LIST_ADMIN_COMPANY_TOOLTIP :{BLACK}Acciones administrativas para esta empresa +STR_NETWORK_CLIENT_LIST_JOIN_TOOLTIP :{BLACK}Unirse a esta empresa +STR_NETWORK_CLIENT_LIST_CHAT_CLIENT_TOOLTIP :{BLACK}Enviar mensaje a este jugador +STR_NETWORK_CLIENT_LIST_CHAT_COMPANY_TOOLTIP :{BLACK}Enviar mensaje a todos los jugadores de la empresa +STR_NETWORK_CLIENT_LIST_CHAT_SPECTATOR_TOOLTIP :{BLACK}Enviar mensaje a los espectadores +STR_NETWORK_CLIENT_LIST_SPECTATORS :Espectadores +STR_NETWORK_CLIENT_LIST_NEW_COMPANY :(Nueva empresa) +STR_NETWORK_CLIENT_LIST_NEW_COMPANY_TOOLTIP :{BLACK}Crear nueva empresa y unirse a ella +STR_NETWORK_CLIENT_LIST_PLAYER_ICON_SELF_TOOLTIP :{BLACK}Este eres tú +STR_NETWORK_CLIENT_LIST_PLAYER_ICON_HOST_TOOLTIP :{BLACK}Este es el host del juego +STR_NETWORK_CLIENT_LIST_ADMIN_CLIENT_KICK :Expulsar +STR_NETWORK_CLIENT_LIST_ADMIN_CLIENT_BAN :Bloquear acceso +STR_NETWORK_CLIENT_LIST_ADMIN_COMPANY_RESET :Eliminar +STR_NETWORK_CLIENT_LIST_ADMIN_COMPANY_UNLOCK :Restablecer contraseña +STR_NETWORK_CLIENT_LIST_ASK_CAPTION :{WHITE}Acción de aministrador +STR_NETWORK_CLIENT_LIST_ASK_CLIENT_KICK :{YELLOW}¿Sacar al jugador "{STRING}"? +STR_NETWORK_CLIENT_LIST_ASK_CLIENT_BAN :{YELLOW}¿Bloquear acceso al jugador "{STRING}"? +STR_NETWORK_CLIENT_LIST_ASK_COMPANY_RESET :{YELLOW}¿Eliminar la empresa "{COMPANY}"? +STR_NETWORK_CLIENT_LIST_ASK_COMPANY_UNLOCK :{YELLOW}¿Restablecer contraseña de la empresa "{COMPANY}"? STR_NETWORK_SERVER :Servidor STR_NETWORK_CLIENT :Cliente @@ -3043,6 +3080,7 @@ STR_NEWGRF_ERROR_MSG_WARNING :{RED}Atención: STR_NEWGRF_ERROR_MSG_ERROR :{RED}Error: {SILVER}{STRING} STR_NEWGRF_ERROR_MSG_FATAL :{RED}Error fatal: {SILVER}{STRING} STR_NEWGRF_ERROR_FATAL_POPUP :{WHITE}Ocurrió un error fatal de NewGRF:{}{STRING} +STR_NEWGRF_ERROR_POPUP :{WHITE}Ocurrió un error de NewGRF:{}{STRING} STR_NEWGRF_ERROR_VERSION_NUMBER :{1:STRING} no funcionará con la con la versión de TTDPatch reportada por OpenTTD STR_NEWGRF_ERROR_DOS_OR_WINDOWS :{1:STRING} es la para la versión {STRING} de TTD STR_NEWGRF_ERROR_UNSET_SWITCH :{1:STRING} está diseñado para usarse con {STRING} From 8e9eca6dddf238c77f01d93afcefcca262447e98 Mon Sep 17 00:00:00 2001 From: Milek7 Date: Wed, 28 Apr 2021 23:06:47 +0200 Subject: [PATCH 191/268] Codechange: Use __attribute__ access none to silence GCC 11 -Wmaybe-uninitialized warnings (#9124) --- src/stdafx.h | 6 ++++++ src/string_func.h | 16 ++++++++-------- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/src/stdafx.h b/src/stdafx.h index 6a33223455..19009f5ff5 100644 --- a/src/stdafx.h +++ b/src/stdafx.h @@ -138,6 +138,12 @@ # endif #endif /* __GNUC__ || __clang__ */ +#if __GNUC__ > 11 || (__GNUC__ == 11 && __GNUC_MINOR__ >= 1) +# define NOACCESS(args) __attribute__ ((access (none, args))) +#else +# define NOACCESS(args) +#endif + #if defined(__WATCOMC__) # define NORETURN # define CDECL diff --git a/src/string_func.h b/src/string_func.h index 6c894b3cba..4fe9d7b825 100644 --- a/src/string_func.h +++ b/src/string_func.h @@ -30,25 +30,25 @@ #include "core/bitmath_func.hpp" #include "string_type.h" -char *strecat(char *dst, const char *src, const char *last); -char *strecpy(char *dst, const char *src, const char *last); -char *stredup(const char *src, const char *last = nullptr); +char *strecat(char *dst, const char *src, const char *last) NOACCESS(3); +char *strecpy(char *dst, const char *src, const char *last) NOACCESS(3); +char *stredup(const char *src, const char *last = nullptr) NOACCESS(2); -int CDECL seprintf(char *str, const char *last, const char *format, ...) WARN_FORMAT(3, 4); -int CDECL vseprintf(char *str, const char *last, const char *format, va_list ap) WARN_FORMAT(3, 0); +int CDECL seprintf(char *str, const char *last, const char *format, ...) WARN_FORMAT(3, 4) NOACCESS(2); +int CDECL vseprintf(char *str, const char *last, const char *format, va_list ap) WARN_FORMAT(3, 0) NOACCESS(2); char *CDECL str_fmt(const char *str, ...) WARN_FORMAT(1, 2); -void str_validate(char *str, const char *last, StringValidationSettings settings = SVS_REPLACE_WITH_QUESTION_MARK); +void str_validate(char *str, const char *last, StringValidationSettings settings = SVS_REPLACE_WITH_QUESTION_MARK) NOACCESS(2); std::string str_validate(const std::string &str, StringValidationSettings settings = SVS_REPLACE_WITH_QUESTION_MARK); void ValidateString(const char *str); -void str_fix_scc_encoded(char *str, const char *last); +void str_fix_scc_encoded(char *str, const char *last) NOACCESS(2); void str_strip_colours(char *str); bool strtolower(char *str); bool strtolower(std::string &str, std::string::size_type offs = 0); -bool StrValid(const char *str, const char *last); +bool StrValid(const char *str, const char *last) NOACCESS(2); void StrTrimInPlace(char *str); /** From 96dc0d04ecb12ee83909782b9a2c39f336de7d9a Mon Sep 17 00:00:00 2001 From: Patric Stout Date: Wed, 28 Apr 2021 23:09:03 +0200 Subject: [PATCH 192/268] Fix b3003dd1: swap SERVER_GAME_INFO with CLIENT_GAME_INFO (#9129) The idea is that if you query an older server that does not support this packet yet, the client receives an error. The assumption was that on every "illegal packet" the connection would be closed. This turns out to be false. Now CLIENT_GAME_INFO aligns with the old PACKET_CLIENT_NEWGRFS_CHECKED, which does a pre-check (which fails), and an error is sent back and the connection is closed. This is not a nice solution, but it is the best we got. --- src/network/core/tcp_game.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/network/core/tcp_game.h b/src/network/core/tcp_game.h index 6dcc0bd01f..5fe9cd465d 100644 --- a/src/network/core/tcp_game.h +++ b/src/network/core/tcp_game.h @@ -43,8 +43,8 @@ enum PacketGameType { PACKET_SERVER_COMPANY_INFO, ///< Information about a single company. /* Packets used to get the game info. */ - PACKET_CLIENT_GAME_INFO, ///< Request information about the server. PACKET_SERVER_GAME_INFO, ///< Information about the server. + PACKET_CLIENT_GAME_INFO, ///< Request information about the server. /* * Packets after here assume that the client From ae7f07de74a8d242dce8a67eaece26be9d06f064 Mon Sep 17 00:00:00 2001 From: PeterN Date: Wed, 28 Apr 2021 22:32:43 +0100 Subject: [PATCH 193/268] Fix: Incorrect vertical alignment of icon and text in DropDownListIconItem. (#9133) This happens if the bounding dimensions are changed so that each item is the same size, as happens on the railtype/roadtype dropdown lists, as the vertical offset was calculated before this dimension is changed. --- src/widgets/dropdown.cpp | 12 +++--------- src/widgets/dropdown_type.h | 1 - 2 files changed, 3 insertions(+), 10 deletions(-) diff --git a/src/widgets/dropdown.cpp b/src/widgets/dropdown.cpp index 97c755e4bc..6b1debb9db 100644 --- a/src/widgets/dropdown.cpp +++ b/src/widgets/dropdown.cpp @@ -72,13 +72,7 @@ StringID DropDownListCharStringItem::String() const DropDownListIconItem::DropDownListIconItem(SpriteID sprite, PaletteID pal, StringID string, int result, bool masked) : DropDownListParamStringItem(string, result, masked), sprite(sprite), pal(pal) { this->dim = GetSpriteSize(sprite); - if (this->dim.height < (uint)FONT_HEIGHT_NORMAL) { - this->sprite_y = (FONT_HEIGHT_NORMAL - dim.height) / 2; - this->text_y = 0; - } else { - this->sprite_y = 0; - this->text_y = (dim.height - FONT_HEIGHT_NORMAL) / 2; - } + this->sprite_y = dim.height; } uint DropDownListIconItem::Height(uint width) const @@ -94,8 +88,8 @@ uint DropDownListIconItem::Width() const void DropDownListIconItem::Draw(int left, int right, int top, int bottom, bool sel, Colours bg_colour) const { bool rtl = _current_text_dir == TD_RTL; - DrawSprite(this->sprite, this->pal, rtl ? right - this->dim.width - WD_FRAMERECT_RIGHT : left + WD_FRAMERECT_LEFT, top + this->sprite_y); - DrawString(left + WD_FRAMERECT_LEFT + (rtl ? 0 : (this->dim.width + WD_FRAMERECT_LEFT)), right - WD_FRAMERECT_RIGHT - (rtl ? (this->dim.width + WD_FRAMERECT_RIGHT) : 0), top + this->text_y, this->String(), sel ? TC_WHITE : TC_BLACK); + DrawSprite(this->sprite, this->pal, rtl ? right - this->dim.width - WD_FRAMERECT_RIGHT : left + WD_FRAMERECT_LEFT, CenterBounds(top, bottom, this->sprite_y)); + DrawString(left + WD_FRAMERECT_LEFT + (rtl ? 0 : (this->dim.width + WD_FRAMERECT_LEFT)), right - WD_FRAMERECT_RIGHT - (rtl ? (this->dim.width + WD_FRAMERECT_RIGHT) : 0), CenterBounds(top, bottom, FONT_HEIGHT_NORMAL), this->String(), sel ? TC_WHITE : TC_BLACK); } void DropDownListIconItem::SetDimension(Dimension d) diff --git a/src/widgets/dropdown_type.h b/src/widgets/dropdown_type.h index a1f240e96d..5dfa9ed58b 100644 --- a/src/widgets/dropdown_type.h +++ b/src/widgets/dropdown_type.h @@ -84,7 +84,6 @@ class DropDownListIconItem : public DropDownListParamStringItem { PaletteID pal; Dimension dim; uint sprite_y; - uint text_y; public: DropDownListIconItem(SpriteID sprite, PaletteID pal, StringID string, int result, bool masked); From 3b3d80c8efbf3310a80e0540b4436a7ded147378 Mon Sep 17 00:00:00 2001 From: Peter Nelson Date: Wed, 28 Apr 2021 21:49:58 +0100 Subject: [PATCH 194/268] Cleanup: Replace FOR_ALL_SORTED_RAILTYPES macro with range iterator. --- src/company_gui.cpp | 9 +++------ src/rail.h | 6 ------ src/rail_gui.cpp | 5 ++--- 3 files changed, 5 insertions(+), 15 deletions(-) diff --git a/src/company_gui.cpp b/src/company_gui.cpp index c9e3eaf861..3706db2f5e 100644 --- a/src/company_gui.cpp +++ b/src/company_gui.cpp @@ -1840,8 +1840,7 @@ struct CompanyInfrastructureWindow : Window size->width = std::max(size->width, GetStringBoundingBox(STR_COMPANY_INFRASTRUCTURE_VIEW_RAIL_SECT).width); - RailType rt; - FOR_ALL_SORTED_RAILTYPES(rt) { + for (const auto &rt : _sorted_railtypes) { if (HasBit(this->railtypes, rt)) { lines++; SetDParam(0, GetRailTypeInfo(rt)->strings.name); @@ -1973,8 +1972,7 @@ struct CompanyInfrastructureWindow : Window if (this->railtypes != RAILTYPES_NONE) { /* Draw name of each valid railtype. */ - RailType rt; - FOR_ALL_SORTED_RAILTYPES(rt) { + for (const auto &rt : _sorted_railtypes) { if (HasBit(this->railtypes, rt)) { SetDParam(0, GetRailTypeInfo(rt)->strings.name); DrawString(r.left + offs_left, r.right - offs_right, y += FONT_HEIGHT_NORMAL, STR_WHITE_STRING); @@ -1991,8 +1989,7 @@ struct CompanyInfrastructureWindow : Window case WID_CI_RAIL_COUNT: { /* Draw infrastructure count for each valid railtype. */ uint32 rail_total = c->infrastructure.GetRailTotal(); - RailType rt; - FOR_ALL_SORTED_RAILTYPES(rt) { + for (const auto &rt : _sorted_railtypes) { if (HasBit(this->railtypes, rt)) { this->DrawCountLine(r, y, c->infrastructure.rail[rt], RailMaintenanceCost(rt, c->infrastructure.rail[rt], rail_total)); } diff --git a/src/rail.h b/src/rail.h index c5775b9467..a0bbb0109b 100644 --- a/src/rail.h +++ b/src/rail.h @@ -464,10 +464,4 @@ RailType AllocateRailType(RailTypeLabel label); extern std::vector _sorted_railtypes; extern RailTypes _railtypes_hidden_mask; -/** - * Loop header for iterating over railtypes, sorted by sortorder. - * @param var Railtype. - */ -#define FOR_ALL_SORTED_RAILTYPES(var) for (uint8 index = 0; index < _sorted_railtypes.size() && (var = _sorted_railtypes[index], true) ; index++) - #endif /* RAIL_H */ diff --git a/src/rail_gui.cpp b/src/rail_gui.cpp index f0f5beccac..49c7d736af 100644 --- a/src/rail_gui.cpp +++ b/src/rail_gui.cpp @@ -2191,17 +2191,16 @@ DropDownList GetRailTypeDropDownList(bool for_replacement, bool all_option) } Dimension d = { 0, 0 }; - RailType rt; /* Get largest icon size, to ensure text is aligned on each menu item. */ if (!for_replacement) { - FOR_ALL_SORTED_RAILTYPES(rt) { + for (const auto &rt : _sorted_railtypes) { if (!HasBit(used_railtypes, rt)) continue; const RailtypeInfo *rti = GetRailTypeInfo(rt); d = maxdim(d, GetSpriteSize(rti->gui_sprites.build_x_rail)); } } - FOR_ALL_SORTED_RAILTYPES(rt) { + for (const auto &rt : _sorted_railtypes) { /* If it's not used ever, don't show it to the user. */ if (!HasBit(used_railtypes, rt)) continue; From de81afdf4bfb97a48533489101e3aa0e5eb2cb4f Mon Sep 17 00:00:00 2001 From: Peter Nelson Date: Wed, 28 Apr 2021 21:50:23 +0100 Subject: [PATCH 195/268] Cleanup: Replace FOR_ALL_SORTED_ROADTYPES macro with range iterator. --- src/company_gui.cpp | 9 +++------ src/road.h | 6 ------ src/road_gui.cpp | 10 ++++------ 3 files changed, 7 insertions(+), 18 deletions(-) diff --git a/src/company_gui.cpp b/src/company_gui.cpp index 3706db2f5e..f9eceba177 100644 --- a/src/company_gui.cpp +++ b/src/company_gui.cpp @@ -1862,8 +1862,7 @@ struct CompanyInfrastructureWindow : Window size->width = std::max(size->width, GetStringBoundingBox(widget == WID_CI_ROAD_DESC ? STR_COMPANY_INFRASTRUCTURE_VIEW_ROAD_SECT : STR_COMPANY_INFRASTRUCTURE_VIEW_TRAM_SECT).width); - RoadType rt; - FOR_ALL_SORTED_ROADTYPES(rt) { + for (const auto &rt : _sorted_roadtypes) { if (HasBit(this->roadtypes, rt) && RoadTypeIsRoad(rt) == (widget == WID_CI_ROAD_DESC)) { lines++; SetDParam(0, GetRoadTypeInfo(rt)->strings.name); @@ -2005,8 +2004,7 @@ struct CompanyInfrastructureWindow : Window DrawString(r.left, r.right, y, widget == WID_CI_ROAD_DESC ? STR_COMPANY_INFRASTRUCTURE_VIEW_ROAD_SECT : STR_COMPANY_INFRASTRUCTURE_VIEW_TRAM_SECT); /* Draw name of each valid roadtype. */ - RoadType rt; - FOR_ALL_SORTED_ROADTYPES(rt) { + for (const auto &rt : _sorted_roadtypes) { if (HasBit(this->roadtypes, rt) && RoadTypeIsRoad(rt) == (widget == WID_CI_ROAD_DESC)) { SetDParam(0, GetRoadTypeInfo(rt)->strings.name); DrawString(r.left + offs_left, r.right - offs_right, y += FONT_HEIGHT_NORMAL, STR_WHITE_STRING); @@ -2019,8 +2017,7 @@ struct CompanyInfrastructureWindow : Window case WID_CI_ROAD_COUNT: case WID_CI_TRAM_COUNT: { uint32 road_tram_total = widget == WID_CI_ROAD_COUNT ? c->infrastructure.GetRoadTotal() : c->infrastructure.GetTramTotal(); - RoadType rt; - FOR_ALL_SORTED_ROADTYPES(rt) { + for (const auto &rt : _sorted_roadtypes) { if (HasBit(this->roadtypes, rt) && RoadTypeIsRoad(rt) == (widget == WID_CI_ROAD_COUNT)) { this->DrawCountLine(r, y, c->infrastructure.road[rt], RoadMaintenanceCost(rt, c->infrastructure.road[rt], road_tram_total)); } diff --git a/src/road.h b/src/road.h index 1082ab2729..becbf40512 100644 --- a/src/road.h +++ b/src/road.h @@ -305,10 +305,4 @@ bool HasAnyRoadTypesAvail(CompanyID company, RoadTramType rtt); extern std::vector _sorted_roadtypes; extern RoadTypes _roadtypes_hidden_mask; -/** - * Loop header for iterating over roadtypes, sorted by sortorder. - * @param var Roadtype. - */ -#define FOR_ALL_SORTED_ROADTYPES(var) for (uint8 index = 0; index < _sorted_roadtypes.size() && (var = _sorted_roadtypes[index], true) ; index++) - #endif /* ROAD_H */ diff --git a/src/road_gui.cpp b/src/road_gui.cpp index 9aa1952477..9cc68028d5 100644 --- a/src/road_gui.cpp +++ b/src/road_gui.cpp @@ -1313,17 +1313,16 @@ DropDownList GetRoadTypeDropDownList(RoadTramTypes rtts, bool for_replacement, b } Dimension d = { 0, 0 }; - RoadType rt; /* Get largest icon size, to ensure text is aligned on each menu item. */ if (!for_replacement) { - FOR_ALL_SORTED_ROADTYPES(rt) { + for (const auto &rt : _sorted_roadtypes) { if (!HasBit(used_roadtypes, rt)) continue; const RoadTypeInfo *rti = GetRoadTypeInfo(rt); d = maxdim(d, GetSpriteSize(rti->gui_sprites.build_x_road)); } } - FOR_ALL_SORTED_ROADTYPES(rt) { + for (const auto &rt : _sorted_roadtypes) { /* If it's not used ever, don't show it to the user. */ if (!HasBit(used_roadtypes, rt)) continue; @@ -1365,13 +1364,12 @@ DropDownList GetScenRoadTypeDropDownList(RoadTramTypes rtts) /* If it's not used ever, don't show it to the user. */ Dimension d = { 0, 0 }; - RoadType rt; - FOR_ALL_SORTED_ROADTYPES(rt) { + for (const auto &rt : _sorted_roadtypes) { if (!HasBit(used_roadtypes, rt)) continue; const RoadTypeInfo *rti = GetRoadTypeInfo(rt); d = maxdim(d, GetSpriteSize(rti->gui_sprites.build_x_road)); } - FOR_ALL_SORTED_ROADTYPES(rt) { + for (const auto &rt : _sorted_roadtypes) { if (!HasBit(used_roadtypes, rt)) continue; const RoadTypeInfo *rti = GetRoadTypeInfo(rt); From 20ac0b41484ebcaef3f8bdc9d4410903fd499386 Mon Sep 17 00:00:00 2001 From: Peter Nelson Date: Wed, 28 Apr 2021 21:50:46 +0100 Subject: [PATCH 196/268] Cleanup: Replace FOR_ALL_SORTED_CARGOSPECS macro with range iterator. --- src/cargotype.cpp | 2 +- src/cargotype.h | 7 ------- src/strings.cpp | 3 +-- src/vehicle_gui.cpp | 3 +-- 4 files changed, 3 insertions(+), 12 deletions(-) diff --git a/src/cargotype.cpp b/src/cargotype.cpp index e30b9899f1..81818d5f9d 100644 --- a/src/cargotype.cpp +++ b/src/cargotype.cpp @@ -181,7 +181,7 @@ void InitializeSortedCargoSpecs() _standard_cargo_mask = 0; _sorted_standard_cargo_specs_size = 0; - FOR_ALL_SORTED_CARGOSPECS(cargo) { + for (const auto &cargo : _sorted_cargo_specs) { if (cargo->classes & CC_SPECIAL) break; _sorted_standard_cargo_specs_size++; SetBit(_standard_cargo_mask, cargo->Index()); diff --git a/src/cargotype.h b/src/cargotype.h index f6b3eaead5..afc501a2f2 100644 --- a/src/cargotype.h +++ b/src/cargotype.h @@ -156,13 +156,6 @@ static inline bool IsCargoInClass(CargoID c, CargoClass cc) #define FOR_EACH_SET_CARGO_ID(var, cargo_bits) FOR_EACH_SET_BIT_EX(CargoID, var, CargoTypes, cargo_bits) -/** - * Loop header for iterating over cargoes, sorted by name. This includes phony cargoes like regearing cargoes. - * @param var Reference getting the cargospec. - * @see CargoSpec - */ -#define FOR_ALL_SORTED_CARGOSPECS(var) for (uint8 index = 0; index < _sorted_cargo_specs.size() && (var = _sorted_cargo_specs[index], true) ; index++) - /** * Loop header for iterating over 'real' cargoes, sorted by name. Phony cargoes like regearing cargoes are skipped. * @param var Reference getting the cargospec. diff --git a/src/strings.cpp b/src/strings.cpp index d533db1fce..e340a2b1c4 100644 --- a/src/strings.cpp +++ b/src/strings.cpp @@ -1171,8 +1171,7 @@ static char *FormatString(char *buff, const char *str_arg, StringParameters *arg CargoTypes cmask = args->GetInt64(SCC_CARGO_LIST); bool first = true; - const CargoSpec *cs; - FOR_ALL_SORTED_CARGOSPECS(cs) { + for (const auto &cs : _sorted_cargo_specs) { if (!HasBit(cmask, cs->Index())) continue; if (buff >= last - 2) break; // ',' and ' ' diff --git a/src/vehicle_gui.cpp b/src/vehicle_gui.cpp index f4b5185865..c6f2ee52a7 100644 --- a/src/vehicle_gui.cpp +++ b/src/vehicle_gui.cpp @@ -513,8 +513,7 @@ struct RefitWindow : public Window { /* Loop through all cargoes in the refit mask */ int current_index = 0; - const CargoSpec *cs; - FOR_ALL_SORTED_CARGOSPECS(cs) { + for (const auto &cs : _sorted_cargo_specs) { CargoID cid = cs->Index(); /* Skip cargo type if it's not listed */ if (!HasBit(cmask, cid)) { From 356bbbb90aaa3ac981b69693ce40d3afdaa6ea33 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Guilloux?= Date: Thu, 29 Apr 2021 14:26:08 +0200 Subject: [PATCH 197/268] Fix: [MinGW] Set minimum OS version to Windows XP (#9135) --- src/network/network_chat_gui.cpp | 6 +++--- src/saveload/saveload.cpp | 2 +- src/script/api/script_date.cpp | 3 ++- src/script/squirrel.cpp | 5 +++-- src/stdafx.h | 15 +++++++++------ 5 files changed, 18 insertions(+), 13 deletions(-) diff --git a/src/network/network_chat_gui.cpp b/src/network/network_chat_gui.cpp index 862a0a4cbf..14edc15032 100644 --- a/src/network/network_chat_gui.cpp +++ b/src/network/network_chat_gui.cpp @@ -7,9 +7,6 @@ /** @file network_chat_gui.cpp GUI for handling chat messages. */ -#include /* va_list */ -#include - #include "../stdafx.h" #include "../strings_func.h" #include "../blitter/factory.hpp" @@ -28,6 +25,9 @@ #include "table/strings.h" +#include /* va_list */ +#include + #include "../safeguards.h" /** The draw buffer must be able to contain the chat message, client name and the "[All]" message, diff --git a/src/saveload/saveload.cpp b/src/saveload/saveload.cpp index dcfcc9f431..f5e823a0df 100644 --- a/src/saveload/saveload.cpp +++ b/src/saveload/saveload.cpp @@ -19,7 +19,6 @@ *

  • repeat this until everything is done, and flush any remaining output to file * */ -#include #include "../stdafx.h" #include "../debug.h" @@ -44,6 +43,7 @@ #include "../fios.h" #include "../error.h" #include +#include #include #ifdef __EMSCRIPTEN__ # include diff --git a/src/script/api/script_date.cpp b/src/script/api/script_date.cpp index 9946a26ec6..ae3dff78c6 100644 --- a/src/script/api/script_date.cpp +++ b/src/script/api/script_date.cpp @@ -7,11 +7,12 @@ /** @file script_date.cpp Implementation of ScriptDate. */ -#include #include "../../stdafx.h" #include "script_date.hpp" #include "../../date_func.h" +#include + #include "../../safeguards.h" /* static */ bool ScriptDate::IsValidDate(Date date) diff --git a/src/script/squirrel.cpp b/src/script/squirrel.cpp index b9d614e8d0..77f84a6416 100644 --- a/src/script/squirrel.cpp +++ b/src/script/squirrel.cpp @@ -7,8 +7,6 @@ /** @file squirrel.cpp the implementation of the Squirrel class. It handles all Squirrel-stuff and gives a nice API back to work with. */ -#include -#include #include "../stdafx.h" #include "../debug.h" #include "squirrel_std.hpp" @@ -21,6 +19,9 @@ #include <../squirrel/sqvm.h> #include "../core/alloc_func.hpp" +#include +#include + /** * In the memory allocator for Squirrel we want to directly use malloc/realloc, so when the OS * does not have enough memory the game does not go into unrecoverable error mode and kill the diff --git a/src/stdafx.h b/src/stdafx.h index 19009f5ff5..819eb0e614 100644 --- a/src/stdafx.h +++ b/src/stdafx.h @@ -10,6 +10,15 @@ #ifndef STDAFX_H #define STDAFX_H +#if defined(_WIN32) + /* MinGW defaults to Windows 7 if none of these are set, and they must be set before any MinGW header is included */ +# define NTDDI_VERSION NTDDI_WINXP // Windows XP +# define _WIN32_WINNT 0x501 // Windows XP +# define _WIN32_WINDOWS 0x501 // Windows XP +# define WINVER 0x0501 // Windows XP +# define _WIN32_IE_ 0x0600 // 6.0 (XP+) +#endif + #ifdef _MSC_VER /* Stop Microsoft (and clang-cl) compilers from complaining about potentially-unsafe/potentially-non-standard functions */ # define _CRT_SECURE_NO_DEPRECATE @@ -164,12 +173,6 @@ /* Stuff for MSVC */ #if defined(_MSC_VER) # pragma once -# define NTDDI_VERSION NTDDI_WINXP // Windows XP -# define _WIN32_WINNT 0x501 // Windows XP -# define _WIN32_WINDOWS 0x501 // Windows XP -# define WINVER 0x0501 // Windows XP -# define _WIN32_IE_ 0x0600 // 6.0 (XP+) - # define NOMINMAX // Disable min/max macros in windows.h. # pragma warning(disable: 4244) // 'conversion' conversion from 'type1' to 'type2', possible loss of data From 72a05921b06b02b04af6e86d981f55de4960ca61 Mon Sep 17 00:00:00 2001 From: translators Date: Thu, 29 Apr 2021 17:52:22 +0000 Subject: [PATCH 198/268] Update: Translations from eints norwegian (bokmal): 16 changes by Anolitt finnish: 2 changes by hpiirai --- src/lang/finnish.txt | 4 ++-- src/lang/norwegian_bokmal.txt | 16 ++++++++++++++++ 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/src/lang/finnish.txt b/src/lang/finnish.txt index 4b5765492b..4435cca260 100644 --- a/src/lang/finnish.txt +++ b/src/lang/finnish.txt @@ -4463,7 +4463,7 @@ STR_ERROR_CAN_T_BUILD_CARGO_TRAM_STATION :{WHITE}Ei voi r STR_ERROR_CAN_T_BUILD_DOCK_HERE :{WHITE}Satamaa ei voi rakentaa tähän... STR_ERROR_CAN_T_BUILD_AIRPORT_HERE :{WHITE}Lentokenttää ei voi rakentaa... -STR_ERROR_ADJOINS_MORE_THAN_ONE_EXISTING :{WHITE}Liitä yhteen useampi asema/lastausalue. +STR_ERROR_ADJOINS_MORE_THAN_ONE_EXISTING :{WHITE}Vieressä on useampi kuin yksi olemassaoleva asema tai kuormausalue. STR_ERROR_STATION_TOO_SPREAD_OUT :{WHITE}... asema liian levittäytynyt STR_ERROR_TOO_MANY_STATIONS_LOADING :{WHITE}Liian monta asemaa ja lastausaluetta. STR_ERROR_TOO_MANY_STATION_SPECS :{WHITE}Rautatieasema on jakautunut liian moneen osaan @@ -4496,7 +4496,7 @@ STR_ERROR_MUST_DEMOLISH_DOCK_FIRST :{WHITE}Satama p STR_ERROR_MUST_DEMOLISH_AIRPORT_FIRST :{WHITE}Lentokenttä pitää tuhota ensin. # Waypoint related errors -STR_ERROR_WAYPOINT_ADJOINS_MORE_THAN_ONE_EXISTING :{WHITE}Liittää useamman kuin yhden reittipisteen +STR_ERROR_WAYPOINT_ADJOINS_MORE_THAN_ONE_EXISTING :{WHITE}Vieressä on useampi kuin yksi olemassaoleva reittipiste. STR_ERROR_TOO_CLOSE_TO_ANOTHER_WAYPOINT :{WHITE}Liian lähellä toista reittipistettä STR_ERROR_CAN_T_BUILD_TRAIN_WAYPOINT :{WHITE}Junien reittipistettä ei voi rakentaa tähän... diff --git a/src/lang/norwegian_bokmal.txt b/src/lang/norwegian_bokmal.txt index 623c346ac1..b79089f237 100644 --- a/src/lang/norwegian_bokmal.txt +++ b/src/lang/norwegian_bokmal.txt @@ -1994,6 +1994,7 @@ STR_FACE_TIE :Slips: STR_FACE_EARRING :Ørering: STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}Endre slips eller ørering +STR_NETWORK_SERVER_VISIBILITY_PUBLIC :Offentlig # Network server list STR_NETWORK_SERVER_LIST_CAPTION :{WHITE}Flerspiller @@ -2057,6 +2058,7 @@ STR_NETWORK_START_SERVER_NEW_GAME_NAME_TOOLTIP :{BLACK}Spillnav STR_NETWORK_START_SERVER_SET_PASSWORD :{BLACK}Sett passord STR_NETWORK_START_SERVER_PASSWORD_TOOLTIP :{BLACK}Beskytt ditt spill med et passord hvis du ikke vil at hvem som helst skal bli med på det +STR_NETWORK_START_SERVER_VISIBILITY_LABEL :{BLACK}Synlighet STR_NETWORK_START_SERVER_CLIENTS_SELECT :{BLACK}{NUM} klient{P "" er} STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS :{BLACK}Maks antall klienter: STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS.small :dra og slipp @@ -2124,8 +2126,21 @@ STR_NETWORK_NEED_COMPANY_PASSWORD_CAPTION :{WHITE}Firmaet STR_NETWORK_COMPANY_LIST_CLIENT_LIST :Liste over klienter # Network client list +STR_NETWORK_CLIENT_LIST_SERVER_NAME :{BLACK}Navn +STR_NETWORK_CLIENT_LIST_SERVER_VISIBILITY_TOOLTIP :{BLACK}Om andre mennesker kan se serveren din i den offentlige oppføringen +STR_NETWORK_CLIENT_LIST_PLAYER :{BLACK}Spiller +STR_NETWORK_CLIENT_LIST_PLAYER_NAME :{BLACK}Navn +STR_NETWORK_CLIENT_LIST_PLAYER_NAME_EDIT_TOOLTIP :{BLACK}Rediger ditt spillernavn +STR_NETWORK_CLIENT_LIST_PLAYER_NAME_QUERY_CAPTION :Ditt spillernavn +STR_NETWORK_CLIENT_LIST_ADMIN_CLIENT_TOOLTIP :{BLACK}Administrative handlinger å utføre for denne klienten +STR_NETWORK_CLIENT_LIST_JOIN_TOOLTIP :{BLACK}Bli med i dette firmaet +STR_NETWORK_CLIENT_LIST_CHAT_COMPANY_TOOLTIP :{BLACK}Send en melding til alle spillerne i dette firmaet +STR_NETWORK_CLIENT_LIST_CHAT_SPECTATOR_TOOLTIP :{BLACK}Send en melding til alle tilskuerne +STR_NETWORK_CLIENT_LIST_NEW_COMPANY_TOOLTIP :{BLACK}Opprett et nytt firma og bli med i det +STR_NETWORK_CLIENT_LIST_ADMIN_CLIENT_KICK :Spark +STR_NETWORK_CLIENT_LIST_ASK_CLIENT_BAN :{YELLOW}Er du sikker på at du vil utestenge spiller '{STRING}'? STR_NETWORK_SERVER :Tjener STR_NETWORK_CLIENT :Klient @@ -3046,6 +3061,7 @@ STR_NEWGRF_ERROR_MSG_WARNING :{RED}Advarsel: STR_NEWGRF_ERROR_MSG_ERROR :{RED}Feil: {SILVER}{STRING} STR_NEWGRF_ERROR_MSG_FATAL :{RED}Fatal: {SILVER}{STRING} STR_NEWGRF_ERROR_FATAL_POPUP :{WHITE}En fatal NewGRF-feil har oppstått:{}{STRING} +STR_NEWGRF_ERROR_POPUP :{WHITE}En NewGRF feil har oppstått:{}{STRING} STR_NEWGRF_ERROR_VERSION_NUMBER :{1:STRING} virker ikke med TTDPatch-versjonen som er rapportert av OpenTTD STR_NEWGRF_ERROR_DOS_OR_WINDOWS :{1:STRING} er for versjon {STRING} av TTD STR_NEWGRF_ERROR_UNSET_SWITCH :{1:STRING} er laget for bruk med {STRING} From 0b460bf4a17e12ba479e0822b8f9b7de29ac5816 Mon Sep 17 00:00:00 2001 From: PeterN Date: Thu, 29 Apr 2021 18:58:26 +0100 Subject: [PATCH 199/268] Fix: 'Cache' top and bottom lines of textfile viewer to avoid overdraw. (#9131) * Fix: 'Cache' top and bottom lines of textfile viewer to avoid overdraw. The text file viewer calculated the number of lines required to set the scrollbar, but did not retain this information, so this was recalculated on every draw operation. This includes overdrawing text outside the bounds of the current scroll position. With this change the top and bottom lines for each line of text are remembered, and reflowing is avoided where possible. Text outside the current scroll bounds is not drawn. Additionally the scroll interval is now based on text lines instead of pixel lines, which increases the text capacity depending on the font size. * Fix: Limit text viewer to showing 64k lines. Text files with more than 64k wrapped lines would exceed the scrollbar capacity and cause an assert. This is harder to reach now that the scrollbar counts lines instead of pixels. --- src/textfile_gui.cpp | 81 ++++++++++++++++++++++++++++++-------------- src/textfile_gui.h | 16 +++++++-- 2 files changed, 70 insertions(+), 27 deletions(-) diff --git a/src/textfile_gui.cpp b/src/textfile_gui.cpp index 075898fd71..ac8da5c2e9 100644 --- a/src/textfile_gui.cpp +++ b/src/textfile_gui.cpp @@ -67,7 +67,6 @@ TextfileWindow::TextfileWindow(TextfileType file_type) : Window(&_textfile_desc) this->GetWidget(WID_TF_CAPTION)->SetDataTip(STR_TEXTFILE_README_CAPTION + file_type, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS); this->hscroll->SetStepSize(10); // Speed up horizontal scrollbar - this->vscroll->SetStepSize(FONT_HEIGHT_MONO); } /* virtual */ TextfileWindow::~TextfileWindow() @@ -79,23 +78,38 @@ TextfileWindow::TextfileWindow(TextfileType file_type) : Window(&_textfile_desc) * Get the total height of the content displayed in this window, if wrapping is disabled. * @return the height in pixels */ -uint TextfileWindow::GetContentHeight() +uint TextfileWindow::ReflowContent() { - int max_width = this->GetWidget(WID_TF_BACKGROUND)->current_x - WD_FRAMETEXT_LEFT - WD_FRAMERECT_RIGHT; - uint height = 0; - for (uint i = 0; i < this->lines.size(); i++) { - height += GetStringHeight(this->lines[i], max_width, FS_MONO); + if (!IsWidgetLowered(WID_TF_WRAPTEXT)) { + for (auto &line : this->lines) { + line.top = height; + height++; + line.bottom = height; + } + } else { + int max_width = this->GetWidget(WID_TF_BACKGROUND)->current_x - WD_FRAMETEXT_LEFT - WD_FRAMERECT_RIGHT; + for (auto &line : this->lines) { + line.top = height; + height += GetStringHeight(line.text, max_width, FS_MONO) / FONT_HEIGHT_MONO; + line.bottom = height; + } } return height; } +uint TextfileWindow::GetContentHeight() +{ + if (this->lines.size() == 0) return 0; + return this->lines.back().bottom; +} + /* virtual */ void TextfileWindow::UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize) { switch (widget) { case WID_TF_BACKGROUND: - resize->height = 1; + resize->height = FONT_HEIGHT_MONO; size->height = 4 * resize->height + TOP_SPACING + BOTTOM_SPACING; // At least 4 lines are visible. size->width = std::max(200u, size->width); // At least 200 pixels wide. @@ -104,18 +118,17 @@ uint TextfileWindow::GetContentHeight() } /** Set scrollbars to the right lengths. */ -void TextfileWindow::SetupScrollbars() +void TextfileWindow::SetupScrollbars(bool force_reflow) { if (IsWidgetLowered(WID_TF_WRAPTEXT)) { - this->vscroll->SetCount(this->GetContentHeight()); + /* Reflow is mandatory if text wrapping is on */ + uint height = this->ReflowContent(); + this->vscroll->SetCount(std::min(UINT16_MAX, height)); this->hscroll->SetCount(0); } else { - uint max_length = 0; - for (uint i = 0; i < this->lines.size(); i++) { - max_length = std::max(max_length, GetStringBoundingBox(this->lines[i], FS_MONO).width); - } - this->vscroll->SetCount((uint)this->lines.size() * FONT_HEIGHT_MONO); - this->hscroll->SetCount(max_length + WD_FRAMETEXT_LEFT + WD_FRAMETEXT_RIGHT); + uint height = force_reflow ? this->ReflowContent() : this->GetContentHeight(); + this->vscroll->SetCount(std::min(UINT16_MAX, height)); + this->hscroll->SetCount(this->max_length + WD_FRAMETEXT_LEFT + WD_FRAMETEXT_RIGHT); } this->SetWidgetDisabledState(WID_TF_HSCROLLBAR, IsWidgetLowered(WID_TF_WRAPTEXT)); @@ -126,7 +139,6 @@ void TextfileWindow::SetupScrollbars() switch (widget) { case WID_TF_WRAPTEXT: this->ToggleWidgetLoweredState(WID_TF_WRAPTEXT); - this->SetupScrollbars(); this->InvalidateData(); break; } @@ -148,14 +160,18 @@ void TextfileWindow::SetupScrollbars() /* Draw content (now coordinates given to DrawString* are local to the new clipping region). */ int line_height = FONT_HEIGHT_MONO; - int y_offset = -this->vscroll->GetPosition(); + int pos = this->vscroll->GetPosition(); + int cap = this->vscroll->GetCapacity(); - for (uint i = 0; i < this->lines.size(); i++) { + for (auto &line : this->lines) { + if (line.bottom < pos) continue; + if (line.top > pos + cap) break; + + int y_offset = (line.top - pos) * line_height; if (IsWidgetLowered(WID_TF_WRAPTEXT)) { - y_offset = DrawStringMultiLine(0, right - x, y_offset, bottom - y, this->lines[i], TC_WHITE, SA_TOP | SA_LEFT, false, FS_MONO); + DrawStringMultiLine(0, right - x, y_offset, bottom - y, line.text, TC_WHITE, SA_TOP | SA_LEFT, false, FS_MONO); } else { - DrawString(-this->hscroll->GetPosition(), right - x, y_offset, this->lines[i], TC_WHITE, SA_TOP | SA_LEFT, false, FS_MONO); - y_offset += line_height; // margin to previous element + DrawString(-this->hscroll->GetPosition(), right - x, y_offset, line.text, TC_WHITE, SA_TOP | SA_LEFT, false, FS_MONO); } } @@ -167,7 +183,14 @@ void TextfileWindow::SetupScrollbars() this->vscroll->SetCapacityFromWidget(this, WID_TF_BACKGROUND, TOP_SPACING + BOTTOM_SPACING); this->hscroll->SetCapacityFromWidget(this, WID_TF_BACKGROUND); - this->SetupScrollbars(); + this->SetupScrollbars(false); +} + +/* virtual */ void TextfileWindow::OnInvalidateData(int data, bool gui_scope) +{ + if (!gui_scope) return; + + this->SetupScrollbars(true); } /* virtual */ void TextfileWindow::Reset() @@ -184,7 +207,7 @@ void TextfileWindow::SetupScrollbars() { if (this->search_iterator >= this->lines.size()) return nullptr; - return this->lines[this->search_iterator++]; + return this->lines[this->search_iterator++].text; } /* virtual */ bool TextfileWindow::Monospace() @@ -364,14 +387,22 @@ static void Xunzip(byte **bufp, size_t *sizep) str_validate(p, this->text + filesize, SVS_REPLACE_WITH_QUESTION_MARK | SVS_ALLOW_NEWLINE); /* Split the string on newlines. */ - this->lines.push_back(p); + int row = 0; + this->lines.emplace_back(row, p); for (; *p != '\0'; p++) { if (*p == '\n') { *p = '\0'; - this->lines.push_back(p + 1); + this->lines.emplace_back(++row, p + 1); } } + /* Calculate maximum text line length. */ + uint max_length = 0; + for (auto &line : this->lines) { + max_length = std::max(max_length, GetStringBoundingBox(line.text, FS_MONO).width); + } + this->max_length = max_length; + CheckForMissingGlyphs(true, this); } diff --git a/src/textfile_gui.h b/src/textfile_gui.h index d67435c015..5f1db14ca9 100644 --- a/src/textfile_gui.h +++ b/src/textfile_gui.h @@ -19,13 +19,23 @@ const char *GetTextfile(TextfileType type, Subdirectory dir, const char *filenam /** Window for displaying a textfile */ struct TextfileWindow : public Window, MissingGlyphSearcher { + struct Line { + int top; ///< Top scroll position. + int bottom; ///< Bottom scroll position. + const char *text; ///< Pointer to text buffer. + + Line(int top, const char *text) : top(top), bottom(top + 1), text(text) {} + }; + TextfileType file_type; ///< Type of textfile to view. Scrollbar *vscroll; ///< Vertical scrollbar. Scrollbar *hscroll; ///< Horizontal scrollbar. char *text; ///< Lines of text from the NewGRF's textfile. - std::vector lines; ///< #text, split into lines in a table with lines. + std::vector lines; ///< #text, split into lines in a table with lines. uint search_iterator; ///< Iterator for the font check search. + uint max_length; ///< Maximum length of unwrapped text line. + static const int TOP_SPACING = WD_FRAMETEXT_TOP; ///< Additional spacing at the top of the #WID_TF_BACKGROUND widget. static const int BOTTOM_SPACING = WD_FRAMETEXT_BOTTOM; ///< Additional spacing at the bottom of the #WID_TF_BACKGROUND widget. @@ -36,6 +46,7 @@ struct TextfileWindow : public Window, MissingGlyphSearcher { void OnClick(Point pt, int widget, int click_count) override; void DrawWidget(const Rect &r, int widget) const override; void OnResize() override; + void OnInvalidateData(int data = 0, bool gui_scope = true) override; void Reset() override; FontSize DefaultSize() override; @@ -46,8 +57,9 @@ struct TextfileWindow : public Window, MissingGlyphSearcher { virtual void LoadTextfile(const char *textfile, Subdirectory dir); private: + uint ReflowContent(); uint GetContentHeight(); - void SetupScrollbars(); + void SetupScrollbars(bool force_reflow); }; #endif /* TEXTFILE_GUI_H */ From a8afbe74bfdbe6aa9ceb88c8e70c63bfe37aeda0 Mon Sep 17 00:00:00 2001 From: Patric Stout Date: Thu, 29 Apr 2021 12:01:54 +0200 Subject: [PATCH 200/268] Cleanup: remove write-only variable "hostname" in NetworkGameList --- src/network/core/game_info.h | 1 - src/network/network_gamelist.cpp | 1 - src/network/network_udp.cpp | 5 ----- 3 files changed, 7 deletions(-) diff --git a/src/network/core/game_info.h b/src/network/core/game_info.h index 28aea75073..324c6ea4aa 100644 --- a/src/network/core/game_info.h +++ b/src/network/core/game_info.h @@ -74,7 +74,6 @@ struct NetworkGameInfo : NetworkServerGameInfo { uint16 map_width; ///< Map width uint16 map_height; ///< Map height char server_name[NETWORK_NAME_LENGTH]; ///< Server name - char hostname[NETWORK_HOSTNAME_LENGTH]; ///< Hostname of the server (if any) char server_revision[NETWORK_REVISION_LENGTH]; ///< The version number the server is using (e.g.: 'r304' or 0.5.0) bool dedicated; ///< Is this a dedicated server? bool version_compatible; ///< Can we connect to this server or not? (based on server_revision) diff --git a/src/network/network_gamelist.cpp b/src/network/network_gamelist.cpp index dfe07bdbbb..3ee5099b73 100644 --- a/src/network/network_gamelist.cpp +++ b/src/network/network_gamelist.cpp @@ -51,7 +51,6 @@ static void NetworkGameListHandleDelayedInsert() ClearGRFConfigList(&item->info.grfconfig); memset(&item->info, 0, sizeof(item->info)); strecpy(item->info.server_name, ins_item->info.server_name, lastof(item->info.server_name)); - strecpy(item->info.hostname, ins_item->info.hostname, lastof(item->info.hostname)); item->online = false; } item->manually |= ins_item->manually; diff --git a/src/network/network_udp.cpp b/src/network/network_udp.cpp index d62998794e..bb71e5ceaa 100644 --- a/src/network/network_udp.cpp +++ b/src/network/network_udp.cpp @@ -92,7 +92,6 @@ static void DoNetworkUDPQueryServer(NetworkAddress &address, bool needs_mutex, b /* Clear item in gamelist */ NetworkGameList *item = CallocT(1); address.GetAddressAsString(item->info.server_name, lastof(item->info.server_name)); - strecpy(item->info.hostname, address.GetHostname(), lastof(item->info.hostname)); item->address = address; item->manually = manually; NetworkGameListAddItemDelayed(item); @@ -365,10 +364,6 @@ void ClientNetworkUDPSocketHandler::Receive_SERVER_RESPONSE(Packet *p, NetworkAd } } - if (item->info.hostname[0] == '\0') { - seprintf(item->info.hostname, lastof(item->info.hostname), "%s", client_addr->GetHostname()); - } - if (client_addr->GetAddress()->ss_family == AF_INET6) { strecat(item->info.server_name, " (IPv6)", lastof(item->info.server_name)); } From 99f998805ba835f48a8061762fa19761760c7451 Mon Sep 17 00:00:00 2001 From: Patric Stout Date: Thu, 29 Apr 2021 12:05:43 +0200 Subject: [PATCH 201/268] Codechange: use std::string over stack-based strings if possible --- src/network/core/address.cpp | 32 +++++++++++++++----------------- src/network/network_gui.cpp | 5 ++--- 2 files changed, 17 insertions(+), 20 deletions(-) diff --git a/src/network/core/address.cpp b/src/network/core/address.cpp index e53566c0bb..8b51e2c082 100644 --- a/src/network/core/address.cpp +++ b/src/network/core/address.cpp @@ -313,8 +313,7 @@ static SOCKET ConnectLoopProc(addrinfo *runp) { const char *type = NetworkAddress::SocketTypeAsString(runp->ai_socktype); const char *family = NetworkAddress::AddressFamilyAsString(runp->ai_family); - char address[NETWORK_HOSTNAME_LENGTH + 6 + 7]; - NetworkAddress(runp->ai_addr, (int)runp->ai_addrlen).GetAddressAsString(address, lastof(address)); + std::string address = NetworkAddress(runp->ai_addr, (int)runp->ai_addrlen).GetAddressAsString(); SOCKET sock = socket(runp->ai_family, runp->ai_socktype, runp->ai_protocol); if (sock == INVALID_SOCKET) { @@ -328,7 +327,7 @@ static SOCKET ConnectLoopProc(addrinfo *runp) int err = connect(sock, runp->ai_addr, (int)runp->ai_addrlen); if (err != 0 && NetworkGetLastError() != EINPROGRESS) { - DEBUG(net, 1, "[%s] could not connect to %s over %s: %s", type, address, family, NetworkGetLastErrorString()); + DEBUG(net, 1, "[%s] could not connect to %s over %s: %s", type, address.c_str(), family, NetworkGetLastErrorString()); closesocket(sock); return INVALID_SOCKET; } @@ -344,14 +343,14 @@ static SOCKET ConnectLoopProc(addrinfo *runp) tv.tv_sec = DEFAULT_CONNECT_TIMEOUT_SECONDS; int n = select(FD_SETSIZE, NULL, &write_fd, NULL, &tv); if (n < 0) { - DEBUG(net, 1, "[%s] could not connect to %s: %s", type, address, NetworkGetLastErrorString()); + DEBUG(net, 1, "[%s] could not connect to %s: %s", type, address.c_str(), NetworkGetLastErrorString()); closesocket(sock); return INVALID_SOCKET; } /* If no fd is selected, the timeout has been reached. */ if (n == 0) { - DEBUG(net, 1, "[%s] timed out while connecting to %s", type, address); + DEBUG(net, 1, "[%s] timed out while connecting to %s", type, address.c_str()); closesocket(sock); return INVALID_SOCKET; } @@ -359,13 +358,13 @@ static SOCKET ConnectLoopProc(addrinfo *runp) /* Retrieve last error, if any, on the socket. */ err = GetSocketError(sock); if (err != 0) { - DEBUG(net, 1, "[%s] could not connect to %s: %s", type, address, NetworkGetErrorString(err)); + DEBUG(net, 1, "[%s] could not connect to %s: %s", type, address.c_str(), NetworkGetErrorString(err)); closesocket(sock); return INVALID_SOCKET; } /* Connection succeeded. */ - DEBUG(net, 1, "[%s] connected to %s", type, address); + DEBUG(net, 1, "[%s] connected to %s", type, address.c_str()); return sock; } @@ -390,48 +389,47 @@ static SOCKET ListenLoopProc(addrinfo *runp) { const char *type = NetworkAddress::SocketTypeAsString(runp->ai_socktype); const char *family = NetworkAddress::AddressFamilyAsString(runp->ai_family); - char address[NETWORK_HOSTNAME_LENGTH + 6 + 7]; - NetworkAddress(runp->ai_addr, (int)runp->ai_addrlen).GetAddressAsString(address, lastof(address)); + std::string address = NetworkAddress(runp->ai_addr, (int)runp->ai_addrlen).GetAddressAsString(); SOCKET sock = socket(runp->ai_family, runp->ai_socktype, runp->ai_protocol); if (sock == INVALID_SOCKET) { - DEBUG(net, 0, "[%s] could not create %s socket on port %s: %s", type, family, address, NetworkGetLastErrorString()); + DEBUG(net, 0, "[%s] could not create %s socket on port %s: %s", type, family, address.c_str(), NetworkGetLastErrorString()); return INVALID_SOCKET; } if (runp->ai_socktype == SOCK_STREAM && !SetNoDelay(sock)) { - DEBUG(net, 3, "[%s] setting TCP_NODELAY failed for port %s", type, address); + DEBUG(net, 3, "[%s] setting TCP_NODELAY failed for port %s", type, address.c_str()); } int on = 1; /* The (const char*) cast is needed for windows!! */ if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (const char*)&on, sizeof(on)) == -1) { - DEBUG(net, 3, "[%s] could not set reusable %s sockets for port %s: %s", type, family, address, NetworkGetLastErrorString()); + DEBUG(net, 3, "[%s] could not set reusable %s sockets for port %s: %s", type, family, address.c_str(), NetworkGetLastErrorString()); } #ifndef __OS2__ if (runp->ai_family == AF_INET6 && setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, (const char*)&on, sizeof(on)) == -1) { - DEBUG(net, 3, "[%s] could not disable IPv4 over IPv6 on port %s: %s", type, address, NetworkGetLastErrorString()); + DEBUG(net, 3, "[%s] could not disable IPv4 over IPv6 on port %s: %s", type, address.c_str(), NetworkGetLastErrorString()); } #endif if (bind(sock, runp->ai_addr, (int)runp->ai_addrlen) != 0) { - DEBUG(net, 1, "[%s] could not bind on %s port %s: %s", type, family, address, NetworkGetLastErrorString()); + DEBUG(net, 1, "[%s] could not bind on %s port %s: %s", type, family, address.c_str(), NetworkGetLastErrorString()); closesocket(sock); return INVALID_SOCKET; } if (runp->ai_socktype != SOCK_DGRAM && listen(sock, 1) != 0) { - DEBUG(net, 1, "[%s] could not listen at %s port %s: %s", type, family, address, NetworkGetLastErrorString()); + DEBUG(net, 1, "[%s] could not listen at %s port %s: %s", type, family, address.c_str(), NetworkGetLastErrorString()); closesocket(sock); return INVALID_SOCKET; } /* Connection succeeded */ - if (!SetNonBlocking(sock)) DEBUG(net, 0, "[%s] setting non-blocking mode failed for %s port %s", type, family, address); + if (!SetNonBlocking(sock)) DEBUG(net, 0, "[%s] setting non-blocking mode failed for %s port %s", type, family, address.c_str()); - DEBUG(net, 1, "[%s] listening on %s port %s", type, family, address); + DEBUG(net, 1, "[%s] listening on %s port %s", type, family, address.c_str()); return sock; } diff --git a/src/network/network_gui.cpp b/src/network/network_gui.cpp index 57127aee81..e1f9a791ba 100644 --- a/src/network/network_gui.cpp +++ b/src/network/network_gui.cpp @@ -645,9 +645,8 @@ public: DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, STR_NETWORK_SERVER_LIST_SERVER_VERSION); // server version y += FONT_HEIGHT_NORMAL; - char network_addr_buffer[NETWORK_HOSTNAME_LENGTH + 6 + 7]; - sel->address.GetAddressAsString(network_addr_buffer, lastof(network_addr_buffer)); - SetDParamStr(0, network_addr_buffer); + std::string address = sel->address.GetAddressAsString(); + SetDParamStr(0, address.c_str()); DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, STR_NETWORK_SERVER_LIST_SERVER_ADDRESS); // server address y += FONT_HEIGHT_NORMAL; From be37a2cab831cb645ef0f51dbcc944bd750f6926 Mon Sep 17 00:00:00 2001 From: Patric Stout Date: Thu, 29 Apr 2021 12:09:03 +0200 Subject: [PATCH 202/268] Codechange: use NetworkAddress instead of two host/port variables where possible This also means we no longer need last_host/last_port, but can just use a single last_joined setting. --- src/console_cmds.cpp | 26 ++----- src/network/core/address.cpp | 4 +- src/network/core/config.h | 1 + src/network/core/tcp_http.cpp | 6 +- src/network/network.cpp | 125 ++++++++++++++++--------------- src/network/network_func.h | 10 ++- src/network/network_gamelist.cpp | 2 +- src/network/network_gui.cpp | 23 +++--- src/openttd.cpp | 40 ++++------ src/settings_type.h | 5 +- src/table/settings.ini | 11 +-- 11 files changed, 112 insertions(+), 141 deletions(-) diff --git a/src/console_cmds.cpp b/src/console_cmds.cpp index 8cdf9c664e..7ab6d3179e 100644 --- a/src/console_cmds.cpp +++ b/src/console_cmds.cpp @@ -893,15 +893,16 @@ DEF_CONSOLE_CMD(ConNetworkReconnect) break; } - if (StrEmpty(_settings_client.network.last_host)) { + if (StrEmpty(_settings_client.network.last_joined)) { IConsolePrint(CC_DEFAULT, "No server for reconnecting."); return true; } /* Don't resolve the address first, just print it directly as it comes from the config file. */ - IConsolePrintF(CC_DEFAULT, "Reconnecting to %s:%d...", _settings_client.network.last_host, _settings_client.network.last_port); + IConsolePrintF(CC_DEFAULT, "Reconnecting to %s ...", _settings_client.network.last_joined); - NetworkClientConnectGame(_settings_client.network.last_host, _settings_client.network.last_port, playas); + NetworkAddress address = ParseConnectionString(_settings_client.network.last_joined, NETWORK_DEFAULT_PORT); + NetworkClientConnectGame(address, playas); return true; } @@ -917,18 +918,11 @@ DEF_CONSOLE_CMD(ConNetworkConnect) if (argc < 2) return false; if (_networking) NetworkDisconnect(); // we are in network-mode, first close it! - const char *port = nullptr; - const char *company = nullptr; - char *ip = argv[1]; - /* Default settings: default port and new company */ - uint16 rport = NETWORK_DEFAULT_PORT; CompanyID join_as = COMPANY_NEW_COMPANY; + NetworkAddress address = ParseGameConnectionString(&join_as, argv[1], NETWORK_DEFAULT_PORT); - ParseGameConnectionString(&company, &port, ip); - - IConsolePrintF(CC_DEFAULT, "Connecting to %s...", ip); - if (company != nullptr) { - join_as = (CompanyID)atoi(company); + IConsolePrintF(CC_DEFAULT, "Connecting to %s...", address.GetAddressAsString().c_str()); + if (join_as != COMPANY_NEW_COMPANY) { IConsolePrintF(CC_DEFAULT, " company-no: %d", join_as); /* From a user pov 0 is a new company, internally it's different and all @@ -938,12 +932,8 @@ DEF_CONSOLE_CMD(ConNetworkConnect) join_as--; } } - if (port != nullptr) { - rport = atoi(port); - IConsolePrintF(CC_DEFAULT, " port: %s", port); - } - NetworkClientConnectGame(ip, rport, join_as); + NetworkClientConnectGame(address, join_as); return true; } diff --git a/src/network/core/address.cpp b/src/network/core/address.cpp index 8b51e2c082..e91751c33f 100644 --- a/src/network/core/address.cpp +++ b/src/network/core/address.cpp @@ -101,8 +101,8 @@ void NetworkAddress::GetAddressAsString(char *buffer, const char *last, bool wit */ std::string NetworkAddress::GetAddressAsString(bool with_family) { - /* 6 = for the : and 5 for the decimal port number */ - char buf[NETWORK_HOSTNAME_LENGTH + 6 + 7]; + /* 7 extra are for with_family, which adds " (IPvX)". */ + char buf[NETWORK_HOSTNAME_PORT_LENGTH + 7]; this->GetAddressAsString(buf, lastof(buf), with_family); return buf; } diff --git a/src/network/core/config.h b/src/network/core/config.h index 866d1791d4..cacc907faf 100644 --- a/src/network/core/config.h +++ b/src/network/core/config.h @@ -56,6 +56,7 @@ static const byte NETWORK_MASTER_SERVER_VERSION = 2; ///< What vers static const uint NETWORK_NAME_LENGTH = 80; ///< The maximum length of the server name and map name, in bytes including '\0' static const uint NETWORK_COMPANY_NAME_LENGTH = 128; ///< The maximum length of the company name, in bytes including '\0' static const uint NETWORK_HOSTNAME_LENGTH = 80; ///< The maximum length of the host name, in bytes including '\0' +static const uint NETWORK_HOSTNAME_PORT_LENGTH = 80 + 6; ///< The maximum length of the host name + port, in bytes including '\0'. The extra six is ":" + port number (with a max of 65536) static const uint NETWORK_SERVER_ID_LENGTH = 33; ///< The maximum length of the network id of the servers, in bytes including '\0' static const uint NETWORK_REVISION_LENGTH = 33; ///< The maximum length of the revision, in bytes including '\0' static const uint NETWORK_PASSWORD_LENGTH = 33; ///< The maximum length of the password, in bytes including '\0' (must be >= NETWORK_SERVER_ID_LENGTH) diff --git a/src/network/core/tcp_http.cpp b/src/network/core/tcp_http.cpp index ee74c45070..99eca1cb74 100644 --- a/src/network/core/tcp_http.cpp +++ b/src/network/core/tcp_http.cpp @@ -203,11 +203,7 @@ int NetworkHTTPSocketHandler::HandleHeader() *url = '\0'; - /* Fetch the hostname, and possible port number. */ - const char *port = nullptr; - ParseConnectionString(&port, hname); - - NetworkAddress address(hname, port == nullptr ? 80 : atoi(port)); + NetworkAddress address = ParseConnectionString(hname, 80); /* Restore the URL. */ *url = '/'; diff --git a/src/network/network.cpp b/src/network/network.cpp index c0b2e06fd4..0be46bbf35 100644 --- a/src/network/network.cpp +++ b/src/network/network.cpp @@ -446,45 +446,15 @@ static void CheckPauseOnJoin() CheckPauseHelper(NetworkHasJoiningClient(), PM_PAUSED_JOIN); } -/** - * Converts a string to ip/port - * Format: IP:port - * - * connection_string will be re-terminated to separate out the hostname, port will - * be set to the port strings given by the user, inside the memory area originally - * occupied by connection_string. - */ -void ParseConnectionString(const char **port, char *connection_string) -{ - bool ipv6 = (strchr(connection_string, ':') != strrchr(connection_string, ':')); - for (char *p = connection_string; *p != '\0'; p++) { - switch (*p) { - case '[': - ipv6 = true; - break; - - case ']': - ipv6 = false; - break; - - case ':': - if (ipv6) break; - *port = p + 1; - *p = '\0'; - break; - } - } -} - /** * Converts a string to ip/port/company * Format: IP:port#company * - * connection_string will be re-terminated to separate out the hostname, and company and port will - * be set to the company and port strings given by the user, inside the memory area originally - * occupied by connection_string. + * connection_string will be re-terminated to separate out the hostname, port will + * be set to the port strings given by the user, inside the memory area originally + * occupied by connection_string. Similar for company, if set. */ -void ParseGameConnectionString(const char **company, const char **port, char *connection_string) +void ParseFullConnectionString(const char **company, const char **port, char *connection_string) { bool ipv6 = (strchr(connection_string, ':') != strrchr(connection_string, ':')); for (char *p = connection_string; *p != '\0'; p++) { @@ -498,6 +468,7 @@ void ParseGameConnectionString(const char **company, const char **port, char *co break; case '#': + if (company == nullptr) continue; *company = p + 1; *p = '\0'; break; @@ -511,6 +482,51 @@ void ParseGameConnectionString(const char **company, const char **port, char *co } } +/** + * Convert a string containing either "hostname" or "hostname:ip" to a + * NetworkAddress. + * + * @param connection_string The string to parse. + * @param default_port The default port to set port to if not in connection_string. + * @return A valid NetworkAddress of the parsed information. + */ +NetworkAddress ParseConnectionString(const char *connection_string, int default_port) +{ + char internal_connection_string[NETWORK_HOSTNAME_PORT_LENGTH]; + strecpy(internal_connection_string, connection_string, lastof(internal_connection_string)); + + const char *port = nullptr; + ParseFullConnectionString(nullptr, &port, internal_connection_string); + + int rport = port != nullptr ? atoi(port) : default_port; + return NetworkAddress(internal_connection_string, rport); +} + +/** + * Convert a string containing either "hostname" or "hostname:ip" to a + * NetworkAddress, where the string can be postfixed with "#company" to + * indicate the requested company. + * + * @param company Pointer to the company variable to set iff indicted. + * @param connection_string The string to parse. + * @param default_port The default port to set port to if not in connection_string. + * @return A valid NetworkAddress of the parsed information. + */ +NetworkAddress ParseGameConnectionString(CompanyID *company, const char *connection_string, int default_port) +{ + char internal_connection_string[NETWORK_HOSTNAME_PORT_LENGTH + 4]; // 4 extra for the "#" and company + strecpy(internal_connection_string, connection_string, lastof(internal_connection_string)); + + const char *port_s = nullptr; + const char *company_s = nullptr; + ParseFullConnectionString(&company_s, &port_s, internal_connection_string); + + if (company_s != nullptr) *company = (CompanyID)atoi(company_s); + + int port = port_s != nullptr ? atoi(port_s) : default_port; + return NetworkAddress(internal_connection_string, port); +} + /** * Handle the accepting of a connection to the server. * @param s The socket of the new connection. @@ -616,26 +632,17 @@ void NetworkTCPQueryServer(NetworkAddress address) new TCPQueryConnecter(address); } -/* Validates an address entered as a string and adds the server to +/** + * Validates an address entered as a string and adds the server to * the list. If you use this function, the games will be marked - * as manually added. */ -void NetworkAddServer(const char *b) + * as manually added. + * @param connection_string The IP:port to add to the list. + */ +void NetworkAddServer(const char *connection_string) { - if (*b != '\0') { - const char *port = nullptr; - char host[NETWORK_HOSTNAME_LENGTH]; - uint16 rport; + if (StrEmpty(connection_string)) return; - strecpy(host, b, lastof(host)); - - strecpy(_settings_client.network.connect_to_ip, b, lastof(_settings_client.network.connect_to_ip)); - rport = NETWORK_DEFAULT_PORT; - - ParseConnectionString(&port, host); - if (port != nullptr) rport = atoi(port); - - NetworkUDPQueryServer(NetworkAddress(host, rport), true); - } + NetworkUDPQueryServer(ParseConnectionString(connection_string, NETWORK_DEFAULT_PORT), true); } /** @@ -688,16 +695,13 @@ public: /* Used by clients, to connect to a server */ -void NetworkClientConnectGame(const char *hostname, uint16 port, CompanyID join_as, const char *join_server_password, const char *join_company_password) +void NetworkClientConnectGame(NetworkAddress &address, CompanyID join_as, const char *join_server_password, const char *join_company_password) { if (!_network_available) return; - - if (port == 0) return; - if (!NetworkValidateClientName()) return; - strecpy(_settings_client.network.last_host, hostname, lastof(_settings_client.network.last_host)); - _settings_client.network.last_port = port; + strecpy(_settings_client.network.last_joined, address.GetAddressAsString(false).c_str(), lastof(_settings_client.network.last_joined)); + _network_join_as = join_as; _network_join_server_password = join_server_password; _network_join_company_password = join_company_password; @@ -708,7 +712,7 @@ void NetworkClientConnectGame(const char *hostname, uint16 port, CompanyID join_ _network_join_status = NETWORK_JOIN_STATUS_CONNECTING; ShowJoinStatusWindow(); - new TCPClientConnecter(NetworkAddress(hostname, port)); + new TCPClientConnecter(address); } static void NetworkInitGameInfo() @@ -1059,13 +1063,12 @@ static void NetworkGenerateServerId() seprintf(_settings_client.network.network_id, lastof(_settings_client.network.network_id), "%s", hex_output); } -void NetworkStartDebugLog(const char *hostname, uint16 port) +void NetworkStartDebugLog(NetworkAddress &address) { extern SOCKET _debug_socket; // Comes from debug.c - DEBUG(net, 0, "Redirecting DEBUG() to %s:%d", hostname, port); + DEBUG(net, 0, "Redirecting DEBUG() to %s", address.GetAddressAsString().c_str()); - NetworkAddress address(hostname, port); SOCKET s = address.Connect(); if (s == INVALID_SOCKET) { DEBUG(net, 0, "Failed to open socket for redirection DEBUG()"); diff --git a/src/network/network_func.h b/src/network/network_func.h index a0f163345e..967bb62603 100644 --- a/src/network/network_func.h +++ b/src/network/network_func.h @@ -18,6 +18,7 @@ // #define DEBUG_FAILED_DUMP_COMMANDS #include "network_type.h" +#include "core/address.h" #include "../console_type.h" #include "../gfx_type.h" #include "../openttd.h" @@ -45,14 +46,15 @@ void NetworkReboot(); void NetworkDisconnect(bool blocking = false, bool close_admins = true); void NetworkGameLoop(); void NetworkBackgroundLoop(); -void ParseConnectionString(const char **port, char *connection_string); -void ParseGameConnectionString(const char **company, const char **port, char *connection_string); -void NetworkStartDebugLog(const char *hostname, uint16 port); +void ParseFullConnectionString(const char **company, const char **port, char *connection_string); +NetworkAddress ParseConnectionString(const char *connection_string, int default_port); +NetworkAddress ParseGameConnectionString(CompanyID *company, const char *connection_string, int default_port); +void NetworkStartDebugLog(NetworkAddress &address); void NetworkPopulateCompanyStats(NetworkCompanyStats *stats); void NetworkUpdateClientInfo(ClientID client_id); void NetworkClientsToSpectators(CompanyID cid); -void NetworkClientConnectGame(const char *hostname, uint16 port, CompanyID join_as, const char *join_server_password = nullptr, const char *join_company_password = nullptr); +void NetworkClientConnectGame(NetworkAddress &address, CompanyID join_as, const char *join_server_password = nullptr, const char *join_company_password = nullptr); void NetworkClientRequestMove(CompanyID company, const char *pass = ""); void NetworkClientSendRcon(const char *password, const char *command); void NetworkClientSendChat(NetworkAction action, DestType type, int dest, const char *msg, int64 data = 0); diff --git a/src/network/network_gamelist.cpp b/src/network/network_gamelist.cpp index 3ee5099b73..6d4285c854 100644 --- a/src/network/network_gamelist.cpp +++ b/src/network/network_gamelist.cpp @@ -151,7 +151,7 @@ void NetworkGameListRequery() /* item gets mostly zeroed by NetworkUDPQueryServer */ uint8 retries = item->retries; - NetworkUDPQueryServer(NetworkAddress(item->address)); + NetworkUDPQueryServer(item->address); item->retries = (retries >= REFRESH_GAMEINFO_X_REQUERIES) ? 0 : retries; } } diff --git a/src/network/network_gui.cpp b/src/network/network_gui.cpp index e1f9a791ba..3204b2b962 100644 --- a/src/network/network_gui.cpp +++ b/src/network/network_gui.cpp @@ -472,7 +472,7 @@ public: EM_ASM(if (window["openttd_server_list"]) openttd_server_list()); #endif - this->last_joined = NetworkGameListAddItem(NetworkAddress(_settings_client.network.last_host, _settings_client.network.last_port)); + this->last_joined = NetworkGameListAddItem(ParseConnectionString(_settings_client.network.last_joined, NETWORK_DEFAULT_PORT)); this->server = this->last_joined; if (this->last_joined != nullptr) NetworkUDPQueryServer(this->last_joined->address); @@ -735,7 +735,7 @@ public: ShowQueryString( STR_JUST_RAW_STRING, STR_NETWORK_SERVER_LIST_ENTER_IP, - NETWORK_HOSTNAME_LENGTH, // maximum number of characters including '\0' + NETWORK_HOSTNAME_PORT_LENGTH, // maximum number of characters including '\0' this, CS_ALPHANUMERAL, QSF_ACCEPT_UNCHANGED); break; @@ -745,8 +745,6 @@ public: case WID_NG_JOIN: // Join Game if (this->server != nullptr) { - seprintf(_settings_client.network.last_host, lastof(_settings_client.network.last_host), "%s", this->server->address.GetHostname()); - _settings_client.network.last_port = this->server->address.GetPort(); ShowNetworkLobbyWindow(this->server); } break; @@ -827,7 +825,10 @@ public: void OnQueryTextFinished(char *str) override { - if (!StrEmpty(str)) NetworkAddServer(str); + if (!StrEmpty(str)) { + strecpy(_settings_client.network.connect_to_ip, str, lastof(_settings_client.network.connect_to_ip)); + NetworkAddServer(str); + } } void OnResize() override @@ -1469,22 +1470,22 @@ struct NetworkLobbyWindow : public Window { case WID_NL_JOIN: // Join company /* Button can be clicked only when it is enabled. */ - NetworkClientConnectGame(_settings_client.network.last_host, _settings_client.network.last_port, this->company); + NetworkClientConnectGame(this->server->address, this->company); break; case WID_NL_NEW: // New company - NetworkClientConnectGame(_settings_client.network.last_host, _settings_client.network.last_port, COMPANY_NEW_COMPANY); + NetworkClientConnectGame(this->server->address, COMPANY_NEW_COMPANY); break; case WID_NL_SPECTATE: // Spectate game - NetworkClientConnectGame(_settings_client.network.last_host, _settings_client.network.last_port, COMPANY_SPECTATOR); + NetworkClientConnectGame(this->server->address, COMPANY_SPECTATOR); break; case WID_NL_REFRESH: // Refresh /* Clear the information so removed companies don't remain */ for (auto &company : this->company_info) company = {}; - NetworkTCPQueryServer(NetworkAddress(_settings_client.network.last_host, _settings_client.network.last_port)); + NetworkTCPQueryServer(this->server->address); break; } } @@ -1552,7 +1553,9 @@ static void ShowNetworkLobbyWindow(NetworkGameList *ngl) DeleteWindowById(WC_NETWORK_WINDOW, WN_NETWORK_WINDOW_START); DeleteWindowById(WC_NETWORK_WINDOW, WN_NETWORK_WINDOW_GAME); - NetworkTCPQueryServer(NetworkAddress(_settings_client.network.last_host, _settings_client.network.last_port)); + strecpy(_settings_client.network.last_joined, ngl->address.GetAddressAsString(false).c_str(), lastof(_settings_client.network.last_joined)); + + NetworkTCPQueryServer(ngl->address); new NetworkLobbyWindow(&_network_lobby_window_desc, ngl); } diff --git a/src/openttd.cpp b/src/openttd.cpp index 016d482873..b128243854 100644 --- a/src/openttd.cpp +++ b/src/openttd.cpp @@ -473,29 +473,21 @@ struct AfterNewGRFScan : NewGRFScanCallback { if (_switch_mode != SM_NONE) MakeNewgameSettingsLive(); if (_network_available && network_conn != nullptr) { - const char *port = nullptr; - const char *company = nullptr; - uint16 rport = NETWORK_DEFAULT_PORT; CompanyID join_as = COMPANY_NEW_COMPANY; + NetworkAddress address = ParseGameConnectionString(&join_as, network_conn, NETWORK_DEFAULT_PORT); - ParseGameConnectionString(&company, &port, network_conn); - - if (company != nullptr) { - join_as = (CompanyID)atoi(company); - - if (join_as != COMPANY_SPECTATOR) { - join_as--; - if (join_as >= MAX_COMPANIES) { - delete this; - return; - } + if (join_as != COMPANY_NEW_COMPANY && join_as != COMPANY_SPECTATOR) { + join_as--; + if (join_as >= MAX_COMPANIES) { + delete this; + return; } } - if (port != nullptr) rport = atoi(port); LoadIntroGame(); _switch_mode = SM_NONE; - NetworkClientConnectGame(network_conn, rport, join_as, join_server_password, join_company_password); + + NetworkClientConnectGame(address, join_as, join_server_password, join_company_password); } /* After the scan we're not used anymore. */ @@ -585,7 +577,7 @@ int openttd_main(int argc, char *argv[]) SetDebugString("net=6"); if (mgo.opt != nullptr) { const char *port = nullptr; - ParseConnectionString(&port, mgo.opt); + ParseFullConnectionString(nullptr, &port, mgo.opt); if (!StrEmpty(mgo.opt)) scanner->dedicated_host = mgo.opt; if (port != nullptr) scanner->dedicated_port = atoi(port); } @@ -771,15 +763,8 @@ int openttd_main(int argc, char *argv[]) NetworkStartUp(); // initialize network-core if (debuglog_conn != nullptr && _network_available) { - const char *port = nullptr; - uint16 rport; - - rport = NETWORK_DEFAULT_DEBUGLOG_PORT; - - ParseConnectionString(&port, debuglog_conn); - if (port != nullptr) rport = atoi(port); - - NetworkStartDebugLog(debuglog_conn, rport); + NetworkAddress address = ParseConnectionString(debuglog_conn, NETWORK_DEFAULT_DEBUGLOG_PORT); + NetworkStartDebugLog(address); } if (!HandleBootstrap()) { @@ -1491,7 +1476,8 @@ void GameLoop() if (_network_reconnect > 0 && --_network_reconnect == 0) { /* This means that we want to reconnect to the last host * We do this here, because it means that the network is really closed */ - NetworkClientConnectGame(_settings_client.network.last_host, _settings_client.network.last_port, COMPANY_SPECTATOR); + NetworkAddress address = ParseConnectionString(_settings_client.network.last_joined, NETWORK_DEFAULT_PORT); + NetworkClientConnectGame(address, COMPANY_SPECTATOR); } /* Singleplayer */ StateGameLoop(); diff --git a/src/settings_type.h b/src/settings_type.h index bb078205b8..5361fc3f1f 100644 --- a/src/settings_type.h +++ b/src/settings_type.h @@ -269,7 +269,7 @@ struct NetworkSettings { bool server_advertise; ///< advertise the server to the masterserver char client_name[NETWORK_CLIENT_NAME_LENGTH]; ///< name of the player (as client) char default_company_pass[NETWORK_PASSWORD_LENGTH]; ///< default password for new companies in encrypted form - char connect_to_ip[NETWORK_HOSTNAME_LENGTH]; ///< default for the "Add server" query + char connect_to_ip[NETWORK_HOSTNAME_PORT_LENGTH]; ///< default for the "Add server" query char network_id[NETWORK_SERVER_ID_LENGTH]; ///< network ID for servers bool autoclean_companies; ///< automatically remove companies that are not in use uint8 autoclean_unprotected; ///< remove passwordless companies after this many months @@ -281,8 +281,7 @@ struct NetworkSettings { Year restart_game_year; ///< year the server restarts uint8 min_active_clients; ///< minimum amount of active clients to unpause the game bool reload_cfg; ///< reload the config file before restarting - char last_host[NETWORK_HOSTNAME_LENGTH]; ///< IP address of the last joined server - uint16 last_port; ///< port of the last joined server + char last_joined[NETWORK_HOSTNAME_PORT_LENGTH]; ///< Last joined server bool no_http_content_downloads; ///< do not do content downloads over HTTP }; diff --git a/src/table/settings.ini b/src/table/settings.ini index 391a5b427f..4e64bd0326 100644 --- a/src/table/settings.ini +++ b/src/table/settings.ini @@ -4069,21 +4069,12 @@ def = false cat = SC_EXPERT [SDTC_STR] -var = network.last_host +var = network.last_joined type = SLE_STRB flags = SLF_NOT_IN_SAVE | SLF_NO_NETWORK_SYNC def = """" cat = SC_EXPERT -[SDTC_VAR] -var = network.last_port -type = SLE_UINT16 -flags = SLF_NOT_IN_SAVE | SLF_NO_NETWORK_SYNC -def = 0 -min = 0 -max = UINT16_MAX -cat = SC_EXPERT - [SDTC_BOOL] var = network.no_http_content_downloads flags = SLF_NOT_IN_SAVE | SLF_NO_NETWORK_SYNC From a61696d6c565ff92c6604b12eefe36198d094056 Mon Sep 17 00:00:00 2001 From: rubidium42 Date: Thu, 29 Apr 2021 16:43:13 +0200 Subject: [PATCH 203/268] Change: [Network] Encapsulate logic about the connection string to the network code (#23) --- src/console_cmds.cpp | 21 ++------------------- src/network/core/tcp_http.cpp | 2 +- src/network/network.cpp | 26 +++++++++++++++++++++----- src/network/network_func.h | 7 ++----- src/network/network_internal.h | 4 ++++ src/openttd.cpp | 19 +++---------------- 6 files changed, 33 insertions(+), 46 deletions(-) diff --git a/src/console_cmds.cpp b/src/console_cmds.cpp index 7ab6d3179e..9dce399096 100644 --- a/src/console_cmds.cpp +++ b/src/console_cmds.cpp @@ -901,8 +901,7 @@ DEF_CONSOLE_CMD(ConNetworkReconnect) /* Don't resolve the address first, just print it directly as it comes from the config file. */ IConsolePrintF(CC_DEFAULT, "Reconnecting to %s ...", _settings_client.network.last_joined); - NetworkAddress address = ParseConnectionString(_settings_client.network.last_joined, NETWORK_DEFAULT_PORT); - NetworkClientConnectGame(address, playas); + NetworkClientConnectGame(_settings_client.network.last_joined, playas); return true; } @@ -918,23 +917,7 @@ DEF_CONSOLE_CMD(ConNetworkConnect) if (argc < 2) return false; if (_networking) NetworkDisconnect(); // we are in network-mode, first close it! - CompanyID join_as = COMPANY_NEW_COMPANY; - NetworkAddress address = ParseGameConnectionString(&join_as, argv[1], NETWORK_DEFAULT_PORT); - - IConsolePrintF(CC_DEFAULT, "Connecting to %s...", address.GetAddressAsString().c_str()); - if (join_as != COMPANY_NEW_COMPANY) { - IConsolePrintF(CC_DEFAULT, " company-no: %d", join_as); - - /* From a user pov 0 is a new company, internally it's different and all - * companies are offset by one to ease up on users (eg companies 1-8 not 0-7) */ - if (join_as != COMPANY_SPECTATOR) { - if (join_as > MAX_COMPANIES) return false; - join_as--; - } - } - - NetworkClientConnectGame(address, join_as); - + NetworkClientConnectGame(argv[1], COMPANY_NEW_COMPANY); return true; } diff --git a/src/network/core/tcp_http.cpp b/src/network/core/tcp_http.cpp index 99eca1cb74..e0c269fafb 100644 --- a/src/network/core/tcp_http.cpp +++ b/src/network/core/tcp_http.cpp @@ -12,7 +12,7 @@ #include "../../stdafx.h" #include "../../debug.h" #include "../../rev.h" -#include "../network_func.h" +#include "../network_internal.h" #include "game_info.h" #include "tcp_http.h" diff --git a/src/network/network.cpp b/src/network/network.cpp index 0be46bbf35..d7cf9367a1 100644 --- a/src/network/network.cpp +++ b/src/network/network.cpp @@ -490,10 +490,10 @@ void ParseFullConnectionString(const char **company, const char **port, char *co * @param default_port The default port to set port to if not in connection_string. * @return A valid NetworkAddress of the parsed information. */ -NetworkAddress ParseConnectionString(const char *connection_string, int default_port) +NetworkAddress ParseConnectionString(const std::string &connection_string, int default_port) { char internal_connection_string[NETWORK_HOSTNAME_PORT_LENGTH]; - strecpy(internal_connection_string, connection_string, lastof(internal_connection_string)); + strecpy(internal_connection_string, connection_string.c_str(), lastof(internal_connection_string)); const char *port = nullptr; ParseFullConnectionString(nullptr, &port, internal_connection_string); @@ -512,10 +512,10 @@ NetworkAddress ParseConnectionString(const char *connection_string, int default_ * @param default_port The default port to set port to if not in connection_string. * @return A valid NetworkAddress of the parsed information. */ -NetworkAddress ParseGameConnectionString(CompanyID *company, const char *connection_string, int default_port) +NetworkAddress ParseGameConnectionString(CompanyID *company, const std::string &connection_string, int default_port) { char internal_connection_string[NETWORK_HOSTNAME_PORT_LENGTH + 4]; // 4 extra for the "#" and company - strecpy(internal_connection_string, connection_string, lastof(internal_connection_string)); + strecpy(internal_connection_string, connection_string.c_str(), lastof(internal_connection_string)); const char *port_s = nullptr; const char *company_s = nullptr; @@ -693,6 +693,20 @@ public: } }; +void NetworkClientConnectGame(const std::string &connection_string, CompanyID default_company, const char *join_server_password, const char *join_company_password) +{ + CompanyID join_as = default_company; + NetworkAddress address = ParseGameConnectionString(&join_as, connection_string, NETWORK_DEFAULT_PORT); + + if (join_as != COMPANY_NEW_COMPANY && join_as != COMPANY_SPECTATOR) { + join_as--; + if (join_as >= MAX_COMPANIES) { + return; + } + } + + NetworkClientConnectGame(address, join_as, join_server_password, join_company_password); +} /* Used by clients, to connect to a server */ void NetworkClientConnectGame(NetworkAddress &address, CompanyID join_as, const char *join_server_password, const char *join_company_password) @@ -1063,10 +1077,12 @@ static void NetworkGenerateServerId() seprintf(_settings_client.network.network_id, lastof(_settings_client.network.network_id), "%s", hex_output); } -void NetworkStartDebugLog(NetworkAddress &address) +void NetworkStartDebugLog(const std::string &connection_string) { extern SOCKET _debug_socket; // Comes from debug.c + NetworkAddress address = ParseConnectionString(connection_string, NETWORK_DEFAULT_DEBUGLOG_PORT); + DEBUG(net, 0, "Redirecting DEBUG() to %s", address.GetAddressAsString().c_str()); SOCKET s = address.Connect(); diff --git a/src/network/network_func.h b/src/network/network_func.h index 967bb62603..cd2b291bc7 100644 --- a/src/network/network_func.h +++ b/src/network/network_func.h @@ -18,7 +18,6 @@ // #define DEBUG_FAILED_DUMP_COMMANDS #include "network_type.h" -#include "core/address.h" #include "../console_type.h" #include "../gfx_type.h" #include "../openttd.h" @@ -47,14 +46,12 @@ void NetworkDisconnect(bool blocking = false, bool close_admins = true); void NetworkGameLoop(); void NetworkBackgroundLoop(); void ParseFullConnectionString(const char **company, const char **port, char *connection_string); -NetworkAddress ParseConnectionString(const char *connection_string, int default_port); -NetworkAddress ParseGameConnectionString(CompanyID *company, const char *connection_string, int default_port); -void NetworkStartDebugLog(NetworkAddress &address); +void NetworkStartDebugLog(const std::string &connection_string); void NetworkPopulateCompanyStats(NetworkCompanyStats *stats); void NetworkUpdateClientInfo(ClientID client_id); void NetworkClientsToSpectators(CompanyID cid); -void NetworkClientConnectGame(NetworkAddress &address, CompanyID join_as, const char *join_server_password = nullptr, const char *join_company_password = nullptr); +void NetworkClientConnectGame(const std::string &connection_string, CompanyID default_company, const char *join_server_password = nullptr, const char *join_company_password = nullptr); void NetworkClientRequestMove(CompanyID company, const char *pass = ""); void NetworkClientSendRcon(const char *password, const char *command); void NetworkClientSendChat(NetworkAction action, DestType type, int dest, const char *msg, int64 data = 0); diff --git a/src/network/network_internal.h b/src/network/network_internal.h index c64eac7954..ff3c8c22d2 100644 --- a/src/network/network_internal.h +++ b/src/network/network_internal.h @@ -119,4 +119,8 @@ StringID GetNetworkErrorMsg(NetworkErrorCode err); bool NetworkFindName(char *new_name, const char *last); const char *GenerateCompanyPasswordHash(const char *password, const char *password_server_id, uint32 password_game_seed); +void NetworkClientConnectGame(NetworkAddress &address, CompanyID join_as, const char *join_server_password = nullptr, const char *join_company_password = nullptr); +NetworkAddress ParseConnectionString(const std::string &connection_string, int default_port); +NetworkAddress ParseGameConnectionString(CompanyID *company, const std::string &connection_string, int default_port); + #endif /* NETWORK_INTERNAL_H */ diff --git a/src/openttd.cpp b/src/openttd.cpp index b128243854..fbeeba793e 100644 --- a/src/openttd.cpp +++ b/src/openttd.cpp @@ -473,21 +473,10 @@ struct AfterNewGRFScan : NewGRFScanCallback { if (_switch_mode != SM_NONE) MakeNewgameSettingsLive(); if (_network_available && network_conn != nullptr) { - CompanyID join_as = COMPANY_NEW_COMPANY; - NetworkAddress address = ParseGameConnectionString(&join_as, network_conn, NETWORK_DEFAULT_PORT); - - if (join_as != COMPANY_NEW_COMPANY && join_as != COMPANY_SPECTATOR) { - join_as--; - if (join_as >= MAX_COMPANIES) { - delete this; - return; - } - } - LoadIntroGame(); _switch_mode = SM_NONE; - NetworkClientConnectGame(address, join_as, join_server_password, join_company_password); + NetworkClientConnectGame(network_conn, COMPANY_NEW_COMPANY, join_server_password, join_company_password); } /* After the scan we're not used anymore. */ @@ -763,8 +752,7 @@ int openttd_main(int argc, char *argv[]) NetworkStartUp(); // initialize network-core if (debuglog_conn != nullptr && _network_available) { - NetworkAddress address = ParseConnectionString(debuglog_conn, NETWORK_DEFAULT_DEBUGLOG_PORT); - NetworkStartDebugLog(address); + NetworkStartDebugLog(debuglog_conn); } if (!HandleBootstrap()) { @@ -1476,8 +1464,7 @@ void GameLoop() if (_network_reconnect > 0 && --_network_reconnect == 0) { /* This means that we want to reconnect to the last host * We do this here, because it means that the network is really closed */ - NetworkAddress address = ParseConnectionString(_settings_client.network.last_joined, NETWORK_DEFAULT_PORT); - NetworkClientConnectGame(address, COMPANY_SPECTATOR); + NetworkClientConnectGame(_settings_client.network.last_joined, COMPANY_SPECTATOR); } /* Singleplayer */ StateGameLoop(); From 14e92bd8e241998ced263ee542965a71bbdd77a5 Mon Sep 17 00:00:00 2001 From: glx22 Date: Wed, 28 Apr 2021 21:24:24 +0200 Subject: [PATCH 204/268] Codechange: Replace window related FOR_ALL with range-based for loops --- src/misc_gui.cpp | 3 +- src/sound.cpp | 3 +- src/viewport.cpp | 11 ++-- src/widgets/dropdown.cpp | 3 +- src/window.cpp | 131 +++++++++++++-------------------------- src/window_gui.h | 68 ++++++++++++++++++-- 6 files changed, 113 insertions(+), 106 deletions(-) diff --git a/src/misc_gui.cpp b/src/misc_gui.cpp index 9be207ac60..b2de451404 100644 --- a/src/misc_gui.cpp +++ b/src/misc_gui.cpp @@ -1284,8 +1284,7 @@ void ShowQuery(StringID caption, StringID message, Window *parent, QueryCallback { if (parent == nullptr) parent = FindWindowById(WC_MAIN_WINDOW, 0); - const Window *w; - FOR_ALL_WINDOWS_FROM_BACK(w) { + for (const Window *w : Window::IterateFromBack()) { if (w->window_class != WC_CONFIRM_POPUP_QUERY) continue; const QueryWindow *qw = (const QueryWindow *)w; diff --git a/src/sound.cpp b/src/sound.cpp index 7d70fa760d..d91476729b 100644 --- a/src/sound.cpp +++ b/src/sound.cpp @@ -238,8 +238,7 @@ static void SndPlayScreenCoordFx(SoundID sound, int left, int right, int top, in { if (_settings_client.music.effect_vol == 0) return; - const Window *w; - FOR_ALL_WINDOWS_FROM_BACK(w) { + for (const Window *w : Window::IterateFromBack()) { const Viewport *vp = w->viewport; if (vp != nullptr && diff --git a/src/viewport.cpp b/src/viewport.cpp index aae659d53d..303e027600 100644 --- a/src/viewport.cpp +++ b/src/viewport.cpp @@ -268,7 +268,7 @@ static Point _vp_move_offs; static void DoSetViewportPosition(const Window *w, int left, int top, int width, int height) { - FOR_ALL_WINDOWS_FROM_BACK_FROM(w, w) { + for (const Window *w : Window::IterateFromBack(w)) { if (left + width > w->left && w->left + w->width > left && top + height > w->top && @@ -1475,8 +1475,7 @@ void ViewportSign::MarkDirty(ZoomLevel maxzoom) const zoomlevels[zoom].bottom = this->top + ScaleByZoom(VPSM_TOP + FONT_HEIGHT_NORMAL + VPSM_BOTTOM + 1, zoom); } - Window *w; - FOR_ALL_WINDOWS_FROM_BACK(w) { + for (const Window *w : Window::IterateFromBack()) { Viewport *vp = w->viewport; if (vp != nullptr && vp->zoom <= maxzoom) { assert(vp->width != 0); @@ -1949,8 +1948,7 @@ bool MarkAllViewportsDirty(int left, int top, int right, int bottom) { bool dirty = false; - Window *w; - FOR_ALL_WINDOWS_FROM_BACK(w) { + for (const Window *w : Window::IterateFromBack()) { Viewport *vp = w->viewport; if (vp != nullptr) { assert(vp->width != 0); @@ -1963,8 +1961,7 @@ bool MarkAllViewportsDirty(int left, int top, int right, int bottom) void ConstrainAllViewportsZoom() { - Window *w; - FOR_ALL_WINDOWS_FROM_FRONT(w) { + for (Window *w : Window::IterateFromFront()) { if (w->viewport == nullptr) continue; ZoomLevel zoom = static_cast(Clamp(w->viewport->zoom, _settings_client.gui.zoom_min, _settings_client.gui.zoom_max)); diff --git a/src/widgets/dropdown.cpp b/src/widgets/dropdown.cpp index 6b1debb9db..f5536f52f4 100644 --- a/src/widgets/dropdown.cpp +++ b/src/widgets/dropdown.cpp @@ -505,8 +505,7 @@ void ShowDropDownMenu(Window *w, const StringID *strings, int selected, int butt */ int HideDropDownMenu(Window *pw) { - Window *w; - FOR_ALL_WINDOWS_FROM_BACK(w) { + for (Window *w : Window::IterateFromBack()) { if (w->window_class != WC_DROPDOWN_MENU) continue; DropdownWindow *dw = dynamic_cast(w); diff --git a/src/window.cpp b/src/window.cpp index 2216fb2418..a82afc7cda 100644 --- a/src/window.cpp +++ b/src/window.cpp @@ -898,8 +898,7 @@ static bool MayBeShown(const Window *w) */ static void DrawOverlappedWindow(Window *w, int left, int top, int right, int bottom) { - const Window *v; - FOR_ALL_WINDOWS_FROM_BACK_FROM(v, w->z_front) { + for (const Window *v : Window::IterateFromBack(w->z_front)) { if (MayBeShown(v) && right > v->left && bottom > v->top && @@ -958,13 +957,11 @@ static void DrawOverlappedWindow(Window *w, int left, int top, int right, int bo */ void DrawOverlappedWindowForAll(int left, int top, int right, int bottom) { - Window *w; - DrawPixelInfo *old_dpi = _cur_dpi; DrawPixelInfo bk; _cur_dpi = &bk; - FOR_ALL_WINDOWS_FROM_BACK(w) { + for (Window *w : Window::IterateFromBack()) { if (MayBeShown(w) && right > w->left && bottom > w->top && @@ -1057,8 +1054,7 @@ void Window::SetShaded(bool make_shaded) */ static Window *FindChildWindow(const Window *w, WindowClass wc) { - Window *v; - FOR_ALL_WINDOWS_FROM_BACK(v) { + for (Window *v : Window::IterateFromBack()) { if ((wc == WC_INVALID || wc == v->window_class) && v->parent == w) return v; } @@ -1132,8 +1128,7 @@ Window::~Window() */ Window *FindWindowById(WindowClass cls, WindowNumber number) { - Window *w; - FOR_ALL_WINDOWS_FROM_BACK(w) { + for (Window *w : Window::IterateFromBack()) { if (w->window_class == cls && w->window_number == number) return w; } @@ -1148,8 +1143,7 @@ Window *FindWindowById(WindowClass cls, WindowNumber number) */ Window *FindWindowByClass(WindowClass cls) { - Window *w; - FOR_ALL_WINDOWS_FROM_BACK(w) { + for (Window *w : Window::IterateFromBack()) { if (w->window_class == cls) return w; } @@ -1176,13 +1170,11 @@ void DeleteWindowById(WindowClass cls, WindowNumber number, bool force) */ void DeleteWindowByClass(WindowClass cls) { - Window *w; - restart_search: /* When we find the window to delete, we need to restart the search * as deleting this window could cascade in deleting (many) others * anywhere in the z-array */ - FOR_ALL_WINDOWS_FROM_BACK(w) { + for (Window *w : Window::IterateFromBack()) { if (w->window_class == cls) { delete w; goto restart_search; @@ -1198,13 +1190,11 @@ restart_search: */ void DeleteCompanyWindows(CompanyID id) { - Window *w; - restart_search: /* When we find the window to delete, we need to restart the search * as deleting this window could cascade in deleting (many) others * anywhere in the z-array */ - FOR_ALL_WINDOWS_FROM_BACK(w) { + for (Window *w : Window::IterateFromBack()) { if (w->owner == id) { delete w; goto restart_search; @@ -1224,8 +1214,7 @@ restart_search: */ void ChangeWindowOwner(Owner old_owner, Owner new_owner) { - Window *w; - FOR_ALL_WINDOWS_FROM_BACK(w) { + for (Window *w : Window::IterateFromBack()) { if (w->owner != old_owner) continue; switch (w->window_class) { @@ -1594,8 +1583,7 @@ static bool IsGoodAutoPlace1(int left, int top, int width, int height, int toolb if (left < 0 || top < toolbar_y || right > _screen.width || bottom > _screen.height) return false; /* Make sure it is not obscured by any window. */ - const Window *w; - FOR_ALL_WINDOWS_FROM_BACK(w) { + for (const Window *w : Window::IterateFromBack()) { if (w->window_class == WC_MAIN_WINDOW) continue; if (right > w->left && @@ -1640,8 +1628,7 @@ static bool IsGoodAutoPlace2(int left, int top, int width, int height, int toolb if (top < toolbar_y || top > _screen.height - (height >> 2)) return false; /* Make sure it is not obscured by any window. */ - const Window *w; - FOR_ALL_WINDOWS_FROM_BACK(w) { + for (const Window *w : Window::IterateFromBack()) { if (w->window_class == WC_MAIN_WINDOW) continue; if (left + width > w->left && @@ -1678,8 +1665,7 @@ static Point GetAutoPlacePosition(int width, int height) * The new window must be entirely on-screen, and not overlap with an existing window. * Eight starting points are tried, two at each corner. */ - const Window *w; - FOR_ALL_WINDOWS_FROM_BACK(w) { + for (const Window *w : Window::IterateFromBack()) { if (w->window_class == WC_MAIN_WINDOW) continue; if (IsGoodAutoPlace1(w->left + w->width, w->top, width, height, toolbar_y, pt)) return pt; @@ -1696,7 +1682,7 @@ static Point GetAutoPlacePosition(int width, int height) * The new window may be partly off-screen, and must not overlap with an existing window. * Only four starting points are tried. */ - FOR_ALL_WINDOWS_FROM_BACK(w) { + for (const Window *w : Window::IterateFromBack()) { if (w->window_class == WC_MAIN_WINDOW) continue; if (IsGoodAutoPlace2(w->left + w->width, w->top, width, height, toolbar_y, pt)) return pt; @@ -1713,7 +1699,7 @@ static Point GetAutoPlacePosition(int width, int height) int offset_y = std::max(NWidgetLeaf::closebox_dimension.height, FONT_HEIGHT_NORMAL + WD_CAPTIONTEXT_TOP + WD_CAPTIONTEXT_BOTTOM); restart: - FOR_ALL_WINDOWS_FROM_BACK(w) { + for (const Window *w : Window::IterateFromBack()) { if (w->left == left && w->top == top) { left += offset_x; top += offset_y; @@ -1880,8 +1866,7 @@ Window::Window(WindowDesc *desc) : window_desc(desc), mouse_capture_widget(-1) */ Window *FindWindowFromPt(int x, int y) { - Window *w; - FOR_ALL_WINDOWS_FROM_FRONT(w) { + for (Window *w : Window::IterateFromFront()) { if (MayBeShown(w) && IsInsideBS(x, w->left, w->width) && IsInsideBS(y, w->top, w->height)) { return w; } @@ -1918,10 +1903,9 @@ void UnInitWindowSystem() { UnshowCriticalError(); - Window *w; - FOR_ALL_WINDOWS_FROM_FRONT(w) delete w; + for (Window *w : Window::IterateFromFront()) delete w; - for (w = _z_front_window; w != nullptr; /* nothing */) { + for (Window *w = _z_front_window; w != nullptr; /* nothing */) { Window *to_del = w; w = w->z_back; free(to_del); @@ -1948,8 +1932,7 @@ static void DecreaseWindowCounters() if (_scroller_click_timeout != 0) _scroller_click_timeout--; if (hundredth_tick_timeout != 0) hundredth_tick_timeout--; - Window *w; - FOR_ALL_WINDOWS_FROM_FRONT(w) { + for (Window *w : Window::IterateFromFront()) { if (!_network_dedicated && hundredth_tick_timeout == 0) w->OnHundredthTick(); if (_scroller_click_timeout == 0) { @@ -1975,7 +1958,7 @@ static void DecreaseWindowCounters() w->OnMouseLoop(); } - FOR_ALL_WINDOWS_FROM_FRONT(w) { + for (Window *w : Window::IterateFromFront()) { if ((w->flags & WF_TIMEOUT) && --w->timeout_timer == 0) { CLRBITS(w->flags, WF_TIMEOUT); @@ -2217,8 +2200,7 @@ static EventState HandleWindowDragging() if (_left_button_down && _cursor.delta.x == 0 && _cursor.delta.y == 0) return ES_HANDLED; /* Otherwise find the window... */ - Window *w; - FOR_ALL_WINDOWS_FROM_BACK(w) { + for (Window *w : Window::IterateFromBack()) { if (w->flags & WF_DRAGGING) { /* Stop the dragging if the left mouse button was released */ if (!_left_button_down) { @@ -2234,13 +2216,11 @@ static EventState HandleWindowDragging() int ny = y; if (_settings_client.gui.window_snap_radius != 0) { - const Window *v; - int hsnap = _settings_client.gui.window_snap_radius; int vsnap = _settings_client.gui.window_snap_radius; int delta; - FOR_ALL_WINDOWS_FROM_BACK(v) { + for (const Window *v : Window::IterateFromBack()) { if (v == w) continue; // Don't snap at yourself if (y + w->height > v->top && y < v->top + v->height) { @@ -2454,8 +2434,7 @@ static void HandleScrollbarScrolling(Window *w) */ static EventState HandleActiveWidget() { - Window *w; - FOR_ALL_WINDOWS_FROM_BACK(w) { + for (Window *w : Window::IterateFromBack()) { if (w->mouse_capture_widget >= 0) { /* Abort if no button is clicked any more. */ if (!_left_button_down) { @@ -2565,8 +2544,7 @@ static bool MaybeBringWindowToFront(Window *w) w_height = w->unshaded_size.height; } - Window *u; - FOR_ALL_WINDOWS_FROM_BACK_FROM(u, w->z_front) { + for (Window *u : Window::IterateFromBack(w->z_front)) { /* A modal child will prevent the activation of the parent window */ if (u->parent == w && (u->window_desc->flags & WDF_MODAL)) { u->SetWhiteBorder(); @@ -2708,8 +2686,7 @@ void HandleKeypress(uint keycode, WChar key) } /* Call the event, start with the uppermost window, but ignore the toolbar. */ - Window *w; - FOR_ALL_WINDOWS_FROM_FRONT(w) { + for (Window *w : Window::IterateFromFront()) { if (w->window_class == WC_MAIN_TOOLBAR) continue; if (w->window_desc->hotkeys != nullptr) { int hotkey = w->window_desc->hotkeys->CheckMatch(keycode); @@ -2718,7 +2695,7 @@ void HandleKeypress(uint keycode, WChar key) if (w->OnKeyPress(key, keycode) == ES_HANDLED) return; } - w = FindWindowById(WC_MAIN_TOOLBAR, 0); + Window *w = FindWindowById(WC_MAIN_TOOLBAR, 0); /* When there is no toolbar w is null, check for that */ if (w != nullptr) { if (w->window_desc->hotkeys != nullptr) { @@ -2737,8 +2714,7 @@ void HandleKeypress(uint keycode, WChar key) void HandleCtrlChanged() { /* Call the event, start with the uppermost window. */ - Window *w; - FOR_ALL_WINDOWS_FROM_FRONT(w) { + for (Window *w : Window::IterateFromFront()) { if (w->OnCTRLStateChange() == ES_HANDLED) return; } } @@ -3073,8 +3049,8 @@ static void CheckSoftLimit() for (;;) { uint deletable_count = 0; - Window *w, *last_deletable = nullptr; - FOR_ALL_WINDOWS_FROM_FRONT(w) { + Window *last_deletable = nullptr; + for (Window *w : Window::IterateFromFront()) { if (w->window_class == WC_MAIN_WINDOW || IsVitalWindow(w) || (w->flags & WF_STICKY)) continue; last_deletable = w; @@ -3127,8 +3103,7 @@ void InputLoop() */ void CallWindowRealtimeTickEvent(uint delta_ms) { - Window *w; - FOR_ALL_WINDOWS_FROM_FRONT(w) { + for (Window *w : Window::IterateFromFront()) { w->OnRealtimeTick(delta_ms); } } @@ -3156,10 +3131,8 @@ void UpdateWindows() NetworkChatMessageLoop(); } - Window *w; - /* Process invalidations before anything else. */ - FOR_ALL_WINDOWS_FROM_FRONT(w) { + for (Window *w : Window::IterateFromFront()) { w->ProcessScheduledInvalidations(); w->ProcessHighlightedInvalidations(); } @@ -3192,7 +3165,7 @@ void UpdateWindows() if (window_timer.HasElapsed()) { window_timer.SetInterval(MILLISECONDS_PER_TICK); - FOR_ALL_WINDOWS_FROM_FRONT(w) { + for (Window *w : Window::IterateFromFront()) { if ((w->flags & WF_WHITE_BORDER) && --w->white_border_timer == 0) { CLRBITS(w->flags, WF_WHITE_BORDER); w->SetDirty(); @@ -3202,7 +3175,7 @@ void UpdateWindows() DrawDirtyBlocks(); - FOR_ALL_WINDOWS_FROM_BACK(w) { + for (Window *w : Window::IterateFromBack()) { /* Update viewport only if window is not shaded. */ if (w->viewport != nullptr && !w->IsShaded()) UpdateViewportPosition(w); } @@ -3218,8 +3191,7 @@ void UpdateWindows() */ void SetWindowDirty(WindowClass cls, WindowNumber number) { - const Window *w; - FOR_ALL_WINDOWS_FROM_BACK(w) { + for (const Window *w : Window::IterateFromBack()) { if (w->window_class == cls && w->window_number == number) w->SetDirty(); } } @@ -3232,8 +3204,7 @@ void SetWindowDirty(WindowClass cls, WindowNumber number) */ void SetWindowWidgetDirty(WindowClass cls, WindowNumber number, byte widget_index) { - const Window *w; - FOR_ALL_WINDOWS_FROM_BACK(w) { + for (const Window *w : Window::IterateFromBack()) { if (w->window_class == cls && w->window_number == number) { w->SetWidgetDirty(widget_index); } @@ -3246,8 +3217,7 @@ void SetWindowWidgetDirty(WindowClass cls, WindowNumber number, byte widget_inde */ void SetWindowClassesDirty(WindowClass cls) { - Window *w; - FOR_ALL_WINDOWS_FROM_BACK(w) { + for (const Window *w : Window::IterateFromBack()) { if (w->window_class == cls) w->SetDirty(); } } @@ -3319,8 +3289,7 @@ void Window::ProcessHighlightedInvalidations() */ void InvalidateWindowData(WindowClass cls, WindowNumber number, int data, bool gui_scope) { - Window *w; - FOR_ALL_WINDOWS_FROM_BACK(w) { + for (Window *w : Window::IterateFromBack()) { if (w->window_class == cls && w->window_number == number) { w->InvalidateData(data, gui_scope); } @@ -3337,9 +3306,7 @@ void InvalidateWindowData(WindowClass cls, WindowNumber number, int data, bool g */ void InvalidateWindowClassesData(WindowClass cls, int data, bool gui_scope) { - Window *w; - - FOR_ALL_WINDOWS_FROM_BACK(w) { + for (Window *w : Window::IterateFromBack()) { if (w->window_class == cls) { w->InvalidateData(data, gui_scope); } @@ -3351,8 +3318,7 @@ void InvalidateWindowClassesData(WindowClass cls, int data, bool gui_scope) */ void CallWindowGameTickEvent() { - Window *w; - FOR_ALL_WINDOWS_FROM_FRONT(w) { + for (Window *w : Window::IterateFromFront()) { w->OnGameTick(); } } @@ -3365,13 +3331,11 @@ void CallWindowGameTickEvent() */ void DeleteNonVitalWindows() { - Window *w; - restart_search: /* When we find the window to delete, we need to restart the search * as deleting this window could cascade in deleting (many) others * anywhere in the z-array */ - FOR_ALL_WINDOWS_FROM_BACK(w) { + for (const Window *w : Window::IterateFromBack()) { if (w->window_class != WC_MAIN_WINDOW && w->window_class != WC_SELECT_GAME && w->window_class != WC_MAIN_TOOLBAR && @@ -3394,8 +3358,6 @@ restart_search: */ void DeleteAllNonVitalWindows() { - Window *w; - /* Delete every window except for stickied ones, then sticky ones as well */ DeleteNonVitalWindows(); @@ -3403,7 +3365,7 @@ restart_search: /* When we find the window to delete, we need to restart the search * as deleting this window could cascade in deleting (many) others * anywhere in the z-array */ - FOR_ALL_WINDOWS_FROM_BACK(w) { + for (const Window *w : Window::IterateFromBack()) { if (w->flags & WF_STICKY) { delete w; goto restart_search; @@ -3428,20 +3390,18 @@ void DeleteAllMessages() */ void DeleteConstructionWindows() { - Window *w; - restart_search: /* When we find the window to delete, we need to restart the search * as deleting this window could cascade in deleting (many) others * anywhere in the z-array */ - FOR_ALL_WINDOWS_FROM_BACK(w) { + for (const Window *w : Window::IterateFromBack()) { if (w->window_desc->flags & WDF_CONSTRUCTION) { delete w; goto restart_search; } } - FOR_ALL_WINDOWS_FROM_BACK(w) w->SetDirty(); + for (const Window *w : Window::IterateFromBack()) w->SetDirty(); } /** Delete all always on-top windows to get an empty screen */ @@ -3460,8 +3420,7 @@ void ReInitAllWindows() extern void InitDepotWindowBlockSizes(); InitDepotWindowBlockSizes(); - Window *w; - FOR_ALL_WINDOWS_FROM_BACK(w) { + for (Window *w : Window::IterateFromBack()) { w->ReInit(); } @@ -3550,8 +3509,7 @@ int PositionNetworkChatWindow(Window *w) */ void ChangeVehicleViewports(VehicleID from_index, VehicleID to_index) { - Window *w; - FOR_ALL_WINDOWS_FROM_BACK(w) { + for (const Window *w : Window::IterateFromBack()) { if (w->viewport != nullptr && w->viewport->follow_vehicle == from_index) { w->viewport->follow_vehicle = to_index; w->SetDirty(); @@ -3569,8 +3527,7 @@ void RelocateAllWindows(int neww, int newh) { DeleteWindowById(WC_DROPDOWN_MENU, 0); - Window *w; - FOR_ALL_WINDOWS_FROM_BACK(w) { + for (Window *w : Window::IterateFromBack()) { int left, top; /* XXX - this probably needs something more sane. For example specifying * in a 'backup'-desc that the window should always be centered. */ diff --git a/src/window_gui.h b/src/window_gui.h index 67a799c3d3..66c867a547 100644 --- a/src/window_gui.h +++ b/src/window_gui.h @@ -810,6 +810,68 @@ public: * @pre this->IsNewGRFInspectable() */ virtual void ShowNewGRFInspectWindow() const { NOT_REACHED(); } + + /** + * Iterator to iterate all valid Windows + * @tparam T Type of the class/struct that is going to be iterated + * @tparam Tfront Wether we iterate from front + */ + template + struct WindowIterator { + typedef T value_type; + typedef T *pointer; + typedef T &reference; + typedef size_t difference_type; + typedef std::forward_iterator_tag iterator_category; + + explicit WindowIterator(T *start) : w(start) + { + this->Validate(); + } + + bool operator==(const WindowIterator &other) const { return this->w == other.w; } + bool operator!=(const WindowIterator &other) const { return !(*this == other); } + T * operator*() const { return this->w; } + WindowIterator & operator++() { this->Next(); this->Validate(); return *this; } + + private: + T *w; + void Validate() { while (this->w != nullptr && this->w->window_class == WC_INVALID) this->Next(); } + void Next() { if (this->w != nullptr) this->w = Tfront ? this->w->z_back : this->w->z_front; } + }; + + /** + * Iterable ensemble of all valid Windows + * @tparam T Type of the class/struct that is going to be iterated + * @tparam Tfront Wether we iterate from front + */ + template + struct Iterate { + Iterate(T *from) : from(from) {} + WindowIterator begin() { return WindowIterator(this->from); } + WindowIterator end() { return WindowIterator(nullptr); } + bool empty() { return this->begin() == this->end(); } + private: + T *from; + }; + + /** + * Returns an iterable ensemble of all valid Window from back to front + * @tparam T Type of the class/struct that is going to be iterated + * @param from index of the first Window to consider + * @return an iterable ensemble of all valid Window + */ + template + static Iterate IterateFromBack(T *from = _z_back_window) { return Iterate(from); } + + /** + * Returns an iterable ensemble of all valid Window from front to back + * @tparam T Type of the class/struct that is going to be iterated + * @param from index of the first Window to consider + * @return an iterable ensemble of all valid Window + */ + template + static Iterate IterateFromFront(T *from = _z_front_window) { return Iterate(from); } }; /** @@ -888,12 +950,6 @@ void GuiShowTooltips(Window *parent, StringID str, uint paramcount = 0, const ui /* widget.cpp */ int GetWidgetFromPos(const Window *w, int x, int y); -/** Iterate over all windows */ -#define FOR_ALL_WINDOWS_FROM_BACK_FROM(w, start) for (w = start; w != nullptr; w = w->z_front) if (w->window_class != WC_INVALID) -#define FOR_ALL_WINDOWS_FROM_FRONT_FROM(w, start) for (w = start; w != nullptr; w = w->z_back) if (w->window_class != WC_INVALID) -#define FOR_ALL_WINDOWS_FROM_BACK(w) FOR_ALL_WINDOWS_FROM_BACK_FROM(w, _z_back_window) -#define FOR_ALL_WINDOWS_FROM_FRONT(w) FOR_ALL_WINDOWS_FROM_FRONT_FROM(w, _z_front_window) - extern Point _cursorpos_drag_start; extern int _scrollbar_start_pos; From 9a8756d7ed6fdde20bad9be8c8b8bc8fda0170f9 Mon Sep 17 00:00:00 2001 From: glx22 Date: Thu, 29 Apr 2021 17:51:05 +0200 Subject: [PATCH 205/268] Codechange: Replace FOR_ALL_CARGOSPECS with range-based for loops --- src/cargotype.cpp | 10 ++---- src/cargotype.h | 47 ++++++++++++++++++++++++++--- src/economy.cpp | 3 +- src/newgrf.cpp | 6 ++-- src/newgrf_station.cpp | 3 +- src/script/api/script_cargolist.cpp | 3 +- src/station_cmd.cpp | 3 +- src/town_cmd.cpp | 3 +- 8 files changed, 53 insertions(+), 25 deletions(-) diff --git a/src/cargotype.cpp b/src/cargotype.cpp index 81818d5f9d..bf9561dda8 100644 --- a/src/cargotype.cpp +++ b/src/cargotype.cpp @@ -84,8 +84,7 @@ void SetupCargoForClimate(LandscapeID l) */ CargoID GetCargoIDByLabel(CargoLabel cl) { - const CargoSpec *cs; - FOR_ALL_CARGOSPECS(cs) { + for (const CargoSpec *cs : CargoSpec::Iterate()) { if (cs->label == cl) return cs->Index(); } @@ -103,8 +102,7 @@ CargoID GetCargoIDByBitnum(uint8 bitnum) { if (bitnum == INVALID_CARGO) return CT_INVALID; - const CargoSpec *cs; - FOR_ALL_CARGOSPECS(cs) { + for (const CargoSpec *cs : CargoSpec::Iterate()) { if (cs->bitnum == bitnum) return cs->Index(); } @@ -132,7 +130,6 @@ SpriteID CargoSpec::GetCargoIcon() const std::vector _sorted_cargo_specs; ///< Cargo specifications sorted alphabetically by name. uint8 _sorted_standard_cargo_specs_size; ///< Number of standard cargo specifications stored in the _sorted_cargo_specs array. - /** Sort cargo specifications by their name. */ static bool CargoSpecNameSorter(const CargoSpec * const &a, const CargoSpec * const &b) { @@ -169,9 +166,8 @@ static bool CargoSpecClassSorter(const CargoSpec * const &a, const CargoSpec * c void InitializeSortedCargoSpecs() { _sorted_cargo_specs.clear(); - const CargoSpec *cargo; /* Add each cargo spec to the list. */ - FOR_ALL_CARGOSPECS(cargo) { + for (const CargoSpec *cargo : CargoSpec::Iterate()) { _sorted_cargo_specs.push_back(cargo); } diff --git a/src/cargotype.h b/src/cargotype.h index afc501a2f2..4a295f1ae5 100644 --- a/src/cargotype.h +++ b/src/cargotype.h @@ -122,6 +122,49 @@ struct CargoSpec { SpriteID GetCargoIcon() const; + /** + * Iterator to iterate all valid CargoSpec + */ + struct Iterator { + typedef CargoSpec value_type; + typedef CargoSpec *pointer; + typedef CargoSpec &reference; + typedef size_t difference_type; + typedef std::forward_iterator_tag iterator_category; + + explicit Iterator(size_t index) : index(index) + { + this->ValidateIndex(); + }; + + bool operator==(const Iterator &other) const { return this->index == other.index; } + bool operator!=(const Iterator &other) const { return !(*this == other); } + CargoSpec * operator*() const { return CargoSpec::Get(this->index); } + Iterator & operator++() { this->index++; this->ValidateIndex(); return *this; } + + private: + size_t index; + void ValidateIndex() { while (this->index < CargoSpec::GetArraySize() && !(CargoSpec::Get(this->index)->IsValid())) this->index++; } + }; + + /* + * Iterable ensemble of all valid CargoSpec + */ + struct IterateWrapper { + size_t from; + IterateWrapper(size_t from = 0) : from(from) {} + Iterator begin() { return Iterator(this->from); } + Iterator end() { return Iterator(CargoSpec::GetArraySize()); } + bool empty() { return this->begin() == this->end(); } + }; + + /** + * Returns an iterable ensemble of all valid CargoSpec + * @param from index of the first CargoSpec to consider + * @return an iterable ensemble of all valid CargoSpec + */ + static IterateWrapper Iterate(size_t from = 0) { return IterateWrapper(from); } + private: static CargoSpec array[NUM_CARGO]; ///< Array holding all CargoSpecs @@ -150,10 +193,6 @@ static inline bool IsCargoInClass(CargoID c, CargoClass cc) return (CargoSpec::Get(c)->classes & cc) != 0; } -#define FOR_ALL_CARGOSPECS_FROM(var, start) for (size_t cargospec_index = start; var = nullptr, cargospec_index < CargoSpec::GetArraySize(); cargospec_index++) \ - if ((var = CargoSpec::Get(cargospec_index))->IsValid()) -#define FOR_ALL_CARGOSPECS(var) FOR_ALL_CARGOSPECS_FROM(var, 0) - #define FOR_EACH_SET_CARGO_ID(var, cargo_bits) FOR_EACH_SET_BIT_EX(CargoID, var, CargoTypes, cargo_bits) /** diff --git a/src/economy.cpp b/src/economy.cpp index 716446ba9e..9982e79e02 100644 --- a/src/economy.cpp +++ b/src/economy.cpp @@ -798,8 +798,7 @@ void RecomputePrices() } /* Setup cargo payment */ - CargoSpec *cs; - FOR_ALL_CARGOSPECS(cs) { + for (CargoSpec *cs : CargoSpec::Iterate()) { cs->current_payment = ((int64)cs->initial_payment * _economy.inflation_payment) >> 16; } diff --git a/src/newgrf.cpp b/src/newgrf.cpp index 46bd5a6558..19cc436dd7 100644 --- a/src/newgrf.cpp +++ b/src/newgrf.cpp @@ -5332,8 +5332,7 @@ static CargoID TranslateCargo(uint8 feature, uint8 ctype) return CT_INVALID; } - const CargoSpec *cs; - FOR_ALL_CARGOSPECS(cs) { + for (const CargoSpec *cs : CargoSpec::Iterate()) { if (cs->bitnum == ctype) { grfmsg(6, "TranslateCargo: Cargo bitnum %d mapped to cargo type %d.", ctype, cs->Index()); return cs->Index(); @@ -8811,8 +8810,7 @@ static void CalculateRefitMasks() if (_gted[engine].cargo_allowed != 0) { /* Build up the list of cargo types from the set cargo classes. */ - const CargoSpec *cs; - FOR_ALL_CARGOSPECS(cs) { + for (const CargoSpec *cs : CargoSpec::Iterate()) { if (_gted[engine].cargo_allowed & cs->classes) SetBit(mask, cs->Index()); if (_gted[engine].cargo_disallowed & cs->classes) SetBit(not_mask, cs->Index()); } diff --git a/src/newgrf_station.cpp b/src/newgrf_station.cpp index a127d54c9b..fa3f831aa5 100644 --- a/src/newgrf_station.cpp +++ b/src/newgrf_station.cpp @@ -572,8 +572,7 @@ StationResolverObject::StationResolverObject(const StationSpec *statspec, BaseSt } else if (Station::IsExpected(this->station_scope.st)) { const Station *st = Station::From(this->station_scope.st); /* Pick the first cargo that we have waiting */ - const CargoSpec *cs; - FOR_ALL_CARGOSPECS(cs) { + for (const CargoSpec *cs : CargoSpec::Iterate()) { if (this->station_scope.statspec->grf_prop.spritegroup[cs->Index()] != nullptr && st->goods[cs->Index()].cargo.TotalCount() > 0) { ctype = cs->Index(); diff --git a/src/script/api/script_cargolist.cpp b/src/script/api/script_cargolist.cpp index fbd150c6cf..0019dc5e91 100644 --- a/src/script/api/script_cargolist.cpp +++ b/src/script/api/script_cargolist.cpp @@ -19,8 +19,7 @@ ScriptCargoList::ScriptCargoList() { - const CargoSpec *cs; - FOR_ALL_CARGOSPECS(cs) { + for (const CargoSpec *cs : CargoSpec::Iterate()) { this->AddItem(cs->Index()); } } diff --git a/src/station_cmd.cpp b/src/station_cmd.cpp index 7266ad6187..482b954625 100644 --- a/src/station_cmd.cpp +++ b/src/station_cmd.cpp @@ -3467,8 +3467,7 @@ static void UpdateStationRating(Station *st) byte_inc_sat(&st->time_since_load); byte_inc_sat(&st->time_since_unload); - const CargoSpec *cs; - FOR_ALL_CARGOSPECS(cs) { + for (const CargoSpec *cs : CargoSpec::Iterate()) { GoodsEntry *ge = &st->goods[cs->Index()]; /* Slowly increase the rating back to his original level in the case we * didn't deliver cargo yet to this station. This happens when a bribe diff --git a/src/town_cmd.cpp b/src/town_cmd.cpp index 43f3523f8e..a0f5df23ed 100644 --- a/src/town_cmd.cpp +++ b/src/town_cmd.cpp @@ -2779,8 +2779,7 @@ CommandCost CmdRenameTown(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 */ const CargoSpec *FindFirstCargoWithTownEffect(TownEffect effect) { - const CargoSpec *cs; - FOR_ALL_CARGOSPECS(cs) { + for (const CargoSpec *cs : CargoSpec::Iterate()) { if (cs->town_effect == effect) return cs; } return nullptr; From f018471b36fe1ffaedf98430b4156ad369e26c66 Mon Sep 17 00:00:00 2001 From: PeterN Date: Thu, 29 Apr 2021 22:46:42 +0100 Subject: [PATCH 206/268] Cleanup: Remove old FiosList helper methods. (#9139) --- src/console_cmds.cpp | 4 +- src/fios.cpp | 35 ++++++++-------- src/fios.h | 86 +--------------------------------------- src/fios_gui.cpp | 20 +++++----- src/os/windows/win32.cpp | 2 +- 5 files changed, 30 insertions(+), 117 deletions(-) diff --git a/src/console_cmds.cpp b/src/console_cmds.cpp index 9dce399096..401a52b1f9 100644 --- a/src/console_cmds.cpp +++ b/src/console_cmds.cpp @@ -59,7 +59,7 @@ public: /** Declare the file storage cache as being invalid, also clears all stored files. */ void InvalidateFileList() { - this->Clear(); + this->clear(); this->file_list_valid = false; } @@ -403,7 +403,7 @@ DEF_CONSOLE_CMD(ConListFiles) } _console_file_list.ValidateFileList(true); - for (uint i = 0; i < _console_file_list.Length(); i++) { + for (uint i = 0; i < _console_file_list.size(); i++) { IConsolePrintF(CC_DEFAULT, "%d) %s", i, _console_file_list[i].title); } diff --git a/src/fios.cpp b/src/fios.cpp index b68da08f80..8528e8c622 100644 --- a/src/fios.cpp +++ b/src/fios.cpp @@ -63,11 +63,6 @@ bool FiosItem::operator< (const FiosItem &other) const return (_savegame_sort_order & SORT_DESCENDING) ? r > 0 : r < 0; } -FileList::~FileList() -{ - this->Clear(); -} - /** * Construct a file list with the given kind of files, for the stated purpose. * @param abstract_filetype Kind of files to collect. @@ -75,7 +70,7 @@ FileList::~FileList() */ void FileList::BuildFileList(AbstractFileType abstract_filetype, SaveLoadOperation fop) { - this->Clear(); + this->clear(); assert(fop == SLO_LOAD || fop == SLO_SAVE); switch (abstract_filetype) { @@ -107,7 +102,8 @@ void FileList::BuildFileList(AbstractFileType abstract_filetype, SaveLoadOperati */ const FiosItem *FileList::FindItem(const char *file) { - for (const FiosItem *item = this->Begin(); item != this->End(); item++) { + for (const auto &it : *this) { + const FiosItem *item = ⁢ if (strcmp(file, item->name) == 0) return item; if (strcmp(file, item->title) == 0) return item; } @@ -117,13 +113,14 @@ const FiosItem *FileList::FindItem(const char *file) int i = strtol(file, &endptr, 10); if (file == endptr || *endptr != '\0') i = -1; - if (IsInsideMM(i, 0, this->Length())) return this->Get(i); + if (IsInsideMM(i, 0, this->size())) return &this->at(i); /* As a last effort assume it is an OpenTTD savegame and * that the ".sav" part was not given. */ char long_file[MAX_PATH]; seprintf(long_file, lastof(long_file), "%s.sav", file); - for (const FiosItem *item = this->Begin(); item != this->End(); item++) { + for (const auto &it : *this) { + const FiosItem *item = ⁢ if (strcmp(long_file, item->name) == 0) return item; if (strcmp(long_file, item->title) == 0) return item; } @@ -302,11 +299,11 @@ bool FiosFileScanner::AddFile(const std::string &filename, size_t basepath_lengt FiosType type = this->callback_proc(this->fop, filename, ext.c_str(), fios_title, lastof(fios_title)); if (type == FIOS_TYPE_INVALID) return false; - for (const FiosItem *fios = file_list.Begin(); fios != file_list.End(); fios++) { - if (filename == fios->name) return false; + for (const auto &fios : file_list) { + if (filename == fios.name) return false; } - FiosItem *fios = file_list.Append(); + FiosItem *fios = &file_list.emplace_back(); #ifdef _WIN32 // Retrieve the file modified date using GetFileTime rather than stat to work around an obscure MSVC bug that affects Windows XP HANDLE fh = CreateFile(OTTD2FS(filename).c_str(), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, OPEN_EXISTING, 0, nullptr); @@ -367,13 +364,13 @@ static void FiosGetFileList(SaveLoadOperation fop, fios_getlist_callback_proc *c size_t sort_start; char d_name[sizeof(fios->name)]; - file_list.Clear(); + file_list.clear(); assert(_fios_path != nullptr); /* A parent directory link exists if we are not in the root directory */ if (!FiosIsRoot(_fios_path->c_str())) { - fios = file_list.Append(); + fios = &file_list.emplace_back(); fios->type = FIOS_TYPE_PARENT; fios->mtime = 0; strecpy(fios->name, "..", lastof(fios->name)); @@ -390,7 +387,7 @@ static void FiosGetFileList(SaveLoadOperation fop, fios_getlist_callback_proc *c if (FiosIsValidFile(_fios_path->c_str(), dirent, &sb) && S_ISDIR(sb.st_mode) && (!FiosIsHiddenFile(dirent) || strncasecmp(d_name, PERSONAL_DIR, strlen(d_name)) == 0) && strcmp(d_name, ".") != 0 && strcmp(d_name, "..") != 0) { - fios = file_list.Append(); + fios = &file_list.emplace_back(); fios->type = FIOS_TYPE_DIR; fios->mtime = 0; strecpy(fios->name, d_name, lastof(fios->name)); @@ -407,12 +404,12 @@ static void FiosGetFileList(SaveLoadOperation fop, fios_getlist_callback_proc *c { SortingBits order = _savegame_sort_order; _savegame_sort_order = SORT_BY_NAME | SORT_ASCENDING; - std::sort(file_list.files.begin(), file_list.files.end()); + std::sort(file_list.begin(), file_list.end()); _savegame_sort_order = order; } /* This is where to start sorting for the filenames */ - sort_start = file_list.Length(); + sort_start = file_list.size(); /* Show files */ FiosFileScanner scanner(fop, callback_proc, file_list); @@ -422,12 +419,12 @@ static void FiosGetFileList(SaveLoadOperation fop, fios_getlist_callback_proc *c scanner.Scan(nullptr, subdir, true, true); } - std::sort(file_list.files.begin() + sort_start, file_list.files.end()); + std::sort(file_list.begin() + sort_start, file_list.end()); /* Show drives */ FiosGetDrives(file_list); - file_list.Compact(); + file_list.shrink_to_fit(); } /** diff --git a/src/fios.h b/src/fios.h index 3a16b6426a..28a1cc6aa5 100644 --- a/src/fios.h +++ b/src/fios.h @@ -109,94 +109,10 @@ struct FiosItem { }; /** List of file information. */ -class FileList { +class FileList : public std::vector { public: - ~FileList(); - - /** - * Construct a new entry in the file list. - * @return Pointer to the new items to be initialized. - */ - inline FiosItem *Append() - { - return &this->files.emplace_back(); - } - - /** - * Get the number of files in the list. - * @return The number of files stored in the list. - */ - inline size_t Length() const - { - return this->files.size(); - } - - /** - * Get a pointer to the first file information. - * @return Address of the first file information. - */ - inline const FiosItem *Begin() const - { - return this->files.data(); - } - - /** - * Get a pointer behind the last file information. - * @return Address behind the last file information. - */ - inline const FiosItem *End() const - { - return this->Begin() + this->Length(); - } - - /** - * Get a pointer to the indicated file information. File information must exist. - * @return Address of the indicated existing file information. - */ - inline const FiosItem *Get(size_t index) const - { - return this->files.data() + index; - } - - /** - * Get a pointer to the indicated file information. File information must exist. - * @return Address of the indicated existing file information. - */ - inline FiosItem *Get(size_t index) - { - return this->files.data() + index; - } - - inline const FiosItem &operator[](size_t index) const - { - return this->files[index]; - } - - /** - * Get a reference to the indicated file information. File information must exist. - * @return The requested file information. - */ - inline FiosItem &operator[](size_t index) - { - return this->files[index]; - } - - /** Remove all items from the list. */ - inline void Clear() - { - this->files.clear(); - } - - /** Compact the list down to the smallest block size boundary. */ - inline void Compact() - { - this->files.shrink_to_fit(); - } - void BuildFileList(AbstractFileType abstract_filetype, SaveLoadOperation fop); const FiosItem *FindItem(const char *file); - - std::vector files; ///< The list of files. }; enum SortingBits { diff --git a/src/fios_gui.cpp b/src/fios_gui.cpp index e1133dd3cc..8de6235d62 100644 --- a/src/fios_gui.cpp +++ b/src/fios_gui.cpp @@ -249,8 +249,8 @@ static void SortSaveGameList(FileList &file_list) * Drives (A:\ (windows only) are always under the files (FIOS_TYPE_DRIVE) * Only sort savegames/scenarios, not directories */ - for (const FiosItem *item = file_list.Begin(); item != file_list.End(); item++) { - switch (item->type) { + for (const auto &item : file_list) { + switch (item.type) { case FIOS_TYPE_DIR: sort_start++; break; case FIOS_TYPE_PARENT: sort_start++; break; case FIOS_TYPE_DRIVE: sort_end++; break; @@ -258,7 +258,7 @@ static void SortSaveGameList(FileList &file_list) } } - std::sort(file_list.files.begin() + sort_start, file_list.files.end() - sort_end); + std::sort(file_list.begin() + sort_start, file_list.end() - sort_end); } struct SaveLoadWindow : public Window { @@ -437,14 +437,14 @@ public: uint y = r.top + WD_FRAMERECT_TOP; uint scroll_pos = this->vscroll->GetPosition(); - for (uint row = 0; row < this->fios_items.Length(); row++) { + for (uint row = 0; row < this->fios_items.size(); row++) { if (!this->fios_items_shown[row]) { /* The current item is filtered out : we do not show it */ scroll_pos++; continue; } if (row < scroll_pos) continue; - const FiosItem *item = this->fios_items.Get(row); + const FiosItem *item = &this->fios_items[row]; if (item == this->selected) { GfxFillRect(r.left + 1, y, r.right, y + this->resize.step_height, PC_DARK_BLUE); @@ -651,7 +651,7 @@ public: if (!this->fios_items_shown[i]) y++; i++; } - const FiosItem *file = this->fios_items.Get(y); + const FiosItem *file = &this->fios_items[y]; const char *name = FiosBrowseTo(file); if (name == nullptr) { @@ -734,7 +734,7 @@ public: if (!this->fios_items_shown[i]) y++; i++; } - const FiosItem *file = this->fios_items.Get(y); + const FiosItem *file = &this->fios_items[y]; if (file != this->highlighted) { this->highlighted = file; @@ -812,7 +812,7 @@ public: _fios_path_changed = true; this->fios_items.BuildFileList(this->abstract_filetype, this->fop); - this->vscroll->SetCount((uint)this->fios_items.Length()); + this->vscroll->SetCount((uint)this->fios_items.size()); this->selected = nullptr; _load_check_data.Clear(); @@ -852,10 +852,10 @@ public: case SLIWD_FILTER_CHANGES: /* Filter changes */ - this->fios_items_shown.resize(this->fios_items.Length()); + this->fios_items_shown.resize(this->fios_items.size()); uint items_shown_count = 0; ///< The number of items shown in the list /* We pass through every fios item */ - for (uint i = 0; i < this->fios_items.Length(); i++) { + for (uint i = 0; i < this->fios_items.size(); i++) { if (this->string_filter.IsEmpty()) { /* We don't filter anything out if the filter editbox is empty */ this->fios_items_shown[i] = true; diff --git a/src/os/windows/win32.cpp b/src/os/windows/win32.cpp index ddcfd4866c..eee81be402 100644 --- a/src/os/windows/win32.cpp +++ b/src/os/windows/win32.cpp @@ -209,7 +209,7 @@ void FiosGetDrives(FileList &file_list) GetLogicalDriveStrings(lengthof(drives), drives); for (s = drives; *s != '\0';) { - FiosItem *fios = file_list.Append(); + FiosItem *fios = &file_list.emplace_back(); fios->type = FIOS_TYPE_DRIVE; fios->mtime = 0; seprintf(fios->name, lastof(fios->name), "%c:", s[0] & 0xFF); From f00564eeb20a09647f6f52b4ca5b39cb1e6a59aa Mon Sep 17 00:00:00 2001 From: rubidium42 Date: Fri, 30 Apr 2021 00:16:41 +0200 Subject: [PATCH 207/268] Fix: String validation could leave invalid Utf8 encoded strings (#9096) In case a character was encoded in multiple bytes, but required fewer bytes to be encoded, the first byte would be copied to the output leaving an invalid Utf8 encoded string. Later uses of the validated string would use the same decode logic, which would yield a question mark and just read a single byte, so nothing dangerous happened. Furthermore, because the next byte would not be a first byte of an encoded Utf8 character, the last few valid characters could be removed by the validation as well. --- src/string.cpp | 42 ++++++++++++++++++++++++++++++------------ 1 file changed, 30 insertions(+), 12 deletions(-) diff --git a/src/string.cpp b/src/string.cpp index dfd01450e0..38f7d1bd10 100644 --- a/src/string.cpp +++ b/src/string.cpp @@ -192,19 +192,35 @@ static void str_validate(T &dst, const char *str, const char *last, StringValida while (str <= last && *str != '\0') { size_t len = Utf8EncodedCharLen(*str); - /* If the character is unknown, i.e. encoded length is 0 - * we assume worst case for the length check. - * The length check is needed to prevent Utf8Decode to read - * over the terminating '\0' if that happens to be placed - * within the encoding of an UTF8 character. */ - if ((len == 0 && str + 4 > last) || str + len > last) break; - WChar c; - len = Utf8Decode(&c, str); - /* It's possible to encode the string termination character - * into a multiple bytes. This prevents those termination - * characters to be skipped */ - if (c == '\0') break; + /* If the first byte does not look like the first byte of an encoded + * character, i.e. encoded length is 0, then this byte is definitely bad + * and it should be skipped. + * When the first byte looks like the first byte of an encoded character, + * then the remaining bytes in the string are checked whether the whole + * encoded character can be there. If that is not the case, this byte is + * skipped. + * Finally we attempt to decode the encoded character, which does certain + * extra validations to see whether the correct number of bytes were used + * to encode the character. If that is not the case, the byte is probably + * invalid and it is skipped. We could emit a question mark, but then the + * logic below cannot just copy bytes, it would need to re-encode the + * decoded characters as the length in bytes may have changed. + * + * The goals here is to get as much valid Utf8 encoded characters from the + * source string to the destination string. + * + * Note: a multi-byte encoded termination ('\0') will trigger the encoded + * char length and the decoded length to differ, so it will be ignored as + * invalid character data. If it were to reach the termination, then we + * would also reach the "last" byte of the string and a normal '\0' + * termination will be placed after it. + */ + if (len == 0 || str + len > last || len != Utf8Decode(&c, str)) { + /* Maybe the next byte is still a valid character? */ + str++; + continue; + } if ((IsPrintable(c) && (c < SCC_SPRITE_START || c > SCC_SPRITE_END)) || ((settings & SVS_ALLOW_CONTROL_CODE) != 0 && c == SCC_ENCODED)) { /* Copy the character back. Even if dst is current the same as str @@ -225,6 +241,8 @@ static void str_validate(T &dst, const char *str, const char *last, StringValida if ((settings & SVS_REPLACE_WITH_QUESTION_MARK) != 0) *dst++ = '?'; } } + + /* String termination, if needed, is left to the caller of this function. */ } /** From 69118d063f9316e10128580f790175cd2779abdc Mon Sep 17 00:00:00 2001 From: Patric Stout Date: Fri, 30 Apr 2021 11:34:47 +0200 Subject: [PATCH 208/268] Change: use TCP for everything except for master-server and initial server scan (#9130) This means that pressing Refresh button and adding servers manually now uses TCP. The master-server and initial scan are still UDP as they will be replaced by Game Coordinator; no need to change this now. If we query a server that is too old, show a proper warning to the user informing him the server is too old. --- os/emscripten/pre.js | 10 +++++--- src/lang/english.txt | 1 + src/network/network.cpp | 44 +++++++++++++++++++++++--------- src/network/network_client.cpp | 35 +++++++++++++++++++------ src/network/network_client.h | 6 +++-- src/network/network_gamelist.cpp | 10 -------- src/network/network_gui.cpp | 11 ++++---- src/network/network_internal.h | 4 +-- 8 files changed, 78 insertions(+), 43 deletions(-) diff --git a/os/emscripten/pre.js b/os/emscripten/pre.js index 1563e4f95b..82664004ea 100644 --- a/os/emscripten/pre.js +++ b/os/emscripten/pre.js @@ -65,10 +65,14 @@ Module.preRun.push(function() { } window.openttd_server_list = function() { - add_server = Module.cwrap("em_openttd_add_server", null, ["string", "number"]); + add_server = Module.cwrap("em_openttd_add_server", null, ["string"]); - /* Add servers that support WebSocket here. Example: - * add_server("localhost", 3979); */ + /* Add servers that support WebSocket here. Examples: + * add_server("localhost"); + * add_server("localhost:3979"); + * add_server("127.0.0.1:3979"); + * add_server("[::1]:3979"); + */ } var leftButtonDown = false; diff --git a/src/lang/english.txt b/src/lang/english.txt index 3037bf90fe..f8e3618e51 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -2217,6 +2217,7 @@ STR_NETWORK_ERROR_TIMEOUT_COMPUTER :{WHITE}Your com STR_NETWORK_ERROR_TIMEOUT_MAP :{WHITE}Your computer took too long to download the map STR_NETWORK_ERROR_TIMEOUT_JOIN :{WHITE}Your computer took too long to join the server STR_NETWORK_ERROR_INVALID_CLIENT_NAME :{WHITE}Your player name is not valid +STR_NETWORK_ERROR_SERVER_TOO_OLD :{WHITE}The queried server is too old for this client ############ Leave those lines in this order!! STR_NETWORK_ERROR_CLIENT_GENERAL :general error diff --git a/src/network/network.cpp b/src/network/network.cpp index d7cf9367a1..c31a67487d 100644 --- a/src/network/network.cpp +++ b/src/network/network.cpp @@ -602,8 +602,11 @@ static void NetworkInitialize(bool close_admins = true) /** Non blocking connection create to query servers */ class TCPQueryConnecter : TCPConnecter { +private: + bool request_company_info; + public: - TCPQueryConnecter(const NetworkAddress &address) : TCPConnecter(address) {} + TCPQueryConnecter(const NetworkAddress &address, bool request_company_info) : TCPConnecter(address), request_company_info(request_company_info) {} void OnFailure() override { @@ -613,36 +616,53 @@ public: void OnConnect(SOCKET s) override { _networking = true; - new ClientNetworkGameSocketHandler(s); - MyClient::SendInformationQuery(); + new ClientNetworkGameSocketHandler(s, address); + MyClient::SendInformationQuery(request_company_info); } }; /** * Query a server to fetch his game-info. * @param address the address to query. + * @param request_company_info Whether to request company info too. */ -void NetworkTCPQueryServer(NetworkAddress address) +void NetworkTCPQueryServer(NetworkAddress address, bool request_company_info) { if (!_network_available) return; NetworkDisconnect(); NetworkInitialize(); - new TCPQueryConnecter(address); + new TCPQueryConnecter(address, request_company_info); } /** * Validates an address entered as a string and adds the server to * the list. If you use this function, the games will be marked * as manually added. - * @param connection_string The IP:port to add to the list. + * @param connection_string The IP:port of the server to add. + * @return The entry on the game list. */ -void NetworkAddServer(const char *connection_string) +NetworkGameList *NetworkAddServer(const std::string &connection_string) { - if (StrEmpty(connection_string)) return; + if (connection_string.empty()) return nullptr; - NetworkUDPQueryServer(ParseConnectionString(connection_string, NETWORK_DEFAULT_PORT), true); + NetworkAddress address = ParseConnectionString(connection_string, NETWORK_DEFAULT_PORT); + + /* Ensure the item already exists in the list */ + NetworkGameList *item = NetworkGameListAddItem(address); + if (StrEmpty(item->info.server_name)) { + ClearGRFConfigList(&item->info.grfconfig); + address.GetAddressAsString(item->info.server_name, lastof(item->info.server_name)); + item->manually = true; + + NetworkRebuildHostList(); + UpdateNetworkGameWindow(); + } + + NetworkTCPQueryServer(address); + + return item; } /** @@ -687,7 +707,7 @@ public: void OnConnect(SOCKET s) override { _networking = true; - new ClientNetworkGameSocketHandler(s); + new ClientNetworkGameSocketHandler(s, this->address); IConsoleCmdExec("exec scripts/on_client.scr 0"); NetworkClient_Connected(); } @@ -1132,9 +1152,9 @@ void NetworkShutDown() #ifdef __EMSCRIPTEN__ extern "C" { -void CDECL em_openttd_add_server(const char *host, int port) +void CDECL em_openttd_add_server(const char *connection_string) { - NetworkUDPQueryServer(NetworkAddress(host, port), true); + NetworkAddServer(connection_string); } } diff --git a/src/network/network_client.cpp b/src/network/network_client.cpp index af7073e558..f73c8be528 100644 --- a/src/network/network_client.cpp +++ b/src/network/network_client.cpp @@ -145,7 +145,7 @@ void ClientNetworkEmergencySave() * Create a new socket for the client side of the game connection. * @param s The socket to connect with. */ -ClientNetworkGameSocketHandler::ClientNetworkGameSocketHandler(SOCKET s) : NetworkGameSocketHandler(s), savegame(nullptr), status(STATUS_INACTIVE) +ClientNetworkGameSocketHandler::ClientNetworkGameSocketHandler(SOCKET s, NetworkAddress address) : NetworkGameSocketHandler(s), address(address), savegame(nullptr), status(STATUS_INACTIVE) { assert(ClientNetworkGameSocketHandler::my_client == nullptr); ClientNetworkGameSocketHandler::my_client = this; @@ -345,14 +345,18 @@ static_assert(NETWORK_SERVER_ID_LENGTH == 16 * 2 + 1); /** * Query the server for server information. */ -NetworkRecvStatus ClientNetworkGameSocketHandler::SendInformationQuery() +NetworkRecvStatus ClientNetworkGameSocketHandler::SendInformationQuery(bool request_company_info) { - my_client->status = STATUS_COMPANY_INFO; - _network_join_status = NETWORK_JOIN_STATUS_GETTING_COMPANY_INFO; - SetWindowDirty(WC_NETWORK_STATUS_WINDOW, WN_NETWORK_STATUS_WINDOW_JOIN); - + my_client->status = STATUS_GAME_INFO; my_client->SendPacket(new Packet(PACKET_CLIENT_GAME_INFO)); - my_client->SendPacket(new Packet(PACKET_CLIENT_COMPANY_INFO)); + + if (request_company_info) { + my_client->status = STATUS_COMPANY_INFO; + _network_join_status = NETWORK_JOIN_STATUS_GETTING_COMPANY_INFO; + SetWindowDirty(WC_NETWORK_STATUS_WINDOW, WN_NETWORK_STATUS_WINDOW_JOIN); + + my_client->SendPacket(new Packet(PACKET_CLIENT_COMPANY_INFO)); + } return NETWORK_RECV_STATUS_OKAY; } @@ -577,9 +581,13 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_BANNED(Packet * NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_GAME_INFO(Packet *p) { - if (this->status != STATUS_COMPANY_INFO && this->status != STATUS_INACTIVE) return NETWORK_RECV_STATUS_MALFORMED_PACKET; + if (this->status != STATUS_COMPANY_INFO && this->status != STATUS_GAME_INFO) return NETWORK_RECV_STATUS_MALFORMED_PACKET; NetworkGameList *item = GetLobbyGameInfo(); + if (item == nullptr) { + /* This is not the lobby, so add it to the game list. */ + item = NetworkGameListAddItem(this->address); + } /* Clear any existing GRFConfig chain. */ ClearGRFConfigList(&item->info.grfconfig); @@ -590,6 +598,8 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_GAME_INFO(Packe /* Ensure we consider the server online. */ item->online = true; + /* It could be either window, but only one is open, so redraw both. */ + SetWindowDirty(WC_NETWORK_WINDOW, WN_NETWORK_WINDOW_GAME); SetWindowDirty(WC_NETWORK_WINDOW, WN_NETWORK_WINDOW_LOBBY); /* We will receive company info next, so keep connection open. */ @@ -727,6 +737,15 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_ERROR(Packet *p NetworkErrorCode error = (NetworkErrorCode)p->Recv_uint8(); + /* If we query a server that is 1.11.1 or older, we get an + * NETWORK_ERROR_NOT_EXPECTED on requesting the game info. Show a special + * error popup in that case. + */ + if (error == NETWORK_ERROR_NOT_EXPECTED && (this->status == STATUS_GAME_INFO || this->status == STATUS_COMPANY_INFO)) { + ShowErrorMessage(STR_NETWORK_ERROR_SERVER_TOO_OLD, INVALID_STRING_ID, WL_CRITICAL); + return NETWORK_RECV_STATUS_CLOSE_QUERY; + } + StringID err = STR_NETWORK_ERROR_LOSTCONNECTION; if (error < (ptrdiff_t)lengthof(network_error_strings)) err = network_error_strings[error]; /* In case of kicking a client, we assume there is a kick message in the packet if we can read one byte */ diff --git a/src/network/network_client.h b/src/network/network_client.h index 66cdb8ba57..40b8eedf92 100644 --- a/src/network/network_client.h +++ b/src/network/network_client.h @@ -15,12 +15,14 @@ /** Class for handling the client side of the game connection. */ class ClientNetworkGameSocketHandler : public ZeroedMemoryAllocator, public NetworkGameSocketHandler { private: + NetworkAddress address; ///< Address we are connected to. struct PacketReader *savegame; ///< Packet reader for reading the savegame. byte token; ///< The token we need to send back to the server to prove we're the right client. /** Status of the connection with the server. */ enum ServerStatus { STATUS_INACTIVE, ///< The client is not connected nor active. + STATUS_GAME_INFO, ///< We are trying to get the game information. STATUS_COMPANY_INFO, ///< We are trying to get company information. STATUS_JOIN, ///< We are trying to join a server. STATUS_NEWGRFS_CHECK, ///< Last action was checking NewGRFs. @@ -74,13 +76,13 @@ protected: static NetworkRecvStatus SendMapOk(); void CheckConnection(); public: - ClientNetworkGameSocketHandler(SOCKET s); + ClientNetworkGameSocketHandler(SOCKET s, NetworkAddress address); ~ClientNetworkGameSocketHandler(); NetworkRecvStatus CloseConnection(NetworkRecvStatus status) override; void ClientError(NetworkRecvStatus res); - static NetworkRecvStatus SendInformationQuery(); + static NetworkRecvStatus SendInformationQuery(bool request_company_info); static NetworkRecvStatus SendJoin(); static NetworkRecvStatus SendCommand(const CommandPacket *cp); diff --git a/src/network/network_gamelist.cpp b/src/network/network_gamelist.cpp index 6d4285c854..12dbbce615 100644 --- a/src/network/network_gamelist.cpp +++ b/src/network/network_gamelist.cpp @@ -69,15 +69,6 @@ static void NetworkGameListHandleDelayedInsert() */ NetworkGameList *NetworkGameListAddItem(NetworkAddress address) { - const char *hostname = address.GetHostname(); - - /* Do not query the 'any' address. */ - if (StrEmpty(hostname) || - strcmp(hostname, "0.0.0.0") == 0 || - strcmp(hostname, "::") == 0) { - return nullptr; - } - NetworkGameList *item, *prev_item; prev_item = nullptr; @@ -95,7 +86,6 @@ NetworkGameList *NetworkGameListAddItem(NetworkAddress address) } else { prev_item->next = item; } - DEBUG(net, 4, "[gamelist] added server to list"); UpdateNetworkGameWindow(); diff --git a/src/network/network_gui.cpp b/src/network/network_gui.cpp index 3204b2b962..590f2f5e09 100644 --- a/src/network/network_gui.cpp +++ b/src/network/network_gui.cpp @@ -472,9 +472,8 @@ public: EM_ASM(if (window["openttd_server_list"]) openttd_server_list()); #endif - this->last_joined = NetworkGameListAddItem(ParseConnectionString(_settings_client.network.last_joined, NETWORK_DEFAULT_PORT)); + this->last_joined = NetworkAddServer(_settings_client.network.last_joined); this->server = this->last_joined; - if (this->last_joined != nullptr) NetworkUDPQueryServer(this->last_joined->address); this->requery_timer.SetInterval(MILLISECONDS_PER_TICK); @@ -750,7 +749,7 @@ public: break; case WID_NG_REFRESH: // Refresh - if (this->server != nullptr) NetworkUDPQueryServer(this->server->address); + if (this->server != nullptr) NetworkTCPQueryServer(this->server->address); break; case WID_NG_NEWGRF: // NewGRF Settings @@ -971,7 +970,7 @@ void ShowNetworkGameWindow() first = false; /* Add all servers from the config file to our list. */ for (const auto &iter : _network_host_list) { - NetworkAddServer(iter.c_str()); + NetworkAddServer(iter); } } @@ -1485,7 +1484,7 @@ struct NetworkLobbyWindow : public Window { /* Clear the information so removed companies don't remain */ for (auto &company : this->company_info) company = {}; - NetworkTCPQueryServer(this->server->address); + NetworkTCPQueryServer(this->server->address, true); break; } } @@ -1555,7 +1554,7 @@ static void ShowNetworkLobbyWindow(NetworkGameList *ngl) strecpy(_settings_client.network.last_joined, ngl->address.GetAddressAsString(false).c_str(), lastof(_settings_client.network.last_joined)); - NetworkTCPQueryServer(ngl->address); + NetworkTCPQueryServer(ngl->address, true); new NetworkLobbyWindow(&_network_lobby_window_desc, ngl); } diff --git a/src/network/network_internal.h b/src/network/network_internal.h index ff3c8c22d2..683c954e82 100644 --- a/src/network/network_internal.h +++ b/src/network/network_internal.h @@ -87,10 +87,10 @@ extern uint8 _network_reconnect; extern CompanyMask _network_company_passworded; -void NetworkTCPQueryServer(NetworkAddress address); +void NetworkTCPQueryServer(NetworkAddress address, bool request_company_info = false); void GetBindAddresses(NetworkAddressList *addresses, uint16 port); -void NetworkAddServer(const char *b); +struct NetworkGameList *NetworkAddServer(const std::string &connection_string); void NetworkRebuildHostList(); void UpdateNetworkGameWindow(); From e162aff7a3d1dfcde1a9d02780235c617b6d4983 Mon Sep 17 00:00:00 2001 From: Patric Stout Date: Fri, 30 Apr 2021 12:05:54 +0200 Subject: [PATCH 209/268] Cleanup: remove weird left-over comment in yapf.hpp --- src/pathfinder/yapf/yapf.hpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/pathfinder/yapf/yapf.hpp b/src/pathfinder/yapf/yapf.hpp index 134c0dcf98..097aaf0f3a 100644 --- a/src/pathfinder/yapf/yapf.hpp +++ b/src/pathfinder/yapf/yapf.hpp @@ -15,9 +15,6 @@ #include "../pf_performance_timer.hpp" #include "yapf.h" -//#undef FORCEINLINE -//#define inline inline - #include "../../misc/fixedsizearray.hpp" #include "../../misc/array.hpp" #include "../../misc/hashtable.hpp" From 665a3928e2f07e6ee1faed1c8b08fe3a68fbc37a Mon Sep 17 00:00:00 2001 From: Patric Stout Date: Fri, 30 Apr 2021 11:57:37 +0200 Subject: [PATCH 210/268] Remove: performance measurements in YAPF YAPF was constantly measuring its performance, but only at certain debug-levels this information was shown. Now after years, I sincerely wonder if anyone still knows about this feature and who still use it. Especially with the new framerate window, this detailed performance is not as meaningful anymore as it once was. --- src/pathfinder/CMakeLists.txt | 1 - src/pathfinder/follow_track.hpp | 18 +++--- src/pathfinder/npf/npf.cpp | 1 + src/pathfinder/pf_performance_timer.hpp | 80 ------------------------- src/pathfinder/yapf/yapf.hpp | 1 - src/pathfinder/yapf/yapf_base.hpp | 38 ++++-------- src/pathfinder/yapf/yapf_costcache.hpp | 8 --- src/pathfinder/yapf/yapf_costrail.hpp | 8 +-- src/pathfinder/yapf/yapf_rail.cpp | 2 - 9 files changed, 20 insertions(+), 137 deletions(-) delete mode 100644 src/pathfinder/pf_performance_timer.hpp diff --git a/src/pathfinder/CMakeLists.txt b/src/pathfinder/CMakeLists.txt index 2e275706f2..0616371622 100644 --- a/src/pathfinder/CMakeLists.txt +++ b/src/pathfinder/CMakeLists.txt @@ -5,5 +5,4 @@ add_files( follow_track.hpp pathfinder_func.h pathfinder_type.h - pf_performance_timer.hpp ) diff --git a/src/pathfinder/follow_track.hpp b/src/pathfinder/follow_track.hpp index 9b95578fd5..7e5e0e39b7 100644 --- a/src/pathfinder/follow_track.hpp +++ b/src/pathfinder/follow_track.hpp @@ -18,7 +18,6 @@ #include "../tunnelbridge_map.h" #include "../depot_map.h" #include "pathfinder_func.h" -#include "pf_performance_timer.hpp" /** * Track follower helper template class (can serve pathfinders and vehicle @@ -49,34 +48,32 @@ struct CFollowTrackT bool m_is_station; ///< last turn passed station int m_tiles_skipped; ///< number of skipped tunnel or station tiles ErrorCode m_err; - CPerformanceTimer *m_pPerf; RailTypes m_railtypes; - inline CFollowTrackT(const VehicleType *v = nullptr, RailTypes railtype_override = INVALID_RAILTYPES, CPerformanceTimer *pPerf = nullptr) + inline CFollowTrackT(const VehicleType *v = nullptr, RailTypes railtype_override = INVALID_RAILTYPES) { - Init(v, railtype_override, pPerf); + Init(v, railtype_override); } - inline CFollowTrackT(Owner o, RailTypes railtype_override = INVALID_RAILTYPES, CPerformanceTimer *pPerf = nullptr) + inline CFollowTrackT(Owner o, RailTypes railtype_override = INVALID_RAILTYPES) { assert(IsRailTT()); m_veh = nullptr; - Init(o, railtype_override, pPerf); + Init(o, railtype_override); } - inline void Init(const VehicleType *v, RailTypes railtype_override, CPerformanceTimer *pPerf) + inline void Init(const VehicleType *v, RailTypes railtype_override) { assert(!IsRailTT() || (v != nullptr && v->type == VEH_TRAIN)); m_veh = v; - Init(v != nullptr ? v->owner : INVALID_OWNER, IsRailTT() && railtype_override == INVALID_RAILTYPES ? Train::From(v)->compatible_railtypes : railtype_override, pPerf); + Init(v != nullptr ? v->owner : INVALID_OWNER, IsRailTT() && railtype_override == INVALID_RAILTYPES ? Train::From(v)->compatible_railtypes : railtype_override); } - inline void Init(Owner o, RailTypes railtype_override, CPerformanceTimer *pPerf) + inline void Init(Owner o, RailTypes railtype_override) { assert(!IsRoadTT() || m_veh != nullptr); assert(!IsRailTT() || railtype_override != INVALID_RAILTYPES); m_veh_owner = o; - m_pPerf = pPerf; /* don't worry, all is inlined so compiler should remove unnecessary initializations */ m_old_tile = INVALID_TILE; m_old_td = INVALID_TRACKDIR; @@ -237,7 +234,6 @@ protected: /** stores track status (available trackdirs) for the new tile into m_new_td_bits */ inline bool QueryNewTileTrackStatus() { - CPerfStart perf(*m_pPerf); if (IsRailTT() && IsPlainRailTile(m_new_tile)) { m_new_td_bits = (TrackdirBits)(GetTrackBits(m_new_tile) * 0x101); } else if (IsRoadTT()) { diff --git a/src/pathfinder/npf/npf.cpp b/src/pathfinder/npf/npf.cpp index 315b43b0e7..0094521615 100644 --- a/src/pathfinder/npf/npf.cpp +++ b/src/pathfinder/npf/npf.cpp @@ -8,6 +8,7 @@ /** @file npf.cpp Implementation of the NPF pathfinder. */ #include "../../stdafx.h" +#include "../../debug.h" #include "../../network/network.h" #include "../../viewport_func.h" #include "../../ship.h" diff --git a/src/pathfinder/pf_performance_timer.hpp b/src/pathfinder/pf_performance_timer.hpp deleted file mode 100644 index 66ec9695f8..0000000000 --- a/src/pathfinder/pf_performance_timer.hpp +++ /dev/null @@ -1,80 +0,0 @@ -/* - * This file is part of OpenTTD. - * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. - * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see . - */ - -/** @file pf_performance_timer.hpp Performance timer for pathfinders. */ - -#ifndef PF_PERFORMANCE_TIMER_HPP -#define PF_PERFORMANCE_TIMER_HPP - -#include "../debug.h" - -struct CPerformanceTimer -{ - int64 m_start; - int64 m_acc; - - CPerformanceTimer() : m_start(0), m_acc(0) {} - - inline void Start() - { - m_start = QueryTime(); - } - - inline void Stop() - { - m_acc += QueryTime() - m_start; - } - - inline int Get(int64 coef) - { - return (int)(m_acc * coef / QueryFrequency()); - } - - inline int64 QueryTime() - { - return ottd_rdtsc(); - } - - inline int64 QueryFrequency() - { - return ((int64)2200 * 1000000); - } -}; - -struct CPerfStartReal -{ - CPerformanceTimer *m_pperf; - - inline CPerfStartReal(CPerformanceTimer& perf) : m_pperf(&perf) - { - if (m_pperf != nullptr) m_pperf->Start(); - } - - inline ~CPerfStartReal() - { - Stop(); - } - - inline void Stop() - { - if (m_pperf != nullptr) { - m_pperf->Stop(); - m_pperf = nullptr; - } - } -}; - -struct CPerfStartFake -{ - inline CPerfStartFake(CPerformanceTimer& perf) {} - inline ~CPerfStartFake() {} - inline void Stop() {} -}; - -typedef CPerfStartFake CPerfStart; - -#endif /* PF_PERFORMANCE_TIMER_HPP */ diff --git a/src/pathfinder/yapf/yapf.hpp b/src/pathfinder/yapf/yapf.hpp index 097aaf0f3a..36df4db951 100644 --- a/src/pathfinder/yapf/yapf.hpp +++ b/src/pathfinder/yapf/yapf.hpp @@ -12,7 +12,6 @@ #include "../../landscape.h" #include "../pathfinder_func.h" -#include "../pf_performance_timer.hpp" #include "yapf.h" #include "../../misc/fixedsizearray.hpp" diff --git a/src/pathfinder/yapf/yapf_base.hpp b/src/pathfinder/yapf/yapf_base.hpp index c04fa2af92..b34e40a673 100644 --- a/src/pathfinder/yapf/yapf_base.hpp +++ b/src/pathfinder/yapf/yapf_base.hpp @@ -13,8 +13,6 @@ #include "../../debug.h" #include "../../settings_type.h" -extern int _total_pf_time_us; - /** * CYapfBaseT - A-star type path finder base class. * Derive your own pathfinder from it. You must provide the following template argument: @@ -67,12 +65,6 @@ protected: int m_stats_cost_calcs; ///< stats - how many node's costs were calculated int m_stats_cache_hits; ///< stats - how many node's costs were reused from cache -public: - CPerformanceTimer m_perf_cost; ///< stats - total CPU time of this run - CPerformanceTimer m_perf_slope_cost; ///< stats - slope calculation CPU time - CPerformanceTimer m_perf_ts_cost; ///< stats - GetTrackStatus() CPU time - CPerformanceTimer m_perf_other_cost; ///< stats - other CPU time - public: int m_num_steps; ///< this is there for debugging purposes (hope it doesn't hurt) @@ -120,9 +112,6 @@ public: { m_veh = v; - CPerformanceTimer perf; - perf.Start(); - Yapf().PfSetStartupNodes(); bool bDestFound = true; @@ -150,25 +139,18 @@ public: bDestFound &= (m_pBestDestNode != nullptr); - perf.Stop(); - if (_debug_yapf_level >= 2) { - int t = perf.Get(1000000); - _total_pf_time_us += t; + if (_debug_yapf_level >= 3) { + UnitID veh_idx = (m_veh != nullptr) ? m_veh->unitnumber : 0; + char ttc = Yapf().TransportTypeChar(); + float cache_hit_ratio = (m_stats_cache_hits == 0) ? 0.0f : ((float)m_stats_cache_hits / (float)(m_stats_cache_hits + m_stats_cost_calcs) * 100.0f); + int cost = bDestFound ? m_pBestDestNode->m_cost : -1; + int dist = bDestFound ? m_pBestDestNode->m_estimate - m_pBestDestNode->m_cost : -1; - if (_debug_yapf_level >= 3) { - UnitID veh_idx = (m_veh != nullptr) ? m_veh->unitnumber : 0; - char ttc = Yapf().TransportTypeChar(); - float cache_hit_ratio = (m_stats_cache_hits == 0) ? 0.0f : ((float)m_stats_cache_hits / (float)(m_stats_cache_hits + m_stats_cost_calcs) * 100.0f); - int cost = bDestFound ? m_pBestDestNode->m_cost : -1; - int dist = bDestFound ? m_pBestDestNode->m_estimate - m_pBestDestNode->m_cost : -1; - - DEBUG(yapf, 3, "[YAPF%c]%c%4d- %d us - %d rounds - %d open - %d closed - CHR %4.1f%% - C %d D %d - c%d(sc%d, ts%d, o%d) -- ", - ttc, bDestFound ? '-' : '!', veh_idx, t, m_num_steps, m_nodes.OpenCount(), m_nodes.ClosedCount(), - cache_hit_ratio, cost, dist, m_perf_cost.Get(1000000), m_perf_slope_cost.Get(1000000), - m_perf_ts_cost.Get(1000000), m_perf_other_cost.Get(1000000) - ); - } + DEBUG(yapf, 3, "[YAPF%c]%c%4d- %d rounds - %d open - %d closed - CHR %4.1f%% - C %d D %d", + ttc, bDestFound ? '-' : '!', veh_idx, m_num_steps, m_nodes.OpenCount(), m_nodes.ClosedCount(), cache_hit_ratio, cost, dist + ); } + return bDestFound; } diff --git a/src/pathfinder/yapf/yapf_costcache.hpp b/src/pathfinder/yapf/yapf_costcache.hpp index c56c47b5f2..1b4d52cff2 100644 --- a/src/pathfinder/yapf/yapf_costcache.hpp +++ b/src/pathfinder/yapf/yapf_costcache.hpp @@ -182,16 +182,8 @@ protected: inline static Cache& stGetGlobalCache() { static int last_rail_change_counter = 0; - static Date last_date = 0; static Cache C; - /* some statistics */ - if (last_date != _date) { - last_date = _date; - DEBUG(yapf, 2, "Pf time today: %5d ms", _total_pf_time_us / 1000); - _total_pf_time_us = 0; - } - /* delete the cache sometimes... */ if (last_rail_change_counter != Cache::s_rail_change_counter) { last_rail_change_counter = Cache::s_rail_change_counter; diff --git a/src/pathfinder/yapf/yapf_costrail.hpp b/src/pathfinder/yapf/yapf_costrail.hpp index 6bed27cba6..b50300b618 100644 --- a/src/pathfinder/yapf/yapf_costrail.hpp +++ b/src/pathfinder/yapf/yapf_costrail.hpp @@ -86,7 +86,6 @@ protected: public: inline int SlopeCost(TileIndex tile, Trackdir td) { - CPerfStart perf_cost(Yapf().m_perf_slope_cost); if (!stSlopeCost(tile, td)) return 0; return Yapf().PfGetSettings().rail_slope_penalty; } @@ -172,7 +171,6 @@ public: { int cost = 0; /* if there is one-way signal in the opposite direction, then it is not our way */ - CPerfStart perf_cost(Yapf().m_perf_other_cost); if (IsTileType(tile, MP_RAILWAY)) { bool has_signal_against = HasSignalOnTrackdir(tile, ReverseTrackdir(trackdir)); bool has_signal_along = HasSignalOnTrackdir(tile, trackdir); @@ -275,8 +273,6 @@ public: assert(tf->m_new_tile == n.m_key.m_tile); assert((HasTrackdir(tf->m_new_td_bits, n.m_key.m_td))); - CPerfStart perf_cost(Yapf().m_perf_cost); - /* Does the node have some parent node? */ bool has_parent = (n.m_parent != nullptr); @@ -326,7 +322,7 @@ public: EndSegmentReasonBits end_segment_reason = ESRB_NONE; - TrackFollower tf_local(v, Yapf().GetCompatibleRailTypes(), &Yapf().m_perf_ts_cost); + TrackFollower tf_local(v, Yapf().GetCompatibleRailTypes()); if (!has_parent) { /* We will jump to the middle of the cost calculator assuming that segment cache is not used. */ @@ -484,7 +480,7 @@ no_entry_cost: // jump here at the beginning if the node has no parent (it is th /* Move to the next tile/trackdir. */ tf = &tf_local; - tf_local.Init(v, Yapf().GetCompatibleRailTypes(), &Yapf().m_perf_ts_cost); + tf_local.Init(v, Yapf().GetCompatibleRailTypes()); if (!tf_local.Follow(cur.tile, cur.td)) { assert(tf_local.m_err != TrackFollower::EC_NONE); diff --git a/src/pathfinder/yapf/yapf_rail.cpp b/src/pathfinder/yapf/yapf_rail.cpp index 3b6686f20d..41aa5fdbd8 100644 --- a/src/pathfinder/yapf/yapf_rail.cpp +++ b/src/pathfinder/yapf/yapf_rail.cpp @@ -34,8 +34,6 @@ template void DumpState(Tpf &pf1, Tpf &pf2) fclose(f2); } -int _total_pf_time_us = 0; - template class CYapfReserveTrack { From 3dbd6475fec5ebd63b18004bb94048460c4fb4a3 Mon Sep 17 00:00:00 2001 From: PeterN Date: Fri, 30 Apr 2021 12:03:07 +0100 Subject: [PATCH 211/268] Codechange: Use C++ features for train wagon overrides. (#9141) This removes the need for C-style array management and allows use of iterators to perform wagon override lookups. --- src/engine.cpp | 11 ----------- src/engine_base.h | 12 ++++++++---- src/newgrf_engine.cpp | 43 +++++-------------------------------------- src/newgrf_engine.h | 2 -- 4 files changed, 13 insertions(+), 55 deletions(-) diff --git a/src/engine.cpp b/src/engine.cpp index 60b0d42224..41cd9dba80 100644 --- a/src/engine.cpp +++ b/src/engine.cpp @@ -66,12 +66,6 @@ static_assert(lengthof(_orig_rail_vehicle_info) + lengthof(_orig_road_vehicle_in const uint EngineOverrideManager::NUM_DEFAULT_ENGINES = _engine_counts[VEH_TRAIN] + _engine_counts[VEH_ROAD] + _engine_counts[VEH_SHIP] + _engine_counts[VEH_AIRCRAFT]; -Engine::Engine() : - overrides_count(0), - overrides(nullptr) -{ -} - Engine::Engine(VehicleType type, EngineID base) { this->type = type; @@ -136,11 +130,6 @@ Engine::Engine(VehicleType type, EngineID base) } } -Engine::~Engine() -{ - UnloadWagonOverrides(this); -} - /** * Checks whether the engine is a valid (non-articulated part of an) engine. * @return true if enabled diff --git a/src/engine_base.h b/src/engine_base.h index 2e434b589e..7822111e77 100644 --- a/src/engine_base.h +++ b/src/engine_base.h @@ -15,6 +15,12 @@ #include "core/pool_type.hpp" #include "newgrf_commons.h" +struct WagonOverride { + std::vector engines; + CargoID cargo; + const SpriteGroup *group; +}; + typedef Pool EnginePool; extern EnginePool _engine_pool; @@ -56,13 +62,11 @@ struct Engine : EnginePool::PoolItem<&_engine_pool> { * evaluating callbacks. */ GRFFilePropsBase grf_prop; - uint16 overrides_count; - struct WagonOverride *overrides; + std::vector overrides; uint16 list_position; - Engine(); + Engine() {} Engine(VehicleType type, EngineID base); - ~Engine(); bool IsEnabled() const; /** diff --git a/src/newgrf_engine.cpp b/src/newgrf_engine.cpp index ee14098c28..cd12148558 100644 --- a/src/newgrf_engine.cpp +++ b/src/newgrf_engine.cpp @@ -26,62 +26,29 @@ #include "safeguards.h" -struct WagonOverride { - EngineID *train_id; - uint trains; - CargoID cargo; - const SpriteGroup *group; -}; - void SetWagonOverrideSprites(EngineID engine, CargoID cargo, const SpriteGroup *group, EngineID *train_id, uint trains) { Engine *e = Engine::Get(engine); - WagonOverride *wo; assert(cargo < NUM_CARGO + 2); // Include CT_DEFAULT and CT_PURCHASE pseudo cargoes. - e->overrides_count++; - e->overrides = ReallocT(e->overrides, e->overrides_count); - - wo = &e->overrides[e->overrides_count - 1]; + WagonOverride *wo = &e->overrides.emplace_back(); wo->group = group; wo->cargo = cargo; - wo->trains = trains; - wo->train_id = MallocT(trains); - memcpy(wo->train_id, train_id, trains * sizeof *train_id); + wo->engines.assign(train_id, train_id + trains); } const SpriteGroup *GetWagonOverrideSpriteSet(EngineID engine, CargoID cargo, EngineID overriding_engine) { const Engine *e = Engine::Get(engine); - for (uint i = 0; i < e->overrides_count; i++) { - const WagonOverride *wo = &e->overrides[i]; - - if (wo->cargo != cargo && wo->cargo != CT_DEFAULT) continue; - - for (uint j = 0; j < wo->trains; j++) { - if (wo->train_id[j] == overriding_engine) return wo->group; - } + for (const WagonOverride &wo : e->overrides) { + if (wo.cargo != cargo && wo.cargo != CT_DEFAULT) continue; + if (std::find(wo.engines.begin(), wo.engines.end(), overriding_engine) != wo.engines.end()) return wo.group; } return nullptr; } -/** - * Unload all wagon override sprite groups. - */ -void UnloadWagonOverrides(Engine *e) -{ - for (uint i = 0; i < e->overrides_count; i++) { - WagonOverride *wo = &e->overrides[i]; - free(wo->train_id); - } - free(e->overrides); - e->overrides_count = 0; - e->overrides = nullptr; -} - - void SetCustomEngineSprites(EngineID engine, byte cargo, const SpriteGroup *group) { Engine *e = Engine::Get(engine); diff --git a/src/newgrf_engine.h b/src/newgrf_engine.h index f830ff499d..90d755d3c4 100644 --- a/src/newgrf_engine.h +++ b/src/newgrf_engine.h @@ -116,8 +116,6 @@ enum VehicleTrigger { }; void TriggerVehicle(Vehicle *veh, VehicleTrigger trigger); -void UnloadWagonOverrides(Engine *e); - void AlterVehicleListOrder(EngineID engine, uint target); void CommitVehicleListOrderChanges(); From 5153e1b6e38a75ef6eeb71760941018139feedea Mon Sep 17 00:00:00 2001 From: Peter Nelson Date: Thu, 22 Apr 2021 01:18:22 +0100 Subject: [PATCH 212/268] Cleanup: Horizontal widget size is commonly width rather than length. --- src/widget.cpp | 6 +++--- src/widget_type.h | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/widget.cpp b/src/widget.cpp index 1e3afb1557..74a89eaeaf 100644 --- a/src/widget.cpp +++ b/src/widget.cpp @@ -1503,12 +1503,12 @@ void NWidgetVertical::AssignSizePosition(SizingType sizing, uint x, uint y, uint /** * Generic spacer widget. - * @param length Horizontal size of the spacer widget. + * @param width Horizontal size of the spacer widget. * @param height Vertical size of the spacer widget. */ -NWidgetSpacer::NWidgetSpacer(int length, int height) : NWidgetResizeBase(NWID_SPACER, 0, 0) +NWidgetSpacer::NWidgetSpacer(int width, int height) : NWidgetResizeBase(NWID_SPACER, 0, 0) { - this->SetMinimalSize(length, height); + this->SetMinimalSize(width, height); this->SetResize(0, 0); } diff --git a/src/widget_type.h b/src/widget_type.h index e1fd92f638..c4c46ce66c 100644 --- a/src/widget_type.h +++ b/src/widget_type.h @@ -543,7 +543,7 @@ private: */ class NWidgetSpacer : public NWidgetResizeBase { public: - NWidgetSpacer(int length, int height); + NWidgetSpacer(int width, int height); void SetupSmallestSize(Window *w, bool init_array) override; void FillNestedArray(NWidgetBase **array, uint length) override; From 4c6cca459a678a5f1970b3ee5f23141d7ed89528 Mon Sep 17 00:00:00 2001 From: Peter Nelson Date: Thu, 22 Apr 2021 01:35:51 +0100 Subject: [PATCH 213/268] Cleanup: Remove fairly redundant DrawDropdown() function. --- src/widget.cpp | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/src/widget.cpp b/src/widget.cpp index 74a89eaeaf..78da9457cc 100644 --- a/src/widget.cpp +++ b/src/widget.cpp @@ -623,19 +623,6 @@ static inline void DrawButtonDropdown(const Rect &r, Colours colour, bool clicke } } -/** - * Draw a dropdown #WWT_DROPDOWN widget. - * @param r Rectangle containing the widget. - * @param colour Background colour of the widget. - * @param clicked The widget is lowered. - * @param str Text of the button. - * @param align Alignment of the text. - */ -static inline void DrawDropdown(const Rect &r, Colours colour, bool clicked, StringID str, StringAlignment align) -{ - DrawButtonDropdown(r, colour, false, clicked, str, align); -} - /** * Paint all widgets of a window. */ @@ -2639,7 +2626,7 @@ void NWidgetLeaf::Draw(const Window *w) case WWT_DROPDOWN: if (this->index >= 0) w->SetStringParameters(this->index); - DrawDropdown(r, this->colour, clicked, this->widget_data, this->align); + DrawButtonDropdown(r, this->colour, false, clicked, this->widget_data, this->align); break; case NWID_BUTTON_DROPDOWN: From 4f93dd95e4251e3227a55ee125838cefce173ef3 Mon Sep 17 00:00:00 2001 From: Peter Nelson Date: Wed, 21 Apr 2021 23:55:16 +0100 Subject: [PATCH 214/268] Cleanup: Tidy up resize, fill and minimal size on widgets in town list window. --- src/town_gui.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/town_gui.cpp b/src/town_gui.cpp index 1a7085c964..0629490393 100644 --- a/src/town_gui.cpp +++ b/src/town_gui.cpp @@ -647,12 +647,12 @@ static const NWidgetPart _nested_town_directory_widgets[] = { NWidget(NWID_HORIZONTAL), NWidget(WWT_TEXTBTN, COLOUR_BROWN, WID_TD_SORT_ORDER), SetDataTip(STR_BUTTON_SORT_BY, STR_TOOLTIP_SORT_ORDER), NWidget(WWT_DROPDOWN, COLOUR_BROWN, WID_TD_SORT_CRITERIA), SetDataTip(STR_JUST_STRING, STR_TOOLTIP_SORT_CRITERIA), - NWidget(WWT_EDITBOX, COLOUR_BROWN, WID_TD_FILTER), SetFill(35, 12), SetDataTip(STR_LIST_FILTER_OSKTITLE, STR_LIST_FILTER_TOOLTIP), + NWidget(WWT_EDITBOX, COLOUR_BROWN, WID_TD_FILTER), SetFill(1, 0), SetDataTip(STR_LIST_FILTER_OSKTITLE, STR_LIST_FILTER_TOOLTIP), EndContainer(), NWidget(WWT_PANEL, COLOUR_BROWN, WID_TD_LIST), SetMinimalSize(196, 0), SetDataTip(0x0, STR_TOWN_DIRECTORY_LIST_TOOLTIP), - SetFill(1, 0), SetResize(0, 10), SetScrollbar(WID_TD_SCROLLBAR), EndContainer(), + SetFill(1, 0), SetResize(0, 1), SetScrollbar(WID_TD_SCROLLBAR), EndContainer(), NWidget(WWT_PANEL, COLOUR_BROWN), - NWidget(WWT_TEXT, COLOUR_BROWN, WID_TD_WORLD_POPULATION), SetPadding(2, 0, 0, 2), SetMinimalSize(196, 12), SetFill(1, 0), SetDataTip(STR_TOWN_POPULATION, STR_NULL), + NWidget(WWT_TEXT, COLOUR_BROWN, WID_TD_WORLD_POPULATION), SetPadding(2, 0, 2, 2), SetMinimalSize(196, 0), SetMinimalTextLines(1, 0), SetFill(1, 0), SetDataTip(STR_TOWN_POPULATION, STR_NULL), EndContainer(), EndContainer(), NWidget(NWID_VERTICAL), From 2efa390a7d2091b2c2cef4aa7f986fdcb24eac2a Mon Sep 17 00:00:00 2001 From: Peter Nelson Date: Thu, 22 Apr 2021 01:17:30 +0100 Subject: [PATCH 215/268] Codechange: Simplify calling of DrawCharCentered() --- src/gfx.cpp | 10 ++++++---- src/gfx_func.h | 2 +- src/osk_gui.cpp | 5 +---- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/src/gfx.cpp b/src/gfx.cpp index d540f4dfe0..d3c730be12 100644 --- a/src/gfx.cpp +++ b/src/gfx.cpp @@ -891,15 +891,17 @@ const char *GetCharAtPosition(const char *str, int x, FontSize start_fontsize) /** * Draw single character horizontally centered around (x,y) * @param c Character (glyph) to draw - * @param x X position to draw character - * @param y Y position to draw character + * @param r Rectangle to draw character within * @param colour Colour to use, for details see _string_colourmap in * table/palettes.h or docs/ottd-colourtext-palette.png or the enum TextColour in gfx_type.h */ -void DrawCharCentered(WChar c, int x, int y, TextColour colour) +void DrawCharCentered(WChar c, const Rect &r, TextColour colour) { SetColourRemap(colour); - GfxMainBlitter(GetGlyph(FS_NORMAL, c), x - GetCharacterWidth(FS_NORMAL, c) / 2, y, BM_COLOUR_REMAP); + GfxMainBlitter(GetGlyph(FS_NORMAL, c), + CenterBounds(r.left, r.right, GetCharacterWidth(FS_NORMAL, c)), + CenterBounds(r.top, r.bottom, FONT_HEIGHT_NORMAL), + BM_COLOUR_REMAP); } /** diff --git a/src/gfx_func.h b/src/gfx_func.h index a4db8a045a..18b1966f9b 100644 --- a/src/gfx_func.h +++ b/src/gfx_func.h @@ -96,7 +96,7 @@ int DrawString(int left, int right, int top, StringID str, TextColour colour = T int DrawStringMultiLine(int left, int right, int top, int bottom, const char *str, TextColour colour = TC_FROMSTRING, StringAlignment align = (SA_TOP | SA_LEFT), bool underline = false, FontSize fontsize = FS_NORMAL); int DrawStringMultiLine(int left, int right, int top, int bottom, StringID str, TextColour colour = TC_FROMSTRING, StringAlignment align = (SA_TOP | SA_LEFT), bool underline = false, FontSize fontsize = FS_NORMAL); -void DrawCharCentered(WChar c, int x, int y, TextColour colour); +void DrawCharCentered(WChar c, const Rect &r, TextColour colour); void GfxFillRect(int left, int top, int right, int bottom, int colour, FillRectMode mode = FILLRECT_OPAQUE); void GfxFillPolygon(const std::vector &shape, int colour, FillRectMode mode = FILLRECT_OPAQUE); diff --git a/src/osk_gui.cpp b/src/osk_gui.cpp index 60c9023b82..233bb1c7aa 100644 --- a/src/osk_gui.cpp +++ b/src/osk_gui.cpp @@ -103,10 +103,7 @@ struct OskWindow : public Window { if (widget < WID_OSK_LETTERS) return; widget -= WID_OSK_LETTERS; - DrawCharCentered(_keyboard[this->shift][widget], - r.left + (r.right - r.left) / 2, - r.top + (r.bottom - r.top - FONT_HEIGHT_NORMAL) / 2, - TC_BLACK); + DrawCharCentered(_keyboard[this->shift][widget], r, TC_BLACK); } void OnClick(Point pt, int widget, int click_count) override From f5569763c9f34a32f59257f61f9797d54ace7b21 Mon Sep 17 00:00:00 2001 From: Peter Nelson Date: Mon, 5 Apr 2021 20:28:00 +0100 Subject: [PATCH 216/268] Fix: Specify width when width is required instead of top. --- src/widget.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/widget.cpp b/src/widget.cpp index 78da9457cc..705b93a946 100644 --- a/src/widget.cpp +++ b/src/widget.cpp @@ -2280,19 +2280,19 @@ NWidgetLeaf::NWidgetLeaf(WidgetType tp, Colours colour, int index, uint32 data, case WWT_SHADEBOX: this->SetFill(0, 0); - this->SetMinimalSize(WD_SHADEBOX_TOP, WD_CAPTION_HEIGHT); + this->SetMinimalSize(WD_SHADEBOX_WIDTH, WD_CAPTION_HEIGHT); this->SetDataTip(STR_NULL, STR_TOOLTIP_SHADE); break; case WWT_DEBUGBOX: this->SetFill(0, 0); - this->SetMinimalSize(WD_DEBUGBOX_TOP, WD_CAPTION_HEIGHT); + this->SetMinimalSize(WD_DEBUGBOX_WIDTH, WD_CAPTION_HEIGHT); this->SetDataTip(STR_NULL, STR_TOOLTIP_DEBUG); break; case WWT_DEFSIZEBOX: this->SetFill(0, 0); - this->SetMinimalSize(WD_DEFSIZEBOX_TOP, WD_CAPTION_HEIGHT); + this->SetMinimalSize(WD_DEFSIZEBOX_WIDTH, WD_CAPTION_HEIGHT); this->SetDataTip(STR_NULL, STR_TOOLTIP_DEFSIZE); break; From 03a43b824711898fe2c9f401a48fe2420d0abc28 Mon Sep 17 00:00:00 2001 From: Peter Nelson Date: Thu, 8 Apr 2021 09:02:38 +0100 Subject: [PATCH 217/268] Cleanup: Call SetMinimalSize instead of setting min_y directly. --- src/widget.cpp | 5 +++-- src/widget_type.h | 12 ++++++------ 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/widget.cpp b/src/widget.cpp index 705b93a946..931b525d47 100644 --- a/src/widget.cpp +++ b/src/widget.cpp @@ -2268,7 +2268,8 @@ NWidgetLeaf::NWidgetLeaf(WidgetType tp, Colours colour, int index, uint32 data, case WWT_CAPTION: this->SetFill(1, 0); this->SetResize(1, 0); - this->min_y = WD_CAPTION_HEIGHT; + this->SetMinimalSize(0, WD_CAPTION_HEIGHT); + this->SetMinimalTextLines(1, WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM, FS_NORMAL); this->SetDataTip(data, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS); break; @@ -2310,7 +2311,7 @@ NWidgetLeaf::NWidgetLeaf(WidgetType tp, Colours colour, int index, uint32 data, case WWT_DROPDOWN: this->SetFill(0, 0); - this->min_y = WD_DROPDOWN_HEIGHT; + this->SetMinimalSize(0, WD_DROPDOWN_HEIGHT); this->SetAlignment(SA_TOP | SA_LEFT); break; diff --git a/src/widget_type.h b/src/widget_type.h index c4c46ce66c..077261ff26 100644 --- a/src/widget_type.h +++ b/src/widget_type.h @@ -251,7 +251,7 @@ public: void SetFill(uint fill_x, uint fill_y); void SetResize(uint resize_x, uint resize_y); - void AssignSizePosition(SizingType sizing, uint x, uint y, uint given_width, uint given_height, bool rtl); + void AssignSizePosition(SizingType sizing, uint x, uint y, uint given_width, uint given_height, bool rtl) override; uint min_x; ///< Minimal horizontal size of only this widget. uint min_y; ///< Minimal vertical size of only this widget. @@ -470,8 +470,8 @@ class NWidgetHorizontal : public NWidgetPIPContainer { public: NWidgetHorizontal(NWidContainerFlags flags = NC_NONE); - void SetupSmallestSize(Window *w, bool init_array); - void AssignSizePosition(SizingType sizing, uint x, uint y, uint given_width, uint given_height, bool rtl); + void SetupSmallestSize(Window *w, bool init_array) override; + void AssignSizePosition(SizingType sizing, uint x, uint y, uint given_width, uint given_height, bool rtl) override; }; /** @@ -482,7 +482,7 @@ class NWidgetHorizontalLTR : public NWidgetHorizontal { public: NWidgetHorizontalLTR(NWidContainerFlags flags = NC_NONE); - void AssignSizePosition(SizingType sizing, uint x, uint y, uint given_width, uint given_height, bool rtl); + void AssignSizePosition(SizingType sizing, uint x, uint y, uint given_width, uint given_height, bool rtl) override; }; /** @@ -493,8 +493,8 @@ class NWidgetVertical : public NWidgetPIPContainer { public: NWidgetVertical(NWidContainerFlags flags = NC_NONE); - void SetupSmallestSize(Window *w, bool init_array); - void AssignSizePosition(SizingType sizing, uint x, uint y, uint given_width, uint given_height, bool rtl); + void SetupSmallestSize(Window *w, bool init_array) override; + void AssignSizePosition(SizingType sizing, uint x, uint y, uint given_width, uint given_height, bool rtl) override; }; /** From 6fe5353da2a0d3d60f8375b28536213e67cd869c Mon Sep 17 00:00:00 2001 From: Peter Nelson Date: Thu, 22 Apr 2021 02:13:55 +0100 Subject: [PATCH 218/268] Cleanup: Set unchanging scrollbar properties in constructor. --- src/widget.cpp | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/src/widget.cpp b/src/widget.cpp index 931b525d47..aae1a51651 100644 --- a/src/widget.cpp +++ b/src/widget.cpp @@ -2108,6 +2108,22 @@ NWidgetScrollbar::NWidgetScrollbar(WidgetType tp, Colours colour, int index) : N { assert(tp == NWID_HSCROLLBAR || tp == NWID_VSCROLLBAR); this->SetIndex(index); + + switch (this->type) { + case NWID_HSCROLLBAR: + this->SetResize(1, 0); + this->SetFill(1, 0); + this->SetDataTip(0x0, STR_TOOLTIP_HSCROLL_BAR_SCROLLS_LIST); + break; + + case NWID_VSCROLLBAR: + this->SetResize(0, 1); + this->SetFill(0, 1); + this->SetDataTip(0x0, STR_TOOLTIP_VSCROLL_BAR_SCROLLS_LIST); + break; + + default: NOT_REACHED(); + } } void NWidgetScrollbar::SetupSmallestSize(Window *w, bool init_array) @@ -2122,16 +2138,10 @@ void NWidgetScrollbar::SetupSmallestSize(Window *w, bool init_array) switch (this->type) { case NWID_HSCROLLBAR: this->SetMinimalSize(NWidgetScrollbar::GetHorizontalDimension().width * 3, NWidgetScrollbar::GetHorizontalDimension().height); - this->SetResize(1, 0); - this->SetFill(1, 0); - this->SetDataTip(0x0, STR_TOOLTIP_HSCROLL_BAR_SCROLLS_LIST); break; case NWID_VSCROLLBAR: this->SetMinimalSize(NWidgetScrollbar::GetVerticalDimension().width, NWidgetScrollbar::GetVerticalDimension().height * 3); - this->SetResize(0, 1); - this->SetFill(0, 1); - this->SetDataTip(0x0, STR_TOOLTIP_VSCROLL_BAR_SCROLLS_LIST); break; default: NOT_REACHED(); From 4791ff28627eabe6322352428dc1b42344f545b9 Mon Sep 17 00:00:00 2001 From: Peter Nelson Date: Mon, 5 Apr 2021 18:43:12 +0100 Subject: [PATCH 219/268] Fix: Recalculate padding and minimum sizes when GUI or Font zoom is changed. --- src/gfx.cpp | 2 +- src/gfxinit.cpp | 2 +- src/network/network_gui.cpp | 4 +- src/newgrf_gui.cpp | 2 +- src/osk_gui.cpp | 2 +- src/settings.cpp | 2 +- src/settings_gui.cpp | 7 +-- src/video/video_driver.cpp | 2 +- src/widget.cpp | 90 ++++++++++++++++++++++++++++++++----- src/widget_type.h | 33 ++++++++++++-- src/widgets/dropdown.cpp | 2 +- src/window.cpp | 3 +- src/window_func.h | 6 ++- 13 files changed, 129 insertions(+), 28 deletions(-) diff --git a/src/gfx.cpp b/src/gfx.cpp index d3c730be12..1f4b40a54c 100644 --- a/src/gfx.cpp +++ b/src/gfx.cpp @@ -1285,7 +1285,7 @@ void LoadStringWidthTable(bool monospace) } } - ReInitAllWindows(); + ReInitAllWindows(false); } /** diff --git a/src/gfxinit.cpp b/src/gfxinit.cpp index 7e2c2cc625..b744eaa5ef 100644 --- a/src/gfxinit.cpp +++ b/src/gfxinit.cpp @@ -323,7 +323,7 @@ void CheckBlitter() ClearFontCache(); GfxClearSpriteCache(); - ReInitAllWindows(); + ReInitAllWindows(false); } /** Initialise and load all the sprites. */ diff --git a/src/network/network_gui.cpp b/src/network/network_gui.cpp index 590f2f5e09..b2feacc955 100644 --- a/src/network/network_gui.cpp +++ b/src/network/network_gui.cpp @@ -98,7 +98,9 @@ public: this->Add(new NWidgetLeaf(WWT_PUSHTXTBTN, COLOUR_WHITE, WID_NG_YEARS, STR_NETWORK_SERVER_LIST_YEARS_CAPTION, STR_NETWORK_SERVER_LIST_YEARS_CAPTION_TOOLTIP)); leaf = new NWidgetLeaf(WWT_PUSHTXTBTN, COLOUR_WHITE, WID_NG_INFO, STR_EMPTY, STR_NETWORK_SERVER_LIST_INFO_ICONS_TOOLTIP); - leaf->SetMinimalSize(14 + GetSpriteSize(SPR_LOCK).width + GetSpriteSize(SPR_BLOT).width + GetSpriteSize(SPR_FLAGS_BASE).width, 12); + leaf->SetMinimalSize(14 + GetSpriteSize(SPR_LOCK, nullptr, ZOOM_LVL_OUT_4X).width + + GetSpriteSize(SPR_BLOT, nullptr, ZOOM_LVL_OUT_4X).width + + GetSpriteSize(SPR_FLAGS_BASE, nullptr, ZOOM_LVL_OUT_4X).width, 12); leaf->SetFill(0, 1); this->Add(leaf); diff --git a/src/newgrf_gui.cpp b/src/newgrf_gui.cpp index 3465a94379..6316a7c4bf 100644 --- a/src/newgrf_gui.cpp +++ b/src/newgrf_gui.cpp @@ -1972,7 +1972,7 @@ static void NewGRFConfirmationCallback(Window *w, bool confirmed) w->InvalidateData(); - ReInitAllWindows(); + ReInitAllWindows(false); DeleteWindowByClass(WC_BUILD_OBJECT); } } diff --git a/src/osk_gui.cpp b/src/osk_gui.cpp index 233bb1c7aa..119f26eae9 100644 --- a/src/osk_gui.cpp +++ b/src/osk_gui.cpp @@ -237,7 +237,7 @@ static void AddKey(NWidgetHorizontal *hor, int height, int num_half, WidgetType hor->Add(spc); } NWidgetLeaf *leaf = new NWidgetLeaf(widtype, COLOUR_GREY, widnum, widdata, STR_NULL); - leaf->SetMinimalSize(ScaleGUITrad(key_width), height); + leaf->SetMinimalSizeAbsolute(ScaleGUITrad(key_width), height); hor->Add(leaf); } diff --git a/src/settings.cpp b/src/settings.cpp index 9b97107a34..c5819297fc 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -1172,7 +1172,7 @@ static bool InvalidateNewGRFChangeWindows(int32 p1) { InvalidateWindowClassesData(WC_SAVELOAD); DeleteWindowByClass(WC_GAME_OPTIONS); - ReInitAllWindows(); + ReInitAllWindows(_gui_zoom_cfg); return true; } diff --git a/src/settings_gui.cpp b/src/settings_gui.cpp index 52d900e6e8..47524bcfba 100644 --- a/src/settings_gui.cpp +++ b/src/settings_gui.cpp @@ -530,7 +530,7 @@ struct GameOptionsWindow : Window { case WID_GO_CURRENCY_DROPDOWN: // Currency if (index == CURRENCY_CUSTOM) ShowCustCurrency(); this->opt->locale.currency = index; - ReInitAllWindows(); + ReInitAllWindows(false); break; case WID_GO_AUTOSAVE_DROPDOWN: // Autosave options @@ -545,7 +545,7 @@ struct GameOptionsWindow : Window { ClearAllCachedNames(); UpdateAllVirtCoords(); CheckBlitter(); - ReInitAllWindows(); + ReInitAllWindows(false); break; case WID_GO_RESOLUTION_DROPDOWN: // Change resolution @@ -573,7 +573,7 @@ struct GameOptionsWindow : Window { UpdateCursorSize(); UpdateAllVirtCoords(); FixTitleGameZoom(); - ReInitAllWindows(); + ReInitAllWindows(true); } break; } @@ -587,6 +587,7 @@ struct GameOptionsWindow : Window { ClearFontCache(); LoadStringWidthTable(); UpdateAllVirtCoords(); + ReInitAllWindows(true); } break; } diff --git a/src/video/video_driver.cpp b/src/video/video_driver.cpp index eaff0b7414..d8fbe0400a 100644 --- a/src/video/video_driver.cpp +++ b/src/video/video_driver.cpp @@ -115,7 +115,7 @@ void VideoDriver::RealChangeBlitter(const char *repl_blitter) this->ClearSystemSprites(); ClearFontCache(); GfxClearSpriteCache(); - ReInitAllWindows(); + ReInitAllWindows(false); } void VideoDriver::Tick() diff --git a/src/widget.cpp b/src/widget.cpp index aae1a51651..68bc43d78f 100644 --- a/src/widget.cpp +++ b/src/widget.cpp @@ -822,6 +822,14 @@ NWidgetBase *NWidgetBase::GetWidgetOfType(WidgetType tp) return (this->type == tp) ? this : nullptr; } +void NWidgetBase::AdjustPaddingForZoom() +{ + this->padding_top = ScaleGUITrad(this->uz_padding_top); + this->padding_right = ScaleGUITrad(this->uz_padding_right); + this->padding_bottom = ScaleGUITrad(this->uz_padding_bottom); + this->padding_left = ScaleGUITrad(this->uz_padding_left); +} + /** * Constructor for resizable nested widgets. * @param tp Nested widget type. @@ -834,6 +842,15 @@ NWidgetResizeBase::NWidgetResizeBase(WidgetType tp, uint fill_x, uint fill_y) : this->fill_y = fill_y; } +void NWidgetResizeBase::AdjustPaddingForZoom() +{ + if (!this->absolute) { + this->min_x = ScaleGUITrad(this->uz_min_x); + this->min_y = std::max(ScaleGUITrad(this->uz_min_y), this->uz_text_lines * GetCharacterHeight(this->uz_text_size) + ScaleGUITrad(this->uz_text_spacing)); + } + NWidgetBase::AdjustPaddingForZoom(); +} + /** * Set minimal size of the widget. * @param min_x Horizontal minimal size of the widget. @@ -841,6 +858,20 @@ NWidgetResizeBase::NWidgetResizeBase(WidgetType tp, uint fill_x, uint fill_y) : */ void NWidgetResizeBase::SetMinimalSize(uint min_x, uint min_y) { + this->uz_min_x = std::max(this->uz_min_x, min_x); + this->uz_min_y = std::max(this->uz_min_y, min_y); + this->min_x = ScaleGUITrad(this->uz_min_x); + this->min_y = std::max(ScaleGUITrad(this->uz_min_y), this->uz_text_lines * GetCharacterHeight(this->uz_text_size) + ScaleGUITrad(this->uz_text_spacing)); +} + +/** + * Set absolute (post-scaling) minimal size of the widget. + * @param min_x Horizontal minimal size of the widget. + * @param min_y Vertical minimal size of the widget. + */ +void NWidgetResizeBase::SetMinimalSizeAbsolute(uint min_x, uint min_y) +{ + this->absolute = true; this->min_x = std::max(this->min_x, min_x); this->min_y = std::max(this->min_y, min_y); } @@ -853,7 +884,10 @@ void NWidgetResizeBase::SetMinimalSize(uint min_x, uint min_y) */ void NWidgetResizeBase::SetMinimalTextLines(uint8 min_lines, uint8 spacing, FontSize size) { - this->min_y = min_lines * GetCharacterHeight(size) + spacing; + this->uz_text_lines = min_lines; + this->uz_text_spacing = spacing; + this->uz_text_size = size; + this->min_y = std::max(ScaleGUITrad(this->uz_min_y), this->uz_text_lines * GetCharacterHeight(this->uz_text_size) + ScaleGUITrad(this->uz_text_spacing)); } /** @@ -991,6 +1025,14 @@ NWidgetBase *NWidgetContainer::GetWidgetOfType(WidgetType tp) return nullptr; } +void NWidgetContainer::AdjustPaddingForZoom() +{ + for (NWidgetBase *child_wid = this->head; child_wid != nullptr; child_wid = child_wid->next) { + child_wid->AdjustPaddingForZoom(); + } + NWidgetBase::AdjustPaddingForZoom(); +} + /** * Append widget \a wid to container. * @param wid Widget to append. @@ -1032,6 +1074,14 @@ void NWidgetStacked::SetIndex(int index) this->index = index; } +void NWidgetStacked::AdjustPaddingForZoom() +{ + for (NWidgetBase *child_wid = this->head; child_wid != nullptr; child_wid = child_wid->next) { + child_wid->AdjustPaddingForZoom(); + } + NWidgetContainer::AdjustPaddingForZoom(); +} + void NWidgetStacked::SetupSmallestSize(Window *w, bool init_array) { if (this->index >= 0 && init_array) { // Fill w->nested_array[] @@ -1145,6 +1195,14 @@ NWidgetPIPContainer::NWidgetPIPContainer(WidgetType tp, NWidContainerFlags flags this->flags = flags; } +void NWidgetPIPContainer::AdjustPaddingForZoom() +{ + this->pip_pre = ScaleGUITrad(this->uz_pip_pre); + this->pip_inter = ScaleGUITrad(this->uz_pip_inter); + this->pip_post = ScaleGUITrad(this->uz_pip_post); + NWidgetContainer::AdjustPaddingForZoom(); +} + /** * Set additional pre/inter/post space for the container. * @@ -1156,9 +1214,13 @@ NWidgetPIPContainer::NWidgetPIPContainer(WidgetType tp, NWidContainerFlags flags */ void NWidgetPIPContainer::SetPIP(uint8 pip_pre, uint8 pip_inter, uint8 pip_post) { - this->pip_pre = pip_pre; - this->pip_inter = pip_inter; - this->pip_post = pip_post; + this->uz_pip_pre = pip_pre; + this->uz_pip_inter = pip_inter; + this->uz_pip_post = pip_post; + + this->pip_pre = ScaleGUITrad(this->uz_pip_pre); + this->pip_inter = ScaleGUITrad(this->uz_pip_inter); + this->pip_post = ScaleGUITrad(this->uz_pip_post); } void NWidgetPIPContainer::Draw(const Window *w) @@ -1813,6 +1875,12 @@ void NWidgetBackground::SetPIP(uint8 pip_pre, uint8 pip_inter, uint8 pip_post) this->child->SetPIP(pip_pre, pip_inter, pip_post); } +void NWidgetBackground::AdjustPaddingForZoom() +{ + if (child != nullptr) child->AdjustPaddingForZoom(); + NWidgetCore::AdjustPaddingForZoom(); +} + void NWidgetBackground::SetupSmallestSize(Window *w, bool init_array) { if (init_array && this->index >= 0) { @@ -2137,11 +2205,11 @@ void NWidgetScrollbar::SetupSmallestSize(Window *w, bool init_array) switch (this->type) { case NWID_HSCROLLBAR: - this->SetMinimalSize(NWidgetScrollbar::GetHorizontalDimension().width * 3, NWidgetScrollbar::GetHorizontalDimension().height); + this->SetMinimalSizeAbsolute(NWidgetScrollbar::GetHorizontalDimension().width * 3, NWidgetScrollbar::GetHorizontalDimension().height); break; case NWID_VSCROLLBAR: - this->SetMinimalSize(NWidgetScrollbar::GetVerticalDimension().width, NWidgetScrollbar::GetVerticalDimension().height * 3); + this->SetMinimalSizeAbsolute(NWidgetScrollbar::GetVerticalDimension().width, NWidgetScrollbar::GetVerticalDimension().height * 3); break; default: NOT_REACHED(); @@ -2768,7 +2836,7 @@ static int MakeNWidget(const NWidgetPart *parts, int count, NWidgetBase **dest, NWidgetResizeBase *nwrb = dynamic_cast(*dest); if (nwrb != nullptr) { assert(parts->u.xy.x >= 0 && parts->u.xy.y >= 0); - nwrb->SetMinimalSize(ScaleGUITrad(parts->u.xy.x), ScaleGUITrad(parts->u.xy.y)); + nwrb->SetMinimalSize(parts->u.xy.x, parts->u.xy.y); } break; } @@ -2814,15 +2882,15 @@ static int MakeNWidget(const NWidgetPart *parts, int count, NWidgetBase **dest, } case WPT_PADDING: - if (*dest != nullptr) (*dest)->SetPadding(ScaleGUITrad(parts->u.padding.top), ScaleGUITrad(parts->u.padding.right), ScaleGUITrad(parts->u.padding.bottom), ScaleGUITrad(parts->u.padding.left)); + if (*dest != nullptr) (*dest)->SetPadding(parts->u.padding.top, parts->u.padding.right, parts->u.padding.bottom, parts->u.padding.left); break; case WPT_PIPSPACE: { NWidgetPIPContainer *nwc = dynamic_cast(*dest); - if (nwc != nullptr) nwc->SetPIP(ScaleGUITrad(parts->u.pip.pre), ScaleGUITrad(parts->u.pip.inter), ScaleGUITrad(parts->u.pip.post)); + if (nwc != nullptr) nwc->SetPIP(parts->u.pip.pre, parts->u.pip.inter, parts->u.pip.post); NWidgetBackground *nwb = dynamic_cast(*dest); - if (nwb != nullptr) nwb->SetPIP(ScaleGUITrad(parts->u.pip.pre), ScaleGUITrad(parts->u.pip.inter), ScaleGUITrad(parts->u.pip.post)); + if (nwb != nullptr) nwb->SetPIP(parts->u.pip.pre, parts->u.pip.inter, parts->u.pip.post); break; } @@ -3019,7 +3087,7 @@ NWidgetBase *MakeCompanyButtonRows(int *biggest_index, int widget_first, int wid NWidgetHorizontal *hor = nullptr; // Storage for buttons in one row. int hor_length = 0; - Dimension sprite_size = GetSpriteSize(SPR_COMPANY_ICON); + Dimension sprite_size = GetSpriteSize(SPR_COMPANY_ICON, nullptr, ZOOM_LVL_OUT_4X); sprite_size.width += WD_MATRIX_LEFT + WD_MATRIX_RIGHT; sprite_size.height += WD_MATRIX_TOP + WD_MATRIX_BOTTOM + 1; // 1 for the 'offset' of being pressed diff --git a/src/widget_type.h b/src/widget_type.h index 077261ff26..2007113565 100644 --- a/src/widget_type.h +++ b/src/widget_type.h @@ -127,6 +127,7 @@ class NWidgetBase : public ZeroedMemoryAllocator { public: NWidgetBase(WidgetType tp); + virtual void AdjustPaddingForZoom(); virtual void SetupSmallestSize(Window *w, bool init_array) = 0; virtual void AssignSizePosition(SizingType sizing, uint x, uint y, uint given_width, uint given_height, bool rtl) = 0; @@ -148,10 +149,11 @@ public: */ inline void SetPadding(uint8 top, uint8 right, uint8 bottom, uint8 left) { - this->padding_top = top; - this->padding_right = right; - this->padding_bottom = bottom; - this->padding_left = left; + this->uz_padding_top = top; + this->uz_padding_right = right; + this->uz_padding_bottom = bottom; + this->uz_padding_left = left; + this->AdjustPaddingForZoom(); } inline uint GetHorizontalStepSize(SizingType sizing) const; @@ -195,6 +197,11 @@ public: uint8 padding_bottom; ///< Paddings added to the bottom of the widget. Managed by parent container widget. uint8 padding_left; ///< Paddings added to the left of the widget. Managed by parent container widget. (parent container may swap this with padding_right for RTL) + uint8 uz_padding_top; ///< Unscaled top padding, for resize calculation. + uint8 uz_padding_right; ///< Unscaled right padding, for resize calculation. + uint8 uz_padding_bottom; ///< Unscaled bottom padding, for resize calculation. + uint8 uz_padding_left; ///< Unscaled left padding, for resize calculation. + protected: inline void StoreSizePosition(SizingType sizing, uint x, uint y, uint given_width, uint given_height); }; @@ -246,7 +253,9 @@ class NWidgetResizeBase : public NWidgetBase { public: NWidgetResizeBase(WidgetType tp, uint fill_x, uint fill_y); + void AdjustPaddingForZoom() override; void SetMinimalSize(uint min_x, uint min_y); + void SetMinimalSizeAbsolute(uint min_x, uint min_y); void SetMinimalTextLines(uint8 min_lines, uint8 spacing, FontSize size); void SetFill(uint fill_x, uint fill_y); void SetResize(uint resize_x, uint resize_y); @@ -255,6 +264,14 @@ public: uint min_x; ///< Minimal horizontal size of only this widget. uint min_y; ///< Minimal vertical size of only this widget. + + bool absolute; ///< Set if minimum size is fixed and should not be resized. + uint uz_min_x; ///< Unscaled Minimal horizontal size of only this widget. + uint uz_min_y; ///< Unscaled Minimal vertical size of only this widget. + + uint8 uz_text_lines; ///< 'Unscaled' text lines, stored for resize calculation. + uint8 uz_text_spacing; ///< 'Unscaled' text padding, stored for resize calculation. + FontSize uz_text_size; ///< 'Unscaled' font size, stored for resize calculation. }; /** Nested widget flags that affect display and interaction with 'real' widgets. */ @@ -385,6 +402,7 @@ public: NWidgetContainer(WidgetType tp); ~NWidgetContainer(); + void AdjustPaddingForZoom() override; void Add(NWidgetBase *wid); void FillNestedArray(NWidgetBase **array, uint length) override; @@ -423,6 +441,7 @@ public: void SetIndex(int index); + void AdjustPaddingForZoom() override; void SetupSmallestSize(Window *w, bool init_array) override; void AssignSizePosition(SizingType sizing, uint x, uint y, uint given_width, uint given_height, bool rtl) override; void FillNestedArray(NWidgetBase **array, uint length) override; @@ -450,6 +469,7 @@ class NWidgetPIPContainer : public NWidgetContainer { public: NWidgetPIPContainer(WidgetType tp, NWidContainerFlags flags = NC_NONE); + void AdjustPaddingForZoom() override; void SetPIP(uint8 pip_pre, uint8 pip_inter, uint8 pip_post); void Draw(const Window *w) override; @@ -460,6 +480,10 @@ protected: uint8 pip_pre; ///< Amount of space before first widget. uint8 pip_inter; ///< Amount of space between widgets. uint8 pip_post; ///< Amount of space after last widget. + + uint8 uz_pip_pre; ///< Unscaled space before first widget. + uint8 uz_pip_inter; ///< Unscaled space between widgets. + uint8 uz_pip_post; ///< Unscaled space after last widget. }; /** @@ -565,6 +589,7 @@ public: void Add(NWidgetBase *nwid); void SetPIP(uint8 pip_pre, uint8 pip_inter, uint8 pip_post); + void AdjustPaddingForZoom() override; void SetupSmallestSize(Window *w, bool init_array) override; void AssignSizePosition(SizingType sizing, uint x, uint y, uint given_width, uint given_height, bool rtl) override; diff --git a/src/widgets/dropdown.cpp b/src/widgets/dropdown.cpp index f5536f52f4..8f4a8634ee 100644 --- a/src/widgets/dropdown.cpp +++ b/src/widgets/dropdown.cpp @@ -153,7 +153,7 @@ struct DropdownWindow : Window { uint items_width = size.width - (scroll ? NWidgetScrollbar::GetVerticalDimension().width : 0); NWidgetCore *nwi = this->GetWidget(WID_DM_ITEMS); - nwi->SetMinimalSize(items_width, size.height + 4); + nwi->SetMinimalSizeAbsolute(items_width, size.height + 4); nwi->colour = wi_colour; nwi = this->GetWidget(WID_DM_SCROLL); diff --git a/src/window.cpp b/src/window.cpp index a82afc7cda..a409726002 100644 --- a/src/window.cpp +++ b/src/window.cpp @@ -3412,7 +3412,7 @@ void HideVitalWindows() } /** Re-initialize all windows. */ -void ReInitAllWindows() +void ReInitAllWindows(bool zoom_changed) { NWidgetLeaf::InvalidateDimensionCache(); // Reset cached sizes of several widgets. NWidgetScrollbar::InvalidateDimensionCache(); @@ -3421,6 +3421,7 @@ void ReInitAllWindows() InitDepotWindowBlockSizes(); for (Window *w : Window::IterateFromBack()) { + if (zoom_changed) w->nested_root->AdjustPaddingForZoom(); w->ReInit(); } diff --git a/src/window_func.h b/src/window_func.h index 20866b34d9..fba00944a5 100644 --- a/src/window_func.h +++ b/src/window_func.h @@ -43,7 +43,11 @@ void DeleteConstructionWindows(); void HideVitalWindows(); void ShowVitalWindows(); -void ReInitAllWindows(); +/** + * Re-initialize all windows. + * @param zoom_changed Set if windows are being re-initialized due to a zoom level changed. + */ +void ReInitAllWindows(bool zoom_changed); void SetWindowWidgetDirty(WindowClass cls, WindowNumber number, byte widget_index); void SetWindowDirty(WindowClass cls, WindowNumber number); From d32df00b5c1040f34a4ade8c2a6911853b3ea00a Mon Sep 17 00:00:00 2001 From: Peter Nelson Date: Sun, 18 Apr 2021 11:30:28 +0100 Subject: [PATCH 220/268] Codechange: No longer necessary to manually resize volume sliders. --- src/settings_gui.cpp | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/src/settings_gui.cpp b/src/settings_gui.cpp index 47524bcfba..34c073fe92 100644 --- a/src/settings_gui.cpp +++ b/src/settings_gui.cpp @@ -402,16 +402,6 @@ struct GameOptionsWindow : Window { } break; - case WID_GO_BASE_SFX_VOLUME: - case WID_GO_BASE_MUSIC_VOLUME: - size->width = ScaleGUITrad(67); - size->height = ScaleGUITrad(12); - resize->width = 0; - resize->height = 0; - fill->width = 0; - fill->height = 0; - break; - default: { int selected; DropDownList list = this->BuildDropDownList(widget, &selected); @@ -716,7 +706,7 @@ static const NWidgetPart _nested_game_options_widgets[] = { NWidget(NWID_HORIZONTAL), SetPIP(0, 30, 7), NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_GO_BASE_SFX_DROPDOWN), SetMinimalSize(150, 12), SetDataTip(STR_BLACK_RAW_STRING, STR_GAME_OPTIONS_BASE_SFX_TOOLTIP), NWidget(NWID_SPACER), SetMinimalSize(150, 12), SetFill(1, 0), - NWidget(WWT_EMPTY, COLOUR_GREY, WID_GO_BASE_SFX_VOLUME), SetMinimalSize(67, 12), SetMinimalTextLines(1, 0), SetDataTip(0x0, STR_MUSIC_TOOLTIP_DRAG_SLIDERS_TO_SET_MUSIC), + NWidget(WWT_EMPTY, COLOUR_GREY, WID_GO_BASE_SFX_VOLUME), SetMinimalSize(67, 12), SetFill(0, 0), SetDataTip(0x0, STR_MUSIC_TOOLTIP_DRAG_SLIDERS_TO_SET_MUSIC), EndContainer(), NWidget(WWT_TEXT, COLOUR_GREY, WID_GO_BASE_SFX_DESCRIPTION), SetMinimalSize(330, 0), SetDataTip(STR_EMPTY, STR_GAME_OPTIONS_BASE_SFX_DESCRIPTION_TOOLTIP), SetFill(1, 0), SetPadding(6, 0, 6, 0), NWidget(NWID_HORIZONTAL, NC_EQUALSIZE), SetPIP(7, 0, 7), @@ -730,7 +720,7 @@ static const NWidgetPart _nested_game_options_widgets[] = { NWidget(NWID_HORIZONTAL), SetPIP(0, 30, 7), NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_GO_BASE_MUSIC_DROPDOWN), SetMinimalSize(150, 12), SetDataTip(STR_BLACK_RAW_STRING, STR_GAME_OPTIONS_BASE_MUSIC_TOOLTIP), NWidget(WWT_TEXT, COLOUR_GREY, WID_GO_BASE_MUSIC_STATUS), SetMinimalSize(150, 12), SetDataTip(STR_EMPTY, STR_NULL), SetFill(1, 0), - NWidget(WWT_EMPTY, COLOUR_GREY, WID_GO_BASE_MUSIC_VOLUME), SetMinimalSize(67, 12), SetMinimalTextLines(1, 0), SetDataTip(0x0, STR_MUSIC_TOOLTIP_DRAG_SLIDERS_TO_SET_MUSIC), + NWidget(WWT_EMPTY, COLOUR_GREY, WID_GO_BASE_MUSIC_VOLUME), SetMinimalSize(67, 12), SetFill(0, 0), SetDataTip(0x0, STR_MUSIC_TOOLTIP_DRAG_SLIDERS_TO_SET_MUSIC), EndContainer(), NWidget(WWT_TEXT, COLOUR_GREY, WID_GO_BASE_MUSIC_DESCRIPTION), SetMinimalSize(330, 0), SetDataTip(STR_EMPTY, STR_GAME_OPTIONS_BASE_MUSIC_DESCRIPTION_TOOLTIP), SetFill(1, 0), SetPadding(6, 0, 6, 0), NWidget(NWID_HORIZONTAL, NC_EQUALSIZE), SetPIP(7, 0, 7), From 63cc340dc189d3613a8604c8287fc8e647d5cb01 Mon Sep 17 00:00:00 2001 From: Peter Nelson Date: Tue, 20 Apr 2021 11:49:34 +0100 Subject: [PATCH 221/268] Codechange: Apply minimum size to toolbar widgets --- src/toolbar_gui.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/toolbar_gui.cpp b/src/toolbar_gui.cpp index fc8b183470..4cbd13dbf7 100644 --- a/src/toolbar_gui.cpp +++ b/src/toolbar_gui.cpp @@ -2234,7 +2234,9 @@ static NWidgetBase *MakeMainToolbar(int *biggest_index) hor->Add(new NWidgetSpacer(0, 0)); break; } - hor->Add(new NWidgetLeaf(i == WID_TN_SAVE ? WWT_IMGBTN_2 : WWT_IMGBTN, COLOUR_GREY, i, toolbar_button_sprites[i], STR_TOOLBAR_TOOLTIP_PAUSE_GAME + i)); + NWidgetLeaf *leaf = new NWidgetLeaf(i == WID_TN_SAVE ? WWT_IMGBTN_2 : WWT_IMGBTN, COLOUR_GREY, i, toolbar_button_sprites[i], STR_TOOLBAR_TOOLTIP_PAUSE_GAME + i); + leaf->SetMinimalSize(20, 20); + hor->Add(leaf); } *biggest_index = std::max(*biggest_index, WID_TN_SWITCH_BAR); From 56a6f66903de5b999c686008b1d3243f0c61f5f4 Mon Sep 17 00:00:00 2001 From: Peter Nelson Date: Mon, 19 Apr 2021 09:29:49 +0100 Subject: [PATCH 222/268] Codechange: Use text lines instead of pixel height of font for link graph widgets. --- src/linkgraph/linkgraph_gui.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/linkgraph/linkgraph_gui.cpp b/src/linkgraph/linkgraph_gui.cpp index 2d715aaf58..e0986fdb59 100644 --- a/src/linkgraph/linkgraph_gui.cpp +++ b/src/linkgraph/linkgraph_gui.cpp @@ -383,7 +383,8 @@ NWidgetBase *MakeSaturationLegendLinkGraphGUI(int *biggest_index) NWidgetVertical *panel = new NWidgetVertical(NC_EQUALSIZE); for (uint i = 0; i < lengthof(LinkGraphOverlay::LINK_COLOURS); ++i) { NWidgetBackground * wid = new NWidgetBackground(WWT_PANEL, COLOUR_DARK_GREEN, i + WID_LGL_SATURATION_FIRST); - wid->SetMinimalSize(50, FONT_HEIGHT_SMALL); + wid->SetMinimalSize(50, 0); + wid->SetMinimalTextLines(1, 0, FS_SMALL); wid->SetFill(1, 1); wid->SetResize(0, 0); panel->Add(wid); @@ -403,14 +404,16 @@ NWidgetBase *MakeCargoesLegendLinkGraphGUI(int *biggest_index) row = new NWidgetHorizontal(NC_EQUALSIZE); } NWidgetBackground * wid = new NWidgetBackground(WWT_PANEL, COLOUR_GREY, i + WID_LGL_CARGO_FIRST); - wid->SetMinimalSize(25, FONT_HEIGHT_SMALL); + wid->SetMinimalSize(25, 0); + wid->SetMinimalTextLines(1, 0, FS_SMALL); wid->SetFill(1, 1); wid->SetResize(0, 0); row->Add(wid); } /* Fill up last row */ for (uint i = 0; i < 4 - (NUM_CARGO - 1) % 5; ++i) { - NWidgetSpacer *spc = new NWidgetSpacer(25, FONT_HEIGHT_SMALL); + NWidgetSpacer *spc = new NWidgetSpacer(25, 0); + spc->SetMinimalTextLines(1, 0, FS_SMALL); spc->SetFill(1, 1); spc->SetResize(0, 0); row->Add(spc); From 09206be05448a7900bfa1f83c0b87212110fd96e Mon Sep 17 00:00:00 2001 From: Peter Nelson Date: Thu, 22 Apr 2021 01:19:54 +0100 Subject: [PATCH 223/268] Fix: Use unscaled values for padding OSK --- src/osk_gui.cpp | 56 ++++++++++++++++++++++++------------------------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/src/osk_gui.cpp b/src/osk_gui.cpp index 119f26eae9..155343f041 100644 --- a/src/osk_gui.cpp +++ b/src/osk_gui.cpp @@ -212,10 +212,13 @@ struct OskWindow : public Window { static const int HALF_KEY_WIDTH = 7; // Width of 1/2 key in pixels. static const int INTER_KEY_SPACE = 2; // Number of pixels between two keys. +static const int TOP_KEY_PADDING = 2; // Vertical padding for the top row of keys. +static const int KEY_PADDING = 6; // Vertical padding for remaining key rows. + /** * Add a key widget to a row of the keyboard. * @param hor Row container to add key widget to. - * @param height Height of the key (all keys in a row should have equal height). + * @param pad_y Vertical padding of the key (all keys in a row should have equal padding). * @param num_half Number of 1/2 key widths that this key has. * @param widtype Widget type of the key. Must be either \c NWID_SPACER for an invisible key, or a \c WWT_* widget. * @param widnum Widget number of the key. @@ -223,21 +226,24 @@ static const int INTER_KEY_SPACE = 2; // Number of pixels between two keys. * @param biggest_index Collected biggest widget index so far. * @note Key width is measured in 1/2 keys to allow for 1/2 key shifting between rows. */ -static void AddKey(NWidgetHorizontal *hor, int height, int num_half, WidgetType widtype, int widnum, uint16 widdata, int *biggest_index) +static void AddKey(NWidgetHorizontal *hor, int pad_y, int num_half, WidgetType widtype, int widnum, uint16 widdata, int *biggest_index) { int key_width = HALF_KEY_WIDTH + (INTER_KEY_SPACE + HALF_KEY_WIDTH) * (num_half - 1); if (widtype == NWID_SPACER) { if (!hor->IsEmpty()) key_width += INTER_KEY_SPACE; - NWidgetSpacer *spc = new NWidgetSpacer(ScaleGUITrad(key_width), height); + NWidgetSpacer *spc = new NWidgetSpacer(key_width, 0); + spc->SetMinimalTextLines(1, pad_y, FS_NORMAL); hor->Add(spc); } else { if (!hor->IsEmpty()) { - NWidgetSpacer *spc = new NWidgetSpacer(ScaleGUITrad(INTER_KEY_SPACE), height); + NWidgetSpacer *spc = new NWidgetSpacer(INTER_KEY_SPACE, 0); + spc->SetMinimalTextLines(1, pad_y, FS_NORMAL); hor->Add(spc); } NWidgetLeaf *leaf = new NWidgetLeaf(widtype, COLOUR_GREY, widnum, widdata, STR_NULL); - leaf->SetMinimalSizeAbsolute(ScaleGUITrad(key_width), height); + leaf->SetMinimalSize(key_width, 0); + leaf->SetMinimalTextLines(1, pad_y, FS_NORMAL); hor->Add(leaf); } @@ -248,11 +254,10 @@ static void AddKey(NWidgetHorizontal *hor, int height, int num_half, WidgetType static NWidgetBase *MakeTopKeys(int *biggest_index) { NWidgetHorizontal *hor = new NWidgetHorizontal(); - int key_height = FONT_HEIGHT_NORMAL + 2; - AddKey(hor, key_height, 6 * 2, WWT_TEXTBTN, WID_OSK_CANCEL, STR_BUTTON_CANCEL, biggest_index); - AddKey(hor, key_height, 6 * 2, WWT_TEXTBTN, WID_OSK_OK, STR_BUTTON_OK, biggest_index); - AddKey(hor, key_height, 2 * 2, WWT_PUSHIMGBTN, WID_OSK_BACKSPACE, SPR_OSK_BACKSPACE, biggest_index); + AddKey(hor, TOP_KEY_PADDING, 6 * 2, WWT_TEXTBTN, WID_OSK_CANCEL, STR_BUTTON_CANCEL, biggest_index); + AddKey(hor, TOP_KEY_PADDING, 6 * 2, WWT_TEXTBTN, WID_OSK_OK, STR_BUTTON_OK, biggest_index); + AddKey(hor, TOP_KEY_PADDING, 2 * 2, WWT_PUSHIMGBTN, WID_OSK_BACKSPACE, SPR_OSK_BACKSPACE, biggest_index); return hor; } @@ -260,10 +265,9 @@ static NWidgetBase *MakeTopKeys(int *biggest_index) static NWidgetBase *MakeNumberKeys(int *biggest_index) { NWidgetHorizontal *hor = new NWidgetHorizontalLTR(); - int key_height = FONT_HEIGHT_NORMAL + 6; for (int widnum = WID_OSK_NUMBERS_FIRST; widnum <= WID_OSK_NUMBERS_LAST; widnum++) { - AddKey(hor, key_height, 2, WWT_PUSHBTN, widnum, 0x0, biggest_index); + AddKey(hor, KEY_PADDING, 2, WWT_PUSHBTN, widnum, 0x0, biggest_index); } return hor; } @@ -272,13 +276,12 @@ static NWidgetBase *MakeNumberKeys(int *biggest_index) static NWidgetBase *MakeQwertyKeys(int *biggest_index) { NWidgetHorizontal *hor = new NWidgetHorizontalLTR(); - int key_height = FONT_HEIGHT_NORMAL + 6; - AddKey(hor, key_height, 3, WWT_PUSHIMGBTN, WID_OSK_SPECIAL, SPR_OSK_SPECIAL, biggest_index); + AddKey(hor, KEY_PADDING, 3, WWT_PUSHIMGBTN, WID_OSK_SPECIAL, SPR_OSK_SPECIAL, biggest_index); for (int widnum = WID_OSK_QWERTY_FIRST; widnum <= WID_OSK_QWERTY_LAST; widnum++) { - AddKey(hor, key_height, 2, WWT_PUSHBTN, widnum, 0x0, biggest_index); + AddKey(hor, KEY_PADDING, 2, WWT_PUSHBTN, widnum, 0x0, biggest_index); } - AddKey(hor, key_height, 1, NWID_SPACER, 0, 0, biggest_index); + AddKey(hor, KEY_PADDING, 1, NWID_SPACER, 0, 0, biggest_index); return hor; } @@ -286,11 +289,10 @@ static NWidgetBase *MakeQwertyKeys(int *biggest_index) static NWidgetBase *MakeAsdfgKeys(int *biggest_index) { NWidgetHorizontal *hor = new NWidgetHorizontalLTR(); - int key_height = FONT_HEIGHT_NORMAL + 6; - AddKey(hor, key_height, 4, WWT_IMGBTN, WID_OSK_CAPS, SPR_OSK_CAPS, biggest_index); + AddKey(hor, KEY_PADDING, 4, WWT_IMGBTN, WID_OSK_CAPS, SPR_OSK_CAPS, biggest_index); for (int widnum = WID_OSK_ASDFG_FIRST; widnum <= WID_OSK_ASDFG_LAST; widnum++) { - AddKey(hor, key_height, 2, WWT_PUSHBTN, widnum, 0x0, biggest_index); + AddKey(hor, KEY_PADDING, 2, WWT_PUSHBTN, widnum, 0x0, biggest_index); } return hor; } @@ -299,13 +301,12 @@ static NWidgetBase *MakeAsdfgKeys(int *biggest_index) static NWidgetBase *MakeZxcvbKeys(int *biggest_index) { NWidgetHorizontal *hor = new NWidgetHorizontalLTR(); - int key_height = FONT_HEIGHT_NORMAL + 6; - AddKey(hor, key_height, 3, WWT_IMGBTN, WID_OSK_SHIFT, SPR_OSK_SHIFT, biggest_index); + AddKey(hor, KEY_PADDING, 3, WWT_IMGBTN, WID_OSK_SHIFT, SPR_OSK_SHIFT, biggest_index); for (int widnum = WID_OSK_ZXCVB_FIRST; widnum <= WID_OSK_ZXCVB_LAST; widnum++) { - AddKey(hor, key_height, 2, WWT_PUSHBTN, widnum, 0x0, biggest_index); + AddKey(hor, KEY_PADDING, 2, WWT_PUSHBTN, widnum, 0x0, biggest_index); } - AddKey(hor, key_height, 1, NWID_SPACER, 0, 0, biggest_index); + AddKey(hor, KEY_PADDING, 1, NWID_SPACER, 0, 0, biggest_index); return hor; } @@ -313,13 +314,12 @@ static NWidgetBase *MakeZxcvbKeys(int *biggest_index) static NWidgetBase *MakeSpacebarKeys(int *biggest_index) { NWidgetHorizontal *hor = new NWidgetHorizontal(); - int key_height = FONT_HEIGHT_NORMAL + 6; - AddKey(hor, key_height, 8, NWID_SPACER, 0, 0, biggest_index); - AddKey(hor, key_height, 13, WWT_PUSHTXTBTN, WID_OSK_SPACE, STR_EMPTY, biggest_index); - AddKey(hor, key_height, 3, NWID_SPACER, 0, 0, biggest_index); - AddKey(hor, key_height, 2, WWT_PUSHIMGBTN, WID_OSK_LEFT, SPR_OSK_LEFT, biggest_index); - AddKey(hor, key_height, 2, WWT_PUSHIMGBTN, WID_OSK_RIGHT, SPR_OSK_RIGHT, biggest_index); + AddKey(hor, KEY_PADDING, 8, NWID_SPACER, 0, 0, biggest_index); + AddKey(hor, KEY_PADDING, 13, WWT_PUSHTXTBTN, WID_OSK_SPACE, STR_EMPTY, biggest_index); + AddKey(hor, KEY_PADDING, 3, NWID_SPACER, 0, 0, biggest_index); + AddKey(hor, KEY_PADDING, 2, WWT_PUSHIMGBTN, WID_OSK_LEFT, SPR_OSK_LEFT, biggest_index); + AddKey(hor, KEY_PADDING, 2, WWT_PUSHIMGBTN, WID_OSK_RIGHT, SPR_OSK_RIGHT, biggest_index); return hor; } From 1df510c297636652d826c512b009015f3de3a3b8 Mon Sep 17 00:00:00 2001 From: Peter Nelson Date: Thu, 22 Apr 2021 18:21:14 +0100 Subject: [PATCH 224/268] Fix: Company Key window scaling. --- src/graph_gui.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/graph_gui.cpp b/src/graph_gui.cpp index 67399c327f..52b37d6779 100644 --- a/src/graph_gui.cpp +++ b/src/graph_gui.cpp @@ -21,6 +21,7 @@ #include "sortlist_type.h" #include "core/geometry_func.hpp" #include "currency.h" +#include "zoom_func.h" #include "widgets/graph_widget.h" @@ -65,11 +66,11 @@ struct GraphLegendWindow : Window { bool rtl = _current_text_dir == TD_RTL; Dimension d = GetSpriteSize(SPR_COMPANY_ICON); - DrawCompanyIcon(cid, rtl ? r.right - d.width - 2 : r.left + 2, r.top + (r.bottom - r.top - d.height) / 2); + DrawCompanyIcon(cid, rtl ? r.right - d.width - ScaleGUITrad(2) : r.left + ScaleGUITrad(2), CenterBounds(r.top, r.bottom, d.height)); SetDParam(0, cid); SetDParam(1, cid); - DrawString(r.left + (rtl ? (uint)WD_FRAMERECT_LEFT : (d.width + 4)), r.right - (rtl ? (d.width + 4) : (uint)WD_FRAMERECT_RIGHT), r.top + (r.bottom - r.top + 1 - FONT_HEIGHT_NORMAL) / 2, STR_COMPANY_NAME_COMPANY_NUM, HasBit(_legend_excluded_companies, cid) ? TC_BLACK : TC_WHITE); + DrawString(r.left + (rtl ? (uint)WD_FRAMERECT_LEFT : (d.width + ScaleGUITrad(4))), r.right - (rtl ? (d.width + ScaleGUITrad(4)) : (uint)WD_FRAMERECT_RIGHT), CenterBounds(r.top, r.bottom, FONT_HEIGHT_NORMAL), STR_COMPANY_NAME_COMPANY_NUM, HasBit(_legend_excluded_companies, cid) ? TC_BLACK : TC_WHITE); } void OnClick(Point pt, int widget, int click_count) override @@ -110,11 +111,12 @@ struct GraphLegendWindow : Window { static NWidgetBase *MakeNWidgetCompanyLines(int *biggest_index) { NWidgetVertical *vert = new NWidgetVertical(); - uint line_height = std::max(GetSpriteSize(SPR_COMPANY_ICON).height, FONT_HEIGHT_NORMAL) + WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM; + uint sprite_height = GetSpriteSize(SPR_COMPANY_ICON, nullptr, ZOOM_LVL_OUT_4X).height; for (int widnum = WID_GL_FIRST_COMPANY; widnum <= WID_GL_LAST_COMPANY; widnum++) { NWidgetBackground *panel = new NWidgetBackground(WWT_PANEL, COLOUR_BROWN, widnum); - panel->SetMinimalSize(246, line_height); + panel->SetMinimalSize(246, sprite_height); + panel->SetMinimalTextLines(1, WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM, FS_NORMAL); panel->SetFill(1, 0); panel->SetDataTip(0x0, STR_GRAPH_KEY_COMPANY_SELECTION_TOOLTIP); vert->Add(panel); @@ -137,6 +139,7 @@ static const NWidgetPart _nested_graph_legend_widgets[] = { NWidgetFunction(MakeNWidgetCompanyLines), NWidget(NWID_SPACER), SetMinimalSize(2, 0), EndContainer(), + NWidget(NWID_SPACER), SetMinimalSize(0, 2), EndContainer(), }; From af70195e448e1a934fe921efcb9450ead99cb49e Mon Sep 17 00:00:00 2001 From: Peter Nelson Date: Thu, 22 Apr 2021 19:07:20 +0100 Subject: [PATCH 225/268] Fix: Tidy up sizing of sprite aligner window sprite list. --- src/newgrf_debug_gui.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/newgrf_debug_gui.cpp b/src/newgrf_debug_gui.cpp index bd78deee97..3a5e05a77b 100644 --- a/src/newgrf_debug_gui.cpp +++ b/src/newgrf_debug_gui.cpp @@ -863,8 +863,9 @@ struct SpriteAlignerWindow : Window { size->height = ScaleGUITrad(200); break; case WID_SA_LIST: - resize->height = std::max(11, FONT_HEIGHT_NORMAL + 1); + resize->height = FONT_HEIGHT_NORMAL + WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM; resize->width = 1; + fill->height = resize->height; break; default: break; From 5434d63f914b38dbffa86fe0e5c651b79816f3f2 Mon Sep 17 00:00:00 2001 From: Peter Nelson Date: Thu, 22 Apr 2021 21:47:18 +0100 Subject: [PATCH 226/268] Fix: Scale smallmap legend 'blob' to fit text. --- src/smallmap_gui.cpp | 21 +++++++++++++-------- src/smallmap_gui.h | 2 +- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/src/smallmap_gui.cpp b/src/smallmap_gui.cpp index 0e7d313a67..b00cf0e7a9 100644 --- a/src/smallmap_gui.cpp +++ b/src/smallmap_gui.cpp @@ -22,6 +22,7 @@ #include "window_func.h" #include "company_base.h" #include "guitimer_func.h" +#include "zoom_func.h" #include "smallmap_gui.h" @@ -1177,8 +1178,11 @@ void SmallMapWindow::RebuildColourIndexIfNecessary() this->min_number_of_columns = std::max(this->min_number_of_columns, num_columns); } + /* Width of the legend blob. */ + this->legend_width = (FONT_HEIGHT_SMALL - ScaleFontTrad(1)) * 8 / 5; + /* The width of a column is the minimum width of all texts + the size of the blob + some spacing */ - this->column_width = min_width + LEGEND_BLOB_WIDTH + WD_FRAMERECT_LEFT + WD_FRAMERECT_RIGHT; + this->column_width = min_width + this->legend_width + WD_FRAMERECT_LEFT + WD_FRAMERECT_RIGHT; } /* virtual */ void SmallMapWindow::OnPaint() @@ -1216,11 +1220,12 @@ void SmallMapWindow::RebuildColourIndexIfNecessary() uint y = y_org; uint i = 0; // Row counter for industry legend. uint row_height = FONT_HEIGHT_SMALL; + int padding = ScaleFontTrad(1); - uint text_left = rtl ? 0 : LEGEND_BLOB_WIDTH + WD_FRAMERECT_LEFT; - uint text_right = this->column_width - 1 - (rtl ? LEGEND_BLOB_WIDTH + WD_FRAMERECT_RIGHT : 0); - uint blob_left = rtl ? this->column_width - 1 - LEGEND_BLOB_WIDTH : 0; - uint blob_right = rtl ? this->column_width - 1 : LEGEND_BLOB_WIDTH; + uint text_left = rtl ? 0 : this->legend_width + WD_FRAMERECT_LEFT; + uint text_right = this->column_width - padding - (rtl ? this->legend_width + WD_FRAMERECT_RIGHT : 0); + uint blob_left = rtl ? this->column_width - padding - this->legend_width : 0; + uint blob_right = rtl ? this->column_width - padding : this->legend_width; StringID string = STR_NULL; switch (this->map_type) { @@ -1272,7 +1277,7 @@ void SmallMapWindow::RebuildColourIndexIfNecessary() DrawString(x + text_left, x + text_right, y, string, TC_GREY); } else { DrawString(x + text_left, x + text_right, y, string, TC_BLACK); - GfxFillRect(x + blob_left, y + 1, x + blob_right, y + row_height - 1, PC_BLACK); // Outer border of the legend colour + GfxFillRect(x + blob_left, y + padding, x + blob_right, y + row_height - 1, PC_BLACK); // Outer border of the legend colour } break; } @@ -1281,11 +1286,11 @@ void SmallMapWindow::RebuildColourIndexIfNecessary() default: if (this->map_type == SMT_CONTOUR) SetDParam(0, tbl->height * TILE_HEIGHT_STEP); /* Anything that is not an industry or a company is using normal process */ - GfxFillRect(x + blob_left, y + 1, x + blob_right, y + row_height - 1, PC_BLACK); + GfxFillRect(x + blob_left, y + padding, x + blob_right, y + row_height - 1, PC_BLACK); DrawString(x + text_left, x + text_right, y, tbl->legend); break; } - GfxFillRect(x + blob_left + 1, y + 2, x + blob_right - 1, y + row_height - 2, legend_colour); // Legend colour + GfxFillRect(x + blob_left + 1, y + padding + 1, x + blob_right - 1, y + row_height - 2, legend_colour); // Legend colour y += row_height; } diff --git a/src/smallmap_gui.h b/src/smallmap_gui.h index f1b8ece731..e2fa9074d7 100644 --- a/src/smallmap_gui.h +++ b/src/smallmap_gui.h @@ -64,7 +64,6 @@ protected: static bool show_towns; ///< Display town names in the smallmap. static int map_height_limit; ///< Currently used/cached map height limit. - static const uint LEGEND_BLOB_WIDTH = 8; ///< Width of the coloured blob in front of a line text in the #WID_SM_LEGEND widget. static const uint INDUSTRY_MIN_NUMBER_OF_COLUMNS = 2; ///< Minimal number of columns in the #WID_SM_LEGEND widget for the #SMT_INDUSTRY legend. static const uint FORCE_REFRESH_PERIOD = 930; ///< map is redrawn after that many milliseconds. static const uint BLINK_PERIOD = 450; ///< highlight blinking interval in milliseconds. @@ -72,6 +71,7 @@ protected: uint min_number_of_columns; ///< Minimal number of columns in legends. uint min_number_of_fixed_rows; ///< Minimal number of rows in the legends for the fixed layouts only (all except #SMT_INDUSTRY). uint column_width; ///< Width of a column in the #WID_SM_LEGEND widget. + uint legend_width; ///< Width of legend 'blob'. int32 scroll_x; ///< Horizontal world coordinate of the base tile left of the top-left corner of the smallmap display. int32 scroll_y; ///< Vertical world coordinate of the base tile left of the top-left corner of the smallmap display. From 617e85cc65c8dae6dbdaad99856e36e094f16a94 Mon Sep 17 00:00:00 2001 From: Peter Nelson Date: Fri, 23 Apr 2021 00:49:11 +0100 Subject: [PATCH 227/268] Fix: Scale legend blobs in Fund new industry window. --- src/industry_gui.cpp | 37 ++++++++++++++++++++++++------------- 1 file changed, 24 insertions(+), 13 deletions(-) diff --git a/src/industry_gui.cpp b/src/industry_gui.cpp index cce96e6d63..85e90515aa 100644 --- a/src/industry_gui.cpp +++ b/src/industry_gui.cpp @@ -38,6 +38,7 @@ #include "widgets/dropdown_type.h" #include "widgets/industry_widget.h" #include "clear_map.h" +#include "zoom_func.h" #include "table/strings.h" @@ -283,9 +284,8 @@ class BuildIndustryWindow : public Window { IndustryType index[NUM_INDUSTRYTYPES + 1]; ///< Type of industry, in the order it was loaded bool enabled[NUM_INDUSTRYTYPES + 1]; ///< availability state, coming from CBID_INDUSTRY_PROBABILITY (if ever) Scrollbar *vscroll; + Dimension legend; ///< Dimension of the legend 'blob'. - /** The offset for the text in the matrix. */ - static const int MATRIX_TEXT_OFFSET = 17; /** The largest allowed minimum-width of the window, given in line heights */ static const int MAX_MINWIDTH_LINEHEIGHTS = 20; @@ -407,6 +407,10 @@ public: void OnInit() override { + /* Width of the legend blob -- slightly larger than the smallmap legend blob. */ + this->legend.height = FONT_HEIGHT_SMALL; + this->legend.width = this->legend.height * 8 / 5; + this->SetupArrays(); } @@ -419,8 +423,8 @@ public: if (this->index[i] == INVALID_INDUSTRYTYPE) continue; d = maxdim(d, GetStringBoundingBox(GetIndustrySpec(this->index[i])->name)); } - resize->height = FONT_HEIGHT_NORMAL + WD_MATRIX_TOP + WD_MATRIX_BOTTOM; - d.width += MATRIX_TEXT_OFFSET + padding.width; + resize->height = std::max(this->legend.height, FONT_HEIGHT_NORMAL) + WD_MATRIX_TOP + WD_MATRIX_BOTTOM; + d.width += this->legend.width + ScaleFontTrad(7) + padding.width; d.height = 5 * resize->height; *size = maxdim(*size, d); break; @@ -509,30 +513,37 @@ public: uint text_left, text_right, icon_left, icon_right; if (_current_text_dir == TD_RTL) { icon_right = r.right - WD_MATRIX_RIGHT; - icon_left = icon_right - 10; - text_right = icon_right - BuildIndustryWindow::MATRIX_TEXT_OFFSET; + icon_left = icon_right - this->legend.width; + text_right = icon_left - ScaleFontTrad(7); text_left = r.left + WD_MATRIX_LEFT; } else { icon_left = r.left + WD_MATRIX_LEFT; - icon_right = icon_left + 10; - text_left = icon_left + BuildIndustryWindow::MATRIX_TEXT_OFFSET; + icon_right = icon_left + this->legend.width; + text_left = icon_right + ScaleFontTrad(7); text_right = r.right - WD_MATRIX_RIGHT; } + /* Vertical offset for legend icon. */ + int icon_top = (this->resize.step_height - this->legend.height + 1) / 2; + int icon_bottom = icon_top + this->legend.height; + + int y = r.top; for (byte i = 0; i < this->vscroll->GetCapacity() && i + this->vscroll->GetPosition() < this->count; i++) { - int y = r.top + WD_MATRIX_TOP + i * this->resize.step_height; bool selected = this->selected_index == i + this->vscroll->GetPosition(); if (this->index[i + this->vscroll->GetPosition()] == INVALID_INDUSTRYTYPE) { - DrawString(text_left, text_right, y, STR_FUND_INDUSTRY_MANY_RANDOM_INDUSTRIES, selected ? TC_WHITE : TC_ORANGE); + DrawString(text_left, text_right, y + WD_MATRIX_TOP, STR_FUND_INDUSTRY_MANY_RANDOM_INDUSTRIES, selected ? TC_WHITE : TC_ORANGE); + y += this->resize.step_height; continue; } const IndustrySpec *indsp = GetIndustrySpec(this->index[i + this->vscroll->GetPosition()]); /* Draw the name of the industry in white is selected, otherwise, in orange */ - DrawString(text_left, text_right, y, indsp->name, selected ? TC_WHITE : TC_ORANGE); - GfxFillRect(icon_left, y + 1, icon_right, y + 7, selected ? PC_WHITE : PC_BLACK); - GfxFillRect(icon_left + 1, y + 2, icon_right - 1, y + 6, indsp->map_colour); + DrawString(text_left, text_right, y + WD_MATRIX_TOP, indsp->name, selected ? TC_WHITE : TC_ORANGE); + GfxFillRect(icon_left, y + icon_top, icon_right, y + icon_bottom, selected ? PC_WHITE : PC_BLACK); + GfxFillRect(icon_left + 1, y + icon_top + 1, icon_right - 1, y + icon_bottom - 1, indsp->map_colour); + + y += this->resize.step_height; } break; } From 957beaaefcaac0fa0b90a0a8354d418e65f53e19 Mon Sep 17 00:00:00 2001 From: Peter Nelson Date: Fri, 23 Apr 2021 10:18:44 +0100 Subject: [PATCH 228/268] Fix: Improved scaling and spacing of sign list window. Both company icon sprite and text now centred within each row, and extra padding added to avoid the sprites running into each other. --- src/signs_gui.cpp | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/signs_gui.cpp b/src/signs_gui.cpp index ea55488cc7..93c4dd4312 100644 --- a/src/signs_gui.cpp +++ b/src/signs_gui.cpp @@ -194,14 +194,16 @@ struct SignListWindow : Window, SignList { switch (widget) { case WID_SIL_LIST: { uint y = r.top + WD_FRAMERECT_TOP; // Offset from top of widget. + uint text_offset_y = (this->resize.step_height - FONT_HEIGHT_NORMAL + 1) / 2; /* No signs? */ if (this->vscroll->GetCount() == 0) { - DrawString(r.left + WD_FRAMETEXT_LEFT, r.right - WD_FRAMETEXT_RIGHT, y, STR_STATION_LIST_NONE); + DrawString(r.left + WD_FRAMETEXT_LEFT, r.right - WD_FRAMETEXT_RIGHT, y + text_offset_y, STR_STATION_LIST_NONE); return; } + Dimension d = GetSpriteSize(SPR_COMPANY_ICON); bool rtl = _current_text_dir == TD_RTL; - int sprite_offset_y = (FONT_HEIGHT_NORMAL - 10) / 2 + 1; + int sprite_offset_y = (this->resize.step_height - d.height + 1) / 2; uint icon_left = 4 + (rtl ? r.right - this->text_offset : r.left); uint text_left = r.left + (rtl ? WD_FRAMERECT_LEFT : this->text_offset); uint text_right = r.right - (rtl ? this->text_offset : WD_FRAMERECT_RIGHT); @@ -213,7 +215,7 @@ struct SignListWindow : Window, SignList { if (si->owner != OWNER_NONE) DrawCompanyIcon(si->owner, icon_left, y + sprite_offset_y); SetDParam(0, si->index); - DrawString(text_left, text_right, y, STR_SIGN_NAME, TC_YELLOW); + DrawString(text_left, text_right, y + text_offset_y, STR_SIGN_NAME, TC_YELLOW); y += this->resize.step_height; } break; @@ -264,7 +266,7 @@ struct SignListWindow : Window, SignList { case WID_SIL_LIST: { Dimension spr_dim = GetSpriteSize(SPR_COMPANY_ICON); this->text_offset = WD_FRAMETEXT_LEFT + spr_dim.width + 2; // 2 pixels space between icon and the sign text. - resize->height = std::max(FONT_HEIGHT_NORMAL, spr_dim.height); + resize->height = std::max(FONT_HEIGHT_NORMAL, spr_dim.height + 2); Dimension d = {(uint)(this->text_offset + WD_FRAMETEXT_RIGHT), WD_FRAMERECT_TOP + 5 * resize->height + WD_FRAMERECT_BOTTOM}; *size = maxdim(*size, d); break; @@ -365,8 +367,8 @@ static const NWidgetPart _nested_sign_list_widgets[] = { EndContainer(), NWidget(NWID_HORIZONTAL), NWidget(NWID_VERTICAL), - NWidget(WWT_PANEL, COLOUR_BROWN, WID_SIL_LIST), SetMinimalSize(WD_FRAMETEXT_LEFT + 16 + 255 + WD_FRAMETEXT_RIGHT, 50), - SetResize(1, 10), SetFill(1, 0), SetScrollbar(WID_SIL_SCROLLBAR), EndContainer(), + NWidget(WWT_PANEL, COLOUR_BROWN, WID_SIL_LIST), SetMinimalSize(WD_FRAMETEXT_LEFT + 16 + 255 + WD_FRAMETEXT_RIGHT, 0), + SetResize(1, 1), SetFill(1, 0), SetScrollbar(WID_SIL_SCROLLBAR), EndContainer(), NWidget(NWID_HORIZONTAL), NWidget(WWT_PANEL, COLOUR_BROWN), SetFill(1, 1), NWidget(WWT_EDITBOX, COLOUR_BROWN, WID_SIL_FILTER_TEXT), SetMinimalSize(80, 12), SetResize(1, 0), SetFill(1, 0), SetPadding(2, 2, 2, 2), From 254ffe9dccbba3ef3199439b6285b2cef6edc79c Mon Sep 17 00:00:00 2001 From: Peter Nelson Date: Fri, 23 Apr 2021 11:07:00 +0100 Subject: [PATCH 229/268] Fix: Scale industry chain legend blob by font size. --- src/industry_gui.cpp | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/src/industry_gui.cpp b/src/industry_gui.cpp index 85e90515aa..2188487453 100644 --- a/src/industry_gui.cpp +++ b/src/industry_gui.cpp @@ -1856,7 +1856,9 @@ struct CargoesField { static const int CARGO_STUB_WIDTH; static const int HOR_CARGO_WIDTH, HOR_CARGO_SPACE; static const int VERT_CARGO_SPACE, VERT_CARGO_EDGE; - static const int BLOB_DISTANCE, BLOB_WIDTH, BLOB_HEIGHT; + static const int BLOB_DISTANCE; + + static Dimension legend; static const int INDUSTRY_LINE_COLOUR; static const int CARGO_LINE_COLOUR; @@ -2060,13 +2062,13 @@ struct CargoesField { int blob_left, blob_right; if (_current_text_dir == TD_RTL) { blob_right = xpos2 - BLOB_DISTANCE; - blob_left = blob_right - BLOB_WIDTH; + blob_left = blob_right - CargoesField::legend.width; } else { blob_left = xpos + BLOB_DISTANCE; - blob_right = blob_left + BLOB_WIDTH; + blob_right = blob_left + CargoesField::legend.width; } - GfxFillRect(blob_left, ypos2 - BLOB_DISTANCE - BLOB_HEIGHT, blob_right, ypos2 - BLOB_DISTANCE, PC_BLACK); // Border - GfxFillRect(blob_left + 1, ypos2 - BLOB_DISTANCE - BLOB_HEIGHT + 1, blob_right - 1, ypos2 - BLOB_DISTANCE - 1, indsp->map_colour); + GfxFillRect(blob_left, ypos2 - BLOB_DISTANCE - CargoesField::legend.height, blob_right, ypos2 - BLOB_DISTANCE, PC_BLACK); // Border + GfxFillRect(blob_left + 1, ypos2 - BLOB_DISTANCE - CargoesField::legend.height + 1, blob_right - 1, ypos2 - BLOB_DISTANCE - 1, indsp->map_colour); } else { DrawString(xpos, xpos2, ypos, STR_INDUSTRY_CARGOES_HOUSES, TC_FROMSTRING, SA_HOR_CENTER); } @@ -2268,6 +2270,8 @@ private: static_assert(MAX_CARGOES >= cpp_lengthof(IndustrySpec, produced_cargo)); static_assert(MAX_CARGOES >= cpp_lengthof(IndustrySpec, accepts_cargo)); +Dimension CargoesField::legend; ///< Dimension of the legend blob. + int CargoesField::small_height; ///< Height of the header row. int CargoesField::normal_height; ///< Height of the non-header rows. int CargoesField::industry_width; ///< Width of an industry field. @@ -2283,8 +2287,6 @@ const int CargoesField::VERT_CARGO_EDGE = 4; ///< Amount of vertical spa const int CargoesField::VERT_CARGO_SPACE = 4; ///< Amount of vertical space between two connected cargoes at an industry. const int CargoesField::BLOB_DISTANCE = 5; ///< Distance of the industry legend colour from the edge of the industry box. -const int CargoesField::BLOB_WIDTH = 12; ///< Width of the industry legend colour, including border. -const int CargoesField::BLOB_HEIGHT = 9; ///< Height of the industry legend colour, including border const int CargoesField::INDUSTRY_LINE_COLOUR = PC_YELLOW; ///< Line colour of the industry type box. const int CargoesField::CARGO_LINE_COLOUR = PC_YELLOW; ///< Line colour around the cargo. @@ -2455,6 +2457,10 @@ struct IndustryCargoesWindow : public Window { d.height += WD_FRAMETEXT_TOP + WD_FRAMETEXT_BOTTOM; CargoesField::small_height = d.height; + /* Width of the legend blob -- slightly larger than the smallmap legend blob. */ + CargoesField::legend.height = FONT_HEIGHT_SMALL; + CargoesField::legend.width = CargoesField::legend.height * 8 / 5; + /* Decide about the size of the box holding the text of an industry type. */ this->ind_textsize.width = 0; this->ind_textsize.height = 0; From 055067c49cb916150912d59d827b7bd706bddcfa Mon Sep 17 00:00:00 2001 From: Peter Nelson Date: Fri, 23 Apr 2021 14:39:00 +0100 Subject: [PATCH 230/268] Fix: Scale cargo lines in industry chain window. Replaces constant pixel values with values scaled based on font size. This allows the industry chain to maintain a consistent look across different sizes. Previously all except cargo line height were fixed. --- src/industry_gui.cpp | 113 ++++++++++++++++++++++++------------------- 1 file changed, 63 insertions(+), 50 deletions(-) diff --git a/src/industry_gui.cpp b/src/industry_gui.cpp index 2188487453..aa4d86da6c 100644 --- a/src/industry_gui.cpp +++ b/src/industry_gui.cpp @@ -1852,13 +1852,13 @@ static const uint MAX_CARGOES = 16; ///< Maximum number of cargoes carried in a /** Data about a single field in the #IndustryCargoesWindow panel. */ struct CargoesField { static const int VERT_INTER_INDUSTRY_SPACE; - static const int HOR_CARGO_BORDER_SPACE; - static const int CARGO_STUB_WIDTH; - static const int HOR_CARGO_WIDTH, HOR_CARGO_SPACE; - static const int VERT_CARGO_SPACE, VERT_CARGO_EDGE; static const int BLOB_DISTANCE; static Dimension legend; + static Dimension cargo_border; + static Dimension cargo_line; + static Dimension cargo_space; + static Dimension cargo_stub; static const int INDUSTRY_LINE_COLOUR; static const int CARGO_LINE_COLOUR; @@ -2022,9 +2022,9 @@ struct CargoesField { int n = this->u.cargo.num_cargoes; if (n % 2 == 0) { - return xpos + cargo_field_width / 2 - (HOR_CARGO_WIDTH + HOR_CARGO_SPACE / 2) * (n / 2); + return xpos + cargo_field_width / 2 - (CargoesField::cargo_line.width + CargoesField::cargo_space.width / 2) * (n / 2); } else { - return xpos + cargo_field_width / 2 - HOR_CARGO_WIDTH / 2 - (HOR_CARGO_WIDTH + HOR_CARGO_SPACE) * (n / 2); + return xpos + cargo_field_width / 2 - CargoesField::cargo_line.width / 2 - (CargoesField::cargo_line.width + CargoesField::cargo_space.width) * (n / 2); } } @@ -2082,21 +2082,21 @@ struct CargoesField { other_right = this->u.industry.other_produced; other_left = this->u.industry.other_accepted; } - ypos1 += VERT_CARGO_EDGE; + ypos1 += CargoesField::cargo_border.height + (FONT_HEIGHT_NORMAL - CargoesField::cargo_line.height) / 2; for (uint i = 0; i < CargoesField::max_cargoes; i++) { if (other_right[i] != INVALID_CARGO) { const CargoSpec *csp = CargoSpec::Get(other_right[i]); - int xp = xpos + industry_width + CARGO_STUB_WIDTH; + int xp = xpos + industry_width + CargoesField::cargo_stub.width; DrawHorConnection(xpos + industry_width, xp - 1, ypos1, csp); - GfxDrawLine(xp, ypos1, xp, ypos1 + FONT_HEIGHT_NORMAL - 1, CARGO_LINE_COLOUR); + GfxDrawLine(xp, ypos1, xp, ypos1 + CargoesField::cargo_line.height - 1, CARGO_LINE_COLOUR); } if (other_left[i] != INVALID_CARGO) { const CargoSpec *csp = CargoSpec::Get(other_left[i]); - int xp = xpos - CARGO_STUB_WIDTH; + int xp = xpos - CargoesField::cargo_stub.width; DrawHorConnection(xp + 1, xpos - 1, ypos1, csp); - GfxDrawLine(xp, ypos1, xp, ypos1 + FONT_HEIGHT_NORMAL - 1, CARGO_LINE_COLOUR); + GfxDrawLine(xp, ypos1, xp, ypos1 + CargoesField::cargo_line.height - 1, CARGO_LINE_COLOUR); } - ypos1 += FONT_HEIGHT_NORMAL + VERT_CARGO_SPACE; + ypos1 += FONT_HEIGHT_NORMAL + CargoesField::cargo_space.height; } break; } @@ -2107,15 +2107,15 @@ struct CargoesField { int bot = ypos - (this->u.cargo.bottom_end ? VERT_INTER_INDUSTRY_SPACE / 2 + 1 : 0) + normal_height - 1; int colpos = cargo_base; for (int i = 0; i < this->u.cargo.num_cargoes; i++) { - if (this->u.cargo.top_end) GfxDrawLine(colpos, top - 1, colpos + HOR_CARGO_WIDTH - 1, top - 1, CARGO_LINE_COLOUR); - if (this->u.cargo.bottom_end) GfxDrawLine(colpos, bot + 1, colpos + HOR_CARGO_WIDTH - 1, bot + 1, CARGO_LINE_COLOUR); + if (this->u.cargo.top_end) GfxDrawLine(colpos, top - 1, colpos + CargoesField::cargo_line.width - 1, top - 1, CARGO_LINE_COLOUR); + if (this->u.cargo.bottom_end) GfxDrawLine(colpos, bot + 1, colpos + CargoesField::cargo_line.width - 1, bot + 1, CARGO_LINE_COLOUR); GfxDrawLine(colpos, top, colpos, bot, CARGO_LINE_COLOUR); colpos++; const CargoSpec *csp = CargoSpec::Get(this->u.cargo.vertical_cargoes[i]); - GfxFillRect(colpos, top, colpos + HOR_CARGO_WIDTH - 2, bot, csp->legend_colour, FILLRECT_OPAQUE); - colpos += HOR_CARGO_WIDTH - 2; + GfxFillRect(colpos, top, colpos + CargoesField::cargo_line.width - 2, bot, csp->legend_colour, FILLRECT_OPAQUE); + colpos += CargoesField::cargo_line.width - 2; GfxDrawLine(colpos, top, colpos, bot, CARGO_LINE_COLOUR); - colpos += 1 + HOR_CARGO_SPACE; + colpos += 1 + CargoesField::cargo_space.width; } const CargoID *hor_left, *hor_right; @@ -2126,15 +2126,15 @@ struct CargoesField { hor_left = this->u.cargo.supp_cargoes; hor_right = this->u.cargo.cust_cargoes; } - ypos += VERT_CARGO_EDGE + VERT_INTER_INDUSTRY_SPACE / 2; + ypos += CargoesField::cargo_border.height + VERT_INTER_INDUSTRY_SPACE / 2 + (FONT_HEIGHT_NORMAL - CargoesField::cargo_line.height) / 2; for (uint i = 0; i < MAX_CARGOES; i++) { if (hor_left[i] != INVALID_CARGO) { int col = hor_left[i]; int dx = 0; const CargoSpec *csp = CargoSpec::Get(this->u.cargo.vertical_cargoes[col]); for (; col > 0; col--) { - int lf = cargo_base + col * HOR_CARGO_WIDTH + (col - 1) * HOR_CARGO_SPACE; - DrawHorConnection(lf, lf + HOR_CARGO_SPACE - dx, ypos, csp); + int lf = cargo_base + col * CargoesField::cargo_line.width + (col - 1) * CargoesField::cargo_space.width; + DrawHorConnection(lf, lf + CargoesField::cargo_space.width - dx, ypos, csp); dx = 1; } DrawHorConnection(xpos, cargo_base - dx, ypos, csp); @@ -2144,26 +2144,26 @@ struct CargoesField { int dx = 0; const CargoSpec *csp = CargoSpec::Get(this->u.cargo.vertical_cargoes[col]); for (; col < this->u.cargo.num_cargoes - 1; col++) { - int lf = cargo_base + (col + 1) * HOR_CARGO_WIDTH + col * HOR_CARGO_SPACE; - DrawHorConnection(lf + dx - 1, lf + HOR_CARGO_SPACE - 1, ypos, csp); + int lf = cargo_base + (col + 1) * CargoesField::cargo_line.width + col * CargoesField::cargo_space.width; + DrawHorConnection(lf + dx - 1, lf + CargoesField::cargo_space.width - 1, ypos, csp); dx = 1; } - DrawHorConnection(cargo_base + col * HOR_CARGO_SPACE + (col + 1) * HOR_CARGO_WIDTH - 1 + dx, xpos + CargoesField::cargo_field_width - 1, ypos, csp); + DrawHorConnection(cargo_base + col * CargoesField::cargo_space.width + (col + 1) * CargoesField::cargo_line.width - 1 + dx, xpos + CargoesField::cargo_field_width - 1, ypos, csp); } - ypos += FONT_HEIGHT_NORMAL + VERT_CARGO_SPACE; + ypos += FONT_HEIGHT_NORMAL + CargoesField::cargo_space.height; } break; } case CFT_CARGO_LABEL: - ypos += VERT_CARGO_EDGE + VERT_INTER_INDUSTRY_SPACE / 2; + ypos += CargoesField::cargo_border.height + VERT_INTER_INDUSTRY_SPACE / 2; for (uint i = 0; i < MAX_CARGOES; i++) { if (this->u.cargo_label.cargoes[i] != INVALID_CARGO) { const CargoSpec *csp = CargoSpec::Get(this->u.cargo_label.cargoes[i]); DrawString(xpos + WD_FRAMERECT_LEFT, xpos + industry_width - 1 - WD_FRAMERECT_RIGHT, ypos, csp->name, TC_WHITE, (this->u.cargo_label.left_align) ? SA_LEFT : SA_RIGHT); } - ypos += FONT_HEIGHT_NORMAL + VERT_CARGO_SPACE; + ypos += FONT_HEIGHT_NORMAL + CargoesField::cargo_space.height; } break; @@ -2188,17 +2188,17 @@ struct CargoesField { uint col; for (col = 0; col < this->u.cargo.num_cargoes; col++) { if (pt.x < cpos) break; - if (pt.x < cpos + CargoesField::HOR_CARGO_WIDTH) return this->u.cargo.vertical_cargoes[col]; - cpos += CargoesField::HOR_CARGO_WIDTH + CargoesField::HOR_CARGO_SPACE; + if (pt.x < cpos + (int)CargoesField::cargo_line.width) return this->u.cargo.vertical_cargoes[col]; + cpos += CargoesField::cargo_line.width + CargoesField::cargo_space.width; } /* col = 0 -> left of first col, 1 -> left of 2nd col, ... this->u.cargo.num_cargoes right of last-col. */ - int vpos = VERT_INTER_INDUSTRY_SPACE / 2 + VERT_CARGO_EDGE; + int vpos = VERT_INTER_INDUSTRY_SPACE / 2 + CargoesField::cargo_border.width; uint row; for (row = 0; row < MAX_CARGOES; row++) { if (pt.y < vpos) return INVALID_CARGO; if (pt.y < vpos + FONT_HEIGHT_NORMAL) break; - vpos += FONT_HEIGHT_NORMAL + VERT_CARGO_SPACE; + vpos += FONT_HEIGHT_NORMAL + CargoesField::cargo_space.width; } if (row == MAX_CARGOES) return INVALID_CARGO; @@ -2240,12 +2240,12 @@ struct CargoesField { { assert(this->type == CFT_CARGO_LABEL); - int vpos = VERT_INTER_INDUSTRY_SPACE / 2 + VERT_CARGO_EDGE; + int vpos = VERT_INTER_INDUSTRY_SPACE / 2 + CargoesField::cargo_border.height; uint row; for (row = 0; row < MAX_CARGOES; row++) { if (pt.y < vpos) return INVALID_CARGO; if (pt.y < vpos + FONT_HEIGHT_NORMAL) break; - vpos += FONT_HEIGHT_NORMAL + VERT_CARGO_SPACE; + vpos += FONT_HEIGHT_NORMAL + CargoesField::cargo_space.height; } if (row == MAX_CARGOES) return INVALID_CARGO; return this->u.cargo_label.cargoes[row]; @@ -2262,15 +2262,19 @@ private: static void DrawHorConnection(int left, int right, int top, const CargoSpec *csp) { GfxDrawLine(left, top, right, top, CARGO_LINE_COLOUR); - GfxFillRect(left, top + 1, right, top + FONT_HEIGHT_NORMAL - 2, csp->legend_colour, FILLRECT_OPAQUE); - GfxDrawLine(left, top + FONT_HEIGHT_NORMAL - 1, right, top + FONT_HEIGHT_NORMAL - 1, CARGO_LINE_COLOUR); + GfxFillRect(left, top + 1, right, top + CargoesField::cargo_line.height - 2, csp->legend_colour, FILLRECT_OPAQUE); + GfxDrawLine(left, top + CargoesField::cargo_line.height - 1, right, top + CargoesField::cargo_line.height - 1, CARGO_LINE_COLOUR); } }; static_assert(MAX_CARGOES >= cpp_lengthof(IndustrySpec, produced_cargo)); static_assert(MAX_CARGOES >= cpp_lengthof(IndustrySpec, accepts_cargo)); -Dimension CargoesField::legend; ///< Dimension of the legend blob. +Dimension CargoesField::legend; ///< Dimension of the legend blob. +Dimension CargoesField::cargo_border; ///< Dimensions of border between cargo lines and industry boxes. +Dimension CargoesField::cargo_line; ///< Dimensions of cargo lines. +Dimension CargoesField::cargo_space; ///< Dimensions of space between cargo lines. +Dimension CargoesField::cargo_stub; ///< Dimensions of cargo stub (unconnected cargo line.) int CargoesField::small_height; ///< Height of the header row. int CargoesField::normal_height; ///< Height of the non-header rows. @@ -2279,13 +2283,6 @@ int CargoesField::cargo_field_width; ///< Width of a cargo field. uint CargoesField::max_cargoes; ///< Largest number of cargoes actually on any industry. const int CargoesField::VERT_INTER_INDUSTRY_SPACE = 6; ///< Amount of space between two industries in a column. -const int CargoesField::HOR_CARGO_BORDER_SPACE = 15; ///< Amount of space between the left/right edge of a #CFT_CARGO field, and the left/right most vertical cargo. -const int CargoesField::CARGO_STUB_WIDTH = 10; ///< Width of a cargo not carried in the column (should be less than #HOR_CARGO_BORDER_SPACE). -const int CargoesField::HOR_CARGO_WIDTH = 15; ///< Width of a vertical cargo column (inclusive the border line). -const int CargoesField::HOR_CARGO_SPACE = 5; ///< Amount of horizontal space between two vertical cargoes. -const int CargoesField::VERT_CARGO_EDGE = 4; ///< Amount of vertical space between top/bottom and the top/bottom connected cargo at an industry. -const int CargoesField::VERT_CARGO_SPACE = 4; ///< Amount of vertical space between two connected cargoes at an industry. - const int CargoesField::BLOB_DISTANCE = 5; ///< Distance of the industry legend colour from the edge of the industry box. const int CargoesField::INDUSTRY_LINE_COLOUR = PC_YELLOW; ///< Line colour of the industry type box. @@ -2457,10 +2454,26 @@ struct IndustryCargoesWindow : public Window { d.height += WD_FRAMETEXT_TOP + WD_FRAMETEXT_BOTTOM; CargoesField::small_height = d.height; - /* Width of the legend blob -- slightly larger than the smallmap legend blob. */ + /* Size of the legend blob -- slightly larger than the smallmap legend blob. */ CargoesField::legend.height = FONT_HEIGHT_SMALL; CargoesField::legend.width = CargoesField::legend.height * 8 / 5; + /* Size of cargo lines. */ + CargoesField::cargo_line.width = FONT_HEIGHT_NORMAL; + CargoesField::cargo_line.height = CargoesField::cargo_line.width; + + /* Size of border between cargo lines and industry boxes. */ + CargoesField::cargo_border.width = CargoesField::cargo_line.width * 3 / 2; + CargoesField::cargo_border.height = CargoesField::cargo_line.width / 2; + + /* Size of space between cargo lines. */ + CargoesField::cargo_space.width = CargoesField::cargo_line.width / 2; + CargoesField::cargo_space.height = CargoesField::cargo_line.height / 2; + + /* Size of cargo stub (unconnected cargo line.) */ + CargoesField::cargo_stub.width = CargoesField::cargo_line.width / 2; + CargoesField::cargo_stub.height = CargoesField::cargo_line.height; /* Unused */ + /* Decide about the size of the box holding the text of an industry type. */ this->ind_textsize.width = 0; this->ind_textsize.height = 0; @@ -2489,21 +2502,23 @@ struct IndustryCargoesWindow : public Window { d.width += 2 * HOR_TEXT_PADDING; /* Ensure the height is enough for the industry type text, for the horizontal connections, and for the cargo labels. */ - uint min_ind_height = CargoesField::VERT_CARGO_EDGE * 2 + CargoesField::max_cargoes * FONT_HEIGHT_NORMAL + (CargoesField::max_cargoes - 1) * CargoesField::VERT_CARGO_SPACE; + uint min_ind_height = CargoesField::cargo_border.height * 2 + CargoesField::max_cargoes * FONT_HEIGHT_NORMAL + (CargoesField::max_cargoes - 1) * CargoesField::cargo_space.height; d.height = std::max(d.height + 2 * VERT_TEXT_PADDING, min_ind_height); CargoesField::industry_width = d.width; CargoesField::normal_height = d.height + CargoesField::VERT_INTER_INDUSTRY_SPACE; /* Width of a #CFT_CARGO field. */ - CargoesField::cargo_field_width = CargoesField::HOR_CARGO_BORDER_SPACE * 2 + CargoesField::HOR_CARGO_WIDTH * CargoesField::max_cargoes + CargoesField::HOR_CARGO_SPACE * (CargoesField::max_cargoes - 1); + CargoesField::cargo_field_width = CargoesField::cargo_border.width * 2 + CargoesField::cargo_line.width * CargoesField::max_cargoes + CargoesField::cargo_space.width * (CargoesField::max_cargoes - 1); } void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize) override { switch (widget) { case WID_IC_PANEL: + resize->height = CargoesField::normal_height; size->width = WD_FRAMETEXT_LEFT + CargoesField::industry_width * 3 + CargoesField::cargo_field_width * 2 + WD_FRAMETEXT_RIGHT; + size->height = WD_FRAMETEXT_TOP + CargoesField::small_height + 2 * resize->height + WD_FRAMETEXT_BOTTOM; break; case WID_IC_IND_DROPDOWN: @@ -2756,8 +2771,7 @@ struct IndustryCargoesWindow : public Window { this->ShortenCargoColumn(1, 1, num_indrows); this->ShortenCargoColumn(3, 1, num_indrows); - const NWidgetBase *nwp = this->GetWidget(WID_IC_PANEL); - this->vscroll->SetCount(CeilDiv(WD_FRAMETEXT_TOP + WD_FRAMETEXT_BOTTOM + CargoesField::small_height + num_indrows * CargoesField::normal_height, nwp->resize_y)); + this->vscroll->SetCount(num_indrows); this->SetDirty(); this->NotifySmallmap(); } @@ -2824,8 +2838,7 @@ struct IndustryCargoesWindow : public Window { } this->ShortenCargoColumn(1, 1, num_indrows); - const NWidgetBase *nwp = this->GetWidget(WID_IC_PANEL); - this->vscroll->SetCount(CeilDiv(WD_FRAMETEXT_TOP + WD_FRAMETEXT_BOTTOM + CargoesField::small_height + num_indrows * CargoesField::normal_height, nwp->resize_y)); + this->vscroll->SetCount(num_indrows); this->SetDirty(); this->NotifySmallmap(); } @@ -3072,7 +3085,7 @@ struct IndustryCargoesWindow : public Window { void OnResize() override { - this->vscroll->SetCapacityFromWidget(this, WID_IC_PANEL); + this->vscroll->SetCapacityFromWidget(this, WID_IC_PANEL, WD_FRAMERECT_TOP + CargoesField::small_height); } }; From 49aa3924404615269986e7040455827fe7263001 Mon Sep 17 00:00:00 2001 From: Peter Nelson Date: Sun, 25 Apr 2021 13:48:00 +0100 Subject: [PATCH 231/268] Fix: Cargo legend blob in cargo payment rate window did not rescale. --- src/graph_gui.cpp | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/src/graph_gui.cpp b/src/graph_gui.cpp index 52b37d6779..c50027718a 100644 --- a/src/graph_gui.cpp +++ b/src/graph_gui.cpp @@ -879,6 +879,7 @@ void ShowCompanyValueGraph() struct PaymentRatesGraphWindow : BaseGraphWindow { uint line_height; ///< Pixel height of each cargo type row. Scrollbar *vscroll; ///< Cargo list scrollbar. + uint legend_width; ///< Width of legend 'blob'. PaymentRatesGraphWindow(WindowDesc *desc, WindowNumber window_number) : BaseGraphWindow(desc, WID_CPR_GRAPH, STR_JUST_CURRENCY_SHORT) @@ -899,6 +900,12 @@ struct PaymentRatesGraphWindow : BaseGraphWindow { this->FinishInitNested(window_number); } + void OnInit() override + { + /* Width of the legend blob. */ + this->legend_width = (FONT_HEIGHT_SMALL - ScaleFontTrad(1)) * 8 / 5; + } + void UpdateExcludedData() { this->excluded_data = 0; @@ -922,7 +929,7 @@ struct PaymentRatesGraphWindow : BaseGraphWindow { FOR_ALL_SORTED_STANDARD_CARGOSPECS(cs) { SetDParam(0, cs->name); Dimension d = GetStringBoundingBox(STR_GRAPH_CARGO_PAYMENT_CARGO); - d.width += 14; // colour field + d.width += this->legend_width + 4; // colour field d.width += WD_FRAMERECT_LEFT + WD_FRAMERECT_RIGHT; d.height += WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM; *size = maxdim(d, *size); @@ -945,6 +952,8 @@ struct PaymentRatesGraphWindow : BaseGraphWindow { int x = r.left + WD_FRAMERECT_LEFT; int y = r.top; + uint row_height = FONT_HEIGHT_SMALL; + int padding = ScaleFontTrad(1); int pos = this->vscroll->GetPosition(); int max = pos + this->vscroll->GetCapacity(); @@ -960,12 +969,12 @@ struct PaymentRatesGraphWindow : BaseGraphWindow { if (lowered) DrawFrameRect(r.left, y, r.right, y + this->line_height - 1, COLOUR_BROWN, lowered ? FR_LOWERED : FR_NONE); byte clk_dif = lowered ? 1 : 0; - int rect_x = clk_dif + (rtl ? r.right - 12 : r.left + WD_FRAMERECT_LEFT); + int rect_x = clk_dif + (rtl ? r.right - this->legend_width - WD_FRAMERECT_RIGHT : r.left + WD_FRAMERECT_LEFT); - GfxFillRect(rect_x, y + clk_dif, rect_x + 8, y + 5 + clk_dif, PC_BLACK); - GfxFillRect(rect_x + 1, y + 1 + clk_dif, rect_x + 7, y + 4 + clk_dif, cs->legend_colour); + GfxFillRect(rect_x, y + padding + clk_dif, rect_x + this->legend_width, y + row_height - 1 + clk_dif, PC_BLACK); + GfxFillRect(rect_x + 1, y + padding + 1 + clk_dif, rect_x + this->legend_width - 1, y + row_height - 2 + clk_dif, cs->legend_colour); SetDParam(0, cs->name); - DrawString(rtl ? r.left : x + 14 + clk_dif, (rtl ? r.right - 14 + clk_dif : r.right), y + clk_dif, STR_GRAPH_CARGO_PAYMENT_CARGO); + DrawString(rtl ? r.left : x + this->legend_width + 4 + clk_dif, (rtl ? r.right - this->legend_width - 4 + clk_dif : r.right), y + clk_dif, STR_GRAPH_CARGO_PAYMENT_CARGO); y += this->line_height; } From ce55cd0ce703acb3a731f7d2d783fb3462fab6c8 Mon Sep 17 00:00:00 2001 From: Peter Nelson Date: Sat, 1 May 2021 08:48:29 +0100 Subject: [PATCH 232/268] Cleanup: Use GetDefaultFontHeight() call instead of direct access. This makes this part of font size setup in FreeTypeFontCache consist with OSX and Windows variants. --- src/fontcache.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/fontcache.cpp b/src/fontcache.cpp index d450deaca6..1290777c0f 100644 --- a/src/fontcache.cpp +++ b/src/fontcache.cpp @@ -428,14 +428,14 @@ void FreeTypeFontCache::SetFontSize(FontSize fs, FT_Face face, int pixels) { if (pixels == 0) { /* Try to determine a good height based on the minimal height recommended by the font. */ - int scaled_height = ScaleFontTrad(_default_font_height[this->fs]); + int scaled_height = ScaleFontTrad(this->GetDefaultFontHeight(this->fs)); pixels = scaled_height; TT_Header *head = (TT_Header *)FT_Get_Sfnt_Table(this->face, ft_sfnt_head); if (head != nullptr) { /* Font height is minimum height plus the difference between the default * height for this font size and the small size. */ - int diff = scaled_height - ScaleFontTrad(_default_font_height[FS_SMALL]); + int diff = scaled_height - ScaleFontTrad(this->GetDefaultFontHeight(FS_SMALL)); pixels = Clamp(std::min(head->Lowest_Rec_PPEM, MAX_FONT_MIN_REC_SIZE) + diff, scaled_height, MAX_FONT_SIZE); } } else { From 9c6c0a0966f79996dfb7b5c7097c7d6c04a8d35c Mon Sep 17 00:00:00 2001 From: Peter Nelson Date: Sat, 1 May 2021 08:54:05 +0100 Subject: [PATCH 233/268] Codechange: Scale sprite font height once on init instead of every call to GetHeight(). Scaling is not expensive, but it does not change either, and this avoids the need for a virtual method call. This cascades back to all GetCharacterHeight(FS_xxx) and FONT_HEIGHT_xxx calls. --- src/fontcache.cpp | 8 ++------ src/fontcache.h | 2 +- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/src/fontcache.cpp b/src/fontcache.cpp index 1290777c0f..ce15233b7e 100644 --- a/src/fontcache.cpp +++ b/src/fontcache.cpp @@ -87,7 +87,6 @@ public: virtual void ClearFontCache(); virtual const Sprite *GetGlyph(GlyphID key); virtual uint GetGlyphWidth(GlyphID key); - virtual int GetHeight() const; virtual bool GetDrawGlyphShadow(); virtual GlyphID MapCharToGlyph(WChar key) { assert(IsPrintable(key)); return SPRITE_GLYPH | key; } virtual const void *GetFontTable(uint32 tag, size_t &length) { length = 0; return nullptr; } @@ -102,6 +101,7 @@ public: SpriteFontCache::SpriteFontCache(FontSize fs) : FontCache(fs), glyph_to_spriteid_map(nullptr) { this->InitializeUnicodeGlyphMap(); + this->height = ScaleFontTrad(this->GetDefaultFontHeight(this->fs)); } /** @@ -177,6 +177,7 @@ void SpriteFontCache::ClearGlyphToSpriteMap() void SpriteFontCache::ClearFontCache() { Layouter::ResetFontCache(this->fs); + this->height = ScaleFontTrad(this->GetDefaultFontHeight(this->fs)); } const Sprite *SpriteFontCache::GetGlyph(GlyphID key) @@ -193,11 +194,6 @@ uint SpriteFontCache::GetGlyphWidth(GlyphID key) return SpriteExists(sprite) ? GetSprite(sprite, ST_FONT)->width + ScaleFontTrad(this->fs != FS_NORMAL ? 1 : 0) : 0; } -int SpriteFontCache::GetHeight() const -{ - return ScaleFontTrad(this->height); -} - bool SpriteFontCache::GetDrawGlyphShadow() { return false; diff --git a/src/fontcache.h b/src/fontcache.h index 73675cf44e..c3b20cb4f7 100644 --- a/src/fontcache.h +++ b/src/fontcache.h @@ -45,7 +45,7 @@ public: * Get the height of the font. * @return The height of the font. */ - virtual int GetHeight() const { return this->height; } + inline int GetHeight() const { return this->height; } /** * Get the ascender value of the font. From 40528db993e6883f7856c8d860ec0385e42e3bad Mon Sep 17 00:00:00 2001 From: rubidium42 Date: Sat, 1 May 2021 10:46:47 +0200 Subject: [PATCH 234/268] Fix #9152, Fix #9153: screenshot command showed error messages when successful --- src/screenshot.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/screenshot.cpp b/src/screenshot.cpp index 06fae707a7..dc76996fbf 100644 --- a/src/screenshot.cpp +++ b/src/screenshot.cpp @@ -957,7 +957,7 @@ bool MakeScreenshot(ScreenshotType t, const char *name, uint32 width, uint32 hei if (t == SC_HEIGHTMAP) { SetDParamStr(0, _screenshot_name); SetDParam(1, _heightmap_highest_peak); - ShowErrorMessage(STR_MESSAGE_HEIGHTMAP_SUCCESSFULLY, INVALID_STRING_ID, WL_CRITICAL); + ShowErrorMessage(STR_MESSAGE_HEIGHTMAP_SUCCESSFULLY, INVALID_STRING_ID, WL_WARNING); } else { SetDParamStr(0, _screenshot_name); ShowErrorMessage(STR_MESSAGE_SCREENSHOT_SUCCESSFULLY, INVALID_STRING_ID, WL_WARNING); From 2985277becdfd390d6116afab1fb2ada0d7940b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Guilloux?= Date: Sat, 1 May 2021 12:11:07 +0200 Subject: [PATCH 235/268] Fix d4f0b6f4: [CMake] CMAKE_PROJECT_VERSION_XXX are not in CMake 3.9 (#9154) --- CMakeLists.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 87d055047f..ecfbe408c6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -74,9 +74,9 @@ add_custom_target(find_version ${CMAKE_COMMAND} -DFIND_VERSION_BINARY_DIR=${CMAKE_BINARY_DIR}/generated -DCPACK_BINARY_DIR=${CMAKE_BINARY_DIR} - -DREV_MAJOR=${CMAKE_PROJECT_VERSION_MAJOR} - -DREV_MINOR=${CMAKE_PROJECT_VERSION_MINOR} - -DREV_BUILD=${CMAKE_PROJECT_VERSION_PATCH} + -DREV_MAJOR=${PROJECT_VERSION_MAJOR} + -DREV_MINOR=${PROJECT_VERSION_MINOR} + -DREV_BUILD=${PROJECT_VERSION_PATCH} -P "${CMAKE_SOURCE_DIR}/cmake/scripts/FindVersion.cmake" WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} BYPRODUCTS ${GENERATED_SOURCE_FILES} From 0345f99180075e44d7c549e2207936a109a5451b Mon Sep 17 00:00:00 2001 From: rubidium42 Date: Sat, 1 May 2021 10:31:00 +0200 Subject: [PATCH 236/268] Feature: make the town directory horizontally resizable --- src/town_gui.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/town_gui.cpp b/src/town_gui.cpp index 0629490393..a189c7ccb3 100644 --- a/src/town_gui.cpp +++ b/src/town_gui.cpp @@ -647,12 +647,12 @@ static const NWidgetPart _nested_town_directory_widgets[] = { NWidget(NWID_HORIZONTAL), NWidget(WWT_TEXTBTN, COLOUR_BROWN, WID_TD_SORT_ORDER), SetDataTip(STR_BUTTON_SORT_BY, STR_TOOLTIP_SORT_ORDER), NWidget(WWT_DROPDOWN, COLOUR_BROWN, WID_TD_SORT_CRITERIA), SetDataTip(STR_JUST_STRING, STR_TOOLTIP_SORT_CRITERIA), - NWidget(WWT_EDITBOX, COLOUR_BROWN, WID_TD_FILTER), SetFill(1, 0), SetDataTip(STR_LIST_FILTER_OSKTITLE, STR_LIST_FILTER_TOOLTIP), + NWidget(WWT_EDITBOX, COLOUR_BROWN, WID_TD_FILTER), SetFill(1, 0), SetResize(1, 0), SetDataTip(STR_LIST_FILTER_OSKTITLE, STR_LIST_FILTER_TOOLTIP), EndContainer(), - NWidget(WWT_PANEL, COLOUR_BROWN, WID_TD_LIST), SetMinimalSize(196, 0), SetDataTip(0x0, STR_TOWN_DIRECTORY_LIST_TOOLTIP), - SetFill(1, 0), SetResize(0, 1), SetScrollbar(WID_TD_SCROLLBAR), EndContainer(), + NWidget(WWT_PANEL, COLOUR_BROWN, WID_TD_LIST), SetDataTip(0x0, STR_TOWN_DIRECTORY_LIST_TOOLTIP), + SetFill(1, 0), SetResize(1, 1), SetScrollbar(WID_TD_SCROLLBAR), EndContainer(), NWidget(WWT_PANEL, COLOUR_BROWN), - NWidget(WWT_TEXT, COLOUR_BROWN, WID_TD_WORLD_POPULATION), SetPadding(2, 0, 2, 2), SetMinimalSize(196, 0), SetMinimalTextLines(1, 0), SetFill(1, 0), SetDataTip(STR_TOWN_POPULATION, STR_NULL), + NWidget(WWT_TEXT, COLOUR_BROWN, WID_TD_WORLD_POPULATION), SetPadding(2, 0, 2, 2), SetMinimalTextLines(1, 0), SetFill(1, 0), SetResize(1, 0), SetDataTip(STR_TOWN_POPULATION, STR_NULL), EndContainer(), EndContainer(), NWidget(NWID_VERTICAL), From 282d5d302dfe81e8a312c96732bb17439a8f8264 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Guilloux?= Date: Sat, 1 May 2021 16:18:41 +0200 Subject: [PATCH 237/268] Change: [Actions] Add a 2 minutes timeout for regression test (#9166) --- .github/workflows/ci-build.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci-build.yml b/.github/workflows/ci-build.yml index 1aa7e17ccb..74677ee6d5 100644 --- a/.github/workflows/ci-build.yml +++ b/.github/workflows/ci-build.yml @@ -143,7 +143,7 @@ jobs: - name: Test run: | cd build - ctest -j $(nproc) + ctest -j $(nproc) --timeout 120 macos: name: Mac OS @@ -224,7 +224,7 @@ jobs: - name: Test run: | cd build - ctest -j $(sysctl -n hw.logicalcpu) + ctest -j $(sysctl -n hw.logicalcpu) --timeout 120 windows: name: Windows @@ -314,4 +314,4 @@ jobs: shell: bash run: | cd ${GITHUB_WORKSPACE}/build - ctest + ctest --timeout 120 From 376f2509ad5b35910ce2cb949f7ba84d86aed739 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Guilloux?= Date: Sat, 1 May 2021 16:19:14 +0200 Subject: [PATCH 238/268] Fix: Don't consider regression AIs when starting a random AI (#9164) --- regression/regression/info.nut | 1 + regression/stationlist/info.nut | 1 + 2 files changed, 2 insertions(+) diff --git a/regression/regression/info.nut b/regression/regression/info.nut index 340dcff5c3..50c9fa5146 100644 --- a/regression/regression/info.nut +++ b/regression/regression/info.nut @@ -7,6 +7,7 @@ class Regression extends AIInfo { function GetAPIVersion() { return "1.12"; } function GetDate() { return "2007-03-18"; } function CreateInstance() { return "Regression"; } + function UseAsRandomAI() { return false; } } RegisterAI(Regression()); diff --git a/regression/stationlist/info.nut b/regression/stationlist/info.nut index a58fd20ba8..0558ccc1fb 100644 --- a/regression/stationlist/info.nut +++ b/regression/stationlist/info.nut @@ -7,6 +7,7 @@ class StationList extends AIInfo { function GetAPIVersion() { return "1.12"; } function GetDate() { return "2007-03-18"; } function CreateInstance() { return "StationList"; } + function UseAsRandomAI() { return false; } } RegisterAI(StationList()); From 3bd416bfdba351d9d76a753a0c5b4abdd84fea39 Mon Sep 17 00:00:00 2001 From: rubidium42 Date: Sat, 1 May 2021 13:54:30 +0200 Subject: [PATCH 239/268] Change: [Console] Show help when passing invalid company number --- src/console_cmds.cpp | 6 ++--- src/network/network.cpp | 41 ++++++++++++++++++++++++++++------ src/network/network_func.h | 2 +- src/network/network_internal.h | 2 +- 4 files changed, 38 insertions(+), 13 deletions(-) diff --git a/src/console_cmds.cpp b/src/console_cmds.cpp index 401a52b1f9..71f2d4b7b0 100644 --- a/src/console_cmds.cpp +++ b/src/console_cmds.cpp @@ -901,8 +901,7 @@ DEF_CONSOLE_CMD(ConNetworkReconnect) /* Don't resolve the address first, just print it directly as it comes from the config file. */ IConsolePrintF(CC_DEFAULT, "Reconnecting to %s ...", _settings_client.network.last_joined); - NetworkClientConnectGame(_settings_client.network.last_joined, playas); - return true; + return NetworkClientConnectGame(_settings_client.network.last_joined, playas); } DEF_CONSOLE_CMD(ConNetworkConnect) @@ -917,8 +916,7 @@ DEF_CONSOLE_CMD(ConNetworkConnect) if (argc < 2) return false; if (_networking) NetworkDisconnect(); // we are in network-mode, first close it! - NetworkClientConnectGame(argv[1], COMPANY_NEW_COMPANY); - return true; + return NetworkClientConnectGame(argv[1], COMPANY_NEW_COMPANY); } /********************************* diff --git a/src/network/network.cpp b/src/network/network.cpp index c31a67487d..22ac5dc74c 100644 --- a/src/network/network.cpp +++ b/src/network/network.cpp @@ -713,7 +713,24 @@ public: } }; -void NetworkClientConnectGame(const std::string &connection_string, CompanyID default_company, const char *join_server_password, const char *join_company_password) +/** + * Join a client to the server at with the given connection string. + * The default for the passwords is \c nullptr. When the server or company needs a + * password and none is given, the user is asked to enter the password in the GUI. + * This function will return false whenever some information required to join is not + * correct such as the company number or the client's name, or when there is not + * networking avalabile at all. If the function returns false the connection with + * the existing server is not disconnected. + * It will return true when it starts the actual join process, i.e. when it + * actually shows the join status window. + * + * @param connection_string The IP address, port and company number to join as. + * @param default_company The company number to join as when none is given. + * @param join_server_password The password for the server. + * @param join_company_password The password for the company. + * @return Whether the join has started. + */ +bool NetworkClientConnectGame(const std::string &connection_string, CompanyID default_company, const char *join_server_password, const char *join_company_password) { CompanyID join_as = default_company; NetworkAddress address = ParseGameConnectionString(&join_as, connection_string, NETWORK_DEFAULT_PORT); @@ -721,18 +738,27 @@ void NetworkClientConnectGame(const std::string &connection_string, CompanyID de if (join_as != COMPANY_NEW_COMPANY && join_as != COMPANY_SPECTATOR) { join_as--; if (join_as >= MAX_COMPANIES) { - return; + return false; } } - NetworkClientConnectGame(address, join_as, join_server_password, join_company_password); + return NetworkClientConnectGame(address, join_as, join_server_password, join_company_password); } -/* Used by clients, to connect to a server */ -void NetworkClientConnectGame(NetworkAddress &address, CompanyID join_as, const char *join_server_password, const char *join_company_password) +/** + * Join a client to the server at the given address. + * See the overloaded NetworkClientConnectGame for more details. + * + * @param address The network address of the server to join to. + * @param join_as The company number to join as. + * @param join_server_password The password for the server. + * @param join_company_password The password for the company. + * @return Whether the join has started. + */ +bool NetworkClientConnectGame(NetworkAddress &address, CompanyID join_as, const char *join_server_password, const char *join_company_password) { - if (!_network_available) return; - if (!NetworkValidateClientName()) return; + if (!_network_available) return false; + if (!NetworkValidateClientName()) return false; strecpy(_settings_client.network.last_joined, address.GetAddressAsString(false).c_str(), lastof(_settings_client.network.last_joined)); @@ -747,6 +773,7 @@ void NetworkClientConnectGame(NetworkAddress &address, CompanyID join_as, const ShowJoinStatusWindow(); new TCPClientConnecter(address); + return true; } static void NetworkInitGameInfo() diff --git a/src/network/network_func.h b/src/network/network_func.h index cd2b291bc7..252d207db5 100644 --- a/src/network/network_func.h +++ b/src/network/network_func.h @@ -51,7 +51,7 @@ void NetworkPopulateCompanyStats(NetworkCompanyStats *stats); void NetworkUpdateClientInfo(ClientID client_id); void NetworkClientsToSpectators(CompanyID cid); -void NetworkClientConnectGame(const std::string &connection_string, CompanyID default_company, const char *join_server_password = nullptr, const char *join_company_password = nullptr); +bool NetworkClientConnectGame(const std::string &connection_string, CompanyID default_company, const char *join_server_password = nullptr, const char *join_company_password = nullptr); void NetworkClientRequestMove(CompanyID company, const char *pass = ""); void NetworkClientSendRcon(const char *password, const char *command); void NetworkClientSendChat(NetworkAction action, DestType type, int dest, const char *msg, int64 data = 0); diff --git a/src/network/network_internal.h b/src/network/network_internal.h index 683c954e82..be5b74a0f5 100644 --- a/src/network/network_internal.h +++ b/src/network/network_internal.h @@ -119,7 +119,7 @@ StringID GetNetworkErrorMsg(NetworkErrorCode err); bool NetworkFindName(char *new_name, const char *last); const char *GenerateCompanyPasswordHash(const char *password, const char *password_server_id, uint32 password_game_seed); -void NetworkClientConnectGame(NetworkAddress &address, CompanyID join_as, const char *join_server_password = nullptr, const char *join_company_password = nullptr); +bool NetworkClientConnectGame(NetworkAddress &address, CompanyID join_as, const char *join_server_password = nullptr, const char *join_company_password = nullptr); NetworkAddress ParseConnectionString(const std::string &connection_string, int default_port); NetworkAddress ParseGameConnectionString(CompanyID *company, const std::string &connection_string, int default_port); From 39c51c35f4566553ec3c6c273c2642d796070521 Mon Sep 17 00:00:00 2001 From: rubidium42 Date: Sat, 1 May 2021 13:55:39 +0200 Subject: [PATCH 240/268] Fix #6598: Do not disconnect before company number validation NetworkClientConnectGame already does a NetworkDisconnect, so no reason to do it here --- src/console_cmds.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/console_cmds.cpp b/src/console_cmds.cpp index 71f2d4b7b0..69a44e63de 100644 --- a/src/console_cmds.cpp +++ b/src/console_cmds.cpp @@ -914,7 +914,6 @@ DEF_CONSOLE_CMD(ConNetworkConnect) } if (argc < 2) return false; - if (_networking) NetworkDisconnect(); // we are in network-mode, first close it! return NetworkClientConnectGame(argv[1], COMPANY_NEW_COMPANY); } From 83985fe26f1ad87df709f122f74e5d50485095fa Mon Sep 17 00:00:00 2001 From: rubidium42 Date: Sat, 1 May 2021 14:21:33 +0200 Subject: [PATCH 241/268] Codechange: Move join information into a single structure --- src/network/network.cpp | 6 +++--- src/network/network_client.cpp | 21 ++++++++------------- src/network/network_client.h | 11 ++++++++--- 3 files changed, 19 insertions(+), 19 deletions(-) diff --git a/src/network/network.cpp b/src/network/network.cpp index 22ac5dc74c..32fc4dec95 100644 --- a/src/network/network.cpp +++ b/src/network/network.cpp @@ -762,9 +762,9 @@ bool NetworkClientConnectGame(NetworkAddress &address, CompanyID join_as, const strecpy(_settings_client.network.last_joined, address.GetAddressAsString(false).c_str(), lastof(_settings_client.network.last_joined)); - _network_join_as = join_as; - _network_join_server_password = join_server_password; - _network_join_company_password = join_company_password; + _network_join.company = join_as; + _network_join.server_password = join_server_password; + _network_join.company_password = join_company_password; NetworkDisconnect(); NetworkInitialize(); diff --git a/src/network/network_client.cpp b/src/network/network_client.cpp index f73c8be528..ceb1463336 100644 --- a/src/network/network_client.cpp +++ b/src/network/network_client.cpp @@ -326,13 +326,8 @@ static uint8 _network_server_max_companies; /** Maximum number of spectators of the currently joined server. */ static uint8 _network_server_max_spectators; -/** Who would we like to join as. */ -CompanyID _network_join_as; - -/** Login password from -p argument */ -const char *_network_join_server_password = nullptr; -/** Company password from -P argument */ -const char *_network_join_company_password = nullptr; +/** Information about the game to join to. */ +NetworkJoinInfo _network_join; /** Make sure the server ID length is the same as a md5 hash. */ static_assert(NETWORK_SERVER_ID_LENGTH == 16 * 2 + 1); @@ -372,7 +367,7 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::SendJoin() p->Send_string(GetNetworkRevisionString()); p->Send_uint32(_openttd_newgrf_version); p->Send_string(_settings_client.network.client_name); // Client name - p->Send_uint8 (_network_join_as); // PlayAs + p->Send_uint8 (_network_join.company); // PlayAs p->Send_uint8 (0); // Used to be language my_client->SendPacket(p); return NETWORK_RECV_STATUS_OKAY; @@ -804,7 +799,7 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_NEED_GAME_PASSW if (this->status < STATUS_JOIN || this->status >= STATUS_AUTH_GAME) return NETWORK_RECV_STATUS_MALFORMED_PACKET; this->status = STATUS_AUTH_GAME; - const char *password = _network_join_server_password; + const char *password = _network_join.server_password; if (!StrEmpty(password)) { return SendGamePassword(password); } @@ -823,7 +818,7 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_NEED_COMPANY_PA p->Recv_string(_password_server_id, sizeof(_password_server_id)); if (this->HasClientQuit()) return NETWORK_RECV_STATUS_MALFORMED_PACKET; - const char *password = _network_join_company_password; + const char *password = _network_join.company_password; if (!StrEmpty(password)) { return SendCompanyPassword(password); } @@ -945,10 +940,10 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_MAP_DONE(Packet /* New company/spectator (invalid company) or company we want to join is not active * Switch local company to spectator and await the server's judgement */ - if (_network_join_as == COMPANY_NEW_COMPANY || !Company::IsValidID(_network_join_as)) { + if (_network_join.company == COMPANY_NEW_COMPANY || !Company::IsValidID(_network_join.company)) { SetLocalCompany(COMPANY_SPECTATOR); - if (_network_join_as != COMPANY_SPECTATOR) { + if (_network_join.company != COMPANY_SPECTATOR) { /* We have arrived and ready to start playing; send a command to make a new company; * the server will give us a client-id and let us in */ _network_join_status = NETWORK_JOIN_STATUS_REGISTERING; @@ -957,7 +952,7 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_MAP_DONE(Packet } } else { /* take control over an existing company */ - SetLocalCompany(_network_join_as); + SetLocalCompany(_network_join.company); } return NETWORK_RECV_STATUS_OKAY; diff --git a/src/network/network_client.h b/src/network/network_client.h index 40b8eedf92..28d2d00214 100644 --- a/src/network/network_client.h +++ b/src/network/network_client.h @@ -112,9 +112,14 @@ typedef ClientNetworkGameSocketHandler MyClient; void NetworkClient_Connected(); void NetworkClientSetCompanyPassword(const char *password); -extern CompanyID _network_join_as; +/** Information required to join a server. */ +struct NetworkJoinInfo { + NetworkJoinInfo() : company(COMPANY_SPECTATOR), server_password(nullptr), company_password(nullptr) {} + CompanyID company; ///< The company to join. + const char *server_password; ///< The password of the server to join. + const char *company_password; ///< The password of the company to join. +}; -extern const char *_network_join_server_password; -extern const char *_network_join_company_password; +extern NetworkJoinInfo _network_join; #endif /* NETWORK_CLIENT_H */ From 05394d5216f89c9a7e14487b571515e510828657 Mon Sep 17 00:00:00 2001 From: rubidium42 Date: Sat, 1 May 2021 14:41:25 +0200 Subject: [PATCH 242/268] Fix #6598: Prevent invalid memory accesses when abandoning a join from within a network game One could join a network game from within an already running network game. This would call a NetworkDisconnect, but keeps the UI alive. If, during that process the join is aborted, e.g. by cancelling on a password dialog, you would still be in your network game but also get shown the server list. Solve all the underlying problems by falling back to the main UI when (re)connecting to a(nother) server. --- src/network/network.cpp | 28 ++++++++++++++++++++++++---- src/network/network_client.h | 1 + src/network/network_func.h | 1 + src/openttd.cpp | 5 +++++ src/openttd.h | 1 + 5 files changed, 32 insertions(+), 4 deletions(-) diff --git a/src/network/network.cpp b/src/network/network.cpp index 32fc4dec95..ce79f4c514 100644 --- a/src/network/network.cpp +++ b/src/network/network.cpp @@ -760,20 +760,40 @@ bool NetworkClientConnectGame(NetworkAddress &address, CompanyID join_as, const if (!_network_available) return false; if (!NetworkValidateClientName()) return false; - strecpy(_settings_client.network.last_joined, address.GetAddressAsString(false).c_str(), lastof(_settings_client.network.last_joined)); - + _network_join.address = address; _network_join.company = join_as; _network_join.server_password = join_server_password; _network_join.company_password = join_company_password; + if (_game_mode == GM_MENU) { + /* From the menu we can immediately continue with the actual join. */ + NetworkClientJoinGame(); + } else { + /* When already playing a game, first go back to the main menu. This + * disconnects the user from the current game, meaning we can safely + * load in the new. After all, there is little point in continueing to + * play on a server if we are connecting to another one. + */ + _switch_mode = SM_JOIN_GAME; + } + return true; +} + +/** + * Actually perform the joining to the server. Use #NetworkClientConnectGame + * when you want to connect to a specific server/company. This function + * assumes _network_join is already fully set up. + */ +void NetworkClientJoinGame() +{ NetworkDisconnect(); NetworkInitialize(); + strecpy(_settings_client.network.last_joined, _network_join.address.GetAddressAsString(false).c_str(), lastof(_settings_client.network.last_joined)); _network_join_status = NETWORK_JOIN_STATUS_CONNECTING; ShowJoinStatusWindow(); - new TCPClientConnecter(address); - return true; + new TCPClientConnecter(_network_join.address); } static void NetworkInitGameInfo() diff --git a/src/network/network_client.h b/src/network/network_client.h index 28d2d00214..81d5b720cd 100644 --- a/src/network/network_client.h +++ b/src/network/network_client.h @@ -115,6 +115,7 @@ void NetworkClientSetCompanyPassword(const char *password); /** Information required to join a server. */ struct NetworkJoinInfo { NetworkJoinInfo() : company(COMPANY_SPECTATOR), server_password(nullptr), company_password(nullptr) {} + NetworkAddress address; ///< The address of the server to join. CompanyID company; ///< The company to join. const char *server_password; ///< The password of the server to join. const char *company_password; ///< The password of the company to join. diff --git a/src/network/network_func.h b/src/network/network_func.h index 252d207db5..5f3e27c12f 100644 --- a/src/network/network_func.h +++ b/src/network/network_func.h @@ -52,6 +52,7 @@ void NetworkPopulateCompanyStats(NetworkCompanyStats *stats); void NetworkUpdateClientInfo(ClientID client_id); void NetworkClientsToSpectators(CompanyID cid); bool NetworkClientConnectGame(const std::string &connection_string, CompanyID default_company, const char *join_server_password = nullptr, const char *join_company_password = nullptr); +void NetworkClientJoinGame(); void NetworkClientRequestMove(CompanyID company, const char *pass = ""); void NetworkClientSendRcon(const char *password, const char *command); void NetworkClientSendChat(NetworkAction action, DestType type, int dest, const char *msg, int64 data = 0); diff --git a/src/openttd.cpp b/src/openttd.cpp index fbeeba793e..234e3a4197 100644 --- a/src/openttd.cpp +++ b/src/openttd.cpp @@ -1068,6 +1068,11 @@ void SwitchToMode(SwitchMode new_mode) break; } + case SM_JOIN_GAME: // Join a multiplayer game + LoadIntroGame(); + NetworkClientJoinGame(); + break; + case SM_MENU: // Switch to game intro menu LoadIntroGame(); if (BaseSounds::ini_set.empty() && BaseSounds::GetUsedSet()->fallback && SoundDriver::GetInstance()->HasOutput()) { diff --git a/src/openttd.h b/src/openttd.h index 77fafab1d1..2cd9cc1f09 100644 --- a/src/openttd.h +++ b/src/openttd.h @@ -36,6 +36,7 @@ enum SwitchMode { SM_START_HEIGHTMAP, ///< Load a heightmap and start a new game from it. SM_LOAD_HEIGHTMAP, ///< Load heightmap from scenario editor. SM_RESTART_HEIGHTMAP, ///< Load a heightmap and start a new game from it with current settings. + SM_JOIN_GAME, ///< Join a network game. }; /** Display Options */ From 520595ff87bc55042010a370ef1070f296ff07c0 Mon Sep 17 00:00:00 2001 From: Matt Kimber Date: Sat, 1 May 2021 18:14:50 +0100 Subject: [PATCH 243/268] Fix 3d7ab09: stopped trains not updating viewport hash when reversed for a second time (#9165) --- src/vehicle.cpp | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/vehicle.cpp b/src/vehicle.cpp index a9af24c55a..90accdd80e 100644 --- a/src/vehicle.cpp +++ b/src/vehicle.cpp @@ -1639,13 +1639,19 @@ void Vehicle::UpdateBoundingBoxCoordinates(bool update_cache) const */ void Vehicle::UpdateViewport(bool dirty) { - Rect old_coord = this->sprite_cache.old_coord; + /* If the existing cache is invalid we should ignore it, as it will be set to the current coords by UpdateBoundingBoxCoordinates */ + bool ignore_cached_coords = this->sprite_cache.old_coord.left == INVALID_COORD; this->UpdateBoundingBoxCoordinates(true); - UpdateVehicleViewportHash(this, this->coord.left, this->coord.top, old_coord.left, old_coord.top); + + if (ignore_cached_coords) { + UpdateVehicleViewportHash(this, this->coord.left, this->coord.top, INVALID_COORD, INVALID_COORD); + } else { + UpdateVehicleViewportHash(this, this->coord.left, this->coord.top, this->sprite_cache.old_coord.left, this->sprite_cache.old_coord.top); + } if (dirty) { - if (old_coord.left == INVALID_COORD) { + if (ignore_cached_coords) { this->sprite_cache.is_viewport_candidate = this->MarkAllViewportsDirty(); } else { this->sprite_cache.is_viewport_candidate = ::MarkAllViewportsDirty( From 67063ceeb3832a7e0f608859ccbd5e79214e9853 Mon Sep 17 00:00:00 2001 From: Matt Kimber Date: Sat, 1 May 2021 18:15:22 +0100 Subject: [PATCH 244/268] Fix 3d7ab09: stopped trains not updating viewport hash when reversed for a second time (#9165) From 0eb17a70af86d11e49d9560088900c9d65cb07c1 Mon Sep 17 00:00:00 2001 From: rubidium42 Date: Fri, 30 Apr 2021 15:39:46 +0200 Subject: [PATCH 245/268] Codechange: rename NetworkError to ShowNetworkError --- src/network/core/tcp_listen.h | 2 +- src/network/network.cpp | 4 ++-- src/network/network_client.cpp | 2 +- src/network/network_internal.h | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/network/core/tcp_listen.h b/src/network/core/tcp_listen.h index 168f49f947..2ceea20aa9 100644 --- a/src/network/core/tcp_listen.h +++ b/src/network/core/tcp_listen.h @@ -151,7 +151,7 @@ public: if (sockets.size() == 0) { DEBUG(net, 0, "[server] could not start network: could not create listening socket"); - NetworkError(STR_NETWORK_ERROR_SERVER_START); + ShowNetworkError(STR_NETWORK_ERROR_SERVER_START); return false; } diff --git a/src/network/network.cpp b/src/network/network.cpp index ce79f4c514..af732facdb 100644 --- a/src/network/network.cpp +++ b/src/network/network.cpp @@ -277,7 +277,7 @@ uint NetworkCalculateLag(const NetworkClientSocket *cs) /* There was a non-recoverable error, drop back to the main menu with a nice * error */ -void NetworkError(StringID error_string) +void ShowNetworkError(StringID error_string) { _switch_mode = SM_MENU; ShowErrorMessage(error_string, INVALID_STRING_ID, WL_CRITICAL); @@ -701,7 +701,7 @@ public: void OnFailure() override { - NetworkError(STR_NETWORK_ERROR_NOCONNECTION); + ShowNetworkError(STR_NETWORK_ERROR_NOCONNECTION); } void OnConnect(SOCKET s) override diff --git a/src/network/network_client.cpp b/src/network/network_client.cpp index ceb1463336..d288c71a6e 100644 --- a/src/network/network_client.cpp +++ b/src/network/network_client.cpp @@ -284,7 +284,7 @@ void ClientNetworkGameSocketHandler::ClientError(NetworkRecvStatus res) #else if (_sync_seed_1 != _random.state[0]) { #endif - NetworkError(STR_NETWORK_ERROR_DESYNC); + ShowNetworkError(STR_NETWORK_ERROR_DESYNC); DEBUG(desync, 1, "sync_err: %08x; %02x", _date, _date_fract); DEBUG(net, 0, "Sync error detected!"); my_client->ClientError(NETWORK_RECV_STATUS_DESYNC); diff --git a/src/network/network_internal.h b/src/network/network_internal.h index be5b74a0f5..1fba1228a5 100644 --- a/src/network/network_internal.h +++ b/src/network/network_internal.h @@ -112,7 +112,7 @@ void NetworkExecuteLocalCommandQueue(); void NetworkFreeLocalCommandQueue(); void NetworkSyncCommandQueue(NetworkClientSocket *cs); -void NetworkError(StringID error_string); +void ShowNetworkError(StringID error_string); void NetworkTextMessage(NetworkAction action, TextColour colour, bool self_send, const char *name, const char *str = "", int64 data = 0); uint NetworkCalculateLag(const NetworkClientSocket *cs); StringID GetNetworkErrorMsg(NetworkErrorCode err); From 22720332eb9922e20148c7aae1127f7304f6f7d3 Mon Sep 17 00:00:00 2001 From: rubidium42 Date: Fri, 30 Apr 2021 15:38:22 +0200 Subject: [PATCH 246/268] Codechange: encapsulate network error handling --- src/network/core/CMakeLists.txt | 1 + src/network/core/address.cpp | 24 +++--- src/network/core/core.cpp | 18 ---- src/network/core/os_abstraction.cpp | 125 ++++++++++++++++++++++++++++ src/network/core/os_abstraction.h | 52 +++++------- src/network/core/tcp.cpp | 22 ++--- src/network/core/tcp_http.cpp | 8 +- src/network/core/tcp_listen.h | 4 +- src/network/core/udp.cpp | 4 +- src/safeguards.h | 8 +- 10 files changed, 183 insertions(+), 83 deletions(-) create mode 100644 src/network/core/os_abstraction.cpp diff --git a/src/network/core/CMakeLists.txt b/src/network/core/CMakeLists.txt index 37cc3e1954..bf713be99c 100644 --- a/src/network/core/CMakeLists.txt +++ b/src/network/core/CMakeLists.txt @@ -8,6 +8,7 @@ add_files( game_info.h host.cpp host.h + os_abstraction.cpp os_abstraction.h packet.cpp packet.h diff --git a/src/network/core/address.cpp b/src/network/core/address.cpp index e91751c33f..d3e373ef76 100644 --- a/src/network/core/address.cpp +++ b/src/network/core/address.cpp @@ -317,7 +317,7 @@ static SOCKET ConnectLoopProc(addrinfo *runp) SOCKET sock = socket(runp->ai_family, runp->ai_socktype, runp->ai_protocol); if (sock == INVALID_SOCKET) { - DEBUG(net, 1, "[%s] could not create %s socket: %s", type, family, NetworkGetLastErrorString()); + DEBUG(net, 1, "[%s] could not create %s socket: %s", type, family, NetworkError::GetLast().AsString()); return INVALID_SOCKET; } @@ -326,8 +326,8 @@ static SOCKET ConnectLoopProc(addrinfo *runp) if (!SetNonBlocking(sock)) DEBUG(net, 0, "[%s] setting non-blocking mode failed", type); int err = connect(sock, runp->ai_addr, (int)runp->ai_addrlen); - if (err != 0 && NetworkGetLastError() != EINPROGRESS) { - DEBUG(net, 1, "[%s] could not connect to %s over %s: %s", type, address.c_str(), family, NetworkGetLastErrorString()); + if (err != 0 && !NetworkError::GetLast().IsConnectInProgress()) { + DEBUG(net, 1, "[%s] could not connect to %s over %s: %s", type, address.c_str(), family, NetworkError::GetLast().AsString()); closesocket(sock); return INVALID_SOCKET; } @@ -343,7 +343,7 @@ static SOCKET ConnectLoopProc(addrinfo *runp) tv.tv_sec = DEFAULT_CONNECT_TIMEOUT_SECONDS; int n = select(FD_SETSIZE, NULL, &write_fd, NULL, &tv); if (n < 0) { - DEBUG(net, 1, "[%s] could not connect to %s: %s", type, address.c_str(), NetworkGetLastErrorString()); + DEBUG(net, 1, "[%s] could not connect to %s: %s", type, address.c_str(), NetworkError::GetLast().AsString()); closesocket(sock); return INVALID_SOCKET; } @@ -356,9 +356,9 @@ static SOCKET ConnectLoopProc(addrinfo *runp) } /* Retrieve last error, if any, on the socket. */ - err = GetSocketError(sock); - if (err != 0) { - DEBUG(net, 1, "[%s] could not connect to %s: %s", type, address.c_str(), NetworkGetErrorString(err)); + NetworkError socket_error = GetSocketError(sock); + if (socket_error.HasError()) { + DEBUG(net, 1, "[%s] could not connect to %s: %s", type, address.c_str(), socket_error.AsString()); closesocket(sock); return INVALID_SOCKET; } @@ -393,7 +393,7 @@ static SOCKET ListenLoopProc(addrinfo *runp) SOCKET sock = socket(runp->ai_family, runp->ai_socktype, runp->ai_protocol); if (sock == INVALID_SOCKET) { - DEBUG(net, 0, "[%s] could not create %s socket on port %s: %s", type, family, address.c_str(), NetworkGetLastErrorString()); + DEBUG(net, 0, "[%s] could not create %s socket on port %s: %s", type, family, address.c_str(), NetworkError::GetLast().AsString()); return INVALID_SOCKET; } @@ -404,24 +404,24 @@ static SOCKET ListenLoopProc(addrinfo *runp) int on = 1; /* The (const char*) cast is needed for windows!! */ if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (const char*)&on, sizeof(on)) == -1) { - DEBUG(net, 3, "[%s] could not set reusable %s sockets for port %s: %s", type, family, address.c_str(), NetworkGetLastErrorString()); + DEBUG(net, 3, "[%s] could not set reusable %s sockets for port %s: %s", type, family, address.c_str(), NetworkError::GetLast().AsString()); } #ifndef __OS2__ if (runp->ai_family == AF_INET6 && setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, (const char*)&on, sizeof(on)) == -1) { - DEBUG(net, 3, "[%s] could not disable IPv4 over IPv6 on port %s: %s", type, address.c_str(), NetworkGetLastErrorString()); + DEBUG(net, 3, "[%s] could not disable IPv4 over IPv6 on port %s: %s", type, address.c_str(), NetworkError::GetLast().AsString()); } #endif if (bind(sock, runp->ai_addr, (int)runp->ai_addrlen) != 0) { - DEBUG(net, 1, "[%s] could not bind on %s port %s: %s", type, family, address.c_str(), NetworkGetLastErrorString()); + DEBUG(net, 1, "[%s] could not bind on %s port %s: %s", type, family, address.c_str(), NetworkError::GetLast().AsString()); closesocket(sock); return INVALID_SOCKET; } if (runp->ai_socktype != SOCK_DGRAM && listen(sock, 1) != 0) { - DEBUG(net, 1, "[%s] could not listen at %s port %s: %s", type, family, address.c_str(), NetworkGetLastErrorString()); + DEBUG(net, 1, "[%s] could not listen at %s port %s: %s", type, family, address.c_str(), NetworkError::GetLast().AsString()); closesocket(sock); return INVALID_SOCKET; } diff --git a/src/network/core/core.cpp b/src/network/core/core.cpp index 5c12cb2242..563deae963 100644 --- a/src/network/core/core.cpp +++ b/src/network/core/core.cpp @@ -13,7 +13,6 @@ #include "../../debug.h" #include "os_abstraction.h" #include "packet.h" -#include "../../string_func.h" #include "../../safeguards.h" @@ -48,20 +47,3 @@ void NetworkCoreShutdown() WSACleanup(); #endif } - -#if defined(_WIN32) -/** - * Return the string representation of the given error from the OS's network functions. - * @param error The error number (from \c NetworkGetLastError()). - * @return The error message, potentially an empty string but never \c nullptr. - */ -const char *NetworkGetErrorString(int error) -{ - static char buffer[512]; - if (FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, error, - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), buffer, sizeof(buffer), NULL) == 0) { - seprintf(buffer, lastof(buffer), "Unknown error %d", error); - } - return buffer; -} -#endif /* defined(_WIN32) */ diff --git a/src/network/core/os_abstraction.cpp b/src/network/core/os_abstraction.cpp new file mode 100644 index 0000000000..75f2224eb0 --- /dev/null +++ b/src/network/core/os_abstraction.cpp @@ -0,0 +1,125 @@ +/* + * This file is part of OpenTTD. + * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. + * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see . + */ + +/** + * @file os_abstraction.cpp OS specific implementations of functions of the OS abstraction layer for network stuff. + * + * The general idea is to have simple abstracting functions for things that + * require different implementations for different environments. + * In here the functions, and their documentation, are defined only once + * and the implementation contains the #ifdefs to change the implementation. + * Since Windows is usually different that is usually the first case, after + * that the behaviour is usually Unix/BSD-like with occasional variation. + */ + +#include "stdafx.h" +#include "os_abstraction.h" +#include "../../string_func.h" +#include + +#include "../../safeguards.h" + +/** + * Construct the network error with the given error code. + * @param error The error code. + */ +NetworkError::NetworkError(int error) : error(error) +{ +} + +/** + * Check whether this error describes that the operation would block. + * @return True iff the operation would block. + */ +bool NetworkError::WouldBlock() const +{ +#if defined(_WIN32) + return this->error == WSAEWOULDBLOCK; +#else + /* Usually EWOULDBLOCK and EAGAIN are the same, but sometimes they are not + * and the POSIX.1 specification states that either should be checked. */ + return this->error == EWOULDBLOCK || this->error == EAGAIN; +#endif +} + +/** + * Check whether this error describes a connection reset. + * @return True iff the connection is reset. + */ +bool NetworkError::IsConnectionReset() const +{ +#if defined(_WIN32) + return this->error == WSAECONNRESET; +#else + return this->error == ECONNRESET; +#endif +} + +/** + * Check whether this error describes a connect is in progress. + * @return True iff the connect is already in progress. + */ +bool NetworkError::IsConnectInProgress() const +{ +#if defined(_WIN32) + return this->error == WSAEWOULDBLOCK; +#else + return this->error == EINPROGRESS; +#endif +} + +/** + * Get the string representation of the error message. + * @return The string representation that will get overwritten by next calls. + */ +const char *NetworkError::AsString() const +{ + if (this->message.empty()) { +#if defined(_WIN32) + char buffer[512]; + if (FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, this->error, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), buffer, sizeof(buffer), NULL) == 0) { + seprintf(buffer, lastof(buffer), "Unknown error %d", this->error); + } + this->message.assign(buffer); +#else + /* Make strerror thread safe by locking access to it. There is a thread safe strerror_r, however + * the non-POSIX variant is available due to defining _GNU_SOURCE meaning it is not portable. + * The problem with the non-POSIX variant is that it does not necessarily fill the buffer with + * the error message but can also return a pointer to a static bit of memory, whereas the POSIX + * variant always fills the buffer. This makes the behaviour too erratic to work with. */ + static std::mutex mutex; + std::lock_guard guard(mutex); + this->message.assign(strerror(this->error)); +#endif + } + return this->message.c_str(); +} + +/** + * Check whether an error was actually set. + * @return True iff an error was set. + */ +bool NetworkError::HasError() const +{ + return this->error != 0; +} + +/** + * Get the last network error. + * @return The network error. + */ +/* static */ NetworkError NetworkError::GetLast() +{ +#if defined(_WIN32) + return NetworkError(WSAGetLastError()); +#elif defined(__OS2__) + return NetworkError(sock_errno()); +#else + return NetworkError(errno); +#endif +} diff --git a/src/network/core/os_abstraction.h b/src/network/core/os_abstraction.h index 9bd0e321f7..e444bc78b4 100644 --- a/src/network/core/os_abstraction.h +++ b/src/network/core/os_abstraction.h @@ -14,6 +14,26 @@ #ifndef NETWORK_CORE_OS_ABSTRACTION_H #define NETWORK_CORE_OS_ABSTRACTION_H +/** + * Abstraction of a network error where all implementation details of the + * error codes are encapsulated in this class and the abstraction layer. + */ +class NetworkError { +private: + int error; ///< The underlying error number from errno or WSAGetLastError. + mutable std::string message; ///< The string representation of the error (set on first call to #AsString). +public: + NetworkError(int error); + + bool HasError() const; + bool WouldBlock() const; + bool IsConnectionReset() const; + bool IsConnectInProgress() const; + const char *AsString() const; + + static NetworkError GetLast(); +}; + /* Include standard stuff per OS */ /* Windows stuff */ @@ -23,21 +43,6 @@ #include #include -/** - * Get the last error code from any of the OS's network functions. - * What it returns and when it is reset, is implementation defined. - * @return The last error code. - */ -#define NetworkGetLastError() WSAGetLastError() -#undef EWOULDBLOCK -#define EWOULDBLOCK WSAEWOULDBLOCK -#undef ECONNRESET -#define ECONNRESET WSAECONNRESET -#undef EINPROGRESS -#define EINPROGRESS WSAEWOULDBLOCK - -const char *NetworkGetErrorString(int error); - /* Windows has some different names for some types */ typedef unsigned long in_addr_t; @@ -63,8 +68,6 @@ typedef unsigned long in_addr_t; # define INVALID_SOCKET -1 # define ioctlsocket ioctl # define closesocket close -# define NetworkGetLastError() (errno) -# define NetworkGetErrorString(error) (strerror(error)) /* Need this for FIONREAD on solaris */ # define BSD_COMP @@ -114,8 +117,6 @@ typedef unsigned long in_addr_t; # define INVALID_SOCKET -1 # define ioctlsocket ioctl # define closesocket close -# define NetworkGetLastError() (sock_errno()) -# define NetworkGetErrorString(error) (strerror(error)) /* Includes needed for OS/2 systems */ # include @@ -187,15 +188,6 @@ static inline socklen_t FixAddrLenForEmscripten(struct sockaddr_storage &address } #endif -/** - * Return the string representation of the last error from the OS's network functions. - * @return The error message, potentially an empty string but never \c nullptr. - */ -static inline const char *NetworkGetLastErrorString() -{ - return NetworkGetErrorString(NetworkGetLastError()); -} - /** * Try to set the socket into non-blocking mode. * @param d The socket to set the non-blocking more for. @@ -237,13 +229,13 @@ static inline bool SetNoDelay(SOCKET d) * @param d The socket to get the error from. * @return The errno on the socket. */ -static inline int GetSocketError(SOCKET d) +static inline NetworkError GetSocketError(SOCKET d) { int err; socklen_t len = sizeof(err); getsockopt(d, SOL_SOCKET, SO_ERROR, (char *)&err, &len); - return err; + return NetworkError(err); } /* Make sure these structures have the size we expect them to be */ diff --git a/src/network/core/tcp.cpp b/src/network/core/tcp.cpp index f23b202c8b..842e1a89b9 100644 --- a/src/network/core/tcp.cpp +++ b/src/network/core/tcp.cpp @@ -86,11 +86,11 @@ SendPacketsState NetworkTCPSocketHandler::SendPackets(bool closing_down) while ((p = this->packet_queue) != nullptr) { res = p->TransferOut(send, this->sock, 0); if (res == -1) { - int err = NetworkGetLastError(); - if (err != EWOULDBLOCK) { + NetworkError err = NetworkError::GetLast(); + if (!err.WouldBlock()) { /* Something went wrong.. close client! */ if (!closing_down) { - DEBUG(net, 0, "send failed with error %s", NetworkGetErrorString(err)); + DEBUG(net, 0, "send failed with error %s", err.AsString()); this->CloseConnection(); } return SPS_CLOSED; @@ -136,10 +136,10 @@ Packet *NetworkTCPSocketHandler::ReceivePacket() while (p->RemainingBytesToTransfer() != 0) { res = p->TransferIn(recv, this->sock, 0); if (res == -1) { - int err = NetworkGetLastError(); - if (err != EWOULDBLOCK) { - /* Something went wrong... (ECONNRESET is connection reset by peer) */ - if (err != ECONNRESET) DEBUG(net, 0, "recv failed with error %s", NetworkGetErrorString(err)); + NetworkError err = NetworkError::GetLast(); + if (!err.WouldBlock()) { + /* Something went wrong... */ + if (!err.IsConnectionReset()) DEBUG(net, 0, "recv failed with error %s", err.AsString()); this->CloseConnection(); return nullptr; } @@ -164,10 +164,10 @@ Packet *NetworkTCPSocketHandler::ReceivePacket() while (p->RemainingBytesToTransfer() != 0) { res = p->TransferIn(recv, this->sock, 0); if (res == -1) { - int err = NetworkGetLastError(); - if (err != EWOULDBLOCK) { - /* Something went wrong... (ECONNRESET is connection reset by peer) */ - if (err != ECONNRESET) DEBUG(net, 0, "recv failed with error %s", NetworkGetErrorString(err)); + NetworkError err = NetworkError::GetLast(); + if (!err.WouldBlock()) { + /* Something went wrong... */ + if (!err.IsConnectionReset()) DEBUG(net, 0, "recv failed with error %s", err.AsString()); this->CloseConnection(); return nullptr; } diff --git a/src/network/core/tcp_http.cpp b/src/network/core/tcp_http.cpp index e0c269fafb..4f29df1912 100644 --- a/src/network/core/tcp_http.cpp +++ b/src/network/core/tcp_http.cpp @@ -225,10 +225,10 @@ int NetworkHTTPSocketHandler::Receive() for (;;) { ssize_t res = recv(this->sock, (char *)this->recv_buffer + this->recv_pos, lengthof(this->recv_buffer) - this->recv_pos, 0); if (res == -1) { - int err = NetworkGetLastError(); - if (err != EWOULDBLOCK) { - /* Something went wrong... (ECONNRESET is connection reset by peer) */ - if (err != ECONNRESET) DEBUG(net, 0, "recv failed with error %s", NetworkGetErrorString(err)); + NetworkError err = NetworkError::GetLast(); + if (!err.WouldBlock()) { + /* Something went wrong... */ + if (!err.IsConnectionReset()) DEBUG(net, 0, "recv failed with error %s", err.AsString()); return -1; } /* Connection would block, so stop for now */ diff --git a/src/network/core/tcp_listen.h b/src/network/core/tcp_listen.h index 2ceea20aa9..e23ecae707 100644 --- a/src/network/core/tcp_listen.h +++ b/src/network/core/tcp_listen.h @@ -64,7 +64,7 @@ public: DEBUG(net, 1, "[%s] Banned ip tried to join (%s), refused", Tsocket::GetName(), entry.c_str()); if (p.TransferOut(send, s, 0) < 0) { - DEBUG(net, 0, "send failed with error %s", NetworkGetLastErrorString()); + DEBUG(net, 0, "send failed with error %s", NetworkError::GetLast().AsString()); } closesocket(s); break; @@ -81,7 +81,7 @@ public: p.PrepareToSend(); if (p.TransferOut(send, s, 0) < 0) { - DEBUG(net, 0, "send failed with error %s", NetworkGetLastErrorString()); + DEBUG(net, 0, "send failed with error %s", NetworkError::GetLast().AsString()); } closesocket(s); diff --git a/src/network/core/udp.cpp b/src/network/core/udp.cpp index ffc86d825f..e7b99a53e8 100644 --- a/src/network/core/udp.cpp +++ b/src/network/core/udp.cpp @@ -95,7 +95,7 @@ void NetworkUDPSocketHandler::SendPacket(Packet *p, NetworkAddress *recv, bool a /* Enable broadcast */ unsigned long val = 1; if (setsockopt(s.second, SOL_SOCKET, SO_BROADCAST, (char *) &val, sizeof(val)) < 0) { - DEBUG(net, 1, "[udp] setting broadcast failed with: %s", NetworkGetLastErrorString()); + DEBUG(net, 1, "[udp] setting broadcast failed with: %s", NetworkError::GetLast().AsString()); } } @@ -104,7 +104,7 @@ void NetworkUDPSocketHandler::SendPacket(Packet *p, NetworkAddress *recv, bool a DEBUG(net, 7, "[udp] sendto(%s)", send.GetAddressAsString().c_str()); /* Check for any errors, but ignore it otherwise */ - if (res == -1) DEBUG(net, 1, "[udp] sendto(%s) failed with: %s", send.GetAddressAsString().c_str(), NetworkGetLastErrorString()); + if (res == -1) DEBUG(net, 1, "[udp] sendto(%s) failed with: %s", send.GetAddressAsString().c_str(), NetworkError::GetLast().AsString()); if (!all) break; } diff --git a/src/safeguards.h b/src/safeguards.h index e3d6c4a3e4..aca461175f 100644 --- a/src/safeguards.h +++ b/src/safeguards.h @@ -70,15 +70,15 @@ #endif #if defined(NETWORK_CORE_OS_ABSTRACTION_H) && defined(_WIN32) -/* Use NetworkGetLastError() instead of errno, or do not (indirectly) include network/core/os_abstraction.h. - * Winsock does not set errno, but one should rather call WSAGetLastError. NetworkGetLastError abstracts that away. */ +/* Use NetworkError::GetLast() instead of errno, or do not (indirectly) include network/core/os_abstraction.h. + * Winsock does not set errno, but one should rather call WSAGetLastError. NetworkError::GetLast abstracts that away. */ #ifdef errno #undef errno #endif #define errno SAFEGUARD_DO_NOT_USE_THIS_METHOD -/* Use NetworkGetLastErrorString() instead of strerror, or do not (indirectly) include network/core/os_abstraction.h. - * Winsock errors are not handled by strerror, but one should rather call FormatMessage. NetworkGetLastErrorString abstracts that away. */ +/* Use NetworkError::AsString() instead of strerror, or do not (indirectly) include network/core/os_abstraction.h. + * Winsock errors are not handled by strerror, but one should rather call FormatMessage. NetworkError::AsString abstracts that away. */ #define strerror SAFEGUARD_DO_NOT_USE_THIS_METHOD #endif /* defined(NETWORK_CORE_OS_ABSTRACTION_H) && defined(_WIN32) */ From e097c83c83ac3be81041a67f8d641650045502fb Mon Sep 17 00:00:00 2001 From: rubidium42 Date: Fri, 30 Apr 2021 19:21:02 +0200 Subject: [PATCH 247/268] Codechange: move some OS abstraction method implementations out of the header --- src/network/core/os_abstraction.cpp | 49 ++++++++++++++++++++++++++ src/network/core/os_abstraction.h | 53 ++--------------------------- 2 files changed, 52 insertions(+), 50 deletions(-) diff --git a/src/network/core/os_abstraction.cpp b/src/network/core/os_abstraction.cpp index 75f2224eb0..b2d3475d01 100644 --- a/src/network/core/os_abstraction.cpp +++ b/src/network/core/os_abstraction.cpp @@ -123,3 +123,52 @@ bool NetworkError::HasError() const return NetworkError(errno); #endif } + + +/** + * Try to set the socket into non-blocking mode. + * @param d The socket to set the non-blocking more for. + * @return True if setting the non-blocking mode succeeded, otherwise false. + */ +bool SetNonBlocking(SOCKET d) +{ +#if defined(_WIN32) + u_long nonblocking = 1; + return ioctlsocket(d, FIONBIO, &nonblocking) == 0; +#elif defined __EMSCRIPTEN__ + return true; +#else + int nonblocking = 1; + return ioctl(d, FIONBIO, &nonblocking) == 0; +#endif +} + +/** + * Try to set the socket to not delay sending. + * @param d The socket to disable the delaying for. + * @return True if disabling the delaying succeeded, otherwise false. + */ +bool SetNoDelay(SOCKET d) +{ +#ifdef __EMSCRIPTEN__ + return true; +#else + int flags = 1; + /* The (const char*) cast is needed for windows */ + return setsockopt(d, IPPROTO_TCP, TCP_NODELAY, (const char *)&flags, sizeof(flags)) == 0; +#endif +} + +/** + * Get the error from a socket, if any. + * @param d The socket to get the error from. + * @return The errno on the socket. + */ +NetworkError GetSocketError(SOCKET d) +{ + int err; + socklen_t len = sizeof(err); + getsockopt(d, SOL_SOCKET, SO_ERROR, (char *)&err, &len); + + return NetworkError(err); +} diff --git a/src/network/core/os_abstraction.h b/src/network/core/os_abstraction.h index e444bc78b4..55d7335017 100644 --- a/src/network/core/os_abstraction.h +++ b/src/network/core/os_abstraction.h @@ -66,7 +66,6 @@ typedef unsigned long in_addr_t; # endif # define SOCKET int # define INVALID_SOCKET -1 -# define ioctlsocket ioctl # define closesocket close /* Need this for FIONREAD on solaris */ # define BSD_COMP @@ -115,7 +114,6 @@ typedef unsigned long in_addr_t; #if defined(__OS2__) # define SOCKET int # define INVALID_SOCKET -1 -# define ioctlsocket ioctl # define closesocket close /* Includes needed for OS/2 systems */ @@ -188,55 +186,10 @@ static inline socklen_t FixAddrLenForEmscripten(struct sockaddr_storage &address } #endif -/** - * Try to set the socket into non-blocking mode. - * @param d The socket to set the non-blocking more for. - * @return True if setting the non-blocking mode succeeded, otherwise false. - */ -static inline bool SetNonBlocking(SOCKET d) -{ -#ifdef __EMSCRIPTEN__ - return true; -#else -# ifdef _WIN32 - u_long nonblocking = 1; -# else - int nonblocking = 1; -# endif - return ioctlsocket(d, FIONBIO, &nonblocking) == 0; -#endif -} -/** - * Try to set the socket to not delay sending. - * @param d The socket to disable the delaying for. - * @return True if disabling the delaying succeeded, otherwise false. - */ -static inline bool SetNoDelay(SOCKET d) -{ -#ifdef __EMSCRIPTEN__ - return true; -#else - /* XXX should this be done at all? */ - int b = 1; - /* The (const char*) cast is needed for windows */ - return setsockopt(d, IPPROTO_TCP, TCP_NODELAY, (const char*)&b, sizeof(b)) == 0; -#endif -} - -/** - * Get the error from a socket, if any. - * @param d The socket to get the error from. - * @return The errno on the socket. - */ -static inline NetworkError GetSocketError(SOCKET d) -{ - int err; - socklen_t len = sizeof(err); - getsockopt(d, SOL_SOCKET, SO_ERROR, (char *)&err, &len); - - return NetworkError(err); -} +bool SetNonBlocking(SOCKET d); +bool SetNoDelay(SOCKET d); +NetworkError GetSocketError(SOCKET d); /* Make sure these structures have the size we expect them to be */ static_assert(sizeof(in_addr) == 4); ///< IPv4 addresses should be 4 bytes. From f785a70a2bc08eaefcb1e2a43b8e6d36a154a00e Mon Sep 17 00:00:00 2001 From: Peter Nelson Date: Sat, 1 May 2021 23:58:18 +0100 Subject: [PATCH 248/268] Cleanup: Use std::vector in RealSpriteGroup. --- src/newgrf.cpp | 9 ++------- src/newgrf_airport.cpp | 4 ++-- src/newgrf_canal.cpp | 2 +- src/newgrf_cargo.cpp | 4 ++-- src/newgrf_engine.cpp | 6 +++--- src/newgrf_generic.cpp | 2 +- src/newgrf_railtype.cpp | 4 ++-- src/newgrf_roadtype.cpp | 4 ++-- src/newgrf_spritegroup.cpp | 6 ------ src/newgrf_spritegroup.h | 7 ++----- src/newgrf_station.cpp | 8 ++++---- 11 files changed, 21 insertions(+), 35 deletions(-) diff --git a/src/newgrf.cpp b/src/newgrf.cpp index 19cc436dd7..1d30cb2d75 100644 --- a/src/newgrf.cpp +++ b/src/newgrf.cpp @@ -5181,23 +5181,18 @@ static void NewSpriteGroup(ByteReader *buf) group->nfo_line = _cur.nfo_line; act_group = group; - group->num_loaded = num_loaded; - group->num_loading = num_loading; - if (num_loaded > 0) group->loaded = CallocT(num_loaded); - if (num_loading > 0) group->loading = CallocT(num_loading); - grfmsg(6, "NewSpriteGroup: New SpriteGroup 0x%02X, %u loaded, %u loading", setid, num_loaded, num_loading); for (uint i = 0; i < num_loaded; i++) { uint16 spriteid = buf->ReadWord(); - group->loaded[i] = CreateGroupFromGroupID(feature, setid, type, spriteid); + group->loaded.push_back(CreateGroupFromGroupID(feature, setid, type, spriteid)); grfmsg(8, "NewSpriteGroup: + rg->loaded[%i] = subset %u", i, spriteid); } for (uint i = 0; i < num_loading; i++) { uint16 spriteid = buf->ReadWord(); - group->loading[i] = CreateGroupFromGroupID(feature, setid, type, spriteid); + group->loading.push_back(CreateGroupFromGroupID(feature, setid, type, spriteid)); grfmsg(8, "NewSpriteGroup: + rg->loading[%i] = subset %u", i, spriteid); } diff --git a/src/newgrf_airport.cpp b/src/newgrf_airport.cpp index 61ad46ac0e..86b65fd0e3 100644 --- a/src/newgrf_airport.cpp +++ b/src/newgrf_airport.cpp @@ -223,8 +223,8 @@ void AirportOverrideManager::SetEntitySpec(AirportSpec *as) { /* Airport action 2s should always have only 1 "loaded" state, but some * times things don't follow the spec... */ - if (group->num_loaded > 0) return group->loaded[0]; - if (group->num_loading > 0) return group->loading[0]; + if (!group->loaded.empty()) return group->loaded[0]; + if (!group->loading.empty()) return group->loading[0]; return nullptr; } diff --git a/src/newgrf_canal.cpp b/src/newgrf_canal.cpp index 7295e5551b..6b3c9b3d42 100644 --- a/src/newgrf_canal.cpp +++ b/src/newgrf_canal.cpp @@ -111,7 +111,7 @@ struct CanalResolverObject : public ResolverObject { /* virtual */ const SpriteGroup *CanalResolverObject::ResolveReal(const RealSpriteGroup *group) const { - if (group->num_loaded == 0) return nullptr; + if (group->loaded.empty()) return nullptr; return group->loaded[0]; } diff --git a/src/newgrf_cargo.cpp b/src/newgrf_cargo.cpp index c2859b71ef..105a2b2524 100644 --- a/src/newgrf_cargo.cpp +++ b/src/newgrf_cargo.cpp @@ -29,8 +29,8 @@ struct CargoResolverObject : public ResolverObject { { /* Cargo action 2s should always have only 1 "loaded" state, but some * times things don't follow the spec... */ - if (group->num_loaded > 0) return group->loaded[0]; - if (group->num_loading > 0) return group->loading[0]; + if (!group->loaded.empty()) return group->loaded[0]; + if (!group->loading.empty()) return group->loading[0]; return nullptr; } diff --git a/src/newgrf_engine.cpp b/src/newgrf_engine.cpp index cd12148558..e028b7a7d6 100644 --- a/src/newgrf_engine.cpp +++ b/src/newgrf_engine.cpp @@ -992,14 +992,14 @@ static uint32 VehicleGetVariable(Vehicle *v, const VehicleScopeResolver *object, const Vehicle *v = this->self_scope.v; if (v == nullptr) { - if (group->num_loading > 0) return group->loading[0]; - if (group->num_loaded > 0) return group->loaded[0]; + if (!group->loading.empty()) return group->loading[0]; + if (!group->loaded.empty()) return group->loaded[0]; return nullptr; } bool in_motion = !v->First()->current_order.IsType(OT_LOADING); - uint totalsets = in_motion ? group->num_loaded : group->num_loading; + uint totalsets = in_motion ? (uint)group->loaded.size() : (uint)group->loading.size(); if (totalsets == 0) return nullptr; diff --git a/src/newgrf_generic.cpp b/src/newgrf_generic.cpp index 6538b79b66..0a138f1d64 100644 --- a/src/newgrf_generic.cpp +++ b/src/newgrf_generic.cpp @@ -150,7 +150,7 @@ void AddGenericCallback(uint8 feature, const GRFFile *file, const SpriteGroup *g /* virtual */ const SpriteGroup *GenericResolverObject::ResolveReal(const RealSpriteGroup *group) const { - if (group->num_loaded == 0) return nullptr; + if (group->loaded.empty()) return nullptr; return group->loaded[0]; } diff --git a/src/newgrf_railtype.cpp b/src/newgrf_railtype.cpp index 326ee80baf..407acc9844 100644 --- a/src/newgrf_railtype.cpp +++ b/src/newgrf_railtype.cpp @@ -60,8 +60,8 @@ /* virtual */ const SpriteGroup *RailTypeResolverObject::ResolveReal(const RealSpriteGroup *group) const { - if (group->num_loading > 0) return group->loading[0]; - if (group->num_loaded > 0) return group->loaded[0]; + if (!group->loading.empty()) return group->loading[0]; + if (!group->loaded.empty()) return group->loaded[0]; return nullptr; } diff --git a/src/newgrf_roadtype.cpp b/src/newgrf_roadtype.cpp index 025d03bb6a..9243bf77e3 100644 --- a/src/newgrf_roadtype.cpp +++ b/src/newgrf_roadtype.cpp @@ -60,8 +60,8 @@ /* virtual */ const SpriteGroup *RoadTypeResolverObject::ResolveReal(const RealSpriteGroup *group) const { - if (group->num_loading > 0) return group->loading[0]; - if (group->num_loaded > 0) return group->loaded[0]; + if (!group->loading.empty()) return group->loading[0]; + if (!group->loaded.empty()) return group->loaded[0]; return nullptr; } diff --git a/src/newgrf_spritegroup.cpp b/src/newgrf_spritegroup.cpp index 871108ec47..fb0d65c319 100644 --- a/src/newgrf_spritegroup.cpp +++ b/src/newgrf_spritegroup.cpp @@ -53,12 +53,6 @@ TemporaryStorageArray _temp_store; } } -RealSpriteGroup::~RealSpriteGroup() -{ - free(this->loaded); - free(this->loading); -} - DeterministicSpriteGroup::~DeterministicSpriteGroup() { free(this->adjusts); diff --git a/src/newgrf_spritegroup.h b/src/newgrf_spritegroup.h index 80f70df55d..a931597a03 100644 --- a/src/newgrf_spritegroup.h +++ b/src/newgrf_spritegroup.h @@ -78,7 +78,6 @@ public: * groups. */ struct RealSpriteGroup : SpriteGroup { RealSpriteGroup() : SpriteGroup(SGT_REAL) {} - ~RealSpriteGroup(); /* Loaded = in motion, loading = not moving * Each group contains several spritesets, for various loading stages */ @@ -87,10 +86,8 @@ struct RealSpriteGroup : SpriteGroup { * with small amount of cargo whilst loading is for stations with a lot * of da stuff. */ - byte num_loaded; ///< Number of loaded groups - byte num_loading; ///< Number of loading groups - const SpriteGroup **loaded; ///< List of loaded groups (can be SpriteIDs or Callback results) - const SpriteGroup **loading; ///< List of loading groups (can be SpriteIDs or Callback results) + std::vector loaded; ///< List of loaded groups (can be SpriteIDs or Callback results) + std::vector loading; ///< List of loading groups (can be SpriteIDs or Callback results) protected: const SpriteGroup *Resolve(ResolverObject &object) const; diff --git a/src/newgrf_station.cpp b/src/newgrf_station.cpp index fa3f831aa5..9aa3ad43be 100644 --- a/src/newgrf_station.cpp +++ b/src/newgrf_station.cpp @@ -523,13 +523,13 @@ uint32 Waypoint::GetNewGRFVariable(const ResolverObject &object, byte variable, cargo = std::min(0xfffu, cargo); if (cargo > this->station_scope.statspec->cargo_threshold) { - if (group->num_loading > 0) { - uint set = ((cargo - this->station_scope.statspec->cargo_threshold) * group->num_loading) / (4096 - this->station_scope.statspec->cargo_threshold); + if (!group->loading.empty()) { + uint set = ((cargo - this->station_scope.statspec->cargo_threshold) * (uint)group->loading.size()) / (4096 - this->station_scope.statspec->cargo_threshold); return group->loading[set]; } } else { - if (group->num_loaded > 0) { - uint set = (cargo * group->num_loaded) / (this->station_scope.statspec->cargo_threshold + 1); + if (!group->loaded.empty()) { + uint set = (cargo * (uint)group->loaded.size()) / (this->station_scope.statspec->cargo_threshold + 1); return group->loaded[set]; } } From 1aeaf399541c229973b576f2f2423fa8c4b49f65 Mon Sep 17 00:00:00 2001 From: Peter Nelson Date: Sun, 2 May 2021 00:00:06 +0100 Subject: [PATCH 249/268] Cleanup: Use std::vector in DeterministicSpriteGroup. --- src/newgrf.cpp | 20 +++----------------- src/newgrf_spritegroup.cpp | 22 ++++++++-------------- src/newgrf_spritegroup.h | 7 ++----- 3 files changed, 13 insertions(+), 36 deletions(-) diff --git a/src/newgrf.cpp b/src/newgrf.cpp index 1d30cb2d75..ba5c098a2d 100644 --- a/src/newgrf.cpp +++ b/src/newgrf.cpp @@ -5024,16 +5024,13 @@ static void NewSpriteGroup(ByteReader *buf) case 2: group->size = DSG_SIZE_DWORD; varsize = 4; break; } - static std::vector adjusts; - adjusts.clear(); - /* Loop through the var adjusts. Unfortunately we don't know how many we have * from the outset, so we shall have to keep reallocing. */ do { - DeterministicSpriteGroupAdjust &adjust = adjusts.emplace_back(); + DeterministicSpriteGroupAdjust &adjust = group->adjusts.emplace_back(); /* The first var adjust doesn't have an operation specified, so we set it to add. */ - adjust.operation = adjusts.size() == 1 ? DSGA_OP_ADD : (DeterministicSpriteGroupAdjustOperation)buf->ReadByte(); + adjust.operation = group->adjusts.size() == 1 ? DSGA_OP_ADD : (DeterministicSpriteGroupAdjustOperation)buf->ReadByte(); adjust.variable = buf->ReadByte(); if (adjust.variable == 0x7E) { /* Link subroutine group */ @@ -5058,10 +5055,6 @@ static void NewSpriteGroup(ByteReader *buf) /* Continue reading var adjusts while bit 5 is set. */ } while (HasBit(varadjust, 5)); - group->num_adjusts = (uint)adjusts.size(); - group->adjusts = MallocT(group->num_adjusts); - MemCpyT(group->adjusts, adjusts.data(), group->num_adjusts); - std::vector ranges; ranges.resize(buf->ReadByte()); for (uint i = 0; i < ranges.size(); i++) { @@ -5098,27 +5091,20 @@ static void NewSpriteGroup(ByteReader *buf) } assert(target.size() == bounds.size()); - std::vector optimised; for (uint j = 0; j < bounds.size(); ) { if (target[j] != group->default_group) { - DeterministicSpriteGroupRange r; + DeterministicSpriteGroupRange &r = group->ranges.emplace_back(); r.group = target[j]; r.low = bounds[j]; while (j < bounds.size() && target[j] == r.group) { j++; } r.high = j < bounds.size() ? bounds[j] - 1 : UINT32_MAX; - optimised.push_back(r); } else { j++; } } - group->num_ranges = (uint)optimised.size(); // cast is safe, there should never be 2**31 elements here - if (group->num_ranges > 0) { - group->ranges = MallocT(group->num_ranges); - MemCpyT(group->ranges, &optimised.front(), group->num_ranges); - } break; } diff --git a/src/newgrf_spritegroup.cpp b/src/newgrf_spritegroup.cpp index fb0d65c319..058baf334e 100644 --- a/src/newgrf_spritegroup.cpp +++ b/src/newgrf_spritegroup.cpp @@ -53,12 +53,6 @@ TemporaryStorageArray _temp_store; } } -DeterministicSpriteGroup::~DeterministicSpriteGroup() -{ - free(this->adjusts); - free(this->ranges); -} - RandomizedSpriteGroup::~RandomizedSpriteGroup() { free(this->groups); @@ -205,8 +199,8 @@ const SpriteGroup *DeterministicSpriteGroup::Resolve(ResolverObject &object) con ScopeResolver *scope = object.GetScope(this->var_scope); - for (i = 0; i < this->num_adjusts; i++) { - DeterministicSpriteGroupAdjust *adjust = &this->adjusts[i]; + for (i = 0; i < this->adjusts.size(); i++) { + const DeterministicSpriteGroupAdjust *adjust = &this->adjusts[i]; /* Try to get the variable. We shall assume it is available, unless told otherwise. */ bool available = true; @@ -250,16 +244,16 @@ const SpriteGroup *DeterministicSpriteGroup::Resolve(ResolverObject &object) con return &nvarzero; } - if (this->num_ranges > 4) { - DeterministicSpriteGroupRange *lower = std::lower_bound(this->ranges + 0, this->ranges + this->num_ranges, value, RangeHighComparator); - if (lower != this->ranges + this->num_ranges && lower->low <= value) { + if (this->ranges.size() > 4) { + const auto &lower = std::lower_bound(this->ranges.begin(), this->ranges.end(), value, RangeHighComparator); + if (lower != this->ranges.end() && lower->low <= value) { assert(lower->low <= value && value <= lower->high); return SpriteGroup::Resolve(lower->group, object, false); } } else { - for (i = 0; i < this->num_ranges; i++) { - if (this->ranges[i].low <= value && value <= this->ranges[i].high) { - return SpriteGroup::Resolve(this->ranges[i].group, object, false); + for (const auto &range : this->ranges) { + if (range.low <= value && value <= range.high) { + return SpriteGroup::Resolve(range.group, object, false); } } } diff --git a/src/newgrf_spritegroup.h b/src/newgrf_spritegroup.h index a931597a03..e91987dbb6 100644 --- a/src/newgrf_spritegroup.h +++ b/src/newgrf_spritegroup.h @@ -166,15 +166,12 @@ struct DeterministicSpriteGroupRange { struct DeterministicSpriteGroup : SpriteGroup { DeterministicSpriteGroup() : SpriteGroup(SGT_DETERMINISTIC) {} - ~DeterministicSpriteGroup(); VarSpriteGroupScope var_scope; DeterministicSpriteGroupSize size; - uint num_adjusts; - uint num_ranges; bool calculated_result; - DeterministicSpriteGroupAdjust *adjusts; - DeterministicSpriteGroupRange *ranges; // Dynamically allocated + std::vector adjusts; + std::vector ranges; // Dynamically allocated /* Dynamically allocated, this is the sole owner */ const SpriteGroup *default_group; From 913d8a7f28cade14577fc147e3bb42fa7d75cad7 Mon Sep 17 00:00:00 2001 From: Peter Nelson Date: Sun, 2 May 2021 00:00:40 +0100 Subject: [PATCH 250/268] Cleanup: Use std::vector in RandomSpriteGroup. --- src/newgrf.cpp | 7 +++---- src/newgrf_spritegroup.cpp | 9 ++------- src/newgrf_spritegroup.h | 4 +--- 3 files changed, 6 insertions(+), 14 deletions(-) diff --git a/src/newgrf.cpp b/src/newgrf.cpp index ba5c098a2d..0377c0bc53 100644 --- a/src/newgrf.cpp +++ b/src/newgrf.cpp @@ -5128,11 +5128,10 @@ static void NewSpriteGroup(ByteReader *buf) group->triggers = GB(triggers, 0, 7); group->cmp_mode = HasBit(triggers, 7) ? RSG_CMP_ALL : RSG_CMP_ANY; group->lowest_randbit = buf->ReadByte(); - group->num_groups = buf->ReadByte(); - group->groups = CallocT(group->num_groups); - for (uint i = 0; i < group->num_groups; i++) { - group->groups[i] = GetGroupFromGroupID(setid, type, buf->ReadWord()); + byte num_groups = buf->ReadByte(); + for (uint i = 0; i < num_groups; i++) { + group->groups.push_back(GetGroupFromGroupID(setid, type, buf->ReadWord())); } break; diff --git a/src/newgrf_spritegroup.cpp b/src/newgrf_spritegroup.cpp index 058baf334e..eef531fb22 100644 --- a/src/newgrf_spritegroup.cpp +++ b/src/newgrf_spritegroup.cpp @@ -53,11 +53,6 @@ TemporaryStorageArray _temp_store; } } -RandomizedSpriteGroup::~RandomizedSpriteGroup() -{ - free(this->groups); -} - static inline uint32 GetVariable(const ResolverObject &object, ScopeResolver *scope, byte variable, uint32 parameter, bool *available) { uint32 value; @@ -272,11 +267,11 @@ const SpriteGroup *RandomizedSpriteGroup::Resolve(ResolverObject &object) const if (res) { object.used_triggers |= match; - object.reseed[this->var_scope] |= (this->num_groups - 1) << this->lowest_randbit; + object.reseed[this->var_scope] |= (this->groups.size() - 1) << this->lowest_randbit; } } - uint32 mask = (this->num_groups - 1) << this->lowest_randbit; + uint32 mask = ((uint)this->groups.size() - 1) << this->lowest_randbit; byte index = (scope->GetRandomBits() & mask) >> this->lowest_randbit; return SpriteGroup::Resolve(this->groups[index], object, false); diff --git a/src/newgrf_spritegroup.h b/src/newgrf_spritegroup.h index e91987dbb6..b172667612 100644 --- a/src/newgrf_spritegroup.h +++ b/src/newgrf_spritegroup.h @@ -189,7 +189,6 @@ enum RandomizedSpriteGroupCompareMode { struct RandomizedSpriteGroup : SpriteGroup { RandomizedSpriteGroup() : SpriteGroup(SGT_RANDOMIZED) {} - ~RandomizedSpriteGroup(); VarSpriteGroupScope var_scope; ///< Take this object: @@ -198,9 +197,8 @@ struct RandomizedSpriteGroup : SpriteGroup { byte count; byte lowest_randbit; ///< Look for this in the per-object randomized bitmask: - byte num_groups; ///< must be power of 2 - const SpriteGroup **groups; ///< Take the group with appropriate index: + std::vector groups; ///< Take the group with appropriate index: protected: const SpriteGroup *Resolve(ResolverObject &object) const; From 6b0b1bb3de17c92881d5a570736103d22c403368 Mon Sep 17 00:00:00 2001 From: Peter Nelson Date: Sun, 2 May 2021 00:04:34 +0100 Subject: [PATCH 251/268] Cleanup: Use range iterator to evaluate DeterministicSpriteGroup. --- src/newgrf_spritegroup.cpp | 29 +++++++++++++---------------- 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/src/newgrf_spritegroup.cpp b/src/newgrf_spritegroup.cpp index eef531fb22..29080894ec 100644 --- a/src/newgrf_spritegroup.cpp +++ b/src/newgrf_spritegroup.cpp @@ -141,18 +141,18 @@ static inline uint32 GetVariable(const ResolverObject &object, ScopeResolver *sc /* Evaluate an adjustment for a variable of the given size. * U is the unsigned type and S is the signed type to use. */ template -static U EvalAdjustT(const DeterministicSpriteGroupAdjust *adjust, ScopeResolver *scope, U last_value, uint32 value) +static U EvalAdjustT(const DeterministicSpriteGroupAdjust &adjust, ScopeResolver *scope, U last_value, uint32 value) { - value >>= adjust->shift_num; - value &= adjust->and_mask; + value >>= adjust.shift_num; + value &= adjust.and_mask; - switch (adjust->type) { - case DSGA_TYPE_DIV: value = ((S)value + (S)adjust->add_val) / (S)adjust->divmod_val; break; - case DSGA_TYPE_MOD: value = ((S)value + (S)adjust->add_val) % (S)adjust->divmod_val; break; + switch (adjust.type) { + case DSGA_TYPE_DIV: value = ((S)value + (S)adjust.add_val) / (S)adjust.divmod_val; break; + case DSGA_TYPE_MOD: value = ((S)value + (S)adjust.add_val) % (S)adjust.divmod_val; break; case DSGA_TYPE_NONE: break; } - switch (adjust->operation) { + switch (adjust.operation) { case DSGA_OP_ADD: return last_value + value; case DSGA_OP_SUB: return last_value - value; case DSGA_OP_SMIN: return std::min(last_value, value); @@ -190,17 +190,14 @@ const SpriteGroup *DeterministicSpriteGroup::Resolve(ResolverObject &object) con { uint32 last_value = 0; uint32 value = 0; - uint i; ScopeResolver *scope = object.GetScope(this->var_scope); - for (i = 0; i < this->adjusts.size(); i++) { - const DeterministicSpriteGroupAdjust *adjust = &this->adjusts[i]; - + for (const auto &adjust : this->adjusts) { /* Try to get the variable. We shall assume it is available, unless told otherwise. */ bool available = true; - if (adjust->variable == 0x7E) { - const SpriteGroup *subgroup = SpriteGroup::Resolve(adjust->subroutine, object, false); + if (adjust.variable == 0x7E) { + const SpriteGroup *subgroup = SpriteGroup::Resolve(adjust.subroutine, object, false); if (subgroup == nullptr) { value = CALLBACK_FAILED; } else { @@ -208,10 +205,10 @@ const SpriteGroup *DeterministicSpriteGroup::Resolve(ResolverObject &object) con } /* Note: 'last_value' and 'reseed' are shared between the main chain and the procedure */ - } else if (adjust->variable == 0x7B) { - value = GetVariable(object, scope, adjust->parameter, last_value, &available); + } else if (adjust.variable == 0x7B) { + value = GetVariable(object, scope, adjust.parameter, last_value, &available); } else { - value = GetVariable(object, scope, adjust->variable, adjust->parameter, &available); + value = GetVariable(object, scope, adjust.variable, adjust.parameter, &available); } if (!available) { From 18fb1c386689f12d2a7e767d9372020c0a8d9fea Mon Sep 17 00:00:00 2001 From: Peter Nelson Date: Sun, 2 May 2021 00:34:17 +0100 Subject: [PATCH 252/268] Codechange: Warn if randomaction2 group count is not a power of 2. Previously noted by a comment, this does not need to be guarded against as non-powers of 2 will not cause issues beyond the choice of results being reduced. --- src/newgrf.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/newgrf.cpp b/src/newgrf.cpp index 0377c0bc53..a9727d38de 100644 --- a/src/newgrf.cpp +++ b/src/newgrf.cpp @@ -5130,6 +5130,10 @@ static void NewSpriteGroup(ByteReader *buf) group->lowest_randbit = buf->ReadByte(); byte num_groups = buf->ReadByte(); + if (!HasExactlyOneBit(num_groups)) { + grfmsg(1, "NewSpriteGroup: Random Action 2 nrand should be power of 2"); + } + for (uint i = 0; i < num_groups; i++) { group->groups.push_back(GetGroupFromGroupID(setid, type, buf->ReadWord())); } From 256dbee25597f4da8726063084f94eb821babf14 Mon Sep 17 00:00:00 2001 From: PeterN Date: Sun, 2 May 2021 10:21:27 +0100 Subject: [PATCH 253/268] Fix: Crash when extra viewport height is zero with sign in view. (#9175) If a viewport sign straddles the top of a viewport, a crash will occur if the viewport height is zero. This is resolved by simply not attempting to draw the viewport in this situation, consistent with other widgets. --- src/widget.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/widget.cpp b/src/widget.cpp index 68bc43d78f..5c1c0ae8fc 100644 --- a/src/widget.cpp +++ b/src/widget.cpp @@ -2029,6 +2029,8 @@ void NWidgetViewport::SetupSmallestSize(Window *w, bool init_array) void NWidgetViewport::Draw(const Window *w) { + if (this->current_x == 0 || this->current_y == 0) return; + if (this->disp_flags & ND_NO_TRANSPARENCY) { TransparencyOptionBits to_backup = _transparency_opt; _transparency_opt &= (1 << TO_SIGNS) | (1 << TO_LOADING); // Disable all transparency, except textual stuff From 18651dd8b13d8a427ae71d8af00792d52ad9ed60 Mon Sep 17 00:00:00 2001 From: PeterN Date: Sun, 2 May 2021 10:43:14 +0100 Subject: [PATCH 254/268] Fix: Update text effect size when font zoom is changed. (#9174) --- src/saveload/afterload.cpp | 1 + src/texteff.cpp | 11 ++++++++++- src/texteff.hpp | 1 + 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/saveload/afterload.cpp b/src/saveload/afterload.cpp index d4b230dfd0..56680511cb 100644 --- a/src/saveload/afterload.cpp +++ b/src/saveload/afterload.cpp @@ -219,6 +219,7 @@ void UpdateAllVirtCoords() UpdateAllStationVirtCoords(); UpdateAllSignVirtCoords(); UpdateAllTownVirtCoords(); + UpdateAllTextEffectVirtCoords(); RebuildViewportKdtree(); } diff --git a/src/texteff.cpp b/src/texteff.cpp index f08701939f..e29326a90a 100644 --- a/src/texteff.cpp +++ b/src/texteff.cpp @@ -73,7 +73,16 @@ void UpdateTextEffect(TextEffectID te_id, StringID msg) te->params_1 = GetDParam(0); te->params_2 = GetDParam(1); - te->UpdatePosition(te->center, te->top, msg); + te->UpdatePosition(te->center, te->top, te->string_id, te->string_id - 1); +} + +void UpdateAllTextEffectVirtCoords() +{ + for (auto &te : _text_effects) { + SetDParam(0, te.params_1); + SetDParam(1, te.params_2); + te.UpdatePosition(te.center, te.top, te.string_id, te.string_id - 1); + } } void RemoveTextEffect(TextEffectID te_id) diff --git a/src/texteff.hpp b/src/texteff.hpp index 789b12d540..d122b17e9b 100644 --- a/src/texteff.hpp +++ b/src/texteff.hpp @@ -32,6 +32,7 @@ void InitTextEffects(); void DrawTextEffects(DrawPixelInfo *dpi); void UpdateTextEffect(TextEffectID effect_id, StringID msg); void RemoveTextEffect(TextEffectID effect_id); +void UpdateAllTextEffectVirtCoords(); /* misc_gui.cpp */ TextEffectID ShowFillingPercent(int x, int y, int z, uint8 percent, StringID colour); From 56aa6d0eddb60fac20cde82cd496005369ca1b8d Mon Sep 17 00:00:00 2001 From: rubidium42 Date: Sun, 2 May 2021 11:05:50 +0200 Subject: [PATCH 255/268] Fix: [Network] Reading beyond the length of the server's ID when hashing password Under normal circumstances the server's ID is 32 characters excluding '\0', however this can be changed at the server. This ID is sent to the server for company name hashing. The client reads it into a statically allocated buffer of 33 bytes, but fills only the bytes it received from the server. However, the hash assumes all 33 bytes are set, thus potentially reading uninitialized data, or a part of the server ID of a previous game in the hashing routine. It is still reading from memory assigned to the server ID, so nothing bad happens, except that company passwords might not work correctly. --- src/network/network.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/network/network.cpp b/src/network/network.cpp index af732facdb..c00a3650fb 100644 --- a/src/network/network.cpp +++ b/src/network/network.cpp @@ -176,12 +176,15 @@ const char *GenerateCompanyPasswordHash(const char *password, const char *passwo if (StrEmpty(password)) return password; char salted_password[NETWORK_SERVER_ID_LENGTH]; + size_t password_length = strlen(password); + size_t password_server_id_length = strlen(password_server_id); - memset(salted_password, 0, sizeof(salted_password)); - seprintf(salted_password, lastof(salted_password), "%s", password); /* Add the game seed and the server's ID as the salt. */ for (uint i = 0; i < NETWORK_SERVER_ID_LENGTH - 1; i++) { - salted_password[i] ^= password_server_id[i] ^ (password_game_seed >> (i % 32)); + char password_char = (i < password_length ? password[i] : 0); + char server_id_char = (i < password_server_id_length ? password_server_id[i] : 0); + char seed_char = password_game_seed >> (i % 32); + salted_password[i] = password_char ^ server_id_char ^ seed_char; } Md5 checksum; From 84aa17cea6f677f2bd6cb0e1c33457dbdcda77dc Mon Sep 17 00:00:00 2001 From: frosch Date: Sat, 1 May 2021 23:59:53 +0200 Subject: [PATCH 256/268] Fix: [NewGRF] industry variables 65 and 66 ignored the parameter, and always used the north tile. --- src/newgrf_industries.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/newgrf_industries.cpp b/src/newgrf_industries.cpp index 5e59634e84..68f28148c7 100644 --- a/src/newgrf_industries.cpp +++ b/src/newgrf_industries.cpp @@ -281,13 +281,17 @@ static uint32 GetCountAndDistanceOfClosestInstance(byte param_setID, byte layout if (this->tile == INVALID_TILE) break; return GetClosestIndustry(this->tile, MapNewGRFIndustryType(parameter, indspec->grf_prop.grffile->grfid), this->industry); /* Get town zone and Manhattan distance of closest town */ - case 0x65: + case 0x65: { if (this->tile == INVALID_TILE) break; - return GetTownRadiusGroup(this->industry->town, this->tile) << 16 | std::min(DistanceManhattan(this->tile, this->industry->town->xy), 0xFFFFu); + TileIndex tile = GetNearbyTile(parameter, this->tile, true); + return GetTownRadiusGroup(this->industry->town, tile) << 16 | std::min(DistanceManhattan(tile, this->industry->town->xy), 0xFFFFu); + } /* Get square of Euclidian distance of closes town */ - case 0x66: + case 0x66: { if (this->tile == INVALID_TILE) break; - return GetTownRadiusGroup(this->industry->town, this->tile) << 16 | std::min(DistanceSquare(this->tile, this->industry->town->xy), 0xFFFFu); + TileIndex tile = GetNearbyTile(parameter, this->tile, true); + return GetTownRadiusGroup(this->industry->town, tile) << 16 | std::min(DistanceSquare(tile, this->industry->town->xy), 0xFFFFu); + } /* Count of industry, distance of closest instance * 68 is the same as 67, but with a filtering on selected layout */ From 2cf5df2a50e9f28ef4ba93a765bfcf79afcbb20c Mon Sep 17 00:00:00 2001 From: frosch Date: Sun, 2 May 2021 00:01:06 +0200 Subject: [PATCH 257/268] Fix: [NewGRF] industry variable 66 and object variable 46 clamped the squared-euclidian distance to 16 bit, when they should not. --- src/newgrf_industries.cpp | 4 ++-- src/newgrf_object.cpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/newgrf_industries.cpp b/src/newgrf_industries.cpp index 68f28148c7..abae446040 100644 --- a/src/newgrf_industries.cpp +++ b/src/newgrf_industries.cpp @@ -286,11 +286,11 @@ static uint32 GetCountAndDistanceOfClosestInstance(byte param_setID, byte layout TileIndex tile = GetNearbyTile(parameter, this->tile, true); return GetTownRadiusGroup(this->industry->town, tile) << 16 | std::min(DistanceManhattan(tile, this->industry->town->xy), 0xFFFFu); } - /* Get square of Euclidian distance of closes town */ + /* Get square of Euclidian distance of closest town */ case 0x66: { if (this->tile == INVALID_TILE) break; TileIndex tile = GetNearbyTile(parameter, this->tile, true); - return GetTownRadiusGroup(this->industry->town, tile) << 16 | std::min(DistanceSquare(tile, this->industry->town->xy), 0xFFFFu); + return DistanceSquare(tile, this->industry->town->xy); } /* Count of industry, distance of closest instance diff --git a/src/newgrf_object.cpp b/src/newgrf_object.cpp index 649a4b58a1..3c069f4c95 100644 --- a/src/newgrf_object.cpp +++ b/src/newgrf_object.cpp @@ -303,8 +303,8 @@ static uint32 GetCountAndDistanceOfClosestInstance(byte local_id, uint32 grfid, /* Get town zone and Manhattan distance of closest town */ case 0x45: return GetTownRadiusGroup(t, this->tile) << 16 | std::min(DistanceManhattan(this->tile, t->xy), 0xFFFFu); - /* Get square of Euclidian distance of closes town */ - case 0x46: return GetTownRadiusGroup(t, this->tile) << 16 | std::min(DistanceSquare(this->tile, t->xy), 0xFFFFu); + /* Get square of Euclidian distance of closest town */ + case 0x46: return DistanceSquare(this->tile, t->xy); /* Object colour */ case 0x47: return this->obj->colour; From 91b8ce073f54dff48cd61186c32d0a720a2abb4d Mon Sep 17 00:00:00 2001 From: Michael Lutz Date: Sat, 1 May 2021 19:39:03 +0200 Subject: [PATCH 258/268] Codechange: Generalise the delayed blitter change to a generic video driver command queue. --- src/gfxinit.cpp | 24 ++++++++++++++++++++++- src/video/video_driver.cpp | 26 +------------------------ src/video/video_driver.hpp | 39 ++++++++++++++++++++++++++++++-------- 3 files changed, 55 insertions(+), 34 deletions(-) diff --git a/src/gfxinit.cpp b/src/gfxinit.cpp index b744eaa5ef..c0ba4c4d4d 100644 --- a/src/gfxinit.cpp +++ b/src/gfxinit.cpp @@ -244,6 +244,28 @@ static void LoadSpriteTables() } +static void RealChangeBlitter(const char *repl_blitter) +{ + const char *cur_blitter = BlitterFactory::GetCurrentBlitter()->GetName(); + if (strcmp(cur_blitter, repl_blitter) == 0) return; + + DEBUG(driver, 1, "Switching blitter from '%s' to '%s'... ", cur_blitter, repl_blitter); + Blitter *new_blitter = BlitterFactory::SelectBlitter(repl_blitter); + if (new_blitter == nullptr) NOT_REACHED(); + DEBUG(driver, 1, "Successfully switched to %s.", repl_blitter); + + if (!VideoDriver::GetInstance()->AfterBlitterChange()) { + /* Failed to switch blitter, let's hope we can return to the old one. */ + if (BlitterFactory::SelectBlitter(cur_blitter) == nullptr || !VideoDriver::GetInstance()->AfterBlitterChange()) usererror("Failed to reinitialize video driver. Specify a fixed blitter in the config"); + } + + /* Clear caches that might have sprites for another blitter. */ + VideoDriver::GetInstance()->ClearSystemSprites(); + ClearFontCache(); + GfxClearSpriteCache(); + ReInitAllWindows(false); +} + /** * Check blitter needed by NewGRF config and switch if needed. * @return False when nothing changed, true otherwise. @@ -309,7 +331,7 @@ static bool SwitchNewGRFBlitter() if (BlitterFactory::GetBlitterFactory(repl_blitter) == nullptr) continue; /* Inform the video driver we want to switch blitter as soon as possible. */ - VideoDriver::GetInstance()->ChangeBlitter(repl_blitter); + VideoDriver::GetInstance()->QueueOnMainThread(std::bind(&RealChangeBlitter, repl_blitter)); break; } diff --git a/src/video/video_driver.cpp b/src/video/video_driver.cpp index d8fbe0400a..2f8efc4ad4 100644 --- a/src/video/video_driver.cpp +++ b/src/video/video_driver.cpp @@ -97,27 +97,6 @@ void VideoDriver::StopGameThread() this->game_thread.join(); } -void VideoDriver::RealChangeBlitter(const char *repl_blitter) -{ - const char *cur_blitter = BlitterFactory::GetCurrentBlitter()->GetName(); - - DEBUG(driver, 1, "Switching blitter from '%s' to '%s'... ", cur_blitter, repl_blitter); - Blitter *new_blitter = BlitterFactory::SelectBlitter(repl_blitter); - if (new_blitter == nullptr) NOT_REACHED(); - DEBUG(driver, 1, "Successfully switched to %s.", repl_blitter); - - if (!this->AfterBlitterChange()) { - /* Failed to switch blitter, let's hope we can return to the old one. */ - if (BlitterFactory::SelectBlitter(cur_blitter) == nullptr || !this->AfterBlitterChange()) usererror("Failed to reinitialize video driver. Specify a fixed blitter in the config"); - } - - /* Clear caches that might have sprites for another blitter. */ - this->ClearSystemSprites(); - ClearFontCache(); - GfxClearSpriteCache(); - ReInitAllWindows(false); -} - void VideoDriver::Tick() { if (!this->is_game_threaded && std::chrono::steady_clock::now() >= this->next_game_tick) { @@ -159,10 +138,7 @@ void VideoDriver::Tick() this->LockVideoBuffer(); - if (this->change_blitter != nullptr) { - this->RealChangeBlitter(this->change_blitter); - this->change_blitter = nullptr; - } + this->DrainCommandQueue(); while (this->PollEvent()) {} ::InputLoop(); diff --git a/src/video/video_driver.hpp b/src/video/video_driver.hpp index 4964e01cb3..db522a761d 100644 --- a/src/video/video_driver.hpp +++ b/src/video/video_driver.hpp @@ -22,6 +22,7 @@ #include #include #include +#include extern std::string _ini_videodriver; extern std::vector _resolutions; @@ -36,7 +37,7 @@ class VideoDriver : public Driver { const uint DEFAULT_WINDOW_HEIGHT = 480u; ///< Default window height. public: - VideoDriver() : fast_forward_key_pressed(false), fast_forward_via_key(false), is_game_threaded(true), change_blitter(nullptr) {} + VideoDriver() : fast_forward_key_pressed(false), fast_forward_via_key(false), is_game_threaded(true) {} /** * Mark a particular area dirty. @@ -178,12 +179,16 @@ public: } /** - * Queue a request to change the blitter. This is not executed immediately, - * but instead on the next draw-tick. + * Queue a function to be called on the main thread with game state + * lock held and video buffer locked. Queued functions will be + * executed on the next draw tick. + * @param func Function to call. */ - void ChangeBlitter(const char *new_blitter) + void QueueOnMainThread(std::function &&func) { - this->change_blitter = new_blitter; + std::lock_guard lock(this->cmd_queue_mutex); + + this->cmd_queue.emplace_back(std::forward>(func)); } void GameLoopPause(); @@ -328,11 +333,29 @@ protected: static void GameThreadThunk(VideoDriver *drv); private: + std::mutex cmd_queue_mutex; + std::vector> cmd_queue; + + /** Execute all queued commands. */ + void DrainCommandQueue() + { + std::vector> cmds{}; + + { + /* Exchange queue with an empty one to limit the time we + * hold the mutex. This also ensures that queued functions can + * add new functions to the queue without everything blocking. */ + std::lock_guard lock(this->cmd_queue_mutex); + cmds.swap(this->cmd_queue); + } + + for (auto &f : cmds) { + f(); + } + } + void GameLoop(); void GameThread(); - void RealChangeBlitter(const char *repl_blitter); - - const char *change_blitter; ///< Request to change the blitter. nullptr if no pending request. }; #endif /* VIDEO_VIDEO_DRIVER_HPP */ From 1f159f79de7428cbaa6133ec9cf06ce559980ea6 Mon Sep 17 00:00:00 2001 From: Michael Lutz Date: Sat, 1 May 2021 19:55:46 +0200 Subject: [PATCH 259/268] Fix #9147: Delay making screenshots until the next draw tick as we may not access the video buffer from the game thread. --- src/console_cmds.cpp | 2 +- src/screenshot.cpp | 38 ++++++++++++++++++++++++++++++-------- src/screenshot.h | 2 +- 3 files changed, 32 insertions(+), 10 deletions(-) diff --git a/src/console_cmds.cpp b/src/console_cmds.cpp index 69a44e63de..99b3b54582 100644 --- a/src/console_cmds.cpp +++ b/src/console_cmds.cpp @@ -1416,7 +1416,7 @@ DEF_CONSOLE_CMD(ConScreenShot) ScreenshotType type = SC_VIEWPORT; uint32 width = 0; uint32 height = 0; - const char *name = nullptr; + std::string name{}; uint32 arg_index = 1; if (argc > arg_index) { diff --git a/src/screenshot.cpp b/src/screenshot.cpp index dc76996fbf..0e3bf3d8e2 100644 --- a/src/screenshot.cpp +++ b/src/screenshot.cpp @@ -864,7 +864,7 @@ static ScreenshotType _confirmed_screenshot_type; ///< Screenshot type the curre */ static void ScreenshotConfirmationCallback(Window *w, bool confirmed) { - if (confirmed) MakeScreenshot(_confirmed_screenshot_type, nullptr); + if (confirmed) MakeScreenshot(_confirmed_screenshot_type, {}); } /** @@ -890,24 +890,20 @@ void MakeScreenshotWithConfirm(ScreenshotType t) ShowQuery(STR_WARNING_SCREENSHOT_SIZE_CAPTION, STR_WARNING_SCREENSHOT_SIZE_MESSAGE, nullptr, ScreenshotConfirmationCallback); } else { /* Less than 64M pixels, just do it */ - MakeScreenshot(t, nullptr); + MakeScreenshot(t, {}); } } /** * Make a screenshot. - * Unconditionally take a screenshot of the requested type. * @param t the type of screenshot to make. * @param name the name to give to the screenshot. * @param width the width of the screenshot of, or 0 for current viewport width (only works for SC_ZOOMEDIN and SC_DEFAULTZOOM). * @param height the height of the screenshot of, or 0 for current viewport height (only works for SC_ZOOMEDIN and SC_DEFAULTZOOM). * @return true iff the screenshot was made successfully - * @see MakeScreenshotWithConfirm */ -bool MakeScreenshot(ScreenshotType t, const char *name, uint32 width, uint32 height) +static bool RealMakeScreenshot(ScreenshotType t, std::string name, uint32 width, uint32 height) { - VideoDriver::VideoBufferLocker lock; - if (t == SC_VIEWPORT) { /* First draw the dirty parts of the screen and only then change the name * of the screenshot. This way the screenshot will always show the name @@ -918,7 +914,7 @@ bool MakeScreenshot(ScreenshotType t, const char *name, uint32 width, uint32 hei } _screenshot_name[0] = '\0'; - if (name != nullptr) strecpy(_screenshot_name, name, lastof(_screenshot_name)); + if (!name.empty()) strecpy(_screenshot_name, name.c_str(), lastof(_screenshot_name)); bool ret; switch (t) { @@ -969,6 +965,32 @@ bool MakeScreenshot(ScreenshotType t, const char *name, uint32 width, uint32 hei return ret; } +/** + * Schedule making a screenshot. + * Unconditionally take a screenshot of the requested type. + * @param t the type of screenshot to make. + * @param name the name to give to the screenshot. + * @param width the width of the screenshot of, or 0 for current viewport width (only works for SC_ZOOMEDIN and SC_DEFAULTZOOM). + * @param height the height of the screenshot of, or 0 for current viewport height (only works for SC_ZOOMEDIN and SC_DEFAULTZOOM). + * @return true iff the screenshot was successfully made. + * @see MakeScreenshotWithConfirm + */ +bool MakeScreenshot(ScreenshotType t, std::string name, uint32 width, uint32 height) +{ + if (t == SC_CRASHLOG) { + /* Video buffer might or might not be locked. */ + VideoDriver::VideoBufferLocker lock; + + return RealMakeScreenshot(t, name, width, height); + } + + VideoDriver::GetInstance()->QueueOnMainThread([=] { // Capture by value to not break scope. + RealMakeScreenshot(t, name, width, height); + }); + + return true; +} + /** * Return the owner of a tile to display it with in the small map in mode "Owner". diff --git a/src/screenshot.h b/src/screenshot.h index 148c018e1e..e65813573f 100644 --- a/src/screenshot.h +++ b/src/screenshot.h @@ -28,7 +28,7 @@ enum ScreenshotType { void SetupScreenshotViewport(ScreenshotType t, struct Viewport *vp, uint32 width = 0, uint32 height = 0); bool MakeHeightmapScreenshot(const char *filename); void MakeScreenshotWithConfirm(ScreenshotType t); -bool MakeScreenshot(ScreenshotType t, const char *name, uint32 width = 0, uint32 height = 0); +bool MakeScreenshot(ScreenshotType t, std::string name, uint32 width = 0, uint32 height = 0); bool MakeMinimapWorldScreenshot(); extern char _screenshot_format_name[8]; From bd1a20f6eee6758f6a812af18fbe5b41b491b5c4 Mon Sep 17 00:00:00 2001 From: Peter Nelson Date: Sat, 1 May 2021 18:07:47 +0100 Subject: [PATCH 260/268] Codechange: Use std::vector for NewGRF station platform layouts. This avoids the need to custom memory management and additional members. This also resolves use-after-free if modifying copied layouts, so presumably nobody has ever done that. --- src/newgrf.cpp | 79 +++++++++++--------------------------------- src/newgrf_station.h | 22 +++++++----- src/station_cmd.cpp | 14 ++++---- src/waypoint_cmd.cpp | 2 +- 4 files changed, 42 insertions(+), 75 deletions(-) diff --git a/src/newgrf.cpp b/src/newgrf.cpp index a9727d38de..9e8bbda190 100644 --- a/src/newgrf.cpp +++ b/src/newgrf.cpp @@ -218,6 +218,19 @@ protected: public: ByteReader(byte *data, byte *end) : data(data), end(end) { } + inline byte *ReadBytes(size_t size) + { + if (data + size >= end) { + /* Put data at the end, as would happen if every byte had been individually read. */ + data = end; + throw OTTDByteReaderSignal(); + } + + byte *ret = data; + data += size; + return ret; + } + inline byte ReadByte() { if (data < end) return *(data)++; @@ -1883,7 +1896,7 @@ static ChangeInfoResult StationChangeInfo(uint stid, int numinfo, int prop, Byte StationSpec **spec = &_cur.grffile->stations[stid + i]; /* Property 0x08 is special; it is where the station is allocated */ - if (*spec == nullptr) *spec = CallocT(1); + if (*spec == nullptr) *spec = new StationSpec(); /* Swap classid because we read it in BE meaning WAYP or DFLT */ uint32 classid = buf->ReadDWord(); @@ -1966,54 +1979,17 @@ static ChangeInfoResult StationChangeInfo(uint stid, int numinfo, int prop, Byte break; case 0x0E: // Define custom layout - statspec->copied_layouts = false; - while (buf->HasData()) { byte length = buf->ReadByte(); byte number = buf->ReadByte(); - StationLayout layout; - uint l, p; if (length == 0 || number == 0) break; - if (length > statspec->lengths) { - byte diff_length = length - statspec->lengths; - statspec->platforms = ReallocT(statspec->platforms, length); - memset(statspec->platforms + statspec->lengths, 0, diff_length); + if (statspec->layouts.size() < length) statspec->layouts.resize(length); + if (statspec->layouts[length - 1].size() < number) statspec->layouts[length - 1].resize(number); - statspec->layouts = ReallocT(statspec->layouts, length); - memset(statspec->layouts + statspec->lengths, 0, diff_length * sizeof(*statspec->layouts)); - - statspec->lengths = length; - } - l = length - 1; // index is zero-based - - if (number > statspec->platforms[l]) { - statspec->layouts[l] = ReallocT(statspec->layouts[l], number); - /* We expect nullptr being 0 here, but C99 guarantees that. */ - memset(statspec->layouts[l] + statspec->platforms[l], 0, - (number - statspec->platforms[l]) * sizeof(**statspec->layouts)); - - statspec->platforms[l] = number; - } - - p = 0; - layout = MallocT(length * number); - try { - for (l = 0; l < length; l++) { - for (p = 0; p < number; p++) { - layout[l * number + p] = buf->ReadByte(); - } - } - } catch (...) { - free(layout); - throw; - } - - l--; - p--; - free(statspec->layouts[l][p]); - statspec->layouts[l][p] = layout; + const byte *layout = buf->ReadBytes(length * number); + statspec->layouts[length - 1][number - 1].assign(layout, layout + length * number); } break; @@ -2026,10 +2002,7 @@ static ChangeInfoResult StationChangeInfo(uint stid, int numinfo, int prop, Byte continue; } - statspec->lengths = srcstatspec->lengths; - statspec->platforms = srcstatspec->platforms; - statspec->layouts = srcstatspec->layouts; - statspec->copied_layouts = true; + statspec->layouts = srcstatspec->layouts; break; } @@ -8399,20 +8372,8 @@ static void ResetCustomStations() delete[] statspec->renderdata; - /* Release platforms and layouts */ - if (!statspec->copied_layouts) { - for (uint l = 0; l < statspec->lengths; l++) { - for (uint p = 0; p < statspec->platforms[l]; p++) { - free(statspec->layouts[l][p]); - } - free(statspec->layouts[l]); - } - free(statspec->layouts); - free(statspec->platforms); - } - /* Release this station */ - free(statspec); + delete statspec; } /* Free and reset the station data */ diff --git a/src/newgrf_station.h b/src/newgrf_station.h index fac5d64ddd..4c4a5831be 100644 --- a/src/newgrf_station.h +++ b/src/newgrf_station.h @@ -109,12 +109,13 @@ enum StationRandomTrigger { SRT_PATH_RESERVATION, ///< Trigger platform when train reserves path. }; -/* Station layout for given dimensions - it is a two-dimensional array - * where index is computed as (x * platforms) + platform. */ -typedef byte *StationLayout; - /** Station specification. */ struct StationSpec { + StationSpec() : cls_id(STAT_CLASS_DFLT), name(0), + disallowed_platforms(0), disallowed_lengths(0), tiles(0), + renderdata(nullptr), cargo_threshold(0), cargo_triggers(0), + callback_mask(0), flags(0), pylons(0), wires(0), blocked(0), + animation({0, 0, 0, 0}) {} /** * Properties related the the grf file. * NUM_CARGO real cargo plus three pseudo cargo sprite groups. @@ -165,10 +166,15 @@ struct StationSpec { AnimationInfo animation; - byte lengths; - byte *platforms; - StationLayout **layouts; - bool copied_layouts; + /** + * Custom platform layouts. + * This is a 2D array containing an array of tiles. + * 1st layer is platform lengths. + * 2nd layer is tracks (width). + * These can be sparsely populated, and the upper limit is not defined but + * limited to 255. + */ + std::vector>> layouts; }; /** Struct containing information relating to station classes. */ diff --git a/src/station_cmd.cpp b/src/station_cmd.cpp index 482b954625..ac78064d49 100644 --- a/src/station_cmd.cpp +++ b/src/station_cmd.cpp @@ -1109,13 +1109,13 @@ static inline byte *CreateMulti(byte *layout, int n, byte b) * @param plat_len The length of the platforms. * @param statspec The specification of the station to (possibly) get the layout from. */ -void GetStationLayout(byte *layout, int numtracks, int plat_len, const StationSpec *statspec) +void GetStationLayout(byte *layout, uint numtracks, uint plat_len, const StationSpec *statspec) { - if (statspec != nullptr && statspec->lengths >= plat_len && - statspec->platforms[plat_len - 1] >= numtracks && - statspec->layouts[plat_len - 1][numtracks - 1]) { + if (statspec != nullptr && statspec->layouts.size() >= plat_len && + statspec->layouts[plat_len - 1].size() >= numtracks && + !statspec->layouts[plat_len - 1][numtracks - 1].empty()) { /* Custom layout defined, follow it. */ - memcpy(layout, statspec->layouts[plat_len - 1][numtracks - 1], + memcpy(layout, statspec->layouts[plat_len - 1][numtracks - 1].data(), plat_len * numtracks); return; } @@ -1124,9 +1124,9 @@ void GetStationLayout(byte *layout, int numtracks, int plat_len, const StationSp CreateSingle(layout, numtracks); } else { if (numtracks & 1) layout = CreateSingle(layout, plat_len); - numtracks >>= 1; + int n = numtracks >> 1; - while (--numtracks >= 0) { + while (--n >= 0) { layout = CreateMulti(layout, plat_len, 4); layout = CreateMulti(layout, plat_len, 6); } diff --git a/src/waypoint_cmd.cpp b/src/waypoint_cmd.cpp index 01cdbc16e0..e8e9e69455 100644 --- a/src/waypoint_cmd.cpp +++ b/src/waypoint_cmd.cpp @@ -153,7 +153,7 @@ static CommandCost IsValidTileForWaypoint(TileIndex tile, Axis axis, StationID * return CommandCost(); } -extern void GetStationLayout(byte *layout, int numtracks, int plat_len, const StationSpec *statspec); +extern void GetStationLayout(byte *layout, uint numtracks, uint plat_len, const StationSpec *statspec); extern CommandCost FindJoiningWaypoint(StationID existing_station, StationID station_to_join, bool adjacent, TileArea ta, Waypoint **wp); extern CommandCost CanExpandRailStation(const BaseStation *st, TileArea &new_ta, Axis axis); From a3e49178d1d5d65e3ce91328d743d1642f324961 Mon Sep 17 00:00:00 2001 From: Peter Nelson Date: Sat, 1 May 2021 20:28:23 +0100 Subject: [PATCH 261/268] Codechange: Use std::vector for NewGRF station tile sprite layouts. --- src/newgrf.cpp | 38 +++++++++++++++++++------------------- src/newgrf_station.cpp | 4 ++-- src/newgrf_station.h | 7 +++---- src/station_cmd.cpp | 4 ++-- 4 files changed, 26 insertions(+), 27 deletions(-) diff --git a/src/newgrf.cpp b/src/newgrf.cpp index 9e8bbda190..a2671eff27 100644 --- a/src/newgrf.cpp +++ b/src/newgrf.cpp @@ -1904,13 +1904,13 @@ static ChangeInfoResult StationChangeInfo(uint stid, int numinfo, int prop, Byte break; } - case 0x09: // Define sprite layout - statspec->tiles = buf->ReadExtendedByte(); - delete[] statspec->renderdata; // delete earlier loaded stuff - statspec->renderdata = new NewGRFSpriteLayout[statspec->tiles]; + case 0x09: { // Define sprite layout + uint16 tiles = buf->ReadExtendedByte(); + statspec->renderdata.clear(); // delete earlier loaded stuff + statspec->renderdata.reserve(tiles); - for (uint t = 0; t < statspec->tiles; t++) { - NewGRFSpriteLayout *dts = &statspec->renderdata[t]; + for (uint t = 0; t < tiles; t++) { + NewGRFSpriteLayout *dts = &statspec->renderdata.emplace_back(); dts->consistent_max_offset = UINT16_MAX; // Spritesets are unknown, so no limit. if (buf->HasData(4) && *(uint32*)buf->Data() == 0) { @@ -1946,6 +1946,7 @@ static ChangeInfoResult StationChangeInfo(uint stid, int numinfo, int prop, Byte dts->Clone(tmp_layout.data()); } break; + } case 0x0A: { // Copy sprite layout byte srcid = buf->ReadByte(); @@ -1956,12 +1957,12 @@ static ChangeInfoResult StationChangeInfo(uint stid, int numinfo, int prop, Byte continue; } - delete[] statspec->renderdata; // delete earlier loaded stuff + statspec->renderdata.clear(); // delete earlier loaded stuff + statspec->renderdata.reserve(srcstatspec->renderdata.size()); - statspec->tiles = srcstatspec->tiles; - statspec->renderdata = new NewGRFSpriteLayout[statspec->tiles]; - for (uint t = 0; t < statspec->tiles; t++) { - statspec->renderdata[t].Clone(&srcstatspec->renderdata[t]); + for (const auto &it : srcstatspec->renderdata) { + NewGRFSpriteLayout *dts = &statspec->renderdata.emplace_back(); + dts->Clone(&it); } break; } @@ -2047,18 +2048,19 @@ static ChangeInfoResult StationChangeInfo(uint stid, int numinfo, int prop, Byte statspec->animation.triggers = buf->ReadWord(); break; - case 0x1A: // Advanced sprite layout - statspec->tiles = buf->ReadExtendedByte(); - delete[] statspec->renderdata; // delete earlier loaded stuff - statspec->renderdata = new NewGRFSpriteLayout[statspec->tiles]; + case 0x1A: { // Advanced sprite layout + uint16 tiles = buf->ReadExtendedByte(); + statspec->renderdata.clear(); // delete earlier loaded stuff + statspec->renderdata.reserve(tiles); - for (uint t = 0; t < statspec->tiles; t++) { - NewGRFSpriteLayout *dts = &statspec->renderdata[t]; + for (uint t = 0; t < tiles; t++) { + NewGRFSpriteLayout *dts = &statspec->renderdata.emplace_back(); uint num_building_sprites = buf->ReadByte(); /* On error, bail out immediately. Temporary GRF data was already freed */ if (ReadSpriteLayout(buf, num_building_sprites, false, GSF_STATIONS, true, false, dts)) return CIR_DISABLED; } break; + } default: ret = CIR_UNKNOWN; @@ -8370,8 +8372,6 @@ static void ResetCustomStations() if (stations[i] == nullptr) continue; StationSpec *statspec = stations[i]; - delete[] statspec->renderdata; - /* Release this station */ delete statspec; } diff --git a/src/newgrf_station.cpp b/src/newgrf_station.cpp index 9aa3ad43be..eff5ef2b2c 100644 --- a/src/newgrf_station.cpp +++ b/src/newgrf_station.cpp @@ -798,10 +798,10 @@ bool DrawStationTile(int x, int y, RailType railtype, Axis axis, StationClassID const NewGRFSpriteLayout *layout = nullptr; DrawTileSprites tmp_rail_layout; - if (statspec->renderdata == nullptr) { + if (statspec->renderdata.empty()) { sprites = GetStationTileLayout(STATION_RAIL, tile + axis); } else { - layout = &statspec->renderdata[(tile < statspec->tiles) ? tile + axis : (uint)axis]; + layout = &statspec->renderdata[(tile < statspec->renderdata.size()) ? tile + axis : (uint)axis]; if (!layout->NeedsPreprocessing()) { sprites = layout; layout = nullptr; diff --git a/src/newgrf_station.h b/src/newgrf_station.h index 4c4a5831be..5273625ae6 100644 --- a/src/newgrf_station.h +++ b/src/newgrf_station.h @@ -112,8 +112,8 @@ enum StationRandomTrigger { /** Station specification. */ struct StationSpec { StationSpec() : cls_id(STAT_CLASS_DFLT), name(0), - disallowed_platforms(0), disallowed_lengths(0), tiles(0), - renderdata(nullptr), cargo_threshold(0), cargo_triggers(0), + disallowed_platforms(0), disallowed_lengths(0), + cargo_threshold(0), cargo_triggers(0), callback_mask(0), flags(0), pylons(0), wires(0), blocked(0), animation({0, 0, 0, 0}) {} /** @@ -145,8 +145,7 @@ struct StationSpec { * 4-5 = platform with roof, left side * 6-7 = platform with roof, right side */ - uint tiles; - NewGRFSpriteLayout *renderdata; ///< Array of tile layouts. + std::vector renderdata; ///< Array of tile layouts. /** * Cargo threshold for choosing between little and lots of cargo diff --git a/src/station_cmd.cpp b/src/station_cmd.cpp index ac78064d49..f642a914f6 100644 --- a/src/station_cmd.cpp +++ b/src/station_cmd.cpp @@ -2844,8 +2844,8 @@ static void DrawTile_Station(TileInfo *ti) } /* Ensure the chosen tile layout is valid for this custom station */ - if (statspec->renderdata != nullptr) { - layout = &statspec->renderdata[tile_layout < statspec->tiles ? tile_layout : (uint)GetRailStationAxis(ti->tile)]; + if (!statspec->renderdata.empty()) { + layout = &statspec->renderdata[tile_layout < statspec->renderdata.size() ? tile_layout : (uint)GetRailStationAxis(ti->tile)]; if (!layout->NeedsPreprocessing()) { t = layout; layout = nullptr; From 756034fa279bb6c3b81efbe13385583eb8a42d0f Mon Sep 17 00:00:00 2001 From: Peter Nelson Date: Sun, 2 May 2021 10:47:05 +0100 Subject: [PATCH 262/268] Codechange: Validate custom station platform layout tiles are permitted values only. --- src/newgrf.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/newgrf.cpp b/src/newgrf.cpp index a2671eff27..3a4fe12619 100644 --- a/src/newgrf.cpp +++ b/src/newgrf.cpp @@ -1991,6 +1991,14 @@ static ChangeInfoResult StationChangeInfo(uint stid, int numinfo, int prop, Byte const byte *layout = buf->ReadBytes(length * number); statspec->layouts[length - 1][number - 1].assign(layout, layout + length * number); + + /* Validate tile values are only the permitted 00, 02, 04 and 06. */ + for (auto &tile : statspec->layouts[length - 1][number - 1]) { + if ((tile & 6) != tile) { + grfmsg(1, "StationChangeInfo: Invalid tile %u in layout %ux%u", tile, length, number); + tile &= 6; + } + } } break; From 20762f9117c8dfbc5cc72771926563b4893592c0 Mon Sep 17 00:00:00 2001 From: Milek7 Date: Sun, 2 May 2021 20:10:07 +0200 Subject: [PATCH 263/268] Codechange: Acquire video buffer before taking game state lock to prevent erratic fast forward behaviour (#9140) --- src/video/video_driver.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/video/video_driver.cpp b/src/video/video_driver.cpp index 2f8efc4ad4..0af716f05e 100644 --- a/src/video/video_driver.cpp +++ b/src/video/video_driver.cpp @@ -131,13 +131,14 @@ void VideoDriver::Tick() this->fast_forward_via_key = false; } + /* Locking video buffer can block (especially with vsync enabled), do it before taking game state lock. */ + this->LockVideoBuffer(); + { /* Tell the game-thread to stop so we can have a go. */ std::lock_guard lock_wait(this->game_thread_wait_mutex); std::lock_guard lock_state(this->game_state_mutex); - this->LockVideoBuffer(); - this->DrainCommandQueue(); while (this->PollEvent()) {} From 1a1def99dc0913d8887a04b8132f308934cd57ee Mon Sep 17 00:00:00 2001 From: translators Date: Sun, 2 May 2021 18:15:54 +0000 Subject: [PATCH 264/268] Update: Translations from eints norwegian (bokmal): 24 changes by Anolitt russian: 8 changes by Ln-Wolf dutch: 46 changes by Afoklala spanish: 43 changes by MontyMontana french: 44 changes by arikover --- src/lang/dutch.txt | 50 ++++++++++++++++++++++++++++++++--- src/lang/french.txt | 46 ++++++++++++++++++++++++++++++-- src/lang/norwegian_bokmal.txt | 26 ++++++++++++++++-- src/lang/russian.txt | 8 ++++++ src/lang/spanish.txt | 45 +++++++++++++++++++++++++++++-- 5 files changed, 165 insertions(+), 10 deletions(-) diff --git a/src/lang/dutch.txt b/src/lang/dutch.txt index ae1af9673a..9e54ba3fdb 100644 --- a/src/lang/dutch.txt +++ b/src/lang/dutch.txt @@ -953,7 +953,7 @@ STR_GAME_OPTIONS_CURRENCY_MYR :Maleisische Rin STR_GAME_OPTIONS_ROAD_VEHICLES_DROPDOWN_LEFT :Links rijden STR_GAME_OPTIONS_ROAD_VEHICLES_DROPDOWN_RIGHT :Rechts rijden -STR_GAME_OPTIONS_TOWN_NAMES_FRAME :{BLACK}Plaatsnamen +STR_GAME_OPTIONS_TOWN_NAMES_FRAME :{BLACK}Plaatsnamen: STR_GAME_OPTIONS_TOWN_NAMES_DROPDOWN_TOOLTIP :{BLACK}Stijl voor plaatsnamen kiezen ############ start of townname region @@ -993,6 +993,7 @@ STR_GAME_OPTIONS_AUTOSAVE_DROPDOWN_EVERY_12_MONTHS :Iedere 12 maand STR_GAME_OPTIONS_LANGUAGE :{BLACK}Taal STR_GAME_OPTIONS_LANGUAGE_TOOLTIP :{BLACK}Taal selecteren voor gebruikersscherm +STR_GAME_OPTIONS_LANGUAGE_PERCENTAGE :{STRING} ({NUM}% voltooid) STR_GAME_OPTIONS_FULLSCREEN :{BLACK}Volledig scherm STR_GAME_OPTIONS_FULLSCREEN_TOOLTIP :{BLACK}Vink dit vakje aan om OpenTTD in het volledige scherm te spelen @@ -1990,6 +1991,8 @@ STR_FACE_TIE :Stropdas: STR_FACE_EARRING :Oorbel: STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}Verander das of oorbel +STR_NETWORK_SERVER_VISIBILITY_PRIVATE :Privé +STR_NETWORK_SERVER_VISIBILITY_PUBLIC :Openbaar # Network server list STR_NETWORK_SERVER_LIST_CAPTION :{WHITE}Netwerkspel @@ -2046,13 +2049,15 @@ STR_NETWORK_SERVER_LIST_PLAYER_NAME_OSKTITLE :{BLACK}Vul je n STR_NETWORK_SERVER_LIST_ENTER_IP :{BLACK}Voer het IP-adres van de server in # Start new multiplayer server -STR_NETWORK_START_SERVER_CAPTION :{WHITE}Start nieuw multiplayerspel +STR_NETWORK_START_SERVER_CAPTION :{WHITE}Nieuw spel met meerdere spelers starten STR_NETWORK_START_SERVER_NEW_GAME_NAME :{BLACK}Spelnaam: -STR_NETWORK_START_SERVER_NEW_GAME_NAME_TOOLTIP :{BLACK}De spelnaam wordt weergegeven aan andere spelers in het multiplayerspelselectiemenu +STR_NETWORK_START_SERVER_NEW_GAME_NAME_TOOLTIP :{BLACK}De spelnaam wordt weergegeven aan andere spelers in het spelselectiemenu voor meerdere spelers STR_NETWORK_START_SERVER_SET_PASSWORD :{BLACK}Wachtwoord instellen STR_NETWORK_START_SERVER_PASSWORD_TOOLTIP :{BLACK}Beveilig je spel met een wachtwoord als je niet wilt dat dit algemeen toegankelijk is +STR_NETWORK_START_SERVER_VISIBILITY_LABEL :{BLACK}Zichtbaarheid +STR_NETWORK_START_SERVER_VISIBILITY_TOOLTIP :{BLACK}Bepaalt of andere mensen je server kunnen zien in de openbare lijst STR_NETWORK_START_SERVER_CLIENTS_SELECT :{BLACK}{NUM} speler{P "" s} STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS :{BLACK}Maximumaantal spelers: STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS_TOOLTIP :{BLACK}Kies het maximaal aantal toegestane spelers. Niet alle posities hoeven gebruikt te worden. @@ -2116,11 +2121,44 @@ STR_NETWORK_NEED_GAME_PASSWORD_CAPTION :{WHITE}Server i STR_NETWORK_NEED_COMPANY_PASSWORD_CAPTION :{WHITE}Bedrijf is beveiligd. Voer wachtwoord in # Network company list added strings -STR_NETWORK_COMPANY_LIST_CLIENT_LIST :Spelerslijst +STR_NETWORK_COMPANY_LIST_CLIENT_LIST :Spelers online # Network client list +STR_NETWORK_CLIENT_LIST_CAPTION :{WHITE}Meerdere spelers +STR_NETWORK_CLIENT_LIST_SERVER :{BLACK}Server +STR_NETWORK_CLIENT_LIST_SERVER_NAME :{BLACK}Naam +STR_NETWORK_CLIENT_LIST_SERVER_NAME_TOOLTIP :{BLACK}De naam van de server waar je speelt +STR_NETWORK_CLIENT_LIST_SERVER_NAME_EDIT_TOOLTIP :{BLACK}De naam van je server bewerken +STR_NETWORK_CLIENT_LIST_SERVER_NAME_QUERY_CAPTION :Servernaam +STR_NETWORK_CLIENT_LIST_SERVER_VISIBILITY :{BLACK}Zichtbaarheid +STR_NETWORK_CLIENT_LIST_SERVER_VISIBILITY_TOOLTIP :{BLACK}Bepaalt of andere mensen je server kunnen zien in de openbare lijst +STR_NETWORK_CLIENT_LIST_PLAYER :{BLACK}Speler +STR_NETWORK_CLIENT_LIST_PLAYER_NAME :{BLACK}Naam +STR_NETWORK_CLIENT_LIST_PLAYER_NAME_TOOLTIP :{BLACK}Je spelernaam +STR_NETWORK_CLIENT_LIST_PLAYER_NAME_EDIT_TOOLTIP :{BLACK}Je spelernaam bewerken +STR_NETWORK_CLIENT_LIST_PLAYER_NAME_QUERY_CAPTION :Je spelernaam +STR_NETWORK_CLIENT_LIST_ADMIN_CLIENT_TOOLTIP :{BLACK}Beheeracties die nodig zijn voor deze client +STR_NETWORK_CLIENT_LIST_ADMIN_COMPANY_TOOLTIP :{BLACK}Beheeracties die nodig zijn voor dit bedrijf +STR_NETWORK_CLIENT_LIST_JOIN_TOOLTIP :{BLACK}Meedoen met dit bedrijf +STR_NETWORK_CLIENT_LIST_CHAT_CLIENT_TOOLTIP :{BLACK}Een bericht sturen naar deze speler +STR_NETWORK_CLIENT_LIST_CHAT_COMPANY_TOOLTIP :{BLACK}Een bericht versturen naar alle spelers van dit bedrijf +STR_NETWORK_CLIENT_LIST_CHAT_SPECTATOR_TOOLTIP :{BLACK}Een bericht sturen naar alle toeschouwers +STR_NETWORK_CLIENT_LIST_SPECTATORS :Toeschouwers +STR_NETWORK_CLIENT_LIST_NEW_COMPANY :(Nieuw bedrijf) +STR_NETWORK_CLIENT_LIST_NEW_COMPANY_TOOLTIP :{BLACK}Een nieuw bedrijf maken en meedoen +STR_NETWORK_CLIENT_LIST_PLAYER_ICON_SELF_TOOLTIP :{BLACK}Dit ben jij +STR_NETWORK_CLIENT_LIST_PLAYER_ICON_HOST_TOOLTIP :{BLACK}Dit is de host van het spel +STR_NETWORK_CLIENT_LIST_ADMIN_CLIENT_KICK :Eruit schoppen +STR_NETWORK_CLIENT_LIST_ADMIN_CLIENT_BAN :Bannen +STR_NETWORK_CLIENT_LIST_ADMIN_COMPANY_RESET :Verwijderen +STR_NETWORK_CLIENT_LIST_ADMIN_COMPANY_UNLOCK :Wachtwoord ontgrendelen +STR_NETWORK_CLIENT_LIST_ASK_CAPTION :{WHITE}Beheeractie +STR_NETWORK_CLIENT_LIST_ASK_CLIENT_KICK :{YELLOW}Weet je zeker dat je de speler '{STRING}' eruit wilt schoppen? +STR_NETWORK_CLIENT_LIST_ASK_CLIENT_BAN :{YELLOW}Weet je zeker dat je de speler '{STRING}' wilt bannen? +STR_NETWORK_CLIENT_LIST_ASK_COMPANY_RESET :{YELLOW}Weet je zeker dat je het bedrijf '{COMPANY}' wilt verwijderen? +STR_NETWORK_CLIENT_LIST_ASK_COMPANY_UNLOCK :{YELLOW}Weet je zeker dat je het wachtwoord voor bedrijf '{COMPANY}' wilt terugstellen? STR_NETWORK_SERVER :Server STR_NETWORK_CLIENT :Speler @@ -2165,6 +2203,7 @@ STR_NETWORK_ERROR_SERVER_START :{WHITE}Kan serv STR_NETWORK_ERROR_CLIENT_START :{WHITE}Kan geen verbinding maken STR_NETWORK_ERROR_TIMEOUT :{WHITE}Verbinding nr. {NUM} kostte te veel tijd STR_NETWORK_ERROR_SERVER_ERROR :{WHITE}Er is een protocolfout gedetecteerd en de verbinding werd gesloten +STR_NETWORK_ERROR_BAD_PLAYER_NAME :{WHITE}Je spelernaam is nog niet ingesteld. Je stelt de naam in bovenin het venster Meerdere spelers STR_NETWORK_ERROR_WRONG_REVISION :{WHITE}De revisie van deze client komt niet overeen met de revisie van de server STR_NETWORK_ERROR_WRONG_PASSWORD :{WHITE}Ongeldig wachtwoord STR_NETWORK_ERROR_SERVER_FULL :{WHITE}De server is vol @@ -2177,6 +2216,7 @@ STR_NETWORK_ERROR_TIMEOUT_PASSWORD :{WHITE}Het invo STR_NETWORK_ERROR_TIMEOUT_COMPUTER :{WHITE}Uw computer is te traag om de server bij te houden STR_NETWORK_ERROR_TIMEOUT_MAP :{WHITE}Uw computer deed er te lang over om de kaart te downloaden STR_NETWORK_ERROR_TIMEOUT_JOIN :{WHITE}Uw computer deed er te lang over om met de server te verbinden +STR_NETWORK_ERROR_INVALID_CLIENT_NAME :{WHITE}Je spelernaam is niet geldig ############ Leave those lines in this order!! STR_NETWORK_ERROR_CLIENT_GENERAL :algemene fout @@ -2199,6 +2239,7 @@ STR_NETWORK_ERROR_CLIENT_TIMEOUT_PASSWORD :wachtwoord niet STR_NETWORK_ERROR_CLIENT_TIMEOUT_COMPUTER :algemene time-out STR_NETWORK_ERROR_CLIENT_TIMEOUT_MAP :downloaden van de kaart duurde te lang STR_NETWORK_ERROR_CLIENT_TIMEOUT_JOIN :verwerken van de kaart duurde te lang +STR_NETWORK_ERROR_CLIENT_INVALID_CLIENT_NAME :ongeldige clientnaam ############ End of leave-in-this-order STR_NETWORK_ERROR_CLIENT_GUI_LOST_CONNECTION_CAPTION :{WHITE}Mogelijk verbinding verbroken @@ -3038,6 +3079,7 @@ STR_NEWGRF_ERROR_MSG_WARNING :{RED}Waarschuwi STR_NEWGRF_ERROR_MSG_ERROR :{RED}Fout: {SILVER}{STRING} STR_NEWGRF_ERROR_MSG_FATAL :{RED}Fatale fout: {SILVER}{STRING} STR_NEWGRF_ERROR_FATAL_POPUP :{WHITE}Een fatale NewGRF-fout is ontstaan:{}{STRING} +STR_NEWGRF_ERROR_POPUP :{WHITE}Er is een NewGRF-fout opgetreden:{}{STRING} STR_NEWGRF_ERROR_VERSION_NUMBER :{1:STRING} werkt niet met de TTDPatch-versie die is opgegeven door OpenTTD STR_NEWGRF_ERROR_DOS_OR_WINDOWS :{1:STRING} is voor versie {STRING} van TTD STR_NEWGRF_ERROR_UNSET_SWITCH :{1:STRING} is ontwikkeld voor {STRING} diff --git a/src/lang/french.txt b/src/lang/french.txt index a578ea2739..b0491a80cb 100644 --- a/src/lang/french.txt +++ b/src/lang/french.txt @@ -954,7 +954,7 @@ STR_GAME_OPTIONS_CURRENCY_MYR :Malaysian Ringg STR_GAME_OPTIONS_ROAD_VEHICLES_DROPDOWN_LEFT :Conduite à gauche STR_GAME_OPTIONS_ROAD_VEHICLES_DROPDOWN_RIGHT :Conduite à droite -STR_GAME_OPTIONS_TOWN_NAMES_FRAME :{BLACK}Noms des villes +STR_GAME_OPTIONS_TOWN_NAMES_FRAME :{BLACK}Nom des villes{NBSP}: STR_GAME_OPTIONS_TOWN_NAMES_DROPDOWN_TOOLTIP :{BLACK}Sélectionner la nationalité des noms des villes ############ start of townname region @@ -994,6 +994,7 @@ STR_GAME_OPTIONS_AUTOSAVE_DROPDOWN_EVERY_12_MONTHS :Tous les 12 moi STR_GAME_OPTIONS_LANGUAGE :{BLACK}Langue STR_GAME_OPTIONS_LANGUAGE_TOOLTIP :{BLACK}Sélectionner la langue à utiliser pour l'interface +STR_GAME_OPTIONS_LANGUAGE_PERCENTAGE :{STRING} ({NUM}{NBSP}% terminé{P "" s}) STR_GAME_OPTIONS_FULLSCREEN :{BLACK}Plein écran STR_GAME_OPTIONS_FULLSCREEN_TOOLTIP :{BLACK}Cocher cette case pour jouer à OpenTTD en plein écran @@ -1991,6 +1992,8 @@ STR_FACE_TIE :Cravate{NBSP}: STR_FACE_EARRING :Boucle d'oreille{NBSP}: STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}Modifier la cravate ou la boucle d'oreille +STR_NETWORK_SERVER_VISIBILITY_PRIVATE :Privé +STR_NETWORK_SERVER_VISIBILITY_PUBLIC :Public # Network server list STR_NETWORK_SERVER_LIST_CAPTION :{WHITE}Multijoueurs @@ -2054,6 +2057,8 @@ STR_NETWORK_START_SERVER_NEW_GAME_NAME_TOOLTIP :{BLACK}Les autr STR_NETWORK_START_SERVER_SET_PASSWORD :{BLACK}Choisir le mot de passe STR_NETWORK_START_SERVER_PASSWORD_TOOLTIP :{BLACK}Protégez votre partie avec un mot de passe si vous ne souhaitez pas que d'autres l'utilisent +STR_NETWORK_START_SERVER_VISIBILITY_LABEL :{BLACK}Visibilité +STR_NETWORK_START_SERVER_VISIBILITY_TOOLTIP :{BLACK}Possibilité pour les autres personnes de vous voir dans la liste publique STR_NETWORK_START_SERVER_CLIENTS_SELECT :{BLACK}{NUM} client{P "" s} STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS :{BLACK}Nombre de clients maximum{NBSP}: STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS_TOOLTIP :{BLACK}Choisir un nombre maximum de clients. Tous les emplacements n'auront pas besoin d'être remplis @@ -2117,11 +2122,44 @@ STR_NETWORK_NEED_GAME_PASSWORD_CAPTION :{WHITE}Le serve STR_NETWORK_NEED_COMPANY_PASSWORD_CAPTION :{WHITE}La compagnie est protégée. Entrez le mot de passe # Network company list added strings -STR_NETWORK_COMPANY_LIST_CLIENT_LIST :Liste des clients +STR_NETWORK_COMPANY_LIST_CLIENT_LIST :Joueurs en ligne # Network client list +STR_NETWORK_CLIENT_LIST_CAPTION :{WHITE}Multijoueur +STR_NETWORK_CLIENT_LIST_SERVER :{BLACK}Serveur +STR_NETWORK_CLIENT_LIST_SERVER_NAME :{BLACK}Nom +STR_NETWORK_CLIENT_LIST_SERVER_NAME_TOOLTIP :{BLACK}Nom du serveur sur lequel vous jouez +STR_NETWORK_CLIENT_LIST_SERVER_NAME_EDIT_TOOLTIP :{BLACK}Éditer le nom de votre serveur +STR_NETWORK_CLIENT_LIST_SERVER_NAME_QUERY_CAPTION :Nom du serveur +STR_NETWORK_CLIENT_LIST_SERVER_VISIBILITY :{BLACK}Visibilité +STR_NETWORK_CLIENT_LIST_SERVER_VISIBILITY_TOOLTIP :{BLACK}Possibilité pour les autres personnes de voir votre serveur dans la liste publique +STR_NETWORK_CLIENT_LIST_PLAYER :{BLACK}Joueur +STR_NETWORK_CLIENT_LIST_PLAYER_NAME :{BLACK}Nom +STR_NETWORK_CLIENT_LIST_PLAYER_NAME_TOOLTIP :{BLACK}Votre nom de jeu +STR_NETWORK_CLIENT_LIST_PLAYER_NAME_EDIT_TOOLTIP :{BLACK}Éditer votre nom +STR_NETWORK_CLIENT_LIST_PLAYER_NAME_QUERY_CAPTION :Votre nom de jeu +STR_NETWORK_CLIENT_LIST_ADMIN_CLIENT_TOOLTIP :{BLACK}Actions administratives à accomplir pour ce client +STR_NETWORK_CLIENT_LIST_ADMIN_COMPANY_TOOLTIP :{BLACK}Actions administratives à accomplir pour cette compagnie +STR_NETWORK_CLIENT_LIST_JOIN_TOOLTIP :{BLACK}Rejoindre cette compagnie +STR_NETWORK_CLIENT_LIST_CHAT_CLIENT_TOOLTIP :{BLACK}Envoyer un message à cette personne +STR_NETWORK_CLIENT_LIST_CHAT_COMPANY_TOOLTIP :{BLACK}Envoyer un message à tous les joueurs de cette compagnie +STR_NETWORK_CLIENT_LIST_CHAT_SPECTATOR_TOOLTIP :{BLACK}Envoyer un message à tous les spectateurs +STR_NETWORK_CLIENT_LIST_SPECTATORS :Spectateurs +STR_NETWORK_CLIENT_LIST_NEW_COMPANY :(Nouvelle compagnie) +STR_NETWORK_CLIENT_LIST_NEW_COMPANY_TOOLTIP :{BLACK}Créer une nouvelle compagnie et la rejoindre +STR_NETWORK_CLIENT_LIST_PLAYER_ICON_SELF_TOOLTIP :{BLACK}C'est vous +STR_NETWORK_CLIENT_LIST_PLAYER_ICON_HOST_TOOLTIP :{BLACK}C'est l'hôte du jeu +STR_NETWORK_CLIENT_LIST_ADMIN_CLIENT_KICK :Exclure +STR_NETWORK_CLIENT_LIST_ADMIN_CLIENT_BAN :Bannir +STR_NETWORK_CLIENT_LIST_ADMIN_COMPANY_RESET :Supprimer +STR_NETWORK_CLIENT_LIST_ADMIN_COMPANY_UNLOCK :Débloquer le mot de passe +STR_NETWORK_CLIENT_LIST_ASK_CAPTION :{WHITE}Action administrative +STR_NETWORK_CLIENT_LIST_ASK_CLIENT_KICK :{YELLOW}Êtes-vous sûr de vouloir exclure '{STRING}'{NBSP}? +STR_NETWORK_CLIENT_LIST_ASK_CLIENT_BAN :{YELLOW}Êtes-vous sûr de vouloir bannir '{STRING}'{NBSP}? +STR_NETWORK_CLIENT_LIST_ASK_COMPANY_RESET :{YELLOW}Êtes-vous sûr de vouloir supprimer la compagnie '{COMPANY}'{NBSP}? +STR_NETWORK_CLIENT_LIST_ASK_COMPANY_UNLOCK :{YELLOW}Êtes-vous sûr de vouloir réinitialiser le mot de passe de la compagnie '{COMPANY}'? STR_NETWORK_SERVER :Serveur STR_NETWORK_CLIENT :Client @@ -2166,6 +2204,7 @@ STR_NETWORK_ERROR_SERVER_START :{WHITE}Le serve STR_NETWORK_ERROR_CLIENT_START :{WHITE}Échec de la connexion STR_NETWORK_ERROR_TIMEOUT :{WHITE}La connexion n°{NBSP}{NUM} a dépassé le temps d'attente STR_NETWORK_ERROR_SERVER_ERROR :{WHITE}Une erreur de protocole a été détectée et la connexion a été fermée +STR_NETWORK_ERROR_BAD_PLAYER_NAME :{WHITE}Vous n'avez pas de nom. Il doit être entré en haut de la fenêtre Multijoueur STR_NETWORK_ERROR_WRONG_REVISION :{WHITE}Le numéro de version/révision de ce client ne correspond pas à celui du serveur STR_NETWORK_ERROR_WRONG_PASSWORD :{WHITE}Mot de passe incorrect STR_NETWORK_ERROR_SERVER_FULL :{WHITE}Le serveur est complet @@ -2178,6 +2217,7 @@ STR_NETWORK_ERROR_TIMEOUT_PASSWORD :{WHITE}Vous ave STR_NETWORK_ERROR_TIMEOUT_COMPUTER :{WHITE}Votre ordinateur est trop lent pour suivre le serveur STR_NETWORK_ERROR_TIMEOUT_MAP :{WHITE}Votre ordinateur a mis trop de temps pour télécharger la carte STR_NETWORK_ERROR_TIMEOUT_JOIN :{WHITE}Votre ordinateur a mis trop de temps pour rejoindre le serveur +STR_NETWORK_ERROR_INVALID_CLIENT_NAME :{WHITE}Votre nom n'est pas valide ############ Leave those lines in this order!! STR_NETWORK_ERROR_CLIENT_GENERAL :erreur générale @@ -2200,6 +2240,7 @@ STR_NETWORK_ERROR_CLIENT_TIMEOUT_PASSWORD :aucun mot de pa STR_NETWORK_ERROR_CLIENT_TIMEOUT_COMPUTER :délai dépassé STR_NETWORK_ERROR_CLIENT_TIMEOUT_MAP :télécharger la carte a pris trop de temps STR_NETWORK_ERROR_CLIENT_TIMEOUT_JOIN :le traitement de la carte a pris trop de temps +STR_NETWORK_ERROR_CLIENT_INVALID_CLIENT_NAME :nom client invalide ############ End of leave-in-this-order STR_NETWORK_ERROR_CLIENT_GUI_LOST_CONNECTION_CAPTION :{WHITE}Possible perte de connexion @@ -3039,6 +3080,7 @@ STR_NEWGRF_ERROR_MSG_WARNING :{RED}Attention{ STR_NEWGRF_ERROR_MSG_ERROR :{RED}Erreur{NBSP}: {SILVER}{STRING} STR_NEWGRF_ERROR_MSG_FATAL :{RED}Erreur fatale{NBSP}: {SILVER}{STRING} STR_NEWGRF_ERROR_FATAL_POPUP :{WHITE}Une erreur NewGRF fatale est survenue{NBSP}:{}{STRING} +STR_NEWGRF_ERROR_POPUP :{WHITE}Une erreur NewGRF est survenue{NBSP}:{}{STRING} STR_NEWGRF_ERROR_VERSION_NUMBER :{1:STRING} ne fonctionnera pas avec la version de TTDPatch rapportée par OpenTTD STR_NEWGRF_ERROR_DOS_OR_WINDOWS :{1:STRING} est conçu pour la version {STRING} de TTD STR_NEWGRF_ERROR_UNSET_SWITCH :{1:STRING} est conçu pour être utilisé avec {STRING} diff --git a/src/lang/norwegian_bokmal.txt b/src/lang/norwegian_bokmal.txt index b79089f237..1cbf14fbec 100644 --- a/src/lang/norwegian_bokmal.txt +++ b/src/lang/norwegian_bokmal.txt @@ -955,7 +955,7 @@ STR_GAME_OPTIONS_CURRENCY_MYR :Malaysisk Ringg STR_GAME_OPTIONS_ROAD_VEHICLES_DROPDOWN_LEFT :Venstrekjøring STR_GAME_OPTIONS_ROAD_VEHICLES_DROPDOWN_RIGHT :Høyrekjøring -STR_GAME_OPTIONS_TOWN_NAMES_FRAME :{BLACK}Bynavn +STR_GAME_OPTIONS_TOWN_NAMES_FRAME :{BLACK}Bynavn: STR_GAME_OPTIONS_TOWN_NAMES_DROPDOWN_TOOLTIP :{BLACK}Velg nasjonalitet på bynavn ############ start of townname region @@ -1994,6 +1994,7 @@ STR_FACE_TIE :Slips: STR_FACE_EARRING :Ørering: STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}Endre slips eller ørering +STR_NETWORK_SERVER_VISIBILITY_PRIVATE :Privat STR_NETWORK_SERVER_VISIBILITY_PUBLIC :Offentlig # Network server list @@ -2059,6 +2060,7 @@ STR_NETWORK_START_SERVER_SET_PASSWORD :{BLACK}Sett pas STR_NETWORK_START_SERVER_PASSWORD_TOOLTIP :{BLACK}Beskytt ditt spill med et passord hvis du ikke vil at hvem som helst skal bli med på det STR_NETWORK_START_SERVER_VISIBILITY_LABEL :{BLACK}Synlighet +STR_NETWORK_START_SERVER_VISIBILITY_TOOLTIP :{BLACK}Om andre mennesker kan se serveren din i den offentlige oppføringen STR_NETWORK_START_SERVER_CLIENTS_SELECT :{BLACK}{NUM} klient{P "" er} STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS :{BLACK}Maks antall klienter: STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS.small :dra og slipp @@ -2123,24 +2125,44 @@ STR_NETWORK_NEED_GAME_PASSWORD_CAPTION :{WHITE}Tjeneren STR_NETWORK_NEED_COMPANY_PASSWORD_CAPTION :{WHITE}Firmaet er beskyttet. Skriv inn passord # Network company list added strings -STR_NETWORK_COMPANY_LIST_CLIENT_LIST :Liste over klienter +STR_NETWORK_COMPANY_LIST_CLIENT_LIST :Påloggede spillere # Network client list +STR_NETWORK_CLIENT_LIST_CAPTION :{WHITE}Flerspiller +STR_NETWORK_CLIENT_LIST_SERVER :{BLACK}Server STR_NETWORK_CLIENT_LIST_SERVER_NAME :{BLACK}Navn +STR_NETWORK_CLIENT_LIST_SERVER_NAME_TOOLTIP :{BLACK}Navnet på serveren du spiller på +STR_NETWORK_CLIENT_LIST_SERVER_NAME_EDIT_TOOLTIP :{BLACK}Rediger navnet på serveren +STR_NETWORK_CLIENT_LIST_SERVER_NAME_QUERY_CAPTION :Servernavn +STR_NETWORK_CLIENT_LIST_SERVER_VISIBILITY :{BLACK}Synlighet STR_NETWORK_CLIENT_LIST_SERVER_VISIBILITY_TOOLTIP :{BLACK}Om andre mennesker kan se serveren din i den offentlige oppføringen STR_NETWORK_CLIENT_LIST_PLAYER :{BLACK}Spiller STR_NETWORK_CLIENT_LIST_PLAYER_NAME :{BLACK}Navn +STR_NETWORK_CLIENT_LIST_PLAYER_NAME_TOOLTIP :{BLACK}Ditt spillernavn STR_NETWORK_CLIENT_LIST_PLAYER_NAME_EDIT_TOOLTIP :{BLACK}Rediger ditt spillernavn STR_NETWORK_CLIENT_LIST_PLAYER_NAME_QUERY_CAPTION :Ditt spillernavn STR_NETWORK_CLIENT_LIST_ADMIN_CLIENT_TOOLTIP :{BLACK}Administrative handlinger å utføre for denne klienten +STR_NETWORK_CLIENT_LIST_ADMIN_COMPANY_TOOLTIP :{BLACK}Administrative tiltak å utføre for dette firmaet STR_NETWORK_CLIENT_LIST_JOIN_TOOLTIP :{BLACK}Bli med i dette firmaet +STR_NETWORK_CLIENT_LIST_CHAT_CLIENT_TOOLTIP :{BLACK}Send en melding til denne spilleren STR_NETWORK_CLIENT_LIST_CHAT_COMPANY_TOOLTIP :{BLACK}Send en melding til alle spillerne i dette firmaet STR_NETWORK_CLIENT_LIST_CHAT_SPECTATOR_TOOLTIP :{BLACK}Send en melding til alle tilskuerne +STR_NETWORK_CLIENT_LIST_SPECTATORS :Tilskuere +STR_NETWORK_CLIENT_LIST_NEW_COMPANY :(Nytt firma) STR_NETWORK_CLIENT_LIST_NEW_COMPANY_TOOLTIP :{BLACK}Opprett et nytt firma og bli med i det +STR_NETWORK_CLIENT_LIST_PLAYER_ICON_SELF_TOOLTIP :{BLACK}Dette er deg +STR_NETWORK_CLIENT_LIST_PLAYER_ICON_HOST_TOOLTIP :{BLACK}Dette er verten for spillet STR_NETWORK_CLIENT_LIST_ADMIN_CLIENT_KICK :Spark +STR_NETWORK_CLIENT_LIST_ADMIN_CLIENT_BAN :Utesteng +STR_NETWORK_CLIENT_LIST_ADMIN_COMPANY_RESET :Slett +STR_NETWORK_CLIENT_LIST_ADMIN_COMPANY_UNLOCK :Låse opp passord +STR_NETWORK_CLIENT_LIST_ASK_CAPTION :{WHITE}Administratorhandling +STR_NETWORK_CLIENT_LIST_ASK_CLIENT_KICK :{YELLOW}Er du sikker på at du vil utestenge spiller '{STRING}'? STR_NETWORK_CLIENT_LIST_ASK_CLIENT_BAN :{YELLOW}Er du sikker på at du vil utestenge spiller '{STRING}'? +STR_NETWORK_CLIENT_LIST_ASK_COMPANY_RESET :{YELLOW}Er du sikker på at du vil slette firmaet '{COMPANY}'? +STR_NETWORK_CLIENT_LIST_ASK_COMPANY_UNLOCK :{YELLOW}Er du sikker på at du vil tilbakestille passordet til firma '{COMPANY}'? STR_NETWORK_SERVER :Tjener STR_NETWORK_CLIENT :Klient diff --git a/src/lang/russian.txt b/src/lang/russian.txt index 1a8d94e9d1..bf9666d409 100644 --- a/src/lang/russian.txt +++ b/src/lang/russian.txt @@ -2142,6 +2142,8 @@ STR_FACE_TIE :Галстук: STR_FACE_EARRING :Серьга: STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}Изменить галстук или серьгу +STR_NETWORK_SERVER_VISIBILITY_PRIVATE :Публичный +STR_NETWORK_SERVER_VISIBILITY_PUBLIC :Частный # Network server list STR_NETWORK_SERVER_LIST_CAPTION :{WHITE}Сетевая игра @@ -2205,6 +2207,8 @@ STR_NETWORK_START_SERVER_NEW_GAME_NAME_TOOLTIP :{BLACK}Назв STR_NETWORK_START_SERVER_SET_PASSWORD :{BLACK}Установить пароль STR_NETWORK_START_SERVER_PASSWORD_TOOLTIP :{BLACK}Защитите вашу игру паролем, если не хотите, чтобы к ней могли подключиться посторонние. +STR_NETWORK_START_SERVER_VISIBILITY_LABEL :{BLACK}Видимость +STR_NETWORK_START_SERVER_VISIBILITY_TOOLTIP :{BLACK}Видимость вашего сервера в публичном списке STR_NETWORK_START_SERVER_CLIENTS_SELECT :{BLACK}{NUM} клиент{P "" а ов} STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS :{BLACK}Макс. количество клиентов: STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS_TOOLTIP :{BLACK}Выбор максимального числа клиентов. Не все места должны быть заняты @@ -2271,6 +2275,10 @@ STR_NETWORK_NEED_COMPANY_PASSWORD_CAPTION :{WHITE}Комп STR_NETWORK_COMPANY_LIST_CLIENT_LIST :Список клиентов # Network client list +STR_NETWORK_CLIENT_LIST_CAPTION :{WHITE}Сетевая игра +STR_NETWORK_CLIENT_LIST_PLAYER_NAME :{BLACK}Имя +STR_NETWORK_CLIENT_LIST_PLAYER_NAME_TOOLTIP :{BLACK}Ваше игровое имя +STR_NETWORK_CLIENT_LIST_PLAYER_NAME_QUERY_CAPTION :Ваше игровое имя STR_NETWORK_CLIENT_LIST_ADMIN_CLIENT_TOOLTIP :{BLACK}Действия администратора, применимые к этому клиенту STR_NETWORK_CLIENT_LIST_ADMIN_CLIENT_KICK :Отключить diff --git a/src/lang/spanish.txt b/src/lang/spanish.txt index 1af0a7fc4f..3ff09a3674 100644 --- a/src/lang/spanish.txt +++ b/src/lang/spanish.txt @@ -954,7 +954,7 @@ STR_GAME_OPTIONS_CURRENCY_MYR :Ringgit malasio STR_GAME_OPTIONS_ROAD_VEHICLES_DROPDOWN_LEFT :Conducir por la izquierda STR_GAME_OPTIONS_ROAD_VEHICLES_DROPDOWN_RIGHT :Conducir por la derecha -STR_GAME_OPTIONS_TOWN_NAMES_FRAME :{BLACK}Nombres de municipios +STR_GAME_OPTIONS_TOWN_NAMES_FRAME :{BLACK}Nombres de municipios: STR_GAME_OPTIONS_TOWN_NAMES_DROPDOWN_TOOLTIP :{BLACK}Selección del estilo del nombre de los municipios ############ start of townname region @@ -1992,6 +1992,8 @@ STR_FACE_TIE :Corbata: STR_FACE_EARRING :Pendientes: STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}Cambiar corbata o pendientes +STR_NETWORK_SERVER_VISIBILITY_PRIVATE :Privado +STR_NETWORK_SERVER_VISIBILITY_PUBLIC :Público # Network server list STR_NETWORK_SERVER_LIST_CAPTION :{WHITE}Multijugador @@ -2055,6 +2057,8 @@ STR_NETWORK_START_SERVER_NEW_GAME_NAME_TOOLTIP :{BLACK}La parti STR_NETWORK_START_SERVER_SET_PASSWORD :{BLACK}Establecer contraseña STR_NETWORK_START_SERVER_PASSWORD_TOOLTIP :{BLACK}Protege tu partida con una contraseña si no quieres que sea universalmente accesible +STR_NETWORK_START_SERVER_VISIBILITY_LABEL :{BLACK}Visibilidad +STR_NETWORK_START_SERVER_VISIBILITY_TOOLTIP :{BLACK}Establece si otras personas pueden ver el servidor en la lista de servidores públicos STR_NETWORK_START_SERVER_CLIENTS_SELECT :{BLACK}{NUM} cliente{P "" s} STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS :{BLACK}Número máximo de clientes: STR_NETWORK_START_SERVER_NUMBER_OF_CLIENTS_TOOLTIP :{BLACK}Selecciona el número máximo de clientes. No es necesario ocupar todos los espacios @@ -2118,11 +2122,44 @@ STR_NETWORK_NEED_GAME_PASSWORD_CAPTION :{WHITE}Servidor STR_NETWORK_NEED_COMPANY_PASSWORD_CAPTION :{WHITE}Empresa protegida. Introduce la contraseña # Network company list added strings -STR_NETWORK_COMPANY_LIST_CLIENT_LIST :Lista de clientes +STR_NETWORK_COMPANY_LIST_CLIENT_LIST :Jugadores en línea # Network client list +STR_NETWORK_CLIENT_LIST_CAPTION :{WHITE}Multijugador +STR_NETWORK_CLIENT_LIST_SERVER :{BLACK}Servidor +STR_NETWORK_CLIENT_LIST_SERVER_NAME :{BLACK}Nombre +STR_NETWORK_CLIENT_LIST_SERVER_NAME_TOOLTIP :{BLACK}Nombre del servidor en el que estás jugando +STR_NETWORK_CLIENT_LIST_SERVER_NAME_EDIT_TOOLTIP :{BLACK}Modifica el nombre de tu servidor +STR_NETWORK_CLIENT_LIST_SERVER_NAME_QUERY_CAPTION :Nombre del servidor +STR_NETWORK_CLIENT_LIST_SERVER_VISIBILITY :{BLACK}Visibilidad +STR_NETWORK_CLIENT_LIST_SERVER_VISIBILITY_TOOLTIP :{BLACK}Establece si otras personas pueden ver el servidor en la lista de servidores públicos +STR_NETWORK_CLIENT_LIST_PLAYER :{BLACK}Jugador +STR_NETWORK_CLIENT_LIST_PLAYER_NAME :{BLACK}Nombre +STR_NETWORK_CLIENT_LIST_PLAYER_NAME_TOOLTIP :{BLACK}Tu nombre de jugador +STR_NETWORK_CLIENT_LIST_PLAYER_NAME_EDIT_TOOLTIP :{BLACK}Modifica tu nombre de jugador +STR_NETWORK_CLIENT_LIST_PLAYER_NAME_QUERY_CAPTION :Tu nombre de jugador +STR_NETWORK_CLIENT_LIST_ADMIN_CLIENT_TOOLTIP :{BLACK}Acciones de administrador a realizar para este cliente +STR_NETWORK_CLIENT_LIST_ADMIN_COMPANY_TOOLTIP :{BLACK}Acciones de administrador a realizar para esta compañía +STR_NETWORK_CLIENT_LIST_JOIN_TOOLTIP :{BLACK}Unirse a esta compañía +STR_NETWORK_CLIENT_LIST_CHAT_CLIENT_TOOLTIP :{BLACK}Manda un mensaje a este jugador +STR_NETWORK_CLIENT_LIST_CHAT_COMPANY_TOOLTIP :{BLACK}Manda un mensaje a todos los jugadores de esta compañía +STR_NETWORK_CLIENT_LIST_CHAT_SPECTATOR_TOOLTIP :{BLACK}Manda un mensaje a todos los observadores +STR_NETWORK_CLIENT_LIST_SPECTATORS :Observadores +STR_NETWORK_CLIENT_LIST_NEW_COMPANY :(Nueva compañía) +STR_NETWORK_CLIENT_LIST_NEW_COMPANY_TOOLTIP :{BLACK}Crea una nueva companía y te une a ella +STR_NETWORK_CLIENT_LIST_PLAYER_ICON_SELF_TOOLTIP :{BLACK}Éste eres tú +STR_NETWORK_CLIENT_LIST_PLAYER_ICON_HOST_TOOLTIP :{BLACK}Éste es el servidor de la partida +STR_NETWORK_CLIENT_LIST_ADMIN_CLIENT_KICK :Expulsar +STR_NETWORK_CLIENT_LIST_ADMIN_CLIENT_BAN :Prohibir el acceso +STR_NETWORK_CLIENT_LIST_ADMIN_COMPANY_RESET :Eliminar +STR_NETWORK_CLIENT_LIST_ADMIN_COMPANY_UNLOCK :Eliminar contraseña +STR_NETWORK_CLIENT_LIST_ASK_CAPTION :{WHITE}Acción de administrador +STR_NETWORK_CLIENT_LIST_ASK_CLIENT_KICK :{YELLOW}¿Estás seguro de que deseas expulsar al jugador '{STRING}'? +STR_NETWORK_CLIENT_LIST_ASK_CLIENT_BAN :{YELLOW}¿Estás seguro de que deseas prohibir el acceso al jugador '{STRING}'? +STR_NETWORK_CLIENT_LIST_ASK_COMPANY_RESET :{YELLOW}¿Estás seguro de que quieres eliminar la compañía '{COMPANY}'? +STR_NETWORK_CLIENT_LIST_ASK_COMPANY_UNLOCK :{YELLOW}¿Estás seguro de que quieres eliminar la contraseña de la compañía '{COMPANY}'? STR_NETWORK_SERVER :Servidor STR_NETWORK_CLIENT :Cliente @@ -2167,6 +2204,7 @@ STR_NETWORK_ERROR_SERVER_START :{WHITE}No se ha STR_NETWORK_ERROR_CLIENT_START :{WHITE}No se pudo conectar STR_NETWORK_ERROR_TIMEOUT :{WHITE}Tiempo de espera agotado en conexión #{NUM} STR_NETWORK_ERROR_SERVER_ERROR :{WHITE}Se ha producido un error de protocolo y la conexión ha sido cerrada +STR_NETWORK_ERROR_BAD_PLAYER_NAME :{WHITE}No se ha establecido tu nombre de jugador. El nombre se puede establecer en la parte superior de la ventana de Multijugador STR_NETWORK_ERROR_WRONG_REVISION :{WHITE}La versión de este cliente no corresponde con la versión del servidor STR_NETWORK_ERROR_WRONG_PASSWORD :{WHITE}Contraseña incorrecta STR_NETWORK_ERROR_SERVER_FULL :{WHITE}El servidor está completo @@ -2179,6 +2217,7 @@ STR_NETWORK_ERROR_TIMEOUT_PASSWORD :{WHITE}Has tard STR_NETWORK_ERROR_TIMEOUT_COMPUTER :{WHITE}Su ordenador es demasiado lento para seguir la velocidad del servidor STR_NETWORK_ERROR_TIMEOUT_MAP :{WHITE}Su ordenador necesitó demasiado tiempo para descargar el mapa STR_NETWORK_ERROR_TIMEOUT_JOIN :{WHITE}Su ordenador necesitó demasiado tiempo para conectar al servidor +STR_NETWORK_ERROR_INVALID_CLIENT_NAME :{WHITE}Tu nombre de jugador no es válido ############ Leave those lines in this order!! STR_NETWORK_ERROR_CLIENT_GENERAL :error general @@ -2201,6 +2240,7 @@ STR_NETWORK_ERROR_CLIENT_TIMEOUT_PASSWORD :no se ha recibi STR_NETWORK_ERROR_CLIENT_TIMEOUT_COMPUTER :tiempo agotado en general STR_NETWORK_ERROR_CLIENT_TIMEOUT_MAP :la descarga del mapa ha necesitado demasiado tiempo STR_NETWORK_ERROR_CLIENT_TIMEOUT_JOIN :el procesado del mapa ha necesitado demasiado tiempo +STR_NETWORK_ERROR_CLIENT_INVALID_CLIENT_NAME :nombre de cliente inválido ############ End of leave-in-this-order STR_NETWORK_ERROR_CLIENT_GUI_LOST_CONNECTION_CAPTION :{WHITE}Posible pérdida de conexión @@ -3040,6 +3080,7 @@ STR_NEWGRF_ERROR_MSG_WARNING :{RED}Atención: STR_NEWGRF_ERROR_MSG_ERROR :{RED}Error: {SILVER}{STRING} STR_NEWGRF_ERROR_MSG_FATAL :{RED}Error Fatal: {SILVER}{STRING} STR_NEWGRF_ERROR_FATAL_POPUP :{WHITE}Ha ocurrido un error fatal de NewGRF:{}{STRING} +STR_NEWGRF_ERROR_POPUP :{WHITE}Ha ocurrido un error de NewGRF:{}{STRING} STR_NEWGRF_ERROR_VERSION_NUMBER :{1:STRING} no funcionará con la con la versión de TTDPatch informada por OpenTTD STR_NEWGRF_ERROR_DOS_OR_WINDOWS :{1:STRING} es para la versión {STRING} de TTD STR_NEWGRF_ERROR_UNSET_SWITCH :{1:STRING} está diseñado para ser usado con {STRING} From ece9a356dcae3c9f7fe85a0f7dab87e06d8eb299 Mon Sep 17 00:00:00 2001 From: Jonathan G Rennison Date: Mon, 3 May 2021 15:03:25 +0100 Subject: [PATCH 265/268] Fix #9113: Assertion failure when removing airport with order backup (#9182) --- src/station_cmd.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/station_cmd.cpp b/src/station_cmd.cpp index f642a914f6..451dedbad4 100644 --- a/src/station_cmd.cpp +++ b/src/station_cmd.cpp @@ -2394,9 +2394,9 @@ static CommandCost RemoveAirport(TileIndex tile, DoCommandFlag flags) if (flags & DC_EXEC) { for (uint i = 0; i < st->airport.GetNumHangars(); ++i) { - DeleteWindowById( - WC_VEHICLE_DEPOT, st->airport.GetHangarTile(i) - ); + TileIndex tile_cur = st->airport.GetHangarTile(i); + OrderBackup::Reset(tile_cur, false); + DeleteWindowById(WC_VEHICLE_DEPOT, tile_cur); } const AirportSpec *as = st->airport.GetSpec(); @@ -2418,7 +2418,6 @@ static CommandCost RemoveAirport(TileIndex tile, DoCommandFlag flags) cost.AddCost(_price[PR_CLEAR_STATION_AIRPORT]); if (flags & DC_EXEC) { - if (IsHangarTile(tile_cur)) OrderBackup::Reset(tile_cur, false); DeleteAnimatedTile(tile_cur); DoClearSquare(tile_cur); DeleteNewGRFInspectWindow(GSF_AIRPORTTILES, tile_cur); From 0bc6f3234664cbd71dab89ddf75b14616cced160 Mon Sep 17 00:00:00 2001 From: PeterN Date: Mon, 3 May 2021 15:12:47 +0100 Subject: [PATCH 266/268] Fix #9174: Don't update text effect if it has been reset. (#9183) --- src/texteff.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/texteff.cpp b/src/texteff.cpp index e29326a90a..1c4722ae61 100644 --- a/src/texteff.cpp +++ b/src/texteff.cpp @@ -79,6 +79,7 @@ void UpdateTextEffect(TextEffectID te_id, StringID msg) void UpdateAllTextEffectVirtCoords() { for (auto &te : _text_effects) { + if (te.string_id == INVALID_STRING_ID) continue; SetDParam(0, te.params_1); SetDParam(1, te.params_2); te.UpdatePosition(te.center, te.top, te.string_id, te.string_id - 1); From 08781d96ed385158a5b452be1779260a92204e4e Mon Sep 17 00:00:00 2001 From: PeterN Date: Mon, 3 May 2021 16:39:20 +0100 Subject: [PATCH 267/268] Fix: Query windows may be partially drawn initially. (#9184) Query window was not marked dirty after being moved on init. It was then marked dirty once the white border flash completed. --- src/misc_gui.cpp | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/misc_gui.cpp b/src/misc_gui.cpp index b2de451404..e9ee7b193b 100644 --- a/src/misc_gui.cpp +++ b/src/misc_gui.cpp @@ -1162,12 +1162,9 @@ struct QueryWindow : public Window { this->caption = caption; this->message = message; this->proc = callback; + this->parent = parent; this->InitNested(WN_CONFIRM_POPUP_QUERY); - - this->parent = parent; - this->left = parent->left + (parent->width / 2) - (this->width / 2); - this->top = parent->top + (parent->height / 2) - (this->height / 2); } ~QueryWindow() @@ -1175,6 +1172,14 @@ struct QueryWindow : public Window { if (this->proc != nullptr) this->proc(this->parent, false); } + void FindWindowPlacementAndResize(int def_width, int def_height) override + { + /* Position query window over the calling window, ensuring it's within screen bounds. */ + this->left = Clamp(parent->left + (parent->width / 2) - (this->width / 2), 0, _screen.width - this->width); + this->top = Clamp(parent->top + (parent->height / 2) - (this->height / 2), 0, _screen.height - this->height); + this->SetDirty(); + } + void SetStringParameters(int widget) const override { switch (widget) { From 6bd7f8816dfee1a5e697d18e30aad4b5ef7e320f Mon Sep 17 00:00:00 2001 From: rubidium42 Date: Mon, 3 May 2021 17:40:19 +0200 Subject: [PATCH 268/268] Fix #9117, 04ce1f07: [Fluidsynth] Infinite wait when stopping song (#9181) In FluidSynth 2.2.0 an extra state was added to denote stopping. To transition from this state to a stopped state the rendering needs to be running. Since 04ce1f07 locking was added that skipped the rendering when something else held a lock, so the state would never get to stopped and join would never return. --- src/music/fluidsynth.cpp | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/music/fluidsynth.cpp b/src/music/fluidsynth.cpp index 000432e1da..b5cbf46aa1 100644 --- a/src/music/fluidsynth.cpp +++ b/src/music/fluidsynth.cpp @@ -163,14 +163,21 @@ void MusicDriver_FluidSynth::PlaySong(const MusicSongInfo &song) void MusicDriver_FluidSynth::StopSong() { - std::lock_guard lock{ _midi.synth_mutex }; + { + std::lock_guard lock{ _midi.synth_mutex }; - if (!_midi.player) return; + if (!_midi.player) return; - fluid_player_stop(_midi.player); + fluid_player_stop(_midi.player); + } + + /* The join must be run without lock as the Music rendering needs to be + * running so FluidSynth's internals can actually stop the playing. */ if (fluid_player_join(_midi.player) != FLUID_OK) { DEBUG(driver, 0, "Could not join player"); } + + std::lock_guard lock{ _midi.synth_mutex }; delete_fluid_player(_midi.player); fluid_synth_system_reset(_midi.synth); fluid_synth_all_sounds_off(_midi.synth, -1);