OpenTTD Source 20241224-master-gf74b0cf984
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"
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 */
55
56/* Scheduled execution handling. */
57static std::string _scheduled_monthly_script;
58
60static 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
75class ConsoleFileList : public FileList {
76public:
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
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
122static 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
135DEF_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
150DEF_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
165DEF_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
180DEF_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
195DEF_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
208DEF_CONSOLE_HOOK(ConHookServerOrNoNetwork)
209{
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
217DEF_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
233DEF_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
241 return true;
242}
243
249DEF_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
275DEF_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
300DEF_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
359DEF_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
413DEF_CONSOLE_CMD(ConSave)
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
440DEF_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
453DEF_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];
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
479DEF_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];
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
505DEF_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];
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
531DEF_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];
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 */
557DEF_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++) {
567 }
568
569 return true;
570}
571
572/* List all the scenarios */
573DEF_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++) {
583 }
584
585 return true;
586}
587
588/* List all the heightmaps */
589DEF_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++) {
599 }
600
601 return true;
602}
603
604/* Change the dir via console */
605DEF_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];
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
632DEF_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
647DEF_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
664static 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
707DEF_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
730DEF_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
754DEF_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
786DEF_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
804DEF_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
826DEF_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
852DEF_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
871DEF_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
882DEF_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
898DEF_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
932DEF_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
979DEF_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
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
1022DEF_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
1063DEF_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
1075DEF_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
1106DEF_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
1124DEF_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
1173DEF_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
1202DEF_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 ******************************/
1216extern bool CloseConsoleLogIfActive();
1217extern const std::vector<GRFFile *> &GetAllGRFFiles();
1218extern void ConPrintFramerate(); // framerate_gui.cpp
1219extern void ShowFramerateWindow();
1220
1221DEF_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
1246DEF_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
1258DEF_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
1270DEF_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
1282DEF_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
1303DEF_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
1327static void PrintLineByLine(const std::string &full_string)
1328{
1329 std::istringstream in(full_string);
1330 std::string line;
1331 while (std::getline(in, line)) {
1333 }
1334}
1335
1336template <typename F, typename ... Args>
1337bool 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
1347DEF_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
1357DEF_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
1367DEF_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
1377DEF_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
1387DEF_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
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
1459DEF_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
1497DEF_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
1534DEF_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
1551DEF_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
1568DEF_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
1582DEF_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
1594DEF_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
1606DEF_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
1618DEF_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
1638DEF_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
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
1719DEF_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
1741DEF_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
1760DEF_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
1773DEF_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
1791DEF_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
1830DEF_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
1847DEF_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
1864DEF_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
1890DEF_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
1909DEF_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
1935DEF_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
1956static std::vector<std::pair<std::string_view, NetworkAuthorizedKeys *>> _console_cmd_authorized_keys{
1960};
1961
1962enum ConNetworkAuthorizedKeyAction {
1963 CNAKA_LIST,
1964 CNAKA_ADD,
1965 CNAKA_REMOVE,
1966};
1967
1968static 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
2008DEF_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)
2093
2095static ContentType StringToContentType(const char *str)
2096{
2097 static const std::initializer_list<std::pair<std::string_view, ContentType>> content_types = {
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
2124 {
2125 IConsolePrint(CC_DEFAULT, "Completed download of {}.", cid);
2126 }
2127};
2128
2133static 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
2143DEF_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 {
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 {
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
2228DEF_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) {
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
2292DEF_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
2311DEF_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
2330DEF_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
2343DEF_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
2354DEF_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
2362 return true;
2363}
2364
2365DEF_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
2431DEF_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::ranges::find(_newgrf_profilers, grf, &NewGRFProfiler::grffile);
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 _newgrf_profilers.erase(std::ranges::find(_newgrf_profilers, grf, &NewGRFProfiler::grffile));
2500 }
2501 return true;
2502 }
2503
2504 /* "start" sub-command */
2505 if (StrStartsWithIgnoreCase(argv[1], "sta")) {
2506 std::string grfids;
2507 size_t started = 0;
2508 for (NewGRFProfiler &pr : _newgrf_profilers) {
2509 if (!pr.active) {
2510 pr.Start();
2511 started++;
2512
2513 if (!grfids.empty()) grfids += ", ";
2514 fmt::format_to(std::back_inserter(grfids), "[{:08X}]", BSWAP32(pr.grffile->grfid));
2515 }
2516 }
2517 if (started > 0) {
2518 IConsolePrint(CC_DEBUG, "Started profiling for GRFID{} {}.", (started > 1) ? "s" : "", grfids);
2519
2520 if (argc >= 3) {
2521 uint64_t ticks = std::max(atoi(argv[2]), 1);
2523 IConsolePrint(CC_DEBUG, "Profiling will automatically stop after {} ticks.", ticks);
2524 }
2525 } else if (_newgrf_profilers.empty()) {
2526 IConsolePrint(CC_ERROR, "No GRFs selected for profiling, did not start.");
2527 } else {
2528 IConsolePrint(CC_ERROR, "Did not start profiling for any GRFs, all selected GRFs are already profiling.");
2529 }
2530 return true;
2531 }
2532
2533 /* "stop" sub-command */
2534 if (StrStartsWithIgnoreCase(argv[1], "sto")) {
2535 NewGRFProfiler::FinishAll();
2536 return true;
2537 }
2538
2539 /* "abort" sub-command */
2540 if (StrStartsWithIgnoreCase(argv[1], "abo")) {
2541 for (NewGRFProfiler &pr : _newgrf_profilers) {
2542 pr.Abort();
2543 }
2545 return true;
2546 }
2547
2548 return false;
2549}
2550
2551#ifdef _DEBUG
2552/******************
2553 * debug commands
2554 ******************/
2555
2556static void IConsoleDebugLibRegister()
2557{
2558 IConsole::CmdRegister("resettile", ConResetTile);
2559 IConsole::AliasRegister("dbg_echo", "echo %A; echo %B");
2560 IConsole::AliasRegister("dbg_echo2", "echo %!");
2561}
2562#endif
2563
2564DEF_CONSOLE_CMD(ConFramerate)
2565{
2566 if (argc == 0) {
2567 IConsolePrint(CC_HELP, "Show frame rate and game speed information.");
2568 return true;
2569 }
2570
2572 return true;
2573}
2574
2575DEF_CONSOLE_CMD(ConFramerateWindow)
2576{
2577 if (argc == 0) {
2578 IConsolePrint(CC_HELP, "Open the frame rate window.");
2579 return true;
2580 }
2581
2582 if (_network_dedicated) {
2583 IConsolePrint(CC_ERROR, "Can not open frame rate window on a dedicated server.");
2584 return false;
2585 }
2586
2588 return true;
2589}
2590
2598static std::string FormatLabel(uint32_t label)
2599{
2600 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))) {
2601 return fmt::format("{:c}{:c}{:c}{:c}", GB(label, 24, 8), GB(label, 16, 8), GB(label, 8, 8), GB(label, 0, 8));
2602 }
2603
2604 return fmt::format("{:08X}", BSWAP32(label));
2605}
2606
2607static void ConDumpRoadTypes()
2608{
2609 IConsolePrint(CC_DEFAULT, " Flags:");
2610 IConsolePrint(CC_DEFAULT, " c = catenary");
2611 IConsolePrint(CC_DEFAULT, " l = no level crossings");
2612 IConsolePrint(CC_DEFAULT, " X = no houses");
2613 IConsolePrint(CC_DEFAULT, " h = hidden");
2614 IConsolePrint(CC_DEFAULT, " T = buildable by towns");
2615
2616 std::map<uint32_t, const GRFFile *> grfs;
2617 for (RoadType rt = ROADTYPE_BEGIN; rt < ROADTYPE_END; rt++) {
2618 const RoadTypeInfo *rti = GetRoadTypeInfo(rt);
2619 if (rti->label == 0) continue;
2620 uint32_t grfid = 0;
2621 const GRFFile *grf = rti->grffile[ROTSG_GROUND];
2622 if (grf != nullptr) {
2623 grfid = grf->grfid;
2624 grfs.emplace(grfid, grf);
2625 }
2626 IConsolePrint(CC_DEFAULT, " {:02d} {} {}, Flags: {}{}{}{}{}, GRF: {:08X}, {}",
2627 (uint)rt,
2628 RoadTypeIsTram(rt) ? "Tram" : "Road",
2629 FormatLabel(rti->label),
2630 HasBit(rti->flags, ROTF_CATENARY) ? 'c' : '-',
2631 HasBit(rti->flags, ROTF_NO_LEVEL_CROSSING) ? 'l' : '-',
2632 HasBit(rti->flags, ROTF_NO_HOUSES) ? 'X' : '-',
2633 HasBit(rti->flags, ROTF_HIDDEN) ? 'h' : '-',
2634 HasBit(rti->flags, ROTF_TOWN_BUILD) ? 'T' : '-',
2635 BSWAP32(grfid),
2636 GetStringPtr(rti->strings.name)
2637 );
2638 }
2639 for (const auto &grf : grfs) {
2640 IConsolePrint(CC_DEFAULT, " GRF: {:08X} = {}", BSWAP32(grf.first), grf.second->filename);
2641 }
2642}
2643
2644static void ConDumpRailTypes()
2645{
2646 IConsolePrint(CC_DEFAULT, " Flags:");
2647 IConsolePrint(CC_DEFAULT, " c = catenary");
2648 IConsolePrint(CC_DEFAULT, " l = no level crossings");
2649 IConsolePrint(CC_DEFAULT, " h = hidden");
2650 IConsolePrint(CC_DEFAULT, " s = no sprite combine");
2651 IConsolePrint(CC_DEFAULT, " a = always allow 90 degree turns");
2652 IConsolePrint(CC_DEFAULT, " d = always disallow 90 degree turns");
2653
2654 std::map<uint32_t, const GRFFile *> grfs;
2655 for (RailType rt = RAILTYPE_BEGIN; rt < RAILTYPE_END; rt++) {
2656 const RailTypeInfo *rti = GetRailTypeInfo(rt);
2657 if (rti->label == 0) continue;
2658 uint32_t grfid = 0;
2659 const GRFFile *grf = rti->grffile[RTSG_GROUND];
2660 if (grf != nullptr) {
2661 grfid = grf->grfid;
2662 grfs.emplace(grfid, grf);
2663 }
2664 IConsolePrint(CC_DEFAULT, " {:02d} {}, Flags: {}{}{}{}{}{}, GRF: {:08X}, {}",
2665 (uint)rt,
2666 FormatLabel(rti->label),
2667 HasBit(rti->flags, RTF_CATENARY) ? 'c' : '-',
2668 HasBit(rti->flags, RTF_NO_LEVEL_CROSSING) ? 'l' : '-',
2669 HasBit(rti->flags, RTF_HIDDEN) ? 'h' : '-',
2670 HasBit(rti->flags, RTF_NO_SPRITE_COMBINE) ? 's' : '-',
2671 HasBit(rti->flags, RTF_ALLOW_90DEG) ? 'a' : '-',
2672 HasBit(rti->flags, RTF_DISALLOW_90DEG) ? 'd' : '-',
2673 BSWAP32(grfid),
2674 GetStringPtr(rti->strings.name)
2675 );
2676 }
2677 for (const auto &grf : grfs) {
2678 IConsolePrint(CC_DEFAULT, " GRF: {:08X} = {}", BSWAP32(grf.first), grf.second->filename);
2679 }
2680}
2681
2682static void ConDumpCargoTypes()
2683{
2684 IConsolePrint(CC_DEFAULT, " Cargo classes:");
2685 IConsolePrint(CC_DEFAULT, " p = passenger");
2686 IConsolePrint(CC_DEFAULT, " m = mail");
2687 IConsolePrint(CC_DEFAULT, " x = express");
2688 IConsolePrint(CC_DEFAULT, " a = armoured");
2689 IConsolePrint(CC_DEFAULT, " b = bulk");
2690 IConsolePrint(CC_DEFAULT, " g = piece goods");
2691 IConsolePrint(CC_DEFAULT, " l = liquid");
2692 IConsolePrint(CC_DEFAULT, " r = refrigerated");
2693 IConsolePrint(CC_DEFAULT, " h = hazardous");
2694 IConsolePrint(CC_DEFAULT, " c = covered/sheltered");
2695 IConsolePrint(CC_DEFAULT, " o = oversized");
2696 IConsolePrint(CC_DEFAULT, " d = powderized");
2697 IConsolePrint(CC_DEFAULT, " n = not pourable");
2698 IConsolePrint(CC_DEFAULT, " e = potable");
2699 IConsolePrint(CC_DEFAULT, " i = non-potable");
2700 IConsolePrint(CC_DEFAULT, " S = special");
2701
2702 std::map<uint32_t, const GRFFile *> grfs;
2703 for (const CargoSpec *spec : CargoSpec::Iterate()) {
2704 if (!spec->IsValid()) continue;
2705 uint32_t grfid = 0;
2706 const GRFFile *grf = spec->grffile;
2707 if (grf != nullptr) {
2708 grfid = grf->grfid;
2709 grfs.emplace(grfid, grf);
2710 }
2711 IConsolePrint(CC_DEFAULT, " {:02d} Bit: {:2d}, Label: {}, Callback mask: 0x{:02X}, Cargo class: {}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}, GRF: {:08X}, {}",
2712 spec->Index(),
2713 spec->bitnum,
2714 FormatLabel(spec->label.base()),
2715 spec->callback_mask,
2716 (spec->classes & CC_PASSENGERS) != 0 ? 'p' : '-',
2717 (spec->classes & CC_MAIL) != 0 ? 'm' : '-',
2718 (spec->classes & CC_EXPRESS) != 0 ? 'x' : '-',
2719 (spec->classes & CC_ARMOURED) != 0 ? 'a' : '-',
2720 (spec->classes & CC_BULK) != 0 ? 'b' : '-',
2721 (spec->classes & CC_PIECE_GOODS) != 0 ? 'g' : '-',
2722 (spec->classes & CC_LIQUID) != 0 ? 'l' : '-',
2723 (spec->classes & CC_REFRIGERATED) != 0 ? 'r' : '-',
2724 (spec->classes & CC_HAZARDOUS) != 0 ? 'h' : '-',
2725 (spec->classes & CC_COVERED) != 0 ? 'c' : '-',
2726 (spec->classes & CC_OVERSIZED) != 0 ? 'o' : '-',
2727 (spec->classes & CC_POWDERIZED) != 0 ? 'd' : '-',
2728 (spec->classes & CC_NOT_POURABLE) != 0 ? 'n' : '-',
2729 (spec->classes & CC_POTABLE) != 0 ? 'e' : '-',
2730 (spec->classes & CC_NON_POTABLE) != 0 ? 'i' : '-',
2731 (spec->classes & CC_SPECIAL) != 0 ? 'S' : '-',
2732 BSWAP32(grfid),
2733 GetStringPtr(spec->name)
2734 );
2735 }
2736 for (const auto &grf : grfs) {
2737 IConsolePrint(CC_DEFAULT, " GRF: {:08X} = {}", BSWAP32(grf.first), grf.second->filename);
2738 }
2739}
2740
2741
2742DEF_CONSOLE_CMD(ConDumpInfo)
2743{
2744 if (argc != 2) {
2745 IConsolePrint(CC_HELP, "Dump debugging information.");
2746 IConsolePrint(CC_HELP, "Usage: 'dump_info roadtypes|railtypes|cargotypes'.");
2747 IConsolePrint(CC_HELP, " Show information about road/tram types, rail types or cargo types.");
2748 return true;
2749 }
2750
2751 if (StrEqualsIgnoreCase(argv[1], "roadtypes")) {
2752 ConDumpRoadTypes();
2753 return true;
2754 }
2755
2756 if (StrEqualsIgnoreCase(argv[1], "railtypes")) {
2757 ConDumpRailTypes();
2758 return true;
2759 }
2760
2761 if (StrEqualsIgnoreCase(argv[1], "cargotypes")) {
2762 ConDumpCargoTypes();
2763 return true;
2764 }
2765
2766 return false;
2767}
2768
2769/*******************************
2770 * console command registration
2771 *******************************/
2772
2773void IConsoleStdLibRegister()
2774{
2775 IConsole::CmdRegister("debug_level", ConDebugLevel);
2776 IConsole::CmdRegister("echo", ConEcho);
2777 IConsole::CmdRegister("echoc", ConEchoC);
2778 IConsole::CmdRegister("exec", ConExec);
2779 IConsole::CmdRegister("schedule", ConSchedule);
2780 IConsole::CmdRegister("exit", ConExit);
2781 IConsole::CmdRegister("part", ConPart);
2782 IConsole::CmdRegister("help", ConHelp);
2783 IConsole::CmdRegister("info_cmd", ConInfoCmd);
2784 IConsole::CmdRegister("list_cmds", ConListCommands);
2785 IConsole::CmdRegister("list_aliases", ConListAliases);
2786 IConsole::CmdRegister("newgame", ConNewGame);
2787 IConsole::CmdRegister("restart", ConRestart);
2788 IConsole::CmdRegister("reload", ConReload);
2789 IConsole::CmdRegister("getseed", ConGetSeed);
2790 IConsole::CmdRegister("getdate", ConGetDate);
2791 IConsole::CmdRegister("getsysdate", ConGetSysDate);
2792 IConsole::CmdRegister("quit", ConExit);
2793 IConsole::CmdRegister("resetengines", ConResetEngines, ConHookNoNetwork);
2794 IConsole::CmdRegister("reset_enginepool", ConResetEnginePool, ConHookNoNetwork);
2795 IConsole::CmdRegister("return", ConReturn);
2796 IConsole::CmdRegister("screenshot", ConScreenShot);
2797 IConsole::CmdRegister("script", ConScript);
2798 IConsole::CmdRegister("zoomto", ConZoomToLevel);
2799 IConsole::CmdRegister("scrollto", ConScrollToTile);
2800 IConsole::CmdRegister("alias", ConAlias);
2801 IConsole::CmdRegister("load", ConLoad);
2802 IConsole::CmdRegister("load_save", ConLoad);
2803 IConsole::CmdRegister("load_scenario", ConLoadScenario);
2804 IConsole::CmdRegister("load_heightmap", ConLoadHeightmap);
2805 IConsole::CmdRegister("rm", ConRemove);
2806 IConsole::CmdRegister("save", ConSave);
2807 IConsole::CmdRegister("saveconfig", ConSaveConfig);
2808 IConsole::CmdRegister("ls", ConListFiles);
2809 IConsole::CmdRegister("list_saves", ConListFiles);
2810 IConsole::CmdRegister("list_scenarios", ConListScenarios);
2811 IConsole::CmdRegister("list_heightmaps", ConListHeightmaps);
2812 IConsole::CmdRegister("cd", ConChangeDirectory);
2813 IConsole::CmdRegister("pwd", ConPrintWorkingDirectory);
2814 IConsole::CmdRegister("clear", ConClearBuffer);
2815 IConsole::CmdRegister("font", ConFont);
2816 IConsole::CmdRegister("setting", ConSetting);
2817 IConsole::CmdRegister("setting_newgame", ConSettingNewgame);
2818 IConsole::CmdRegister("list_settings", ConListSettings);
2819 IConsole::CmdRegister("gamelog", ConGamelogPrint);
2820 IConsole::CmdRegister("rescan_newgrf", ConRescanNewGRF);
2821 IConsole::CmdRegister("list_dirs", ConListDirs);
2822
2823 IConsole::AliasRegister("dir", "ls");
2824 IConsole::AliasRegister("del", "rm %+");
2825 IConsole::AliasRegister("newmap", "newgame");
2826 IConsole::AliasRegister("patch", "setting %+");
2827 IConsole::AliasRegister("set", "setting %+");
2828 IConsole::AliasRegister("set_newgame", "setting_newgame %+");
2829 IConsole::AliasRegister("list_patches", "list_settings %+");
2830 IConsole::AliasRegister("developer", "setting developer %+");
2831
2832 IConsole::CmdRegister("list_ai_libs", ConListAILibs);
2833 IConsole::CmdRegister("list_ai", ConListAI);
2834 IConsole::CmdRegister("reload_ai", ConReloadAI);
2835 IConsole::CmdRegister("rescan_ai", ConRescanAI);
2836 IConsole::CmdRegister("start_ai", ConStartAI);
2837 IConsole::CmdRegister("stop_ai", ConStopAI);
2838
2839 IConsole::CmdRegister("list_game", ConListGame);
2840 IConsole::CmdRegister("list_game_libs", ConListGameLibs);
2841 IConsole::CmdRegister("rescan_game", ConRescanGame);
2842
2843 IConsole::CmdRegister("companies", ConCompanies);
2844 IConsole::AliasRegister("players", "companies");
2845
2846 /* networking functions */
2847
2848/* Content downloading is only available with ZLIB */
2849#if defined(WITH_ZLIB)
2850 IConsole::CmdRegister("content", ConContent);
2851#endif /* defined(WITH_ZLIB) */
2852
2853 /*** Networking commands ***/
2854 IConsole::CmdRegister("say", ConSay, ConHookNeedNetwork);
2855 IConsole::CmdRegister("say_company", ConSayCompany, ConHookNeedNetwork);
2856 IConsole::AliasRegister("say_player", "say_company %+");
2857 IConsole::CmdRegister("say_client", ConSayClient, ConHookNeedNetwork);
2858
2859 IConsole::CmdRegister("connect", ConNetworkConnect, ConHookClientOnly);
2860 IConsole::CmdRegister("clients", ConNetworkClients, ConHookNeedNetwork);
2861 IConsole::CmdRegister("status", ConStatus, ConHookServerOnly);
2862 IConsole::CmdRegister("server_info", ConServerInfo, ConHookServerOnly);
2863 IConsole::AliasRegister("info", "server_info");
2864 IConsole::CmdRegister("reconnect", ConNetworkReconnect, ConHookClientOnly);
2865 IConsole::CmdRegister("rcon", ConRcon, ConHookNeedNetwork);
2866
2867 IConsole::CmdRegister("join", ConJoinCompany, ConHookNeedNonDedicatedNetwork);
2868 IConsole::AliasRegister("spectate", "join 255");
2869 IConsole::CmdRegister("move", ConMoveClient, ConHookServerOnly);
2870 IConsole::CmdRegister("reset_company", ConResetCompany, ConHookServerOnly);
2871 IConsole::AliasRegister("clean_company", "reset_company %A");
2872 IConsole::CmdRegister("client_name", ConClientNickChange, ConHookServerOnly);
2873 IConsole::CmdRegister("kick", ConKick, ConHookServerOnly);
2874 IConsole::CmdRegister("ban", ConBan, ConHookServerOnly);
2875 IConsole::CmdRegister("unban", ConUnBan, ConHookServerOnly);
2876 IConsole::CmdRegister("banlist", ConBanList, ConHookServerOnly);
2877
2878 IConsole::CmdRegister("pause", ConPauseGame, ConHookServerOrNoNetwork);
2879 IConsole::CmdRegister("unpause", ConUnpauseGame, ConHookServerOrNoNetwork);
2880
2881 IConsole::CmdRegister("authorized_key", ConNetworkAuthorizedKey, ConHookServerOnly);
2882 IConsole::AliasRegister("ak", "authorized_key %+");
2883
2884 IConsole::AliasRegister("net_frame_freq", "setting frame_freq %+");
2885 IConsole::AliasRegister("net_sync_freq", "setting sync_freq %+");
2886 IConsole::AliasRegister("server_pw", "setting server_password %+");
2887 IConsole::AliasRegister("server_password", "setting server_password %+");
2888 IConsole::AliasRegister("rcon_pw", "setting rcon_password %+");
2889 IConsole::AliasRegister("rcon_password", "setting rcon_password %+");
2890 IConsole::AliasRegister("name", "setting client_name %+");
2891 IConsole::AliasRegister("server_name", "setting server_name %+");
2892 IConsole::AliasRegister("server_port", "setting server_port %+");
2893 IConsole::AliasRegister("max_clients", "setting max_clients %+");
2894 IConsole::AliasRegister("max_companies", "setting max_companies %+");
2895 IConsole::AliasRegister("max_join_time", "setting max_join_time %+");
2896 IConsole::AliasRegister("pause_on_join", "setting pause_on_join %+");
2897 IConsole::AliasRegister("autoclean_companies", "setting autoclean_companies %+");
2898 IConsole::AliasRegister("autoclean_protected", "setting autoclean_protected %+");
2899 IConsole::AliasRegister("restart_game_year", "setting restart_game_year %+");
2900 IConsole::AliasRegister("min_players", "setting min_active_clients %+");
2901 IConsole::AliasRegister("reload_cfg", "setting reload_cfg %+");
2902
2903 /* debugging stuff */
2904#ifdef _DEBUG
2905 IConsoleDebugLibRegister();
2906#endif
2907 IConsole::CmdRegister("fps", ConFramerate);
2908 IConsole::CmdRegister("fps_wnd", ConFramerateWindow);
2909
2910 /* NewGRF development stuff */
2911 IConsole::CmdRegister("reload_newgrfs", ConNewGRFReload, ConHookNewGRFDeveloperTool);
2912 IConsole::CmdRegister("newgrf_profile", ConNewGRFProfile, ConHookNewGRFDeveloperTool);
2913
2914 IConsole::CmdRegister("dump_info", ConDumpInfo);
2915}
Base functions for all AIs.
AIConfig stores the configuration settings of every AI.
debug_inline constexpr 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.
debug_inline static constexpr 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_POWDERIZED
Powderized, moist protected (powder/silo wagon)
Definition cargotype.h:61
@ 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_NON_POTABLE
Non-potable / non-food / dirty.
Definition cargotype.h:64
@ 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_OVERSIZED
Oversized (stake/flatbed wagon)
Definition cargotype.h:60
@ CC_POTABLE
Potable / food / clean.
Definition cargotype.h:63
@ CC_NOT_POURABLE
Not Pourable (open wagon, but not hopper wagon)
Definition cargotype.h:62
@ CC_SPECIAL
Special bit used for livery refit tricks instead of normal cargoes.
Definition cargotype.h:65
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.
static void GetConsoleLibraryList(std::back_insert_iterator< std::string > &output_iterator)
Wrapper function for GameScanner::GetConsoleLibraryList.
void PrintConsole()
Print the gamelog data to the console.
Definition gamelog.cpp:310
An interval timer will fire every interval, and will continue to fire until it is deleted.
Definition timer.h:76
Simple helper to (more easily) manage authorized keys.
bool Contains(std::string_view key) const
Check whether the given key is contains in these authorized keys.
Definition network.cpp:178
bool Add(std::string_view key)
Add the given key to the authorized keys, when it is not already contained.
Definition network.cpp:188
bool Remove(std::string_view key)
Remove the given key from the authorized keys, when it is exists.
Definition network.cpp:204
This struct contains all the info that is needed to draw and construct tracks.
Definition rail.h:127
RailTypeLabel label
Unique 32 bit rail type identifier.
Definition rail.h:236
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
const GRFFile * grffile[ROTSG_END]
NewGRF providing the Action3 for the roadtype.
Definition road.h:187
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.
Commands
List of 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.
CompanyID _local_company
Company controlled by the human player at this client. Can also be COMPANY_SPECTATOR.
CompanyID _current_company
Company currently doing an action.
Command definitions related to companies.
Functions related to companies.
@ CCA_NEW_AI
Create a new AI company.
@ CCA_DELETE
Delete a company.
@ CALCA_REMOVE
Remove a public key.
@ CALCA_ADD
Create a public key.
Owner
Enum for all companies/owners.
@ INVALID_COMPANY
An invalid company.
@ COMPANY_SPECTATOR
The client is spectating.
@ COMPANY_NEW_COMPANY
The client wants a new company.
@ MAX_COMPANIES
Maximum number of companies.
@ CRR_NONE
Dummy reason for actions that don't need one.
@ CRR_MANUAL
The company is manually removed.
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.
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.
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.
static const TextColour CC_WHITE
White console lines for various things such as the welcome.
static const TextColour CC_INFO
Colour for information lines.
static const TextColour CC_COMMAND
Colour for the console's commands.
static const TextColour CC_WARNING
Colour for warning lines.
static const TextColour CC_DEBUG
Colour for debug output.
static const TextColour CC_DEFAULT
Default colour of the console.
static const TextColour CC_ERROR
Colour for error lines.
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:799
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.
Subdirectory
The different kinds of subdirectories OpenTTD uses.
@ AI_LIBRARY_DIR
Subdirectory for all AI libraries.
@ SCREENSHOT_DIR
Subdirectory for all screenshots.
@ SOCIAL_INTEGRATION_DIR
Subdirectory for all social integration plugins.
@ GAME_LIBRARY_DIR
Subdirectory for all GS libraries.
@ AI_DIR
Subdirectory for all AI files.
@ SCENARIO_DIR
Base directory for all scenarios.
@ BASE_DIR
Base directory for all subdirectories.
@ SAVE_DIR
Base directory for all savegames.
@ HEIGHTMAP_DIR
Subdirectory of scenario for heightmaps.
@ NEWGRF_DIR
Subdirectory for all NewGRFs.
@ AUTOSAVE_DIR
Subdirectory of save for autosaves.
@ BASESET_DIR
Subdirectory for all base data (base sets, intro game)
@ GAME_DIR
Subdirectory for all game scripts.
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.
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.
@ DESTTYPE_CLIENT
Send message/notice to only a certain client (Private)
@ DESTTYPE_TEAM
Send message/notice to everyone playing the same company (Team)
@ DESTTYPE_BROADCAST
Send message/notice to all clients (All)
ClientID
'Unique' identifier to be given to clients
@ INVALID_CLIENT_ID
Client is not part of anything.
@ CLIENT_ID_SERVER
Servers always have this ID.
Base for the NewGRF implementation.
void ReloadNewGRFData()
Reload all NewGRF files during a running game.
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.
@ 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
const RoadTypeInfo * GetRoadTypeInfo(RoadType roadtype)
Returns a pointer to the Roadtype information for a given roadtype.
Definition road.h:227
@ 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.
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)
Functions/types related to saving and loading games.
@ SL_OK
completed successfully
Definition saveload.h:404
bool MakeScreenshot(ScreenshotType t, std::string name, uint32_t width, uint32_t height)
Schedule making a screenshot.
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.
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.
void IConsoleListSettings(const char *prefilter)
List all settings and their value to the console.
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:333
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:76
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.
static bool IsHumanID(size_t index)
Is this company a company not controlled by a NoAI program?
Asynchronous callback.
void OnConnect(bool success) override
Callback for when the connection has finished.
void OnDownloadComplete(ContentID cid) override
We have finished downloading a file.
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:585
AbstractFileType abstract_ftype
Abstract type of file (scenario, heightmap, etc).
Definition saveload.h:413
void Set(const FiosItem &item)
Set the title of the file.
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.
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)
ClientID client_id
Client identifier (same as ClientState->client_id)
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.
static size_t GetNumItems()
Returns number of valid items in the pool.
static bool IsValidID(size_t index)
Tests whether given index can be used to get valid (non-nullptr) Titem.
static Titem * GetIfValid(size_t index)
Returns Titem with given index.
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.
ZoomLevel zoom
The zoom level of the viewport.
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.
@ VEH_AIRCRAFT
Aircraft vehicle type.
@ VEH_SHIP
Ship vehicle type.
@ VEH_TRAIN
Train vehicle type.
bool ScrollMainWindowToTile(TileIndex tile, bool instant)
Scrolls the viewport of the main window to a given location.
Functions related to (drawing on) viewports.
@ ZOOM_IN
Zoom in (get more detailed view).
@ ZOOM_OUT
Zoom out (get helicopter view).
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:3101
Window functions not directly related to making/drawing windows.
@ WC_CONSOLE
Console; Window numbers:
@ ZOOM_LVL_MAX
Maximum zoom level.
Definition zoom_type.h:42
@ ZOOM_LVL_MIN
Minimum zoom level.
Definition zoom_type.h:41