OpenTTD Source 20260721-master-g25ec12c62d
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 <https://www.gnu.org/licenses/old-licenses/gpl-2.0>.
6 */
7
9
10#include "stdafx.h"
12#include "console_internal.h"
13#include "debug.h"
14#include "engine_func.h"
15#include "landscape.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 "string_func.h"
31#include "strings_func.h"
32#include "viewport_func.h"
33#include "window_func.h"
34#include "timer/timer.h"
35#include "company_func.h"
36#include "gamelog.h"
37#include "ai/ai.hpp"
38#include "ai/ai_config.hpp"
39#include "newgrf.h"
40#include "newgrf_profiling.h"
41#include "console_func.h"
42#include "engine_base.h"
43#include "road.h"
44#include "rail.h"
45#include "game/game.hpp"
46#include "3rdparty/fmt/chrono.h"
47#include "company_cmd.h"
48#include "misc_cmd.h"
49
50#if defined(WITH_ZLIB)
52#endif /* WITH_ZLIB */
53
54#include "table/strings.h"
55
56#include "safeguards.h"
57
58/* scriptfile handling */
60
61/* Scheduled execution handling. */
62static std::string _scheduled_monthly_script;
63
65static const IntervalTimer<TimerGameCalendar> _scheduled_monthly_timer = {{TimerGameCalendar::Trigger::Month, TimerGameCalendar::Priority::None}, [](auto) {
66 if (_scheduled_monthly_script.empty()) {
67 return;
68 }
69
70 /* Clear the schedule before rather than after the script to allow the script to itself call
71 * schedule without it getting immediately cleared. */
72 const std::string filename = _scheduled_monthly_script;
74
75 IConsolePrint(CC_DEFAULT, "Executing scheduled script file '{}'...", filename);
76 IConsoleCmdExec(fmt::format("exec {}", filename));
77}};
78
85template <typename T>
86static std::optional<T> ParseType(std::string_view arg)
87{
88 auto i = ParseInteger(arg);
89 if (i.has_value()) return static_cast<T>(*i);
90 return std::nullopt;
91}
92
94class ConsoleFileList : public FileList {
95public:
97 {
98 }
99
102 {
103 this->clear();
104 this->file_list_valid = false;
105 }
106
111 void ValidateFileList(bool force_reload = false)
112 {
113 if (force_reload || !this->file_list_valid) {
114 this->BuildFileList(this->abstract_filetype, SaveLoadOperation::Load, this->show_dirs);
115 this->file_list_valid = true;
116 }
117 }
118
121 bool file_list_valid = false;
122};
123
127
128/****************
129 * command hooks
130 ****************/
131
137static inline bool NetworkAvailable(bool echo)
138{
139 if (!_network_available) {
140 if (echo) IConsolePrint(CC_ERROR, "You cannot use this command because there is no network available.");
141 return false;
142 }
143 return true;
144}
145
151{
153
154 if (!_network_server) {
155 if (echo) IConsolePrint(CC_ERROR, "This command is only available to a network server.");
157 }
159}
160
166{
168
169 if (_network_server) {
170 if (echo) IConsolePrint(CC_ERROR, "This command is not available to a network server.");
172 }
174}
175
181{
183
185 if (echo) IConsolePrint(CC_ERROR, "Not connected. This command is only available in multiplayer.");
187 }
189}
190
196{
198
199 if (_network_dedicated) {
200 if (echo) IConsolePrint(CC_ERROR, "This command is not available to a dedicated network server.");
202 }
204}
205
211{
212 if (_networking) {
213 if (echo) IConsolePrint(CC_ERROR, "This command is forbidden in multiplayer.");
215 }
217}
218
224{
226 if (echo) IConsolePrint(CC_ERROR, "This command is only available to a network server.");
228 }
230}
231
237{
238 if (_settings_client.gui.newgrf_developer_tools) {
239 if (_game_mode == GameMode::Menu) {
240 if (echo) IConsolePrint(CC_ERROR, "This command is only available in-game and in the editor.");
242 }
243 return ConHookNoNetwork(echo);
244 }
246}
247
252static bool ConResetEngines(std::span<std::string_view> argv)
253{
254 if (argv.empty()) {
255 IConsolePrint(CC_HELP, "Reset status data of all engines. This might solve some issues with 'lost' engines. Usage: 'resetengines'.");
256 return true;
257 }
258
260 return true;
261}
262
268static bool ConResetEnginePool(std::span<std::string_view> argv)
269{
270 if (argv.empty()) {
271 IConsolePrint(CC_HELP, "Reset NewGRF allocations of engine slots. This will remove invalid engine definitions, and might make default engines available again.");
272 return true;
273 }
274
275 if (_game_mode == GameMode::Menu) {
276 IConsolePrint(CC_ERROR, "This command is only available in-game and in the editor.");
277 return true;
278 }
279
281 IConsolePrint(CC_ERROR, "This can only be done when there are no vehicles in the game.");
282 return true;
283 }
284
285 return true;
286}
287
288#ifdef _DEBUG
293static bool ConResetTile(std::span<std::string_view> argv)
294{
295 if (argv.empty()) {
296 IConsolePrint(CC_HELP, "Reset a tile to bare land. Usage: 'resettile <tile>'.");
297 IConsolePrint(CC_HELP, "Tile can be either decimal (34161) or hexadecimal (0x4a5B).");
298 return true;
299 }
300
301 if (argv.size() == 2) {
302 auto result = ParseInteger(argv[1], 0);
303 if (result.has_value() && IsValidTile(*result)) {
304 DoClearSquare(TileIndex{*result});
305 return true;
306 }
307 }
308
309 return false;
310}
311#endif /* _DEBUG */
312
317static bool ConZoomToLevel(std::span<std::string_view> argv)
318{
319 switch (argv.size()) {
320 case 0:
321 IConsolePrint(CC_HELP, "Set the current zoom level of the main viewport.");
322 IConsolePrint(CC_HELP, "Usage: 'zoomto <level>'.");
323
324 if (ZoomLevel::Min < _settings_client.gui.zoom_min) {
325 IConsolePrint(CC_HELP, "The lowest zoom-in level allowed by current client settings is {}.", std::max(ZoomLevel::Min, _settings_client.gui.zoom_min));
326 } else {
327 IConsolePrint(CC_HELP, "The lowest supported zoom-in level is {}.", std::max(ZoomLevel::Min, _settings_client.gui.zoom_min));
328 }
329
330 if (_settings_client.gui.zoom_max < ZoomLevel::Max) {
331 IConsolePrint(CC_HELP, "The highest zoom-out level allowed by current client settings is {}.", std::min(_settings_client.gui.zoom_max, ZoomLevel::Max));
332 } else {
333 IConsolePrint(CC_HELP, "The highest supported zoom-out level is {}.", std::min(_settings_client.gui.zoom_max, ZoomLevel::Max));
334 }
335 return true;
336
337 case 2: {
339 if (level.has_value()) {
340 auto zoom_lvl = static_cast<ZoomLevel>(*level);
341 if (!IsInsideMM(zoom_lvl, ZoomLevel::Begin, ZoomLevel::End)) {
342 IConsolePrint(CC_ERROR, "Invalid zoom level. Valid range is {} to {}.", ZoomLevel::Min, ZoomLevel::Max);
343 } else if (!IsInsideMM(zoom_lvl, _settings_client.gui.zoom_min, _settings_client.gui.zoom_max + 1)) {
344 IConsolePrint(CC_ERROR, "Current client settings limit zoom levels to range {} to {}.", _settings_client.gui.zoom_min, _settings_client.gui.zoom_max);
345 } else {
346 Window *w = GetMainWindow();
347 Viewport &vp = *w->viewport;
348 while (vp.zoom > zoom_lvl) DoZoomInOutWindow(ZOOM_IN, w);
349 while (vp.zoom < zoom_lvl) DoZoomInOutWindow(ZOOM_OUT, w);
350 }
351 return true;
352 }
353 break;
354 }
355 }
356
357 return false;
358}
359
364static bool ConScrollToTile(std::span<std::string_view> argv)
365{
366 if (argv.empty()) {
367 IConsolePrint(CC_HELP, "Center the screen on a given tile.");
368 IConsolePrint(CC_HELP, "Usage: 'scrollto [instant] <tile>' or 'scrollto [instant] <x> <y>'.");
369 IConsolePrint(CC_HELP, "Numbers can be either decimal (34161) or hexadecimal (0x4a5B).");
370 IConsolePrint(CC_HELP, "'instant' will immediately move and redraw viewport without smooth scrolling.");
371 return true;
372 }
373 if (argv.size() < 2) return false;
374
375 uint32_t arg_index = 1;
376 bool instant = false;
377 if (argv[arg_index] == "instant") {
378 ++arg_index;
379 instant = true;
380 }
381
382 switch (argv.size() - arg_index) {
383 case 1: {
384 auto result = ParseInteger(argv[arg_index], 0);
385 if (result.has_value()) {
386 if (*result >= Map::Size()) {
387 IConsolePrint(CC_ERROR, "Tile does not exist.");
388 return true;
389 }
390 ScrollMainWindowToTile(TileIndex{*result}, instant);
391 return true;
392 }
393 break;
394 }
395
396 case 2: {
397 auto x = ParseInteger(argv[arg_index], 0);
398 auto y = ParseInteger(argv[arg_index + 1], 0);
399 if (x.has_value() && y.has_value()) {
400 if (*x >= Map::SizeX() || *y >= Map::SizeY()) {
401 IConsolePrint(CC_ERROR, "Tile does not exist.");
402 return true;
403 }
404 ScrollMainWindowToTile(TileXY(*x, *y), instant);
405 return true;
406 }
407 break;
408 }
409 }
410
411 return false;
412}
413
418static bool ConSave(std::span<std::string_view> argv)
419{
420 if (argv.empty()) {
421 IConsolePrint(CC_HELP, "Save the current game. Usage: 'save <filename>'.");
422 return true;
423 }
424
425 if (argv.size() == 2) {
426 std::string filename = fmt::format("{}.sav", argv[1]);
427 IConsolePrint(CC_DEFAULT, "Saving map...");
428
430 IConsolePrint(CC_ERROR, "Saving map failed.");
431 } else {
432 IConsolePrint(CC_INFO, "Map successfully saved to '{}'.", filename);
433 }
434 return true;
435 }
436
437 return false;
438}
439
444static bool ConSaveConfig(std::span<std::string_view> argv)
445{
446 if (argv.empty()) {
447 IConsolePrint(CC_HELP, "Saves the configuration for new games to the configuration file, typically 'openttd.cfg'.");
448 IConsolePrint(CC_HELP, "It does not save the configuration of the current game to the configuration file.");
449 return true;
450 }
451
452 SaveToConfig();
453 IConsolePrint(CC_DEFAULT, "Saved config.");
454 return true;
455}
456
458static bool ConLoad(std::span<std::string_view> argv)
459{
460 if (argv.empty()) {
461 IConsolePrint(CC_HELP, "Load a game by name or index. Usage: 'load <file | number>'.");
462 return true;
463 }
464
465 if (argv.size() != 2) return false;
466
467 std::string_view file = argv[1];
468 _console_file_list_savegame.ValidateFileList();
469 const FiosItem *item = _console_file_list_savegame.FindItem(file);
470 if (item != nullptr) {
471 if (item->type.abstract == AbstractFileType::Savegame) {
473 _file_to_saveload.Set(*item);
474 } else {
475 IConsolePrint(CC_ERROR, "'{}' is not a savegame.", file);
476 }
477 } else {
478 IConsolePrint(CC_ERROR, "'{}' cannot be found.", file);
479 }
480
481 return true;
482}
483
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];
495 _console_file_list_scenario.ValidateFileList();
496 const FiosItem *item = _console_file_list_scenario.FindItem(file);
497 if (item != nullptr) {
498 if (item->type.abstract == AbstractFileType::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
512static bool ConLoadHeightmap(std::span<std::string_view> argv)
513{
514 if (argv.empty()) {
515 IConsolePrint(CC_HELP, "Load a heightmap by name or index. Usage: 'load_heightmap <file | number>'.");
516 return true;
517 }
518
519 if (argv.size() != 2) return false;
520
521 std::string_view file = argv[1];
522 _console_file_list_heightmap.ValidateFileList();
523 const FiosItem *item = _console_file_list_heightmap.FindItem(file);
524 if (item != nullptr) {
525 if (item->type.abstract == AbstractFileType::Heightmap) {
527 _file_to_saveload.Set(*item);
528 } else {
529 IConsolePrint(CC_ERROR, "'{}' is not a heightmap.", file);
530 }
531 } else {
532 IConsolePrint(CC_ERROR, "'{}' cannot be found.", file);
533 }
534
535 return true;
536}
537
539static bool ConRemove(std::span<std::string_view> argv)
540{
541 if (argv.empty()) {
542 IConsolePrint(CC_HELP, "Remove a savegame by name or index. Usage: 'rm <file | number>'.");
543 return true;
544 }
545
546 if (argv.size() != 2) return false;
547
548 std::string_view file = argv[1];
549 _console_file_list_savegame.ValidateFileList();
550 const FiosItem *item = _console_file_list_savegame.FindItem(file);
551 if (item != nullptr) {
552 if (item->type.abstract == AbstractFileType::Savegame) {
553 if (!FioRemove(item->name)) {
554 IConsolePrint(CC_ERROR, "Failed to delete '{}'.", item->name);
555 }
556 } else {
557 IConsolePrint(CC_ERROR, "'{}' is not a savegame.", file);
558 }
559 } else {
560 IConsolePrint(CC_ERROR, "'{}' could not be found.", file);
561 }
562
563 _console_file_list_savegame.InvalidateFileList();
564 return true;
565}
566
567
569static bool ConListFiles(std::span<std::string_view> argv)
570{
571 if (argv.empty()) {
572 IConsolePrint(CC_HELP, "List all loadable savegames and directories in the current dir via console. Usage: 'ls | dir'.");
573 return true;
574 }
575
576 _console_file_list_savegame.ValidateFileList(true);
577 for (uint i = 0; i < _console_file_list_savegame.size(); i++) {
578 IConsolePrint(CC_DEFAULT, "{}) {}", i, _console_file_list_savegame[i].title.GetDecodedString());
579 }
580
581 return true;
582}
583
585static bool ConListScenarios(std::span<std::string_view> argv)
586{
587 if (argv.empty()) {
588 IConsolePrint(CC_HELP, "List all loadable scenarios. Usage: 'list_scenarios'.");
589 return true;
590 }
591
592 _console_file_list_scenario.ValidateFileList(true);
593 for (uint i = 0; i < _console_file_list_scenario.size(); i++) {
594 IConsolePrint(CC_DEFAULT, "{}) {}", i, _console_file_list_scenario[i].title.GetDecodedString());
595 }
596
597 return true;
598}
599
601static bool ConListHeightmaps(std::span<std::string_view> argv)
602{
603 if (argv.empty()) {
604 IConsolePrint(CC_HELP, "List all loadable heightmaps. Usage: 'list_heightmaps'.");
605 return true;
606 }
607
608 _console_file_list_heightmap.ValidateFileList(true);
609 for (uint i = 0; i < _console_file_list_heightmap.size(); i++) {
610 IConsolePrint(CC_DEFAULT, "{}) {}", i, _console_file_list_heightmap[i].title.GetDecodedString());
611 }
612
613 return true;
614}
615
617static bool ConChangeDirectory(std::span<std::string_view> argv)
618{
619 if (argv.empty()) {
620 IConsolePrint(CC_HELP, "Change the dir via console. Usage: 'cd <directory | number>'.");
621 return true;
622 }
623
624 if (argv.size() != 2) return false;
625
626 std::string_view file = argv[1];
627 _console_file_list_savegame.ValidateFileList(true);
628 const FiosItem *item = _console_file_list_savegame.FindItem(file);
629 if (item != nullptr) {
630 switch (item->type.detailed) {
634 FiosBrowseTo(item);
635 break;
636 default: IConsolePrint(CC_ERROR, "{}: Not a directory.", file);
637 }
638 } else {
639 IConsolePrint(CC_ERROR, "{}: No such file or directory.", file);
640 }
641
642 _console_file_list_savegame.InvalidateFileList();
643 return true;
644}
645
647static bool ConPrintWorkingDirectory(std::span<std::string_view> argv)
648{
649 if (argv.empty()) {
650 IConsolePrint(CC_HELP, "Print out the current working directory. Usage: 'pwd'.");
651 return true;
652 }
653
654 /* XXX - Workaround for broken file handling */
655 _console_file_list_savegame.ValidateFileList(true);
656 _console_file_list_savegame.InvalidateFileList();
657
659 return true;
660}
661
663static bool ConClearBuffer(std::span<std::string_view> argv)
664{
665 if (argv.empty()) {
666 IConsolePrint(CC_HELP, "Clear the console buffer. Usage: 'clear'.");
667 return true;
668 }
669
670 IConsoleClearBuffer();
671 SetWindowDirty(WindowClass::Console, 0);
672 return true;
673}
674
675
676/**********************************
677 * Network Core Console Commands
678 **********************************/
679
687static bool ConKickOrBan(std::string_view arg, bool ban, std::string_view reason)
688{
689 uint n;
690
691 if (arg.find_first_of(".:") == std::string::npos) { // banning with ID
692 auto client_id = ParseType<ClientID>(arg);
693 if (!client_id.has_value()) {
694 IConsolePrint(CC_ERROR, "The given client-id is not a valid number.");
695 return true;
696 }
697
698 /* Don't kill the server, or the client doing the rcon. The latter can't be kicked because
699 * kicking frees closes and subsequently free the connection related instances, which we
700 * would be reading from and writing to after returning. So we would read or write data
701 * from freed memory up till the segfault triggers. */
702 if (*client_id == ClientID::Server || *client_id == _redirect_console_to_client) {
703 IConsolePrint(CC_ERROR, "You can not {} yourself!", ban ? "ban" : "kick");
704 return true;
705 }
706
708 if (ci == nullptr) {
709 IConsolePrint(CC_ERROR, "Invalid client-id.");
710 return true;
711 }
712
713 if (!ban) {
714 /* Kick only this client, not all clients with that IP */
715 NetworkServerKickClient(*client_id, reason);
716 return true;
717 }
718
719 /* When banning, kick+ban all clients with that IP */
720 n = NetworkServerKickOrBanIP(*client_id, ban, reason);
721 } else {
722 n = NetworkServerKickOrBanIP(arg, ban, reason);
723 }
724
725 if (n == 0) {
726 IConsolePrint(CC_DEFAULT, ban ? "Client not online, address added to banlist." : "Client not found.");
727 } else {
728 IConsolePrint(CC_DEFAULT, "{}ed {} client(s).", ban ? "Bann" : "Kick", n);
729 }
730
731 return true;
732}
733
735static bool ConKick(std::span<std::string_view> argv)
736{
737 if (argv.empty()) {
738 IConsolePrint(CC_HELP, "Kick a client from a network game. Usage: 'kick <ip | client-id> [<kick-reason>]'.");
739 IConsolePrint(CC_HELP, "For client-id's, see the command 'clients'.");
740 return true;
741 }
742
743 if (argv.size() != 2 && argv.size() != 3) return false;
744
745 /* No reason supplied for kicking */
746 if (argv.size() == 2) return ConKickOrBan(argv[1], false, {});
747
748 /* Reason for kicking supplied */
749 size_t kick_message_length = argv[2].size();
750 if (kick_message_length >= 255) {
751 IConsolePrint(CC_ERROR, "Maximum kick message length is 254 characters. You entered {} characters.", kick_message_length);
752 return false;
753 } else {
754 return ConKickOrBan(argv[1], false, argv[2]);
755 }
756}
757
759static bool ConBan(std::span<std::string_view> argv)
760{
761 if (argv.empty()) {
762 IConsolePrint(CC_HELP, "Ban a client from a network game. Usage: 'ban <ip | client-id> [<ban-reason>]'.");
763 IConsolePrint(CC_HELP, "For client-id's, see the command 'clients'.");
764 IConsolePrint(CC_HELP, "If the client is no longer online, you can still ban their IP.");
765 return true;
766 }
767
768 if (argv.size() != 2 && argv.size() != 3) return false;
769
770 /* No reason supplied for kicking */
771 if (argv.size() == 2) return ConKickOrBan(argv[1], true, {});
772
773 /* Reason for kicking supplied */
774 size_t kick_message_length = argv[2].size();
775 if (kick_message_length >= 255) {
776 IConsolePrint(CC_ERROR, "Maximum kick message length is 254 characters. You entered {} characters.", kick_message_length);
777 return false;
778 } else {
779 return ConKickOrBan(argv[1], true, argv[2]);
780 }
781}
782
784static bool ConUnBan(std::span<std::string_view> argv)
785{
786 if (argv.empty()) {
787 IConsolePrint(CC_HELP, "Unban a client from a network game. Usage: 'unban <ip | banlist-index>'.");
788 IConsolePrint(CC_HELP, "For a list of banned IP's, see the command 'banlist'.");
789 return true;
790 }
791
792 if (argv.size() != 2) return false;
793
794 /* Try by IP. */
795 uint index;
796 for (index = 0; index < _network_ban_list.size(); index++) {
797 if (_network_ban_list[index] == argv[1]) break;
798 }
799
800 /* Try by index. */
801 if (index >= _network_ban_list.size()) {
802 index = ParseInteger(argv[1]).value_or(0) - 1U; // let it wrap
803 }
804
805 if (index < _network_ban_list.size()) {
806 IConsolePrint(CC_DEFAULT, "Unbanned {}.", _network_ban_list[index]);
807 _network_ban_list.erase(_network_ban_list.begin() + index);
808 } else {
809 IConsolePrint(CC_DEFAULT, "Invalid list index or IP not in ban-list.");
810 IConsolePrint(CC_DEFAULT, "For a list of banned IP's, see the command 'banlist'.");
811 }
812
813 return true;
814}
815
817static bool ConBanList(std::span<std::string_view> argv)
818{
819 if (argv.empty()) {
820 IConsolePrint(CC_HELP, "List the IP's of banned clients: Usage 'banlist'.");
821 return true;
822 }
823
824 IConsolePrint(CC_DEFAULT, "Banlist:");
825
826 uint i = 1;
827 for (const auto &entry : _network_ban_list) {
828 IConsolePrint(CC_DEFAULT, " {}) {}", i, entry);
829 i++;
830 }
831
832 return true;
833}
834
836static bool ConPauseGame(std::span<std::string_view> argv)
837{
838 if (argv.empty()) {
839 IConsolePrint(CC_HELP, "Pause a network game. Usage: 'pause'.");
840 return true;
841 }
842
843 if (_game_mode == GameMode::Menu) {
844 IConsolePrint(CC_ERROR, "This command is only available in-game and in the editor.");
845 return true;
846 }
847
848 if (!_pause_mode.Test(PauseMode::Normal)) {
849 Command<Commands::Pause>::Post(PauseMode::Normal, true);
850 if (!_networking) IConsolePrint(CC_DEFAULT, "Game paused.");
851 } else {
852 IConsolePrint(CC_DEFAULT, "Game is already paused.");
853 }
854
855 return true;
856}
857
859static bool ConUnpauseGame(std::span<std::string_view> argv)
860{
861 if (argv.empty()) {
862 IConsolePrint(CC_HELP, "Unpause a network game. Usage: 'unpause'.");
863 return true;
864 }
865
866 if (_game_mode == GameMode::Menu) {
867 IConsolePrint(CC_ERROR, "This command is only available in-game and in the editor.");
868 return true;
869 }
870
871 if (_pause_mode.Test(PauseMode::Normal)) {
872 Command<Commands::Pause>::Post(PauseMode::Normal, false);
873 if (!_networking) IConsolePrint(CC_DEFAULT, "Game unpaused.");
874 } else if (_pause_mode.Test(PauseMode::Error)) {
875 IConsolePrint(CC_DEFAULT, "Game is in error state and cannot be unpaused via console.");
876 } else if (_pause_mode.Any()) {
877 IConsolePrint(CC_DEFAULT, "Game cannot be unpaused manually; disable pause_on_join/min_active_clients.");
878 } else {
879 IConsolePrint(CC_DEFAULT, "Game is already unpaused.");
880 }
881
882 return true;
883}
884
886static bool ConRcon(std::span<std::string_view> argv)
887{
888 if (argv.empty()) {
889 IConsolePrint(CC_HELP, "Remote control the server from another client. Usage: 'rcon <password> <command>'.");
890 IConsolePrint(CC_HELP, "Remember to enclose the command in quotes, otherwise only the first parameter is sent.");
891 IConsolePrint(CC_HELP, "When your client's public key is in the 'authorized keys' for 'rcon', the password is not checked and may be '*'.");
892 return true;
893 }
894
895 if (argv.size() < 3) return false;
896
897 if (_network_server) {
898 IConsoleCmdExec(argv[2]);
899 } else {
900 NetworkClientSendRcon(argv[1], argv[2]);
901 }
902 return true;
903}
904
906static bool ConStatus(std::span<std::string_view> argv)
907{
908 if (argv.empty()) {
909 IConsolePrint(CC_HELP, "List the status of all clients connected to the server. Usage 'status'.");
910 return true;
911 }
912
914 return true;
915}
916
918static bool ConServerInfo(std::span<std::string_view> argv)
919{
920 if (argv.empty()) {
921 IConsolePrint(CC_HELP, "List current and maximum client/company limits. Usage 'server_info'.");
922 IConsolePrint(CC_HELP, "You can change these values by modifying settings 'network.max_clients' and 'network.max_companies'.");
923 return true;
924 }
925
927 IConsolePrint(CC_DEFAULT, "Current/maximum clients: {:3d}/{:3d}", _network_game_info.clients_on, _settings_client.network.max_clients);
928 IConsolePrint(CC_DEFAULT, "Current/maximum companies: {:3d}/{:3d}", Company::GetNumItems(), _settings_client.network.max_companies);
929 IConsolePrint(CC_DEFAULT, "Current spectators: {:3d}", NetworkSpectatorCount());
930
931 return true;
932}
933
935static bool ConClientNickChange(std::span<std::string_view> argv)
936{
937 if (argv.size() != 3) {
938 IConsolePrint(CC_HELP, "Change the nickname of a connected client. Usage: 'client_name <client-id> <new-name>'.");
939 IConsolePrint(CC_HELP, "For client-id's, see the command 'clients'.");
940 return true;
941 }
942
943 auto client_id = ParseType<ClientID>(argv[1]);
944 if (!client_id.has_value()) {
945 IConsolePrint(CC_ERROR, "The given client-id is not a valid number.");
946 return true;
947 }
948
949 if (*client_id == ClientID::Server) {
950 IConsolePrint(CC_ERROR, "Please use the command 'name' to change your own name!");
951 return true;
952 }
953
954 if (NetworkClientInfo::GetByClientID(*client_id) == nullptr) {
955 IConsolePrint(CC_ERROR, "Invalid client-id.");
956 return true;
957 }
958
959 std::string client_name{StrTrimView(argv[2], StringConsumer::WHITESPACE_NO_NEWLINE)};
960 if (!NetworkIsValidClientName(client_name)) {
961 IConsolePrint(CC_ERROR, "Cannot give a client an empty name.");
962 return true;
963 }
964
965 if (!NetworkServerChangeClientName(*client_id, client_name)) {
966 IConsolePrint(CC_ERROR, "Cannot give a client a duplicate name.");
967 }
968
969 return true;
970}
971
977static std::optional<CompanyID> ParseCompanyID(std::string_view arg)
978{
979 auto company_id = ParseType<CompanyID>(arg);
980 if (company_id.has_value() && *company_id <= MAX_COMPANIES) return static_cast<CompanyID>(*company_id - 1);
981 return company_id;
982}
983
985static bool ConJoinCompany(std::span<std::string_view> argv)
986{
987 if (argv.size() < 2) {
988 IConsolePrint(CC_HELP, "Request joining another company. Usage: 'join <company-id>'.");
989 IConsolePrint(CC_HELP, "For valid company-id see company list, use 255 for spectator.");
990 return true;
991 }
992
993 auto company_id = ParseCompanyID(argv[1]);
994 if (!company_id.has_value()) {
995 IConsolePrint(CC_ERROR, "The given company-id is not a valid number.");
996 return true;
997 }
998
1000 if (info == nullptr) {
1001 IConsolePrint(CC_ERROR, "You have not joined the game yet!");
1002 return true;
1003 }
1004
1005 /* Check we have a valid company id! */
1006 if (!Company::IsValidID(*company_id) && *company_id != COMPANY_SPECTATOR) {
1007 IConsolePrint(CC_ERROR, "Company does not exist. Company-id must be between 1 and {}.", MAX_COMPANIES);
1008 return true;
1009 }
1010
1011 if (info->client_playas == *company_id) {
1012 IConsolePrint(CC_ERROR, "You are already there!");
1013 return true;
1014 }
1015
1016 if (*company_id != COMPANY_SPECTATOR && !Company::IsHumanID(*company_id)) {
1017 IConsolePrint(CC_ERROR, "Cannot join AI company.");
1018 return true;
1019 }
1020
1021 if (!info->CanJoinCompany(*company_id)) {
1022 IConsolePrint(CC_ERROR, "You are not allowed to join this company.");
1023 return true;
1024 }
1025
1026 /* non-dedicated server may just do the move! */
1027 if (_network_server) {
1029 } else {
1030 NetworkClientRequestMove(*company_id);
1031 }
1032
1033 return true;
1034}
1035
1037static bool ConMoveClient(std::span<std::string_view> argv)
1038{
1039 if (argv.size() < 3) {
1040 IConsolePrint(CC_HELP, "Move a client to another company. Usage: 'move <client-id> <company-id>'.");
1041 IConsolePrint(CC_HELP, "For valid client-id see 'clients', for valid company-id see 'companies', use 255 for moving to spectators.");
1042 return true;
1043 }
1044
1045 auto client_id = ParseType<ClientID>(argv[1]);
1046 if (!client_id.has_value()) {
1047 IConsolePrint(CC_ERROR, "The given client-id is not a valid number.");
1048 return true;
1049 }
1051
1052 auto company_id = ParseCompanyID(argv[2]);
1053 if (!company_id.has_value()) {
1054 IConsolePrint(CC_ERROR, "The given company-id is not a valid number.");
1055 return true;
1056 }
1057
1058 /* check the client exists */
1059 if (ci == nullptr) {
1060 IConsolePrint(CC_ERROR, "Invalid client-id, check the command 'clients' for valid client-id's.");
1061 return true;
1062 }
1063
1064 if (!Company::IsValidID(*company_id) && *company_id != COMPANY_SPECTATOR) {
1065 IConsolePrint(CC_ERROR, "Company does not exist. Company-id must be between 1 and {}.", MAX_COMPANIES);
1066 return true;
1067 }
1068
1069 if (*company_id != COMPANY_SPECTATOR && !Company::IsHumanID(*company_id)) {
1070 IConsolePrint(CC_ERROR, "You cannot move clients to AI companies.");
1071 return true;
1072 }
1073
1075 IConsolePrint(CC_ERROR, "You cannot move the server!");
1076 return true;
1077 }
1078
1079 if (ci->client_playas == *company_id) {
1080 IConsolePrint(CC_ERROR, "You cannot move someone to where they already are!");
1081 return true;
1082 }
1083
1084 /* we are the server, so force the update */
1085 NetworkServerDoMove(ci->client_id, *company_id);
1086
1087 return true;
1088}
1089
1091static bool ConResetCompany(std::span<std::string_view> argv)
1092{
1093 if (argv.empty()) {
1094 IConsolePrint(CC_HELP, "Remove an idle company from the game. Usage: 'reset_company <company-id>'.");
1095 IConsolePrint(CC_HELP, "For company-id's, see the list of companies from the dropdown menu. Company 1 is 1, etc.");
1096 return true;
1097 }
1098
1099 if (argv.size() != 2) return false;
1100
1101 auto index = ParseCompanyID(argv[1]);
1102 if (!index.has_value()) {
1103 IConsolePrint(CC_ERROR, "The given company-id is not a valid number.");
1104 return true;
1105 }
1106
1107 /* Check valid range */
1108 if (!Company::IsValidID(*index)) {
1109 IConsolePrint(CC_ERROR, "Company does not exist. company-id must be between 1 and {}.", MAX_COMPANIES);
1110 return true;
1111 }
1112
1113 if (!Company::IsHumanID(*index)) {
1114 IConsolePrint(CC_ERROR, "Company is owned by an AI.");
1115 return true;
1116 }
1117
1118 if (NetworkCompanyHasClients(*index)) {
1119 IConsolePrint(CC_ERROR, "Cannot remove company: a client is connected to that company.");
1120 return false;
1121 }
1123 assert(ci != nullptr);
1124 if (ci->client_playas == *index) {
1125 IConsolePrint(CC_ERROR, "Cannot remove company: the server is connected to that company.");
1126 return true;
1127 }
1128
1129 /* It is safe to remove this company */
1130 Command<Commands::CompanyControl>::Post(CompanyCtrlAction::Delete, *index, CompanyRemoveReason::Manual, ClientID::Invalid);
1131 IConsolePrint(CC_DEFAULT, "Company deleted.");
1132
1133 return true;
1134}
1135
1137static bool ConNetworkClients(std::span<std::string_view> argv)
1138{
1139 if (argv.empty()) {
1140 IConsolePrint(CC_HELP, "Get a list of connected clients including their ID, name, company-id, and IP. Usage: 'clients'.");
1141 return true;
1142 }
1143
1145
1146 return true;
1147}
1148
1150static bool ConNetworkReconnect(std::span<std::string_view> argv)
1151{
1152 if (argv.empty()) {
1153 IConsolePrint(CC_HELP, "Reconnect to server to which you were connected last time. Usage: 'reconnect [<company-id>]'.");
1154 IConsolePrint(CC_HELP, "Company 255 is spectator (default, if not specified), 254 means creating new company.");
1155 IConsolePrint(CC_HELP, "All others are a certain company with Company 1 being #1.");
1156 return true;
1157 }
1158
1159 CompanyID playas = COMPANY_SPECTATOR;
1160 if (argv.size() >= 2) {
1161 auto company_id = ParseCompanyID(argv[1]);
1162 if (!company_id.has_value()) {
1163 IConsolePrint(CC_ERROR, "The given company-id is not a valid number.");
1164 return true;
1165 }
1166 if (*company_id >= MAX_COMPANIES && *company_id != COMPANY_NEW_COMPANY && *company_id != COMPANY_SPECTATOR) return false;
1167 playas = *company_id;
1168 }
1169
1170 if (_settings_client.network.last_joined.empty()) {
1171 IConsolePrint(CC_DEFAULT, "No server for reconnecting.");
1172 return true;
1173 }
1174
1175 /* Don't resolve the address first, just print it directly as it comes from the config file. */
1176 IConsolePrint(CC_DEFAULT, "Reconnecting to {} ...", _settings_client.network.last_joined);
1177
1178 return NetworkClientConnectGame(_settings_client.network.last_joined, playas);
1179}
1180
1182static bool ConNetworkConnect(std::span<std::string_view> argv)
1183{
1184 if (argv.empty()) {
1185 IConsolePrint(CC_HELP, "Connect to a remote OTTD server and join the game. Usage: 'connect <ip>'.");
1186 IConsolePrint(CC_HELP, "IP can contain port and company: 'IP[:Port][#Company]', eg: 'server.ottd.org:443#2'.");
1187 IConsolePrint(CC_HELP, "Company #255 is spectator all others are a certain company with Company 1 being #1.");
1188 return true;
1189 }
1190
1191 if (argv.size() < 2) return false;
1192
1194}
1195
1196/*********************************
1197 * script file console commands
1198 *********************************/
1199
1201static bool ConExec(std::span<std::string_view> argv)
1202{
1203 if (argv.empty()) {
1204 IConsolePrint(CC_HELP, "Execute a local script file. Usage: 'exec <script> [0]'.");
1205 IConsolePrint(CC_HELP, "By passing '0' after the script name, no warning about a missing script file will be shown.");
1206 return true;
1207 }
1208
1209 if (argv.size() < 2) return false;
1210
1211 auto script_file = FioFOpenFile(argv[1], "r", Subdirectory::Base);
1212
1213 if (!script_file.has_value()) {
1214 if (argv.size() == 2 || argv[2] != "0") IConsolePrint(CC_ERROR, "Script file '{}' not found.", argv[1]);
1215 return true;
1216 }
1217
1218 if (_script_current_depth == 11) {
1219 IConsolePrint(CC_ERROR, "Maximum 'exec' depth reached; script A is calling script B is calling script C ... more than 10 times.");
1220 return true;
1221 }
1222
1224 uint script_depth = _script_current_depth;
1225
1226 char buffer[ICON_CMDLN_SIZE];
1227 while (fgets(buffer, sizeof(buffer), *script_file) != nullptr) {
1228 /* Remove newline characters from the executing script */
1229 std::string_view cmdline{buffer};
1230 auto last_non_newline = cmdline.find_last_not_of("\r\n");
1231 if (last_non_newline != std::string_view::npos) cmdline = cmdline.substr(0, last_non_newline + 1);
1232
1233 IConsoleCmdExec(cmdline);
1234 /* Ensure that we are still on the same depth or that we returned via 'return'. */
1235 assert(_script_current_depth == script_depth || _script_current_depth == script_depth - 1);
1236
1237 /* The 'return' command was executed. */
1238 if (_script_current_depth == script_depth - 1) break;
1239 }
1240
1241 if (ferror(*script_file) != 0) {
1242 IConsolePrint(CC_ERROR, "Encountered error while trying to read from script file '{}'.", argv[1]);
1243 }
1244
1245 if (_script_current_depth == script_depth) _script_current_depth--;
1246 return true;
1247}
1248
1250static bool ConSchedule(std::span<std::string_view> argv)
1251{
1252 if (argv.size() < 3 || std::string_view(argv[1]) != "on-next-calendar-month") {
1253 IConsolePrint(CC_HELP, "Schedule a local script to execute later. Usage: 'schedule on-next-calendar-month <script>'.");
1254 return true;
1255 }
1256
1257 /* Check if the file exists. It might still go away later, but helpful to show an error now. */
1258 if (!FioCheckFileExists(argv[2], Subdirectory::Base)) {
1259 IConsolePrint(CC_ERROR, "Script file '{}' not found.", argv[2]);
1260 return true;
1261 }
1262
1263 /* We only support a single script scheduled, so we tell the user what's happening if there was already one. */
1264 std::string_view filename = std::string_view(argv[2]);
1265 if (!_scheduled_monthly_script.empty() && filename == _scheduled_monthly_script) {
1266 IConsolePrint(CC_INFO, "Script file '{}' was already scheduled to execute at the start of next calendar month.", filename);
1267 } else if (!_scheduled_monthly_script.empty() && filename != _scheduled_monthly_script) {
1268 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);
1269 } else {
1270 IConsolePrint(CC_INFO, "Script file '{}' scheduled to execute at the start of next calendar month.", filename);
1271 }
1272
1273 /* Store the filename to be used by _schedule_timer on the start of next calendar month. */
1274 _scheduled_monthly_script = filename;
1275
1276 return true;
1277}
1278
1280static bool ConReturn(std::span<std::string_view> argv)
1281{
1282 if (argv.empty()) {
1283 IConsolePrint(CC_HELP, "Stop executing a running script. Usage: 'return'.");
1284 return true;
1285 }
1286
1288 return true;
1289}
1290
1291/*****************************
1292 * default console commands
1293 ******************************/
1294extern bool CloseConsoleLogIfActive();
1295extern std::span<const GRFFile> GetAllGRFFiles();
1296extern void ConPrintFramerate(); // framerate_gui.cpp
1297extern void ShowFramerateWindow();
1298
1300static bool ConScript(std::span<std::string_view> argv)
1301{
1302 extern std::optional<FileHandle> _iconsole_output_file;
1303
1304 if (argv.empty()) {
1305 IConsolePrint(CC_HELP, "Start or stop logging console output to a file. Usage: 'script <filename>'.");
1306 IConsolePrint(CC_HELP, "If filename is omitted, a running log is stopped if it is active.");
1307 return true;
1308 }
1309
1310 if (!CloseConsoleLogIfActive()) {
1311 if (argv.size() < 2) return false;
1312
1313 _iconsole_output_file = FileHandle::Open(argv[1], "ab");
1314 if (!_iconsole_output_file.has_value()) {
1315 IConsolePrint(CC_ERROR, "Could not open console log file '{}'.", argv[1]);
1316 } else {
1317 IConsolePrint(CC_INFO, "Console log output started to '{}'.", argv[1]);
1318 }
1319 }
1320
1321 return true;
1322}
1323
1325static bool ConEcho(std::span<std::string_view> argv)
1326{
1327 if (argv.empty()) {
1328 IConsolePrint(CC_HELP, "Print back the first argument to the console. Usage: 'echo <arg>'.");
1329 return true;
1330 }
1331
1332 if (argv.size() < 2) return false;
1333 IConsolePrint(CC_DEFAULT, "{}", argv[1]);
1334 return true;
1335}
1336
1338static bool ConEchoC(std::span<std::string_view> argv)
1339{
1340 if (argv.empty()) {
1341 IConsolePrint(CC_HELP, "Print back the first argument to the console in a given colour. Usage: 'echoc <colour> <arg2>'.");
1342 return true;
1343 }
1344
1345 if (argv.size() < 3) return false;
1346
1347 auto colour = ParseInteger(argv[1]);
1348 if (!colour.has_value() || !IsInsideMM(*colour, to_underlying(TextColour::Begin), to_underlying(TextColour::End))) {
1349 IConsolePrint(CC_ERROR, "The colour must be a number between {} and {}.", TextColour::Begin, to_underlying(TextColour::End) - 1);
1350 return true;
1351 }
1352
1353 IConsolePrint(static_cast<TextColour>(*colour), "{}", argv[2]);
1354 return true;
1355}
1356
1358static bool ConNewGame(std::span<std::string_view> argv)
1359{
1360 if (argv.empty()) {
1361 IConsolePrint(CC_HELP, "Start a new game. Usage: 'newgame [seed]'.");
1362 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.");
1363 return true;
1364 }
1365
1366 uint32_t seed = GENERATE_NEW_SEED;
1367 if (argv.size() >= 2) {
1368 auto param = ParseInteger(argv[1]);
1369 if (!param.has_value()) {
1370 IConsolePrint(CC_ERROR, "The given seed must be a valid number.");
1371 return true;
1372 }
1373 seed = *param;
1374 }
1375
1377 return true;
1378}
1379
1381static bool ConRestart(std::span<std::string_view> argv)
1382{
1383 if (argv.empty() || argv.size() > 2) {
1384 IConsolePrint(CC_HELP, "Restart game. Usage: 'restart [current|newgame]'.");
1385 IConsolePrint(CC_HELP, "Restarts a game, using either the current or newgame (default) settings.");
1386 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.");
1387 IConsolePrint(CC_HELP, " * if you started from a savegame / scenario / heightmap, the game might be different, because the current/newgame settings might differ.");
1388 return true;
1389 }
1390
1391 if (argv.size() == 1 || std::string_view(argv[1]) == "newgame") {
1392 StartNewGameWithoutGUI(_settings_game.game_creation.generation_seed);
1393 } else {
1394 _settings_game.game_creation.map_x = Map::LogX();
1395 _settings_game.game_creation.map_y = Map::LogY();
1397 }
1398
1399 return true;
1400}
1401
1403static bool ConReload(std::span<std::string_view> argv)
1404{
1405 if (argv.empty()) {
1406 IConsolePrint(CC_HELP, "Reload game. Usage: 'reload'.");
1407 IConsolePrint(CC_HELP, "Reloads a game if loaded via savegame / scenario / heightmap.");
1408 return true;
1409 }
1410
1411 if (_file_to_saveload.ftype.abstract == AbstractFileType::None || _file_to_saveload.ftype.abstract == AbstractFileType::Invalid) {
1412 IConsolePrint(CC_ERROR, "No game loaded to reload.");
1413 return true;
1414 }
1415
1416 /* Use a switch-mode to prevent copying over newgame settings to active settings. */
1417 _settings_game.game_creation.map_x = Map::LogX();
1418 _settings_game.game_creation.map_y = Map::LogY();
1420 return true;
1421}
1422
1427static void PrintLineByLine(const std::string &full_string)
1428{
1429 std::istringstream in(full_string);
1430 std::string line;
1431 while (std::getline(in, line)) {
1433 }
1434}
1435
1442template <typename F, typename ... Args>
1443bool PrintList(F list_function, Args... args)
1444{
1445 std::string output_str;
1446 auto inserter = std::back_inserter(output_str);
1447 list_function(inserter, args...);
1448 PrintLineByLine(output_str);
1449
1450 return true;
1451}
1452
1454static bool ConListAILibs(std::span<std::string_view> argv)
1455{
1456 if (argv.empty()) {
1457 IConsolePrint(CC_HELP, "List installed AI libraries. Usage: 'list_ai_libs'.");
1458 return true;
1459 }
1460
1462}
1463
1465static bool ConListAI(std::span<std::string_view> argv)
1466{
1467 if (argv.empty()) {
1468 IConsolePrint(CC_HELP, "List installed AIs. Usage: 'list_ai'.");
1469 return true;
1470 }
1471
1472 return PrintList(AI::GetConsoleList, false);
1473}
1474
1476static bool ConListGameLibs(std::span<std::string_view> argv)
1477{
1478 if (argv.empty()) {
1479 IConsolePrint(CC_HELP, "List installed Game Script libraries. Usage: 'list_game_libs'.");
1480 return true;
1481 }
1482
1484}
1485
1487static bool ConListGame(std::span<std::string_view> argv)
1488{
1489 if (argv.empty()) {
1490 IConsolePrint(CC_HELP, "List installed Game Scripts. Usage: 'list_game'.");
1491 return true;
1492 }
1493
1494 return PrintList(Game::GetConsoleList, false);
1495}
1496
1498static bool ConStartAI(std::span<std::string_view> argv)
1499{
1500 if (argv.empty() || argv.size() > 3) {
1501 IConsolePrint(CC_HELP, "Start a new AI. Usage: 'start_ai [<AI>] [<settings>]'.");
1502 IConsolePrint(CC_HELP, "Start a new AI. If <AI> is given, it starts that specific AI (if found).");
1503 IConsolePrint(CC_HELP, "If <settings> is given, it is parsed and the AI settings are set to that.");
1504 return true;
1505 }
1506
1507 if (_game_mode != GameMode::Normal) {
1508 IConsolePrint(CC_ERROR, "AIs can only be managed in a game.");
1509 return true;
1510 }
1511
1513 IConsolePrint(CC_ERROR, "Can't start a new AI (no more free slots).");
1514 return true;
1515 }
1516 if (_networking && !_network_server) {
1517 IConsolePrint(CC_ERROR, "Only the server can start a new AI.");
1518 return true;
1519 }
1520 if (_networking && !_settings_game.ai.ai_in_multiplayer) {
1521 IConsolePrint(CC_ERROR, "AIs are not allowed in multiplayer by configuration.");
1522 IConsolePrint(CC_ERROR, "Switch AI -> AI in multiplayer to True.");
1523 return true;
1524 }
1525 if (!AI::CanStartNew()) {
1526 IConsolePrint(CC_ERROR, "Can't start a new AI.");
1527 return true;
1528 }
1529
1530 int n = 0;
1531 /* Find the next free slot */
1532 for (const Company *c : Company::Iterate()) {
1533 if (c->index != n) break;
1534 n++;
1535 }
1536
1537 AIConfig *config = AIConfig::GetConfig((CompanyID)n);
1538 if (argv.size() >= 2) {
1539 config->Change(argv[1], -1, false);
1540
1541 /* If the name is not found, and there is a dot in the name,
1542 * try again with the assumption everything right of the dot is
1543 * the version the user wants to load. */
1544 if (!config->HasScript()) {
1545 StringConsumer consumer{std::string_view{argv[1]}};
1546 auto name = consumer.ReadUntilChar('.', StringConsumer::SKIP_ONE_SEPARATOR);
1547 if (consumer.AnyBytesLeft()) {
1548 auto version = consumer.TryReadIntegerBase<uint32_t>(10);
1549 if (!version.has_value()) {
1550 IConsolePrint(CC_ERROR, "The version is not a valid number.");
1551 return true;
1552 }
1553 config->Change(name, *version, true);
1554 }
1555 }
1556
1557 if (!config->HasScript()) {
1558 IConsolePrint(CC_ERROR, "Failed to load the specified AI.");
1559 return true;
1560 }
1561 if (argv.size() == 3) {
1562 config->StringToSettings(argv[2]);
1563 }
1564 }
1565
1566 /* Start a new AI company */
1567 Command<Commands::CompanyControl>::Post(CompanyCtrlAction::NewAI, CompanyID::Invalid(), CompanyRemoveReason::None, ClientID::Invalid);
1568
1569 return true;
1570}
1571
1573static bool ConReloadAI(std::span<std::string_view> argv)
1574{
1575 if (argv.size() != 2) {
1576 IConsolePrint(CC_HELP, "Reload an AI. Usage: 'reload_ai <company-id>'.");
1577 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.");
1578 return true;
1579 }
1580
1581 if (_game_mode != GameMode::Normal) {
1582 IConsolePrint(CC_ERROR, "AIs can only be managed in a game.");
1583 return true;
1584 }
1585
1586 if (_networking && !_network_server) {
1587 IConsolePrint(CC_ERROR, "Only the server can reload an AI.");
1588 return true;
1589 }
1590
1591 auto company_id = ParseCompanyID(argv[1]);
1592 if (!company_id.has_value()) {
1593 IConsolePrint(CC_ERROR, "The given company-id is not a valid number.");
1594 return true;
1595 }
1596
1597 if (!Company::IsValidID(*company_id)) {
1598 IConsolePrint(CC_ERROR, "Unknown company. Company range is between 1 and {}.", MAX_COMPANIES);
1599 return true;
1600 }
1601
1602 /* In singleplayer mode the player can be in an AI company, after cheating or loading network save with an AI in first slot. */
1603 if (Company::IsHumanID(*company_id) || *company_id == _local_company) {
1604 IConsolePrint(CC_ERROR, "Company is not controlled by an AI.");
1605 return true;
1606 }
1607
1608 /* First kill the company of the AI, then start a new one. This should start the current AI again */
1609 Command<Commands::CompanyControl>::Post(CompanyCtrlAction::Delete, *company_id, CompanyRemoveReason::Manual, ClientID::Invalid);
1610 Command<Commands::CompanyControl>::Post(CompanyCtrlAction::NewAI, *company_id, CompanyRemoveReason::None, ClientID::Invalid);
1611 IConsolePrint(CC_DEFAULT, "AI reloaded.");
1612
1613 return true;
1614}
1615
1617static bool ConStopAI(std::span<std::string_view> argv)
1618{
1619 if (argv.size() != 2) {
1620 IConsolePrint(CC_HELP, "Stop an AI. Usage: 'stop_ai <company-id>'.");
1621 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.");
1622 return true;
1623 }
1624
1625 if (_game_mode != GameMode::Normal) {
1626 IConsolePrint(CC_ERROR, "AIs can only be managed in a game.");
1627 return true;
1628 }
1629
1630 if (_networking && !_network_server) {
1631 IConsolePrint(CC_ERROR, "Only the server can stop an AI.");
1632 return true;
1633 }
1634
1635 auto company_id = ParseCompanyID(argv[1]);
1636 if (!company_id.has_value()) {
1637 IConsolePrint(CC_ERROR, "The given company-id is not a valid number.");
1638 return true;
1639 }
1640
1641 if (!Company::IsValidID(*company_id)) {
1642 IConsolePrint(CC_ERROR, "Unknown company. Company range is between 1 and {}.", MAX_COMPANIES);
1643 return true;
1644 }
1645
1646 /* In singleplayer mode the player can be in an AI company, after cheating or loading network save with an AI in first slot. */
1647 if (Company::IsHumanID(*company_id) || *company_id == _local_company) {
1648 IConsolePrint(CC_ERROR, "Company is not controlled by an AI.");
1649 return true;
1650 }
1651
1652 /* Now kill the company of the AI. */
1653 Command<Commands::CompanyControl>::Post(CompanyCtrlAction::Delete, *company_id, CompanyRemoveReason::Manual, ClientID::Invalid);
1654 IConsolePrint(CC_DEFAULT, "AI stopped, company deleted.");
1655
1656 return true;
1657}
1658
1660static bool ConRescanAI(std::span<std::string_view> argv)
1661{
1662 if (argv.empty()) {
1663 IConsolePrint(CC_HELP, "Rescan the AI dir for scripts. Usage: 'rescan_ai'.");
1664 return true;
1665 }
1666
1667 if (_networking && !_network_server) {
1668 IConsolePrint(CC_ERROR, "Only the server can rescan the AI dir for scripts.");
1669 return true;
1670 }
1671
1672 AI::Rescan();
1673
1674 return true;
1675}
1676
1678static bool ConRescanGame(std::span<std::string_view> argv)
1679{
1680 if (argv.empty()) {
1681 IConsolePrint(CC_HELP, "Rescan the Game Script dir for scripts. Usage: 'rescan_game'.");
1682 return true;
1683 }
1684
1685 if (_networking && !_network_server) {
1686 IConsolePrint(CC_ERROR, "Only the server can rescan the Game Script dir for scripts.");
1687 return true;
1688 }
1689
1690 Game::Rescan();
1691
1692 return true;
1693}
1694
1696static bool ConRescanNewGRF(std::span<std::string_view> argv)
1697{
1698 if (argv.empty()) {
1699 IConsolePrint(CC_HELP, "Rescan the data dir for NewGRFs. Usage: 'rescan_newgrf'.");
1700 return true;
1701 }
1702
1703 if (!RequestNewGRFScan()) {
1704 IConsolePrint(CC_ERROR, "NewGRF scanning is already running. Please wait until completed to run again.");
1705 }
1706
1707 return true;
1708}
1709
1711static bool ConGetSeed(std::span<std::string_view> argv)
1712{
1713 if (argv.empty()) {
1714 IConsolePrint(CC_HELP, "Returns the seed used to create this game. Usage: 'getseed'.");
1715 IConsolePrint(CC_HELP, "The seed can be used to reproduce the exact same map as the game started with.");
1716 return true;
1717 }
1718
1719 IConsolePrint(CC_DEFAULT, "Generation Seed: {}", _settings_game.game_creation.generation_seed);
1720 return true;
1721}
1722
1724static bool ConGetDate(std::span<std::string_view> argv)
1725{
1726 if (argv.empty()) {
1727 IConsolePrint(CC_HELP, "Returns the current date (year-month-day) of the game. Usage: 'getdate'.");
1728 return true;
1729 }
1730
1731 TimerGameCalendar::YearMonthDay ymd = TimerGameCalendar::ConvertDateToYMD(TimerGameCalendar::date);
1732 IConsolePrint(CC_DEFAULT, "Date: {:04d}-{:02d}-{:02d}", ymd.year, ymd.month + 1, ymd.day);
1733 return true;
1734}
1735
1737static bool ConGetSysDate(std::span<std::string_view> argv)
1738{
1739 if (argv.empty()) {
1740 IConsolePrint(CC_HELP, "Returns the current date (year-month-day) of your system. Usage: 'getsysdate'.");
1741 return true;
1742 }
1743
1744 IConsolePrint(CC_DEFAULT, "System Date: {:%Y-%m-%d %H:%M:%S}", fmt::localtime(time(nullptr)));
1745 return true;
1746}
1747
1749static bool ConAlias(std::span<std::string_view> argv)
1750{
1751 IConsoleAlias *alias;
1752
1753 if (argv.empty()) {
1754 IConsolePrint(CC_HELP, "Add a new alias, or redefine the behaviour of an existing alias . Usage: 'alias <name> <command>'.");
1755 return true;
1756 }
1757
1758 if (argv.size() < 3) return false;
1759
1760 alias = IConsole::AliasGet(std::string(argv[1]));
1761 if (alias == nullptr) {
1762 IConsole::AliasRegister(std::string(argv[1]), argv[2]);
1763 } else {
1764 alias->cmdline = argv[2];
1765 }
1766 return true;
1767}
1768
1770static bool ConScreenShot(std::span<std::string_view> argv)
1771{
1772 if (argv.empty()) {
1773 IConsolePrint(CC_HELP, "Create a screenshot of the game. Usage: 'screenshot [viewport | normal | big | giant | heightmap | minimap] [no_con] [size <width> <height>] [<filename>]'.");
1774 IConsolePrint(CC_HELP, " 'viewport' (default) makes a screenshot of the current viewport (including menus, windows).");
1775 IConsolePrint(CC_HELP, " 'normal' makes a screenshot of the visible area.");
1776 IConsolePrint(CC_HELP, " 'big' makes a zoomed-in screenshot of the visible area.");
1777 IConsolePrint(CC_HELP, " 'giant' makes a screenshot of the whole map.");
1778 IConsolePrint(CC_HELP, " 'heightmap' makes a heightmap screenshot of the map that can be loaded in as heightmap.");
1779 IConsolePrint(CC_HELP, " 'minimap' makes a top-viewed minimap screenshot of the whole world which represents one tile by one pixel.");
1780 IConsolePrint(CC_HELP, " 'no_con' hides the console to create the screenshot (only useful in combination with 'viewport').");
1781 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').");
1782 IConsolePrint(CC_HELP, " A filename ending in # will prevent overwriting existing files and will number files counting upwards.");
1783 return true;
1784 }
1785
1786 if (argv.size() > 7) return false;
1787
1789 uint32_t width = 0;
1790 uint32_t height = 0;
1791 std::string name{};
1792 uint32_t arg_index = 1;
1793
1794 if (argv.size() > arg_index) {
1795 if (argv[arg_index] == "viewport") {
1796 type = SC_VIEWPORT;
1797 arg_index += 1;
1798 } else if (argv[arg_index] == "normal") {
1799 type = SC_DEFAULTZOOM;
1800 arg_index += 1;
1801 } else if (argv[arg_index] == "big") {
1802 type = SC_ZOOMEDIN;
1803 arg_index += 1;
1804 } else if (argv[arg_index] == "giant") {
1805 type = SC_WORLD;
1806 arg_index += 1;
1807 } else if (argv[arg_index] == "heightmap") {
1808 type = SC_HEIGHTMAP;
1809 arg_index += 1;
1810 } else if (argv[arg_index] == "minimap") {
1811 type = SC_MINIMAP;
1812 arg_index += 1;
1813 }
1814 }
1815
1816 if (argv.size() > arg_index && argv[arg_index] == "no_con") {
1817 if (type != SC_VIEWPORT) {
1818 IConsolePrint(CC_ERROR, "'no_con' can only be used in combination with 'viewport'.");
1819 return true;
1820 }
1821 IConsoleClose();
1822 arg_index += 1;
1823 }
1824
1825 if (argv.size() > arg_index + 2 && argv[arg_index] == "size") {
1826 /* size <width> <height> */
1827 if (type != SC_DEFAULTZOOM && type != SC_ZOOMEDIN) {
1828 IConsolePrint(CC_ERROR, "'size' can only be used in combination with 'normal' or 'big'.");
1829 return true;
1830 }
1831 auto t = ParseInteger(argv[arg_index + 1]);
1832 if (!t.has_value()) {
1833 IConsolePrint(CC_ERROR, "Invalid width '{}'", argv[arg_index + 1]);
1834 return true;
1835 }
1836 width = *t;
1837
1838 t = ParseInteger(argv[arg_index + 2]);
1839 if (!t.has_value()) {
1840 IConsolePrint(CC_ERROR, "Invalid height '{}'", argv[arg_index + 2]);
1841 return true;
1842 }
1843 height = *t;
1844 arg_index += 3;
1845 }
1846
1847 if (argv.size() > arg_index) {
1848 /* Last parameter that was not one of the keywords must be the filename. */
1849 name = argv[arg_index];
1850 arg_index += 1;
1851 }
1852
1853 if (argv.size() > arg_index) {
1854 /* We have parameters we did not process; means we misunderstood any of the above. */
1855 return false;
1856 }
1857
1858 MakeScreenshot(type, std::move(name), width, height);
1859 return true;
1860}
1861
1863static bool ConInfoCmd(std::span<std::string_view> argv)
1864{
1865 if (argv.empty()) {
1866 IConsolePrint(CC_HELP, "Print out debugging information about a command. Usage: 'info_cmd <cmd>'.");
1867 return true;
1868 }
1869
1870 if (argv.size() < 2) return false;
1871
1872 const IConsoleCmd *cmd = IConsole::CmdGet(std::string(argv[1]));
1873 if (cmd == nullptr) {
1874 IConsolePrint(CC_ERROR, "The given command was not found.");
1875 return true;
1876 }
1877
1878 IConsolePrint(CC_DEFAULT, "Command name: '{}'", cmd->name);
1879
1880 if (cmd->hook != nullptr) IConsolePrint(CC_DEFAULT, "Command is hooked.");
1881
1882 return true;
1883}
1884
1886static bool ConDebugLevel(std::span<std::string_view> argv)
1887{
1888 if (argv.empty()) {
1889 IConsolePrint(CC_HELP, "Get/set the default debugging level for the game. Usage: 'debug_level [<level>]'.");
1890 IConsolePrint(CC_HELP, "Level can be any combination of names, levels. Eg 'net=5 ms=4'. Remember to enclose it in \"'\"s.");
1891 return true;
1892 }
1893
1894 if (argv.size() > 2) return false;
1895
1896 if (argv.size() == 1) {
1897 IConsolePrint(CC_DEFAULT, "Current debug-level: '{}'", GetDebugString());
1898 } else {
1899 SetDebugString(argv[1], [](std::string_view err) { IConsolePrint(CC_ERROR, "{}", err); });
1900 }
1901
1902 return true;
1903}
1904
1906static bool ConExit(std::span<std::string_view> argv)
1907{
1908 if (argv.empty()) {
1909 IConsolePrint(CC_HELP, "Exit the game. Usage: 'exit'.");
1910 return true;
1911 }
1912
1913 if (_game_mode == GameMode::Normal && _settings_client.gui.autosave_on_exit) DoExitSave();
1914
1915 _exit_game = true;
1916 return true;
1917}
1918
1920static bool ConPart(std::span<std::string_view> argv)
1921{
1922 if (argv.empty()) {
1923 IConsolePrint(CC_HELP, "Leave the currently joined/running game (only ingame). Usage: 'part'.");
1924 return true;
1925 }
1926
1927 if (_game_mode != GameMode::Normal) return false;
1928
1929 if (_network_dedicated) {
1930 IConsolePrint(CC_ERROR, "A dedicated server can not leave the game.");
1931 return false;
1932 }
1933
1935 return true;
1936}
1937
1939static bool ConHelp(std::span<std::string_view> argv)
1940{
1941 if (argv.size() == 2) {
1942 const IConsoleCmd *cmd;
1943 const IConsoleAlias *alias;
1944
1945 cmd = IConsole::CmdGet(std::string(argv[1]));
1946 if (cmd != nullptr) {
1947 cmd->proc({});
1948 return true;
1949 }
1950
1951 alias = IConsole::AliasGet(std::string(argv[1]));
1952 if (alias != nullptr) {
1953 cmd = IConsole::CmdGet(alias->cmdline);
1954 if (cmd != nullptr) {
1955 cmd->proc({});
1956 return true;
1957 }
1958 IConsolePrint(CC_ERROR, "Alias is of special type, please see its execution-line: '{}'.", alias->cmdline);
1959 return true;
1960 }
1961
1962 IConsolePrint(CC_ERROR, "Command not found.");
1963 return true;
1964 }
1965
1966 IConsolePrint(TextColour::LightBlue, " ---- OpenTTD Console Help ---- ");
1967 IConsolePrint(CC_DEFAULT, " - commands: the command to list all commands is 'list_cmds'.");
1968 IConsolePrint(CC_DEFAULT, " call commands with '<command> <arg2> <arg3>...'");
1969 IConsolePrint(CC_DEFAULT, " - to assign strings, or use them as arguments, enclose it within quotes.");
1970 IConsolePrint(CC_DEFAULT, " like this: '<command> \"string argument with spaces\"'.");
1971 IConsolePrint(CC_DEFAULT, " - use 'help <command>' to get specific information.");
1972 IConsolePrint(CC_DEFAULT, " - scroll console output with shift + (up | down | pageup | pagedown).");
1973 IConsolePrint(CC_DEFAULT, " - scroll console input history with the up or down arrows.");
1975 return true;
1976}
1977
1979static bool ConListCommands(std::span<std::string_view> argv)
1980{
1981 if (argv.empty()) {
1982 IConsolePrint(CC_HELP, "List all registered commands. Usage: 'list_cmds [<pre-filter>]'.");
1983 return true;
1984 }
1985
1986 for (auto &it : IConsole::Commands()) {
1987 const IConsoleCmd *cmd = &it.second;
1988 if (argv.size() <= 1|| cmd->name.find(argv[1]) != std::string::npos) {
1989 if (cmd->hook == nullptr || cmd->hook(false) != ConsoleHookResult::Hide) IConsolePrint(CC_DEFAULT, cmd->name);
1990 }
1991 }
1992
1993 return true;
1994}
1995
1997static bool ConListAliases(std::span<std::string_view> argv)
1998{
1999 if (argv.empty()) {
2000 IConsolePrint(CC_HELP, "List all registered aliases. Usage: 'list_aliases [<pre-filter>]'.");
2001 return true;
2002 }
2003
2004 for (auto &it : IConsole::Aliases()) {
2005 const IConsoleAlias *alias = &it.second;
2006 if (argv.size() <= 1 || alias->name.find(argv[1]) != std::string::npos) {
2007 IConsolePrint(CC_DEFAULT, "{} => {}", alias->name, alias->cmdline);
2008 }
2009 }
2010
2011 return true;
2012}
2013
2015static bool ConCompanies(std::span<std::string_view> argv)
2016{
2017 if (argv.empty()) {
2018 IConsolePrint(CC_HELP, "List the details of all companies in the game. Usage 'companies'.");
2019 return true;
2020 }
2021
2022 for (const Company *c : Company::Iterate()) {
2023 /* Grab the company name */
2024 std::string company_name = GetString(STR_COMPANY_NAME, c->index);
2025
2026 std::string colour = GetString(STR_COLOUR_DARK_BLUE + to_underlying(_company_colours[c->index]));
2027 IConsolePrint(CC_INFO, "#:{}({}) Company Name: '{}' Year Founded: {} Money: {} Loan: {} Value: {} (T:{}, R:{}, P:{}, S:{}) {}",
2028 c->index + 1, colour, company_name,
2029 c->inaugurated_year, (int64_t)c->money, (int64_t)c->current_loan, (int64_t)CalculateCompanyValue(c),
2030 c->group_all[VehicleType::Train].num_vehicle,
2031 c->group_all[VehicleType::Road].num_vehicle,
2032 c->group_all[VehicleType::Aircraft].num_vehicle,
2033 c->group_all[VehicleType::Ship].num_vehicle,
2034 c->is_ai ? "AI" : "");
2035 }
2036
2037 return true;
2038}
2039
2041static bool ConSay(std::span<std::string_view> argv)
2042{
2043 if (argv.empty()) {
2044 IConsolePrint(CC_HELP, "Chat to your fellow players in a multiplayer game. Usage: 'say \"<msg>\"'.");
2045 return true;
2046 }
2047
2048 if (argv.size() != 2) return false;
2049
2050 if (!_network_server) {
2052 } else {
2053 bool from_admin = (_redirect_console_to_admin < AdminID::Invalid());
2055 }
2056
2057 return true;
2058}
2059
2061static bool ConSayCompany(std::span<std::string_view> argv)
2062{
2063 if (argv.empty()) {
2064 IConsolePrint(CC_HELP, "Chat to a certain company in a multiplayer game. Usage: 'say_company <company-no> \"<msg>\"'.");
2065 IConsolePrint(CC_HELP, "CompanyNo is the company that plays as company <companyno>, 1 through max_companies.");
2066 return true;
2067 }
2068
2069 if (argv.size() != 3) return false;
2070
2071 auto company_id = ParseCompanyID(argv[1]);
2072 if (!company_id.has_value()) {
2073 IConsolePrint(CC_ERROR, "The given company-id is not a valid number.");
2074 return true;
2075 }
2076
2077 if (!Company::IsValidID(*company_id)) {
2078 IConsolePrint(CC_DEFAULT, "Unknown company. Company range is between 1 and {}.", MAX_COMPANIES);
2079 return true;
2080 }
2081
2082 if (!_network_server) {
2084 } else {
2085 bool from_admin = (_redirect_console_to_admin < AdminID::Invalid());
2087 }
2088
2089 return true;
2090}
2091
2093static bool ConSayClient(std::span<std::string_view> argv)
2094{
2095 if (argv.empty()) {
2096 IConsolePrint(CC_HELP, "Chat to a certain client in a multiplayer game. Usage: 'say_client <client-id> \"<msg>\"'.");
2097 IConsolePrint(CC_HELP, "For client-id's, see the command 'clients'.");
2098 return true;
2099 }
2100
2101 if (argv.size() != 3) return false;
2102
2103 auto client_id = ParseType<ClientID>(argv[1]);
2104 if (!client_id.has_value()) {
2105 IConsolePrint(CC_ERROR, "The given client-id is not a valid number.");
2106 return true;
2107 }
2108
2109 if (!_network_server) {
2111 } else {
2112 bool from_admin = (_redirect_console_to_admin < AdminID::Invalid());
2114 }
2115
2116 return true;
2117}
2118
2120static const std::initializer_list<std::pair<std::string_view, NetworkAuthorizedKeys *>> _console_cmd_authorized_keys{
2121 { "admin", &_settings_client.network.admin_authorized_keys },
2122 { "rcon", &_settings_client.network.rcon_authorized_keys },
2123 { "server", &_settings_client.network.server_authorized_keys },
2124};
2125
2132
2133static void PerformNetworkAuthorizedKeyAction(std::string_view name, NetworkAuthorizedKeys *authorized_keys, ConNetworkAuthorizedKeyAction action, const std::string &authorized_key, CompanyID company = CompanyID::Invalid())
2134{
2135 switch (action) {
2137 IConsolePrint(CC_WHITE, "The authorized keys for {} are:", name);
2138 for (auto &ak : *authorized_keys) IConsolePrint(CC_INFO, " {}", ak);
2139 return;
2140
2142 if (authorized_keys->Contains(authorized_key)) {
2143 IConsolePrint(CC_WARNING, "Not added {} to {} as it already exists.", authorized_key, name);
2144 return;
2145 }
2146
2147 if (company == CompanyID::Invalid()) {
2148 authorized_keys->Add(authorized_key);
2149 } else {
2150 AutoRestoreBackup backup(_current_company, company);
2151 Command<Commands::CompanyAllowListControl>::Post(CompanyAllowListCtrlAction::AddKey, authorized_key);
2152 }
2153 IConsolePrint(CC_INFO, "Added {} to {}.", authorized_key, name);
2154 return;
2155
2157 if (!authorized_keys->Contains(authorized_key)) {
2158 IConsolePrint(CC_WARNING, "Not removed {} from {} as it does not exist.", authorized_key, name);
2159 return;
2160 }
2161
2162 if (company == CompanyID::Invalid()) {
2163 authorized_keys->Remove(authorized_key);
2164 } else {
2165 AutoRestoreBackup backup(_current_company, company);
2166 Command<Commands::CompanyAllowListControl>::Post(CompanyAllowListCtrlAction::RemoveKey, authorized_key);
2167 }
2168 IConsolePrint(CC_INFO, "Removed {} from {}.", authorized_key, name);
2169 return;
2170 }
2171}
2172
2174static bool ConNetworkAuthorizedKey(std::span<std::string_view> argv)
2175{
2176 if (argv.size() <= 2) {
2177 IConsolePrint(CC_HELP, "List and update authorized keys. Usage: 'authorized_key list [type]|add [type] [key]|remove [type] [key]'.");
2178 IConsolePrint(CC_HELP, " list: list all the authorized keys of the given type.");
2179 IConsolePrint(CC_HELP, " add: add the given key to the authorized keys of the given type.");
2180 IConsolePrint(CC_HELP, " remove: remove the given key from the authorized keys of the given type; use 'all' to remove all authorized keys.");
2181 IConsolePrint(CC_HELP, "Instead of a key, use 'client:<id>' to add/remove the key of that given client.");
2182
2183 std::string buffer;
2184 for (auto [name, _] : _console_cmd_authorized_keys) format_append(buffer, ", {}", name);
2185 IConsolePrint(CC_HELP, "The supported types are: all{} and company:<id>.", buffer);
2186 return true;
2187 }
2188
2190 std::string_view action_string = argv[1];
2191 if (StrEqualsIgnoreCase(action_string, "list")) {
2193 } else if (StrEqualsIgnoreCase(action_string, "add")) {
2195 } else if (StrEqualsIgnoreCase(action_string, "remove") || StrEqualsIgnoreCase(action_string, "delete")) {
2197 } else {
2198 IConsolePrint(CC_WARNING, "No valid action was given.");
2199 return false;
2200 }
2201
2202 std::string authorized_key;
2204 if (argv.size() <= 3) {
2205 IConsolePrint(CC_ERROR, "You must enter the key.");
2206 return false;
2207 }
2208
2209 authorized_key = argv[3];
2210 if (StrStartsWithIgnoreCase(authorized_key, "client:")) {
2211 auto value = ParseInteger<uint32_t>(authorized_key.substr(7));
2212 if (value.has_value()) authorized_key = NetworkGetPublicKeyOfClient(static_cast<ClientID>(*value));
2213 if (!value.has_value() || authorized_key.empty()) {
2214 IConsolePrint(CC_ERROR, "You must enter a valid client id; see 'clients'.");
2215 return false;
2216 }
2217 }
2218
2219 if (authorized_key.size() != NETWORK_PUBLIC_KEY_LENGTH - 1) {
2220 IConsolePrint(CC_ERROR, "You must enter a valid authorized key.");
2221 return false;
2222 }
2223 }
2224
2225 std::string_view type = argv[2];
2226 if (StrEqualsIgnoreCase(type, "all")) {
2227 for (auto [name, authorized_keys] : _console_cmd_authorized_keys) PerformNetworkAuthorizedKeyAction(name, authorized_keys, action, authorized_key);
2228 for (Company *c : Company::Iterate()) PerformNetworkAuthorizedKeyAction(fmt::format("company:{}", c->index + 1), &c->allow_list, action, authorized_key, c->index);
2229 return true;
2230 }
2231
2232 if (StrStartsWithIgnoreCase(type, "company:")) {
2233 auto value = ParseInteger<uint32_t>(type.substr(8));
2234 Company *c = value.has_value() ? Company::GetIfValid(*value - 1) : nullptr;
2235 if (c == nullptr) {
2236 IConsolePrint(CC_ERROR, "You must enter a valid company id; see 'companies'.");
2237 return false;
2238 }
2239
2240 PerformNetworkAuthorizedKeyAction(type, &c->allow_list, action, authorized_key, c->index);
2241 return true;
2242 }
2243
2244 for (auto [name, authorized_keys] : _console_cmd_authorized_keys) {
2245 if (!StrEqualsIgnoreCase(type, name)) continue;
2246
2247 PerformNetworkAuthorizedKeyAction(name, authorized_keys, action, authorized_key);
2248 return true;
2249 }
2250
2251 IConsolePrint(CC_WARNING, "No valid type was given.");
2252 return false;
2253}
2254
2255
2256/* Content downloading only is available with ZLIB */
2257#if defined(WITH_ZLIB)
2258
2264static ContentType StringToContentType(std::string_view str)
2265{
2266 static const std::initializer_list<std::pair<std::string_view, ContentType>> content_types = {
2267 {"base", ContentType::BaseGraphics},
2268 {"newgrf", ContentType::NewGRF},
2269 {"ai", ContentType::Ai},
2270 {"ailib", ContentType::AiLibrary},
2271 {"scenario", ContentType::Scenario},
2272 {"heightmap", ContentType::Heightmap},
2273 };
2274 for (const auto &ct : content_types) {
2275 if (StrEqualsIgnoreCase(str, ct.first)) return ct.second;
2276 }
2277 return ContentType::End;
2278}
2279
2282 void OnConnect(bool success) override
2283 {
2284 IConsolePrint(CC_DEFAULT, "Content server connection {}.", success ? "established" : "failed");
2285 }
2286
2287 void OnDisconnect() override
2288 {
2289 IConsolePrint(CC_DEFAULT, "Content server connection closed.");
2290 }
2291
2293 {
2294 IConsolePrint(CC_DEFAULT, "Completed download of {}.", cid);
2295 }
2296};
2297
2302static void OutputContentState(const ContentInfo &ci)
2303{
2305 "", "Base graphics", "NewGRF", "AI", "AI library", "Scenario", "Heightmap", "Base sound", "Base music", "Game script", "GS library"
2306 };
2308 "Not selected", "Selected", "Dep Selected", "Installed", "Unknown"
2309 };
2312 };
2313
2314 IConsolePrint(state_to_colour[ci.state], "{}, {}, {}, {}, {:08X}, {}", ci.id, types[ci.type], states[ci.state], ci.name, ci.unique_id, FormatArrayAsHex(ci.md5sum));
2315}
2316
2318static bool ConContent(std::span<std::string_view> argv)
2319{
2320 [[maybe_unused]] static ContentCallback *const cb = []() {
2321 auto res = new ConsoleContentCallback();
2322 _network_content_client.AddCallback(res);
2323 return res;
2324 }();
2325
2326 if (argv.size() <= 1) {
2327 IConsolePrint(CC_HELP, "Query, select and download content. Usage: 'content update|upgrade|select [id]|unselect [all|id]|state [filter]|download'.");
2328 IConsolePrint(CC_HELP, " update: get a new list of downloadable content; must be run first.");
2329 IConsolePrint(CC_HELP, " upgrade: select all items that are upgrades.");
2330 IConsolePrint(CC_HELP, " select: select a specific item given by its id. If no parameter is given, all selected content will be listed.");
2331 IConsolePrint(CC_HELP, " unselect: unselect a specific item given by its id or 'all' to unselect all.");
2332 IConsolePrint(CC_HELP, " state: show the download/select state of all downloadable content. Optionally give a filter string.");
2333 IConsolePrint(CC_HELP, " download: download all content you've selected.");
2334 return true;
2335 }
2336
2337 if (StrEqualsIgnoreCase(argv[1], "update")) {
2338 _network_content_client.RequestContentList((argv.size() > 2) ? StringToContentType(argv[2]) : ContentType::End);
2339 return true;
2340 }
2341
2342 if (StrEqualsIgnoreCase(argv[1], "upgrade")) {
2343 _network_content_client.SelectUpgrade();
2344 return true;
2345 }
2346
2347 if (StrEqualsIgnoreCase(argv[1], "select")) {
2348 if (argv.size() <= 2) {
2349 /* List selected content */
2350 IConsolePrint(CC_WHITE, "id, type, state, name");
2351 for (const ContentInfo &ci : _network_content_client.Info()) {
2352 if (ci.state != ContentInfo::State::Selected && ci.state != ContentInfo::State::Autoselected) continue;
2354 }
2355 } else if (StrEqualsIgnoreCase(argv[2], "all")) {
2356 /* The intention of this function was that you could download
2357 * everything after a filter was applied; but this never really
2358 * took off. Instead, a select few people used this functionality
2359 * to download every available package on BaNaNaS. This is not in
2360 * the spirit of this service. Additionally, these few people were
2361 * good for 70% of the consumed bandwidth of BaNaNaS. */
2362 IConsolePrint(CC_ERROR, "'select all' is no longer supported since 1.11.");
2363 } else if (auto content_id = ParseType<ContentID>(argv[2]); content_id.has_value()) {
2364 _network_content_client.Select(*content_id);
2365 } else {
2366 IConsolePrint(CC_ERROR, "The given content-id is not a number or 'all'");
2367 }
2368 return true;
2369 }
2370
2371 if (StrEqualsIgnoreCase(argv[1], "unselect")) {
2372 if (argv.size() <= 2) {
2373 IConsolePrint(CC_ERROR, "You must enter the id.");
2374 return false;
2375 }
2376 if (StrEqualsIgnoreCase(argv[2], "all")) {
2377 _network_content_client.UnselectAll();
2378 } else if (auto content_id = ParseType<ContentID>(argv[2]); content_id.has_value()) {
2379 _network_content_client.Unselect(*content_id);
2380 } else {
2381 IConsolePrint(CC_ERROR, "The given content-id is not a number or 'all'");
2382 }
2383 return true;
2384 }
2385
2386 if (StrEqualsIgnoreCase(argv[1], "state")) {
2387 IConsolePrint(CC_WHITE, "id, type, state, name");
2388 for (const ContentInfo &ci : _network_content_client.Info()) {
2389 if (argv.size() > 2 && !StrContainsIgnoreCase(ci.name, argv[2])) continue;
2391 }
2392 return true;
2393 }
2394
2395 if (StrEqualsIgnoreCase(argv[1], "download")) {
2396 uint files;
2397 uint bytes;
2398 _network_content_client.DownloadSelectedContent(files, bytes);
2399 IConsolePrint(CC_DEFAULT, "Downloading {} file(s) ({} bytes).", files, bytes);
2400 return true;
2401 }
2402
2403 return false;
2404}
2405#endif /* defined(WITH_ZLIB) */
2406
2412static FontSize GetFontSizeByName(std::string_view name)
2413{
2414 for (FontSize fs : EnumRange(FontSize::End)) {
2415 if (StrEqualsIgnoreCase(name, FontSizeToName(fs))) return fs;
2416 }
2417 return FontSize::End;
2418}
2419
2421static bool ConFont(std::span<std::string_view> argv)
2422{
2423 if (argv.empty()) {
2424 IConsolePrint(CC_HELP, "Manage the fonts configuration.");
2425 IConsolePrint(CC_HELP, "Usage 'font'.");
2426 IConsolePrint(CC_HELP, " Print out the fonts configuration.");
2427 IConsolePrint(CC_HELP, " The \"Currently active\" configuration is the one actually in effect (after interface scaling and replacing unavailable fonts).");
2428 IConsolePrint(CC_HELP, " The \"Requested\" configuration is the one requested via console command or config file.");
2429 IConsolePrint(CC_HELP, "Usage 'font [medium|small|large|mono] [<font name>] [<size>]'.");
2430 IConsolePrint(CC_HELP, " Change the configuration for a font.");
2431 IConsolePrint(CC_HELP, " Omitting an argument will keep the current value.");
2432 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.");
2433 IConsolePrint(CC_HELP, " If the sprite font is enabled in Game Options, it is used instead of the default font.");
2434 IConsolePrint(CC_HELP, " The <size> is automatically multiplied by the current interface scaling.");
2435 return true;
2436 }
2437
2438 if (argv.size() > 2) {
2439 /* First argument must be a FontSize. */
2440 FontSize argfs = GetFontSizeByName(argv[1]);
2441 if (argfs == FontSize::End) return false;
2442
2444 std::string font = setting->font;
2445 uint size = setting->size;
2446 uint8_t arg_index = 2;
2447 /* For <name> we want a string. */
2448
2449 if (!ParseInteger(argv[arg_index]).has_value()) {
2450 font = argv[arg_index++];
2451 }
2452
2453 if (argv.size() > arg_index) {
2454 /* For <size> we want a number. */
2455 auto v = ParseInteger(argv[arg_index]);
2456 if (v.has_value()) {
2457 size = *v;
2458 arg_index++;
2459 }
2460 }
2461
2462 SetFont(argfs, font, size);
2463 }
2464
2465 for (FontSize fs : EnumRange(FontSize::End)) {
2466 FontCache *fc = FontCache::Get(fs);
2468 /* Make sure all non sprite fonts are loaded. */
2469 if (!setting->font.empty() && !fc->HasParent()) {
2471 fc = FontCache::Get(fs);
2472 }
2473 IConsolePrint(CC_DEFAULT, "{} font:", FontSizeToName(fs));
2474 IConsolePrint(CC_DEFAULT, "Currently active: \"{}\", size {}", fc->GetFontName(), fc->GetFontSize());
2475 IConsolePrint(CC_DEFAULT, "Requested: \"{}\", size {}", setting->font, setting->size);
2476 }
2477
2478 return true;
2479}
2480
2482static bool ConSetting(std::span<std::string_view> argv)
2483{
2484 if (argv.empty()) {
2485 IConsolePrint(CC_HELP, "Change setting for all clients. Usage: 'setting <name> [<value>]'.");
2486 IConsolePrint(CC_HELP, "Omitting <value> will print out the current value of the setting.");
2487 return true;
2488 }
2489
2490 if (argv.size() == 1 || argv.size() > 3) return false;
2491
2492 if (argv.size() == 2) {
2493 IConsoleGetSetting(argv[1]);
2494 } else {
2495 IConsoleSetSetting(argv[1], argv[2]);
2496 }
2497
2498 return true;
2499}
2500
2502static bool ConSettingNewgame(std::span<std::string_view> argv)
2503{
2504 if (argv.empty()) {
2505 IConsolePrint(CC_HELP, "Change setting for the next game. Usage: 'setting_newgame <name> [<value>]'.");
2506 IConsolePrint(CC_HELP, "Omitting <value> will print out the current value of the setting.");
2507 return true;
2508 }
2509
2510 if (argv.size() == 1 || argv.size() > 3) return false;
2511
2512 if (argv.size() == 2) {
2513 IConsoleGetSetting(argv[1], true);
2514 } else {
2515 IConsoleSetSetting(argv[1], argv[2], true);
2516 }
2517
2518 return true;
2519}
2520
2522static bool ConListSettings(std::span<std::string_view> argv)
2523{
2524 if (argv.empty()) {
2525 IConsolePrint(CC_HELP, "List settings. Usage: 'list_settings [<pre-filter>]'.");
2526 return true;
2527 }
2528
2529 if (argv.size() > 2) return false;
2530
2531 IConsoleListSettings((argv.size() == 2) ? argv[1] : std::string_view{});
2532 return true;
2533}
2534
2536static bool ConGamelogPrint(std::span<std::string_view> argv)
2537{
2538 if (argv.empty()) {
2539 IConsolePrint(CC_HELP, "Print logged fundamental changes to the game since the start. Usage: 'gamelog'.");
2540 return true;
2541 }
2542
2543 _gamelog.PrintConsole();
2544 return true;
2545}
2546
2548static bool ConNewGRFReload(std::span<std::string_view> argv)
2549{
2550 if (argv.empty()) {
2551 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!");
2552 return true;
2553 }
2554
2556 return true;
2557}
2558
2560static bool ConListDirs(std::span<std::string_view> argv)
2561{
2562 struct SubdirNameMap {
2563 std::string_view name;
2564 Subdirectory subdir;
2565 bool default_only;
2566 };
2567 static const SubdirNameMap subdir_name_map[] = {
2568 /* Game data directories */
2569 { "baseset", Subdirectory::Baseset, false },
2570 { "newgrf", Subdirectory::NewGrf, false },
2571 { "ai", Subdirectory::Ai, false },
2572 { "ailib", Subdirectory::AiLibrary, false },
2573 { "gs", Subdirectory::Gs, false },
2574 { "gslib", Subdirectory::GsLibrary, false },
2575 { "scenario", Subdirectory::Scenario, false },
2576 { "heightmap", Subdirectory::Heightmap, false },
2577 /* Default save locations for user data */
2578 { "save", Subdirectory::Save, true },
2579 { "autosave", Subdirectory::Autosave, true },
2580 { "screenshot", Subdirectory::Screenshot, true },
2581 { "social_integration", Subdirectory::SocialIntegration, true },
2582 };
2583
2584 if (argv.size() != 2) {
2585 IConsolePrint(CC_HELP, "List all search paths or default directories for various categories.");
2586 IConsolePrint(CC_HELP, "Usage: list_dirs <category>");
2587 std::string cats{subdir_name_map[0].name};
2588 bool first = true;
2589 for (const SubdirNameMap &sdn : subdir_name_map) {
2590 if (!first) {
2591 cats += ", ";
2592 cats += sdn.name;
2593 }
2594 first = false;
2595 }
2596 IConsolePrint(CC_HELP, "Valid categories: {}", cats);
2597 return true;
2598 }
2599
2600 std::set<std::string> seen_dirs;
2601 for (const SubdirNameMap &sdn : subdir_name_map) {
2602 if (!StrEqualsIgnoreCase(argv[1], sdn.name)) continue;
2603 bool found = false;
2604 for (Searchpath sp : _valid_searchpaths) {
2605 /* Get the directory */
2606 std::string path = FioGetDirectory(sp, sdn.subdir);
2607 /* Check it hasn't already been listed */
2608 if (seen_dirs.find(path) != seen_dirs.end()) continue;
2609 seen_dirs.insert(path);
2610 /* Check if exists and mark found */
2611 bool exists = FileExists(path);
2612 found |= exists;
2613 /* Print */
2614 if (!sdn.default_only || exists) {
2615 IConsolePrint(exists ? CC_DEFAULT : CC_INFO, "{} {}", path, exists ? "[ok]" : "[not found]");
2616 if (sdn.default_only) break;
2617 }
2618 }
2619 if (!found) {
2620 IConsolePrint(CC_ERROR, "No directories exist for category {}", argv[1]);
2621 }
2622 return true;
2623 }
2624
2625 IConsolePrint(CC_ERROR, "Invalid category name: {}", argv[1]);
2626 return false;
2627}
2628
2630static bool ConNewGRFProfile(std::span<std::string_view> argv)
2631{
2632 if (argv.empty()) {
2633 IConsolePrint(CC_HELP, "Collect performance data about NewGRF sprite requests and callbacks. Sub-commands can be abbreviated.");
2634 IConsolePrint(CC_HELP, "Usage: 'newgrf_profile [list]':");
2635 IConsolePrint(CC_HELP, " List all NewGRFs that can be profiled, and their status.");
2636 IConsolePrint(CC_HELP, "Usage: 'newgrf_profile select <grf-num>...':");
2637 IConsolePrint(CC_HELP, " Select one or more GRFs for profiling.");
2638 IConsolePrint(CC_HELP, "Usage: 'newgrf_profile unselect <grf-num>...':");
2639 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.");
2640 IConsolePrint(CC_HELP, "Usage: 'newgrf_profile start [<num-ticks>]':");
2641 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.");
2642 IConsolePrint(CC_HELP, "Usage: 'newgrf_profile stop':");
2643 IConsolePrint(CC_HELP, " End profiling and write the collected data to CSV files.");
2644 IConsolePrint(CC_HELP, "Usage: 'newgrf_profile abort':");
2645 IConsolePrint(CC_HELP, " End profiling and discard all collected data.");
2646 return true;
2647 }
2648
2649 std::span<const GRFFile> files = GetAllGRFFiles();
2650
2651 /* "list" sub-command */
2652 if (argv.size() == 1 || StrStartsWithIgnoreCase(argv[1], "lis")) {
2653 IConsolePrint(CC_INFO, "Loaded GRF files:");
2654 int i = 1;
2655 for (const auto &grf : files) {
2656 auto profiler = std::ranges::find(_newgrf_profilers, &grf, &NewGRFProfiler::grffile);
2657 bool selected = profiler != _newgrf_profilers.end();
2658 bool active = selected && profiler->active;
2659 TextColour tc = active ? TextColour::LightBlue : selected ? TextColour::Green : CC_INFO;
2660 std::string_view statustext = active ? " (active)" : selected ? " (selected)" : "";
2661 IConsolePrint(tc, "{}: [{}] {}{}", i, FormatArrayAsHex(grf.grfid), grf.filename, statustext);
2662 i++;
2663 }
2664 return true;
2665 }
2666
2667 /* "select" sub-command */
2668 if (StrStartsWithIgnoreCase(argv[1], "sel") && argv.size() >= 3) {
2669 for (size_t argnum = 2; argnum < argv.size(); ++argnum) {
2670 auto grfnum = ParseInteger(argv[argnum]);
2671 if (!grfnum.has_value() || *grfnum < 1 || static_cast<size_t>(*grfnum) > files.size()) {
2672 IConsolePrint(CC_WARNING, "GRF number {} out of range, not added.", *grfnum);
2673 continue;
2674 }
2675 const GRFFile *grf = &files[*grfnum - 1];
2676 if (std::any_of(_newgrf_profilers.begin(), _newgrf_profilers.end(), [&](NewGRFProfiler &pr) { return pr.grffile == grf; })) {
2677 IConsolePrint(CC_WARNING, "GRF number {} [{}] is already selected for profiling.", *grfnum, FormatArrayAsHex(grf->grfid));
2678 continue;
2679 }
2680 _newgrf_profilers.emplace_back(grf);
2681 }
2682 return true;
2683 }
2684
2685 /* "unselect" sub-command */
2686 if (StrStartsWithIgnoreCase(argv[1], "uns") && argv.size() >= 3) {
2687 for (size_t argnum = 2; argnum < argv.size(); ++argnum) {
2688 if (StrEqualsIgnoreCase(argv[argnum], "all")) {
2689 _newgrf_profilers.clear();
2690 break;
2691 }
2692 auto grfnum = ParseInteger(argv[argnum]);
2693 if (!grfnum.has_value() || *grfnum < 1 || static_cast<size_t>(*grfnum) > files.size()) {
2694 IConsolePrint(CC_WARNING, "GRF number {} out of range, not removing.", *grfnum);
2695 continue;
2696 }
2697 const GRFFile *grf = &files[*grfnum - 1];
2698 _newgrf_profilers.erase(std::ranges::find(_newgrf_profilers, grf, &NewGRFProfiler::grffile));
2699 }
2700 return true;
2701 }
2702
2703 /* "start" sub-command */
2704 if (StrStartsWithIgnoreCase(argv[1], "sta")) {
2705 std::string grfids;
2706 size_t started = 0;
2707 for (NewGRFProfiler &pr : _newgrf_profilers) {
2708 if (!pr.active) {
2709 pr.Start();
2710 started++;
2711
2712 if (!grfids.empty()) grfids += ", ";
2713 format_append(grfids, "[{}]", FormatArrayAsHex(pr.grffile->grfid));
2714 }
2715 }
2716 if (started > 0) {
2717 IConsolePrint(CC_DEBUG, "Started profiling for GRFID{} {}.", (started > 1) ? "s" : "", grfids);
2718
2719 if (argv.size() >= 3) {
2720 auto ticks = StringConsumer{argv[2]}.TryReadIntegerBase<uint64_t>(0);
2721 if (!ticks.has_value()) {
2722 IConsolePrint(CC_ERROR, "No valid amount of ticks was given, profiling will not stop automatically.");
2723 } else {
2725 IConsolePrint(CC_DEBUG, "Profiling will automatically stop after {} ticks.", *ticks);
2726 }
2727 }
2728 } else if (_newgrf_profilers.empty()) {
2729 IConsolePrint(CC_ERROR, "No GRFs selected for profiling, did not start.");
2730 } else {
2731 IConsolePrint(CC_ERROR, "Did not start profiling for any GRFs, all selected GRFs are already profiling.");
2732 }
2733 return true;
2734 }
2735
2736 /* "stop" sub-command */
2737 if (StrStartsWithIgnoreCase(argv[1], "sto")) {
2738 NewGRFProfiler::FinishAll();
2739 return true;
2740 }
2741
2742 /* "abort" sub-command */
2743 if (StrStartsWithIgnoreCase(argv[1], "abo")) {
2744 for (NewGRFProfiler &pr : _newgrf_profilers) {
2745 pr.Abort();
2746 }
2748 return true;
2749 }
2750
2751 return false;
2752}
2753
2754#ifdef _DEBUG
2755/******************
2756 * debug commands
2757 ******************/
2758
2759static void IConsoleDebugLibRegister()
2760{
2761 IConsole::CmdRegister("resettile", ConResetTile);
2762 IConsole::AliasRegister("dbg_echo", "echo %A; echo %B");
2763 IConsole::AliasRegister("dbg_echo2", "echo %!");
2764}
2765#endif
2766
2768static bool ConFramerate(std::span<std::string_view> argv)
2769{
2770 if (argv.empty()) {
2771 IConsolePrint(CC_HELP, "Show frame rate and game speed information.");
2772 return true;
2773 }
2774
2776 return true;
2777}
2778
2780static bool ConFramerateWindow(std::span<std::string_view> argv)
2781{
2782 if (argv.empty()) {
2783 IConsolePrint(CC_HELP, "Open the frame rate window.");
2784 return true;
2785 }
2786
2787 if (_network_dedicated) {
2788 IConsolePrint(CC_ERROR, "Can not open frame rate window on a dedicated server.");
2789 return false;
2790 }
2791
2793 return true;
2794}
2795
2797static void ConDumpRoadTypes()
2798{
2799 IConsolePrint(CC_DEFAULT, " Flags:");
2800 IConsolePrint(CC_DEFAULT, " c = catenary");
2801 IConsolePrint(CC_DEFAULT, " l = no level crossings");
2802 IConsolePrint(CC_DEFAULT, " X = no houses");
2803 IConsolePrint(CC_DEFAULT, " h = hidden");
2804 IConsolePrint(CC_DEFAULT, " T = buildable by towns");
2805
2806 std::map<GrfID, const GRFFile *> grfs;
2807 for (RoadType rt : EnumRange(ROADTYPE_END)) {
2808 const RoadTypeInfo *rti = GetRoadTypeInfo(rt);
2809 if (rti->label.Empty()) continue;
2810 GrfID grfid{};
2811 const GRFFile *grf = rti->grffile[RoadSpriteType::Ground];
2812 if (grf != nullptr) {
2813 grfid = grf->grfid;
2814 grfs.emplace(grfid, grf);
2815 }
2816 IConsolePrint(CC_DEFAULT, " {:02d} {} {}, Flags: {}{}{}{}{}, GRF: {}, {}",
2817 (uint)rt,
2818 RoadTypeIsTram(rt) ? "Tram" : "Road",
2819 rti->label.AsString(),
2820 rti->flags.Test(RoadTypeFlag::Catenary) ? 'c' : '-',
2821 rti->flags.Test(RoadTypeFlag::NoLevelCrossing) ? 'l' : '-',
2822 rti->flags.Test(RoadTypeFlag::NoHouses) ? 'X' : '-',
2823 rti->flags.Test(RoadTypeFlag::Hidden) ? 'h' : '-',
2824 rti->flags.Test(RoadTypeFlag::TownBuild) ? 'T' : '-',
2825 FormatArrayAsHex(grfid),
2826 GetStringPtr(rti->strings.name)
2827 );
2828 }
2829 for (const auto &grf : grfs) {
2830 IConsolePrint(CC_DEFAULT, " GRF: {} = {}", FormatArrayAsHex(grf.first), grf.second->filename);
2831 }
2832}
2833
2835static void ConDumpRailTypes()
2836{
2837 IConsolePrint(CC_DEFAULT, " Flags:");
2838 IConsolePrint(CC_DEFAULT, " c = catenary");
2839 IConsolePrint(CC_DEFAULT, " l = no level crossings");
2840 IConsolePrint(CC_DEFAULT, " h = hidden");
2841 IConsolePrint(CC_DEFAULT, " s = no sprite combine");
2842 IConsolePrint(CC_DEFAULT, " a = always allow 90 degree turns");
2843 IConsolePrint(CC_DEFAULT, " d = always disallow 90 degree turns");
2844
2845 std::map<GrfID, const GRFFile *> grfs;
2846 for (RailType rt : EnumRange(RAILTYPE_END)) {
2847 const RailTypeInfo *rti = GetRailTypeInfo(rt);
2848 if (rti->label.Empty()) continue;
2849 GrfID grfid{};
2850 const GRFFile *grf = rti->grffile[RailSpriteType::Ground];
2851 if (grf != nullptr) {
2852 grfid = grf->grfid;
2853 grfs.emplace(grfid, grf);
2854 }
2855 IConsolePrint(CC_DEFAULT, " {:02d} {}, Flags: {}{}{}{}{}{}, GRF: {}, {}",
2856 (uint)rt,
2857 rti->label.AsString(),
2858 rti->flags.Test(RailTypeFlag::Catenary) ? 'c' : '-',
2859 rti->flags.Test(RailTypeFlag::NoLevelCrossing) ? 'l' : '-',
2860 rti->flags.Test(RailTypeFlag::Hidden) ? 'h' : '-',
2861 rti->flags.Test(RailTypeFlag::NoSpriteCombine) ? 's' : '-',
2862 rti->flags.Test(RailTypeFlag::Allow90Deg) ? 'a' : '-',
2863 rti->flags.Test(RailTypeFlag::Disallow90Deg) ? 'd' : '-',
2864 FormatArrayAsHex(grfid),
2865 GetStringPtr(rti->strings.name)
2866 );
2867 }
2868 for (const auto &grf : grfs) {
2869 IConsolePrint(CC_DEFAULT, " GRF: {} = {}", FormatArrayAsHex(grf.first), grf.second->filename);
2870 }
2871}
2872
2875{
2876 IConsolePrint(CC_DEFAULT, " Cargo classes:");
2877 IConsolePrint(CC_DEFAULT, " p = passenger");
2878 IConsolePrint(CC_DEFAULT, " m = mail");
2879 IConsolePrint(CC_DEFAULT, " x = express");
2880 IConsolePrint(CC_DEFAULT, " a = armoured");
2881 IConsolePrint(CC_DEFAULT, " b = bulk");
2882 IConsolePrint(CC_DEFAULT, " g = piece goods");
2883 IConsolePrint(CC_DEFAULT, " l = liquid");
2884 IConsolePrint(CC_DEFAULT, " r = refrigerated");
2885 IConsolePrint(CC_DEFAULT, " h = hazardous");
2886 IConsolePrint(CC_DEFAULT, " c = covered/sheltered");
2887 IConsolePrint(CC_DEFAULT, " o = oversized");
2888 IConsolePrint(CC_DEFAULT, " d = powderized");
2889 IConsolePrint(CC_DEFAULT, " n = not pourable");
2890 IConsolePrint(CC_DEFAULT, " e = potable");
2891 IConsolePrint(CC_DEFAULT, " i = non-potable");
2892 IConsolePrint(CC_DEFAULT, " S = special");
2893
2894 std::map<GrfID, const GRFFile *> grfs;
2895 for (const CargoSpec *spec : CargoSpec::Iterate()) {
2896 GrfID grfid{};
2897 const GRFFile *grf = spec->grffile;
2898 if (grf != nullptr) {
2899 grfid = grf->grfid;
2900 grfs.emplace(grfid, grf);
2901 }
2902 IConsolePrint(CC_DEFAULT, " {:02d} Bit: {:2d}, Label: {}, Callback mask: 0x{:02X}, Cargo class: {}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}, GRF: {}, {}",
2903 spec->Index(),
2904 spec->bitnum,
2905 spec->label.AsString(),
2906 spec->callback_mask.base(),
2907 spec->classes.Test(CargoClass::Passengers) ? 'p' : '-',
2908 spec->classes.Test(CargoClass::Mail) ? 'm' : '-',
2909 spec->classes.Test(CargoClass::Express) ? 'x' : '-',
2910 spec->classes.Test(CargoClass::Armoured) ? 'a' : '-',
2911 spec->classes.Test(CargoClass::Bulk) ? 'b' : '-',
2912 spec->classes.Test(CargoClass::PieceGoods) ? 'g' : '-',
2913 spec->classes.Test(CargoClass::Liquid) ? 'l' : '-',
2914 spec->classes.Test(CargoClass::Refrigerated) ? 'r' : '-',
2915 spec->classes.Test(CargoClass::Hazardous) ? 'h' : '-',
2916 spec->classes.Test(CargoClass::Covered) ? 'c' : '-',
2917 spec->classes.Test(CargoClass::Oversized) ? 'o' : '-',
2918 spec->classes.Test(CargoClass::Powderized) ? 'd' : '-',
2919 spec->classes.Test(CargoClass::NotPourable) ? 'n' : '-',
2920 spec->classes.Test(CargoClass::Potable) ? 'e' : '-',
2921 spec->classes.Test(CargoClass::NonPotable) ? 'i' : '-',
2922 spec->classes.Test(CargoClass::Special) ? 'S' : '-',
2923 FormatArrayAsHex(grfid),
2924 GetStringPtr(spec->name)
2925 );
2926 }
2927 for (const auto &grf : grfs) {
2928 IConsolePrint(CC_DEFAULT, " GRF: {} = {}", FormatArrayAsHex(grf.first), grf.second->filename);
2929 }
2930}
2931
2933static bool ConDumpInfo(std::span<std::string_view> argv)
2934{
2935 if (argv.size() != 2) {
2936 IConsolePrint(CC_HELP, "Dump debugging information.");
2937 IConsolePrint(CC_HELP, "Usage: 'dump_info roadtypes|railtypes|cargotypes'.");
2938 IConsolePrint(CC_HELP, " Show information about road/tram types, rail types or cargo types.");
2939 return true;
2940 }
2941
2942 if (StrEqualsIgnoreCase(argv[1], "roadtypes")) {
2944 return true;
2945 }
2946
2947 if (StrEqualsIgnoreCase(argv[1], "railtypes")) {
2949 return true;
2950 }
2951
2952 if (StrEqualsIgnoreCase(argv[1], "cargotypes")) {
2954 return true;
2955 }
2956
2957 return false;
2958}
2959
2962{
2963 IConsole::CmdRegister("debug_level", ConDebugLevel);
2971 IConsole::CmdRegister("info_cmd", ConInfoCmd);
2973 IConsole::CmdRegister("list_aliases", ConListAliases);
2979 IConsole::CmdRegister("getsysdate", ConGetSysDate);
2984 IConsole::CmdRegister("screenshot", ConScreenShot);
2990 IConsole::CmdRegister("load_save", ConLoad);
2991 IConsole::CmdRegister("load_scenario", ConLoadScenario);
2992 IConsole::CmdRegister("load_heightmap", ConLoadHeightmap);
2995 IConsole::CmdRegister("saveconfig", ConSaveConfig);
2997 IConsole::CmdRegister("list_saves", ConListFiles);
2998 IConsole::CmdRegister("list_scenarios", ConListScenarios);
2999 IConsole::CmdRegister("list_heightmaps", ConListHeightmaps);
3005 IConsole::CmdRegister("setting_newgame", ConSettingNewgame);
3006 IConsole::CmdRegister("list_settings", ConListSettings);
3008 IConsole::CmdRegister("rescan_newgrf", ConRescanNewGRF);
3009 IConsole::CmdRegister("list_dirs", ConListDirs);
3010
3011 IConsole::AliasRegister("dir", "ls");
3012 IConsole::AliasRegister("del", "rm %+");
3013 IConsole::AliasRegister("newmap", "newgame");
3014 IConsole::AliasRegister("patch", "setting %+");
3015 IConsole::AliasRegister("set", "setting %+");
3016 IConsole::AliasRegister("set_newgame", "setting_newgame %+");
3017 IConsole::AliasRegister("list_patches", "list_settings %+");
3018 IConsole::AliasRegister("developer", "setting developer %+");
3019
3020 IConsole::CmdRegister("list_ai_libs", ConListAILibs);
3021 IConsole::CmdRegister("list_ai", ConListAI);
3022 IConsole::CmdRegister("reload_ai", ConReloadAI);
3023 IConsole::CmdRegister("rescan_ai", ConRescanAI);
3024 IConsole::CmdRegister("start_ai", ConStartAI);
3025 IConsole::CmdRegister("stop_ai", ConStopAI);
3026
3027 IConsole::CmdRegister("list_game", ConListGame);
3028 IConsole::CmdRegister("list_game_libs", ConListGameLibs);
3029 IConsole::CmdRegister("rescan_game", ConRescanGame);
3030
3031 IConsole::CmdRegister("companies", ConCompanies);
3032 IConsole::AliasRegister("players", "companies");
3033
3034 /* networking functions */
3035
3036/* Content downloading is only available with ZLIB */
3037#if defined(WITH_ZLIB)
3039#endif /* defined(WITH_ZLIB) */
3040
3041 /*** Networking commands ***/
3044 IConsole::AliasRegister("say_player", "say_company %+");
3046
3051 IConsole::AliasRegister("info", "server_info");
3054
3056 IConsole::AliasRegister("spectate", "join 255");
3059 IConsole::AliasRegister("clean_company", "reset_company %A");
3065
3068
3070 IConsole::AliasRegister("ak", "authorized_key %+");
3071
3072 IConsole::AliasRegister("net_frame_freq", "setting frame_freq %+");
3073 IConsole::AliasRegister("net_sync_freq", "setting sync_freq %+");
3074 IConsole::AliasRegister("server_pw", "setting server_password %+");
3075 IConsole::AliasRegister("server_password", "setting server_password %+");
3076 IConsole::AliasRegister("rcon_pw", "setting rcon_password %+");
3077 IConsole::AliasRegister("rcon_password", "setting rcon_password %+");
3078 IConsole::AliasRegister("name", "setting client_name %+");
3079 IConsole::AliasRegister("server_name", "setting server_name %+");
3080 IConsole::AliasRegister("server_port", "setting server_port %+");
3081 IConsole::AliasRegister("max_clients", "setting max_clients %+");
3082 IConsole::AliasRegister("max_companies", "setting max_companies %+");
3083 IConsole::AliasRegister("max_join_time", "setting max_join_time %+");
3084 IConsole::AliasRegister("pause_on_join", "setting pause_on_join %+");
3085 IConsole::AliasRegister("autoclean_companies", "setting autoclean_companies %+");
3086 IConsole::AliasRegister("autoclean_protected", "setting autoclean_protected %+");
3087 IConsole::AliasRegister("restart_game_year", "setting restart_game_year %+");
3088 IConsole::AliasRegister("min_players", "setting min_active_clients %+");
3089 IConsole::AliasRegister("reload_cfg", "setting reload_cfg %+");
3090
3091 /* debugging stuff */
3092#ifdef _DEBUG
3093 IConsoleDebugLibRegister();
3094#endif
3097
3098 /* NewGRF development stuff */
3101
3102 IConsole::CmdRegister("dump_info", ConDumpInfo);
3103}
Base functions for all AIs.
AIConfig stores the configuration settings of every AI.
@ NotPourable
Not Pourable (open wagon, but not hopper wagon).
Definition cargotype.h:63
@ Powderized
Powderized, moist protected (powder/silo wagon).
Definition cargotype.h:62
@ Hazardous
Hazardous cargo (Nuclear Fuel, Explosives, etc.).
Definition cargotype.h:59
@ NonPotable
Non-potable / non-food / dirty.
Definition cargotype.h:65
@ Bulk
Bulk cargo (Coal, Grain etc., Ores, Fruit).
Definition cargotype.h:55
@ Mail
Mail.
Definition cargotype.h:52
@ Liquid
Liquids (Oil, Water, Rubber).
Definition cargotype.h:57
@ Passengers
Passengers.
Definition cargotype.h:51
@ Potable
Potable / food / clean.
Definition cargotype.h:64
@ Oversized
Oversized (stake/flatbed wagon).
Definition cargotype.h:61
@ Armoured
Armoured cargo (Valuables, Gold, Diamonds).
Definition cargotype.h:54
@ Refrigerated
Refrigerated cargo (Food, Fruit).
Definition cargotype.h:58
@ Express
Express cargo (Goods, Food, Candy, but also possible for passengers).
Definition cargotype.h:53
@ Special
Special bit used for livery refit tricks instead of normal cargoes.
Definition cargotype.h:66
@ PieceGoods
Piece goods (Livestock, Wood, Steel, Paper).
Definition cargotype.h:56
@ Covered
Covered/Sheltered Freight (Transportation in Box Vans, Silo Wagons, etc.).
Definition cargotype.h:60
AI instantion of script configuration.
Definition ai_config.hpp:17
static AIConfig * GetConfig(CompanyID company, ScriptSettingSource source=ScriptSettingSource::Default)
Get the AI configuration of specific company.
Definition ai_config.cpp:20
static void GetConsoleLibraryList(std::back_insert_iterator< std::string > &output_iterator, bool newest_only)
Get the list of registered scripts to print on the console.
Definition ai_core.cpp:287
static void GetConsoleList(std::back_insert_iterator< std::string > &output_iterator, bool newest_only)
Get the list of registered scripts to print on the console.
Definition ai_core.cpp:282
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:312
constexpr bool Test(Tvalue_type value) const
Test if the value-th bit is set.
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.
Iterate a range of enum values.
static std::optional< FileHandle > Open(const std::string &filename, std::string_view mode)
Open an RAII file handle if possible.
Definition fileio.cpp:1219
List of file information.
Definition fios.h:86
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:57
Font cache for basic fonts.
Definition fontcache.h:23
virtual std::string GetFontName()=0
Get the name of this font.
bool HasParent()
Check whether the font cache has a parent.
Definition fontcache.h:148
virtual int GetFontSize() const
Get the nominal font size of the font.
Definition fontcache.h:83
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:136
static void Rescan()
Rescans all searchpaths for available Game scripts.
static void GetConsoleLibraryList(std::back_insert_iterator< std::string > &output_iterator, bool newest_only)
Get the list of registered scripts to print on the console.
static void GetConsoleList(std::back_insert_iterator< std::string > &output_iterator, bool newest_only)
Get the list of registered scripts to print on the console.
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.
This struct contains all the info that is needed to draw and construct tracks.
Definition rail.h:117
struct RailTypeInfo::@157247141350136173143103254227157213063052244122 strings
Strings associated with the rail type.
EnumIndexArray< const GRFFile *, RailSpriteType, RailSpriteType::End > grffile
NewGRF providing the Action3 for the railtype.
Definition rail.h:267
RailTypeLabel label
Unique 32 bit rail type identifier.
Definition rail.h:227
StringID name
Name of this rail type.
Definition rail.h:167
RailTypeFlags flags
Bit mask of rail type flags.
Definition rail.h:202
RoadTypeLabel label
Unique 32 bit road type identifier.
Definition road.h:123
RoadTypeFlags flags
Bit mask of road type flags.
Definition road.h:103
struct RoadTypeInfo::@070000167274302256150317022075324310363002361255 strings
Strings associated with the rail type.
StringID name
Name of this rail type.
Definition road.h:79
EnumIndexArray< const GRFFile *, RoadSpriteType, RoadSpriteType::End > grffile
NewGRF providing the Action3 for the roadtype.
Definition road.h:163
bool HasScript() const
Is this config attached to an Script?
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.
bool AnyBytesLeft() const noexcept
Check whether any bytes left to read.
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.
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.
@ NewAI
Create a new AI company.
@ Delete
Delete a company.
static constexpr CompanyID COMPANY_SPECTATOR
The client is spectating.
@ RemoveKey
Remove a public key.
@ AddKey
Create a public key.
static constexpr CompanyID COMPANY_NEW_COMPANY
The client wants a new company.
@ None
Dummy reason for actions that don't need one.
@ 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:99
void IConsolePrint(ExtendedTextColour 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
void IConsoleCmdExec(std::string_view command_string, const uint recurse_count)
Execute a given command passed to us.
Definition console.cpp:271
static bool ConEcho(std::span< std::string_view > argv)
Simply print the arguments.
static bool ConRescanAI(std::span< std::string_view > argv)
Rescan the folder structure for new/changed AIs and libraries.
static void OutputContentState(const ContentInfo &ci)
Outputs content state information to console.
static bool ConListScenarios(std::span< std::string_view > argv)
List all the scenarios.
static bool ConReturn(std::span< std::string_view > argv)
End the execution of the current script.
static const std::initializer_list< std::pair< std::string_view, NetworkAuthorizedKeys * > > _console_cmd_authorized_keys
All the known authorized keys with their name.
ConNetworkAuthorizedKeyAction
Actions that can be performed on authorized keys from the console.
@ Remove
Remove an authorized key.
@ List
List all authorized keys.
@ Add
Add an authorized key.
static FontSize GetFontSizeByName(std::string_view name)
Get FontSize by 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 ConReload(std::span< std::string_view > argv)
Reload a game from the loaded savegame/scenario/heightmap.
static bool ConFont(std::span< std::string_view > argv)
Managing the font configuration.
static bool ConStatus(std::span< std::string_view > argv)
Get the status of connected clients.
static bool ConScreenShot(std::span< std::string_view > argv)
Make a screenshot.
static bool ConMoveClient(std::span< std::string_view > argv)
Move a client to a specific company.
static bool ConNetworkClients(std::span< std::string_view > argv)
List the clients.
bool PrintList(F list_function, Args... args)
Helper to print a list to the console.
static bool ConHelp(std::span< std::string_view > argv)
Show generic help and specific help for commands.
static bool ConSaveConfig(std::span< std::string_view > argv)
Explicitly save the configuration.
static std::optional< CompanyID > ParseCompanyID(std::string_view arg)
Helper to parse a company ID.
static bool ConBanList(std::span< std::string_view > argv)
Show the list of banned clients.
void ShowFramerateWindow()
Open the general framerate window.
static bool ConNewGRFReload(std::span< std::string_view > argv)
Reload all active NewGRFs.
static bool ConSayCompany(std::span< std::string_view > argv)
Say something to all clients in your company in a network game.
static bool ConResetEngines(std::span< std::string_view > argv)
Reset status of all engines.
static void ConDumpRailTypes()
List all rail types and their configuration.
static bool ConLoad(std::span< std::string_view > argv)
Load a savegame.
static bool ConEchoC(std::span< std::string_view > argv)
Print the arguments in a particular colour.
static bool ConAlias(std::span< std::string_view > argv)
Create an alias for a command.
static bool ConJoinCompany(std::span< std::string_view > argv)
As client, join a company.
static bool ConPrintWorkingDirectory(std::span< std::string_view > argv)
Print the current working directory.
static bool ConListAI(std::span< std::string_view > argv)
List all AI scripts.
static bool ConClearBuffer(std::span< std::string_view > argv)
Clear the console's buffer.
static std::optional< T > ParseType(std::string_view arg)
Parse an integer using ParseInteger and convert it to the requested type.
static bool ConListCommands(std::span< std::string_view > argv)
List all registered commands that are not hidden.
static bool ConResetCompany(std::span< std::string_view > argv)
Remove a company from the game.
static bool ConPauseGame(std::span< std::string_view > argv)
Manually pause the game.
static bool ConRestart(std::span< std::string_view > argv)
Restart the game.
static bool ConCompanies(std::span< std::string_view > argv)
List all companies.
static bool ConNewGRFProfile(std::span< std::string_view > argv)
Management of NewGRF profiling.
static bool ConListDirs(std::span< std::string_view > argv)
List the locations of all of the game's different sub directories.
static bool ConKick(std::span< std::string_view > argv)
Kick a user from a network game.
static bool ConResetEnginePool(std::span< std::string_view > argv)
Reset status of the engine pool.
static bool ConClientNickChange(std::span< std::string_view > argv)
Change the name of a client.
static bool ConNetworkAuthorizedKey(std::span< std::string_view > argv)
Management of authorized keys.
static bool ConUnpauseGame(std::span< std::string_view > argv)
Manually unpause the game.
static ConsoleHookResult ConHookServerOrNoNetwork(bool echo)
Check if are either in singleplayer or a server.
static bool ConListGameLibs(std::span< std::string_view > argv)
List all game script libraries.
static bool ConDumpInfo(std::span< std::string_view > argv)
Dump information about some NewGRF types.
static bool ConFramerate(std::span< std::string_view > argv)
Show the current framerate statistics.
static bool ConListGame(std::span< std::string_view > argv)
List all game scripts.
static ConsoleFileList _console_file_list_scenario
File storage cache for scenarios.
static ConsoleFileList _console_file_list_heightmap
File storage cache for heightmaps.
static bool ConDebugLevel(std::span< std::string_view > argv)
Change the debug levels of the game.
static ConsoleHookResult ConHookServerOnly(bool echo)
Check whether we are a server.
static bool ConListSettings(std::span< std::string_view > argv)
List all settings.
static const IntervalTimer< TimerGameCalendar > _scheduled_monthly_timer
Timer that runs every month of game time for the 'schedule' console command.
static bool ConLoadScenario(std::span< std::string_view > argv)
Load a scenario.
static bool ConSayClient(std::span< std::string_view > argv)
Say something to a specific client in a network game.
static bool ConGetDate(std::span< std::string_view > argv)
Get the current game date.
static bool ConLoadHeightmap(std::span< std::string_view > argv)
Load a heightmap.
static bool ConGamelogPrint(std::span< std::string_view > argv)
Print the gamelog.
static bool ConInfoCmd(std::span< std::string_view > argv)
Get debug information about a command.
static bool ConListAliases(std::span< std::string_view > argv)
List all registered aliases.
static bool ConPart(std::span< std::string_view > argv)
Part the game, i.e.
static bool ConSave(std::span< std::string_view > argv)
Save the map to a file.
static bool ConRemove(std::span< std::string_view > argv)
Remove a savegame file from disk.
static bool ConNetworkReconnect(std::span< std::string_view > argv)
Connect to the last client you were connected to.
static bool ConBan(std::span< std::string_view > argv)
Ban a user from a network game.
void ConPrintFramerate()
Print performance statistics to game console.
void IConsoleStdLibRegister()
Console command registration.
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 ConListHeightmaps(std::span< std::string_view > argv)
List all the heightmaps.
static bool ConStartAI(std::span< std::string_view > argv)
Start a new AI.
static bool ConStopAI(std::span< std::string_view > argv)
Stop a currently running AI.
static ConsoleHookResult ConHookNewGRFDeveloperTool(bool echo)
Check whether NewGRF developer tools are enabled.
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 bool ConRescanNewGRF(std::span< std::string_view > argv)
Rescan the folder structure for new/changed NewGRFs.
static void ConDumpRoadTypes()
List all road types and their configuration.
static bool ConExec(std::span< std::string_view > argv)
Run a local script file.
static bool ConServerInfo(std::span< std::string_view > argv)
Get information like client/company count/limits for the server.
static bool ConSchedule(std::span< std::string_view > argv)
Schedule the execution of a script.
static void ConDumpCargoTypes()
List all cargo types and their configuration.
static bool ConScript(std::span< std::string_view > argv)
Enable or disable logging of console output.
static bool ConGetSysDate(std::span< std::string_view > argv)
Get the current system date.
static bool ConSay(std::span< std::string_view > argv)
Say something to all clients in a network game.
static uint _script_current_depth
Depth of scripts running (used to abort execution when ConReturn is encountered).
static bool ConExit(std::span< std::string_view > argv)
Exit the game, i.e.
static bool ConChangeDirectory(std::span< std::string_view > argv)
Change the dir via console.
static bool ConListAILibs(std::span< std::string_view > argv)
List all AI libraries.
static bool ConKickOrBan(std::string_view arg, bool ban, std::string_view reason)
Helper to kick or ban a user.
static bool ConSettingNewgame(std::span< std::string_view > argv)
Change settings of for a new game.
static bool NetworkAvailable(bool echo)
Check network availability and inform in console about failure of detection.
static bool ConRescanGame(std::span< std::string_view > argv)
Rescan the folder structure for new/changed game scripts and libraries.
static ConsoleHookResult ConHookNeedNonDedicatedNetwork(bool echo)
Check whether we are in a multiplayer game and are playing, i.e.
static bool ConRcon(std::span< std::string_view > argv)
Run a console command on the server.
static bool ConListFiles(std::span< std::string_view > argv)
List all the files in the current dir via console.
static bool ConGetSeed(std::span< std::string_view > argv)
Get the seed that was used to create this game.
static bool ConContent(std::span< std::string_view > argv)
Downloading of content from the server.
static ConsoleFileList _console_file_list_savegame
File storage cache for savegames.
static bool ConReloadAI(std::span< std::string_view > argv)
Reload/restart an AI.
static bool ConNetworkConnect(std::span< std::string_view > argv)
Connect to a specific server.
static bool ConFramerateWindow(std::span< std::string_view > argv)
Show the framerate statistics window.
static bool ConSetting(std::span< std::string_view > argv)
Change settings of the current game.
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.
static bool ConNewGame(std::span< std::string_view > argv)
Start/create a new game.
static bool ConUnBan(std::span< std::string_view > argv)
Unban a user from a network game.
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).
@ Disallow
Disallow command execution.
@ Allow
Allow command execution.
@ Hide
Hide the existence of the command.
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:145
std::string GetDebugString()
Print out the current debug-level.
Definition debug.cpp:202
Functions related to debugging.
void StartupEngines()
Start/initialise all our engines.
Definition engine.cpp:835
Base class for engines.
Functions related to engines.
#define T
Climate temperate.
Definition engines.h:91
constexpr std::underlying_type_t< enum_type > to_underlying(enum_type e)
Implementation of std::to_underlying (from C++23).
Definition enum_type.hpp:21
EnumClassIndexContainer< std::array< T, to_underlying(N)>, Index > EnumIndexArray
A typedef for EnumClassIndexContainer using std::array as the backing container type.
bool FioRemove(const std::string &filename)
Remove a file.
Definition fileio.cpp:334
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:249
bool FileExists(std::string_view filename)
Test whether the given filename exists.
Definition fileio.cpp:134
bool FioCheckFileExists(std::string_view filename, Subdirectory subdir)
Check whether the given file exists.
Definition fileio.cpp:123
Functions for standard in/out file operations.
@ Save
File is being saved.
Definition fileio_type.h:55
@ Load
File is being loaded.
Definition fileio_type.h:54
@ FiosDrive
A drive (letter) entry.
Definition fileio_type.h:41
@ GameFile
Save game or scenario file.
Definition fileio_type.h:31
@ FiosDirectory
A directory entry.
Definition fileio_type.h:43
@ FiosParent
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
@ Base
Base directory for all subdirectories.
Definition fileio_type.h:89
@ Autosave
Subdirectory of save for autosaves.
Definition fileio_type.h:91
@ NewGrf
Subdirectory for all NewGRFs.
Definition fileio_type.h:97
@ Screenshot
Subdirectory for all screenshots.
@ GsLibrary
Subdirectory for all GS libraries.
@ Scenario
Base directory for all scenarios.
Definition fileio_type.h:92
@ Heightmap
Subdirectory of scenario for heightmaps.
Definition fileio_type.h:93
@ Ai
Subdirectory for all AI files.
Definition fileio_type.h:99
@ SocialIntegration
Subdirectory for all social integration plugins.
@ Gs
Subdirectory for all game scripts.
@ Baseset
Subdirectory for all base data (base sets, intro game).
Definition fileio_type.h:96
@ Save
Base directory for all savegames.
Definition fileio_type.h:90
@ AiLibrary
Subdirectory for all AI libraries.
AbstractFileType
The different abstract types of files that the system knows about.
Definition fileio_type.h:17
@ Savegame
old or new savegame
Definition fileio_type.h:19
@ Invalid
Invalid or unknown file type.
Definition fileio_type.h:24
@ Scenario
old or new scenario
Definition fileio_type.h:20
@ Heightmap
heightmap file
Definition fileio_type.h:21
@ None
nothing to do
Definition fileio_type.h:18
std::string FiosGetCurrentPath()
Get the current path/working directory.
Definition fios.cpp:123
bool FiosBrowseTo(const FiosItem *item)
Browse to a new path based on the passed item, starting at _fios_path.
Definition fios.cpp:133
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:214
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
@ End
Marker for the end of the enumerations.
Definition gfx_type.h:254
TextColour
Colour of the strings, see _string_colourmap in table/string_colours.h or docs/ottd-colourtext-palett...
Definition gfx_type.h:315
@ Begin
Marker for the begin of the range.
Definition gfx_type.h:316
@ LightBlue
Light blue colour.
Definition gfx_type.h:331
@ End
Marker for the end of the range.
Definition gfx_type.h:335
@ Green
Green colour.
Definition gfx_type.h:325
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 TileIndex TileXY(uint x, uint y)
Returns the TileIndex of a coordinate.
Definition map_func.h:376
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
uint8_t NetworkSpectatorCount()
Get the number of clients that are playing as a spectator.
Definition network.cpp:220
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:809
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 NetworkClientSendChat(NetworkAction action, NetworkChatDestinationType type, int dest, std::string_view msg, int64_t data)
Send a chat message.
void NetworkClientSendRcon(std::string_view password, std::string_view command)
Send a remote console command.
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 NetworkServerSendChat(NetworkAction action, NetworkChatDestinationType type, int dest, std::string_view msg, ClientID from_id, int64_t data=0, bool from_admin=false)
Send an actual chat message.
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 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.
NetworkServerGameInfo _network_game_info
Information about our game.
Convert NetworkGameInfo to Packet and back.
@ ChatTeam
A chat sent to all clients of a team/company.
@ ChatClient
A chat sent to a specific client.
@ ChatBroadcast
A chat broadcast to all clients.
@ Client
Send message/notice to only a certain client (Private).
@ Team
Send message/notice to everyone playing the same company (Team).
@ Broadcast
Send message/notice to all clients (All).
ClientID
'Unique' identifier to be given to clients
@ Invalid
Client is not part of anything.
@ 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.
Label< struct GrfIDTag > GrfID
The unique identifier of a NewGRF.
Definition newgrf_type.h:17
bool RequestNewGRFScan(NewGRFScanCallback *callback)
Request a new NewGRF scan.
Definition openttd.cpp:1323
@ Error
A game paused because a (critical) error.
Definition openttd.h:75
@ Normal
A game normally paused.
Definition openttd.h:72
@ Normal
Playing a game.
Definition openttd.h:20
@ Menu
In the main menu.
Definition openttd.h:19
@ LoadGame
Load game, Play Scenario.
Definition openttd.h:32
@ StartHeightmap
Load a heightmap and start a new game from it.
Definition openttd.h:38
@ ReloadGame
Reload the savegame / scenario / heightmap you started the game with.
Definition openttd.h:30
@ Menu
Switch to game intro menu.
Definition openttd.h:33
@ RestartGame
Restart --> 'Random game' with current settings.
Definition openttd.h:29
Rail specific functions.
@ Ground
Main group of ground images.
Definition rail.h:45
const RailTypeInfo * GetRailTypeInfo(RailType railtype)
Returns a pointer to the Railtype information for a given railtype.
Definition rail.h:303
@ Catenary
Bit number for drawing a catenary.
Definition rail.h:28
@ Allow90Deg
Bit number for always allowed 90 degree turns, regardless of setting.
Definition rail.h:32
@ Disallow90Deg
Bit number for never allowed 90 degree turns, regardless of setting.
Definition rail.h:33
@ Hidden
Bit number for hiding from selection.
Definition rail.h:30
@ NoSpriteCombine
Bit number for using non-combined junctions.
Definition rail.h:31
@ NoLevelCrossing
Bit number for disallowing level crossings.
Definition rail.h:29
RailType
Enumeration for all possible railtypes.
Definition rail_type.h:26
@ RAILTYPE_END
Used for iterations.
Definition rail_type.h:32
Road specific functions.
@ Catenary
Bit number for adding catenary.
Definition road.h:25
@ NoHouses
Bit number for setting this roadtype as not house friendly.
Definition road.h:27
@ Hidden
Bit number for hidden from construction.
Definition road.h:28
@ NoLevelCrossing
Bit number for disabling level crossing.
Definition road.h:26
@ TownBuild
Bit number for allowing towns to build this roadtype.
Definition road.h:29
const RoadTypeInfo * GetRoadTypeInfo(RoadType roadtype)
Returns a pointer to the Roadtype information for a given roadtype.
Definition road.h:217
@ Ground
Required: Main group of ground images.
Definition road.h:41
RoadType
The different roadtypes we support.
Definition road_type.h:24
@ ROADTYPE_END
Used for iterations.
Definition road_type.h:28
A number of safeguards to prevent using unsafe methods.
SaveLoadResult 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 related to saving and loading games.
@ Ok
completed successfully
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.
std::string FormatArrayAsHex(std::span< const uint8_t > data)
Format a byte array into a continuous hex string.
Definition string.cpp:77
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:325
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:259
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:338
Parse strings.
static std::optional< T > ParseInteger(std::string_view arg, int base=10, bool clamp=false)
Change a string into its number representation.
Functions related to low-level strings.
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.
Class to backup a specific variable and restore it upon destruction of this object to prevent stack v...
constexpr bool Empty() const
Check whether the label is empty.
std::string AsString() const
Get the label as a std::string.
Definition label.cpp:22
Specification of a cargo type.
Definition cargotype.h:77
static IterateWrapper Iterate(size_t from=0)
Returns an iterable ensemble of all valid CargoSpec.
Definition cargotype.h:194
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:619
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:190
std::string font
The name of the font, or path to the font.
Definition fontcache.h:191
uint size
The (requested) size of the font.
Definition fontcache.h:192
Dynamic data of a loaded NewGRF.
Definition newgrf.h:124
–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
–Commands– Commands are commands, or functions.
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:160
static IConsoleAlias * AliasGet(const std::string &name)
Find the alias pointed to by its string.
Definition console.cpp:171
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:138
static IConsoleCmd * CmdGet(const std::string &name)
Find the command pointed to by its string.
Definition console.cpp:148
static uint SizeX()
Get the size of the map along the X.
Definition map_func.h:262
static uint SizeY()
Get the size of the map along the Y.
Definition map_func.h:271
static uint LogX()
Logarithm of the map size along the X side.
Definition map_func.h:243
static uint LogY()
Logarithm of the map size along the y side.
Definition map_func.h:253
static uint Size()
Get the size of the map.
Definition map_func.h:280
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).
Callback profiler for NewGRF development.
static void StartTimer(uint64_t ticks)
Start the timeout timer that will finish all profiling sessions.
const GRFFile * grffile
Which GRF is being profiled.
static void AbortTimer()
Abort the timeout timer, so the timer callback is never called.
static Pool::IterateWrapper< Company > Iterate(size_t from=0)
static Company * GetIfValid(auto index)
static constexpr size_t MAX_SIZE
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 received over the network from the content servers.
@ Scenario
The content consists of a scenario.
@ Heightmap
The content consists of a heightmap.
@ BaseGraphics
The content consists of base graphics.
@ Ai
The content consists of an AI.
@ End
Helper to mark the end of the types.
@ NewGRF
The content consists of a NewGRF.
@ AiLibrary
The content consists of an AI library.
bool IsValidTile(Tile tile)
Checks if a tile is valid.
Definition tile_map.h:161
StrongType::Typedef< uint32_t, struct TileIndexTag, StrongType::Compare, StrongType::Integer, StrongType::Compatible< int32_t >, StrongType::Compatible< int64_t > > TileIndex
The index/ID of a Tile.
Definition tile_type.h:92
Definition of Interval and OneShot timers.
@ Ship
Ship vehicle type.
@ Aircraft
Aircraft vehicle type.
@ Road
Road vehicle type.
@ 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:1187
void SetWindowDirty(WindowClass cls, WindowNumber number)
Mark window as dirty (in need of repainting).
Definition window.cpp:3193
Window functions not directly related to making/drawing windows.
ZoomLevel
All zoom levels we know.
Definition zoom_type.h:20
@ Begin
Begin for iteration.
Definition zoom_type.h:22
@ Max
Maximum zoom level.
Definition zoom_type.h:30
@ Min
Minimum zoom level.
Definition zoom_type.h:23
@ End
End for iteration.
Definition zoom_type.h:31