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