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