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