diff --git a/bin/ai/compat_1.10.nut b/bin/ai/compat_1.10.nut
index 3081fb58e8..2baaddb836 100644
--- a/bin/ai/compat_1.10.nut
+++ b/bin/ai/compat_1.10.nut
@@ -4,3 +4,5 @@
* 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 .
*/
+
+AILog.Info("1.10 API compatibility in effect.");
diff --git a/bin/ai/compat_1.11.nut b/bin/ai/compat_1.11.nut
new file mode 100644
index 0000000000..3081fb58e8
--- /dev/null
+++ b/bin/ai/compat_1.11.nut
@@ -0,0 +1,6 @@
+/*
+ * 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 .
+ */
diff --git a/bin/ai/regression/regression_info.nut b/bin/ai/regression/regression_info.nut
index 020b186faf..758754cfe7 100644
--- a/bin/ai/regression/regression_info.nut
+++ b/bin/ai/regression/regression_info.nut
@@ -4,7 +4,7 @@ class Regression extends AIInfo {
function GetShortName() { return "REGR"; }
function GetDescription() { return "This runs regression-tests on some commands. On the same map the result should always be the same."; }
function GetVersion() { return 1; }
- function GetAPIVersion() { return "1.10"; }
+ function GetAPIVersion() { return "1.11"; }
function GetDate() { return "2007-03-18"; }
function CreateInstance() { return "Regression"; }
}
diff --git a/bin/game/compat_1.10.nut b/bin/game/compat_1.10.nut
index 3081fb58e8..1c85766c0f 100644
--- a/bin/game/compat_1.10.nut
+++ b/bin/game/compat_1.10.nut
@@ -4,3 +4,5 @@
* 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 .
*/
+
+GSLog.Info("1.10 API compatibility in effect.");
diff --git a/bin/game/compat_1.11.nut b/bin/game/compat_1.11.nut
new file mode 100644
index 0000000000..3081fb58e8
--- /dev/null
+++ b/bin/game/compat_1.11.nut
@@ -0,0 +1,6 @@
+/*
+ * 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 .
+ */
diff --git a/os/os2/installer/make_installer.cmd b/os/os2/installer/make_installer.cmd
index 3addeca1ae..885b2dc623 100644
--- a/os/os2/installer/make_installer.cmd
+++ b/os/os2/installer/make_installer.cmd
@@ -1,6 +1,6 @@
@echo off
-set OPENTTD_VERSION=1.10.0
+set OPENTTD_VERSION=1.11.0
set OPENSFX_VERSION=0.8.0
set NOSOUND_VERSION=0.8.0
set OPENGFX_VERSION=1.2.0
diff --git a/os/rpm/openttd.spec b/os/rpm/openttd.spec
index 340a9886b9..134caad38e 100644
--- a/os/rpm/openttd.spec
+++ b/os/rpm/openttd.spec
@@ -17,9 +17,9 @@
#
Name: openttd
-Version: 1.10.beta2
+Version: 1.11.beta1
Release: 0
-%define srcver 1.10.0-beta2
+%define srcver 1.11.0-beta1
Summary: An open source reimplementation of Chris Sawyer's Transport Tycoon Deluxe
License: GPL-2.0
Group: Amusements/Games/Strategy/Other
diff --git a/os/windows/installer/install.nsi b/os/windows/installer/install.nsi
index 2561b76d55..b6837c559e 100644
--- a/os/windows/installer/install.nsi
+++ b/os/windows/installer/install.nsi
@@ -1,9 +1,9 @@
# Version numbers to update
!define APPV_MAJOR 1
-!define APPV_MINOR 10
+!define APPV_MINOR 11
!define APPV_MAINT 0
-!define APPV_BUILD 1
-!define APPV_EXTRA "-beta2"
+!define APPV_BUILD 0
+!define APPV_EXTRA "-beta1"
!define APPNAME "OpenTTD" ; Define application name
!define APPVERSION "${APPV_MAJOR}.${APPV_MINOR}.${APPV_MAINT}${APPV_EXTRA}" ; Define application version
diff --git a/src/3rdparty/squirrel/sqstdlib/sqstdaux.cpp b/src/3rdparty/squirrel/sqstdlib/sqstdaux.cpp
index 540975fb5c..a4f4e21c33 100644
--- a/src/3rdparty/squirrel/sqstdlib/sqstdaux.cpp
+++ b/src/3rdparty/squirrel/sqstdlib/sqstdaux.cpp
@@ -38,7 +38,7 @@ void sqstd_printcallstack(HSQUIRRELVM v)
src = si.source;
}
}
- pf(v,"*FUNCTION [%s()] %s line [%d]\n",fn,src,si.line);
+ pf(v,"*FUNCTION [%s()] %s line [" OTTD_PRINTF64 "]\n",fn,src,si.line);
level++;
}
level=0;
@@ -56,7 +56,7 @@ void sqstd_printcallstack(HSQUIRRELVM v)
break;
case OT_INTEGER:
sq_getinteger(v,-1,&i);
- pf(v,"[%s] %d\n",name,i);
+ pf(v,"[%s] " OTTD_PRINTF64 "\n",name,i);
break;
case OT_FLOAT:
sq_getfloat(v,-1,&f);
@@ -134,7 +134,7 @@ void _sqstd_compiler_error(HSQUIRRELVM v,const SQChar *sErr,const SQChar *sSourc
{
SQPRINTFUNCTION pf = sq_getprintfunc(v);
if(pf) {
- pf(v,"%s line = (%d) column = (%d) : error %s\n",sSource,line,column,sErr);
+ pf(v,"%s line = (" OTTD_PRINTF64 ") column = (" OTTD_PRINTF64 ") : error %s\n",sSource,line,column,sErr);
}
}
diff --git a/src/3rdparty/squirrel/squirrel/sqbaselib.cpp b/src/3rdparty/squirrel/squirrel/sqbaselib.cpp
index 86a1b11858..e5de01a635 100644
--- a/src/3rdparty/squirrel/squirrel/sqbaselib.cpp
+++ b/src/3rdparty/squirrel/squirrel/sqbaselib.cpp
@@ -219,7 +219,7 @@ static SQInteger base_array(HSQUIRRELVM v)
SQInteger nInitialSize = tointeger(stack_get(v,2));
SQInteger ret = 1;
if (nInitialSize < 0) {
- v->Raise_Error("can't create/resize array with/to size %d", nInitialSize);
+ v->Raise_Error("can't create/resize array with/to size " OTTD_PRINTF64, nInitialSize);
nInitialSize = 0;
ret = -1;
}
diff --git a/src/3rdparty/squirrel/squirrel/sqdebug.cpp b/src/3rdparty/squirrel/squirrel/sqdebug.cpp
index 2f24e83b66..44f767c297 100644
--- a/src/3rdparty/squirrel/squirrel/sqdebug.cpp
+++ b/src/3rdparty/squirrel/squirrel/sqdebug.cpp
@@ -122,5 +122,5 @@ void SQVM::Raise_ParamTypeError(SQInteger nparam,SQInteger typemask,SQInteger ty
StringCat(exptypes,SQString::Create(_ss(this), IdType2Name((SQObjectType)mask), -1), exptypes);
}
}
- Raise_Error("parameter %d has an invalid type '%s' ; expected: '%s'", nparam, IdType2Name((SQObjectType)type), _stringval(exptypes));
+ Raise_Error("parameter " OTTD_PRINTF64 " has an invalid type '%s' ; expected: '%s'", nparam, IdType2Name((SQObjectType)type), _stringval(exptypes));
}
diff --git a/src/ai/ai_gui.cpp b/src/ai/ai_gui.cpp
index a684e4a350..a0552628cd 100644
--- a/src/ai/ai_gui.cpp
+++ b/src/ai/ai_gui.cpp
@@ -179,6 +179,7 @@ struct AIListWindow : public Window {
InvalidateWindowData(WC_GAME_OPTIONS, WN_GAME_OPTIONS_AI);
InvalidateWindowClassesData(WC_AI_SETTINGS);
DeleteWindowByClass(WC_QUERY_STRING);
+ InvalidateWindowClassesData(WC_TEXTFILE);
}
void OnClick(Point pt, int widget, int click_count) override
@@ -640,15 +641,24 @@ struct ScriptTextfileWindow : public TextfileWindow {
ScriptTextfileWindow(TextfileType file_type, CompanyID slot) : TextfileWindow(file_type), slot(slot)
{
- const char *textfile = GetConfig(slot)->GetTextfile(file_type, slot);
- this->LoadTextfile(textfile, (slot == OWNER_DEITY) ? GAME_DIR : AI_DIR);
+ this->OnInvalidateData();
}
void SetStringParameters(int widget) const override
{
if (widget == WID_TF_CAPTION) {
SetDParam(0, (slot == OWNER_DEITY) ? STR_CONTENT_TYPE_GAME_SCRIPT : STR_CONTENT_TYPE_AI);
- SetDParamStr(1, GetConfig(slot)->GetName());
+ SetDParamStr(1, GetConfig(slot)->GetInfo()->GetName());
+ }
+ }
+
+ void OnInvalidateData(int data = 0, bool gui_scope = true) override
+ {
+ const char *textfile = GetConfig(slot)->GetTextfile(file_type, slot);
+ if (textfile == nullptr) {
+ delete this;
+ } else {
+ this->LoadTextfile(textfile, (slot == OWNER_DEITY) ? GAME_DIR : AI_DIR);
}
}
};
diff --git a/src/ai/ai_info.cpp b/src/ai/ai_info.cpp
index 2d80003f6b..6e1ef6cecd 100644
--- a/src/ai/ai_info.cpp
+++ b/src/ai/ai_info.cpp
@@ -27,7 +27,8 @@ static bool CheckAPIVersion(const char *api_version)
return strcmp(api_version, "0.7") == 0 || strcmp(api_version, "1.0") == 0 || strcmp(api_version, "1.1") == 0 ||
strcmp(api_version, "1.2") == 0 || strcmp(api_version, "1.3") == 0 || strcmp(api_version, "1.4") == 0 ||
strcmp(api_version, "1.5") == 0 || strcmp(api_version, "1.6") == 0 || strcmp(api_version, "1.7") == 0 ||
- strcmp(api_version, "1.8") == 0 || strcmp(api_version, "1.9") == 0 || strcmp(api_version, "1.10") == 0;
+ strcmp(api_version, "1.8") == 0 || strcmp(api_version, "1.9") == 0 || strcmp(api_version, "1.10") == 0 ||
+ strcmp(api_version, "1.11") == 0;
}
#if defined(_WIN32)
diff --git a/src/console.cpp b/src/console.cpp
index 3155b08c63..8e1dce0135 100644
--- a/src/console.cpp
+++ b/src/console.cpp
@@ -21,6 +21,7 @@
#include "safeguards.h"
static const uint ICON_TOKEN_COUNT = 20; ///< Maximum number of tokens in one command
+static const uint ICON_MAX_RECURSE = 10; ///< Maximum number of recursion
/* console parser */
IConsoleCmd *_iconsole_cmds; ///< list of registered commands
@@ -317,13 +318,18 @@ IConsoleAlias *IConsoleAliasGet(const char *name)
* @param tokencount the number of parameters passed
* @param *tokens are the parameters given to the original command (0 is the first param)
*/
-static void IConsoleAliasExec(const IConsoleAlias *alias, byte tokencount, char *tokens[ICON_TOKEN_COUNT])
+static void IConsoleAliasExec(const IConsoleAlias *alias, byte tokencount, char *tokens[ICON_TOKEN_COUNT], const uint recurse_count)
{
char alias_buffer[ICON_MAX_STREAMSIZE] = { '\0' };
char *alias_stream = alias_buffer;
DEBUG(console, 6, "Requested command is an alias; parsing...");
+ if (recurse_count > ICON_MAX_RECURSE) {
+ IConsoleError("Too many alias expansions, recursion limit reached. Aborting");
+ return;
+ }
+
for (const char *cmdptr = alias->cmdline; *cmdptr != '\0'; cmdptr++) {
switch (*cmdptr) {
case '\'': // ' will double for ""
@@ -331,7 +337,7 @@ static void IConsoleAliasExec(const IConsoleAlias *alias, byte tokencount, char
break;
case ';': // Cmd separator; execute previous and start new command
- IConsoleCmdExec(alias_buffer);
+ IConsoleCmdExec(alias_buffer, recurse_count);
alias_stream = alias_buffer;
*alias_stream = '\0'; // Make sure the new command is terminated.
@@ -391,7 +397,7 @@ static void IConsoleAliasExec(const IConsoleAlias *alias, byte tokencount, char
}
}
- IConsoleCmdExec(alias_buffer);
+ IConsoleCmdExec(alias_buffer, recurse_count);
}
/**
@@ -399,7 +405,7 @@ static void IConsoleAliasExec(const IConsoleAlias *alias, byte tokencount, char
* individual tokens (separated by spaces), then execute it if possible
* @param cmdstr string to be parsed and executed
*/
-void IConsoleCmdExec(const char *cmdstr)
+void IConsoleCmdExec(const char *cmdstr, const uint recurse_count)
{
const char *cmdptr;
char *tokens[ICON_TOKEN_COUNT], tokenstream[ICON_MAX_STREAMSIZE];
@@ -505,7 +511,7 @@ void IConsoleCmdExec(const char *cmdstr)
t_index--;
IConsoleAlias *alias = IConsoleAliasGet(tokens[0]);
if (alias != nullptr) {
- IConsoleAliasExec(alias, t_index, &tokens[1]);
+ IConsoleAliasExec(alias, t_index, &tokens[1], recurse_count + 1);
return;
}
diff --git a/src/console_cmds.cpp b/src/console_cmds.cpp
index 3096322802..9475e1929b 100644
--- a/src/console_cmds.cpp
+++ b/src/console_cmds.cpp
@@ -526,7 +526,7 @@ DEF_CONSOLE_CMD(ConClearBuffer)
* Network Core Console Commands
**********************************/
-static bool ConKickOrBan(const char *argv, bool ban)
+static bool ConKickOrBan(const char *argv, bool ban, const char *reason)
{
uint n;
@@ -550,14 +550,14 @@ static bool ConKickOrBan(const char *argv, bool ban)
if (!ban) {
/* Kick only this client, not all clients with that IP */
- NetworkServerKickClient(client_id);
+ NetworkServerKickClient(client_id, reason);
return true;
}
/* When banning, kick+ban all clients with that IP */
- n = NetworkServerKickOrBanIP(client_id, ban);
+ n = NetworkServerKickOrBanIP(client_id, ban, reason);
} else {
- n = NetworkServerKickOrBanIP(argv, ban);
+ n = NetworkServerKickOrBanIP(argv, ban, reason);
}
if (n == 0) {
@@ -572,28 +572,48 @@ static bool ConKickOrBan(const char *argv, bool ban)
DEF_CONSOLE_CMD(ConKick)
{
if (argc == 0) {
- IConsoleHelp("Kick a client from a network game. Usage: 'kick '");
+ IConsoleHelp("Kick a client from a network game. Usage: 'kick []'");
IConsoleHelp("For client-id's, see the command 'clients'");
return true;
}
- if (argc != 2) return false;
+ if (argc != 2 && argc != 3) return false;
- return ConKickOrBan(argv[1], false);
+ /* No reason supplied for kicking */
+ if (argc == 2) return ConKickOrBan(argv[1], false, nullptr);
+
+ /* Reason for kicking supplied */
+ size_t kick_message_length = strlen(argv[2]);
+ if (kick_message_length >= 255) {
+ IConsolePrintF(CC_ERROR, "ERROR: Maximum kick message length is 254 characters. You entered %d characters.", kick_message_length);
+ return false;
+ } else {
+ return ConKickOrBan(argv[1], false, argv[2]);
+ }
}
DEF_CONSOLE_CMD(ConBan)
{
if (argc == 0) {
- IConsoleHelp("Ban a client from a network game. Usage: 'ban '");
+ IConsoleHelp("Ban a client from a network game. Usage: 'ban []'");
IConsoleHelp("For client-id's, see the command 'clients'");
IConsoleHelp("If the client is no longer online, you can still ban his/her IP");
return true;
}
- if (argc != 2) return false;
+ if (argc != 2 && argc != 3) return false;
- return ConKickOrBan(argv[1], true);
+ /* No reason supplied for kicking */
+ if (argc == 2) return ConKickOrBan(argv[1], true, nullptr);
+
+ /* Reason for kicking supplied */
+ size_t kick_message_length = strlen(argv[2]);
+ if (kick_message_length >= 255) {
+ IConsolePrintF(CC_ERROR, "ERROR: Maximum kick message length is 254 characters. You entered %d characters.", kick_message_length);
+ return false;
+ } else {
+ return ConKickOrBan(argv[1], true, argv[2]);
+ }
}
DEF_CONSOLE_CMD(ConUnBan)
diff --git a/src/console_func.h b/src/console_func.h
index 1dad477b8a..6d634a4553 100644
--- a/src/console_func.h
+++ b/src/console_func.h
@@ -28,7 +28,7 @@ void IConsoleWarning(const char *string);
void IConsoleError(const char *string);
/* Parser */
-void IConsoleCmdExec(const char *cmdstr);
+void IConsoleCmdExec(const char *cmdstr, const uint recurse_count = 0);
bool IsValidConsoleColour(TextColour c);
diff --git a/src/depot.cpp b/src/depot.cpp
index 5819c3b0fd..35dcadbdb3 100644
--- a/src/depot.cpp
+++ b/src/depot.cpp
@@ -28,6 +28,8 @@ INSTANTIATE_POOL_METHODS(Depot)
*/
Depot::~Depot()
{
+ free(this->name);
+
if (CleaningPool()) return;
if (!IsDepotTile(this->xy) || GetDepotIndex(this->xy) != this->index) {
diff --git a/src/game/game_info.cpp b/src/game/game_info.cpp
index 91a463bed5..a039401b32 100644
--- a/src/game/game_info.cpp
+++ b/src/game/game_info.cpp
@@ -24,7 +24,8 @@ static bool CheckAPIVersion(const char *api_version)
{
return strcmp(api_version, "1.2") == 0 || strcmp(api_version, "1.3") == 0 || strcmp(api_version, "1.4") == 0 ||
strcmp(api_version, "1.5") == 0 || strcmp(api_version, "1.6") == 0 || strcmp(api_version, "1.7") == 0 ||
- strcmp(api_version, "1.8") == 0 || strcmp(api_version, "1.9") == 0 || strcmp(api_version, "1.10") == 0;
+ strcmp(api_version, "1.8") == 0 || strcmp(api_version, "1.9") == 0 || strcmp(api_version, "1.10") == 0 ||
+ strcmp(api_version, "1.11") == 0;
}
#if defined(_WIN32)
diff --git a/src/group_gui.cpp b/src/group_gui.cpp
index c86c11e2cb..ee0113db93 100644
--- a/src/group_gui.cpp
+++ b/src/group_gui.cpp
@@ -1102,6 +1102,7 @@ public:
}
this->groups.ForceRebuild();
this->BuildGroupList(this->owner);
+ this->group_sb->SetCount((uint)this->groups.size());
id_g = find_index(this->groups, g);
}
this->group_sb->ScrollTowards(id_g);
diff --git a/src/lang/czech.txt b/src/lang/czech.txt
index 964375e8ed..0861cc3f45 100644
--- a/src/lang/czech.txt
+++ b/src/lang/czech.txt
@@ -1395,6 +1395,8 @@ STR_CONFIG_SETTING_POPULATION_IN_LABEL :Zobrazovat popu
STR_CONFIG_SETTING_POPULATION_IN_LABEL_HELPTEXT :Zobrazovat městskou populaci u názvu města na mapě
STR_CONFIG_SETTING_GRAPH_LINE_THICKNESS :Tloušťky čar v grafech: {STRING}
STR_CONFIG_SETTING_GRAPH_LINE_THICKNESS_HELPTEXT :Tloušťka čáry v grafech. Tenká čára se čte přesněji, silnější je lépe viditelná a barva je snadněji rozpoznatelná.
+STR_CONFIG_SETTING_SHOW_NEWGRF_NAME :Zobrazovat název NewGRF v okně nákupu vozidel: {STRING}
+STR_CONFIG_SETTING_SHOW_NEWGRF_NAME_HELPTEXT :Přidá řádek do okna nákupu vozidel informující, ze které NewGRF vybrané vozidlo pochází.
STR_CONFIG_SETTING_LANDSCAPE :Klima: {STRING}
STR_CONFIG_SETTING_LANDSCAPE_HELPTEXT :Klima určuje základy herního scénáře s rozdílnými druhy nákladu a požadavky na růst měst. Nové GRaFiky a Herní Skripty umožní ještě jemnější kontrolu
@@ -1632,6 +1634,10 @@ STR_CONFIG_SETTING_NEWS_MESSAGES_FULL :Plná
STR_CONFIG_SETTING_COLOURED_NEWS_YEAR :Barevné noviny se objeví v roce: {STRING}
STR_CONFIG_SETTING_COLOURED_NEWS_YEAR_HELPTEXT :Rok od kterého budou novinová oznámeni zobrazena v barvách. Před tímto rokem jsou černobílá.
STR_CONFIG_SETTING_STARTING_YEAR :Počáteční datum: {STRING}
+STR_CONFIG_SETTING_ENDING_YEAR :Rok vyhodnocení: {STRING}
+STR_CONFIG_SETTING_ENDING_YEAR_HELPTEXT :Rok, kdy je ve hře uzavřeno hodnocení společností. Na konci tohoto roku je zaznamenáno skóre společností a je zobrazena tabulka nejlepších společností, ale ve hře je možné pokračovat i dál.{}Pokud je nastaven rok před rokem počátku hry, tabulka s hodnocením nebude zobrazena nikdy.
+STR_CONFIG_SETTING_ENDING_YEAR_VALUE :{NUM}
+STR_CONFIG_SETTING_ENDING_YEAR_ZERO :Nikdy
STR_CONFIG_SETTING_SMOOTH_ECONOMY :Plynulé změny ekonomiky (více menších změn): {STRING}
STR_CONFIG_SETTING_SMOOTH_ECONOMY_HELPTEXT :Pokud je zapnuto, produkce průmyslu se mění častěji ale změny jsou menší. Toto nastavení většinou nemá vliv na průmysl, který je přidaný novou grafikou
STR_CONFIG_SETTING_ALLOW_SHARES :Povolit kupování podílu z ostatních společností: {STRING}
@@ -3477,8 +3483,17 @@ STR_COMPANY_INFRASTRUCTURE_VIEW_TOTAL :{WHITE}{CURRENC
# Industry directory
STR_INDUSTRY_DIRECTORY_CAPTION :{WHITE}Průmysl
STR_INDUSTRY_DIRECTORY_NONE :{ORANGE}- Nic -
+STR_INDUSTRY_DIRECTORY_ITEM_INFO :{BLACK}{CARGO_LONG}{STRING}{YELLOW} ({COMMA}% přepraveno){BLACK}
STR_INDUSTRY_DIRECTORY_ITEM_NOPROD :{ORANGE}{INDUSTRY}
+STR_INDUSTRY_DIRECTORY_ITEM_PROD1 :{ORANGE}{INDUSTRY} {STRING}
+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} a {NUM} další...
STR_INDUSTRY_DIRECTORY_LIST_CAPTION :{BLACK}Název průmyslu - pohled na něj zaměříš kliknutím na jeho jméno. Při stisknutém Ctrl otevřeš nový pohled
+STR_INDUSTRY_DIRECTORY_ACCEPTED_CARGO_FILTER :{BLACK}Přijímá náklad: {SILVER}{STRING}
+STR_INDUSTRY_DIRECTORY_PRODUCED_CARGO_FILTER :{BLACK}Produkuje náklad: {SILVER}{STRING}
+STR_INDUSTRY_DIRECTORY_FILTER_ALL_TYPES :Všechny druhy nákladu
+STR_INDUSTRY_DIRECTORY_FILTER_NONE :Žádný
# Industry view
STR_INDUSTRY_VIEW_CAPTION :{WHITE}{INDUSTRY}
@@ -4256,6 +4271,13 @@ STR_AI_LIST_ACCEPT_TOOLTIP :{BLACK}Vybrat o
STR_AI_LIST_CANCEL :{BLACK}Zrušit
STR_AI_LIST_CANCEL_TOOLTIP :{BLACK}Neměňte skript
+STR_SCREENSHOT_CAPTION :{WHITE}Pořídit snímek obrazovky
+STR_SCREENSHOT_SCREENSHOT :{BLACK}Běžný snímek obrazovky
+STR_SCREENSHOT_ZOOMIN_SCREENSHOT :{BLACK}Snímek obrazovky v úplném přiblížení
+STR_SCREENSHOT_DEFAULTZOOM_SCREENSHOT :{BLACK}Snímek obrazovky ve výchozím přiblížení
+STR_SCREENSHOT_WORLD_SCREENSHOT :{BLACK}Snímek celé mapy
+STR_SCREENSHOT_HEIGHTMAP_SCREENSHOT :{BLACK}Snímek výškové mapy
+STR_SCREENSHOT_MINIMAP_SCREENSHOT :{BLACK}Snímek (náhledové) mapy světa
# AI Parameters
STR_AI_SETTINGS_CAPTION :{WHITE}{STRING} Parametry
diff --git a/src/lang/english.txt b/src/lang/english.txt
index 5063e30999..c52fae2259 100644
--- a/src/lang/english.txt
+++ b/src/lang/english.txt
@@ -2402,6 +2402,7 @@ STR_NETWORK_ERROR_WRONG_PASSWORD :{WHITE}Wrong pa
STR_NETWORK_ERROR_SERVER_FULL :{WHITE}The server is full
STR_NETWORK_ERROR_SERVER_BANNED :{WHITE}You are banned from this server
STR_NETWORK_ERROR_KICKED :{WHITE}You were kicked out of the game
+STR_NETWORK_ERROR_KICK_MESSAGE :{WHITE}Reason: {RAW_STRING}
STR_NETWORK_ERROR_CHEATER :{WHITE}Cheating is not allowed on this server
STR_NETWORK_ERROR_TOO_MANY_COMMANDS :{WHITE}You were sending too many commands to the server
STR_NETWORK_ERROR_TIMEOUT_PASSWORD :{WHITE}You took too long to enter the password
@@ -2465,6 +2466,7 @@ STR_NETWORK_MESSAGE_MONEY_GIVEN :*** {RAW_STRING
STR_NETWORK_MESSAGE_MONEY_GIVE_SRC_DESCRIPTION :{RAW_STRING} ({COMPANY})
STR_NETWORK_MESSAGE_SERVER_SHUTDOWN :{WHITE}The server closed the session
STR_NETWORK_MESSAGE_SERVER_REBOOT :{WHITE}The server is restarting...{}Please wait...
+STR_NETWORK_MESSAGE_KICKED :*** {RAW_STRING} was kicked. Reason: ({RAW_STRING})
# Content downloading window
STR_CONTENT_TITLE :{WHITE}Content downloading
diff --git a/src/lang/korean.txt b/src/lang/korean.txt
index 7640d42b15..cc1509da7d 100644
--- a/src/lang/korean.txt
+++ b/src/lang/korean.txt
@@ -799,8 +799,8 @@ STR_NEWS_FIRST_SHIP_ARRIVAL :{BIG_FONT}{BLAC
STR_NEWS_FIRST_AIRCRAFT_ARRIVAL :{BIG_FONT}{BLACK}시민들이 축하하고 있습니다 . . .{}{STATION}에 처음으로 항공기가 도착했습니다!
STR_NEWS_TRAIN_CRASH :{BIG_FONT}{BLACK}열차 충돌 사고!{}충돌로 인한 폭발로 {COMMA}명의 사망자가 발생하였습니다!
-STR_NEWS_ROAD_VEHICLE_CRASH_DRIVER :{BIG_FONT}{BLACK}차량 충돌!{}열차와의 충돌로 인해 운전자가 사망했습니다!
-STR_NEWS_ROAD_VEHICLE_CRASH :{BIG_FONT}{BLACK}차량 충돌!{}열차와의 충돌로 {COMMA}명이 사망했습니다!
+STR_NEWS_ROAD_VEHICLE_CRASH_DRIVER :{BIG_FONT}{BLACK}차량 충돌!{}열차 충돌로 운전자가 사망했습니다!
+STR_NEWS_ROAD_VEHICLE_CRASH :{BIG_FONT}{BLACK}차량 충돌!{}열차 충돌로 {COMMA}명이 사망했습니다!
STR_NEWS_AIRCRAFT_CRASH :{BIG_FONT}{BLACK}항공기 충돌사고 발생!{}{COMMA}명이 {STATION}공항에서 사망하였습니다!
STR_NEWS_PLANE_CRASH_OUT_OF_FUEL :{BIG_FONT}{BLACK}항공기 추락사고 발생!{}연료 부족으로 인하여 {COMMA}명이 사망하였습니다!
@@ -1739,7 +1739,7 @@ STR_CONFIG_SETTING_TOWN_LAYOUT_BETTER_ROADS :개선된 도
STR_CONFIG_SETTING_TOWN_LAYOUT_2X2_GRID :2x2 칸
STR_CONFIG_SETTING_TOWN_LAYOUT_3X3_GRID :3x3 칸
STR_CONFIG_SETTING_TOWN_LAYOUT_RANDOM :무작위
-STR_CONFIG_SETTING_ALLOW_TOWN_ROADS :도시 스스로의 도로 건설 허용: {STRING}
+STR_CONFIG_SETTING_ALLOW_TOWN_ROADS :도시 스스로 도로를 건설하는 것을 허용: {STRING}
STR_CONFIG_SETTING_ALLOW_TOWN_ROADS_HELPTEXT :도시가 성장하기 위해 도로를 건설할 수 있도록 허용합니다. 도시 당국이 스스로 도로를 만들지 못하도록 하려면 이 설정을 끄십시오.
STR_CONFIG_SETTING_ALLOW_TOWN_LEVEL_CROSSINGS :도시가 회사 소유의 선로에 건널목을 만드는 것을 허용: {STRING}
STR_CONFIG_SETTING_ALLOW_TOWN_LEVEL_CROSSINGS_HELPTEXT :도시가 회사 소유의 선로에 건널목을 건설할 수 있도록 허용합니다.
@@ -2369,7 +2369,7 @@ STR_COMPANY_PASSWORD_MAKE_DEFAULT_TOOLTIP :{BLACK}이 회
STR_COMPANY_VIEW_JOIN :{BLACK}참여
STR_COMPANY_VIEW_JOIN_TOOLTIP :{BLACK}이 회사로 참가해서 플레이합니다
STR_COMPANY_VIEW_PASSWORD :{BLACK}암호
-STR_COMPANY_VIEW_PASSWORD_TOOLTIP :{BLACK}다른 참가자가 이 회사로의 플레이를 하지 못하도록 암호로 보호합니다
+STR_COMPANY_VIEW_PASSWORD_TOOLTIP :{BLACK}다른 참가자가 이 회사에 참여하여 플레이하지 못 하도록 암호로 보호합니다
STR_COMPANY_VIEW_SET_PASSWORD :{BLACK}회사 암호 설정
# Network chat
@@ -2402,6 +2402,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}비밀번호 입력 시간을 초과하였습니다
@@ -2465,6 +2466,7 @@ STR_NETWORK_MESSAGE_MONEY_GIVEN :*** {STRING}
STR_NETWORK_MESSAGE_MONEY_GIVE_SRC_DESCRIPTION :{STRING} ({COMPANY})
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}콘텐츠 다운로드
diff --git a/src/lang/russian.txt b/src/lang/russian.txt
index e0eb067b5e..4d9a2903fc 100644
--- a/src/lang/russian.txt
+++ b/src/lang/russian.txt
@@ -2320,6 +2320,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}Вы не успели ввести пароль
@@ -2379,6 +2380,7 @@ STR_NETWORK_MESSAGE_GIVE_MONEY :*** {STRING} п
STR_NETWORK_MESSAGE_GAVE_MONEY_AWAY :*** Вы передали {1:STRING} {2:CURRENCY_LONG}
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}Загрузка контента
diff --git a/src/music/fluidsynth.cpp b/src/music/fluidsynth.cpp
index 89cb7273f5..64abe0e538 100644
--- a/src/music/fluidsynth.cpp
+++ b/src/music/fluidsynth.cpp
@@ -15,11 +15,13 @@
#include "midifile.hpp"
#include
#include "../mixer.h"
+#include
static struct {
fluid_settings_t* settings; ///< FluidSynth settings handle
fluid_synth_t* synth; ///< FluidSynth synthesizer handle
fluid_player_t* player; ///< FluidSynth MIDI player handle
+ std::mutex synth_mutex; ///< Guard mutex for synth access
} _midi; ///< Metadata about the midi we're playing.
/** Factory for the FluidSynth driver. */
@@ -42,12 +44,16 @@ static const char *default_sf[] = {
static void RenderMusicStream(int16 *buffer, size_t samples)
{
- if (!_midi.synth || !_midi.player) return;
+ std::unique_lock lock{ _midi.synth_mutex, std::try_to_lock };
+
+ if (!lock.owns_lock() || !_midi.synth || !_midi.player) return;
fluid_synth_write_s16(_midi.synth, samples, buffer, 0, 2, buffer, 1, 2);
}
const char *MusicDriver_FluidSynth::Start(const char * const *param)
{
+ std::lock_guard lock{ _midi.synth_mutex };
+
const char *sfont_name = GetDriverParam(param, "soundfont");
int sfont_id;
@@ -59,6 +65,11 @@ const char *MusicDriver_FluidSynth::Start(const char * const *param)
/* Don't try to lock sample data in memory, OTTD usually does not run with privileges allowing that */
fluid_settings_setint(_midi.settings, "synth.lock-memory", 0);
+ /* Install the music render routine and set up the samplerate */
+ uint32 samplerate = MxSetMusicSource(RenderMusicStream);
+ fluid_settings_setnum(_midi.settings, "synth.sample-rate", samplerate);
+ DEBUG(driver, 1, "Fluidsynth: samplerate %.0f", (float)samplerate);
+
/* Create the synthesizer. */
_midi.synth = new_fluid_synth(_midi.settings);
if (!_midi.synth) return "Could not open synth";
@@ -81,19 +92,23 @@ const char *MusicDriver_FluidSynth::Start(const char * const *param)
_midi.player = nullptr;
- uint32 samplerate = MxSetMusicSource(RenderMusicStream);
- fluid_synth_set_sample_rate(_midi.synth, samplerate);
- DEBUG(driver, 1, "Fluidsynth: samplerate %.0f", (float)samplerate);
-
return nullptr;
}
void MusicDriver_FluidSynth::Stop()
{
MxSetMusicSource(nullptr);
- this->StopSong();
- delete_fluid_synth(_midi.synth);
- delete_fluid_settings(_midi.settings);
+
+ std::lock_guard lock{ _midi.synth_mutex };
+
+ if (_midi.player != nullptr) delete_fluid_player(_midi.player);
+ _midi.player = nullptr;
+
+ if (_midi.synth != nullptr) delete_fluid_synth(_midi.synth);
+ _midi.synth = nullptr;
+
+ if (_midi.settings != nullptr) delete_fluid_settings(_midi.settings);
+ _midi.settings = nullptr;
}
void MusicDriver_FluidSynth::PlaySong(const MusicSongInfo &song)
@@ -106,6 +121,8 @@ void MusicDriver_FluidSynth::PlaySong(const MusicSongInfo &song)
return;
}
+ std::lock_guard lock{ _midi.synth_mutex };
+
_midi.player = new_fluid_player(_midi.synth);
if (!_midi.player) {
DEBUG(driver, 0, "Could not create midi player");
@@ -128,6 +145,8 @@ void MusicDriver_FluidSynth::PlaySong(const MusicSongInfo &song)
void MusicDriver_FluidSynth::StopSong()
{
+ std::lock_guard lock{ _midi.synth_mutex };
+
if (!_midi.player) return;
fluid_player_stop(_midi.player);
@@ -142,6 +161,7 @@ void MusicDriver_FluidSynth::StopSong()
bool MusicDriver_FluidSynth::IsSongPlaying()
{
+ std::lock_guard lock{ _midi.synth_mutex };
if (!_midi.player) return false;
return fluid_player_get_status(_midi.player) == FLUID_PLAYER_PLAYING;
@@ -149,6 +169,9 @@ bool MusicDriver_FluidSynth::IsSongPlaying()
void MusicDriver_FluidSynth::SetVolume(byte vol)
{
+ std::lock_guard lock{ _midi.synth_mutex };
+ if (_midi.settings == nullptr) return;
+
/* Allowed range of synth.gain is 0.0 to 10.0 */
/* fluidsynth's default gain is 0.2, so use this as "full
* volume". Set gain using OpenTTD's volume, as a number between 0
diff --git a/src/network/network.cpp b/src/network/network.cpp
index e28e38f48d..6e5f34add9 100644
--- a/src/network/network.cpp
+++ b/src/network/network.cpp
@@ -267,6 +267,7 @@ void NetworkTextMessage(NetworkAction action, TextColour colour, bool self_send,
case NETWORK_ACTION_CHAT_COMPANY: strid = self_send ? STR_NETWORK_CHAT_TO_COMPANY : STR_NETWORK_CHAT_COMPANY; break;
case NETWORK_ACTION_CHAT_CLIENT: strid = self_send ? STR_NETWORK_CHAT_TO_CLIENT : STR_NETWORK_CHAT_CLIENT; break;
+ case NETWORK_ACTION_KICKED: strid = STR_NETWORK_MESSAGE_KICKED; break;
default: strid = STR_NETWORK_CHAT_ALL; break;
}
diff --git a/src/network/network_client.cpp b/src/network/network_client.cpp
index 26dbd347ce..1700954850 100644
--- a/src/network/network_client.cpp
+++ b/src/network/network_client.cpp
@@ -776,8 +776,15 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_ERROR(Packet *p
StringID err = STR_NETWORK_ERROR_LOSTCONNECTION;
if (error < (ptrdiff_t)lengthof(network_error_strings)) err = network_error_strings[error];
-
- ShowErrorMessage(err, INVALID_STRING_ID, WL_CRITICAL);
+ /* In case of kicking a client, we assume there is a kick message in the packet if we can read one byte */
+ if (error == NETWORK_ERROR_KICKED && p->CanReadFromPacket(1)) {
+ char kick_msg[255];
+ p->Recv_string(kick_msg, sizeof(kick_msg));
+ SetDParamStr(0, kick_msg);
+ ShowErrorMessage(err, STR_NETWORK_ERROR_KICK_MESSAGE, WL_CRITICAL);
+ } else {
+ ShowErrorMessage(err, INVALID_STRING_ID, WL_CRITICAL);
+ }
/* Perform an emergency save if we had already entered the game */
if (this->status == STATUS_ACTIVE) ClientNetworkEmergencySave();
diff --git a/src/network/network_func.h b/src/network/network_func.h
index 13ba6d4a95..a075a2c3e4 100644
--- a/src/network/network_func.h
+++ b/src/network/network_func.h
@@ -76,9 +76,9 @@ void NetworkServerDoMove(ClientID client_id, CompanyID company_id);
void NetworkServerSendRcon(ClientID client_id, TextColour colour_code, const char *string);
void NetworkServerSendChat(NetworkAction action, DestType type, int dest, const char *msg, ClientID from_id, NetworkTextMessageData data = NetworkTextMessageData(), bool from_admin = false);
-void NetworkServerKickClient(ClientID client_id);
-uint NetworkServerKickOrBanIP(ClientID client_id, bool ban);
-uint NetworkServerKickOrBanIP(const char *ip, bool ban);
+void NetworkServerKickClient(ClientID client_id, const char *reason);
+uint NetworkServerKickOrBanIP(ClientID client_id, bool ban, const char *reason);
+uint NetworkServerKickOrBanIP(const char *ip, bool ban, const char *reason);
void NetworkInitChatMessage();
void CDECL NetworkAddChatMessage(TextColour colour, uint duration, const char *message, ...) WARN_FORMAT(3, 4);
diff --git a/src/network/network_gui.cpp b/src/network/network_gui.cpp
index ee29883572..0494261b25 100644
--- a/src/network/network_gui.cpp
+++ b/src/network/network_gui.cpp
@@ -1688,12 +1688,12 @@ static WindowDesc _client_list_popup_desc(
/* Here we start to define the options out of the menu */
static void ClientList_Kick(const NetworkClientInfo *ci)
{
- NetworkServerKickClient(ci->client_id);
+ NetworkServerKickClient(ci->client_id, nullptr);
}
static void ClientList_Ban(const NetworkClientInfo *ci)
{
- NetworkServerKickOrBanIP(ci->client_id, true);
+ NetworkServerKickOrBanIP(ci->client_id, true, nullptr);
}
static void ClientList_SpeakToClient(const NetworkClientInfo *ci)
diff --git a/src/network/network_server.cpp b/src/network/network_server.cpp
index 147b922479..65a735d2c8 100644
--- a/src/network/network_server.cpp
+++ b/src/network/network_server.cpp
@@ -422,13 +422,15 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::SendCompanyInfo()
/**
* Send an error to the client, and close its connection.
* @param error The error to disconnect for.
+ * @param reason In case of kicking a client, specifies the reason for kicking the client.
*/
-NetworkRecvStatus ServerNetworkGameSocketHandler::SendError(NetworkErrorCode error)
+NetworkRecvStatus ServerNetworkGameSocketHandler::SendError(NetworkErrorCode error, const char *reason)
{
char str[100];
Packet *p = new Packet(PACKET_SERVER_ERROR);
p->Send_uint8(error);
+ if (reason != nullptr) p->Send_string(reason);
this->SendPacket(p);
StringID strid = GetNetworkErrorMsg(error);
@@ -442,7 +444,11 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::SendError(NetworkErrorCode err
DEBUG(net, 1, "'%s' made an error and has been disconnected. Reason: '%s'", client_name, str);
- NetworkTextMessage(NETWORK_ACTION_LEAVE, CC_DEFAULT, false, client_name, nullptr, strid);
+ if (error == NETWORK_ERROR_KICKED && reason != nullptr) {
+ NetworkTextMessage(NETWORK_ACTION_KICKED, CC_DEFAULT, false, client_name, reason, strid);
+ } else {
+ NetworkTextMessage(NETWORK_ACTION_LEAVE, CC_DEFAULT, false, client_name, nullptr, strid);
+ }
for (NetworkClientSocket *new_cs : NetworkClientSocket::Iterate()) {
if (new_cs->status > STATUS_AUTHORIZED && new_cs != this) {
@@ -2139,29 +2145,32 @@ void NetworkServerSendRcon(ClientID client_id, TextColour colour_code, const cha
/**
* Kick a single client.
* @param client_id The client to kick.
+ * @param reason In case of kicking a client, specifies the reason for kicking the client.
*/
-void NetworkServerKickClient(ClientID client_id)
+void NetworkServerKickClient(ClientID client_id, const char *reason)
{
if (client_id == CLIENT_ID_SERVER) return;
- NetworkClientSocket::GetByClientID(client_id)->SendError(NETWORK_ERROR_KICKED);
+ NetworkClientSocket::GetByClientID(client_id)->SendError(NETWORK_ERROR_KICKED, reason);
}
/**
* Ban, or kick, everyone joined from the given client's IP.
* @param client_id The client to check for.
* @param ban Whether to ban or kick.
+ * @param reason In case of kicking a client, specifies the reason for kicking the client.
*/
-uint NetworkServerKickOrBanIP(ClientID client_id, bool ban)
+uint NetworkServerKickOrBanIP(ClientID client_id, bool ban, const char *reason)
{
- return NetworkServerKickOrBanIP(NetworkClientSocket::GetByClientID(client_id)->GetClientIP(), ban);
+ return NetworkServerKickOrBanIP(NetworkClientSocket::GetByClientID(client_id)->GetClientIP(), ban, reason);
}
/**
* Kick or ban someone based on an IP address.
* @param ip The IP address/range to ban/kick.
* @param ban Whether to ban or just kick.
+ * @param reason In case of kicking a client, specifies the reason for kicking the client.
*/
-uint NetworkServerKickOrBanIP(const char *ip, bool ban)
+uint NetworkServerKickOrBanIP(const char *ip, bool ban, const char *reason)
{
/* Add address to ban-list */
if (ban) {
@@ -2177,11 +2186,16 @@ uint NetworkServerKickOrBanIP(const char *ip, bool ban)
uint n = 0;
- /* There can be multiple clients with the same IP, kick them all */
+ /* There can be multiple clients with the same IP, kick them all but don't kill the server,
+ * or the client doing the rcon. The latter can't be kicked because kicking frees closes
+ * and subsequently free the connection related instances, which we would be reading from
+ * and writing to after returning. So we would read or write data from freed memory up till
+ * the segfault triggers. */
for (NetworkClientSocket *cs : NetworkClientSocket::Iterate()) {
if (cs->client_id == CLIENT_ID_SERVER) continue;
+ if (cs->client_id == _redirect_console_to_client) continue;
if (cs->client_address.IsInNetmask(ip)) {
- NetworkServerKickClient(cs->client_id);
+ NetworkServerKickClient(cs->client_id, reason);
n++;
}
}
diff --git a/src/network/network_server.h b/src/network/network_server.h
index 2ade4bf231..209a4684a9 100644
--- a/src/network/network_server.h
+++ b/src/network/network_server.h
@@ -98,7 +98,7 @@ public:
NetworkRecvStatus SendMove(ClientID client_id, CompanyID company_id);
NetworkRecvStatus SendClientInfo(NetworkClientInfo *ci);
- NetworkRecvStatus SendError(NetworkErrorCode error);
+ NetworkRecvStatus SendError(NetworkErrorCode error, const char *reason = nullptr);
NetworkRecvStatus SendDesyncLog(const std::string &log);
NetworkRecvStatus SendChat(NetworkAction action, ClientID client_id, bool self_send, const char *msg, NetworkTextMessageData data);
NetworkRecvStatus SendJoin(ClientID client_id);
diff --git a/src/network/network_type.h b/src/network/network_type.h
index 3ea700e784..d176d4a486 100644
--- a/src/network/network_type.h
+++ b/src/network/network_type.h
@@ -86,6 +86,7 @@ enum DestType {
enum NetworkAction {
NETWORK_ACTION_JOIN,
NETWORK_ACTION_LEAVE,
+ NETWORK_ACTION_KICKED,
NETWORK_ACTION_SERVER_MESSAGE,
NETWORK_ACTION_CHAT,
NETWORK_ACTION_CHAT_COMPANY,
diff --git a/src/newgrf_station.cpp b/src/newgrf_station.cpp
index cfa4ce0018..6e17932ac8 100644
--- a/src/newgrf_station.cpp
+++ b/src/newgrf_station.cpp
@@ -368,6 +368,16 @@ TownScopeResolver *StationResolverObject::GetTown()
return res;
}
+ case 0x6A: { // GRFID of nearby station tiles
+ TileIndex nearby_tile = GetNearbyTile(parameter, this->tile);
+
+ if (!HasStationTileRail(nearby_tile)) return 0xFFFFFFFF;
+ if (!IsCustomStationSpecIndex(nearby_tile)) return 0;
+
+ const StationSpecList ssl = BaseStation::GetByTile(nearby_tile)->speclist[GetCustomStationSpecIndex(nearby_tile)];
+ return ssl.grfid;
+ }
+
/* General station variables */
case 0x82: return 50;
case 0x84: return this->st->string_id;
diff --git a/src/os/windows/ottdres.rc.in b/src/os/windows/ottdres.rc.in
index f51023843f..8cce72cea8 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,10,0,!!ISODATE!!
- PRODUCTVERSION 1,10,0,!!ISODATE!!
+ FILEVERSION 1,11,0,!!ISODATE!!
+ PRODUCTVERSION 1,11,0,!!ISODATE!!
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L
diff --git a/src/pathfinder/pathfinder_type.h b/src/pathfinder/pathfinder_type.h
index 29b02cde75..79eeb8ec73 100644
--- a/src/pathfinder/pathfinder_type.h
+++ b/src/pathfinder/pathfinder_type.h
@@ -44,6 +44,9 @@ static const int YAPF_SHIP_PATH_CACHE_LENGTH = 32;
/** Maximum segments of road vehicle path cache */
static const int YAPF_ROADVEH_PATH_CACHE_SEGMENTS = 16;
+/** Distance from destination road stops to not cache any further */
+static const int YAPF_ROADVEH_PATH_CACHE_DESTINATION_LIMIT = 8;
+
/**
* Helper container to find a depot
*/
diff --git a/src/pathfinder/yapf/yapf_road.cpp b/src/pathfinder/yapf/yapf_road.cpp
index 99ebc92235..349c75264a 100644
--- a/src/pathfinder/yapf/yapf_road.cpp
+++ b/src/pathfinder/yapf/yapf_road.cpp
@@ -429,9 +429,9 @@ public:
const RoadStop *stop = st->GetPrimaryRoadStop(v);
if (stop != nullptr && (IsDriveThroughStopTile(stop->xy) || stop->GetNextRoadStop(v) != nullptr)) {
/* Destination station has at least 2 usable road stops, or first is a drive-through stop,
- * trim end of path cache within 8 tiles of road stop tile area */
+ * trim end of path cache within a number of tiles of road stop tile area */
TileArea non_cached_area = v->IsBus() ? st->bus_station : st->truck_station;
- non_cached_area.Expand(8);
+ non_cached_area.Expand(YAPF_ROADVEH_PATH_CACHE_DESTINATION_LIMIT);
while (!path_cache.empty() && non_cached_area.Contains(path_cache.tile.back())) {
path_cache.td.pop_back();
path_cache.tile.pop_back();
diff --git a/src/rev.cpp.in b/src/rev.cpp.in
index 836c32231d..50c7e06e12 100644
--- a/src/rev.cpp.in
+++ b/src/rev.cpp.in
@@ -91,4 +91,4 @@ const byte _openttd_revision_tagged = !!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 | 10 << 24 | 0 << 20 | !!ISSTABLETAG!! << 19 | 28004;
+const uint32 _openttd_newgrf_version = 1 << 28 | 11 << 24 | 0 << 20 | !!ISSTABLETAG!! << 19 | 28004;
diff --git a/src/saveload/saveload.h b/src/saveload/saveload.h
index c57f403741..a97d489272 100644
--- a/src/saveload/saveload.h
+++ b/src/saveload/saveload.h
@@ -305,7 +305,7 @@ enum SaveLoadVersion : uint16 {
SLV_SCRIPT_MEMLIMIT, ///< 215 PR#7516 Limit on AI/GS memory consumption.
SLV_MULTITILE_DOCKS, ///< 216 PR#7380 Multiple docks per station.
SLV_TRADING_AGE, ///< 217 PR#7780 Configurable company trading age.
- SLV_ENDING_YEAR, ///< 218 PR#7747 Configurable ending year.
+ SLV_ENDING_YEAR, ///< 218 PR#7747 v1.10 Configurable ending year.
SL_MAX_VERSION, ///< Highest possible saveload version
@@ -506,7 +506,8 @@ enum VarTypes {
SLF_NO_NETWORK_SYNC = 1 << 10, ///< do not synchronize over network (but it is saved if SLF_NOT_IN_SAVE is not set)
SLF_ALLOW_CONTROL = 1 << 11, ///< allow control codes in the strings
SLF_ALLOW_NEWLINE = 1 << 12, ///< allow new lines in the strings
- /* 3 more possible flags */
+ SLF_HEX = 1 << 13, ///< print numbers as hex in the config file (only useful for unsigned)
+ /* 2 more possible flags */
};
typedef uint32 VarType;
diff --git a/src/script/api/ai_changelog.hpp b/src/script/api/ai_changelog.hpp
index 9eea9ab152..bcb7df67f2 100644
--- a/src/script/api/ai_changelog.hpp
+++ b/src/script/api/ai_changelog.hpp
@@ -13,10 +13,12 @@
* functions may still be available if you return an older API version
* in GetAPIVersion() in info.nut.
*
- * \b 1.10.0
+ * \b 1.11.0
*
* This version is not yet released. The following changes are not set in stone yet.
*
+ * \b 1.10.0
+ *
* API additions:
* \li AIGroup::SetPrimaryColour
* \li AIGroup::SetSecondaryColour
diff --git a/src/script/api/game_changelog.hpp b/src/script/api/game_changelog.hpp
index 235dcee986..df8c663a03 100644
--- a/src/script/api/game_changelog.hpp
+++ b/src/script/api/game_changelog.hpp
@@ -13,10 +13,12 @@
* functions may still be available if you return an older API version
* in GetAPIVersion() in info.nut.
*
- * \b 1.10.0
+ * \b 1.11.0
*
* This version is not yet released. The following changes are not set in stone yet.
*
+ * \b 1.10.0
+ *
* API additions:
* \li GSVehicle::BuildVehicleWithRefit
* \li GSVehicle::GetBuildWithRefitCapacity
diff --git a/src/settings.cpp b/src/settings.cpp
index d587585d26..7c3a400103 100644
--- a/src/settings.cpp
+++ b/src/settings.cpp
@@ -22,6 +22,7 @@
*/
#include "stdafx.h"
+#include
#include "currency.h"
#include "screenshot.h"
#include "network/network.h"
@@ -176,7 +177,8 @@ static size_t LookupManyOfMany(const char *many, const char *str)
* @param maxitems the maximum number of elements the integerlist-array has
* @return returns the number of items found, or -1 on an error
*/
-static int ParseIntList(const char *p, int *items, int maxitems)
+template
+static int ParseIntList(const char *p, T *items, int maxitems)
{
int n = 0; // number of items read so far
bool comma = false; // do we accept comma?
@@ -196,9 +198,9 @@ static int ParseIntList(const char *p, int *items, int maxitems)
default: {
if (n == maxitems) return -1; // we don't accept that many numbers
char *end;
- long v = strtol(p, &end, 0);
+ unsigned long v = strtoul(p, &end, 0);
if (p == end) return -1; // invalid character (not a number)
- if (sizeof(int) < sizeof(long)) v = ClampToI32(v);
+ if (sizeof(T) < sizeof(v)) v = Clamp(v, std::numeric_limits::min(), std::numeric_limits::max());
items[n++] = v;
p = end; // first non-number
comma = true; // we accept comma now
@@ -224,7 +226,7 @@ static int ParseIntList(const char *p, int *items, int maxitems)
*/
static bool LoadIntList(const char *str, void *array, int nelems, VarType type)
{
- int items[64];
+ unsigned long items[64];
int i, nitems;
if (str == nullptr) {
@@ -273,7 +275,7 @@ static void MakeIntList(char *buf, const char *last, const void *array, int nele
const byte *p = (const byte *)array;
for (i = 0; i != nelems; i++) {
- switch (type) {
+ switch (GetVarMemType(type)) {
case SLE_VAR_BL:
case SLE_VAR_I8: v = *(const int8 *)p; p += 1; break;
case SLE_VAR_U8: v = *(const uint8 *)p; p += 1; break;
@@ -283,7 +285,13 @@ static void MakeIntList(char *buf, const char *last, const void *array, int nele
case SLE_VAR_U32: v = *(const uint32 *)p; p += 4; break;
default: NOT_REACHED();
}
- buf += seprintf(buf, last, (i == 0) ? "%d" : ",%d", v);
+ if (IsSignedVarMemType(type)) {
+ buf += seprintf(buf, last, (i == 0) ? "%d" : ",%d", v);
+ } else if (type & SLF_HEX) {
+ buf += seprintf(buf, last, (i == 0) ? "0x%X" : ",0x%X", v);
+ } else {
+ buf += seprintf(buf, last, (i == 0) ? "%u" : ",%u", v);
+ }
}
}
@@ -696,7 +704,7 @@ static void IniSaveSettings(IniFile *ini, const SettingDesc *sd, const char *grp
switch (sdb->cmd) {
case SDT_BOOLX: strecpy(buf, (i != 0) ? "true" : "false", lastof(buf)); break;
- case SDT_NUMX: seprintf(buf, lastof(buf), IsSignedVarMemType(sld->conv) ? "%d" : "%u", i); break;
+ case SDT_NUMX: seprintf(buf, lastof(buf), IsSignedVarMemType(sld->conv) ? "%d" : (sld->conv & SLF_HEX) ? "%X" : "%u", i); break;
case SDT_ONEOFMANY: MakeOneOfMany(buf, lastof(buf), sdb->many, i); break;
case SDT_MANYOFMANY: MakeManyOfMany(buf, lastof(buf), sdb->many, i); break;
default: NOT_REACHED();
@@ -724,7 +732,7 @@ static void IniSaveSettings(IniFile *ini, const SettingDesc *sd, const char *grp
break;
case SDT_INTLIST:
- MakeIntList(buf, lastof(buf), ptr, sld->length, GetVarMemType(sld->conv));
+ MakeIntList(buf, lastof(buf), ptr, sld->length, sld->conv);
break;
default: NOT_REACHED();
@@ -1703,7 +1711,7 @@ static GRFConfig *GRFLoadConfig(IniFile *ini, const char *grpname, bool is_stati
/* Parse parameters */
if (!StrEmpty(item->value)) {
- int count = ParseIntList(item->value, (int*)c->param, lengthof(c->param));
+ int count = ParseIntList(item->value, c->param, lengthof(c->param));
if (count < 0) {
SetDParamStr(0, filename);
ShowErrorMessage(STR_CONFIG_ERROR, STR_CONFIG_ERROR_ARRAY, WL_CRITICAL);
diff --git a/src/station_cmd.cpp b/src/station_cmd.cpp
index 675d955a97..275c1958f0 100644
--- a/src/station_cmd.cpp
+++ b/src/station_cmd.cpp
@@ -4081,7 +4081,7 @@ void StationMonthlyLoop()
void ModifyStationRatingAround(TileIndex tile, Owner owner, int amount, uint radius)
{
ForAllStationsRadius(tile, radius, [&](Station *st) {
- if (st->owner == owner) {
+ if (st->owner == owner && DistanceManhattan(tile, st->xy) <= radius) {
for (CargoID i = 0; i < NUM_CARGO; i++) {
GoodsEntry *ge = &st->goods[i];
diff --git a/src/table/company_settings.ini b/src/table/company_settings.ini
index 29b5eef1eb..30ff023702 100644
--- a/src/table/company_settings.ini
+++ b/src/table/company_settings.ini
@@ -45,6 +45,7 @@ var = engine_renew
def = false
str = STR_CONFIG_SETTING_AUTORENEW_VEHICLE
strhelp = STR_CONFIG_SETTING_AUTORENEW_VEHICLE_HELPTEXT
+cat = SC_BASIC
[SDT_VAR]
base = CompanySettings
diff --git a/src/table/newgrf_debug_data.h b/src/table/newgrf_debug_data.h
index 1362857b3a..c555e7e0d3 100644
--- a/src/table/newgrf_debug_data.h
+++ b/src/table/newgrf_debug_data.h
@@ -176,6 +176,7 @@ static const NIVariable _niv_stations[] = {
NIV(0x67, "land info of nearby tiles"),
NIV(0x68, "station info of nearby tiles"),
NIV(0x69, "information about cargo accepted in the past"),
+ NIV(0x6A, "GRFID of nearby station tiles"),
NIV_END()
};