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