OpenTTD Source  20241121-master-g67a0fccfad
console_cmds.cpp
Go to the documentation of this file.
1 /*
2  * This file is part of OpenTTD.
3  * 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.
4  * 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.
5  * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
6  */
7 
10 #include "stdafx.h"
11 #include "console_internal.h"
12 #include "debug.h"
13 #include "engine_func.h"
14 #include "landscape.h"
15 #include "saveload/saveload.h"
16 #include "network/core/network_game_info.h"
17 #include "network/network.h"
18 #include "network/network_func.h"
19 #include "network/network_base.h"
20 #include "network/network_admin.h"
21 #include "network/network_client.h"
22 #include "command_func.h"
23 #include "settings_func.h"
24 #include "fios.h"
25 #include "fileio_func.h"
26 #include "fontcache.h"
27 #include "screenshot.h"
28 #include "genworld.h"
29 #include "strings_func.h"
30 #include "viewport_func.h"
31 #include "window_func.h"
32 #include "timer/timer.h"
33 #include "company_func.h"
34 #include "gamelog.h"
35 #include "ai/ai.hpp"
36 #include "ai/ai_config.hpp"
37 #include "newgrf.h"
38 #include "newgrf_profiling.h"
39 #include "console_func.h"
40 #include "engine_base.h"
41 #include "road.h"
42 #include "rail.h"
43 #include "game/game.hpp"
44 #include "table/strings.h"
45 #include "3rdparty/fmt/chrono.h"
46 #include "company_cmd.h"
47 #include "misc_cmd.h"
48 
49 #include <sstream>
50 
51 #include "safeguards.h"
52 
53 /* scriptfile handling */
54 static uint _script_current_depth;
55 
56 /* Scheduled execution handling. */
57 static std::string _scheduled_monthly_script;
58 
60 static IntervalTimer<TimerGameCalendar> _scheduled_monthly_timer = {{TimerGameCalendar::MONTH, TimerGameCalendar::Priority::NONE}, [](auto) {
61  if (_scheduled_monthly_script.empty()) {
62  return;
63  }
64 
65  /* Clear the schedule before rather than after the script to allow the script to itself call
66  * schedule without it getting immediately cleared. */
67  const std::string filename = _scheduled_monthly_script;
69 
70  IConsolePrint(CC_DEFAULT, "Executing scheduled script file '{}'...", filename);
71  IConsoleCmdExec(std::string("exec") + " " + filename);
72 }};
73 
75 class ConsoleFileList : public FileList {
76 public:
78  {
79  }
80 
83  {
84  this->clear();
85  this->file_list_valid = false;
86  }
87 
92  void ValidateFileList(bool force_reload = false)
93  {
94  if (force_reload || !this->file_list_valid) {
95  this->BuildFileList(this->abstract_filetype, SLO_LOAD, this->show_dirs);
96  this->file_list_valid = true;
97  }
98  }
99 
101  bool show_dirs;
102  bool file_list_valid = false;
103 };
104 
108 
109 /* console command defines */
110 #define DEF_CONSOLE_CMD(function) static bool function([[maybe_unused]] uint8_t argc, [[maybe_unused]] char *argv[])
111 #define DEF_CONSOLE_HOOK(function) static ConsoleHookResult function(bool echo)
112 
113 
114 /****************
115  * command hooks
116  ****************/
117 
122 static inline bool NetworkAvailable(bool echo)
123 {
124  if (!_network_available) {
125  if (echo) IConsolePrint(CC_ERROR, "You cannot use this command because there is no network available.");
126  return false;
127  }
128  return true;
129 }
130 
135 DEF_CONSOLE_HOOK(ConHookServerOnly)
136 {
137  if (!NetworkAvailable(echo)) return CHR_DISALLOW;
138 
139  if (!_network_server) {
140  if (echo) IConsolePrint(CC_ERROR, "This command is only available to a network server.");
141  return CHR_DISALLOW;
142  }
143  return CHR_ALLOW;
144 }
145 
150 DEF_CONSOLE_HOOK(ConHookClientOnly)
151 {
152  if (!NetworkAvailable(echo)) return CHR_DISALLOW;
153 
154  if (_network_server) {
155  if (echo) IConsolePrint(CC_ERROR, "This command is not available to a network server.");
156  return CHR_DISALLOW;
157  }
158  return CHR_ALLOW;
159 }
160 
165 DEF_CONSOLE_HOOK(ConHookNeedNetwork)
166 {
167  if (!NetworkAvailable(echo)) return CHR_DISALLOW;
168 
170  if (echo) IConsolePrint(CC_ERROR, "Not connected. This command is only available in multiplayer.");
171  return CHR_DISALLOW;
172  }
173  return CHR_ALLOW;
174 }
175 
180 DEF_CONSOLE_HOOK(ConHookNeedNonDedicatedNetwork)
181 {
182  if (!NetworkAvailable(echo)) return CHR_DISALLOW;
183 
184  if (_network_dedicated) {
185  if (echo) IConsolePrint(CC_ERROR, "This command is not available to a dedicated network server.");
186  return CHR_DISALLOW;
187  }
188  return CHR_ALLOW;
189 }
190 
195 DEF_CONSOLE_HOOK(ConHookNoNetwork)
196 {
197  if (_networking) {
198  if (echo) IConsolePrint(CC_ERROR, "This command is forbidden in multiplayer.");
199  return CHR_DISALLOW;
200  }
201  return CHR_ALLOW;
202 }
203 
208 DEF_CONSOLE_HOOK(ConHookServerOrNoNetwork)
209 {
210  if (_networking && !_network_server) {
211  if (echo) IConsolePrint(CC_ERROR, "This command is only available to a network server.");
212  return CHR_DISALLOW;
213  }
214  return CHR_ALLOW;
215 }
216 
217 DEF_CONSOLE_HOOK(ConHookNewGRFDeveloperTool)
218 {
220  if (_game_mode == GM_MENU) {
221  if (echo) IConsolePrint(CC_ERROR, "This command is only available in-game and in the editor.");
222  return CHR_DISALLOW;
223  }
224  return ConHookNoNetwork(echo);
225  }
226  return CHR_HIDE;
227 }
228 
233 DEF_CONSOLE_CMD(ConResetEngines)
234 {
235  if (argc == 0) {
236  IConsolePrint(CC_HELP, "Reset status data of all engines. This might solve some issues with 'lost' engines. Usage: 'resetengines'.");
237  return true;
238  }
239 
240  StartupEngines();
241  return true;
242 }
243 
249 DEF_CONSOLE_CMD(ConResetEnginePool)
250 {
251  if (argc == 0) {
252  IConsolePrint(CC_HELP, "Reset NewGRF allocations of engine slots. This will remove invalid engine definitions, and might make default engines available again.");
253  return true;
254  }
255 
256  if (_game_mode == GM_MENU) {
257  IConsolePrint(CC_ERROR, "This command is only available in-game and in the editor.");
258  return true;
259  }
260 
262  IConsolePrint(CC_ERROR, "This can only be done when there are no vehicles in the game.");
263  return true;
264  }
265 
266  return true;
267 }
268 
269 #ifdef _DEBUG
275 DEF_CONSOLE_CMD(ConResetTile)
276 {
277  if (argc == 0) {
278  IConsolePrint(CC_HELP, "Reset a tile to bare land. Usage: 'resettile <tile>'.");
279  IConsolePrint(CC_HELP, "Tile can be either decimal (34161) or hexadecimal (0x4a5B).");
280  return true;
281  }
282 
283  if (argc == 2) {
284  uint32_t result;
285  if (GetArgumentInteger(&result, argv[1])) {
286  DoClearSquare((TileIndex)result);
287  return true;
288  }
289  }
290 
291  return false;
292 }
293 #endif /* _DEBUG */
294 
300 DEF_CONSOLE_CMD(ConZoomToLevel)
301 {
302  switch (argc) {
303  case 0:
304  IConsolePrint(CC_HELP, "Set the current zoom level of the main viewport.");
305  IConsolePrint(CC_HELP, "Usage: 'zoomto <level>'.");
306 
308  IConsolePrint(CC_HELP, "The lowest zoom-in level allowed by current client settings is {}.", std::max(ZOOM_LVL_MIN, _settings_client.gui.zoom_min));
309  } else {
310  IConsolePrint(CC_HELP, "The lowest supported zoom-in level is {}.", std::max(ZOOM_LVL_MIN, _settings_client.gui.zoom_min));
311  }
312 
314  IConsolePrint(CC_HELP, "The highest zoom-out level allowed by current client settings is {}.", std::min(_settings_client.gui.zoom_max, ZOOM_LVL_MAX));
315  } else {
316  IConsolePrint(CC_HELP, "The highest supported zoom-out level is {}.", std::min(_settings_client.gui.zoom_max, ZOOM_LVL_MAX));
317  }
318  return true;
319 
320  case 2: {
321  uint32_t level;
322  if (GetArgumentInteger(&level, argv[1])) {
323  /* In case ZOOM_LVL_MIN is more than 0, the next if statement needs to be amended.
324  * A simple check for less than ZOOM_LVL_MIN does not work here because we are
325  * reading an unsigned integer from the console, so just check for a '-' char. */
326  static_assert(ZOOM_LVL_MIN == 0);
327  if (argv[1][0] == '-') {
328  IConsolePrint(CC_ERROR, "Zoom-in levels below {} are not supported.", ZOOM_LVL_MIN);
329  } else if (level < _settings_client.gui.zoom_min) {
330  IConsolePrint(CC_ERROR, "Current client settings do not allow zooming in below level {}.", _settings_client.gui.zoom_min);
331  } else if (level > ZOOM_LVL_MAX) {
332  IConsolePrint(CC_ERROR, "Zoom-in levels above {} are not supported.", ZOOM_LVL_MAX);
333  } else if (level > _settings_client.gui.zoom_max) {
334  IConsolePrint(CC_ERROR, "Current client settings do not allow zooming out beyond level {}.", _settings_client.gui.zoom_max);
335  } else {
336  Window *w = GetMainWindow();
337  Viewport *vp = w->viewport;
338  while (vp->zoom > level) DoZoomInOutWindow(ZOOM_IN, w);
339  while (vp->zoom < level) DoZoomInOutWindow(ZOOM_OUT, w);
340  }
341  return true;
342  }
343  break;
344  }
345  }
346 
347  return false;
348 }
349 
359 DEF_CONSOLE_CMD(ConScrollToTile)
360 {
361  if (argc == 0) {
362  IConsolePrint(CC_HELP, "Center the screen on a given tile.");
363  IConsolePrint(CC_HELP, "Usage: 'scrollto [instant] <tile>' or 'scrollto [instant] <x> <y>'.");
364  IConsolePrint(CC_HELP, "Numbers can be either decimal (34161) or hexadecimal (0x4a5B).");
365  IConsolePrint(CC_HELP, "'instant' will immediately move and redraw viewport without smooth scrolling.");
366  return true;
367  }
368  if (argc < 2) return false;
369 
370  uint32_t arg_index = 1;
371  bool instant = false;
372  if (strcmp(argv[arg_index], "instant") == 0) {
373  ++arg_index;
374  instant = true;
375  }
376 
377  switch (argc - arg_index) {
378  case 1: {
379  uint32_t result;
380  if (GetArgumentInteger(&result, argv[arg_index])) {
381  if (result >= Map::Size()) {
382  IConsolePrint(CC_ERROR, "Tile does not exist.");
383  return true;
384  }
385  ScrollMainWindowToTile((TileIndex)result, instant);
386  return true;
387  }
388  break;
389  }
390 
391  case 2: {
392  uint32_t x, y;
393  if (GetArgumentInteger(&x, argv[arg_index]) && GetArgumentInteger(&y, argv[arg_index + 1])) {
394  if (x >= Map::SizeX() || y >= Map::SizeY()) {
395  IConsolePrint(CC_ERROR, "Tile does not exist.");
396  return true;
397  }
398  ScrollMainWindowToTile(TileXY(x, y), instant);
399  return true;
400  }
401  break;
402  }
403  }
404 
405  return false;
406 }
407 
414 {
415  if (argc == 0) {
416  IConsolePrint(CC_HELP, "Save the current game. Usage: 'save <filename>'.");
417  return true;
418  }
419 
420  if (argc == 2) {
421  std::string filename = argv[1];
422  filename += ".sav";
423  IConsolePrint(CC_DEFAULT, "Saving map...");
424 
425  if (SaveOrLoad(filename, SLO_SAVE, DFT_GAME_FILE, SAVE_DIR) != SL_OK) {
426  IConsolePrint(CC_ERROR, "Saving map failed.");
427  } else {
428  IConsolePrint(CC_INFO, "Map successfully saved to '{}'.", filename);
429  }
430  return true;
431  }
432 
433  return false;
434 }
435 
440 DEF_CONSOLE_CMD(ConSaveConfig)
441 {
442  if (argc == 0) {
443  IConsolePrint(CC_HELP, "Saves the configuration for new games to the configuration file, typically 'openttd.cfg'.");
444  IConsolePrint(CC_HELP, "It does not save the configuration of the current game to the configuration file.");
445  return true;
446  }
447 
448  SaveToConfig();
449  IConsolePrint(CC_DEFAULT, "Saved config.");
450  return true;
451 }
452 
453 DEF_CONSOLE_CMD(ConLoad)
454 {
455  if (argc == 0) {
456  IConsolePrint(CC_HELP, "Load a game by name or index. Usage: 'load <file | number>'.");
457  return true;
458  }
459 
460  if (argc != 2) return false;
461 
462  const char *file = argv[1];
464  const FiosItem *item = _console_file_list_savegame.FindItem(file);
465  if (item != nullptr) {
466  if (GetAbstractFileType(item->type) == FT_SAVEGAME) {
468  _file_to_saveload.Set(*item);
469  } else {
470  IConsolePrint(CC_ERROR, "'{}' is not a savegame.", file);
471  }
472  } else {
473  IConsolePrint(CC_ERROR, "'{}' cannot be found.", file);
474  }
475 
476  return true;
477 }
478 
479 DEF_CONSOLE_CMD(ConLoadScenario)
480 {
481  if (argc == 0) {
482  IConsolePrint(CC_HELP, "Load a scenario by name or index. Usage: 'load_scenario <file | number>'.");
483  return true;
484  }
485 
486  if (argc != 2) return false;
487 
488  const char *file = argv[1];
490  const FiosItem *item = _console_file_list_scenario.FindItem(file);
491  if (item != nullptr) {
492  if (GetAbstractFileType(item->type) == FT_SCENARIO) {
494  _file_to_saveload.Set(*item);
495  } else {
496  IConsolePrint(CC_ERROR, "'{}' is not a scenario.", file);
497  }
498  } else {
499  IConsolePrint(CC_ERROR, "'{}' cannot be found.", file);
500  }
501 
502  return true;
503 }
504 
505 DEF_CONSOLE_CMD(ConLoadHeightmap)
506 {
507  if (argc == 0) {
508  IConsolePrint(CC_HELP, "Load a heightmap by name or index. Usage: 'load_heightmap <file | number>'.");
509  return true;
510  }
511 
512  if (argc != 2) return false;
513 
514  const char *file = argv[1];
516  const FiosItem *item = _console_file_list_heightmap.FindItem(file);
517  if (item != nullptr) {
518  if (GetAbstractFileType(item->type) == FT_HEIGHTMAP) {
520  _file_to_saveload.Set(*item);
521  } else {
522  IConsolePrint(CC_ERROR, "'{}' is not a heightmap.", file);
523  }
524  } else {
525  IConsolePrint(CC_ERROR, "'{}' cannot be found.", file);
526  }
527 
528  return true;
529 }
530 
531 DEF_CONSOLE_CMD(ConRemove)
532 {
533  if (argc == 0) {
534  IConsolePrint(CC_HELP, "Remove a savegame by name or index. Usage: 'rm <file | number>'.");
535  return true;
536  }
537 
538  if (argc != 2) return false;
539 
540  const char *file = argv[1];
542  const FiosItem *item = _console_file_list_savegame.FindItem(file);
543  if (item != nullptr) {
544  if (!FioRemove(item->name)) {
545  IConsolePrint(CC_ERROR, "Failed to delete '{}'.", item->name);
546  }
547  } else {
548  IConsolePrint(CC_ERROR, "'{}' could not be found.", file);
549  }
550 
552  return true;
553 }
554 
555 
556 /* List all the files in the current dir via console */
557 DEF_CONSOLE_CMD(ConListFiles)
558 {
559  if (argc == 0) {
560  IConsolePrint(CC_HELP, "List all loadable savegames and directories in the current dir via console. Usage: 'ls | dir'.");
561  return true;
562  }
563 
565  for (uint i = 0; i < _console_file_list_savegame.size(); i++) {
566  IConsolePrint(CC_DEFAULT, "{}) {}", i, _console_file_list_savegame[i].title);
567  }
568 
569  return true;
570 }
571 
572 /* List all the scenarios */
573 DEF_CONSOLE_CMD(ConListScenarios)
574 {
575  if (argc == 0) {
576  IConsolePrint(CC_HELP, "List all loadable scenarios. Usage: 'list_scenarios'.");
577  return true;
578  }
579 
581  for (uint i = 0; i < _console_file_list_scenario.size(); i++) {
582  IConsolePrint(CC_DEFAULT, "{}) {}", i, _console_file_list_scenario[i].title);
583  }
584 
585  return true;
586 }
587 
588 /* List all the heightmaps */
589 DEF_CONSOLE_CMD(ConListHeightmaps)
590 {
591  if (argc == 0) {
592  IConsolePrint(CC_HELP, "List all loadable heightmaps. Usage: 'list_heightmaps'.");
593  return true;
594  }
595 
597  for (uint i = 0; i < _console_file_list_heightmap.size(); i++) {
598  IConsolePrint(CC_DEFAULT, "{}) {}", i, _console_file_list_heightmap[i].title);
599  }
600 
601  return true;
602 }
603 
604 /* Change the dir via console */
605 DEF_CONSOLE_CMD(ConChangeDirectory)
606 {
607  if (argc == 0) {
608  IConsolePrint(CC_HELP, "Change the dir via console. Usage: 'cd <directory | number>'.");
609  return true;
610  }
611 
612  if (argc != 2) return false;
613 
614  const char *file = argv[1];
616  const FiosItem *item = _console_file_list_savegame.FindItem(file);
617  if (item != nullptr) {
618  switch (item->type) {
619  case FIOS_TYPE_DIR: case FIOS_TYPE_DRIVE: case FIOS_TYPE_PARENT:
620  FiosBrowseTo(item);
621  break;
622  default: IConsolePrint(CC_ERROR, "{}: Not a directory.", file);
623  }
624  } else {
625  IConsolePrint(CC_ERROR, "{}: No such file or directory.", file);
626  }
627 
629  return true;
630 }
631 
632 DEF_CONSOLE_CMD(ConPrintWorkingDirectory)
633 {
634  if (argc == 0) {
635  IConsolePrint(CC_HELP, "Print out the current working directory. Usage: 'pwd'.");
636  return true;
637  }
638 
639  /* XXX - Workaround for broken file handling */
642 
644  return true;
645 }
646 
647 DEF_CONSOLE_CMD(ConClearBuffer)
648 {
649  if (argc == 0) {
650  IConsolePrint(CC_HELP, "Clear the console buffer. Usage: 'clear'.");
651  return true;
652  }
653 
654  IConsoleClearBuffer();
656  return true;
657 }
658 
659 
660 /**********************************
661  * Network Core Console Commands
662  **********************************/
663 
664 static bool ConKickOrBan(const char *argv, bool ban, const std::string &reason)
665 {
666  uint n;
667 
668  if (strchr(argv, '.') == nullptr && strchr(argv, ':') == nullptr) { // banning with ID
669  ClientID client_id = (ClientID)atoi(argv);
670 
671  /* Don't kill the server, or the client doing the rcon. The latter can't be kicked because
672  * kicking frees closes and subsequently free the connection related instances, which we
673  * would be reading from and writing to after returning. So we would read or write data
674  * from freed memory up till the segfault triggers. */
675  if (client_id == CLIENT_ID_SERVER || client_id == _redirect_console_to_client) {
676  IConsolePrint(CC_ERROR, "You can not {} yourself!", ban ? "ban" : "kick");
677  return true;
678  }
679 
681  if (ci == nullptr) {
682  IConsolePrint(CC_ERROR, "Invalid client ID.");
683  return true;
684  }
685 
686  if (!ban) {
687  /* Kick only this client, not all clients with that IP */
688  NetworkServerKickClient(client_id, reason);
689  return true;
690  }
691 
692  /* When banning, kick+ban all clients with that IP */
693  n = NetworkServerKickOrBanIP(client_id, ban, reason);
694  } else {
695  n = NetworkServerKickOrBanIP(argv, ban, reason);
696  }
697 
698  if (n == 0) {
699  IConsolePrint(CC_DEFAULT, ban ? "Client not online, address added to banlist." : "Client not found.");
700  } else {
701  IConsolePrint(CC_DEFAULT, "{}ed {} client(s).", ban ? "Bann" : "Kick", n);
702  }
703 
704  return true;
705 }
706 
707 DEF_CONSOLE_CMD(ConKick)
708 {
709  if (argc == 0) {
710  IConsolePrint(CC_HELP, "Kick a client from a network game. Usage: 'kick <ip | client-id> [<kick-reason>]'.");
711  IConsolePrint(CC_HELP, "For client-id's, see the command 'clients'.");
712  return true;
713  }
714 
715  if (argc != 2 && argc != 3) return false;
716 
717  /* No reason supplied for kicking */
718  if (argc == 2) return ConKickOrBan(argv[1], false, {});
719 
720  /* Reason for kicking supplied */
721  size_t kick_message_length = strlen(argv[2]);
722  if (kick_message_length >= 255) {
723  IConsolePrint(CC_ERROR, "Maximum kick message length is 254 characters. You entered {} characters.", kick_message_length);
724  return false;
725  } else {
726  return ConKickOrBan(argv[1], false, argv[2]);
727  }
728 }
729 
730 DEF_CONSOLE_CMD(ConBan)
731 {
732  if (argc == 0) {
733  IConsolePrint(CC_HELP, "Ban a client from a network game. Usage: 'ban <ip | client-id> [<ban-reason>]'.");
734  IConsolePrint(CC_HELP, "For client-id's, see the command 'clients'.");
735  IConsolePrint(CC_HELP, "If the client is no longer online, you can still ban their IP.");
736  return true;
737  }
738 
739  if (argc != 2 && argc != 3) return false;
740 
741  /* No reason supplied for kicking */
742  if (argc == 2) return ConKickOrBan(argv[1], true, {});
743 
744  /* Reason for kicking supplied */
745  size_t kick_message_length = strlen(argv[2]);
746  if (kick_message_length >= 255) {
747  IConsolePrint(CC_ERROR, "Maximum kick message length is 254 characters. You entered {} characters.", kick_message_length);
748  return false;
749  } else {
750  return ConKickOrBan(argv[1], true, argv[2]);
751  }
752 }
753 
754 DEF_CONSOLE_CMD(ConUnBan)
755 {
756  if (argc == 0) {
757  IConsolePrint(CC_HELP, "Unban a client from a network game. Usage: 'unban <ip | banlist-index>'.");
758  IConsolePrint(CC_HELP, "For a list of banned IP's, see the command 'banlist'.");
759  return true;
760  }
761 
762  if (argc != 2) return false;
763 
764  /* Try by IP. */
765  uint index;
766  for (index = 0; index < _network_ban_list.size(); index++) {
767  if (_network_ban_list[index] == argv[1]) break;
768  }
769 
770  /* Try by index. */
771  if (index >= _network_ban_list.size()) {
772  index = atoi(argv[1]) - 1U; // let it wrap
773  }
774 
775  if (index < _network_ban_list.size()) {
776  IConsolePrint(CC_DEFAULT, "Unbanned {}.", _network_ban_list[index]);
777  _network_ban_list.erase(_network_ban_list.begin() + index);
778  } else {
779  IConsolePrint(CC_DEFAULT, "Invalid list index or IP not in ban-list.");
780  IConsolePrint(CC_DEFAULT, "For a list of banned IP's, see the command 'banlist'.");
781  }
782 
783  return true;
784 }
785 
786 DEF_CONSOLE_CMD(ConBanList)
787 {
788  if (argc == 0) {
789  IConsolePrint(CC_HELP, "List the IP's of banned clients: Usage 'banlist'.");
790  return true;
791  }
792 
793  IConsolePrint(CC_DEFAULT, "Banlist:");
794 
795  uint i = 1;
796  for (const auto &entry : _network_ban_list) {
797  IConsolePrint(CC_DEFAULT, " {}) {}", i, entry);
798  i++;
799  }
800 
801  return true;
802 }
803 
804 DEF_CONSOLE_CMD(ConPauseGame)
805 {
806  if (argc == 0) {
807  IConsolePrint(CC_HELP, "Pause a network game. Usage: 'pause'.");
808  return true;
809  }
810 
811  if (_game_mode == GM_MENU) {
812  IConsolePrint(CC_ERROR, "This command is only available in-game and in the editor.");
813  return true;
814  }
815 
818  if (!_networking) IConsolePrint(CC_DEFAULT, "Game paused.");
819  } else {
820  IConsolePrint(CC_DEFAULT, "Game is already paused.");
821  }
822 
823  return true;
824 }
825 
826 DEF_CONSOLE_CMD(ConUnpauseGame)
827 {
828  if (argc == 0) {
829  IConsolePrint(CC_HELP, "Unpause a network game. Usage: 'unpause'.");
830  return true;
831  }
832 
833  if (_game_mode == GM_MENU) {
834  IConsolePrint(CC_ERROR, "This command is only available in-game and in the editor.");
835  return true;
836  }
837 
840  if (!_networking) IConsolePrint(CC_DEFAULT, "Game unpaused.");
841  } else if ((_pause_mode & PM_PAUSED_ERROR) != PM_UNPAUSED) {
842  IConsolePrint(CC_DEFAULT, "Game is in error state and cannot be unpaused via console.");
843  } else if (_pause_mode != PM_UNPAUSED) {
844  IConsolePrint(CC_DEFAULT, "Game cannot be unpaused manually; disable pause_on_join/min_active_clients.");
845  } else {
846  IConsolePrint(CC_DEFAULT, "Game is already unpaused.");
847  }
848 
849  return true;
850 }
851 
852 DEF_CONSOLE_CMD(ConRcon)
853 {
854  if (argc == 0) {
855  IConsolePrint(CC_HELP, "Remote control the server from another client. Usage: 'rcon <password> <command>'.");
856  IConsolePrint(CC_HELP, "Remember to enclose the command in quotes, otherwise only the first parameter is sent.");
857  IConsolePrint(CC_HELP, "When your client's public key is in the 'authorized keys' for 'rcon', the password is not checked and may be '*'.");
858  return true;
859  }
860 
861  if (argc < 3) return false;
862 
863  if (_network_server) {
864  IConsoleCmdExec(argv[2]);
865  } else {
866  NetworkClientSendRcon(argv[1], argv[2]);
867  }
868  return true;
869 }
870 
871 DEF_CONSOLE_CMD(ConStatus)
872 {
873  if (argc == 0) {
874  IConsolePrint(CC_HELP, "List the status of all clients connected to the server. Usage 'status'.");
875  return true;
876  }
877 
879  return true;
880 }
881 
882 DEF_CONSOLE_CMD(ConServerInfo)
883 {
884  if (argc == 0) {
885  IConsolePrint(CC_HELP, "List current and maximum client/company limits. Usage 'server_info'.");
886  IConsolePrint(CC_HELP, "You can change these values by modifying settings 'network.max_clients' and 'network.max_companies'.");
887  return true;
888  }
889 
891  IConsolePrint(CC_DEFAULT, "Current/maximum clients: {:3d}/{:3d}", _network_game_info.clients_on, _settings_client.network.max_clients);
892  IConsolePrint(CC_DEFAULT, "Current/maximum companies: {:3d}/{:3d}", Company::GetNumItems(), _settings_client.network.max_companies);
893  IConsolePrint(CC_DEFAULT, "Current spectators: {:3d}", NetworkSpectatorCount());
894 
895  return true;
896 }
897 
898 DEF_CONSOLE_CMD(ConClientNickChange)
899 {
900  if (argc != 3) {
901  IConsolePrint(CC_HELP, "Change the nickname of a connected client. Usage: 'client_name <client-id> <new-name>'.");
902  IConsolePrint(CC_HELP, "For client-id's, see the command 'clients'.");
903  return true;
904  }
905 
906  ClientID client_id = (ClientID)atoi(argv[1]);
907 
908  if (client_id == CLIENT_ID_SERVER) {
909  IConsolePrint(CC_ERROR, "Please use the command 'name' to change your own name!");
910  return true;
911  }
912 
913  if (NetworkClientInfo::GetByClientID(client_id) == nullptr) {
914  IConsolePrint(CC_ERROR, "Invalid client ID.");
915  return true;
916  }
917 
918  std::string client_name(argv[2]);
919  StrTrimInPlace(client_name);
920  if (!NetworkIsValidClientName(client_name)) {
921  IConsolePrint(CC_ERROR, "Cannot give a client an empty name.");
922  return true;
923  }
924 
925  if (!NetworkServerChangeClientName(client_id, client_name)) {
926  IConsolePrint(CC_ERROR, "Cannot give a client a duplicate name.");
927  }
928 
929  return true;
930 }
931 
932 DEF_CONSOLE_CMD(ConJoinCompany)
933 {
934  if (argc < 2) {
935  IConsolePrint(CC_HELP, "Request joining another company. Usage: 'join <company-id>'.");
936  IConsolePrint(CC_HELP, "For valid company-id see company list, use 255 for spectator.");
937  return true;
938  }
939 
940  CompanyID company_id = (CompanyID)(atoi(argv[1]) <= MAX_COMPANIES ? atoi(argv[1]) - 1 : atoi(argv[1]));
941 
943  if (info == nullptr) {
944  IConsolePrint(CC_ERROR, "You have not joined the game yet!");
945  return true;
946  }
947 
948  /* Check we have a valid company id! */
949  if (!Company::IsValidID(company_id) && company_id != COMPANY_SPECTATOR) {
950  IConsolePrint(CC_ERROR, "Company does not exist. Company-id must be between 1 and {}.", MAX_COMPANIES);
951  return true;
952  }
953 
954  if (info->client_playas == company_id) {
955  IConsolePrint(CC_ERROR, "You are already there!");
956  return true;
957  }
958 
959  if (company_id != COMPANY_SPECTATOR && !Company::IsHumanID(company_id)) {
960  IConsolePrint(CC_ERROR, "Cannot join AI company.");
961  return true;
962  }
963 
964  if (!info->CanJoinCompany(company_id)) {
965  IConsolePrint(CC_ERROR, "You are not allowed to join this company.");
966  return true;
967  }
968 
969  /* non-dedicated server may just do the move! */
970  if (_network_server) {
972  } else {
973  NetworkClientRequestMove(company_id);
974  }
975 
976  return true;
977 }
978 
979 DEF_CONSOLE_CMD(ConMoveClient)
980 {
981  if (argc < 3) {
982  IConsolePrint(CC_HELP, "Move a client to another company. Usage: 'move <client-id> <company-id>'.");
983  IConsolePrint(CC_HELP, "For valid client-id see 'clients', for valid company-id see 'companies', use 255 for moving to spectators.");
984  return true;
985  }
986 
987  const NetworkClientInfo *ci = NetworkClientInfo::GetByClientID((ClientID)atoi(argv[1]));
988  CompanyID company_id = (CompanyID)(atoi(argv[2]) <= MAX_COMPANIES ? atoi(argv[2]) - 1 : atoi(argv[2]));
989 
990  /* check the client exists */
991  if (ci == nullptr) {
992  IConsolePrint(CC_ERROR, "Invalid client-id, check the command 'clients' for valid client-id's.");
993  return true;
994  }
995 
996  if (!Company::IsValidID(company_id) && company_id != COMPANY_SPECTATOR) {
997  IConsolePrint(CC_ERROR, "Company does not exist. Company-id must be between 1 and {}.", MAX_COMPANIES);
998  return true;
999  }
1000 
1001  if (company_id != COMPANY_SPECTATOR && !Company::IsHumanID(company_id)) {
1002  IConsolePrint(CC_ERROR, "You cannot move clients to AI companies.");
1003  return true;
1004  }
1005 
1007  IConsolePrint(CC_ERROR, "You cannot move the server!");
1008  return true;
1009  }
1010 
1011  if (ci->client_playas == company_id) {
1012  IConsolePrint(CC_ERROR, "You cannot move someone to where they already are!");
1013  return true;
1014  }
1015 
1016  /* we are the server, so force the update */
1017  NetworkServerDoMove(ci->client_id, company_id);
1018 
1019  return true;
1020 }
1021 
1022 DEF_CONSOLE_CMD(ConResetCompany)
1023 {
1024  if (argc == 0) {
1025  IConsolePrint(CC_HELP, "Remove an idle company from the game. Usage: 'reset_company <company-id>'.");
1026  IConsolePrint(CC_HELP, "For company-id's, see the list of companies from the dropdown menu. Company 1 is 1, etc.");
1027  return true;
1028  }
1029 
1030  if (argc != 2) return false;
1031 
1032  CompanyID index = (CompanyID)(atoi(argv[1]) - 1);
1033 
1034  /* Check valid range */
1035  if (!Company::IsValidID(index)) {
1036  IConsolePrint(CC_ERROR, "Company does not exist. Company-id must be between 1 and {}.", MAX_COMPANIES);
1037  return true;
1038  }
1039 
1040  if (!Company::IsHumanID(index)) {
1041  IConsolePrint(CC_ERROR, "Company is owned by an AI.");
1042  return true;
1043  }
1044 
1045  if (NetworkCompanyHasClients(index)) {
1046  IConsolePrint(CC_ERROR, "Cannot remove company: a client is connected to that company.");
1047  return false;
1048  }
1050  assert(ci != nullptr);
1051  if (ci->client_playas == index) {
1052  IConsolePrint(CC_ERROR, "Cannot remove company: the server is connected to that company.");
1053  return true;
1054  }
1055 
1056  /* It is safe to remove this company */
1058  IConsolePrint(CC_DEFAULT, "Company deleted.");
1059 
1060  return true;
1061 }
1062 
1063 DEF_CONSOLE_CMD(ConNetworkClients)
1064 {
1065  if (argc == 0) {
1066  IConsolePrint(CC_HELP, "Get a list of connected clients including their ID, name, company-id, and IP. Usage: 'clients'.");
1067  return true;
1068  }
1069 
1071 
1072  return true;
1073 }
1074 
1075 DEF_CONSOLE_CMD(ConNetworkReconnect)
1076 {
1077  if (argc == 0) {
1078  IConsolePrint(CC_HELP, "Reconnect to server to which you were connected last time. Usage: 'reconnect [<company>]'.");
1079  IConsolePrint(CC_HELP, "Company 255 is spectator (default, if not specified), 0 means creating new company.");
1080  IConsolePrint(CC_HELP, "All others are a certain company with Company 1 being #1.");
1081  return true;
1082  }
1083 
1084  CompanyID playas = (argc >= 2) ? (CompanyID)atoi(argv[1]) : COMPANY_SPECTATOR;
1085  switch (playas) {
1086  case 0: playas = COMPANY_NEW_COMPANY; break;
1087  case COMPANY_SPECTATOR: /* nothing to do */ break;
1088  default:
1089  /* From a user pov 0 is a new company, internally it's different and all
1090  * companies are offset by one to ease up on users (eg companies 1-8 not 0-7) */
1091  if (playas < COMPANY_FIRST + 1 || playas > MAX_COMPANIES + 1) return false;
1092  break;
1093  }
1094 
1095  if (_settings_client.network.last_joined.empty()) {
1096  IConsolePrint(CC_DEFAULT, "No server for reconnecting.");
1097  return true;
1098  }
1099 
1100  /* Don't resolve the address first, just print it directly as it comes from the config file. */
1101  IConsolePrint(CC_DEFAULT, "Reconnecting to {} ...", _settings_client.network.last_joined);
1102 
1104 }
1105 
1106 DEF_CONSOLE_CMD(ConNetworkConnect)
1107 {
1108  if (argc == 0) {
1109  IConsolePrint(CC_HELP, "Connect to a remote OTTD server and join the game. Usage: 'connect <ip>'.");
1110  IConsolePrint(CC_HELP, "IP can contain port and company: 'IP[:Port][#Company]', eg: 'server.ottd.org:443#2'.");
1111  IConsolePrint(CC_HELP, "Company #255 is spectator all others are a certain company with Company 1 being #1.");
1112  return true;
1113  }
1114 
1115  if (argc < 2) return false;
1116 
1118 }
1119 
1120 /*********************************
1121  * script file console commands
1122  *********************************/
1123 
1124 DEF_CONSOLE_CMD(ConExec)
1125 {
1126  if (argc == 0) {
1127  IConsolePrint(CC_HELP, "Execute a local script file. Usage: 'exec <script> <?>'.");
1128  return true;
1129  }
1130 
1131  if (argc < 2) return false;
1132 
1133  auto script_file = FioFOpenFile(argv[1], "r", BASE_DIR);
1134 
1135  if (!script_file.has_value()) {
1136  if (argc == 2 || atoi(argv[2]) != 0) IConsolePrint(CC_ERROR, "Script file '{}' not found.", argv[1]);
1137  return true;
1138  }
1139 
1140  if (_script_current_depth == 11) {
1141  IConsolePrint(CC_ERROR, "Maximum 'exec' depth reached; script A is calling script B is calling script C ... more than 10 times.");
1142  return true;
1143  }
1144 
1146  uint script_depth = _script_current_depth;
1147 
1148  char cmdline[ICON_CMDLN_SIZE];
1149  while (fgets(cmdline, sizeof(cmdline), *script_file) != nullptr) {
1150  /* Remove newline characters from the executing script */
1151  for (char *cmdptr = cmdline; *cmdptr != '\0'; cmdptr++) {
1152  if (*cmdptr == '\n' || *cmdptr == '\r') {
1153  *cmdptr = '\0';
1154  break;
1155  }
1156  }
1157  IConsoleCmdExec(cmdline);
1158  /* Ensure that we are still on the same depth or that we returned via 'return'. */
1159  assert(_script_current_depth == script_depth || _script_current_depth == script_depth - 1);
1160 
1161  /* The 'return' command was executed. */
1162  if (_script_current_depth == script_depth - 1) break;
1163  }
1164 
1165  if (ferror(*script_file) != 0) {
1166  IConsolePrint(CC_ERROR, "Encountered error while trying to read from script file '{}'.", argv[1]);
1167  }
1168 
1169  if (_script_current_depth == script_depth) _script_current_depth--;
1170  return true;
1171 }
1172 
1173 DEF_CONSOLE_CMD(ConSchedule)
1174 {
1175  if (argc < 3 || std::string_view(argv[1]) != "on-next-calendar-month") {
1176  IConsolePrint(CC_HELP, "Schedule a local script to execute later. Usage: 'schedule on-next-calendar-month <script>'.");
1177  return true;
1178  }
1179 
1180  /* Check if the file exists. It might still go away later, but helpful to show an error now. */
1181  if (!FioCheckFileExists(argv[2], BASE_DIR)) {
1182  IConsolePrint(CC_ERROR, "Script file '{}' not found.", argv[2]);
1183  return true;
1184  }
1185 
1186  /* We only support a single script scheduled, so we tell the user what's happening if there was already one. */
1187  const std::string_view filename = std::string_view(argv[2]);
1188  if (!_scheduled_monthly_script.empty() && filename == _scheduled_monthly_script) {
1189  IConsolePrint(CC_INFO, "Script file '{}' was already scheduled to execute at the start of next calendar month.", filename);
1190  } else if (!_scheduled_monthly_script.empty() && filename != _scheduled_monthly_script) {
1191  IConsolePrint(CC_INFO, "Script file '{}' scheduled to execute at the start of next calendar month, replacing the previously scheduled script file '{}'.", filename, _scheduled_monthly_script);
1192  } else {
1193  IConsolePrint(CC_INFO, "Script file '{}' scheduled to execute at the start of next calendar month.", filename);
1194  }
1195 
1196  /* Store the filename to be used by _schedule_timer on the start of next calendar month. */
1197  _scheduled_monthly_script = filename;
1198 
1199  return true;
1200 }
1201 
1202 DEF_CONSOLE_CMD(ConReturn)
1203 {
1204  if (argc == 0) {
1205  IConsolePrint(CC_HELP, "Stop executing a running script. Usage: 'return'.");
1206  return true;
1207  }
1208 
1210  return true;
1211 }
1212 
1213 /*****************************
1214  * default console commands
1215  ******************************/
1216 extern bool CloseConsoleLogIfActive();
1217 extern const std::vector<GRFFile *> &GetAllGRFFiles();
1218 extern void ConPrintFramerate(); // framerate_gui.cpp
1219 extern void ShowFramerateWindow();
1220 
1221 DEF_CONSOLE_CMD(ConScript)
1222 {
1223  extern std::optional<FileHandle> _iconsole_output_file;
1224 
1225  if (argc == 0) {
1226  IConsolePrint(CC_HELP, "Start or stop logging console output to a file. Usage: 'script <filename>'.");
1227  IConsolePrint(CC_HELP, "If filename is omitted, a running log is stopped if it is active.");
1228  return true;
1229  }
1230 
1231  if (!CloseConsoleLogIfActive()) {
1232  if (argc < 2) return false;
1233 
1234  _iconsole_output_file = FileHandle::Open(argv[1], "ab");
1235  if (!_iconsole_output_file.has_value()) {
1236  IConsolePrint(CC_ERROR, "Could not open console log file '{}'.", argv[1]);
1237  } else {
1238  IConsolePrint(CC_INFO, "Console log output started to '{}'.", argv[1]);
1239  }
1240  }
1241 
1242  return true;
1243 }
1244 
1245 
1246 DEF_CONSOLE_CMD(ConEcho)
1247 {
1248  if (argc == 0) {
1249  IConsolePrint(CC_HELP, "Print back the first argument to the console. Usage: 'echo <arg>'.");
1250  return true;
1251  }
1252 
1253  if (argc < 2) return false;
1254  IConsolePrint(CC_DEFAULT, argv[1]);
1255  return true;
1256 }
1257 
1258 DEF_CONSOLE_CMD(ConEchoC)
1259 {
1260  if (argc == 0) {
1261  IConsolePrint(CC_HELP, "Print back the first argument to the console in a given colour. Usage: 'echoc <colour> <arg2>'.");
1262  return true;
1263  }
1264 
1265  if (argc < 3) return false;
1266  IConsolePrint((TextColour)Clamp(atoi(argv[1]), TC_BEGIN, TC_END - 1), argv[2]);
1267  return true;
1268 }
1269 
1270 DEF_CONSOLE_CMD(ConNewGame)
1271 {
1272  if (argc == 0) {
1273  IConsolePrint(CC_HELP, "Start a new game. Usage: 'newgame [seed]'.");
1274  IConsolePrint(CC_HELP, "The server can force a new game using 'newgame'; any client joined will rejoin after the server is done generating the new game.");
1275  return true;
1276  }
1277 
1278  StartNewGameWithoutGUI((argc == 2) ? std::strtoul(argv[1], nullptr, 10) : GENERATE_NEW_SEED);
1279  return true;
1280 }
1281 
1282 DEF_CONSOLE_CMD(ConRestart)
1283 {
1284  if (argc == 0 || argc > 2) {
1285  IConsolePrint(CC_HELP, "Restart game. Usage: 'restart [current|newgame]'.");
1286  IConsolePrint(CC_HELP, "Restarts a game, using either the current or newgame (default) settings.");
1287  IConsolePrint(CC_HELP, " * if you started from a new game, and your current/newgame settings haven't changed, the game will be identical to when you started it.");
1288  IConsolePrint(CC_HELP, " * if you started from a savegame / scenario / heightmap, the game might be different, because the current/newgame settings might differ.");
1289  return true;
1290  }
1291 
1292  if (argc == 1 || std::string_view(argv[1]) == "newgame") {
1294  } else {
1298  }
1299 
1300  return true;
1301 }
1302 
1303 DEF_CONSOLE_CMD(ConReload)
1304 {
1305  if (argc == 0) {
1306  IConsolePrint(CC_HELP, "Reload game. Usage: 'reload'.");
1307  IConsolePrint(CC_HELP, "Reloads a game if loaded via savegame / scenario / heightmap.");
1308  return true;
1309  }
1310 
1312  IConsolePrint(CC_ERROR, "No game loaded to reload.");
1313  return true;
1314  }
1315 
1316  /* Use a switch-mode to prevent copying over newgame settings to active settings. */
1320  return true;
1321 }
1322 
1327 static void PrintLineByLine(const std::string &full_string)
1328 {
1329  std::istringstream in(full_string);
1330  std::string line;
1331  while (std::getline(in, line)) {
1332  IConsolePrint(CC_DEFAULT, line);
1333  }
1334 }
1335 
1336 template <typename F, typename ... Args>
1337 bool PrintList(F list_function, Args... args)
1338 {
1339  std::string output_str;
1340  auto inserter = std::back_inserter(output_str);
1341  list_function(inserter, args...);
1342  PrintLineByLine(output_str);
1343 
1344  return true;
1345 }
1346 
1347 DEF_CONSOLE_CMD(ConListAILibs)
1348 {
1349  if (argc == 0) {
1350  IConsolePrint(CC_HELP, "List installed AI libraries. Usage: 'list_ai_libs'.");
1351  return true;
1352  }
1353 
1354  return PrintList(AI::GetConsoleLibraryList);
1355 }
1356 
1357 DEF_CONSOLE_CMD(ConListAI)
1358 {
1359  if (argc == 0) {
1360  IConsolePrint(CC_HELP, "List installed AIs. Usage: 'list_ai'.");
1361  return true;
1362  }
1363 
1364  return PrintList(AI::GetConsoleList, false);
1365 }
1366 
1367 DEF_CONSOLE_CMD(ConListGameLibs)
1368 {
1369  if (argc == 0) {
1370  IConsolePrint(CC_HELP, "List installed Game Script libraries. Usage: 'list_game_libs'.");
1371  return true;
1372  }
1373 
1374  return PrintList(Game::GetConsoleLibraryList);
1375 }
1376 
1377 DEF_CONSOLE_CMD(ConListGame)
1378 {
1379  if (argc == 0) {
1380  IConsolePrint(CC_HELP, "List installed Game Scripts. Usage: 'list_game'.");
1381  return true;
1382  }
1383 
1384  return PrintList(Game::GetConsoleList, false);
1385 }
1386 
1387 DEF_CONSOLE_CMD(ConStartAI)
1388 {
1389  if (argc == 0 || argc > 3) {
1390  IConsolePrint(CC_HELP, "Start a new AI. Usage: 'start_ai [<AI>] [<settings>]'.");
1391  IConsolePrint(CC_HELP, "Start a new AI. If <AI> is given, it starts that specific AI (if found).");
1392  IConsolePrint(CC_HELP, "If <settings> is given, it is parsed and the AI settings are set to that.");
1393  return true;
1394  }
1395 
1396  if (_game_mode != GM_NORMAL) {
1397  IConsolePrint(CC_ERROR, "AIs can only be managed in a game.");
1398  return true;
1399  }
1400 
1402  IConsolePrint(CC_ERROR, "Can't start a new AI (no more free slots).");
1403  return true;
1404  }
1405  if (_networking && !_network_server) {
1406  IConsolePrint(CC_ERROR, "Only the server can start a new AI.");
1407  return true;
1408  }
1410  IConsolePrint(CC_ERROR, "AIs are not allowed in multiplayer by configuration.");
1411  IConsolePrint(CC_ERROR, "Switch AI -> AI in multiplayer to True.");
1412  return true;
1413  }
1414  if (!AI::CanStartNew()) {
1415  IConsolePrint(CC_ERROR, "Can't start a new AI.");
1416  return true;
1417  }
1418 
1419  int n = 0;
1420  /* Find the next free slot */
1421  for (const Company *c : Company::Iterate()) {
1422  if (c->index != n) break;
1423  n++;
1424  }
1425 
1426  AIConfig *config = AIConfig::GetConfig((CompanyID)n);
1427  if (argc >= 2) {
1428  config->Change(argv[1], -1, false);
1429 
1430  /* If the name is not found, and there is a dot in the name,
1431  * try again with the assumption everything right of the dot is
1432  * the version the user wants to load. */
1433  if (!config->HasScript()) {
1434  const char *e = strrchr(argv[1], '.');
1435  if (e != nullptr) {
1436  size_t name_length = e - argv[1];
1437  e++;
1438 
1439  int version = atoi(e);
1440  config->Change(std::string(argv[1], name_length), version, true);
1441  }
1442  }
1443 
1444  if (!config->HasScript()) {
1445  IConsolePrint(CC_ERROR, "Failed to load the specified AI.");
1446  return true;
1447  }
1448  if (argc == 3) {
1449  config->StringToSettings(argv[2]);
1450  }
1451  }
1452 
1453  /* Start a new AI company */
1455 
1456  return true;
1457 }
1458 
1459 DEF_CONSOLE_CMD(ConReloadAI)
1460 {
1461  if (argc != 2) {
1462  IConsolePrint(CC_HELP, "Reload an AI. Usage: 'reload_ai <company-id>'.");
1463  IConsolePrint(CC_HELP, "Reload the AI with the given company id. For company-id's, see the list of companies from the dropdown menu. Company 1 is 1, etc.");
1464  return true;
1465  }
1466 
1467  if (_game_mode != GM_NORMAL) {
1468  IConsolePrint(CC_ERROR, "AIs can only be managed in a game.");
1469  return true;
1470  }
1471 
1472  if (_networking && !_network_server) {
1473  IConsolePrint(CC_ERROR, "Only the server can reload an AI.");
1474  return true;
1475  }
1476 
1477  CompanyID company_id = (CompanyID)(atoi(argv[1]) - 1);
1478  if (!Company::IsValidID(company_id)) {
1479  IConsolePrint(CC_ERROR, "Unknown company. Company range is between 1 and {}.", MAX_COMPANIES);
1480  return true;
1481  }
1482 
1483  /* In singleplayer mode the player can be in an AI company, after cheating or loading network save with an AI in first slot. */
1484  if (Company::IsHumanID(company_id) || company_id == _local_company) {
1485  IConsolePrint(CC_ERROR, "Company is not controlled by an AI.");
1486  return true;
1487  }
1488 
1489  /* First kill the company of the AI, then start a new one. This should start the current AI again */
1492  IConsolePrint(CC_DEFAULT, "AI reloaded.");
1493 
1494  return true;
1495 }
1496 
1497 DEF_CONSOLE_CMD(ConStopAI)
1498 {
1499  if (argc != 2) {
1500  IConsolePrint(CC_HELP, "Stop an AI. Usage: 'stop_ai <company-id>'.");
1501  IConsolePrint(CC_HELP, "Stop the AI with the given company id. For company-id's, see the list of companies from the dropdown menu. Company 1 is 1, etc.");
1502  return true;
1503  }
1504 
1505  if (_game_mode != GM_NORMAL) {
1506  IConsolePrint(CC_ERROR, "AIs can only be managed in a game.");
1507  return true;
1508  }
1509 
1510  if (_networking && !_network_server) {
1511  IConsolePrint(CC_ERROR, "Only the server can stop an AI.");
1512  return true;
1513  }
1514 
1515  CompanyID company_id = (CompanyID)(atoi(argv[1]) - 1);
1516  if (!Company::IsValidID(company_id)) {
1517  IConsolePrint(CC_ERROR, "Unknown company. Company range is between 1 and {}.", MAX_COMPANIES);
1518  return true;
1519  }
1520 
1521  /* In singleplayer mode the player can be in an AI company, after cheating or loading network save with an AI in first slot. */
1522  if (Company::IsHumanID(company_id) || company_id == _local_company) {
1523  IConsolePrint(CC_ERROR, "Company is not controlled by an AI.");
1524  return true;
1525  }
1526 
1527  /* Now kill the company of the AI. */
1529  IConsolePrint(CC_DEFAULT, "AI stopped, company deleted.");
1530 
1531  return true;
1532 }
1533 
1534 DEF_CONSOLE_CMD(ConRescanAI)
1535 {
1536  if (argc == 0) {
1537  IConsolePrint(CC_HELP, "Rescan the AI dir for scripts. Usage: 'rescan_ai'.");
1538  return true;
1539  }
1540 
1541  if (_networking && !_network_server) {
1542  IConsolePrint(CC_ERROR, "Only the server can rescan the AI dir for scripts.");
1543  return true;
1544  }
1545 
1546  AI::Rescan();
1547 
1548  return true;
1549 }
1550 
1551 DEF_CONSOLE_CMD(ConRescanGame)
1552 {
1553  if (argc == 0) {
1554  IConsolePrint(CC_HELP, "Rescan the Game Script dir for scripts. Usage: 'rescan_game'.");
1555  return true;
1556  }
1557 
1558  if (_networking && !_network_server) {
1559  IConsolePrint(CC_ERROR, "Only the server can rescan the Game Script dir for scripts.");
1560  return true;
1561  }
1562 
1563  Game::Rescan();
1564 
1565  return true;
1566 }
1567 
1568 DEF_CONSOLE_CMD(ConRescanNewGRF)
1569 {
1570  if (argc == 0) {
1571  IConsolePrint(CC_HELP, "Rescan the data dir for NewGRFs. Usage: 'rescan_newgrf'.");
1572  return true;
1573  }
1574 
1575  if (!RequestNewGRFScan()) {
1576  IConsolePrint(CC_ERROR, "NewGRF scanning is already running. Please wait until completed to run again.");
1577  }
1578 
1579  return true;
1580 }
1581 
1582 DEF_CONSOLE_CMD(ConGetSeed)
1583 {
1584  if (argc == 0) {
1585  IConsolePrint(CC_HELP, "Returns the seed used to create this game. Usage: 'getseed'.");
1586  IConsolePrint(CC_HELP, "The seed can be used to reproduce the exact same map as the game started with.");
1587  return true;
1588  }
1589 
1591  return true;
1592 }
1593 
1594 DEF_CONSOLE_CMD(ConGetDate)
1595 {
1596  if (argc == 0) {
1597  IConsolePrint(CC_HELP, "Returns the current date (year-month-day) of the game. Usage: 'getdate'.");
1598  return true;
1599  }
1600 
1601  TimerGameCalendar::YearMonthDay ymd = TimerGameCalendar::ConvertDateToYMD(TimerGameCalendar::date);
1602  IConsolePrint(CC_DEFAULT, "Date: {:04d}-{:02d}-{:02d}", ymd.year, ymd.month + 1, ymd.day);
1603  return true;
1604 }
1605 
1606 DEF_CONSOLE_CMD(ConGetSysDate)
1607 {
1608  if (argc == 0) {
1609  IConsolePrint(CC_HELP, "Returns the current date (year-month-day) of your system. Usage: 'getsysdate'.");
1610  return true;
1611  }
1612 
1613  IConsolePrint(CC_DEFAULT, "System Date: {:%Y-%m-%d %H:%M:%S}", fmt::localtime(time(nullptr)));
1614  return true;
1615 }
1616 
1617 
1618 DEF_CONSOLE_CMD(ConAlias)
1619 {
1620  IConsoleAlias *alias;
1621 
1622  if (argc == 0) {
1623  IConsolePrint(CC_HELP, "Add a new alias, or redefine the behaviour of an existing alias . Usage: 'alias <name> <command>'.");
1624  return true;
1625  }
1626 
1627  if (argc < 3) return false;
1628 
1629  alias = IConsole::AliasGet(argv[1]);
1630  if (alias == nullptr) {
1631  IConsole::AliasRegister(argv[1], argv[2]);
1632  } else {
1633  alias->cmdline = argv[2];
1634  }
1635  return true;
1636 }
1637 
1638 DEF_CONSOLE_CMD(ConScreenShot)
1639 {
1640  if (argc == 0) {
1641  IConsolePrint(CC_HELP, "Create a screenshot of the game. Usage: 'screenshot [viewport | normal | big | giant | heightmap | minimap] [no_con] [size <width> <height>] [<filename>]'.");
1642  IConsolePrint(CC_HELP, " 'viewport' (default) makes a screenshot of the current viewport (including menus, windows).");
1643  IConsolePrint(CC_HELP, " 'normal' makes a screenshot of the visible area.");
1644  IConsolePrint(CC_HELP, " 'big' makes a zoomed-in screenshot of the visible area.");
1645  IConsolePrint(CC_HELP, " 'giant' makes a screenshot of the whole map.");
1646  IConsolePrint(CC_HELP, " 'heightmap' makes a heightmap screenshot of the map that can be loaded in as heightmap.");
1647  IConsolePrint(CC_HELP, " 'minimap' makes a top-viewed minimap screenshot of the whole world which represents one tile by one pixel.");
1648  IConsolePrint(CC_HELP, " 'no_con' hides the console to create the screenshot (only useful in combination with 'viewport').");
1649  IConsolePrint(CC_HELP, " 'size' sets the width and height of the viewport to make a screenshot of (only useful in combination with 'normal' or 'big').");
1650  IConsolePrint(CC_HELP, " A filename ending in # will prevent overwriting existing files and will number files counting upwards.");
1651  return true;
1652  }
1653 
1654  if (argc > 7) return false;
1655 
1656  ScreenshotType type = SC_VIEWPORT;
1657  uint32_t width = 0;
1658  uint32_t height = 0;
1659  std::string name{};
1660  uint32_t arg_index = 1;
1661 
1662  if (argc > arg_index) {
1663  if (strcmp(argv[arg_index], "viewport") == 0) {
1664  type = SC_VIEWPORT;
1665  arg_index += 1;
1666  } else if (strcmp(argv[arg_index], "normal") == 0) {
1667  type = SC_DEFAULTZOOM;
1668  arg_index += 1;
1669  } else if (strcmp(argv[arg_index], "big") == 0) {
1670  type = SC_ZOOMEDIN;
1671  arg_index += 1;
1672  } else if (strcmp(argv[arg_index], "giant") == 0) {
1673  type = SC_WORLD;
1674  arg_index += 1;
1675  } else if (strcmp(argv[arg_index], "heightmap") == 0) {
1676  type = SC_HEIGHTMAP;
1677  arg_index += 1;
1678  } else if (strcmp(argv[arg_index], "minimap") == 0) {
1679  type = SC_MINIMAP;
1680  arg_index += 1;
1681  }
1682  }
1683 
1684  if (argc > arg_index && strcmp(argv[arg_index], "no_con") == 0) {
1685  if (type != SC_VIEWPORT) {
1686  IConsolePrint(CC_ERROR, "'no_con' can only be used in combination with 'viewport'.");
1687  return true;
1688  }
1689  IConsoleClose();
1690  arg_index += 1;
1691  }
1692 
1693  if (argc > arg_index + 2 && strcmp(argv[arg_index], "size") == 0) {
1694  /* size <width> <height> */
1695  if (type != SC_DEFAULTZOOM && type != SC_ZOOMEDIN) {
1696  IConsolePrint(CC_ERROR, "'size' can only be used in combination with 'normal' or 'big'.");
1697  return true;
1698  }
1699  GetArgumentInteger(&width, argv[arg_index + 1]);
1700  GetArgumentInteger(&height, argv[arg_index + 2]);
1701  arg_index += 3;
1702  }
1703 
1704  if (argc > arg_index) {
1705  /* Last parameter that was not one of the keywords must be the filename. */
1706  name = argv[arg_index];
1707  arg_index += 1;
1708  }
1709 
1710  if (argc > arg_index) {
1711  /* We have parameters we did not process; means we misunderstood any of the above. */
1712  return false;
1713  }
1714 
1715  MakeScreenshot(type, name, width, height);
1716  return true;
1717 }
1718 
1719 DEF_CONSOLE_CMD(ConInfoCmd)
1720 {
1721  if (argc == 0) {
1722  IConsolePrint(CC_HELP, "Print out debugging information about a command. Usage: 'info_cmd <cmd>'.");
1723  return true;
1724  }
1725 
1726  if (argc < 2) return false;
1727 
1728  const IConsoleCmd *cmd = IConsole::CmdGet(argv[1]);
1729  if (cmd == nullptr) {
1730  IConsolePrint(CC_ERROR, "The given command was not found.");
1731  return true;
1732  }
1733 
1734  IConsolePrint(CC_DEFAULT, "Command name: '{}'", cmd->name);
1735 
1736  if (cmd->hook != nullptr) IConsolePrint(CC_DEFAULT, "Command is hooked.");
1737 
1738  return true;
1739 }
1740 
1741 DEF_CONSOLE_CMD(ConDebugLevel)
1742 {
1743  if (argc == 0) {
1744  IConsolePrint(CC_HELP, "Get/set the default debugging level for the game. Usage: 'debug_level [<level>]'.");
1745  IConsolePrint(CC_HELP, "Level can be any combination of names, levels. Eg 'net=5 ms=4'. Remember to enclose it in \"'\"s.");
1746  return true;
1747  }
1748 
1749  if (argc > 2) return false;
1750 
1751  if (argc == 1) {
1752  IConsolePrint(CC_DEFAULT, "Current debug-level: '{}'", GetDebugString());
1753  } else {
1754  SetDebugString(argv[1], [](const std::string &err) { IConsolePrint(CC_ERROR, err); });
1755  }
1756 
1757  return true;
1758 }
1759 
1760 DEF_CONSOLE_CMD(ConExit)
1761 {
1762  if (argc == 0) {
1763  IConsolePrint(CC_HELP, "Exit the game. Usage: 'exit'.");
1764  return true;
1765  }
1766 
1767  if (_game_mode == GM_NORMAL && _settings_client.gui.autosave_on_exit) DoExitSave();
1768 
1769  _exit_game = true;
1770  return true;
1771 }
1772 
1773 DEF_CONSOLE_CMD(ConPart)
1774 {
1775  if (argc == 0) {
1776  IConsolePrint(CC_HELP, "Leave the currently joined/running game (only ingame). Usage: 'part'.");
1777  return true;
1778  }
1779 
1780  if (_game_mode != GM_NORMAL) return false;
1781 
1782  if (_network_dedicated) {
1783  IConsolePrint(CC_ERROR, "A dedicated server can not leave the game.");
1784  return false;
1785  }
1786 
1788  return true;
1789 }
1790 
1791 DEF_CONSOLE_CMD(ConHelp)
1792 {
1793  if (argc == 2) {
1794  const IConsoleCmd *cmd;
1795  const IConsoleAlias *alias;
1796 
1797  cmd = IConsole::CmdGet(argv[1]);
1798  if (cmd != nullptr) {
1799  cmd->proc(0, nullptr);
1800  return true;
1801  }
1802 
1803  alias = IConsole::AliasGet(argv[1]);
1804  if (alias != nullptr) {
1805  cmd = IConsole::CmdGet(alias->cmdline);
1806  if (cmd != nullptr) {
1807  cmd->proc(0, nullptr);
1808  return true;
1809  }
1810  IConsolePrint(CC_ERROR, "Alias is of special type, please see its execution-line: '{}'.", alias->cmdline);
1811  return true;
1812  }
1813 
1814  IConsolePrint(CC_ERROR, "Command not found.");
1815  return true;
1816  }
1817 
1818  IConsolePrint(TC_LIGHT_BLUE, " ---- OpenTTD Console Help ---- ");
1819  IConsolePrint(CC_DEFAULT, " - commands: the command to list all commands is 'list_cmds'.");
1820  IConsolePrint(CC_DEFAULT, " call commands with '<command> <arg2> <arg3>...'");
1821  IConsolePrint(CC_DEFAULT, " - to assign strings, or use them as arguments, enclose it within quotes.");
1822  IConsolePrint(CC_DEFAULT, " like this: '<command> \"string argument with spaces\"'.");
1823  IConsolePrint(CC_DEFAULT, " - use 'help <command>' to get specific information.");
1824  IConsolePrint(CC_DEFAULT, " - scroll console output with shift + (up | down | pageup | pagedown).");
1825  IConsolePrint(CC_DEFAULT, " - scroll console input history with the up or down arrows.");
1827  return true;
1828 }
1829 
1830 DEF_CONSOLE_CMD(ConListCommands)
1831 {
1832  if (argc == 0) {
1833  IConsolePrint(CC_HELP, "List all registered commands. Usage: 'list_cmds [<pre-filter>]'.");
1834  return true;
1835  }
1836 
1837  for (auto &it : IConsole::Commands()) {
1838  const IConsoleCmd *cmd = &it.second;
1839  if (argv[1] == nullptr || cmd->name.find(argv[1]) != std::string::npos) {
1840  if (cmd->hook == nullptr || cmd->hook(false) != CHR_HIDE) IConsolePrint(CC_DEFAULT, cmd->name);
1841  }
1842  }
1843 
1844  return true;
1845 }
1846 
1847 DEF_CONSOLE_CMD(ConListAliases)
1848 {
1849  if (argc == 0) {
1850  IConsolePrint(CC_HELP, "List all registered aliases. Usage: 'list_aliases [<pre-filter>]'.");
1851  return true;
1852  }
1853 
1854  for (auto &it : IConsole::Aliases()) {
1855  const IConsoleAlias *alias = &it.second;
1856  if (argv[1] == nullptr || alias->name.find(argv[1]) != std::string::npos) {
1857  IConsolePrint(CC_DEFAULT, "{} => {}", alias->name, alias->cmdline);
1858  }
1859  }
1860 
1861  return true;
1862 }
1863 
1864 DEF_CONSOLE_CMD(ConCompanies)
1865 {
1866  if (argc == 0) {
1867  IConsolePrint(CC_HELP, "List the details of all companies in the game. Usage 'companies'.");
1868  return true;
1869  }
1870 
1871  for (const Company *c : Company::Iterate()) {
1872  /* Grab the company name */
1873  SetDParam(0, c->index);
1874  std::string company_name = GetString(STR_COMPANY_NAME);
1875 
1876  std::string colour = GetString(STR_COLOUR_DARK_BLUE + _company_colours[c->index]);
1877  IConsolePrint(CC_INFO, "#:{}({}) Company Name: '{}' Year Founded: {} Money: {} Loan: {} Value: {} (T:{}, R:{}, P:{}, S:{}) {}",
1878  c->index + 1, colour, company_name,
1879  c->inaugurated_year, (int64_t)c->money, (int64_t)c->current_loan, (int64_t)CalculateCompanyValue(c),
1880  c->group_all[VEH_TRAIN].num_vehicle,
1881  c->group_all[VEH_ROAD].num_vehicle,
1882  c->group_all[VEH_AIRCRAFT].num_vehicle,
1883  c->group_all[VEH_SHIP].num_vehicle,
1884  c->is_ai ? "AI" : "");
1885  }
1886 
1887  return true;
1888 }
1889 
1890 DEF_CONSOLE_CMD(ConSay)
1891 {
1892  if (argc == 0) {
1893  IConsolePrint(CC_HELP, "Chat to your fellow players in a multiplayer game. Usage: 'say \"<msg>\"'.");
1894  return true;
1895  }
1896 
1897  if (argc != 2) return false;
1898 
1899  if (!_network_server) {
1900  NetworkClientSendChat(NETWORK_ACTION_CHAT, DESTTYPE_BROADCAST, 0 /* param does not matter */, argv[1]);
1901  } else {
1902  bool from_admin = (_redirect_console_to_admin < INVALID_ADMIN_ID);
1903  NetworkServerSendChat(NETWORK_ACTION_CHAT, DESTTYPE_BROADCAST, 0, argv[1], CLIENT_ID_SERVER, from_admin);
1904  }
1905 
1906  return true;
1907 }
1908 
1909 DEF_CONSOLE_CMD(ConSayCompany)
1910 {
1911  if (argc == 0) {
1912  IConsolePrint(CC_HELP, "Chat to a certain company in a multiplayer game. Usage: 'say_company <company-no> \"<msg>\"'.");
1913  IConsolePrint(CC_HELP, "CompanyNo is the company that plays as company <companyno>, 1 through max_companies.");
1914  return true;
1915  }
1916 
1917  if (argc != 3) return false;
1918 
1919  CompanyID company_id = (CompanyID)(atoi(argv[1]) - 1);
1920  if (!Company::IsValidID(company_id)) {
1921  IConsolePrint(CC_DEFAULT, "Unknown company. Company range is between 1 and {}.", MAX_COMPANIES);
1922  return true;
1923  }
1924 
1925  if (!_network_server) {
1926  NetworkClientSendChat(NETWORK_ACTION_CHAT_COMPANY, DESTTYPE_TEAM, company_id, argv[2]);
1927  } else {
1928  bool from_admin = (_redirect_console_to_admin < INVALID_ADMIN_ID);
1929  NetworkServerSendChat(NETWORK_ACTION_CHAT_COMPANY, DESTTYPE_TEAM, company_id, argv[2], CLIENT_ID_SERVER, from_admin);
1930  }
1931 
1932  return true;
1933 }
1934 
1935 DEF_CONSOLE_CMD(ConSayClient)
1936 {
1937  if (argc == 0) {
1938  IConsolePrint(CC_HELP, "Chat to a certain client in a multiplayer game. Usage: 'say_client <client-no> \"<msg>\"'.");
1939  IConsolePrint(CC_HELP, "For client-id's, see the command 'clients'.");
1940  return true;
1941  }
1942 
1943  if (argc != 3) return false;
1944 
1945  if (!_network_server) {
1946  NetworkClientSendChat(NETWORK_ACTION_CHAT_CLIENT, DESTTYPE_CLIENT, atoi(argv[1]), argv[2]);
1947  } else {
1948  bool from_admin = (_redirect_console_to_admin < INVALID_ADMIN_ID);
1949  NetworkServerSendChat(NETWORK_ACTION_CHAT_CLIENT, DESTTYPE_CLIENT, atoi(argv[1]), argv[2], CLIENT_ID_SERVER, from_admin);
1950  }
1951 
1952  return true;
1953 }
1954 
1956 static std::vector<std::pair<std::string_view, NetworkAuthorizedKeys *>> _console_cmd_authorized_keys{
1960 };
1961 
1962 enum ConNetworkAuthorizedKeyAction {
1963  CNAKA_LIST,
1964  CNAKA_ADD,
1965  CNAKA_REMOVE,
1966 };
1967 
1968 static void PerformNetworkAuthorizedKeyAction(std::string_view name, NetworkAuthorizedKeys *authorized_keys, ConNetworkAuthorizedKeyAction action, const std::string &authorized_key, CompanyID company = INVALID_COMPANY)
1969 {
1970  switch (action) {
1971  case CNAKA_LIST:
1972  IConsolePrint(CC_WHITE, "The authorized keys for {} are:", name);
1973  for (auto &ak : *authorized_keys) IConsolePrint(CC_INFO, " {}", ak);
1974  return;
1975 
1976  case CNAKA_ADD:
1977  if (authorized_keys->Contains(authorized_key)) {
1978  IConsolePrint(CC_WARNING, "Not added {} to {} as it already exists.", authorized_key, name);
1979  return;
1980  }
1981 
1982  if (company == INVALID_COMPANY) {
1983  authorized_keys->Add(authorized_key);
1984  } else {
1985  AutoRestoreBackup backup(_current_company, company);
1987  }
1988  IConsolePrint(CC_INFO, "Added {} to {}.", authorized_key, name);
1989  return;
1990 
1991  case CNAKA_REMOVE:
1992  if (!authorized_keys->Contains(authorized_key)) {
1993  IConsolePrint(CC_WARNING, "Not removed {} from {} as it does not exist.", authorized_key, name);
1994  return;
1995  }
1996 
1997  if (company == INVALID_COMPANY) {
1998  authorized_keys->Remove(authorized_key);
1999  } else {
2000  AutoRestoreBackup backup(_current_company, company);
2002  }
2003  IConsolePrint(CC_INFO, "Removed {} from {}.", authorized_key, name);
2004  return;
2005  }
2006 }
2007 
2008 DEF_CONSOLE_CMD(ConNetworkAuthorizedKey)
2009 {
2010  if (argc <= 2) {
2011  IConsolePrint(CC_HELP, "List and update authorized keys. Usage: 'authorized_key list [type]|add [type] [key]|remove [type] [key]'.");
2012  IConsolePrint(CC_HELP, " list: list all the authorized keys of the given type.");
2013  IConsolePrint(CC_HELP, " add: add the given key to the authorized keys of the given type.");
2014  IConsolePrint(CC_HELP, " remove: remove the given key from the authorized keys of the given type; use 'all' to remove all authorized keys.");
2015  IConsolePrint(CC_HELP, "Instead of a key, use 'client:<id>' to add/remove the key of that given client.");
2016 
2017  std::string buffer;
2018  for (auto [name, _] : _console_cmd_authorized_keys) fmt::format_to(std::back_inserter(buffer), ", {}", name);
2019  IConsolePrint(CC_HELP, "The supported types are: all{} and company:<id>.", buffer);
2020  return true;
2021  }
2022 
2023  ConNetworkAuthorizedKeyAction action;
2024  std::string_view action_string = argv[1];
2025  if (StrEqualsIgnoreCase(action_string, "list")) {
2026  action = CNAKA_LIST;
2027  } else if (StrEqualsIgnoreCase(action_string, "add")) {
2028  action = CNAKA_ADD;
2029  } else if (StrEqualsIgnoreCase(action_string, "remove") || StrEqualsIgnoreCase(action_string, "delete")) {
2030  action = CNAKA_REMOVE;
2031  } else {
2032  IConsolePrint(CC_WARNING, "No valid action was given.");
2033  return false;
2034  }
2035 
2036  std::string authorized_key;
2037  if (action != CNAKA_LIST) {
2038  if (argc <= 3) {
2039  IConsolePrint(CC_ERROR, "You must enter the key.");
2040  return false;
2041  }
2042 
2043  authorized_key = argv[3];
2044  if (StrStartsWithIgnoreCase(authorized_key, "client:")) {
2045  std::string id_string(authorized_key.substr(7));
2046  authorized_key = NetworkGetPublicKeyOfClient(static_cast<ClientID>(std::stoi(id_string)));
2047  if (authorized_key.empty()) {
2048  IConsolePrint(CC_ERROR, "You must enter a valid client id; see 'clients'.");
2049  return false;
2050  }
2051  }
2052 
2053  if (authorized_key.size() != NETWORK_PUBLIC_KEY_LENGTH - 1) {
2054  IConsolePrint(CC_ERROR, "You must enter a valid authorized key.");
2055  return false;
2056  }
2057  }
2058 
2059  std::string_view type = argv[2];
2060  if (StrEqualsIgnoreCase(type, "all")) {
2061  for (auto [name, authorized_keys] : _console_cmd_authorized_keys) PerformNetworkAuthorizedKeyAction(name, authorized_keys, action, authorized_key);
2062  for (Company *c : Company::Iterate()) PerformNetworkAuthorizedKeyAction(fmt::format("company:{}", c->index + 1), &c->allow_list, action, authorized_key, c->index);
2063  return true;
2064  }
2065 
2066  if (StrStartsWithIgnoreCase(type, "company:")) {
2067  std::string id_string(type.substr(8));
2068  Company *c = Company::GetIfValid(std::stoi(id_string) - 1);
2069  if (c == nullptr) {
2070  IConsolePrint(CC_ERROR, "You must enter a valid company id; see 'companies'.");
2071  return false;
2072  }
2073 
2074  PerformNetworkAuthorizedKeyAction(type, &c->allow_list, action, authorized_key, c->index);
2075  return true;
2076  }
2077 
2078  for (auto [name, authorized_keys] : _console_cmd_authorized_keys) {
2079  if (StrEqualsIgnoreCase(type, name)) continue;
2080 
2081  PerformNetworkAuthorizedKeyAction(name, authorized_keys, action, authorized_key);
2082  return true;
2083  }
2084 
2085  IConsolePrint(CC_WARNING, "No valid type was given.");
2086  return false;
2087 }
2088 
2089 
2090 /* Content downloading only is available with ZLIB */
2091 #if defined(WITH_ZLIB)
2092 #include "network/network_content.h"
2093 
2095 static ContentType StringToContentType(const char *str)
2096 {
2097  static const std::initializer_list<std::pair<std::string_view, ContentType>> content_types = {
2098  {"base", CONTENT_TYPE_BASE_GRAPHICS},
2099  {"newgrf", CONTENT_TYPE_NEWGRF},
2100  {"ai", CONTENT_TYPE_AI},
2101  {"ailib", CONTENT_TYPE_AI_LIBRARY},
2102  {"scenario", CONTENT_TYPE_SCENARIO},
2103  {"heightmap", CONTENT_TYPE_HEIGHTMAP},
2104  };
2105  for (const auto &ct : content_types) {
2106  if (StrEqualsIgnoreCase(str, ct.first)) return ct.second;
2107  }
2108  return CONTENT_TYPE_END;
2109 }
2110 
2113  void OnConnect(bool success) override
2114  {
2115  IConsolePrint(CC_DEFAULT, "Content server connection {}.", success ? "established" : "failed");
2116  }
2117 
2118  void OnDisconnect() override
2119  {
2120  IConsolePrint(CC_DEFAULT, "Content server connection closed.");
2121  }
2122 
2123  void OnDownloadComplete(ContentID cid) override
2124  {
2125  IConsolePrint(CC_DEFAULT, "Completed download of {}.", cid);
2126  }
2127 };
2128 
2133 static void OutputContentState(const ContentInfo *const ci)
2134 {
2135  static const char * const types[] = { "Base graphics", "NewGRF", "AI", "AI library", "Scenario", "Heightmap", "Base sound", "Base music", "Game script", "GS library" };
2136  static_assert(lengthof(types) == CONTENT_TYPE_END - CONTENT_TYPE_BEGIN);
2137  static const char * const states[] = { "Not selected", "Selected", "Dep Selected", "Installed", "Unknown" };
2138  static const TextColour state_to_colour[] = { CC_COMMAND, CC_INFO, CC_INFO, CC_WHITE, CC_ERROR };
2139 
2140  IConsolePrint(state_to_colour[ci->state], "{}, {}, {}, {}, {:08X}, {}", ci->id, types[ci->type - 1], states[ci->state], ci->name, ci->unique_id, FormatArrayAsHex(ci->md5sum));
2141 }
2142 
2143 DEF_CONSOLE_CMD(ConContent)
2144 {
2145  static ContentCallback *cb = nullptr;
2146  if (cb == nullptr) {
2147  cb = new ConsoleContentCallback();
2149  }
2150 
2151  if (argc <= 1) {
2152  IConsolePrint(CC_HELP, "Query, select and download content. Usage: 'content update|upgrade|select [id]|unselect [all|id]|state [filter]|download'.");
2153  IConsolePrint(CC_HELP, " update: get a new list of downloadable content; must be run first.");
2154  IConsolePrint(CC_HELP, " upgrade: select all items that are upgrades.");
2155  IConsolePrint(CC_HELP, " select: select a specific item given by its id. If no parameter is given, all selected content will be listed.");
2156  IConsolePrint(CC_HELP, " unselect: unselect a specific item given by its id or 'all' to unselect all.");
2157  IConsolePrint(CC_HELP, " state: show the download/select state of all downloadable content. Optionally give a filter string.");
2158  IConsolePrint(CC_HELP, " download: download all content you've selected.");
2159  return true;
2160  }
2161 
2162  if (StrEqualsIgnoreCase(argv[1], "update")) {
2164  return true;
2165  }
2166 
2167  if (StrEqualsIgnoreCase(argv[1], "upgrade")) {
2169  return true;
2170  }
2171 
2172  if (StrEqualsIgnoreCase(argv[1], "select")) {
2173  if (argc <= 2) {
2174  /* List selected content */
2175  IConsolePrint(CC_WHITE, "id, type, state, name");
2177  if ((*iter)->state != ContentInfo::SELECTED && (*iter)->state != ContentInfo::AUTOSELECTED) continue;
2178  OutputContentState(*iter);
2179  }
2180  } else if (StrEqualsIgnoreCase(argv[2], "all")) {
2181  /* The intention of this function was that you could download
2182  * everything after a filter was applied; but this never really
2183  * took off. Instead, a select few people used this functionality
2184  * to download every available package on BaNaNaS. This is not in
2185  * the spirit of this service. Additionally, these few people were
2186  * good for 70% of the consumed bandwidth of BaNaNaS. */
2187  IConsolePrint(CC_ERROR, "'select all' is no longer supported since 1.11.");
2188  } else {
2189  _network_content_client.Select((ContentID)atoi(argv[2]));
2190  }
2191  return true;
2192  }
2193 
2194  if (StrEqualsIgnoreCase(argv[1], "unselect")) {
2195  if (argc <= 2) {
2196  IConsolePrint(CC_ERROR, "You must enter the id.");
2197  return false;
2198  }
2199  if (StrEqualsIgnoreCase(argv[2], "all")) {
2201  } else {
2202  _network_content_client.Unselect((ContentID)atoi(argv[2]));
2203  }
2204  return true;
2205  }
2206 
2207  if (StrEqualsIgnoreCase(argv[1], "state")) {
2208  IConsolePrint(CC_WHITE, "id, type, state, name");
2210  if (argc > 2 && strcasestr((*iter)->name.c_str(), argv[2]) == nullptr) continue;
2211  OutputContentState(*iter);
2212  }
2213  return true;
2214  }
2215 
2216  if (StrEqualsIgnoreCase(argv[1], "download")) {
2217  uint files;
2218  uint bytes;
2220  IConsolePrint(CC_DEFAULT, "Downloading {} file(s) ({} bytes).", files, bytes);
2221  return true;
2222  }
2223 
2224  return false;
2225 }
2226 #endif /* defined(WITH_ZLIB) */
2227 
2228 DEF_CONSOLE_CMD(ConFont)
2229 {
2230  if (argc == 0) {
2231  IConsolePrint(CC_HELP, "Manage the fonts configuration.");
2232  IConsolePrint(CC_HELP, "Usage 'font'.");
2233  IConsolePrint(CC_HELP, " Print out the fonts configuration.");
2234  IConsolePrint(CC_HELP, " The \"Currently active\" configuration is the one actually in effect (after interface scaling and replacing unavailable fonts).");
2235  IConsolePrint(CC_HELP, " The \"Requested\" configuration is the one requested via console command or config file.");
2236  IConsolePrint(CC_HELP, "Usage 'font [medium|small|large|mono] [<font name>] [<size>]'.");
2237  IConsolePrint(CC_HELP, " Change the configuration for a font.");
2238  IConsolePrint(CC_HELP, " Omitting an argument will keep the current value.");
2239  IConsolePrint(CC_HELP, " Set <font name> to \"\" for the default font. Note that <size> has no effect if the default font is in use, and fixed defaults are used instead.");
2240  IConsolePrint(CC_HELP, " If the sprite font is enabled in Game Options, it is used instead of the default font.");
2241  IConsolePrint(CC_HELP, " The <size> is automatically multiplied by the current interface scaling.");
2242  return true;
2243  }
2244 
2245  FontSize argfs;
2246  for (argfs = FS_BEGIN; argfs < FS_END; argfs++) {
2247  if (argc > 1 && StrEqualsIgnoreCase(argv[1], FontSizeToName(argfs))) break;
2248  }
2249 
2250  /* First argument must be a FontSize. */
2251  if (argc > 1 && argfs == FS_END) return false;
2252 
2253  if (argc > 2) {
2254  FontCacheSubSetting *setting = GetFontCacheSubSetting(argfs);
2255  std::string font = setting->font;
2256  uint size = setting->size;
2257  uint v;
2258  uint8_t arg_index = 2;
2259  /* For <name> we want a string. */
2260 
2261  if (!GetArgumentInteger(&v, argv[arg_index])) {
2262  font = argv[arg_index++];
2263  }
2264 
2265  if (argc > arg_index) {
2266  /* For <size> we want a number. */
2267  if (GetArgumentInteger(&v, argv[arg_index])) {
2268  size = v;
2269  arg_index++;
2270  }
2271  }
2272 
2273  SetFont(argfs, font, size);
2274  }
2275 
2276  for (FontSize fs = FS_BEGIN; fs < FS_END; fs++) {
2277  FontCache *fc = FontCache::Get(fs);
2279  /* Make sure all non sprite fonts are loaded. */
2280  if (!setting->font.empty() && !fc->HasParent()) {
2281  InitFontCache(fs == FS_MONO);
2282  fc = FontCache::Get(fs);
2283  }
2284  IConsolePrint(CC_DEFAULT, "{} font:", FontSizeToName(fs));
2285  IConsolePrint(CC_DEFAULT, "Currently active: \"{}\", size {}", fc->GetFontName(), fc->GetFontSize());
2286  IConsolePrint(CC_DEFAULT, "Requested: \"{}\", size {}", setting->font, setting->size);
2287  }
2288 
2289  return true;
2290 }
2291 
2292 DEF_CONSOLE_CMD(ConSetting)
2293 {
2294  if (argc == 0) {
2295  IConsolePrint(CC_HELP, "Change setting for all clients. Usage: 'setting <name> [<value>]'.");
2296  IConsolePrint(CC_HELP, "Omitting <value> will print out the current value of the setting.");
2297  return true;
2298  }
2299 
2300  if (argc == 1 || argc > 3) return false;
2301 
2302  if (argc == 2) {
2303  IConsoleGetSetting(argv[1]);
2304  } else {
2305  IConsoleSetSetting(argv[1], argv[2]);
2306  }
2307 
2308  return true;
2309 }
2310 
2311 DEF_CONSOLE_CMD(ConSettingNewgame)
2312 {
2313  if (argc == 0) {
2314  IConsolePrint(CC_HELP, "Change setting for the next game. Usage: 'setting_newgame <name> [<value>]'.");
2315  IConsolePrint(CC_HELP, "Omitting <value> will print out the current value of the setting.");
2316  return true;
2317  }
2318 
2319  if (argc == 1 || argc > 3) return false;
2320 
2321  if (argc == 2) {
2322  IConsoleGetSetting(argv[1], true);
2323  } else {
2324  IConsoleSetSetting(argv[1], argv[2], true);
2325  }
2326 
2327  return true;
2328 }
2329 
2330 DEF_CONSOLE_CMD(ConListSettings)
2331 {
2332  if (argc == 0) {
2333  IConsolePrint(CC_HELP, "List settings. Usage: 'list_settings [<pre-filter>]'.");
2334  return true;
2335  }
2336 
2337  if (argc > 2) return false;
2338 
2339  IConsoleListSettings((argc == 2) ? argv[1] : nullptr);
2340  return true;
2341 }
2342 
2343 DEF_CONSOLE_CMD(ConGamelogPrint)
2344 {
2345  if (argc == 0) {
2346  IConsolePrint(CC_HELP, "Print logged fundamental changes to the game since the start. Usage: 'gamelog'.");
2347  return true;
2348  }
2349 
2351  return true;
2352 }
2353 
2354 DEF_CONSOLE_CMD(ConNewGRFReload)
2355 {
2356  if (argc == 0) {
2357  IConsolePrint(CC_HELP, "Reloads all active NewGRFs from disk. Equivalent to reapplying NewGRFs via the settings, but without asking for confirmation. This might crash OpenTTD!");
2358  return true;
2359  }
2360 
2361  ReloadNewGRFData();
2362  return true;
2363 }
2364 
2365 DEF_CONSOLE_CMD(ConListDirs)
2366 {
2367  struct SubdirNameMap {
2368  Subdirectory subdir;
2369  const char *name;
2370  bool default_only;
2371  };
2372  static const SubdirNameMap subdir_name_map[] = {
2373  /* Game data directories */
2374  { BASESET_DIR, "baseset", false },
2375  { NEWGRF_DIR, "newgrf", false },
2376  { AI_DIR, "ai", false },
2377  { AI_LIBRARY_DIR, "ailib", false },
2378  { GAME_DIR, "gs", false },
2379  { GAME_LIBRARY_DIR, "gslib", false },
2380  { SCENARIO_DIR, "scenario", false },
2381  { HEIGHTMAP_DIR, "heightmap", false },
2382  /* Default save locations for user data */
2383  { SAVE_DIR, "save", true },
2384  { AUTOSAVE_DIR, "autosave", true },
2385  { SCREENSHOT_DIR, "screenshot", true },
2386  { SOCIAL_INTEGRATION_DIR, "social_integration", true },
2387  };
2388 
2389  if (argc != 2) {
2390  IConsolePrint(CC_HELP, "List all search paths or default directories for various categories.");
2391  IConsolePrint(CC_HELP, "Usage: list_dirs <category>");
2392  std::string cats = subdir_name_map[0].name;
2393  bool first = true;
2394  for (const SubdirNameMap &sdn : subdir_name_map) {
2395  if (!first) cats = cats + ", " + sdn.name;
2396  first = false;
2397  }
2398  IConsolePrint(CC_HELP, "Valid categories: {}", cats);
2399  return true;
2400  }
2401 
2402  std::set<std::string> seen_dirs;
2403  for (const SubdirNameMap &sdn : subdir_name_map) {
2404  if (!StrEqualsIgnoreCase(argv[1], sdn.name)) continue;
2405  bool found = false;
2406  for (Searchpath sp : _valid_searchpaths) {
2407  /* Get the directory */
2408  std::string path = FioGetDirectory(sp, sdn.subdir);
2409  /* Check it hasn't already been listed */
2410  if (seen_dirs.find(path) != seen_dirs.end()) continue;
2411  seen_dirs.insert(path);
2412  /* Check if exists and mark found */
2413  bool exists = FileExists(path);
2414  found |= exists;
2415  /* Print */
2416  if (!sdn.default_only || exists) {
2417  IConsolePrint(exists ? CC_DEFAULT : CC_INFO, "{} {}", path, exists ? "[ok]" : "[not found]");
2418  if (sdn.default_only) break;
2419  }
2420  }
2421  if (!found) {
2422  IConsolePrint(CC_ERROR, "No directories exist for category {}", argv[1]);
2423  }
2424  return true;
2425  }
2426 
2427  IConsolePrint(CC_ERROR, "Invalid category name: {}", argv[1]);
2428  return false;
2429 }
2430 
2431 DEF_CONSOLE_CMD(ConNewGRFProfile)
2432 {
2433  if (argc == 0) {
2434  IConsolePrint(CC_HELP, "Collect performance data about NewGRF sprite requests and callbacks. Sub-commands can be abbreviated.");
2435  IConsolePrint(CC_HELP, "Usage: 'newgrf_profile [list]':");
2436  IConsolePrint(CC_HELP, " List all NewGRFs that can be profiled, and their status.");
2437  IConsolePrint(CC_HELP, "Usage: 'newgrf_profile select <grf-num>...':");
2438  IConsolePrint(CC_HELP, " Select one or more GRFs for profiling.");
2439  IConsolePrint(CC_HELP, "Usage: 'newgrf_profile unselect <grf-num>...':");
2440  IConsolePrint(CC_HELP, " Unselect one or more GRFs from profiling. Use the keyword \"all\" instead of a GRF number to unselect all. Removing an active profiler aborts data collection.");
2441  IConsolePrint(CC_HELP, "Usage: 'newgrf_profile start [<num-ticks>]':");
2442  IConsolePrint(CC_HELP, " Begin profiling all selected GRFs. If a number of ticks is provided, profiling stops after that many game ticks. There are 74 ticks in a calendar day.");
2443  IConsolePrint(CC_HELP, "Usage: 'newgrf_profile stop':");
2444  IConsolePrint(CC_HELP, " End profiling and write the collected data to CSV files.");
2445  IConsolePrint(CC_HELP, "Usage: 'newgrf_profile abort':");
2446  IConsolePrint(CC_HELP, " End profiling and discard all collected data.");
2447  return true;
2448  }
2449 
2450  const std::vector<GRFFile *> &files = GetAllGRFFiles();
2451 
2452  /* "list" sub-command */
2453  if (argc == 1 || StrStartsWithIgnoreCase(argv[1], "lis")) {
2454  IConsolePrint(CC_INFO, "Loaded GRF files:");
2455  int i = 1;
2456  for (GRFFile *grf : files) {
2457  auto profiler = std::find_if(_newgrf_profilers.begin(), _newgrf_profilers.end(), [&](NewGRFProfiler &pr) { return pr.grffile == grf; });
2458  bool selected = profiler != _newgrf_profilers.end();
2459  bool active = selected && profiler->active;
2460  TextColour tc = active ? TC_LIGHT_BLUE : selected ? TC_GREEN : CC_INFO;
2461  const char *statustext = active ? " (active)" : selected ? " (selected)" : "";
2462  IConsolePrint(tc, "{}: [{:08X}] {}{}", i, BSWAP32(grf->grfid), grf->filename, statustext);
2463  i++;
2464  }
2465  return true;
2466  }
2467 
2468  /* "select" sub-command */
2469  if (StrStartsWithIgnoreCase(argv[1], "sel") && argc >= 3) {
2470  for (size_t argnum = 2; argnum < argc; ++argnum) {
2471  int grfnum = atoi(argv[argnum]);
2472  if (grfnum < 1 || grfnum > (int)files.size()) { // safe cast, files.size() should not be larger than a few hundred in the most extreme cases
2473  IConsolePrint(CC_WARNING, "GRF number {} out of range, not added.", grfnum);
2474  continue;
2475  }
2476  GRFFile *grf = files[grfnum - 1];
2477  if (std::any_of(_newgrf_profilers.begin(), _newgrf_profilers.end(), [&](NewGRFProfiler &pr) { return pr.grffile == grf; })) {
2478  IConsolePrint(CC_WARNING, "GRF number {} [{:08X}] is already selected for profiling.", grfnum, BSWAP32(grf->grfid));
2479  continue;
2480  }
2481  _newgrf_profilers.emplace_back(grf);
2482  }
2483  return true;
2484  }
2485 
2486  /* "unselect" sub-command */
2487  if (StrStartsWithIgnoreCase(argv[1], "uns") && argc >= 3) {
2488  for (size_t argnum = 2; argnum < argc; ++argnum) {
2489  if (StrEqualsIgnoreCase(argv[argnum], "all")) {
2490  _newgrf_profilers.clear();
2491  break;
2492  }
2493  int grfnum = atoi(argv[argnum]);
2494  if (grfnum < 1 || grfnum > (int)files.size()) {
2495  IConsolePrint(CC_WARNING, "GRF number {} out of range, not removing.", grfnum);
2496  continue;
2497  }
2498  GRFFile *grf = files[grfnum - 1];
2499  auto pos = std::find_if(_newgrf_profilers.begin(), _newgrf_profilers.end(), [&](NewGRFProfiler &pr) { return pr.grffile == grf; });
2500  if (pos != _newgrf_profilers.end()) _newgrf_profilers.erase(pos);
2501  }
2502  return true;
2503  }
2504 
2505  /* "start" sub-command */
2506  if (StrStartsWithIgnoreCase(argv[1], "sta")) {
2507  std::string grfids;
2508  size_t started = 0;
2509  for (NewGRFProfiler &pr : _newgrf_profilers) {
2510  if (!pr.active) {
2511  pr.Start();
2512  started++;
2513 
2514  if (!grfids.empty()) grfids += ", ";
2515  fmt::format_to(std::back_inserter(grfids), "[{:08X}]", BSWAP32(pr.grffile->grfid));
2516  }
2517  }
2518  if (started > 0) {
2519  IConsolePrint(CC_DEBUG, "Started profiling for GRFID{} {}.", (started > 1) ? "s" : "", grfids);
2520 
2521  if (argc >= 3) {
2522  uint64_t ticks = std::max(atoi(argv[2]), 1);
2524  IConsolePrint(CC_DEBUG, "Profiling will automatically stop after {} ticks.", ticks);
2525  }
2526  } else if (_newgrf_profilers.empty()) {
2527  IConsolePrint(CC_ERROR, "No GRFs selected for profiling, did not start.");
2528  } else {
2529  IConsolePrint(CC_ERROR, "Did not start profiling for any GRFs, all selected GRFs are already profiling.");
2530  }
2531  return true;
2532  }
2533 
2534  /* "stop" sub-command */
2535  if (StrStartsWithIgnoreCase(argv[1], "sto")) {
2536  NewGRFProfiler::FinishAll();
2537  return true;
2538  }
2539 
2540  /* "abort" sub-command */
2541  if (StrStartsWithIgnoreCase(argv[1], "abo")) {
2542  for (NewGRFProfiler &pr : _newgrf_profilers) {
2543  pr.Abort();
2544  }
2546  return true;
2547  }
2548 
2549  return false;
2550 }
2551 
2552 #ifdef _DEBUG
2553 /******************
2554  * debug commands
2555  ******************/
2556 
2557 static void IConsoleDebugLibRegister()
2558 {
2559  IConsole::CmdRegister("resettile", ConResetTile);
2560  IConsole::AliasRegister("dbg_echo", "echo %A; echo %B");
2561  IConsole::AliasRegister("dbg_echo2", "echo %!");
2562 }
2563 #endif
2564 
2565 DEF_CONSOLE_CMD(ConFramerate)
2566 {
2567  if (argc == 0) {
2568  IConsolePrint(CC_HELP, "Show frame rate and game speed information.");
2569  return true;
2570  }
2571 
2573  return true;
2574 }
2575 
2576 DEF_CONSOLE_CMD(ConFramerateWindow)
2577 {
2578  if (argc == 0) {
2579  IConsolePrint(CC_HELP, "Open the frame rate window.");
2580  return true;
2581  }
2582 
2583  if (_network_dedicated) {
2584  IConsolePrint(CC_ERROR, "Can not open frame rate window on a dedicated server.");
2585  return false;
2586  }
2587 
2589  return true;
2590 }
2591 
2599 static std::string FormatLabel(uint32_t label)
2600 {
2601  if (std::isgraph(GB(label, 24, 8)) && std::isgraph(GB(label, 16, 8)) && std::isgraph(GB(label, 8, 8)) && std::isgraph(GB(label, 0, 8))) {
2602  return fmt::format("{:c}{:c}{:c}{:c}", GB(label, 24, 8), GB(label, 16, 8), GB(label, 8, 8), GB(label, 0, 8));
2603  }
2604 
2605  return fmt::format("{:08X}", BSWAP32(label));
2606 }
2607 
2608 static void ConDumpRoadTypes()
2609 {
2610  IConsolePrint(CC_DEFAULT, " Flags:");
2611  IConsolePrint(CC_DEFAULT, " c = catenary");
2612  IConsolePrint(CC_DEFAULT, " l = no level crossings");
2613  IConsolePrint(CC_DEFAULT, " X = no houses");
2614  IConsolePrint(CC_DEFAULT, " h = hidden");
2615  IConsolePrint(CC_DEFAULT, " T = buildable by towns");
2616 
2617  std::map<uint32_t, const GRFFile *> grfs;
2618  for (RoadType rt = ROADTYPE_BEGIN; rt < ROADTYPE_END; rt++) {
2619  const RoadTypeInfo *rti = GetRoadTypeInfo(rt);
2620  if (rti->label == 0) continue;
2621  uint32_t grfid = 0;
2622  const GRFFile *grf = rti->grffile[ROTSG_GROUND];
2623  if (grf != nullptr) {
2624  grfid = grf->grfid;
2625  grfs.emplace(grfid, grf);
2626  }
2627  IConsolePrint(CC_DEFAULT, " {:02d} {} {}, Flags: {}{}{}{}{}, GRF: {:08X}, {}",
2628  (uint)rt,
2629  RoadTypeIsTram(rt) ? "Tram" : "Road",
2630  FormatLabel(rti->label),
2631  HasBit(rti->flags, ROTF_CATENARY) ? 'c' : '-',
2632  HasBit(rti->flags, ROTF_NO_LEVEL_CROSSING) ? 'l' : '-',
2633  HasBit(rti->flags, ROTF_NO_HOUSES) ? 'X' : '-',
2634  HasBit(rti->flags, ROTF_HIDDEN) ? 'h' : '-',
2635  HasBit(rti->flags, ROTF_TOWN_BUILD) ? 'T' : '-',
2636  BSWAP32(grfid),
2637  GetStringPtr(rti->strings.name)
2638  );
2639  }
2640  for (const auto &grf : grfs) {
2641  IConsolePrint(CC_DEFAULT, " GRF: {:08X} = {}", BSWAP32(grf.first), grf.second->filename);
2642  }
2643 }
2644 
2645 static void ConDumpRailTypes()
2646 {
2647  IConsolePrint(CC_DEFAULT, " Flags:");
2648  IConsolePrint(CC_DEFAULT, " c = catenary");
2649  IConsolePrint(CC_DEFAULT, " l = no level crossings");
2650  IConsolePrint(CC_DEFAULT, " h = hidden");
2651  IConsolePrint(CC_DEFAULT, " s = no sprite combine");
2652  IConsolePrint(CC_DEFAULT, " a = always allow 90 degree turns");
2653  IConsolePrint(CC_DEFAULT, " d = always disallow 90 degree turns");
2654 
2655  std::map<uint32_t, const GRFFile *> grfs;
2656  for (RailType rt = RAILTYPE_BEGIN; rt < RAILTYPE_END; rt++) {
2657  const RailTypeInfo *rti = GetRailTypeInfo(rt);
2658  if (rti->label == 0) continue;
2659  uint32_t grfid = 0;
2660  const GRFFile *grf = rti->grffile[RTSG_GROUND];
2661  if (grf != nullptr) {
2662  grfid = grf->grfid;
2663  grfs.emplace(grfid, grf);
2664  }
2665  IConsolePrint(CC_DEFAULT, " {:02d} {}, Flags: {}{}{}{}{}{}, GRF: {:08X}, {}",
2666  (uint)rt,
2667  FormatLabel(rti->label),
2668  HasBit(rti->flags, RTF_CATENARY) ? 'c' : '-',
2669  HasBit(rti->flags, RTF_NO_LEVEL_CROSSING) ? 'l' : '-',
2670  HasBit(rti->flags, RTF_HIDDEN) ? 'h' : '-',
2671  HasBit(rti->flags, RTF_NO_SPRITE_COMBINE) ? 's' : '-',
2672  HasBit(rti->flags, RTF_ALLOW_90DEG) ? 'a' : '-',
2673  HasBit(rti->flags, RTF_DISALLOW_90DEG) ? 'd' : '-',
2674  BSWAP32(grfid),
2675  GetStringPtr(rti->strings.name)
2676  );
2677  }
2678  for (const auto &grf : grfs) {
2679  IConsolePrint(CC_DEFAULT, " GRF: {:08X} = {}", BSWAP32(grf.first), grf.second->filename);
2680  }
2681 }
2682 
2683 static void ConDumpCargoTypes()
2684 {
2685  IConsolePrint(CC_DEFAULT, " Cargo classes:");
2686  IConsolePrint(CC_DEFAULT, " p = passenger");
2687  IConsolePrint(CC_DEFAULT, " m = mail");
2688  IConsolePrint(CC_DEFAULT, " x = express");
2689  IConsolePrint(CC_DEFAULT, " a = armoured");
2690  IConsolePrint(CC_DEFAULT, " b = bulk");
2691  IConsolePrint(CC_DEFAULT, " g = piece goods");
2692  IConsolePrint(CC_DEFAULT, " l = liquid");
2693  IConsolePrint(CC_DEFAULT, " r = refrigerated");
2694  IConsolePrint(CC_DEFAULT, " h = hazardous");
2695  IConsolePrint(CC_DEFAULT, " c = covered/sheltered");
2696  IConsolePrint(CC_DEFAULT, " S = special");
2697 
2698  std::map<uint32_t, const GRFFile *> grfs;
2699  for (const CargoSpec *spec : CargoSpec::Iterate()) {
2700  if (!spec->IsValid()) continue;
2701  uint32_t grfid = 0;
2702  const GRFFile *grf = spec->grffile;
2703  if (grf != nullptr) {
2704  grfid = grf->grfid;
2705  grfs.emplace(grfid, grf);
2706  }
2707  IConsolePrint(CC_DEFAULT, " {:02d} Bit: {:2d}, Label: {}, Callback mask: 0x{:02X}, Cargo class: {}{}{}{}{}{}{}{}{}{}{}, GRF: {:08X}, {}",
2708  spec->Index(),
2709  spec->bitnum,
2710  FormatLabel(spec->label.base()),
2711  spec->callback_mask,
2712  (spec->classes & CC_PASSENGERS) != 0 ? 'p' : '-',
2713  (spec->classes & CC_MAIL) != 0 ? 'm' : '-',
2714  (spec->classes & CC_EXPRESS) != 0 ? 'x' : '-',
2715  (spec->classes & CC_ARMOURED) != 0 ? 'a' : '-',
2716  (spec->classes & CC_BULK) != 0 ? 'b' : '-',
2717  (spec->classes & CC_PIECE_GOODS) != 0 ? 'g' : '-',
2718  (spec->classes & CC_LIQUID) != 0 ? 'l' : '-',
2719  (spec->classes & CC_REFRIGERATED) != 0 ? 'r' : '-',
2720  (spec->classes & CC_HAZARDOUS) != 0 ? 'h' : '-',
2721  (spec->classes & CC_COVERED) != 0 ? 'c' : '-',
2722  (spec->classes & CC_SPECIAL) != 0 ? 'S' : '-',
2723  BSWAP32(grfid),
2724  GetStringPtr(spec->name)
2725  );
2726  }
2727  for (const auto &grf : grfs) {
2728  IConsolePrint(CC_DEFAULT, " GRF: {:08X} = {}", BSWAP32(grf.first), grf.second->filename);
2729  }
2730 }
2731 
2732 
2733 DEF_CONSOLE_CMD(ConDumpInfo)
2734 {
2735  if (argc != 2) {
2736  IConsolePrint(CC_HELP, "Dump debugging information.");
2737  IConsolePrint(CC_HELP, "Usage: 'dump_info roadtypes|railtypes|cargotypes'.");
2738  IConsolePrint(CC_HELP, " Show information about road/tram types, rail types or cargo types.");
2739  return true;
2740  }
2741 
2742  if (StrEqualsIgnoreCase(argv[1], "roadtypes")) {
2743  ConDumpRoadTypes();
2744  return true;
2745  }
2746 
2747  if (StrEqualsIgnoreCase(argv[1], "railtypes")) {
2748  ConDumpRailTypes();
2749  return true;
2750  }
2751 
2752  if (StrEqualsIgnoreCase(argv[1], "cargotypes")) {
2753  ConDumpCargoTypes();
2754  return true;
2755  }
2756 
2757  return false;
2758 }
2759 
2760 /*******************************
2761  * console command registration
2762  *******************************/
2763 
2764 void IConsoleStdLibRegister()
2765 {
2766  IConsole::CmdRegister("debug_level", ConDebugLevel);
2767  IConsole::CmdRegister("echo", ConEcho);
2768  IConsole::CmdRegister("echoc", ConEchoC);
2769  IConsole::CmdRegister("exec", ConExec);
2770  IConsole::CmdRegister("schedule", ConSchedule);
2771  IConsole::CmdRegister("exit", ConExit);
2772  IConsole::CmdRegister("part", ConPart);
2773  IConsole::CmdRegister("help", ConHelp);
2774  IConsole::CmdRegister("info_cmd", ConInfoCmd);
2775  IConsole::CmdRegister("list_cmds", ConListCommands);
2776  IConsole::CmdRegister("list_aliases", ConListAliases);
2777  IConsole::CmdRegister("newgame", ConNewGame);
2778  IConsole::CmdRegister("restart", ConRestart);
2779  IConsole::CmdRegister("reload", ConReload);
2780  IConsole::CmdRegister("getseed", ConGetSeed);
2781  IConsole::CmdRegister("getdate", ConGetDate);
2782  IConsole::CmdRegister("getsysdate", ConGetSysDate);
2783  IConsole::CmdRegister("quit", ConExit);
2784  IConsole::CmdRegister("resetengines", ConResetEngines, ConHookNoNetwork);
2785  IConsole::CmdRegister("reset_enginepool", ConResetEnginePool, ConHookNoNetwork);
2786  IConsole::CmdRegister("return", ConReturn);
2787  IConsole::CmdRegister("screenshot", ConScreenShot);
2788  IConsole::CmdRegister("script", ConScript);
2789  IConsole::CmdRegister("zoomto", ConZoomToLevel);
2790  IConsole::CmdRegister("scrollto", ConScrollToTile);
2791  IConsole::CmdRegister("alias", ConAlias);
2792  IConsole::CmdRegister("load", ConLoad);
2793  IConsole::CmdRegister("load_save", ConLoad);
2794  IConsole::CmdRegister("load_scenario", ConLoadScenario);
2795  IConsole::CmdRegister("load_heightmap", ConLoadHeightmap);
2796  IConsole::CmdRegister("rm", ConRemove);
2797  IConsole::CmdRegister("save", ConSave);
2798  IConsole::CmdRegister("saveconfig", ConSaveConfig);
2799  IConsole::CmdRegister("ls", ConListFiles);
2800  IConsole::CmdRegister("list_saves", ConListFiles);
2801  IConsole::CmdRegister("list_scenarios", ConListScenarios);
2802  IConsole::CmdRegister("list_heightmaps", ConListHeightmaps);
2803  IConsole::CmdRegister("cd", ConChangeDirectory);
2804  IConsole::CmdRegister("pwd", ConPrintWorkingDirectory);
2805  IConsole::CmdRegister("clear", ConClearBuffer);
2806  IConsole::CmdRegister("font", ConFont);
2807  IConsole::CmdRegister("setting", ConSetting);
2808  IConsole::CmdRegister("setting_newgame", ConSettingNewgame);
2809  IConsole::CmdRegister("list_settings", ConListSettings);
2810  IConsole::CmdRegister("gamelog", ConGamelogPrint);
2811  IConsole::CmdRegister("rescan_newgrf", ConRescanNewGRF);
2812  IConsole::CmdRegister("list_dirs", ConListDirs);
2813 
2814  IConsole::AliasRegister("dir", "ls");
2815  IConsole::AliasRegister("del", "rm %+");
2816  IConsole::AliasRegister("newmap", "newgame");
2817  IConsole::AliasRegister("patch", "setting %+");
2818  IConsole::AliasRegister("set", "setting %+");
2819  IConsole::AliasRegister("set_newgame", "setting_newgame %+");
2820  IConsole::AliasRegister("list_patches", "list_settings %+");
2821  IConsole::AliasRegister("developer", "setting developer %+");
2822 
2823  IConsole::CmdRegister("list_ai_libs", ConListAILibs);
2824  IConsole::CmdRegister("list_ai", ConListAI);
2825  IConsole::CmdRegister("reload_ai", ConReloadAI);
2826  IConsole::CmdRegister("rescan_ai", ConRescanAI);
2827  IConsole::CmdRegister("start_ai", ConStartAI);
2828  IConsole::CmdRegister("stop_ai", ConStopAI);
2829 
2830  IConsole::CmdRegister("list_game", ConListGame);
2831  IConsole::CmdRegister("list_game_libs", ConListGameLibs);
2832  IConsole::CmdRegister("rescan_game", ConRescanGame);
2833 
2834  IConsole::CmdRegister("companies", ConCompanies);
2835  IConsole::AliasRegister("players", "companies");
2836 
2837  /* networking functions */
2838 
2839 /* Content downloading is only available with ZLIB */
2840 #if defined(WITH_ZLIB)
2841  IConsole::CmdRegister("content", ConContent);
2842 #endif /* defined(WITH_ZLIB) */
2843 
2844  /*** Networking commands ***/
2845  IConsole::CmdRegister("say", ConSay, ConHookNeedNetwork);
2846  IConsole::CmdRegister("say_company", ConSayCompany, ConHookNeedNetwork);
2847  IConsole::AliasRegister("say_player", "say_company %+");
2848  IConsole::CmdRegister("say_client", ConSayClient, ConHookNeedNetwork);
2849 
2850  IConsole::CmdRegister("connect", ConNetworkConnect, ConHookClientOnly);
2851  IConsole::CmdRegister("clients", ConNetworkClients, ConHookNeedNetwork);
2852  IConsole::CmdRegister("status", ConStatus, ConHookServerOnly);
2853  IConsole::CmdRegister("server_info", ConServerInfo, ConHookServerOnly);
2854  IConsole::AliasRegister("info", "server_info");
2855  IConsole::CmdRegister("reconnect", ConNetworkReconnect, ConHookClientOnly);
2856  IConsole::CmdRegister("rcon", ConRcon, ConHookNeedNetwork);
2857 
2858  IConsole::CmdRegister("join", ConJoinCompany, ConHookNeedNonDedicatedNetwork);
2859  IConsole::AliasRegister("spectate", "join 255");
2860  IConsole::CmdRegister("move", ConMoveClient, ConHookServerOnly);
2861  IConsole::CmdRegister("reset_company", ConResetCompany, ConHookServerOnly);
2862  IConsole::AliasRegister("clean_company", "reset_company %A");
2863  IConsole::CmdRegister("client_name", ConClientNickChange, ConHookServerOnly);
2864  IConsole::CmdRegister("kick", ConKick, ConHookServerOnly);
2865  IConsole::CmdRegister("ban", ConBan, ConHookServerOnly);
2866  IConsole::CmdRegister("unban", ConUnBan, ConHookServerOnly);
2867  IConsole::CmdRegister("banlist", ConBanList, ConHookServerOnly);
2868 
2869  IConsole::CmdRegister("pause", ConPauseGame, ConHookServerOrNoNetwork);
2870  IConsole::CmdRegister("unpause", ConUnpauseGame, ConHookServerOrNoNetwork);
2871 
2872  IConsole::CmdRegister("authorized_key", ConNetworkAuthorizedKey, ConHookServerOnly);
2873  IConsole::AliasRegister("ak", "authorized_key %+");
2874 
2875  IConsole::AliasRegister("net_frame_freq", "setting frame_freq %+");
2876  IConsole::AliasRegister("net_sync_freq", "setting sync_freq %+");
2877  IConsole::AliasRegister("server_pw", "setting server_password %+");
2878  IConsole::AliasRegister("server_password", "setting server_password %+");
2879  IConsole::AliasRegister("rcon_pw", "setting rcon_password %+");
2880  IConsole::AliasRegister("rcon_password", "setting rcon_password %+");
2881  IConsole::AliasRegister("name", "setting client_name %+");
2882  IConsole::AliasRegister("server_name", "setting server_name %+");
2883  IConsole::AliasRegister("server_port", "setting server_port %+");
2884  IConsole::AliasRegister("max_clients", "setting max_clients %+");
2885  IConsole::AliasRegister("max_companies", "setting max_companies %+");
2886  IConsole::AliasRegister("max_join_time", "setting max_join_time %+");
2887  IConsole::AliasRegister("pause_on_join", "setting pause_on_join %+");
2888  IConsole::AliasRegister("autoclean_companies", "setting autoclean_companies %+");
2889  IConsole::AliasRegister("autoclean_protected", "setting autoclean_protected %+");
2890  IConsole::AliasRegister("restart_game_year", "setting restart_game_year %+");
2891  IConsole::AliasRegister("min_players", "setting min_active_clients %+");
2892  IConsole::AliasRegister("reload_cfg", "setting reload_cfg %+");
2893 
2894  /* debugging stuff */
2895 #ifdef _DEBUG
2896  IConsoleDebugLibRegister();
2897 #endif
2898  IConsole::CmdRegister("fps", ConFramerate);
2899  IConsole::CmdRegister("fps_wnd", ConFramerateWindow);
2900 
2901  /* NewGRF development stuff */
2902  IConsole::CmdRegister("reload_newgrfs", ConNewGRFReload, ConHookNewGRFDeveloperTool);
2903  IConsole::CmdRegister("newgrf_profile", ConNewGRFProfile, ConHookNewGRFDeveloperTool);
2904 
2905  IConsole::CmdRegister("dump_info", ConDumpInfo);
2906 }
Base functions for all AIs.
AIConfig stores the configuration settings of every AI.
constexpr debug_inline bool HasBit(const T x, const uint8_t y)
Checks if a bit in a value is set.
static uint32_t BSWAP32(uint32_t x)
Perform a 32 bits endianness bitswap on x.
constexpr static debug_inline uint GB(const T x, const uint8_t s, const uint8_t n)
Fetch n bits from x, started at bit s.
@ CC_MAIL
Mail.
Definition: cargotype.h:51
@ CC_REFRIGERATED
Refrigerated cargo (Food, Fruit)
Definition: cargotype.h:57
@ CC_ARMOURED
Armoured cargo (Valuables, Gold, Diamonds)
Definition: cargotype.h:53
@ CC_BULK
Bulk cargo (Coal, Grain etc., Ores, Fruit)
Definition: cargotype.h:54
@ CC_EXPRESS
Express cargo (Goods, Food, Candy, but also possible for passengers)
Definition: cargotype.h:52
@ CC_COVERED
Covered/Sheltered Freight (Transportation in Box Vans, Silo Wagons, etc.)
Definition: cargotype.h:59
@ CC_LIQUID
Liquids (Oil, Water, Rubber)
Definition: cargotype.h:56
@ CC_PIECE_GOODS
Piece goods (Livestock, Wood, Steel, Paper)
Definition: cargotype.h:55
@ CC_PASSENGERS
Passengers.
Definition: cargotype.h:50
@ CC_HAZARDOUS
Hazardous cargo (Nuclear Fuel, Explosives, etc.)
Definition: cargotype.h:58
@ CC_SPECIAL
Special bit used for livery refit tricks instead of normal cargoes.
Definition: cargotype.h:60
static AIConfig * GetConfig(CompanyID company, ScriptSettingSource source=SSS_DEFAULT)
Get the config of a company.
Definition: ai_config.cpp:20
static void GetConsoleLibraryList(std::back_insert_iterator< std::string > &output_iterator)
Wrapper function for AIScanner::GetAIConsoleLibraryList.
Definition: ai_core.cpp:301
static void GetConsoleList(std::back_insert_iterator< std::string > &output_iterator, bool newest_only)
Wrapper function for AIScanner::GetAIConsoleList.
Definition: ai_core.cpp:296
static bool CanStartNew()
Is it possible to start a new AI company?
Definition: ai_core.cpp:30
static void Rescan()
Rescans all searchpaths for available AIs.
Definition: ai_core.cpp:326
ConstContentIterator Begin() const
Get the begin of the content inf iterator.
void SelectUpgrade()
Select everything that's an update for something we've got.
void DownloadSelectedContent(uint &files, uint &bytes, bool fallback=false)
Actually begin downloading the content we selected.
void Select(ContentID cid)
Select a specific content id.
void UnselectAll()
Unselect everything that we've not downloaded so far.
void RequestContentList(ContentType type)
Request the content list for the given type.
void AddCallback(ContentCallback *cb)
Add a callback to this class.
ConstContentIterator End() const
Get the end of the content inf iterator.
void Unselect(ContentID cid)
Unselect a specific content id.
static bool IsConnected()
Check whether the client is actually connected (and in the game).
File list storage for the console, for caching the last 'ls' command.
void ValidateFileList(bool force_reload=false)
(Re-)validate the file storage cache.
bool file_list_valid
If set, the file list is valid.
AbstractFileType abstract_filetype
The abstract file type to list.
void InvalidateFileList()
Declare the file storage cache as being invalid, also clears all stored files.
bool show_dirs
Whether to show directories in the file list.
static std::optional< FileHandle > Open(const std::string &filename, const std::string &mode)
Open an RAII file handle if possible.
Definition: fileio.cpp:1170
List of file information.
Definition: fios.h:88
const FiosItem * FindItem(const std::string_view file)
Find file information of a file by its name from the file list.
Definition: fios.cpp:102
void BuildFileList(AbstractFileType abstract_filetype, SaveLoadOperation fop, bool show_dirs)
Construct a file list with the given kind of files, for the stated purpose.
Definition: fios.cpp:66
Font cache for basic fonts.
Definition: fontcache.h:21
virtual std::string GetFontName()=0
Get the name of this font.
bool HasParent()
Check whether the font cache has a parent.
Definition: fontcache.h:140
virtual int GetFontSize() const
Get the nominal font size of the font.
Definition: fontcache.h:66
static FontCache * Get(FontSize fs)
Get the font cache of a given font size.
Definition: fontcache.h:129
static void GetConsoleList(std::back_insert_iterator< std::string > &output_iterator, bool newest_only)
Wrapper function for GameScanner::GetConsoleList.
Definition: game_core.cpp:217
static void GetConsoleLibraryList(std::back_insert_iterator< std::string > &output_iterator)
Wrapper function for GameScanner::GetConsoleLibraryList.
Definition: game_core.cpp:222
void PrintConsole()
Print the gamelog data to the console.
Definition: gamelog.cpp:310
Simple helper to (more easily) manage authorized keys.
Definition: network_type.h:148
This struct contains all the info that is needed to draw and construct tracks.
Definition: rail.h:127
struct RailTypeInfo::@26 strings
Strings associated with the rail type.
RailTypeLabel label
Unique 32 bit rail type identifier.
Definition: rail.h:236
StringID name
Name of this rail type.
Definition: rail.h:176
RailTypeFlags flags
Bit mask of rail type flags.
Definition: rail.h:211
const GRFFile * grffile[RTSG_END]
NewGRF providing the Action3 for the railtype.
Definition: rail.h:276
RoadTypeLabel label
Unique 32 bit road type identifier.
Definition: road.h:147
RoadTypeFlags flags
Bit mask of road type flags.
Definition: road.h:127
struct RoadTypeInfo::@29 strings
Strings associated with the rail type.
const GRFFile * grffile[ROTSG_END]
NewGRF providing the Action3 for the roadtype.
Definition: road.h:187
StringID name
Name of this rail type.
Definition: road.h:103
bool HasScript() const
Is this config attached to an Script? In other words, is there a Script that is assigned to this slot...
void Change(std::optional< const std::string > name, int version=-1, bool force_exact_match=false)
Set another Script to be loaded in this slot.
void StringToSettings(const std::string &value)
Convert a string which is stored in the config file or savegames to custom settings of this Script.
static YearMonthDay ConvertDateToYMD(Date date)
Converts a Date to a Year, Month & Day.
static Date date
Current date in days (day counter).
Functions related to commands.
Money CalculateCompanyValue(const Company *c, bool including_loan=true)
Calculate the value of the company.
Definition: economy.cpp:149
Colours _company_colours[MAX_COMPANIES]
NOSAVE: can be determined from company structs.
Definition: company_cmd.cpp:54
CompanyID _local_company
Company controlled by the human player at this client. Can also be COMPANY_SPECTATOR.
Definition: company_cmd.cpp:52
CompanyID _current_company
Company currently doing an action.
Definition: company_cmd.cpp:53
Command definitions related to companies.
Functions related to companies.
@ CCA_NEW_AI
Create a new AI company.
Definition: company_type.h:69
@ CCA_DELETE
Delete a company.
Definition: company_type.h:70
@ CALCA_REMOVE
Remove a public key.
Definition: company_type.h:78
@ CALCA_ADD
Create a public key.
Definition: company_type.h:77
Owner
Enum for all companies/owners.
Definition: company_type.h:18
@ INVALID_COMPANY
An invalid company.
Definition: company_type.h:30
@ COMPANY_SPECTATOR
The client is spectating.
Definition: company_type.h:35
@ COMPANY_NEW_COMPANY
The client wants a new company.
Definition: company_type.h:34
@ MAX_COMPANIES
Maximum number of companies.
Definition: company_type.h:23
@ CRR_NONE
Dummy reason for actions that don't need one.
Definition: company_type.h:63
@ CRR_MANUAL
The company is manually removed.
Definition: company_type.h:57
static const uint NETWORK_PUBLIC_KEY_LENGTH
The maximum length of the hexadecimal encoded public keys, in bytes including '\0'.
Definition: config.h:101
bool GetArgumentInteger(uint32_t *value, const char *arg)
Change a string into its number representation.
Definition: console.cpp:127
void IConsoleCmdExec(const std::string &command_string, const uint recurse_count)
Execute a given command passed to us.
Definition: console.cpp:291
void IConsolePrint(TextColour colour_code, const std::string &string)
Handle the printing of text entered into the console or redirected there by any other means.
Definition: console.cpp:89
static std::vector< std::pair< std::string_view, NetworkAuthorizedKeys * > > _console_cmd_authorized_keys
All the known authorized keys with their name.
static std::string _scheduled_monthly_script
Script scheduled to execute by the 'schedule' console command (empty if no script is scheduled).
static void OutputContentState(const ContentInfo *const ci)
Outputs content state information to console.
void ShowFramerateWindow()
Open the general framerate window.
DEF_CONSOLE_CMD(ConResetEngines)
Reset status of all engines.
static IntervalTimer< TimerGameCalendar > _scheduled_monthly_timer
Timer that runs every month of game time for the 'schedule' console command.
static ConsoleFileList _console_file_list_scenario
File storage cache for scenarios.
static ConsoleFileList _console_file_list_heightmap
File storage cache for heightmaps.
DEF_CONSOLE_HOOK(ConHookServerOnly)
Check whether we are a server.
static std::string FormatLabel(uint32_t label)
Format a label as a string.
void ConPrintFramerate()
Print performance statistics to game console.
static uint _script_current_depth
Depth of scripts running (used to abort execution when #ConReturn is encountered).
static ContentType StringToContentType(const char *str)
Resolve a string to a content type.
static bool NetworkAvailable(bool echo)
Check network availability and inform in console about failure of detection.
static ConsoleFileList _console_file_list_savegame
File storage cache for savegames.
static void PrintLineByLine(const std::string &full_string)
Print a text buffer line by line to the console.
Console functions used outside of the console code.
void IConsoleClose()
Close the in-game console.
Internally used functions for the console.
@ CHR_HIDE
Hide the existence of the command.
@ CHR_DISALLOW
Disallow command execution.
@ CHR_ALLOW
Allow command execution.
static const uint ICON_CMDLN_SIZE
maximum length of a typed in command
static const TextColour CC_HELP
Colour for help lines.
Definition: console_type.h:26
static const TextColour CC_WHITE
White console lines for various things such as the welcome.
Definition: console_type.h:30
static const TextColour CC_INFO
Colour for information lines.
Definition: console_type.h:27
static const TextColour CC_COMMAND
Colour for the console's commands.
Definition: console_type.h:29
static const TextColour CC_WARNING
Colour for warning lines.
Definition: console_type.h:25
static const TextColour CC_DEBUG
Colour for debug output.
Definition: console_type.h:28
static const TextColour CC_DEFAULT
Default colour of the console.
Definition: console_type.h:23
static const TextColour CC_ERROR
Colour for error lines.
Definition: console_type.h:24
void SetDebugString(const char *s, void(*error_func)(const std::string &))
Set debugging levels by parsing the text in s.
Definition: debug.cpp:143
std::string GetDebugString()
Print out the current debug-level.
Definition: debug.cpp:206
Functions related to debugging.
void StartupEngines()
Start/initialise all our engines.
Definition: engine.cpp:763
Base class for engines.
Functions related to engines.
bool FioRemove(const std::string &filename)
Remove a file.
Definition: fileio.cpp:328
bool FioCheckFileExists(const std::string &filename, Subdirectory subdir)
Check whether the given file exists.
Definition: fileio.cpp:121
bool FileExists(const std::string &filename)
Test whether the given filename exists.
Definition: fileio.cpp:132
std::optional< FileHandle > FioFOpenFile(const std::string &filename, const char *mode, Subdirectory subdir, size_t *filesize)
Opens a OpenTTD file somewhere in a personal or global directory.
Definition: fileio.cpp:242
Functions for Standard In/Out file operations.
AbstractFileType GetAbstractFileType(FiosType fios_type)
Extract the abstract file type from a FiosType.
Definition: fileio_type.h:97
AbstractFileType
The different abstract types of files that the system knows about.
Definition: fileio_type.h:16
@ FT_SCENARIO
old or new scenario
Definition: fileio_type.h:19
@ FT_HEIGHTMAP
heightmap file
Definition: fileio_type.h:20
@ FT_NONE
nothing to do
Definition: fileio_type.h:17
@ FT_SAVEGAME
old or new savegame
Definition: fileio_type.h:18
@ FT_INVALID
Invalid or unknown file type.
Definition: fileio_type.h:23
@ SLO_SAVE
File is being saved.
Definition: fileio_type.h:56
@ SLO_LOAD
File is being loaded.
Definition: fileio_type.h:55
@ DFT_GAME_FILE
Save game or scenario file.
Definition: fileio_type.h:32
Searchpath
Types of searchpaths OpenTTD might use.
Definition: fileio_type.h:139
Subdirectory
The different kinds of subdirectories OpenTTD uses.
Definition: fileio_type.h:115
@ AI_LIBRARY_DIR
Subdirectory for all AI libraries.
Definition: fileio_type.h:127
@ SCREENSHOT_DIR
Subdirectory for all screenshots.
Definition: fileio_type.h:130
@ SOCIAL_INTEGRATION_DIR
Subdirectory for all social integration plugins.
Definition: fileio_type.h:131
@ GAME_LIBRARY_DIR
Subdirectory for all GS libraries.
Definition: fileio_type.h:129
@ AI_DIR
Subdirectory for all AI files.
Definition: fileio_type.h:126
@ SCENARIO_DIR
Base directory for all scenarios.
Definition: fileio_type.h:119
@ BASE_DIR
Base directory for all subdirectories.
Definition: fileio_type.h:116
@ SAVE_DIR
Base directory for all savegames.
Definition: fileio_type.h:117
@ HEIGHTMAP_DIR
Subdirectory of scenario for heightmaps.
Definition: fileio_type.h:120
@ NEWGRF_DIR
Subdirectory for all NewGRFs.
Definition: fileio_type.h:124
@ AUTOSAVE_DIR
Subdirectory of save for autosaves.
Definition: fileio_type.h:118
@ BASESET_DIR
Subdirectory for all base data (base sets, intro game)
Definition: fileio_type.h:123
@ GAME_DIR
Subdirectory for all game scripts.
Definition: fileio_type.h:128
std::string FiosGetCurrentPath()
Get the current path/working directory.
Definition: fios.cpp:133
bool FiosBrowseTo(const FiosItem *item)
Browse to a new path based on the passed item, starting at #_fios_path.
Definition: fios.cpp:143
Declarations for savegames operations.
void InitFontCache(bool monospace)
(Re)initialize the font cache related things, i.e.
Definition: fontcache.cpp:218
Functions to read fonts from files and cache them.
FontCacheSubSetting * GetFontCacheSubSetting(FontSize fs)
Get the settings of a given font size.
Definition: fontcache.h:216
Base functions for all Games.
Gamelog _gamelog
Gamelog instance.
Definition: gamelog.cpp:31
Functions to be called to log fundamental changes to the game.
Functions related to world/map generation.
static const uint32_t GENERATE_NEW_SEED
Create a new random seed.
Definition: genworld.h:24
void StartNewGameWithoutGUI(uint32_t seed)
Start a normal game without the GUI.
PauseMode _pause_mode
The current pause mode.
Definition: gfx.cpp:50
SwitchMode _switch_mode
The next mainloop command.
Definition: gfx.cpp:49
FontSize
Available font sizes.
Definition: gfx_type.h:208
@ FS_MONO
Index of the monospaced font in the font tables.
Definition: gfx_type.h:212
@ FS_BEGIN
First font.
Definition: gfx_type.h:215
TextColour
Colour of the strings, see _string_colourmap in table/string_colours.h or docs/ottd-colourtext-palett...
Definition: gfx_type.h:260
Functions related to OTTD's landscape.
bool DoZoomInOutWindow(ZoomStateChange how, Window *w)
Zooms a viewport in a window in or out.
Definition: main_gui.cpp:93
static debug_inline TileIndex TileXY(uint x, uint y)
Returns the TileIndex of a coordinate.
Definition: map_func.h:373
constexpr T Clamp(const T a, const T min, const T max)
Clamp a value between an interval.
Definition: math_func.hpp:79
Miscellaneous command definitions.
ClientID _redirect_console_to_client
If not invalid, redirect the console output to a client.
Definition: network.cpp:71
bool _network_available
is network mode available?
Definition: network.cpp:67
bool _networking
are we in networking mode?
Definition: network.cpp:65
StringList _network_ban_list
The banned clients.
Definition: network.cpp:75
bool _network_dedicated
are we a dedicated server?
Definition: network.cpp:68
bool _network_server
network-server is active
Definition: network.cpp:66
ClientID _network_own_client_id
Our client identifier.
Definition: network.cpp:70
bool NetworkClientConnectGame(const std::string &connection_string, CompanyID default_company, const std::string &join_server_password)
Join a client to the server at with the given connection string.
Definition: network.cpp:785
Basic functions/variables used all over the place.
AdminIndex _redirect_console_to_admin
Redirection of the (remote) console to the admin.
Server part of the admin network protocol.
Base core network types and some helper functions to access them.
void NetworkClientSendChat(NetworkAction action, DestType type, int dest, const std::string &msg, int64_t data)
Send a chat message.
bool NetworkIsValidClientName(const std::string_view client_name)
Check whether the given client name is deemed valid for use in network games.
void NetworkClientRequestMove(CompanyID company_id)
Notify the server of this client wanting to be moved to another company.
void NetworkClientSendRcon(const std::string &password, const std::string &command)
Send a remote console command.
Client part of the network protocol.
ClientNetworkContentSocketHandler _network_content_client
The client we use to connect to the server.
Part of the network protocol handling content distribution.
const ContentInfo *const * ConstContentIterator
Iterator for the constant content vector.
std::string _network_server_invite_code
Our invite code as indicated by the Game Coordinator.
Network functions used by other parts of OpenTTD.
void NetworkServerSendChat(NetworkAction action, DestType type, int dest, const std::string &msg, ClientID from_id, int64_t data=0, bool from_admin=false)
Send an actual chat message.
bool NetworkServerChangeClientName(ClientID client_id, const std::string &new_name)
Change the client name of the given client.
void NetworkPrintClients()
Print all the clients to the console.
uint NetworkServerKickOrBanIP(ClientID client_id, bool ban, const std::string &reason)
Ban, or kick, everyone joined from the given client's IP.
void NetworkServerKickClient(ClientID client_id, const std::string &reason)
Kick a single client.
void NetworkServerDoMove(ClientID client_id, CompanyID company_id)
Handle the tid-bits of moving a client from one company to another.
bool NetworkCompanyHasClients(CompanyID company)
Check whether a particular company has clients.
std::string_view NetworkGetPublicKeyOfClient(ClientID client_id)
Get the public key of the client with the given id.
void NetworkServerShowStatusToConsole()
Show the status message of all clients on the console.
static const AdminIndex INVALID_ADMIN_ID
An invalid admin marker.
Definition: network_type.h:64
@ DESTTYPE_CLIENT
Send message/notice to only a certain client (Private)
Definition: network_type.h:82
@ DESTTYPE_TEAM
Send message/notice to everyone playing the same company (Team)
Definition: network_type.h:81
@ DESTTYPE_BROADCAST
Send message/notice to all clients (All)
Definition: network_type.h:80
ClientID
'Unique' identifier to be given to clients
Definition: network_type.h:49
@ INVALID_CLIENT_ID
Client is not part of anything.
Definition: network_type.h:50
@ CLIENT_ID_SERVER
Servers always have this ID.
Definition: network_type.h:51
Base for the NewGRF implementation.
void ReloadNewGRFData()
Reload all NewGRF files during a running game.
Definition: afterload.cpp:3364
Profiling of NewGRF action 2 handling.
bool RequestNewGRFScan(NewGRFScanCallback *callback)
Request a new NewGRF scan.
Definition: openttd.cpp:1331
@ SM_START_HEIGHTMAP
Load a heightmap and start a new game from it.
Definition: openttd.h:38
@ SM_MENU
Switch to game intro menu.
Definition: openttd.h:33
@ SM_RESTARTGAME
Restart --> 'Random game' with current settings.
Definition: openttd.h:29
@ SM_RELOADGAME
Reload the savegame / scenario / heightmap you started the game with.
Definition: openttd.h:30
@ SM_LOAD_GAME
Load game, Play Scenario.
Definition: openttd.h:32
@ PM_UNPAUSED
A normal unpaused game.
Definition: openttd.h:69
@ PM_PAUSED_ERROR
A game paused because a (critical) error.
Definition: openttd.h:73
@ PM_PAUSED_NORMAL
A game normally paused.
Definition: openttd.h:70
Rail specific functions.
@ RTSG_GROUND
Main group of ground images.
Definition: rail.h:52
const RailTypeInfo * GetRailTypeInfo(RailType railtype)
Returns a pointer to the Railtype information for a given railtype.
Definition: rail.h:307
@ RTF_NO_LEVEL_CROSSING
Bit number for disallowing level crossings.
Definition: rail.h:27
@ RTF_HIDDEN
Bit number for hiding from selection.
Definition: rail.h:28
@ RTF_NO_SPRITE_COMBINE
Bit number for using non-combined junctions.
Definition: rail.h:29
@ RTF_ALLOW_90DEG
Bit number for always allowed 90 degree turns, regardless of setting.
Definition: rail.h:30
@ RTF_DISALLOW_90DEG
Bit number for never allowed 90 degree turns, regardless of setting.
Definition: rail.h:31
@ RTF_CATENARY
Bit number for drawing a catenary.
Definition: rail.h:26
RailType
Enumeration for all possible railtypes.
Definition: rail_type.h:27
@ RAILTYPE_BEGIN
Used for iterations.
Definition: rail_type.h:28
@ RAILTYPE_END
Used for iterations.
Definition: rail_type.h:33
Road specific functions.
const RoadTypeInfo * GetRoadTypeInfo(RoadType roadtype)
Returns a pointer to the Roadtype information for a given roadtype.
Definition: road.h:227
@ ROTF_NO_HOUSES
Bit number for setting this roadtype as not house friendly.
Definition: road.h:40
@ ROTF_HIDDEN
Bit number for hidden from construction.
Definition: road.h:41
@ ROTF_NO_LEVEL_CROSSING
Bit number for disabling level crossing.
Definition: road.h:39
@ ROTF_TOWN_BUILD
Bit number for allowing towns to build this roadtype.
Definition: road.h:42
@ ROTF_CATENARY
Bit number for adding catenary.
Definition: road.h:38
@ ROTSG_GROUND
Required: Main group of ground images.
Definition: road.h:62
RoadType
The different roadtypes we support.
Definition: road_type.h:25
@ ROADTYPE_END
Used for iterations.
Definition: road_type.h:29
@ ROADTYPE_BEGIN
Used for iterations.
Definition: road_type.h:26
A number of safeguards to prevent using unsafe methods.
SaveOrLoadResult SaveOrLoad(const std::string &filename, SaveLoadOperation fop, DetailedFileType dft, Subdirectory sb, bool threaded)
Main Save or Load function where the high-level saveload functions are handled.
Definition: saveload.cpp:3097
FileToSaveLoad _file_to_saveload
File to save or load in the openttd loop.
Definition: saveload.cpp:60
void DoExitSave()
Do a save when exiting the game (_settings_client.gui.autosave_on_exit)
Definition: saveload.cpp:3206
Functions/types related to saving and loading games.
@ SL_OK
completed successfully
Definition: saveload.h:402
bool MakeScreenshot(ScreenshotType t, std::string name, uint32_t width, uint32_t height)
Schedule making a screenshot.
Definition: screenshot.cpp:949
Functions to make screenshots.
ScreenshotType
Type of requested screenshot.
Definition: screenshot.h:18
@ SC_VIEWPORT
Screenshot of viewport.
Definition: screenshot.h:19
@ SC_ZOOMEDIN
Fully zoomed in screenshot of the visible area.
Definition: screenshot.h:21
@ SC_HEIGHTMAP
Heightmap of the world.
Definition: screenshot.h:24
@ SC_WORLD
World screenshot.
Definition: screenshot.h:23
@ SC_MINIMAP
Minimap screenshot.
Definition: screenshot.h:25
@ SC_DEFAULTZOOM
Zoomed to default zoom level screenshot of the visible area.
Definition: screenshot.h:22
void SaveToConfig()
Save the values to the configuration file.
Definition: settings.cpp:1447
GameSettings _settings_game
Game settings of a running game or the scenario editor.
Definition: settings.cpp:57
void IConsoleGetSetting(const char *name, bool force_newgame)
Output value of a specific setting to the console.
Definition: settings.cpp:1894
void IConsoleListSettings(const char *prefilter)
List all settings and their value to the console.
Definition: settings.cpp:1929
ClientSettings _settings_client
The current settings for this game.
Definition: settings.cpp:56
Functions related to setting/changing the settings.
Definition of base types and functions in a cross-platform compatible way.
#define lengthof(array)
Return the length of an fixed size array.
Definition: stdafx.h:280
bool StrEqualsIgnoreCase(const std::string_view str1, const std::string_view str2)
Compares two string( view)s for equality, while ignoring the case of the characters.
Definition: string.cpp:347
std::string FormatArrayAsHex(std::span< const uint8_t > data)
Format a byte array into a continuous hex string.
Definition: string.cpp:81
bool StrStartsWithIgnoreCase(std::string_view str, const std::string_view prefix)
Check whether the given string starts with the given prefix, ignoring case.
Definition: string.cpp:281
void StrTrimInPlace(std::string &str)
Trim the spaces from given string in place, i.e.
Definition: string.cpp:260
void SetDParam(size_t n, uint64_t v)
Set a string parameter v at index n in the global string parameter array.
Definition: strings.cpp:104
std::string GetString(StringID string)
Resolve the given StringID into a std::string with all the associated DParam lookups and formatting.
Definition: strings.cpp:319
Functions related to OTTD's strings.
bool ai_in_multiplayer
so we allow AIs in multiplayer
Class to backup a specific variable and restore it upon destruction of this object to prevent stack v...
Specification of a cargo type.
Definition: cargotype.h:71
static IterateWrapper Iterate(size_t from=0)
Returns an iterable ensemble of all valid CargoSpec.
Definition: cargotype.h:190
NetworkSettings network
settings related to the network
GUISettings gui
settings related to the GUI
NetworkAuthorizedKeys allow_list
Public keys of clients that are allowed to join this company.
Definition: company_base.h:78
static bool IsHumanID(size_t index)
Is this company a company not controlled by a NoAI program?
Definition: company_base.h:184
Asynchronous callback.
void OnDisconnect() override
Callback for when the connection got disconnected.
Callbacks for notifying others about incoming data.
Container for all important information about a piece of content.
uint32_t unique_id
Unique ID; either GRF ID or shortname.
MD5Hash md5sum
The MD5 checksum.
State state
Whether the content info is selected (for download)
std::string name
Name of the content.
ContentID id
Unique (server side) ID for the content.
@ AUTOSELECTED
The content has been selected as dependency.
@ SELECTED
The content has been manually selected.
ContentType type
Type of content.
static bool ResetToCurrentNewGRFConfig()
Tries to reset the engine mapping to match the current NewGRF configuration.
Definition: engine.cpp:549
AbstractFileType abstract_ftype
Abstract type of file (scenario, heightmap, etc).
Definition: saveload.h:411
void Set(const FiosItem &item)
Set the title of the file.
Definition: saveload.cpp:3286
Deals with finding savegames.
Definition: fios.h:79
Settings for a single font.
Definition: fontcache.h:192
std::string font
The name of the font, or path to the font.
Definition: fontcache.h:193
uint size
The (requested) size of the font.
Definition: fontcache.h:194
Dynamic data of a loaded NewGRF.
Definition: newgrf.h:108
bool autosave_on_exit
save an autosave when you quit the game, but do not ask "Do you really want to quit?...
ZoomLevel zoom_min
minimum zoom out level
bool newgrf_developer_tools
activate NewGRF developer tools and allow modifying NewGRFs in an existing game
ZoomLevel zoom_max
maximum zoom out level
uint8_t map_x
X size of map.
uint8_t map_y
Y size of map.
uint32_t generation_seed
noise seed for world generation
AISettings ai
what may the AI do?
GameCreationSettings game_creation
settings used during the creation of a game (map)
–Aliases– Aliases are like shortcuts for complex functions, variable assignments, etc.
std::string cmdline
command(s) that is/are being aliased
std::string name
name of the alias
IConsoleCmdProc * proc
process executed when command is typed
IConsoleHook * hook
any special trigger action that needs executing
std::string name
name of command
static IConsoleAlias * AliasGet(const std::string &name)
Find the alias pointed to by its string.
Definition: console.cpp:193
static void CmdRegister(const std::string &name, IConsoleCmdProc *proc, IConsoleHook *hook=nullptr)
Register a new command to be used in the console.
Definition: console.cpp:160
static IConsoleCmd * CmdGet(const std::string &name)
Find the command pointed to by its string.
Definition: console.cpp:170
static void AliasRegister(const std::string &name, const std::string &cmd)
Register a an alias for an already existing command in the console.
Definition: console.cpp:182
static uint SizeY()
Get the size of the map along the Y.
Definition: map_func.h:279
static debug_inline uint SizeX()
Get the size of the map along the X.
Definition: map_func.h:270
static debug_inline uint LogX()
Logarithm of the map size along the X side.
Definition: map_func.h:251
static uint LogY()
Logarithm of the map size along the y side.
Definition: map_func.h:261
static debug_inline uint Size()
Get the size of the map.
Definition: map_func.h:288
Container for all information known about a client.
Definition: network_base.h:24
static NetworkClientInfo * GetByClientID(ClientID client_id)
Return the CI given it's client-identifier.
Definition: network.cpp:116
bool CanJoinCompany(CompanyID company_id) const
Returns whether the given company can be joined by this client.
Definition: network.cpp:130
CompanyID client_playas
As which company is this client playing (CompanyID)
Definition: network_base.h:28
ClientID client_id
Client identifier (same as ClientState->client_id)
Definition: network_base.h:25
uint8_t clients_on
Current count of clients on server.
uint8_t max_clients
maximum amount of clients
uint8_t max_companies
maximum amount of companies
NetworkAuthorizedKeys admin_authorized_keys
Public keys of clients that are authorized to use the admin network.
NetworkAuthorizedKeys rcon_authorized_keys
Public keys of clients that are authorized to use the rconsole (server side).
std::string last_joined
Last joined server.
NetworkAuthorizedKeys server_authorized_keys
Public keys of clients that are authorized to connect to the game.
Callback profiler for NewGRF development.
static void StartTimer(uint64_t ticks)
Start the timeout timer that will finish all profiling sessions.
bool active
Is this profiler collecting data.
const GRFFile * grffile
Which GRF is being profiled.
static void AbortTimer()
Abort the timeout timer, so the timer callback is never called.
Tindex index
Index of this pool item.
Definition: pool_type.hpp:238
static size_t GetNumItems()
Returns number of valid items in the pool.
Definition: pool_type.hpp:369
static bool IsValidID(size_t index)
Tests whether given index can be used to get valid (non-nullptr) Titem.
Definition: pool_type.hpp:328
static Titem * GetIfValid(size_t index)
Returns Titem with given index.
Definition: pool_type.hpp:350
static Pool::IterateWrapper< Titem > Iterate(size_t from=0)
Returns an iterable ensemble of all valid Titem.
Definition: pool_type.hpp:388
static constexpr size_t MAX_SIZE
Make template parameter accessible from outside.
Definition: pool_type.hpp:84
Data structure for viewport, display of a part of the world.
Definition: viewport_type.h:22
ZoomLevel zoom
The zoom level of the viewport.
Definition: viewport_type.h:33
Data structure for an opened window.
Definition: window_gui.h:273
ViewportData * viewport
Pointer to viewport data, if present.
Definition: window_gui.h:318
ContentType
The values in the enum are important; they are used as database 'keys'.
@ CONTENT_TYPE_AI_LIBRARY
The content consists of an AI library.
@ CONTENT_TYPE_BASE_GRAPHICS
The content consists of base graphics.
@ CONTENT_TYPE_AI
The content consists of an AI.
@ CONTENT_TYPE_SCENARIO
The content consists of a scenario.
@ CONTENT_TYPE_NEWGRF
The content consists of a NewGRF.
@ CONTENT_TYPE_BEGIN
Helper to mark the begin of the types.
@ CONTENT_TYPE_END
Helper to mark the end of the types.
@ CONTENT_TYPE_HEIGHTMAP
The content consists of a heightmap.
ContentID
Unique identifier for the content.
Definition of Interval and OneShot timers.
@ VEH_ROAD
Road vehicle type.
Definition: vehicle_type.h:25
@ VEH_AIRCRAFT
Aircraft vehicle type.
Definition: vehicle_type.h:27
@ VEH_SHIP
Ship vehicle type.
Definition: vehicle_type.h:26
@ VEH_TRAIN
Train vehicle type.
Definition: vehicle_type.h:24
bool ScrollMainWindowToTile(TileIndex tile, bool instant)
Scrolls the viewport of the main window to a given location.
Definition: viewport.cpp:2515
Functions related to (drawing on) viewports.
@ ZOOM_IN
Zoom in (get more detailed view).
Definition: viewport_type.h:77
@ ZOOM_OUT
Zoom out (get helicopter view).
Definition: viewport_type.h:78
Window * GetMainWindow()
Get the main window, i.e.
Definition: window.cpp:1127
void SetWindowDirty(WindowClass cls, WindowNumber number)
Mark window as dirty (in need of repainting)
Definition: window.cpp:3093
Window functions not directly related to making/drawing windows.
@ WC_CONSOLE
Console; Window numbers:
Definition: window_type.h:649
@ ZOOM_LVL_MAX
Maximum zoom level.
Definition: zoom_type.h:42
@ ZOOM_LVL_MIN
Minimum zoom level.
Definition: zoom_type.h:41