OpenTTD Source 20250524-master-gc366e6a48e
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 (!FioRemove(item->name)) {
547 IConsolePrint(CC_ERROR, "Failed to delete '{}'.", item->name);
548 }
549 } else {
550 IConsolePrint(CC_ERROR, "'{}' could not be found.", file);
551 }
552
554 return true;
555}
556
557
558/* List all the files in the current dir via console */
559static bool ConListFiles(std::span<std::string_view> argv)
560{
561 if (argv.empty()) {
562 IConsolePrint(CC_HELP, "List all loadable savegames and directories in the current dir via console. Usage: 'ls | dir'.");
563 return true;
564 }
565
567 for (uint i = 0; i < _console_file_list_savegame.size(); i++) {
569 }
570
571 return true;
572}
573
574/* List all the scenarios */
575static bool ConListScenarios(std::span<std::string_view> argv)
576{
577 if (argv.empty()) {
578 IConsolePrint(CC_HELP, "List all loadable scenarios. Usage: 'list_scenarios'.");
579 return true;
580 }
581
583 for (uint i = 0; i < _console_file_list_scenario.size(); i++) {
585 }
586
587 return true;
588}
589
590/* List all the heightmaps */
591static bool ConListHeightmaps(std::span<std::string_view> argv)
592{
593 if (argv.empty()) {
594 IConsolePrint(CC_HELP, "List all loadable heightmaps. Usage: 'list_heightmaps'.");
595 return true;
596 }
597
599 for (uint i = 0; i < _console_file_list_heightmap.size(); i++) {
601 }
602
603 return true;
604}
605
606/* Change the dir via console */
607static bool ConChangeDirectory(std::span<std::string_view> argv)
608{
609 if (argv.empty()) {
610 IConsolePrint(CC_HELP, "Change the dir via console. Usage: 'cd <directory | number>'.");
611 return true;
612 }
613
614 if (argv.size() != 2) return false;
615
616 std::string_view file = argv[1];
619 if (item != nullptr) {
620 switch (item->type.detailed) {
621 case DFT_FIOS_DIR:
622 case DFT_FIOS_DRIVE:
623 case DFT_FIOS_PARENT:
624 FiosBrowseTo(item);
625 break;
626 default: IConsolePrint(CC_ERROR, "{}: Not a directory.", file);
627 }
628 } else {
629 IConsolePrint(CC_ERROR, "{}: No such file or directory.", file);
630 }
631
633 return true;
634}
635
636static bool ConPrintWorkingDirectory(std::span<std::string_view> argv)
637{
638 if (argv.empty()) {
639 IConsolePrint(CC_HELP, "Print out the current working directory. Usage: 'pwd'.");
640 return true;
641 }
642
643 /* XXX - Workaround for broken file handling */
646
648 return true;
649}
650
651static bool ConClearBuffer(std::span<std::string_view> argv)
652{
653 if (argv.empty()) {
654 IConsolePrint(CC_HELP, "Clear the console buffer. Usage: 'clear'.");
655 return true;
656 }
657
658 IConsoleClearBuffer();
660 return true;
661}
662
663
664/**********************************
665 * Network Core Console Commands
666 **********************************/
667
668static bool ConKickOrBan(std::string_view arg, bool ban, std::string_view reason)
669{
670 uint n;
671
672 if (arg.find_first_of(".:") == std::string::npos) { // banning with ID
673 auto client_id = ParseType<ClientID>(arg);
674 if (!client_id.has_value()) {
675 IConsolePrint(CC_ERROR, "The given client-id is not a valid number.");
676 return true;
677 }
678
679 /* Don't kill the server, or the client doing the rcon. The latter can't be kicked because
680 * kicking frees closes and subsequently free the connection related instances, which we
681 * would be reading from and writing to after returning. So we would read or write data
682 * from freed memory up till the segfault triggers. */
683 if (*client_id == CLIENT_ID_SERVER || *client_id == _redirect_console_to_client) {
684 IConsolePrint(CC_ERROR, "You can not {} yourself!", ban ? "ban" : "kick");
685 return true;
686 }
687
689 if (ci == nullptr) {
690 IConsolePrint(CC_ERROR, "Invalid client-id.");
691 return true;
692 }
693
694 if (!ban) {
695 /* Kick only this client, not all clients with that IP */
696 NetworkServerKickClient(*client_id, reason);
697 return true;
698 }
699
700 /* When banning, kick+ban all clients with that IP */
701 n = NetworkServerKickOrBanIP(*client_id, ban, reason);
702 } else {
703 n = NetworkServerKickOrBanIP(arg, ban, reason);
704 }
705
706 if (n == 0) {
707 IConsolePrint(CC_DEFAULT, ban ? "Client not online, address added to banlist." : "Client not found.");
708 } else {
709 IConsolePrint(CC_DEFAULT, "{}ed {} client(s).", ban ? "Bann" : "Kick", n);
710 }
711
712 return true;
713}
714
715static bool ConKick(std::span<std::string_view> argv)
716{
717 if (argv.empty()) {
718 IConsolePrint(CC_HELP, "Kick a client from a network game. Usage: 'kick <ip | client-id> [<kick-reason>]'.");
719 IConsolePrint(CC_HELP, "For client-id's, see the command 'clients'.");
720 return true;
721 }
722
723 if (argv.size() != 2 && argv.size() != 3) return false;
724
725 /* No reason supplied for kicking */
726 if (argv.size() == 2) return ConKickOrBan(argv[1], false, {});
727
728 /* Reason for kicking supplied */
729 size_t kick_message_length = argv[2].size();
730 if (kick_message_length >= 255) {
731 IConsolePrint(CC_ERROR, "Maximum kick message length is 254 characters. You entered {} characters.", kick_message_length);
732 return false;
733 } else {
734 return ConKickOrBan(argv[1], false, argv[2]);
735 }
736}
737
738static bool ConBan(std::span<std::string_view> argv)
739{
740 if (argv.empty()) {
741 IConsolePrint(CC_HELP, "Ban a client from a network game. Usage: 'ban <ip | client-id> [<ban-reason>]'.");
742 IConsolePrint(CC_HELP, "For client-id's, see the command 'clients'.");
743 IConsolePrint(CC_HELP, "If the client is no longer online, you can still ban their IP.");
744 return true;
745 }
746
747 if (argv.size() != 2 && argv.size() != 3) return false;
748
749 /* No reason supplied for kicking */
750 if (argv.size() == 2) return ConKickOrBan(argv[1], true, {});
751
752 /* Reason for kicking supplied */
753 size_t kick_message_length = argv[2].size();
754 if (kick_message_length >= 255) {
755 IConsolePrint(CC_ERROR, "Maximum kick message length is 254 characters. You entered {} characters.", kick_message_length);
756 return false;
757 } else {
758 return ConKickOrBan(argv[1], true, argv[2]);
759 }
760}
761
762static bool ConUnBan(std::span<std::string_view> argv)
763{
764 if (argv.empty()) {
765 IConsolePrint(CC_HELP, "Unban a client from a network game. Usage: 'unban <ip | banlist-index>'.");
766 IConsolePrint(CC_HELP, "For a list of banned IP's, see the command 'banlist'.");
767 return true;
768 }
769
770 if (argv.size() != 2) return false;
771
772 /* Try by IP. */
773 uint index;
774 for (index = 0; index < _network_ban_list.size(); index++) {
775 if (_network_ban_list[index] == argv[1]) break;
776 }
777
778 /* Try by index. */
779 if (index >= _network_ban_list.size()) {
780 index = ParseInteger(argv[1]).value_or(0) - 1U; // let it wrap
781 }
782
783 if (index < _network_ban_list.size()) {
784 IConsolePrint(CC_DEFAULT, "Unbanned {}.", _network_ban_list[index]);
785 _network_ban_list.erase(_network_ban_list.begin() + index);
786 } else {
787 IConsolePrint(CC_DEFAULT, "Invalid list index or IP not in ban-list.");
788 IConsolePrint(CC_DEFAULT, "For a list of banned IP's, see the command 'banlist'.");
789 }
790
791 return true;
792}
793
794static bool ConBanList(std::span<std::string_view> argv)
795{
796 if (argv.empty()) {
797 IConsolePrint(CC_HELP, "List the IP's of banned clients: Usage 'banlist'.");
798 return true;
799 }
800
801 IConsolePrint(CC_DEFAULT, "Banlist:");
802
803 uint i = 1;
804 for (const auto &entry : _network_ban_list) {
805 IConsolePrint(CC_DEFAULT, " {}) {}", i, entry);
806 i++;
807 }
808
809 return true;
810}
811
812static bool ConPauseGame(std::span<std::string_view> argv)
813{
814 if (argv.empty()) {
815 IConsolePrint(CC_HELP, "Pause a network game. Usage: 'pause'.");
816 return true;
817 }
818
819 if (_game_mode == GM_MENU) {
820 IConsolePrint(CC_ERROR, "This command is only available in-game and in the editor.");
821 return true;
822 }
823
826 if (!_networking) IConsolePrint(CC_DEFAULT, "Game paused.");
827 } else {
828 IConsolePrint(CC_DEFAULT, "Game is already paused.");
829 }
830
831 return true;
832}
833
834static bool ConUnpauseGame(std::span<std::string_view> argv)
835{
836 if (argv.empty()) {
837 IConsolePrint(CC_HELP, "Unpause a network game. Usage: 'unpause'.");
838 return true;
839 }
840
841 if (_game_mode == GM_MENU) {
842 IConsolePrint(CC_ERROR, "This command is only available in-game and in the editor.");
843 return true;
844 }
845
848 if (!_networking) IConsolePrint(CC_DEFAULT, "Game unpaused.");
849 } else if (_pause_mode.Test(PauseMode::Error)) {
850 IConsolePrint(CC_DEFAULT, "Game is in error state and cannot be unpaused via console.");
851 } else if (_pause_mode.Any()) {
852 IConsolePrint(CC_DEFAULT, "Game cannot be unpaused manually; disable pause_on_join/min_active_clients.");
853 } else {
854 IConsolePrint(CC_DEFAULT, "Game is already unpaused.");
855 }
856
857 return true;
858}
859
860static bool ConRcon(std::span<std::string_view> argv)
861{
862 if (argv.empty()) {
863 IConsolePrint(CC_HELP, "Remote control the server from another client. Usage: 'rcon <password> <command>'.");
864 IConsolePrint(CC_HELP, "Remember to enclose the command in quotes, otherwise only the first parameter is sent.");
865 IConsolePrint(CC_HELP, "When your client's public key is in the 'authorized keys' for 'rcon', the password is not checked and may be '*'.");
866 return true;
867 }
868
869 if (argv.size() < 3) return false;
870
871 if (_network_server) {
872 IConsoleCmdExec(argv[2]);
873 } else {
874 NetworkClientSendRcon(argv[1], argv[2]);
875 }
876 return true;
877}
878
879static bool ConStatus(std::span<std::string_view> argv)
880{
881 if (argv.empty()) {
882 IConsolePrint(CC_HELP, "List the status of all clients connected to the server. Usage 'status'.");
883 return true;
884 }
885
887 return true;
888}
889
890static bool ConServerInfo(std::span<std::string_view> argv)
891{
892 if (argv.empty()) {
893 IConsolePrint(CC_HELP, "List current and maximum client/company limits. Usage 'server_info'.");
894 IConsolePrint(CC_HELP, "You can change these values by modifying settings 'network.max_clients' and 'network.max_companies'.");
895 return true;
896 }
897
899 IConsolePrint(CC_DEFAULT, "Current/maximum clients: {:3d}/{:3d}", _network_game_info.clients_on, _settings_client.network.max_clients);
900 IConsolePrint(CC_DEFAULT, "Current/maximum companies: {:3d}/{:3d}", Company::GetNumItems(), _settings_client.network.max_companies);
901 IConsolePrint(CC_DEFAULT, "Current spectators: {:3d}", NetworkSpectatorCount());
902
903 return true;
904}
905
906static bool ConClientNickChange(std::span<std::string_view> argv)
907{
908 if (argv.size() != 3) {
909 IConsolePrint(CC_HELP, "Change the nickname of a connected client. Usage: 'client_name <client-id> <new-name>'.");
910 IConsolePrint(CC_HELP, "For client-id's, see the command 'clients'.");
911 return true;
912 }
913
914 auto client_id = ParseType<ClientID>(argv[1]);
915 if (!client_id.has_value()) {
916 IConsolePrint(CC_ERROR, "The given client-id is not a valid number.");
917 return true;
918 }
919
920 if (*client_id == CLIENT_ID_SERVER) {
921 IConsolePrint(CC_ERROR, "Please use the command 'name' to change your own name!");
922 return true;
923 }
924
925 if (NetworkClientInfo::GetByClientID(*client_id) == nullptr) {
926 IConsolePrint(CC_ERROR, "Invalid client-id.");
927 return true;
928 }
929
930 std::string client_name{StrTrimView(argv[2], StringConsumer::WHITESPACE_NO_NEWLINE)};
931 if (!NetworkIsValidClientName(client_name)) {
932 IConsolePrint(CC_ERROR, "Cannot give a client an empty name.");
933 return true;
934 }
935
936 if (!NetworkServerChangeClientName(*client_id, client_name)) {
937 IConsolePrint(CC_ERROR, "Cannot give a client a duplicate name.");
938 }
939
940 return true;
941}
942
943static std::optional<CompanyID> ParseCompanyID(std::string_view arg)
944{
945 auto company_id = ParseType<CompanyID>(arg);
946 if (company_id.has_value() && *company_id <= MAX_COMPANIES) return static_cast<CompanyID>(*company_id - 1);
947 return company_id;
948}
949
950static bool ConJoinCompany(std::span<std::string_view> argv)
951{
952 if (argv.size() < 2) {
953 IConsolePrint(CC_HELP, "Request joining another company. Usage: 'join <company-id>'.");
954 IConsolePrint(CC_HELP, "For valid company-id see company list, use 255 for spectator.");
955 return true;
956 }
957
958 auto company_id = ParseCompanyID(argv[1]);
959 if (!company_id.has_value()) {
960 IConsolePrint(CC_ERROR, "The given company-id is not a valid number.");
961 return true;
962 }
963
965 if (info == nullptr) {
966 IConsolePrint(CC_ERROR, "You have not joined the game yet!");
967 return true;
968 }
969
970 /* Check we have a valid company id! */
971 if (!Company::IsValidID(*company_id) && *company_id != COMPANY_SPECTATOR) {
972 IConsolePrint(CC_ERROR, "Company does not exist. Company-id must be between 1 and {}.", MAX_COMPANIES);
973 return true;
974 }
975
976 if (info->client_playas == *company_id) {
977 IConsolePrint(CC_ERROR, "You are already there!");
978 return true;
979 }
980
981 if (*company_id != COMPANY_SPECTATOR && !Company::IsHumanID(*company_id)) {
982 IConsolePrint(CC_ERROR, "Cannot join AI company.");
983 return true;
984 }
985
986 if (!info->CanJoinCompany(*company_id)) {
987 IConsolePrint(CC_ERROR, "You are not allowed to join this company.");
988 return true;
989 }
990
991 /* non-dedicated server may just do the move! */
992 if (_network_server) {
994 } else {
995 NetworkClientRequestMove(*company_id);
996 }
997
998 return true;
999}
1000
1001static bool ConMoveClient(std::span<std::string_view> argv)
1002{
1003 if (argv.size() < 3) {
1004 IConsolePrint(CC_HELP, "Move a client to another company. Usage: 'move <client-id> <company-id>'.");
1005 IConsolePrint(CC_HELP, "For valid client-id see 'clients', for valid company-id see 'companies', use 255 for moving to spectators.");
1006 return true;
1007 }
1008
1009 auto client_id = ParseType<ClientID>(argv[1]);
1010 if (!client_id.has_value()) {
1011 IConsolePrint(CC_ERROR, "The given client-id is not a valid number.");
1012 return true;
1013 }
1015
1016 auto company_id = ParseCompanyID(argv[2]);
1017 if (!company_id.has_value()) {
1018 IConsolePrint(CC_ERROR, "The given company-id is not a valid number.");
1019 return true;
1020 }
1021
1022 /* check the client exists */
1023 if (ci == nullptr) {
1024 IConsolePrint(CC_ERROR, "Invalid client-id, check the command 'clients' for valid client-id's.");
1025 return true;
1026 }
1027
1028 if (!Company::IsValidID(*company_id) && *company_id != COMPANY_SPECTATOR) {
1029 IConsolePrint(CC_ERROR, "Company does not exist. Company-id must be between 1 and {}.", MAX_COMPANIES);
1030 return true;
1031 }
1032
1033 if (*company_id != COMPANY_SPECTATOR && !Company::IsHumanID(*company_id)) {
1034 IConsolePrint(CC_ERROR, "You cannot move clients to AI companies.");
1035 return true;
1036 }
1037
1039 IConsolePrint(CC_ERROR, "You cannot move the server!");
1040 return true;
1041 }
1042
1043 if (ci->client_playas == *company_id) {
1044 IConsolePrint(CC_ERROR, "You cannot move someone to where they already are!");
1045 return true;
1046 }
1047
1048 /* we are the server, so force the update */
1049 NetworkServerDoMove(ci->client_id, *company_id);
1050
1051 return true;
1052}
1053
1054static bool ConResetCompany(std::span<std::string_view> argv)
1055{
1056 if (argv.empty()) {
1057 IConsolePrint(CC_HELP, "Remove an idle company from the game. Usage: 'reset_company <company-id>'.");
1058 IConsolePrint(CC_HELP, "For company-id's, see the list of companies from the dropdown menu. Company 1 is 1, etc.");
1059 return true;
1060 }
1061
1062 if (argv.size() != 2) return false;
1063
1064 auto index = ParseCompanyID(argv[1]);
1065 if (!index.has_value()) {
1066 IConsolePrint(CC_ERROR, "The given company-id is not a valid number.");
1067 return true;
1068 }
1069
1070 /* Check valid range */
1071 if (!Company::IsValidID(*index)) {
1072 IConsolePrint(CC_ERROR, "Company does not exist. company-id must be between 1 and {}.", MAX_COMPANIES);
1073 return true;
1074 }
1075
1076 if (!Company::IsHumanID(*index)) {
1077 IConsolePrint(CC_ERROR, "Company is owned by an AI.");
1078 return true;
1079 }
1080
1081 if (NetworkCompanyHasClients(*index)) {
1082 IConsolePrint(CC_ERROR, "Cannot remove company: a client is connected to that company.");
1083 return false;
1084 }
1086 assert(ci != nullptr);
1087 if (ci->client_playas == *index) {
1088 IConsolePrint(CC_ERROR, "Cannot remove company: the server is connected to that company.");
1089 return true;
1090 }
1091
1092 /* It is safe to remove this company */
1094 IConsolePrint(CC_DEFAULT, "Company deleted.");
1095
1096 return true;
1097}
1098
1099static bool ConNetworkClients(std::span<std::string_view> argv)
1100{
1101 if (argv.empty()) {
1102 IConsolePrint(CC_HELP, "Get a list of connected clients including their ID, name, company-id, and IP. Usage: 'clients'.");
1103 return true;
1104 }
1105
1107
1108 return true;
1109}
1110
1111static bool ConNetworkReconnect(std::span<std::string_view> argv)
1112{
1113 if (argv.empty()) {
1114 IConsolePrint(CC_HELP, "Reconnect to server to which you were connected last time. Usage: 'reconnect [<company-id>]'.");
1115 IConsolePrint(CC_HELP, "Company 255 is spectator (default, if not specified), 254 means creating new company.");
1116 IConsolePrint(CC_HELP, "All others are a certain company with Company 1 being #1.");
1117 return true;
1118 }
1119
1121 if (argv.size() >= 2) {
1122 auto company_id = ParseCompanyID(argv[1]);
1123 if (!company_id.has_value()) {
1124 IConsolePrint(CC_ERROR, "The given company-id is not a valid number.");
1125 return true;
1126 }
1127 if (*company_id >= MAX_COMPANIES && *company_id != COMPANY_NEW_COMPANY && *company_id != COMPANY_SPECTATOR) return false;
1128 playas = *company_id;
1129 }
1130
1131 if (_settings_client.network.last_joined.empty()) {
1132 IConsolePrint(CC_DEFAULT, "No server for reconnecting.");
1133 return true;
1134 }
1135
1136 /* Don't resolve the address first, just print it directly as it comes from the config file. */
1137 IConsolePrint(CC_DEFAULT, "Reconnecting to {} ...", _settings_client.network.last_joined);
1138
1140}
1141
1142static bool ConNetworkConnect(std::span<std::string_view> argv)
1143{
1144 if (argv.empty()) {
1145 IConsolePrint(CC_HELP, "Connect to a remote OTTD server and join the game. Usage: 'connect <ip>'.");
1146 IConsolePrint(CC_HELP, "IP can contain port and company: 'IP[:Port][#Company]', eg: 'server.ottd.org:443#2'.");
1147 IConsolePrint(CC_HELP, "Company #255 is spectator all others are a certain company with Company 1 being #1.");
1148 return true;
1149 }
1150
1151 if (argv.size() < 2) return false;
1152
1154}
1155
1156/*********************************
1157 * script file console commands
1158 *********************************/
1159
1160static bool ConExec(std::span<std::string_view> argv)
1161{
1162 if (argv.empty()) {
1163 IConsolePrint(CC_HELP, "Execute a local script file. Usage: 'exec <script> [0]'.");
1164 IConsolePrint(CC_HELP, "By passing '0' after the script name, no warning about a missing script file will be shown.");
1165 return true;
1166 }
1167
1168 if (argv.size() < 2) return false;
1169
1170 auto script_file = FioFOpenFile(argv[1], "r", BASE_DIR);
1171
1172 if (!script_file.has_value()) {
1173 if (argv.size() == 2 || argv[2] != "0") IConsolePrint(CC_ERROR, "Script file '{}' not found.", argv[1]);
1174 return true;
1175 }
1176
1177 if (_script_current_depth == 11) {
1178 IConsolePrint(CC_ERROR, "Maximum 'exec' depth reached; script A is calling script B is calling script C ... more than 10 times.");
1179 return true;
1180 }
1181
1183 uint script_depth = _script_current_depth;
1184
1185 char buffer[ICON_CMDLN_SIZE];
1186 while (fgets(buffer, sizeof(buffer), *script_file) != nullptr) {
1187 /* Remove newline characters from the executing script */
1188 std::string_view cmdline{buffer};
1189 auto last_non_newline = cmdline.find_last_not_of("\r\n");
1190 if (last_non_newline != std::string_view::npos) cmdline = cmdline.substr(0, last_non_newline + 1);
1191
1192 IConsoleCmdExec(cmdline);
1193 /* Ensure that we are still on the same depth or that we returned via 'return'. */
1194 assert(_script_current_depth == script_depth || _script_current_depth == script_depth - 1);
1195
1196 /* The 'return' command was executed. */
1197 if (_script_current_depth == script_depth - 1) break;
1198 }
1199
1200 if (ferror(*script_file) != 0) {
1201 IConsolePrint(CC_ERROR, "Encountered error while trying to read from script file '{}'.", argv[1]);
1202 }
1203
1204 if (_script_current_depth == script_depth) _script_current_depth--;
1205 return true;
1206}
1207
1208static bool ConSchedule(std::span<std::string_view> argv)
1209{
1210 if (argv.size() < 3 || std::string_view(argv[1]) != "on-next-calendar-month") {
1211 IConsolePrint(CC_HELP, "Schedule a local script to execute later. Usage: 'schedule on-next-calendar-month <script>'.");
1212 return true;
1213 }
1214
1215 /* Check if the file exists. It might still go away later, but helpful to show an error now. */
1216 if (!FioCheckFileExists(argv[2], BASE_DIR)) {
1217 IConsolePrint(CC_ERROR, "Script file '{}' not found.", argv[2]);
1218 return true;
1219 }
1220
1221 /* We only support a single script scheduled, so we tell the user what's happening if there was already one. */
1222 std::string_view filename = std::string_view(argv[2]);
1223 if (!_scheduled_monthly_script.empty() && filename == _scheduled_monthly_script) {
1224 IConsolePrint(CC_INFO, "Script file '{}' was already scheduled to execute at the start of next calendar month.", filename);
1225 } else if (!_scheduled_monthly_script.empty() && filename != _scheduled_monthly_script) {
1226 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);
1227 } else {
1228 IConsolePrint(CC_INFO, "Script file '{}' scheduled to execute at the start of next calendar month.", filename);
1229 }
1230
1231 /* Store the filename to be used by _schedule_timer on the start of next calendar month. */
1232 _scheduled_monthly_script = filename;
1233
1234 return true;
1235}
1236
1237static bool ConReturn(std::span<std::string_view> argv)
1238{
1239 if (argv.empty()) {
1240 IConsolePrint(CC_HELP, "Stop executing a running script. Usage: 'return'.");
1241 return true;
1242 }
1243
1245 return true;
1246}
1247
1248/*****************************
1249 * default console commands
1250 ******************************/
1251extern bool CloseConsoleLogIfActive();
1252extern std::span<const GRFFile> GetAllGRFFiles();
1253extern void ConPrintFramerate(); // framerate_gui.cpp
1254extern void ShowFramerateWindow();
1255
1256static bool ConScript(std::span<std::string_view> argv)
1257{
1258 extern std::optional<FileHandle> _iconsole_output_file;
1259
1260 if (argv.empty()) {
1261 IConsolePrint(CC_HELP, "Start or stop logging console output to a file. Usage: 'script <filename>'.");
1262 IConsolePrint(CC_HELP, "If filename is omitted, a running log is stopped if it is active.");
1263 return true;
1264 }
1265
1266 if (!CloseConsoleLogIfActive()) {
1267 if (argv.size() < 2) return false;
1268
1269 _iconsole_output_file = FileHandle::Open(argv[1], "ab");
1270 if (!_iconsole_output_file.has_value()) {
1271 IConsolePrint(CC_ERROR, "Could not open console log file '{}'.", argv[1]);
1272 } else {
1273 IConsolePrint(CC_INFO, "Console log output started to '{}'.", argv[1]);
1274 }
1275 }
1276
1277 return true;
1278}
1279
1280static bool ConEcho(std::span<std::string_view> argv)
1281{
1282 if (argv.empty()) {
1283 IConsolePrint(CC_HELP, "Print back the first argument to the console. Usage: 'echo <arg>'.");
1284 return true;
1285 }
1286
1287 if (argv.size() < 2) return false;
1288 IConsolePrint(CC_DEFAULT, "{}", argv[1]);
1289 return true;
1290}
1291
1292static bool ConEchoC(std::span<std::string_view> argv)
1293{
1294 if (argv.empty()) {
1295 IConsolePrint(CC_HELP, "Print back the first argument to the console in a given colour. Usage: 'echoc <colour> <arg2>'.");
1296 return true;
1297 }
1298
1299 if (argv.size() < 3) return false;
1300
1301 auto colour = ParseInteger(argv[1]);
1302 if (!colour.has_value() || !IsInsideMM(*colour, TC_BEGIN, TC_END)) {
1303 IConsolePrint(CC_ERROR, "The colour must be a number between {} and {}.", TC_BEGIN, TC_END - 1);
1304 return true;
1305 }
1306
1307 IConsolePrint(static_cast<TextColour>(*colour), "{}", argv[2]);
1308 return true;
1309}
1310
1311static bool ConNewGame(std::span<std::string_view> argv)
1312{
1313 if (argv.empty()) {
1314 IConsolePrint(CC_HELP, "Start a new game. Usage: 'newgame [seed]'.");
1315 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.");
1316 return true;
1317 }
1318
1319 uint32_t seed = GENERATE_NEW_SEED;
1320 if (argv.size() >= 2) {
1321 auto param = ParseInteger(argv[1]);
1322 if (!param.has_value()) {
1323 IConsolePrint(CC_ERROR, "The given seed must be a valid number.");
1324 return true;
1325 }
1326 seed = *param;
1327 }
1328
1330 return true;
1331}
1332
1333static bool ConRestart(std::span<std::string_view> argv)
1334{
1335 if (argv.empty() || argv.size() > 2) {
1336 IConsolePrint(CC_HELP, "Restart game. Usage: 'restart [current|newgame]'.");
1337 IConsolePrint(CC_HELP, "Restarts a game, using either the current or newgame (default) settings.");
1338 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.");
1339 IConsolePrint(CC_HELP, " * if you started from a savegame / scenario / heightmap, the game might be different, because the current/newgame settings might differ.");
1340 return true;
1341 }
1342
1343 if (argv.size() == 1 || std::string_view(argv[1]) == "newgame") {
1345 } else {
1349 }
1350
1351 return true;
1352}
1353
1354static bool ConReload(std::span<std::string_view> argv)
1355{
1356 if (argv.empty()) {
1357 IConsolePrint(CC_HELP, "Reload game. Usage: 'reload'.");
1358 IConsolePrint(CC_HELP, "Reloads a game if loaded via savegame / scenario / heightmap.");
1359 return true;
1360 }
1361
1363 IConsolePrint(CC_ERROR, "No game loaded to reload.");
1364 return true;
1365 }
1366
1367 /* Use a switch-mode to prevent copying over newgame settings to active settings. */
1371 return true;
1372}
1373
1378static void PrintLineByLine(const std::string &full_string)
1379{
1380 std::istringstream in(full_string);
1381 std::string line;
1382 while (std::getline(in, line)) {
1384 }
1385}
1386
1387template <typename F, typename ... Args>
1388bool PrintList(F list_function, Args... args)
1389{
1390 std::string output_str;
1391 auto inserter = std::back_inserter(output_str);
1392 list_function(inserter, args...);
1393 PrintLineByLine(output_str);
1394
1395 return true;
1396}
1397
1398static bool ConListAILibs(std::span<std::string_view> argv)
1399{
1400 if (argv.empty()) {
1401 IConsolePrint(CC_HELP, "List installed AI libraries. Usage: 'list_ai_libs'.");
1402 return true;
1403 }
1404
1405 return PrintList(AI::GetConsoleLibraryList);
1406}
1407
1408static bool ConListAI(std::span<std::string_view> argv)
1409{
1410 if (argv.empty()) {
1411 IConsolePrint(CC_HELP, "List installed AIs. Usage: 'list_ai'.");
1412 return true;
1413 }
1414
1415 return PrintList(AI::GetConsoleList, false);
1416}
1417
1418static bool ConListGameLibs(std::span<std::string_view> argv)
1419{
1420 if (argv.empty()) {
1421 IConsolePrint(CC_HELP, "List installed Game Script libraries. Usage: 'list_game_libs'.");
1422 return true;
1423 }
1424
1425 return PrintList(Game::GetConsoleLibraryList);
1426}
1427
1428static bool ConListGame(std::span<std::string_view> argv)
1429{
1430 if (argv.empty()) {
1431 IConsolePrint(CC_HELP, "List installed Game Scripts. Usage: 'list_game'.");
1432 return true;
1433 }
1434
1435 return PrintList(Game::GetConsoleList, false);
1436}
1437
1438static bool ConStartAI(std::span<std::string_view> argv)
1439{
1440 if (argv.empty() || argv.size() > 3) {
1441 IConsolePrint(CC_HELP, "Start a new AI. Usage: 'start_ai [<AI>] [<settings>]'.");
1442 IConsolePrint(CC_HELP, "Start a new AI. If <AI> is given, it starts that specific AI (if found).");
1443 IConsolePrint(CC_HELP, "If <settings> is given, it is parsed and the AI settings are set to that.");
1444 return true;
1445 }
1446
1447 if (_game_mode != GM_NORMAL) {
1448 IConsolePrint(CC_ERROR, "AIs can only be managed in a game.");
1449 return true;
1450 }
1451
1453 IConsolePrint(CC_ERROR, "Can't start a new AI (no more free slots).");
1454 return true;
1455 }
1456 if (_networking && !_network_server) {
1457 IConsolePrint(CC_ERROR, "Only the server can start a new AI.");
1458 return true;
1459 }
1461 IConsolePrint(CC_ERROR, "AIs are not allowed in multiplayer by configuration.");
1462 IConsolePrint(CC_ERROR, "Switch AI -> AI in multiplayer to True.");
1463 return true;
1464 }
1465 if (!AI::CanStartNew()) {
1466 IConsolePrint(CC_ERROR, "Can't start a new AI.");
1467 return true;
1468 }
1469
1470 int n = 0;
1471 /* Find the next free slot */
1472 for (const Company *c : Company::Iterate()) {
1473 if (c->index != n) break;
1474 n++;
1475 }
1476
1478 if (argv.size() >= 2) {
1479 config->Change(argv[1], -1, false);
1480
1481 /* If the name is not found, and there is a dot in the name,
1482 * try again with the assumption everything right of the dot is
1483 * the version the user wants to load. */
1484 if (!config->HasScript()) {
1485 StringConsumer consumer{std::string_view{argv[1]}};
1486 auto name = consumer.ReadUntilChar('.', StringConsumer::SKIP_ONE_SEPARATOR);
1487 if (consumer.AnyBytesLeft()) {
1488 auto version = consumer.TryReadIntegerBase<uint32_t>(10);
1489 if (!version.has_value()) {
1490 IConsolePrint(CC_ERROR, "The version is not a valid number.");
1491 return true;
1492 }
1493 config->Change(name, *version, true);
1494 }
1495 }
1496
1497 if (!config->HasScript()) {
1498 IConsolePrint(CC_ERROR, "Failed to load the specified AI.");
1499 return true;
1500 }
1501 if (argv.size() == 3) {
1502 config->StringToSettings(argv[2]);
1503 }
1504 }
1505
1506 /* Start a new AI company */
1508
1509 return true;
1510}
1511
1512static bool ConReloadAI(std::span<std::string_view> argv)
1513{
1514 if (argv.size() != 2) {
1515 IConsolePrint(CC_HELP, "Reload an AI. Usage: 'reload_ai <company-id>'.");
1516 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.");
1517 return true;
1518 }
1519
1520 if (_game_mode != GM_NORMAL) {
1521 IConsolePrint(CC_ERROR, "AIs can only be managed in a game.");
1522 return true;
1523 }
1524
1525 if (_networking && !_network_server) {
1526 IConsolePrint(CC_ERROR, "Only the server can reload an AI.");
1527 return true;
1528 }
1529
1530 auto company_id = ParseCompanyID(argv[1]);
1531 if (!company_id.has_value()) {
1532 IConsolePrint(CC_ERROR, "The given company-id is not a valid number.");
1533 return true;
1534 }
1535
1536 if (!Company::IsValidID(*company_id)) {
1537 IConsolePrint(CC_ERROR, "Unknown company. Company range is between 1 and {}.", MAX_COMPANIES);
1538 return true;
1539 }
1540
1541 /* In singleplayer mode the player can be in an AI company, after cheating or loading network save with an AI in first slot. */
1542 if (Company::IsHumanID(*company_id) || *company_id == _local_company) {
1543 IConsolePrint(CC_ERROR, "Company is not controlled by an AI.");
1544 return true;
1545 }
1546
1547 /* First kill the company of the AI, then start a new one. This should start the current AI again */
1550 IConsolePrint(CC_DEFAULT, "AI reloaded.");
1551
1552 return true;
1553}
1554
1555static bool ConStopAI(std::span<std::string_view> argv)
1556{
1557 if (argv.size() != 2) {
1558 IConsolePrint(CC_HELP, "Stop an AI. Usage: 'stop_ai <company-id>'.");
1559 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.");
1560 return true;
1561 }
1562
1563 if (_game_mode != GM_NORMAL) {
1564 IConsolePrint(CC_ERROR, "AIs can only be managed in a game.");
1565 return true;
1566 }
1567
1568 if (_networking && !_network_server) {
1569 IConsolePrint(CC_ERROR, "Only the server can stop an AI.");
1570 return true;
1571 }
1572
1573 auto company_id = ParseCompanyID(argv[1]);
1574 if (!company_id.has_value()) {
1575 IConsolePrint(CC_ERROR, "The given company-id is not a valid number.");
1576 return true;
1577 }
1578
1579 if (!Company::IsValidID(*company_id)) {
1580 IConsolePrint(CC_ERROR, "Unknown company. Company range is between 1 and {}.", MAX_COMPANIES);
1581 return true;
1582 }
1583
1584 /* In singleplayer mode the player can be in an AI company, after cheating or loading network save with an AI in first slot. */
1585 if (Company::IsHumanID(*company_id) || *company_id == _local_company) {
1586 IConsolePrint(CC_ERROR, "Company is not controlled by an AI.");
1587 return true;
1588 }
1589
1590 /* Now kill the company of the AI. */
1592 IConsolePrint(CC_DEFAULT, "AI stopped, company deleted.");
1593
1594 return true;
1595}
1596
1597static bool ConRescanAI(std::span<std::string_view> argv)
1598{
1599 if (argv.empty()) {
1600 IConsolePrint(CC_HELP, "Rescan the AI dir for scripts. Usage: 'rescan_ai'.");
1601 return true;
1602 }
1603
1604 if (_networking && !_network_server) {
1605 IConsolePrint(CC_ERROR, "Only the server can rescan the AI dir for scripts.");
1606 return true;
1607 }
1608
1609 AI::Rescan();
1610
1611 return true;
1612}
1613
1614static bool ConRescanGame(std::span<std::string_view> argv)
1615{
1616 if (argv.empty()) {
1617 IConsolePrint(CC_HELP, "Rescan the Game Script dir for scripts. Usage: 'rescan_game'.");
1618 return true;
1619 }
1620
1621 if (_networking && !_network_server) {
1622 IConsolePrint(CC_ERROR, "Only the server can rescan the Game Script dir for scripts.");
1623 return true;
1624 }
1625
1626 Game::Rescan();
1627
1628 return true;
1629}
1630
1631static bool ConRescanNewGRF(std::span<std::string_view> argv)
1632{
1633 if (argv.empty()) {
1634 IConsolePrint(CC_HELP, "Rescan the data dir for NewGRFs. Usage: 'rescan_newgrf'.");
1635 return true;
1636 }
1637
1638 if (!RequestNewGRFScan()) {
1639 IConsolePrint(CC_ERROR, "NewGRF scanning is already running. Please wait until completed to run again.");
1640 }
1641
1642 return true;
1643}
1644
1645static bool ConGetSeed(std::span<std::string_view> argv)
1646{
1647 if (argv.empty()) {
1648 IConsolePrint(CC_HELP, "Returns the seed used to create this game. Usage: 'getseed'.");
1649 IConsolePrint(CC_HELP, "The seed can be used to reproduce the exact same map as the game started with.");
1650 return true;
1651 }
1652
1654 return true;
1655}
1656
1657static bool ConGetDate(std::span<std::string_view> argv)
1658{
1659 if (argv.empty()) {
1660 IConsolePrint(CC_HELP, "Returns the current date (year-month-day) of the game. Usage: 'getdate'.");
1661 return true;
1662 }
1663
1664 TimerGameCalendar::YearMonthDay ymd = TimerGameCalendar::ConvertDateToYMD(TimerGameCalendar::date);
1665 IConsolePrint(CC_DEFAULT, "Date: {:04d}-{:02d}-{:02d}", ymd.year, ymd.month + 1, ymd.day);
1666 return true;
1667}
1668
1669static bool ConGetSysDate(std::span<std::string_view> argv)
1670{
1671 if (argv.empty()) {
1672 IConsolePrint(CC_HELP, "Returns the current date (year-month-day) of your system. Usage: 'getsysdate'.");
1673 return true;
1674 }
1675
1676 IConsolePrint(CC_DEFAULT, "System Date: {:%Y-%m-%d %H:%M:%S}", fmt::localtime(time(nullptr)));
1677 return true;
1678}
1679
1680static bool ConAlias(std::span<std::string_view> argv)
1681{
1682 IConsoleAlias *alias;
1683
1684 if (argv.empty()) {
1685 IConsolePrint(CC_HELP, "Add a new alias, or redefine the behaviour of an existing alias . Usage: 'alias <name> <command>'.");
1686 return true;
1687 }
1688
1689 if (argv.size() < 3) return false;
1690
1691 alias = IConsole::AliasGet(std::string(argv[1]));
1692 if (alias == nullptr) {
1693 IConsole::AliasRegister(std::string(argv[1]), argv[2]);
1694 } else {
1695 alias->cmdline = argv[2];
1696 }
1697 return true;
1698}
1699
1700static bool ConScreenShot(std::span<std::string_view> argv)
1701{
1702 if (argv.empty()) {
1703 IConsolePrint(CC_HELP, "Create a screenshot of the game. Usage: 'screenshot [viewport | normal | big | giant | heightmap | minimap] [no_con] [size <width> <height>] [<filename>]'.");
1704 IConsolePrint(CC_HELP, " 'viewport' (default) makes a screenshot of the current viewport (including menus, windows).");
1705 IConsolePrint(CC_HELP, " 'normal' makes a screenshot of the visible area.");
1706 IConsolePrint(CC_HELP, " 'big' makes a zoomed-in screenshot of the visible area.");
1707 IConsolePrint(CC_HELP, " 'giant' makes a screenshot of the whole map.");
1708 IConsolePrint(CC_HELP, " 'heightmap' makes a heightmap screenshot of the map that can be loaded in as heightmap.");
1709 IConsolePrint(CC_HELP, " 'minimap' makes a top-viewed minimap screenshot of the whole world which represents one tile by one pixel.");
1710 IConsolePrint(CC_HELP, " 'no_con' hides the console to create the screenshot (only useful in combination with 'viewport').");
1711 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').");
1712 IConsolePrint(CC_HELP, " A filename ending in # will prevent overwriting existing files and will number files counting upwards.");
1713 return true;
1714 }
1715
1716 if (argv.size() > 7) return false;
1717
1719 uint32_t width = 0;
1720 uint32_t height = 0;
1721 std::string name{};
1722 uint32_t arg_index = 1;
1723
1724 if (argv.size() > arg_index) {
1725 if (argv[arg_index] == "viewport") {
1726 type = SC_VIEWPORT;
1727 arg_index += 1;
1728 } else if (argv[arg_index] == "normal") {
1729 type = SC_DEFAULTZOOM;
1730 arg_index += 1;
1731 } else if (argv[arg_index] == "big") {
1732 type = SC_ZOOMEDIN;
1733 arg_index += 1;
1734 } else if (argv[arg_index] == "giant") {
1735 type = SC_WORLD;
1736 arg_index += 1;
1737 } else if (argv[arg_index] == "heightmap") {
1738 type = SC_HEIGHTMAP;
1739 arg_index += 1;
1740 } else if (argv[arg_index] == "minimap") {
1741 type = SC_MINIMAP;
1742 arg_index += 1;
1743 }
1744 }
1745
1746 if (argv.size() > arg_index && argv[arg_index] == "no_con") {
1747 if (type != SC_VIEWPORT) {
1748 IConsolePrint(CC_ERROR, "'no_con' can only be used in combination with 'viewport'.");
1749 return true;
1750 }
1751 IConsoleClose();
1752 arg_index += 1;
1753 }
1754
1755 if (argv.size() > arg_index + 2 && argv[arg_index] == "size") {
1756 /* size <width> <height> */
1757 if (type != SC_DEFAULTZOOM && type != SC_ZOOMEDIN) {
1758 IConsolePrint(CC_ERROR, "'size' can only be used in combination with 'normal' or 'big'.");
1759 return true;
1760 }
1761 auto t = ParseInteger(argv[arg_index + 1]);
1762 if (!t.has_value()) {
1763 IConsolePrint(CC_ERROR, "Invalid width '{}'", argv[arg_index + 1]);
1764 return true;
1765 }
1766 width = *t;
1767
1768 t = ParseInteger(argv[arg_index + 2]);
1769 if (!t.has_value()) {
1770 IConsolePrint(CC_ERROR, "Invalid height '{}'", argv[arg_index + 2]);
1771 return true;
1772 }
1773 height = *t;
1774 arg_index += 3;
1775 }
1776
1777 if (argv.size() > arg_index) {
1778 /* Last parameter that was not one of the keywords must be the filename. */
1779 name = argv[arg_index];
1780 arg_index += 1;
1781 }
1782
1783 if (argv.size() > arg_index) {
1784 /* We have parameters we did not process; means we misunderstood any of the above. */
1785 return false;
1786 }
1787
1788 MakeScreenshot(type, std::move(name), width, height);
1789 return true;
1790}
1791
1792static bool ConInfoCmd(std::span<std::string_view> argv)
1793{
1794 if (argv.empty()) {
1795 IConsolePrint(CC_HELP, "Print out debugging information about a command. Usage: 'info_cmd <cmd>'.");
1796 return true;
1797 }
1798
1799 if (argv.size() < 2) return false;
1800
1801 const IConsoleCmd *cmd = IConsole::CmdGet(std::string(argv[1]));
1802 if (cmd == nullptr) {
1803 IConsolePrint(CC_ERROR, "The given command was not found.");
1804 return true;
1805 }
1806
1807 IConsolePrint(CC_DEFAULT, "Command name: '{}'", cmd->name);
1808
1809 if (cmd->hook != nullptr) IConsolePrint(CC_DEFAULT, "Command is hooked.");
1810
1811 return true;
1812}
1813
1814static bool ConDebugLevel(std::span<std::string_view> argv)
1815{
1816 if (argv.empty()) {
1817 IConsolePrint(CC_HELP, "Get/set the default debugging level for the game. Usage: 'debug_level [<level>]'.");
1818 IConsolePrint(CC_HELP, "Level can be any combination of names, levels. Eg 'net=5 ms=4'. Remember to enclose it in \"'\"s.");
1819 return true;
1820 }
1821
1822 if (argv.size() > 2) return false;
1823
1824 if (argv.size() == 1) {
1825 IConsolePrint(CC_DEFAULT, "Current debug-level: '{}'", GetDebugString());
1826 } else {
1827 SetDebugString(argv[1], [](std::string_view err) { IConsolePrint(CC_ERROR, "{}", err); });
1828 }
1829
1830 return true;
1831}
1832
1833static bool ConExit(std::span<std::string_view> argv)
1834{
1835 if (argv.empty()) {
1836 IConsolePrint(CC_HELP, "Exit the game. Usage: 'exit'.");
1837 return true;
1838 }
1839
1840 if (_game_mode == GM_NORMAL && _settings_client.gui.autosave_on_exit) DoExitSave();
1841
1842 _exit_game = true;
1843 return true;
1844}
1845
1846static bool ConPart(std::span<std::string_view> argv)
1847{
1848 if (argv.empty()) {
1849 IConsolePrint(CC_HELP, "Leave the currently joined/running game (only ingame). Usage: 'part'.");
1850 return true;
1851 }
1852
1853 if (_game_mode != GM_NORMAL) return false;
1854
1855 if (_network_dedicated) {
1856 IConsolePrint(CC_ERROR, "A dedicated server can not leave the game.");
1857 return false;
1858 }
1859
1861 return true;
1862}
1863
1864static bool ConHelp(std::span<std::string_view> argv)
1865{
1866 if (argv.size() == 2) {
1867 const IConsoleCmd *cmd;
1868 const IConsoleAlias *alias;
1869
1870 cmd = IConsole::CmdGet(std::string(argv[1]));
1871 if (cmd != nullptr) {
1872 cmd->proc({});
1873 return true;
1874 }
1875
1876 alias = IConsole::AliasGet(std::string(argv[1]));
1877 if (alias != nullptr) {
1878 cmd = IConsole::CmdGet(alias->cmdline);
1879 if (cmd != nullptr) {
1880 cmd->proc({});
1881 return true;
1882 }
1883 IConsolePrint(CC_ERROR, "Alias is of special type, please see its execution-line: '{}'.", alias->cmdline);
1884 return true;
1885 }
1886
1887 IConsolePrint(CC_ERROR, "Command not found.");
1888 return true;
1889 }
1890
1891 IConsolePrint(TC_LIGHT_BLUE, " ---- OpenTTD Console Help ---- ");
1892 IConsolePrint(CC_DEFAULT, " - commands: the command to list all commands is 'list_cmds'.");
1893 IConsolePrint(CC_DEFAULT, " call commands with '<command> <arg2> <arg3>...'");
1894 IConsolePrint(CC_DEFAULT, " - to assign strings, or use them as arguments, enclose it within quotes.");
1895 IConsolePrint(CC_DEFAULT, " like this: '<command> \"string argument with spaces\"'.");
1896 IConsolePrint(CC_DEFAULT, " - use 'help <command>' to get specific information.");
1897 IConsolePrint(CC_DEFAULT, " - scroll console output with shift + (up | down | pageup | pagedown).");
1898 IConsolePrint(CC_DEFAULT, " - scroll console input history with the up or down arrows.");
1900 return true;
1901}
1902
1903static bool ConListCommands(std::span<std::string_view> argv)
1904{
1905 if (argv.empty()) {
1906 IConsolePrint(CC_HELP, "List all registered commands. Usage: 'list_cmds [<pre-filter>]'.");
1907 return true;
1908 }
1909
1910 for (auto &it : IConsole::Commands()) {
1911 const IConsoleCmd *cmd = &it.second;
1912 if (argv.size() <= 1|| cmd->name.find(argv[1]) != std::string::npos) {
1913 if (cmd->hook == nullptr || cmd->hook(false) != CHR_HIDE) IConsolePrint(CC_DEFAULT, cmd->name);
1914 }
1915 }
1916
1917 return true;
1918}
1919
1920static bool ConListAliases(std::span<std::string_view> argv)
1921{
1922 if (argv.empty()) {
1923 IConsolePrint(CC_HELP, "List all registered aliases. Usage: 'list_aliases [<pre-filter>]'.");
1924 return true;
1925 }
1926
1927 for (auto &it : IConsole::Aliases()) {
1928 const IConsoleAlias *alias = &it.second;
1929 if (argv.size() <= 1 || alias->name.find(argv[1]) != std::string::npos) {
1930 IConsolePrint(CC_DEFAULT, "{} => {}", alias->name, alias->cmdline);
1931 }
1932 }
1933
1934 return true;
1935}
1936
1937static bool ConCompanies(std::span<std::string_view> argv)
1938{
1939 if (argv.empty()) {
1940 IConsolePrint(CC_HELP, "List the details of all companies in the game. Usage 'companies'.");
1941 return true;
1942 }
1943
1944 for (const Company *c : Company::Iterate()) {
1945 /* Grab the company name */
1946 std::string company_name = GetString(STR_COMPANY_NAME, c->index);
1947
1948 std::string colour = GetString(STR_COLOUR_DARK_BLUE + _company_colours[c->index]);
1949 IConsolePrint(CC_INFO, "#:{}({}) Company Name: '{}' Year Founded: {} Money: {} Loan: {} Value: {} (T:{}, R:{}, P:{}, S:{}) {}",
1950 c->index + 1, colour, company_name,
1951 c->inaugurated_year, (int64_t)c->money, (int64_t)c->current_loan, (int64_t)CalculateCompanyValue(c),
1952 c->group_all[VEH_TRAIN].num_vehicle,
1953 c->group_all[VEH_ROAD].num_vehicle,
1954 c->group_all[VEH_AIRCRAFT].num_vehicle,
1955 c->group_all[VEH_SHIP].num_vehicle,
1956 c->is_ai ? "AI" : "");
1957 }
1958
1959 return true;
1960}
1961
1962static bool ConSay(std::span<std::string_view> argv)
1963{
1964 if (argv.empty()) {
1965 IConsolePrint(CC_HELP, "Chat to your fellow players in a multiplayer game. Usage: 'say \"<msg>\"'.");
1966 return true;
1967 }
1968
1969 if (argv.size() != 2) return false;
1970
1971 if (!_network_server) {
1972 NetworkClientSendChat(NETWORK_ACTION_CHAT, DESTTYPE_BROADCAST, 0 /* param does not matter */, argv[1]);
1973 } else {
1974 bool from_admin = (_redirect_console_to_admin < AdminID::Invalid());
1975 NetworkServerSendChat(NETWORK_ACTION_CHAT, DESTTYPE_BROADCAST, 0, argv[1], CLIENT_ID_SERVER, from_admin);
1976 }
1977
1978 return true;
1979}
1980
1981static bool ConSayCompany(std::span<std::string_view> argv)
1982{
1983 if (argv.empty()) {
1984 IConsolePrint(CC_HELP, "Chat to a certain company in a multiplayer game. Usage: 'say_company <company-no> \"<msg>\"'.");
1985 IConsolePrint(CC_HELP, "CompanyNo is the company that plays as company <companyno>, 1 through max_companies.");
1986 return true;
1987 }
1988
1989 if (argv.size() != 3) return false;
1990
1991 auto company_id = ParseCompanyID(argv[1]);
1992 if (!company_id.has_value()) {
1993 IConsolePrint(CC_ERROR, "The given company-id is not a valid number.");
1994 return true;
1995 }
1996
1997 if (!Company::IsValidID(*company_id)) {
1998 IConsolePrint(CC_DEFAULT, "Unknown company. Company range is between 1 and {}.", MAX_COMPANIES);
1999 return true;
2000 }
2001
2002 if (!_network_server) {
2003 NetworkClientSendChat(NETWORK_ACTION_CHAT_COMPANY, DESTTYPE_TEAM, company_id->base(), argv[2]);
2004 } else {
2005 bool from_admin = (_redirect_console_to_admin < AdminID::Invalid());
2006 NetworkServerSendChat(NETWORK_ACTION_CHAT_COMPANY, DESTTYPE_TEAM, company_id->base(), argv[2], CLIENT_ID_SERVER, from_admin);
2007 }
2008
2009 return true;
2010}
2011
2012static bool ConSayClient(std::span<std::string_view> argv)
2013{
2014 if (argv.empty()) {
2015 IConsolePrint(CC_HELP, "Chat to a certain client in a multiplayer game. Usage: 'say_client <client-id> \"<msg>\"'.");
2016 IConsolePrint(CC_HELP, "For client-id's, see the command 'clients'.");
2017 return true;
2018 }
2019
2020 if (argv.size() != 3) return false;
2021
2022 auto client_id = ParseType<ClientID>(argv[1]);
2023 if (!client_id.has_value()) {
2024 IConsolePrint(CC_ERROR, "The given client-id is not a valid number.");
2025 return true;
2026 }
2027
2028 if (!_network_server) {
2029 NetworkClientSendChat(NETWORK_ACTION_CHAT_CLIENT, DESTTYPE_CLIENT, *client_id, argv[2]);
2030 } else {
2031 bool from_admin = (_redirect_console_to_admin < AdminID::Invalid());
2032 NetworkServerSendChat(NETWORK_ACTION_CHAT_CLIENT, DESTTYPE_CLIENT, *client_id, argv[2], CLIENT_ID_SERVER, from_admin);
2033 }
2034
2035 return true;
2036}
2037
2039static const std::initializer_list<std::pair<std::string_view, NetworkAuthorizedKeys *>> _console_cmd_authorized_keys{
2043};
2044
2045enum ConNetworkAuthorizedKeyAction : uint8_t {
2046 CNAKA_LIST,
2047 CNAKA_ADD,
2048 CNAKA_REMOVE,
2049};
2050
2051static void PerformNetworkAuthorizedKeyAction(std::string_view name, NetworkAuthorizedKeys *authorized_keys, ConNetworkAuthorizedKeyAction action, const std::string &authorized_key, CompanyID company = CompanyID::Invalid())
2052{
2053 switch (action) {
2054 case CNAKA_LIST:
2055 IConsolePrint(CC_WHITE, "The authorized keys for {} are:", name);
2056 for (auto &ak : *authorized_keys) IConsolePrint(CC_INFO, " {}", ak);
2057 return;
2058
2059 case CNAKA_ADD:
2060 if (authorized_keys->Contains(authorized_key)) {
2061 IConsolePrint(CC_WARNING, "Not added {} to {} as it already exists.", authorized_key, name);
2062 return;
2063 }
2064
2065 if (company == CompanyID::Invalid()) {
2066 authorized_keys->Add(authorized_key);
2067 } else {
2068 AutoRestoreBackup backup(_current_company, company);
2070 }
2071 IConsolePrint(CC_INFO, "Added {} to {}.", authorized_key, name);
2072 return;
2073
2074 case CNAKA_REMOVE:
2075 if (!authorized_keys->Contains(authorized_key)) {
2076 IConsolePrint(CC_WARNING, "Not removed {} from {} as it does not exist.", authorized_key, name);
2077 return;
2078 }
2079
2080 if (company == CompanyID::Invalid()) {
2081 authorized_keys->Remove(authorized_key);
2082 } else {
2083 AutoRestoreBackup backup(_current_company, company);
2085 }
2086 IConsolePrint(CC_INFO, "Removed {} from {}.", authorized_key, name);
2087 return;
2088 }
2089}
2090
2091static bool ConNetworkAuthorizedKey(std::span<std::string_view> argv)
2092{
2093 if (argv.size() <= 2) {
2094 IConsolePrint(CC_HELP, "List and update authorized keys. Usage: 'authorized_key list [type]|add [type] [key]|remove [type] [key]'.");
2095 IConsolePrint(CC_HELP, " list: list all the authorized keys of the given type.");
2096 IConsolePrint(CC_HELP, " add: add the given key to the authorized keys of the given type.");
2097 IConsolePrint(CC_HELP, " remove: remove the given key from the authorized keys of the given type; use 'all' to remove all authorized keys.");
2098 IConsolePrint(CC_HELP, "Instead of a key, use 'client:<id>' to add/remove the key of that given client.");
2099
2100 std::string buffer;
2101 for (auto [name, _] : _console_cmd_authorized_keys) format_append(buffer, ", {}", name);
2102 IConsolePrint(CC_HELP, "The supported types are: all{} and company:<id>.", buffer);
2103 return true;
2104 }
2105
2106 ConNetworkAuthorizedKeyAction action;
2107 std::string_view action_string = argv[1];
2108 if (StrEqualsIgnoreCase(action_string, "list")) {
2109 action = CNAKA_LIST;
2110 } else if (StrEqualsIgnoreCase(action_string, "add")) {
2111 action = CNAKA_ADD;
2112 } else if (StrEqualsIgnoreCase(action_string, "remove") || StrEqualsIgnoreCase(action_string, "delete")) {
2113 action = CNAKA_REMOVE;
2114 } else {
2115 IConsolePrint(CC_WARNING, "No valid action was given.");
2116 return false;
2117 }
2118
2119 std::string authorized_key;
2120 if (action != CNAKA_LIST) {
2121 if (argv.size() <= 3) {
2122 IConsolePrint(CC_ERROR, "You must enter the key.");
2123 return false;
2124 }
2125
2126 authorized_key = argv[3];
2127 if (StrStartsWithIgnoreCase(authorized_key, "client:")) {
2128 auto value = ParseInteger<uint32_t>(authorized_key.substr(7));
2129 if (value.has_value()) authorized_key = NetworkGetPublicKeyOfClient(static_cast<ClientID>(*value));
2130 if (!value.has_value() || authorized_key.empty()) {
2131 IConsolePrint(CC_ERROR, "You must enter a valid client id; see 'clients'.");
2132 return false;
2133 }
2134 }
2135
2136 if (authorized_key.size() != NETWORK_PUBLIC_KEY_LENGTH - 1) {
2137 IConsolePrint(CC_ERROR, "You must enter a valid authorized key.");
2138 return false;
2139 }
2140 }
2141
2142 std::string_view type = argv[2];
2143 if (StrEqualsIgnoreCase(type, "all")) {
2144 for (auto [name, authorized_keys] : _console_cmd_authorized_keys) PerformNetworkAuthorizedKeyAction(name, authorized_keys, action, authorized_key);
2145 for (Company *c : Company::Iterate()) PerformNetworkAuthorizedKeyAction(fmt::format("company:{}", c->index + 1), &c->allow_list, action, authorized_key, c->index);
2146 return true;
2147 }
2148
2149 if (StrStartsWithIgnoreCase(type, "company:")) {
2150 auto value = ParseInteger<uint32_t>(type.substr(8));
2151 Company *c = value.has_value() ? Company::GetIfValid(*value - 1) : nullptr;
2152 if (c == nullptr) {
2153 IConsolePrint(CC_ERROR, "You must enter a valid company id; see 'companies'.");
2154 return false;
2155 }
2156
2157 PerformNetworkAuthorizedKeyAction(type, &c->allow_list, action, authorized_key, c->index);
2158 return true;
2159 }
2160
2161 for (auto [name, authorized_keys] : _console_cmd_authorized_keys) {
2162 if (StrEqualsIgnoreCase(type, name)) continue;
2163
2164 PerformNetworkAuthorizedKeyAction(name, authorized_keys, action, authorized_key);
2165 return true;
2166 }
2167
2168 IConsolePrint(CC_WARNING, "No valid type was given.");
2169 return false;
2170}
2171
2172
2173/* Content downloading only is available with ZLIB */
2174#if defined(WITH_ZLIB)
2176
2178static ContentType StringToContentType(std::string_view str)
2179{
2180 static const std::initializer_list<std::pair<std::string_view, ContentType>> content_types = {
2182 {"newgrf", CONTENT_TYPE_NEWGRF},
2183 {"ai", CONTENT_TYPE_AI},
2184 {"ailib", CONTENT_TYPE_AI_LIBRARY},
2185 {"scenario", CONTENT_TYPE_SCENARIO},
2186 {"heightmap", CONTENT_TYPE_HEIGHTMAP},
2187 };
2188 for (const auto &ct : content_types) {
2189 if (StrEqualsIgnoreCase(str, ct.first)) return ct.second;
2190 }
2191 return CONTENT_TYPE_END;
2192}
2193
2196 void OnConnect(bool success) override
2197 {
2198 IConsolePrint(CC_DEFAULT, "Content server connection {}.", success ? "established" : "failed");
2199 }
2200
2201 void OnDisconnect() override
2202 {
2203 IConsolePrint(CC_DEFAULT, "Content server connection closed.");
2204 }
2205
2207 {
2208 IConsolePrint(CC_DEFAULT, "Completed download of {}.", cid);
2209 }
2210};
2211
2216static void OutputContentState(const ContentInfo &ci)
2217{
2218 static const std::string_view types[] = { "Base graphics", "NewGRF", "AI", "AI library", "Scenario", "Heightmap", "Base sound", "Base music", "Game script", "GS library" };
2219 static_assert(lengthof(types) == CONTENT_TYPE_END - CONTENT_TYPE_BEGIN);
2220 static const std::string_view states[] = { "Not selected", "Selected", "Dep Selected", "Installed", "Unknown" };
2221 static const TextColour state_to_colour[] = { CC_COMMAND, CC_INFO, CC_INFO, CC_WHITE, CC_ERROR };
2222
2223 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));
2224}
2225
2226static bool ConContent(std::span<std::string_view> argv)
2227{
2228 [[maybe_unused]] static ContentCallback *const cb = []() {
2229 auto res = new ConsoleContentCallback();
2231 return res;
2232 }();
2233
2234 if (argv.size() <= 1) {
2235 IConsolePrint(CC_HELP, "Query, select and download content. Usage: 'content update|upgrade|select [id]|unselect [all|id]|state [filter]|download'.");
2236 IConsolePrint(CC_HELP, " update: get a new list of downloadable content; must be run first.");
2237 IConsolePrint(CC_HELP, " upgrade: select all items that are upgrades.");
2238 IConsolePrint(CC_HELP, " select: select a specific item given by its id. If no parameter is given, all selected content will be listed.");
2239 IConsolePrint(CC_HELP, " unselect: unselect a specific item given by its id or 'all' to unselect all.");
2240 IConsolePrint(CC_HELP, " state: show the download/select state of all downloadable content. Optionally give a filter string.");
2241 IConsolePrint(CC_HELP, " download: download all content you've selected.");
2242 return true;
2243 }
2244
2245 if (StrEqualsIgnoreCase(argv[1], "update")) {
2247 return true;
2248 }
2249
2250 if (StrEqualsIgnoreCase(argv[1], "upgrade")) {
2252 return true;
2253 }
2254
2255 if (StrEqualsIgnoreCase(argv[1], "select")) {
2256 if (argv.size() <= 2) {
2257 /* List selected content */
2258 IConsolePrint(CC_WHITE, "id, type, state, name");
2259 for (const ContentInfo &ci : _network_content_client.Info()) {
2260 if (ci.state != ContentInfo::State::Selected && ci.state != ContentInfo::State::Autoselected) continue;
2262 }
2263 } else if (StrEqualsIgnoreCase(argv[2], "all")) {
2264 /* The intention of this function was that you could download
2265 * everything after a filter was applied; but this never really
2266 * took off. Instead, a select few people used this functionality
2267 * to download every available package on BaNaNaS. This is not in
2268 * the spirit of this service. Additionally, these few people were
2269 * good for 70% of the consumed bandwidth of BaNaNaS. */
2270 IConsolePrint(CC_ERROR, "'select all' is no longer supported since 1.11.");
2271 } else if (auto content_id = ParseType<ContentID>(argv[2]); content_id.has_value()) {
2272 _network_content_client.Select(*content_id);
2273 } else {
2274 IConsolePrint(CC_ERROR, "The given content-id is not a number or 'all'");
2275 }
2276 return true;
2277 }
2278
2279 if (StrEqualsIgnoreCase(argv[1], "unselect")) {
2280 if (argv.size() <= 2) {
2281 IConsolePrint(CC_ERROR, "You must enter the id.");
2282 return false;
2283 }
2284 if (StrEqualsIgnoreCase(argv[2], "all")) {
2286 } else if (auto content_id = ParseType<ContentID>(argv[2]); content_id.has_value()) {
2287 _network_content_client.Unselect(*content_id);
2288 } else {
2289 IConsolePrint(CC_ERROR, "The given content-id is not a number or 'all'");
2290 }
2291 return true;
2292 }
2293
2294 if (StrEqualsIgnoreCase(argv[1], "state")) {
2295 IConsolePrint(CC_WHITE, "id, type, state, name");
2296 for (const ContentInfo &ci : _network_content_client.Info()) {
2297 if (argv.size() > 2 && !StrContainsIgnoreCase(ci.name, argv[2])) continue;
2299 }
2300 return true;
2301 }
2302
2303 if (StrEqualsIgnoreCase(argv[1], "download")) {
2304 uint files;
2305 uint bytes;
2307 IConsolePrint(CC_DEFAULT, "Downloading {} file(s) ({} bytes).", files, bytes);
2308 return true;
2309 }
2310
2311 return false;
2312}
2313#endif /* defined(WITH_ZLIB) */
2314
2315static bool ConFont(std::span<std::string_view> argv)
2316{
2317 if (argv.empty()) {
2318 IConsolePrint(CC_HELP, "Manage the fonts configuration.");
2319 IConsolePrint(CC_HELP, "Usage 'font'.");
2320 IConsolePrint(CC_HELP, " Print out the fonts configuration.");
2321 IConsolePrint(CC_HELP, " The \"Currently active\" configuration is the one actually in effect (after interface scaling and replacing unavailable fonts).");
2322 IConsolePrint(CC_HELP, " The \"Requested\" configuration is the one requested via console command or config file.");
2323 IConsolePrint(CC_HELP, "Usage 'font [medium|small|large|mono] [<font name>] [<size>]'.");
2324 IConsolePrint(CC_HELP, " Change the configuration for a font.");
2325 IConsolePrint(CC_HELP, " Omitting an argument will keep the current value.");
2326 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.");
2327 IConsolePrint(CC_HELP, " If the sprite font is enabled in Game Options, it is used instead of the default font.");
2328 IConsolePrint(CC_HELP, " The <size> is automatically multiplied by the current interface scaling.");
2329 return true;
2330 }
2331
2332 FontSize argfs;
2333 for (argfs = FS_BEGIN; argfs < FS_END; argfs++) {
2334 if (argv.size() > 1 && StrEqualsIgnoreCase(argv[1], FontSizeToName(argfs))) break;
2335 }
2336
2337 /* First argument must be a FontSize. */
2338 if (argv.size() > 1 && argfs == FS_END) return false;
2339
2340 if (argv.size() > 2) {
2342 std::string font = setting->font;
2343 uint size = setting->size;
2344 uint8_t arg_index = 2;
2345 /* For <name> we want a string. */
2346
2347 if (!ParseInteger(argv[arg_index]).has_value()) {
2348 font = argv[arg_index++];
2349 }
2350
2351 if (argv.size() > arg_index) {
2352 /* For <size> we want a number. */
2353 auto v = ParseInteger(argv[arg_index]);
2354 if (v.has_value()) {
2355 size = *v;
2356 arg_index++;
2357 }
2358 }
2359
2360 SetFont(argfs, font, size);
2361 }
2362
2363 for (FontSize fs = FS_BEGIN; fs < FS_END; fs++) {
2364 FontCache *fc = FontCache::Get(fs);
2366 /* Make sure all non sprite fonts are loaded. */
2367 if (!setting->font.empty() && !fc->HasParent()) {
2368 InitFontCache(fs == FS_MONO);
2369 fc = FontCache::Get(fs);
2370 }
2371 IConsolePrint(CC_DEFAULT, "{} font:", FontSizeToName(fs));
2372 IConsolePrint(CC_DEFAULT, "Currently active: \"{}\", size {}", fc->GetFontName(), fc->GetFontSize());
2373 IConsolePrint(CC_DEFAULT, "Requested: \"{}\", size {}", setting->font, setting->size);
2374 }
2375
2376 return true;
2377}
2378
2379static bool ConSetting(std::span<std::string_view> argv)
2380{
2381 if (argv.empty()) {
2382 IConsolePrint(CC_HELP, "Change setting for all clients. Usage: 'setting <name> [<value>]'.");
2383 IConsolePrint(CC_HELP, "Omitting <value> will print out the current value of the setting.");
2384 return true;
2385 }
2386
2387 if (argv.size() == 1 || argv.size() > 3) return false;
2388
2389 if (argv.size() == 2) {
2390 IConsoleGetSetting(argv[1]);
2391 } else {
2392 IConsoleSetSetting(argv[1], argv[2]);
2393 }
2394
2395 return true;
2396}
2397
2398static bool ConSettingNewgame(std::span<std::string_view> argv)
2399{
2400 if (argv.empty()) {
2401 IConsolePrint(CC_HELP, "Change setting for the next game. Usage: 'setting_newgame <name> [<value>]'.");
2402 IConsolePrint(CC_HELP, "Omitting <value> will print out the current value of the setting.");
2403 return true;
2404 }
2405
2406 if (argv.size() == 1 || argv.size() > 3) return false;
2407
2408 if (argv.size() == 2) {
2409 IConsoleGetSetting(argv[1], true);
2410 } else {
2411 IConsoleSetSetting(argv[1], argv[2], true);
2412 }
2413
2414 return true;
2415}
2416
2417static bool ConListSettings(std::span<std::string_view> argv)
2418{
2419 if (argv.empty()) {
2420 IConsolePrint(CC_HELP, "List settings. Usage: 'list_settings [<pre-filter>]'.");
2421 return true;
2422 }
2423
2424 if (argv.size() > 2) return false;
2425
2426 IConsoleListSettings((argv.size() == 2) ? argv[1] : std::string_view{});
2427 return true;
2428}
2429
2430static bool ConGamelogPrint(std::span<std::string_view> argv)
2431{
2432 if (argv.empty()) {
2433 IConsolePrint(CC_HELP, "Print logged fundamental changes to the game since the start. Usage: 'gamelog'.");
2434 return true;
2435 }
2436
2438 return true;
2439}
2440
2441static bool ConNewGRFReload(std::span<std::string_view> argv)
2442{
2443 if (argv.empty()) {
2444 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!");
2445 return true;
2446 }
2447
2449 return true;
2450}
2451
2452static bool ConListDirs(std::span<std::string_view> argv)
2453{
2454 struct SubdirNameMap {
2455 Subdirectory subdir;
2456 std::string_view name;
2457 bool default_only;
2458 };
2459 static const SubdirNameMap subdir_name_map[] = {
2460 /* Game data directories */
2461 { BASESET_DIR, "baseset", false },
2462 { NEWGRF_DIR, "newgrf", false },
2463 { AI_DIR, "ai", false },
2464 { AI_LIBRARY_DIR, "ailib", false },
2465 { GAME_DIR, "gs", false },
2466 { GAME_LIBRARY_DIR, "gslib", false },
2467 { SCENARIO_DIR, "scenario", false },
2468 { HEIGHTMAP_DIR, "heightmap", false },
2469 /* Default save locations for user data */
2470 { SAVE_DIR, "save", true },
2471 { AUTOSAVE_DIR, "autosave", true },
2472 { SCREENSHOT_DIR, "screenshot", true },
2473 { SOCIAL_INTEGRATION_DIR, "social_integration", true },
2474 };
2475
2476 if (argv.size() != 2) {
2477 IConsolePrint(CC_HELP, "List all search paths or default directories for various categories.");
2478 IConsolePrint(CC_HELP, "Usage: list_dirs <category>");
2479 std::string cats{subdir_name_map[0].name};
2480 bool first = true;
2481 for (const SubdirNameMap &sdn : subdir_name_map) {
2482 if (!first) {
2483 cats += ", ";
2484 cats += sdn.name;
2485 }
2486 first = false;
2487 }
2488 IConsolePrint(CC_HELP, "Valid categories: {}", cats);
2489 return true;
2490 }
2491
2492 std::set<std::string> seen_dirs;
2493 for (const SubdirNameMap &sdn : subdir_name_map) {
2494 if (!StrEqualsIgnoreCase(argv[1], sdn.name)) continue;
2495 bool found = false;
2496 for (Searchpath sp : _valid_searchpaths) {
2497 /* Get the directory */
2498 std::string path = FioGetDirectory(sp, sdn.subdir);
2499 /* Check it hasn't already been listed */
2500 if (seen_dirs.find(path) != seen_dirs.end()) continue;
2501 seen_dirs.insert(path);
2502 /* Check if exists and mark found */
2503 bool exists = FileExists(path);
2504 found |= exists;
2505 /* Print */
2506 if (!sdn.default_only || exists) {
2507 IConsolePrint(exists ? CC_DEFAULT : CC_INFO, "{} {}", path, exists ? "[ok]" : "[not found]");
2508 if (sdn.default_only) break;
2509 }
2510 }
2511 if (!found) {
2512 IConsolePrint(CC_ERROR, "No directories exist for category {}", argv[1]);
2513 }
2514 return true;
2515 }
2516
2517 IConsolePrint(CC_ERROR, "Invalid category name: {}", argv[1]);
2518 return false;
2519}
2520
2521static bool ConNewGRFProfile(std::span<std::string_view> argv)
2522{
2523 if (argv.empty()) {
2524 IConsolePrint(CC_HELP, "Collect performance data about NewGRF sprite requests and callbacks. Sub-commands can be abbreviated.");
2525 IConsolePrint(CC_HELP, "Usage: 'newgrf_profile [list]':");
2526 IConsolePrint(CC_HELP, " List all NewGRFs that can be profiled, and their status.");
2527 IConsolePrint(CC_HELP, "Usage: 'newgrf_profile select <grf-num>...':");
2528 IConsolePrint(CC_HELP, " Select one or more GRFs for profiling.");
2529 IConsolePrint(CC_HELP, "Usage: 'newgrf_profile unselect <grf-num>...':");
2530 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.");
2531 IConsolePrint(CC_HELP, "Usage: 'newgrf_profile start [<num-ticks>]':");
2532 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.");
2533 IConsolePrint(CC_HELP, "Usage: 'newgrf_profile stop':");
2534 IConsolePrint(CC_HELP, " End profiling and write the collected data to CSV files.");
2535 IConsolePrint(CC_HELP, "Usage: 'newgrf_profile abort':");
2536 IConsolePrint(CC_HELP, " End profiling and discard all collected data.");
2537 return true;
2538 }
2539
2540 std::span<const GRFFile> files = GetAllGRFFiles();
2541
2542 /* "list" sub-command */
2543 if (argv.size() == 1 || StrStartsWithIgnoreCase(argv[1], "lis")) {
2544 IConsolePrint(CC_INFO, "Loaded GRF files:");
2545 int i = 1;
2546 for (const auto &grf : files) {
2547 auto profiler = std::ranges::find(_newgrf_profilers, &grf, &NewGRFProfiler::grffile);
2548 bool selected = profiler != _newgrf_profilers.end();
2549 bool active = selected && profiler->active;
2550 TextColour tc = active ? TC_LIGHT_BLUE : selected ? TC_GREEN : CC_INFO;
2551 std::string_view statustext = active ? " (active)" : selected ? " (selected)" : "";
2552 IConsolePrint(tc, "{}: [{:08X}] {}{}", i, std::byteswap(grf.grfid), grf.filename, statustext);
2553 i++;
2554 }
2555 return true;
2556 }
2557
2558 /* "select" sub-command */
2559 if (StrStartsWithIgnoreCase(argv[1], "sel") && argv.size() >= 3) {
2560 for (size_t argnum = 2; argnum < argv.size(); ++argnum) {
2561 auto grfnum = ParseInteger(argv[argnum]);
2562 if (!grfnum.has_value() || *grfnum < 1 || static_cast<size_t>(*grfnum) > files.size()) {
2563 IConsolePrint(CC_WARNING, "GRF number {} out of range, not added.", *grfnum);
2564 continue;
2565 }
2566 const GRFFile *grf = &files[*grfnum - 1];
2567 if (std::any_of(_newgrf_profilers.begin(), _newgrf_profilers.end(), [&](NewGRFProfiler &pr) { return pr.grffile == grf; })) {
2568 IConsolePrint(CC_WARNING, "GRF number {} [{:08X}] is already selected for profiling.", *grfnum, std::byteswap(grf->grfid));
2569 continue;
2570 }
2571 _newgrf_profilers.emplace_back(grf);
2572 }
2573 return true;
2574 }
2575
2576 /* "unselect" sub-command */
2577 if (StrStartsWithIgnoreCase(argv[1], "uns") && argv.size() >= 3) {
2578 for (size_t argnum = 2; argnum < argv.size(); ++argnum) {
2579 if (StrEqualsIgnoreCase(argv[argnum], "all")) {
2580 _newgrf_profilers.clear();
2581 break;
2582 }
2583 auto grfnum = ParseInteger(argv[argnum]);
2584 if (!grfnum.has_value() || *grfnum < 1 || static_cast<size_t>(*grfnum) > files.size()) {
2585 IConsolePrint(CC_WARNING, "GRF number {} out of range, not removing.", *grfnum);
2586 continue;
2587 }
2588 const GRFFile *grf = &files[*grfnum - 1];
2589 _newgrf_profilers.erase(std::ranges::find(_newgrf_profilers, grf, &NewGRFProfiler::grffile));
2590 }
2591 return true;
2592 }
2593
2594 /* "start" sub-command */
2595 if (StrStartsWithIgnoreCase(argv[1], "sta")) {
2596 std::string grfids;
2597 size_t started = 0;
2598 for (NewGRFProfiler &pr : _newgrf_profilers) {
2599 if (!pr.active) {
2600 pr.Start();
2601 started++;
2602
2603 if (!grfids.empty()) grfids += ", ";
2604 format_append(grfids, "[{:08X}]", std::byteswap(pr.grffile->grfid));
2605 }
2606 }
2607 if (started > 0) {
2608 IConsolePrint(CC_DEBUG, "Started profiling for GRFID{} {}.", (started > 1) ? "s" : "", grfids);
2609
2610 if (argv.size() >= 3) {
2611 auto ticks = StringConsumer{argv[2]}.TryReadIntegerBase<uint64_t>(0);
2612 if (!ticks.has_value()) {
2613 IConsolePrint(CC_ERROR, "No valid amount of ticks was given, profiling will not stop automatically.");
2614 } else {
2616 IConsolePrint(CC_DEBUG, "Profiling will automatically stop after {} ticks.", *ticks);
2617 }
2618 }
2619 } else if (_newgrf_profilers.empty()) {
2620 IConsolePrint(CC_ERROR, "No GRFs selected for profiling, did not start.");
2621 } else {
2622 IConsolePrint(CC_ERROR, "Did not start profiling for any GRFs, all selected GRFs are already profiling.");
2623 }
2624 return true;
2625 }
2626
2627 /* "stop" sub-command */
2628 if (StrStartsWithIgnoreCase(argv[1], "sto")) {
2629 NewGRFProfiler::FinishAll();
2630 return true;
2631 }
2632
2633 /* "abort" sub-command */
2634 if (StrStartsWithIgnoreCase(argv[1], "abo")) {
2635 for (NewGRFProfiler &pr : _newgrf_profilers) {
2636 pr.Abort();
2637 }
2639 return true;
2640 }
2641
2642 return false;
2643}
2644
2645#ifdef _DEBUG
2646/******************
2647 * debug commands
2648 ******************/
2649
2650static void IConsoleDebugLibRegister()
2651{
2652 IConsole::CmdRegister("resettile", ConResetTile);
2653 IConsole::AliasRegister("dbg_echo", "echo %A; echo %B");
2654 IConsole::AliasRegister("dbg_echo2", "echo %!");
2655}
2656#endif
2657
2658static bool ConFramerate(std::span<std::string_view> argv)
2659{
2660 if (argv.empty()) {
2661 IConsolePrint(CC_HELP, "Show frame rate and game speed information.");
2662 return true;
2663 }
2664
2666 return true;
2667}
2668
2669static bool ConFramerateWindow(std::span<std::string_view> argv)
2670{
2671 if (argv.empty()) {
2672 IConsolePrint(CC_HELP, "Open the frame rate window.");
2673 return true;
2674 }
2675
2676 if (_network_dedicated) {
2677 IConsolePrint(CC_ERROR, "Can not open frame rate window on a dedicated server.");
2678 return false;
2679 }
2680
2682 return true;
2683}
2684
2692static std::string FormatLabel(uint32_t label)
2693{
2694 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))) {
2695 return fmt::format("{:c}{:c}{:c}{:c}", GB(label, 24, 8), GB(label, 16, 8), GB(label, 8, 8), GB(label, 0, 8));
2696 }
2697
2698 return fmt::format("{:08X}", std::byteswap(label));
2699}
2700
2701static void ConDumpRoadTypes()
2702{
2703 IConsolePrint(CC_DEFAULT, " Flags:");
2704 IConsolePrint(CC_DEFAULT, " c = catenary");
2705 IConsolePrint(CC_DEFAULT, " l = no level crossings");
2706 IConsolePrint(CC_DEFAULT, " X = no houses");
2707 IConsolePrint(CC_DEFAULT, " h = hidden");
2708 IConsolePrint(CC_DEFAULT, " T = buildable by towns");
2709
2710 std::map<uint32_t, const GRFFile *> grfs;
2711 for (RoadType rt = ROADTYPE_BEGIN; rt < ROADTYPE_END; rt++) {
2712 const RoadTypeInfo *rti = GetRoadTypeInfo(rt);
2713 if (rti->label == 0) continue;
2714 uint32_t grfid = 0;
2715 const GRFFile *grf = rti->grffile[ROTSG_GROUND];
2716 if (grf != nullptr) {
2717 grfid = grf->grfid;
2718 grfs.emplace(grfid, grf);
2719 }
2720 IConsolePrint(CC_DEFAULT, " {:02d} {} {}, Flags: {}{}{}{}{}, GRF: {:08X}, {}",
2721 (uint)rt,
2722 RoadTypeIsTram(rt) ? "Tram" : "Road",
2723 FormatLabel(rti->label),
2724 rti->flags.Test(RoadTypeFlag::Catenary) ? 'c' : '-',
2725 rti->flags.Test(RoadTypeFlag::NoLevelCrossing) ? 'l' : '-',
2726 rti->flags.Test(RoadTypeFlag::NoHouses) ? 'X' : '-',
2727 rti->flags.Test(RoadTypeFlag::Hidden) ? 'h' : '-',
2728 rti->flags.Test(RoadTypeFlag::TownBuild) ? 'T' : '-',
2729 std::byteswap(grfid),
2730 GetStringPtr(rti->strings.name)
2731 );
2732 }
2733 for (const auto &grf : grfs) {
2734 IConsolePrint(CC_DEFAULT, " GRF: {:08X} = {}", std::byteswap(grf.first), grf.second->filename);
2735 }
2736}
2737
2738static void ConDumpRailTypes()
2739{
2740 IConsolePrint(CC_DEFAULT, " Flags:");
2741 IConsolePrint(CC_DEFAULT, " c = catenary");
2742 IConsolePrint(CC_DEFAULT, " l = no level crossings");
2743 IConsolePrint(CC_DEFAULT, " h = hidden");
2744 IConsolePrint(CC_DEFAULT, " s = no sprite combine");
2745 IConsolePrint(CC_DEFAULT, " a = always allow 90 degree turns");
2746 IConsolePrint(CC_DEFAULT, " d = always disallow 90 degree turns");
2747
2748 std::map<uint32_t, const GRFFile *> grfs;
2749 for (RailType rt = RAILTYPE_BEGIN; rt < RAILTYPE_END; rt++) {
2750 const RailTypeInfo *rti = GetRailTypeInfo(rt);
2751 if (rti->label == 0) continue;
2752 uint32_t grfid = 0;
2753 const GRFFile *grf = rti->grffile[RTSG_GROUND];
2754 if (grf != nullptr) {
2755 grfid = grf->grfid;
2756 grfs.emplace(grfid, grf);
2757 }
2758 IConsolePrint(CC_DEFAULT, " {:02d} {}, Flags: {}{}{}{}{}{}, GRF: {:08X}, {}",
2759 (uint)rt,
2760 FormatLabel(rti->label),
2761 rti->flags.Test(RailTypeFlag::Catenary) ? 'c' : '-',
2762 rti->flags.Test(RailTypeFlag::NoLevelCrossing) ? 'l' : '-',
2763 rti->flags.Test(RailTypeFlag::Hidden) ? 'h' : '-',
2764 rti->flags.Test(RailTypeFlag::NoSpriteCombine) ? 's' : '-',
2765 rti->flags.Test(RailTypeFlag::Allow90Deg) ? 'a' : '-',
2766 rti->flags.Test(RailTypeFlag::Disallow90Deg) ? 'd' : '-',
2767 std::byteswap(grfid),
2768 GetStringPtr(rti->strings.name)
2769 );
2770 }
2771 for (const auto &grf : grfs) {
2772 IConsolePrint(CC_DEFAULT, " GRF: {:08X} = {}", std::byteswap(grf.first), grf.second->filename);
2773 }
2774}
2775
2776static void ConDumpCargoTypes()
2777{
2778 IConsolePrint(CC_DEFAULT, " Cargo classes:");
2779 IConsolePrint(CC_DEFAULT, " p = passenger");
2780 IConsolePrint(CC_DEFAULT, " m = mail");
2781 IConsolePrint(CC_DEFAULT, " x = express");
2782 IConsolePrint(CC_DEFAULT, " a = armoured");
2783 IConsolePrint(CC_DEFAULT, " b = bulk");
2784 IConsolePrint(CC_DEFAULT, " g = piece goods");
2785 IConsolePrint(CC_DEFAULT, " l = liquid");
2786 IConsolePrint(CC_DEFAULT, " r = refrigerated");
2787 IConsolePrint(CC_DEFAULT, " h = hazardous");
2788 IConsolePrint(CC_DEFAULT, " c = covered/sheltered");
2789 IConsolePrint(CC_DEFAULT, " o = oversized");
2790 IConsolePrint(CC_DEFAULT, " d = powderized");
2791 IConsolePrint(CC_DEFAULT, " n = not pourable");
2792 IConsolePrint(CC_DEFAULT, " e = potable");
2793 IConsolePrint(CC_DEFAULT, " i = non-potable");
2794 IConsolePrint(CC_DEFAULT, " S = special");
2795
2796 std::map<uint32_t, const GRFFile *> grfs;
2797 for (const CargoSpec *spec : CargoSpec::Iterate()) {
2798 uint32_t grfid = 0;
2799 const GRFFile *grf = spec->grffile;
2800 if (grf != nullptr) {
2801 grfid = grf->grfid;
2802 grfs.emplace(grfid, grf);
2803 }
2804 IConsolePrint(CC_DEFAULT, " {:02d} Bit: {:2d}, Label: {}, Callback mask: 0x{:02X}, Cargo class: {}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}, GRF: {:08X}, {}",
2805 spec->Index(),
2806 spec->bitnum,
2807 FormatLabel(spec->label.base()),
2808 spec->callback_mask.base(),
2809 spec->classes.Test(CargoClass::Passengers) ? 'p' : '-',
2810 spec->classes.Test(CargoClass::Mail) ? 'm' : '-',
2811 spec->classes.Test(CargoClass::Express) ? 'x' : '-',
2812 spec->classes.Test(CargoClass::Armoured) ? 'a' : '-',
2813 spec->classes.Test(CargoClass::Bulk) ? 'b' : '-',
2814 spec->classes.Test(CargoClass::PieceGoods) ? 'g' : '-',
2815 spec->classes.Test(CargoClass::Liquid) ? 'l' : '-',
2816 spec->classes.Test(CargoClass::Refrigerated) ? 'r' : '-',
2817 spec->classes.Test(CargoClass::Hazardous) ? 'h' : '-',
2818 spec->classes.Test(CargoClass::Covered) ? 'c' : '-',
2819 spec->classes.Test(CargoClass::Oversized) ? 'o' : '-',
2820 spec->classes.Test(CargoClass::Powderized) ? 'd' : '-',
2821 spec->classes.Test(CargoClass::NotPourable) ? 'n' : '-',
2822 spec->classes.Test(CargoClass::Potable) ? 'e' : '-',
2823 spec->classes.Test(CargoClass::NonPotable) ? 'i' : '-',
2824 spec->classes.Test(CargoClass::Special) ? 'S' : '-',
2825 std::byteswap(grfid),
2826 GetStringPtr(spec->name)
2827 );
2828 }
2829 for (const auto &grf : grfs) {
2830 IConsolePrint(CC_DEFAULT, " GRF: {:08X} = {}", std::byteswap(grf.first), grf.second->filename);
2831 }
2832}
2833
2834static bool ConDumpInfo(std::span<std::string_view> argv)
2835{
2836 if (argv.size() != 2) {
2837 IConsolePrint(CC_HELP, "Dump debugging information.");
2838 IConsolePrint(CC_HELP, "Usage: 'dump_info roadtypes|railtypes|cargotypes'.");
2839 IConsolePrint(CC_HELP, " Show information about road/tram types, rail types or cargo types.");
2840 return true;
2841 }
2842
2843 if (StrEqualsIgnoreCase(argv[1], "roadtypes")) {
2844 ConDumpRoadTypes();
2845 return true;
2846 }
2847
2848 if (StrEqualsIgnoreCase(argv[1], "railtypes")) {
2849 ConDumpRailTypes();
2850 return true;
2851 }
2852
2853 if (StrEqualsIgnoreCase(argv[1], "cargotypes")) {
2854 ConDumpCargoTypes();
2855 return true;
2856 }
2857
2858 return false;
2859}
2860
2861/*******************************
2862 * console command registration
2863 *******************************/
2864
2865void IConsoleStdLibRegister()
2866{
2867 IConsole::CmdRegister("debug_level", ConDebugLevel);
2868 IConsole::CmdRegister("echo", ConEcho);
2869 IConsole::CmdRegister("echoc", ConEchoC);
2870 IConsole::CmdRegister("exec", ConExec);
2871 IConsole::CmdRegister("schedule", ConSchedule);
2872 IConsole::CmdRegister("exit", ConExit);
2873 IConsole::CmdRegister("part", ConPart);
2874 IConsole::CmdRegister("help", ConHelp);
2875 IConsole::CmdRegister("info_cmd", ConInfoCmd);
2876 IConsole::CmdRegister("list_cmds", ConListCommands);
2877 IConsole::CmdRegister("list_aliases", ConListAliases);
2878 IConsole::CmdRegister("newgame", ConNewGame);
2879 IConsole::CmdRegister("restart", ConRestart);
2880 IConsole::CmdRegister("reload", ConReload);
2881 IConsole::CmdRegister("getseed", ConGetSeed);
2882 IConsole::CmdRegister("getdate", ConGetDate);
2883 IConsole::CmdRegister("getsysdate", ConGetSysDate);
2884 IConsole::CmdRegister("quit", ConExit);
2887 IConsole::CmdRegister("return", ConReturn);
2888 IConsole::CmdRegister("screenshot", ConScreenShot);
2889 IConsole::CmdRegister("script", ConScript);
2892 IConsole::CmdRegister("alias", ConAlias);
2893 IConsole::CmdRegister("load", ConLoad);
2894 IConsole::CmdRegister("load_save", ConLoad);
2895 IConsole::CmdRegister("load_scenario", ConLoadScenario);
2896 IConsole::CmdRegister("load_heightmap", ConLoadHeightmap);
2897 IConsole::CmdRegister("rm", ConRemove);
2899 IConsole::CmdRegister("saveconfig", ConSaveConfig);
2900 IConsole::CmdRegister("ls", ConListFiles);
2901 IConsole::CmdRegister("list_saves", ConListFiles);
2902 IConsole::CmdRegister("list_scenarios", ConListScenarios);
2903 IConsole::CmdRegister("list_heightmaps", ConListHeightmaps);
2904 IConsole::CmdRegister("cd", ConChangeDirectory);
2905 IConsole::CmdRegister("pwd", ConPrintWorkingDirectory);
2906 IConsole::CmdRegister("clear", ConClearBuffer);
2907 IConsole::CmdRegister("font", ConFont);
2908 IConsole::CmdRegister("setting", ConSetting);
2909 IConsole::CmdRegister("setting_newgame", ConSettingNewgame);
2910 IConsole::CmdRegister("list_settings", ConListSettings);
2911 IConsole::CmdRegister("gamelog", ConGamelogPrint);
2912 IConsole::CmdRegister("rescan_newgrf", ConRescanNewGRF);
2913 IConsole::CmdRegister("list_dirs", ConListDirs);
2914
2915 IConsole::AliasRegister("dir", "ls");
2916 IConsole::AliasRegister("del", "rm %+");
2917 IConsole::AliasRegister("newmap", "newgame");
2918 IConsole::AliasRegister("patch", "setting %+");
2919 IConsole::AliasRegister("set", "setting %+");
2920 IConsole::AliasRegister("set_newgame", "setting_newgame %+");
2921 IConsole::AliasRegister("list_patches", "list_settings %+");
2922 IConsole::AliasRegister("developer", "setting developer %+");
2923
2924 IConsole::CmdRegister("list_ai_libs", ConListAILibs);
2925 IConsole::CmdRegister("list_ai", ConListAI);
2926 IConsole::CmdRegister("reload_ai", ConReloadAI);
2927 IConsole::CmdRegister("rescan_ai", ConRescanAI);
2928 IConsole::CmdRegister("start_ai", ConStartAI);
2929 IConsole::CmdRegister("stop_ai", ConStopAI);
2930
2931 IConsole::CmdRegister("list_game", ConListGame);
2932 IConsole::CmdRegister("list_game_libs", ConListGameLibs);
2933 IConsole::CmdRegister("rescan_game", ConRescanGame);
2934
2935 IConsole::CmdRegister("companies", ConCompanies);
2936 IConsole::AliasRegister("players", "companies");
2937
2938 /* networking functions */
2939
2940/* Content downloading is only available with ZLIB */
2941#if defined(WITH_ZLIB)
2942 IConsole::CmdRegister("content", ConContent);
2943#endif /* defined(WITH_ZLIB) */
2944
2945 /*** Networking commands ***/
2947 IConsole::CmdRegister("say_company", ConSayCompany, ConHookNeedNetwork);
2948 IConsole::AliasRegister("say_player", "say_company %+");
2949 IConsole::CmdRegister("say_client", ConSayClient, ConHookNeedNetwork);
2950
2951 IConsole::CmdRegister("connect", ConNetworkConnect, ConHookClientOnly);
2952 IConsole::CmdRegister("clients", ConNetworkClients, ConHookNeedNetwork);
2953 IConsole::CmdRegister("status", ConStatus, ConHookServerOnly);
2954 IConsole::CmdRegister("server_info", ConServerInfo, ConHookServerOnly);
2955 IConsole::AliasRegister("info", "server_info");
2956 IConsole::CmdRegister("reconnect", ConNetworkReconnect, ConHookClientOnly);
2957 IConsole::CmdRegister("rcon", ConRcon, ConHookNeedNetwork);
2958
2960 IConsole::AliasRegister("spectate", "join 255");
2961 IConsole::CmdRegister("move", ConMoveClient, ConHookServerOnly);
2962 IConsole::CmdRegister("reset_company", ConResetCompany, ConHookServerOnly);
2963 IConsole::AliasRegister("clean_company", "reset_company %A");
2964 IConsole::CmdRegister("client_name", ConClientNickChange, ConHookServerOnly);
2965 IConsole::CmdRegister("kick", ConKick, ConHookServerOnly);
2967 IConsole::CmdRegister("unban", ConUnBan, ConHookServerOnly);
2968 IConsole::CmdRegister("banlist", ConBanList, ConHookServerOnly);
2969
2970 IConsole::CmdRegister("pause", ConPauseGame, ConHookServerOrNoNetwork);
2971 IConsole::CmdRegister("unpause", ConUnpauseGame, ConHookServerOrNoNetwork);
2972
2973 IConsole::CmdRegister("authorized_key", ConNetworkAuthorizedKey, ConHookServerOnly);
2974 IConsole::AliasRegister("ak", "authorized_key %+");
2975
2976 IConsole::AliasRegister("net_frame_freq", "setting frame_freq %+");
2977 IConsole::AliasRegister("net_sync_freq", "setting sync_freq %+");
2978 IConsole::AliasRegister("server_pw", "setting server_password %+");
2979 IConsole::AliasRegister("server_password", "setting server_password %+");
2980 IConsole::AliasRegister("rcon_pw", "setting rcon_password %+");
2981 IConsole::AliasRegister("rcon_password", "setting rcon_password %+");
2982 IConsole::AliasRegister("name", "setting client_name %+");
2983 IConsole::AliasRegister("server_name", "setting server_name %+");
2984 IConsole::AliasRegister("server_port", "setting server_port %+");
2985 IConsole::AliasRegister("max_clients", "setting max_clients %+");
2986 IConsole::AliasRegister("max_companies", "setting max_companies %+");
2987 IConsole::AliasRegister("max_join_time", "setting max_join_time %+");
2988 IConsole::AliasRegister("pause_on_join", "setting pause_on_join %+");
2989 IConsole::AliasRegister("autoclean_companies", "setting autoclean_companies %+");
2990 IConsole::AliasRegister("autoclean_protected", "setting autoclean_protected %+");
2991 IConsole::AliasRegister("restart_game_year", "setting restart_game_year %+");
2992 IConsole::AliasRegister("min_players", "setting min_active_clients %+");
2993 IConsole::AliasRegister("reload_cfg", "setting reload_cfg %+");
2994
2995 /* debugging stuff */
2996#ifdef _DEBUG
2997 IConsoleDebugLibRegister();
2998#endif
2999 IConsole::CmdRegister("fps", ConFramerate);
3000 IConsole::CmdRegister("fps_wnd", ConFramerateWindow);
3001
3002 /* NewGRF development stuff */
3003 IConsole::CmdRegister("reload_newgrfs", ConNewGRFReload, ConHookNewGRFDeveloperTool);
3004 IConsole::CmdRegister("newgrf_profile", ConNewGRFProfile, ConHookNewGRFDeveloperTool);
3005
3006 IConsole::CmdRegister("dump_info", ConDumpInfo);
3007}
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:140
virtual int GetFontSize() const
Get the nominal font size of the font.
Definition fontcache.h:66
static FontCache * Get(FontSize fs)
Get the font cache of a given font size.
Definition fontcache.h:129
static void GetConsoleList(std::back_insert_iterator< std::string > &output_iterator, bool newest_only)
Wrapper function for GameScanner::GetConsoleList.
static void GetConsoleLibraryList(std::back_insert_iterator< std::string > &output_iterator)
Wrapper function for GameScanner::GetConsoleLibraryList.
void PrintConsole()
Print the gamelog data to the console.
Definition gamelog.cpp:310
An interval timer will fire every interval, and will continue to fire until it is deleted.
Definition timer.h:76
Simple helper to (more easily) manage authorized keys.
bool Contains(std::string_view key) const
Check whether the given key is contains in these authorized keys.
Definition network.cpp: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
ReferenceThroughBaseContainer< std::array< Colours, MAX_COMPANIES > > _company_colours
NOSAVE: can be determined from company structs.
CompanyID _local_company
Company controlled by the human player at this client. Can also be COMPANY_SPECTATOR.
CompanyID _current_company
Company currently doing an action.
Command definitions related to companies.
Functions related to companies.
@ CCA_NEW_AI
Create a new AI company.
@ CCA_DELETE
Delete a company.
static constexpr CompanyID COMPANY_SPECTATOR
The client is spectating.
@ CALCA_REMOVE
Remove a public key.
@ CALCA_ADD
Create a public key.
static constexpr CompanyID COMPANY_NEW_COMPANY
The client wants a new company.
@ CRR_NONE
Dummy reason for actions that don't need one.
@ CRR_MANUAL
The company is manually removed.
static const uint NETWORK_PUBLIC_KEY_LENGTH
The maximum length of the hexadecimal encoded public keys, in bytes including '\0'.
Definition config.h:101
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:54
@ SLO_LOAD
File is being loaded.
Definition fileio_type.h:53
@ DFT_FIOS_DRIVE
A drive (letter) entry.
Definition fileio_type.h:40
@ DFT_GAME_FILE
Save game or scenario file.
Definition fileio_type.h:30
@ DFT_FIOS_DIR
A directory entry.
Definition fileio_type.h:42
@ DFT_FIOS_PARENT
A parent directory entry.
Definition fileio_type.h:41
Searchpath
Types of searchpaths OpenTTD might use.
Subdirectory
The different kinds of subdirectories OpenTTD uses.
Definition fileio_type.h:87
@ AI_LIBRARY_DIR
Subdirectory for all AI libraries.
Definition fileio_type.h:99
@ 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:98
@ SCENARIO_DIR
Base directory for all scenarios.
Definition fileio_type.h:91
@ BASE_DIR
Base directory for all subdirectories.
Definition fileio_type.h:88
@ SAVE_DIR
Base directory for all savegames.
Definition fileio_type.h:89
@ HEIGHTMAP_DIR
Subdirectory of scenario for heightmaps.
Definition fileio_type.h:92
@ NEWGRF_DIR
Subdirectory for all NewGRFs.
Definition fileio_type.h:96
@ AUTOSAVE_DIR
Subdirectory of save for autosaves.
Definition fileio_type.h:90
@ BASESET_DIR
Subdirectory for all base data (base sets, intro game)
Definition fileio_type.h:95
@ GAME_DIR
Subdirectory for all game scripts.
AbstractFileType
The different abstract types of files that the system knows about.
Definition fileio_type.h:16
@ FT_SCENARIO
old or new scenario
Definition fileio_type.h:19
@ FT_HEIGHTMAP
heightmap file
Definition fileio_type.h:20
@ FT_NONE
nothing to do
Definition fileio_type.h:17
@ FT_SAVEGAME
old or new savegame
Definition fileio_type.h:18
@ FT_INVALID
Invalid or unknown file type.
Definition fileio_type.h:23
std::string FiosGetCurrentPath()
Get the current path/working directory.
Definition fios.cpp: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:216
Base functions for all Games.
Gamelog _gamelog
Gamelog instance.
Definition gamelog.cpp:31
Functions to be called to log fundamental changes to the game.
Functions related to world/map generation.
static const uint32_t GENERATE_NEW_SEED
Create a new random seed.
Definition genworld.h:25
void StartNewGameWithoutGUI(uint32_t seed)
Start a normal game without the GUI.
PauseModes _pause_mode
The current pause mode.
Definition gfx.cpp:50
SwitchMode _switch_mode
The next mainloop command.
Definition gfx.cpp:49
FontSize
Available font sizes.
Definition gfx_type.h: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:411
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:60
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:59
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:415
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:419
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:64
AbstractFileType abstract
Abstract file type.
Definition fileio_type.h:63
Settings for a single font.
Definition fontcache.h:192
std::string font
The name of the font, or path to the font.
Definition fontcache.h:193
uint size
The (requested) size of the font.
Definition fontcache.h:194
Dynamic data of a loaded NewGRF.
Definition newgrf.h: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.