OpenTTD Source 20260206-master-g4d4e37dbf1
command.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"
11#include "landscape.h"
12#include "error.h"
13#include "gui.h"
14#include "command_func.h"
16#include "network/network.h"
17#include "genworld.h"
18#include "strings_func.h"
19#include "texteff.hpp"
20#include "town.h"
22#include "company_func.h"
23#include "company_base.h"
24#include "signal_func.h"
25#include "core/backup_type.hpp"
26#include "object_base.h"
27#include "autoreplace_cmd.h"
28#include "company_cmd.h"
29#include "depot_cmd.h"
30#include "economy_cmd.h"
31#include "engine_cmd.h"
32#include "goal_cmd.h"
33#include "group_cmd.h"
34#include "industry_cmd.h"
35#include "league_cmd.h"
36#include "landscape_cmd.h"
37#include "misc_cmd.h"
38#include "news_cmd.h"
39#include "object_cmd.h"
40#include "order_cmd.h"
41#include "rail_cmd.h"
42#include "road_cmd.h"
43#include "roadveh_cmd.h"
44#include "settings_cmd.h"
45#include "signs_cmd.h"
46#include "station_cmd.h"
47#include "story_cmd.h"
48#include "subsidy_cmd.h"
49#include "terraform_cmd.h"
50#include "timetable_cmd.h"
51#include "town_cmd.h"
52#include "train_cmd.h"
53#include "tree_cmd.h"
54#include "tunnelbridge_cmd.h"
55#include "vehicle_cmd.h"
56#include "viewport_cmd.h"
57#include "water_cmd.h"
58#include "waypoint_cmd.h"
60#include "string_func.h"
61
62#include "table/strings.h"
63
64#include "safeguards.h"
65
66
67int RecursiveCommandCounter::_counter = 0;
68
69
77 std::string_view name;
78 CommandFlags flags;
80};
81/* Helpers to generate the master command table from the command traits. */
82template <typename T>
83inline constexpr CommandInfo CommandFromTrait() noexcept { return { T::name, T::flags, T::type }; };
84
85template <typename T, T... i>
86inline constexpr auto MakeCommandsFromTraits(std::integer_sequence<T, i...>) noexcept {
87 return EnumClassIndexContainer<std::array<CommandInfo, sizeof...(i)>, Commands>{{{ CommandFromTrait<CommandTraits<static_cast<Commands>(i)>>()... }}};
88}
89
95static constexpr auto _command_table = MakeCommandsFromTraits(std::make_integer_sequence<std::underlying_type_t<Commands>, to_underlying(Commands::End)>{});
96
97
104{
105 return to_underlying(cmd) < _command_table.size();
106}
107
113CommandFlags GetCommandFlags(Commands cmd)
114{
115 assert(IsValidCommand(cmd));
116
117 return _command_table[cmd].flags;
118}
119
125std::string_view GetCommandName(Commands cmd)
126{
127 assert(IsValidCommand(cmd));
128
129 return _command_table[cmd].name;
130}
131
138{
139 /* Lookup table for the command types that are allowed for a given pause level setting. */
140 static constexpr CommandPauseLevel command_type_lookup[] = {
141 CommandPauseLevel::AllActions, // CommandType::LandscapeConstruction
142 CommandPauseLevel::NoLandscaping, // CommandType::VehicleConstruction
143 CommandPauseLevel::NoLandscaping, // CommandType::MoneyManagement
144 CommandPauseLevel::NoConstruction, // CommandType::VehicleManagement
145 CommandPauseLevel::NoConstruction, // CommandType::RouteManagement
146 CommandPauseLevel::NoConstruction, // CommandType::OtherManagement
147 CommandPauseLevel::NoActions, // CommandType::CompanySetting
148 CommandPauseLevel::NoActions, // CommandType::ServerSetting
149 CommandPauseLevel::NoActions, // CommandType::Cheat
150 };
151 static_assert(std::size(command_type_lookup) == to_underlying(CommandType::End));
152
153 assert(IsValidCommand(cmd));
154 return _game_mode == GM_EDITOR || command_type_lookup[to_underlying(_command_table[cmd].type)] <= _settings_game.construction.command_pause_level;
155}
156
162void CommandHelperBase::InternalDoBefore(bool top_level, bool test)
163{
164 if (top_level) _cleared_object_areas.clear();
165 if (test) SetTownRatingTestMode(true);
166}
167
175void CommandHelperBase::InternalDoAfter(CommandCost &res, DoCommandFlags flags, bool top_level, bool test)
176{
177 if (test) {
179
180 if (res.Succeeded() && top_level && !flags.Test(DoCommandFlag::QueryCost) && !flags.Test(DoCommandFlag::Bankrupt)) {
181 CheckCompanyHasMoney(res); // CheckCompanyHasMoney() modifies 'res' to an error if it fails.
182 }
183 } else {
184 /* If top-level, subtract the money. */
185 if (res.Succeeded() && top_level && !flags.Test(DoCommandFlag::Bankrupt)) {
187 }
188 }
189}
190
200std::tuple<bool, bool, bool> CommandHelperBase::InternalPostBefore(Commands cmd, CommandFlags flags, TileIndex tile, StringID err_message, bool network_command)
201{
202 /* Cost estimation is generally only done when the
203 * local user presses shift while doing something.
204 * However, in case of incoming network commands,
205 * map generation or the pause button we do want
206 * to execute. */
207 bool estimate_only = _shift_pressed && IsLocalCompany() && !_generating_world && !network_command && !flags.Test(CommandFlag::NoEst);
208
209 /* We're only sending the command, so don't do
210 * fancy things for 'success'. */
211 bool only_sending = _networking && !network_command;
212
213 if (_pause_mode.Any() && !IsCommandAllowedWhilePaused(cmd) && !estimate_only) {
214 ShowErrorMessage(GetEncodedString(err_message), GetEncodedString(STR_ERROR_NOT_ALLOWED_WHILE_PAUSED),
215 WL_INFO, TileX(tile) * TILE_SIZE, TileY(tile) * TILE_SIZE);
216 return { true, estimate_only, only_sending };
217 } else {
218 return { false, estimate_only, only_sending };
219 }
220}
221
231void CommandHelperBase::InternalPostResult(CommandCost &res, TileIndex tile, bool estimate_only, bool only_sending, StringID err_message, bool my_cmd)
232{
233 int x = TileX(tile) * TILE_SIZE;
234 int y = TileY(tile) * TILE_SIZE;
235
236 if (res.Failed()) {
237 /* Only show the error when it's for us. */
238 if (estimate_only || (IsLocalCompany() && err_message != 0 && my_cmd)) {
239 ShowErrorMessage(GetEncodedString(err_message), x, y, res);
240 }
241 } else if (estimate_only) {
243 } else if (!only_sending && tile != 0 && IsLocalCompany() && _game_mode != GM_EDITOR) {
244 /* Only show the cost animation when we did actually
245 * execute the command, i.e. we're not sending it to
246 * the server, when it has cost the local company
247 * something. Furthermore in the editor there is no
248 * concept of cost, so don't show it there either. */
250 }
251}
252
260void CommandHelperBase::LogCommandExecution(Commands cmd, StringID err_message, const CommandDataBuffer &args, bool failed)
261{
262 Debug(desync, 1, "{}: {:08x}; {:02x}; {:02x}; {:08x}; {:08x}; {} ({})", failed ? "cmdf" : "cmd", (uint32_t)TimerGameEconomy::date.base(), TimerGameEconomy::date_fract, _current_company, cmd, err_message, FormatArrayAsHex(args), GetCommandName(cmd));
263}
264
271bool CommandHelperBase::InternalExecutePrepTest(CommandFlags cmd_flags, Backup<CompanyID> &cur_company)
272{
273 /* Always execute server and spectator commands as spectator */
274 bool exec_as_spectator = cmd_flags.Any({CommandFlag::Spectator, CommandFlag::Server});
275
276 /* If the company isn't valid it may only do server command or start a new company!
277 * The server will ditch any server commands a client sends to it, so effectively
278 * this guards the server from executing functions for an invalid company. */
279 if (_game_mode == GM_NORMAL && !exec_as_spectator && !Company::IsValidID(_current_company) && !(_current_company == OWNER_DEITY && cmd_flags.Test(CommandFlag::Deity))) {
280 return false;
281 }
282
283 if (exec_as_spectator) cur_company.Change(COMPANY_SPECTATOR);
284
285 /* Enter test mode. */
286 _cleared_object_areas.clear();
289 return true;
290}
291
301std::tuple<bool, bool, bool> CommandHelperBase::InternalExecuteValidateTestAndPrepExec(CommandCost &res, CommandFlags cmd_flags, bool estimate_only, bool network_command, [[maybe_unused]] Backup<CompanyID> &cur_company)
302{
305
306 /* Make sure we're not messing things up here. */
307 assert(cmd_flags.Any({CommandFlag::Spectator, CommandFlag::Server}) ? _current_company == COMPANY_SPECTATOR : cur_company.Verify());
308
309 /* If the command fails, we're doing an estimate
310 * or the player does not have enough money
311 * (unless it's a command where the test and
312 * execution phase might return different costs)
313 * we bail out here. */
314 bool test_and_exec_can_differ = cmd_flags.Test(CommandFlag::NoTest);
315 if (res.Failed() || estimate_only || (!test_and_exec_can_differ && !CheckCompanyHasMoney(res))) {
316 return { true, !_networking || _generating_world || network_command, false };
317 }
318
319 bool send_net = _networking && !_generating_world && !network_command;
320
321 if (!send_net) {
322 /* Prepare for command execution. */
323 _cleared_object_areas.clear();
325 }
326
327 return { false, _debug_desync_level >= 1, send_net };
328}
329
341CommandCost CommandHelperBase::InternalExecuteProcessResult(Commands cmd, CommandFlags cmd_flags, [[maybe_unused]] const CommandCost &res_test, const CommandCost &res_exec, Money extra_cash, TileIndex tile, Backup<CompanyID> &cur_company)
342{
344
345 if (cmd == Commands::CompanyControl) {
346 cur_company.Trash();
347 /* We are a new company -> Switch to new local company.
348 * We were closed down -> Switch to spectator
349 * Some other company opened/closed down -> The outside function will switch back */
351 } else {
352 /* Make sure nothing bad happened, like changing the current company. */
353 assert(cmd_flags.Any({CommandFlag::Spectator, CommandFlag::Server}) ? _current_company == COMPANY_SPECTATOR : cur_company.Verify());
354 cur_company.Restore();
355 }
356
357 /* If the test and execution can differ we have to check the
358 * return of the command. Otherwise we can check whether the
359 * test and execution have yielded the same result,
360 * i.e. cost and error state are the same. */
361 bool test_and_exec_can_differ = cmd_flags.Test(CommandFlag::NoTest);
362 if (!test_and_exec_can_differ) {
363 assert(res_test.GetCost() == res_exec.GetCost() && res_test.Failed() == res_exec.Failed()); // sanity check
364 } else if (res_exec.Failed()) {
365 return res_exec;
366 }
367
368 /* If we're needing more money and we haven't done
369 * anything yet, ask for the money! */
370 if (extra_cash != 0 && res_exec.GetCost() == 0) {
371 /* It could happen we removed rail, thus gained money, and deleted something else.
372 * So make sure the signal buffer is empty even in this case */
374 return CommandCostWithParam(STR_ERROR_NOT_ENOUGH_CASH_REQUIRES_CURRENCY, extra_cash);
375 }
376
377 /* update last build coordinate of company. */
378 if (tile != 0) {
380 if (c != nullptr) c->last_build_coordinate = tile;
381 }
382
384
385 /* Record if there was a command issues during pause; ignore pause/other setting related changes. */
387
388 /* update signals if needed */
390
391 return res_exec;
392}
393
394
401{
402 this->AddCost(ret.cost);
403 if (this->success && !ret.success) {
404 this->message = ret.message;
405 this->encoded_message = std::move(ret.encoded_message);
406 this->success = false;
407 }
408}
409
417{
418 CommandCost error = CommandCost(str);
419 if (IsLocalCompany()) {
420 error.SetEncodedMessage(GetEncodedString(str, value));
421 }
422 return error;
423}
Command definitions related to autoreplace.
Class for backupping variables and making sure they are restored later.
constexpr bool Test(Tvalue_type value) const
Test if the value-th bit is set.
constexpr bool Any(const Timpl &other) const
Test if any of the given values are set.
Common return value for all commands.
bool Succeeded() const
Did this command succeed?
void AddCost(const Money &cost)
Adds the given cost to the cost of the command.
Money GetCost() const
The costs as made up to this moment.
void SetEncodedMessage(EncodedString &&message)
Set the encoded message string.
StringID message
Warning message for when success is unset.
bool Failed() const
Did this command fail?
CommandCost()
Creates a command cost return with no cost and no error.
bool success
Whether the command went fine up to this moment.
EncodedString encoded_message
Encoded error message, used if the error message includes parameters.
static std::tuple< bool, bool, bool > InternalPostBefore(Commands cmd, CommandFlags flags, TileIndex tile, StringID err_message, bool network_command)
Decide what to do with the command depending on current game state.
Definition command.cpp:200
static void InternalPostResult(CommandCost &res, TileIndex tile, bool estimate_only, bool only_sending, StringID err_message, bool my_cmd)
Process result of executing a command, possibly displaying any error to the player.
Definition command.cpp:231
static void InternalDoBefore(bool top_level, bool test)
Prepare for calling a command proc.
Definition command.cpp:162
static void LogCommandExecution(Commands cmd, StringID err_message, const CommandDataBuffer &args, bool failed)
Helper to make a desync log for a command.
Definition command.cpp:260
static CommandCost InternalExecuteProcessResult(Commands cmd, CommandFlags cmd_flags, const CommandCost &res_test, const CommandCost &res_exec, Money extra_cash, TileIndex tile, Backup< CompanyID > &cur_company)
Process the result of a command test run and execution run.
Definition command.cpp:341
static bool InternalExecutePrepTest(CommandFlags cmd_flags, Backup< CompanyID > &cur_company)
Prepare for the test run of a command proc call.
Definition command.cpp:271
static void InternalDoAfter(CommandCost &res, DoCommandFlags flags, bool top_level, bool test)
Process result after calling a command proc.
Definition command.cpp:175
static std::tuple< bool, bool, bool > InternalExecuteValidateTestAndPrepExec(CommandCost &res, CommandFlags cmd_flags, bool estimate_only, bool network_command, Backup< CompanyID > &cur_company)
Validate result of test run and prepare for real execution.
Definition command.cpp:301
A sort-of mixin that implements 'at(pos)' and 'operator[](pos)' only for a specific enum class.
static Date date
Current date in days (day counter).
static DateFract date_fract
Fractional part of the day.
CommandFlags GetCommandFlags(Commands cmd)
Get the command flags associated with the given command.
Definition command.cpp:113
CommandCost CommandCostWithParam(StringID str, uint64_t value)
Return an error status, with string and parameter.
Definition command.cpp:416
bool IsCommandAllowedWhilePaused(Commands cmd)
Returns whether the command is allowed while the game is paused.
Definition command.cpp:137
static constexpr auto _command_table
The master command table.
Definition command.cpp:95
std::string_view GetCommandName(Commands cmd)
Get the name of the given command.
Definition command.cpp:125
bool IsValidCommand(Commands cmd)
This function range-checks a Commands.
Definition command.cpp:103
Functions related to commands.
bool IsCommandAllowedWhilePaused(Commands cmd)
Returns whether the command is allowed while the game is paused.
Definition command.cpp:137
std::string_view GetCommandName(Commands cmd)
Get the name of the given command.
Definition command.cpp:125
CommandType
Types of commands we have.
@ End
End marker.
@ ServerSetting
Pausing/removing companies/server settings.
@ QueryCost
query cost only, don't build.
@ Bankrupt
company bankrupts, skip money check, skip vehicle on tile check in some cases
@ NoEst
the command is never estimated.
@ Deity
the command may be executed by COMPANY_DEITY
@ Spectator
the command may be initiated by a spectator
@ Server
the command can only be initiated by the server
@ NoTest
the command's output may differ between test and execute due to town rating changes etc.
CommandPauseLevel
Different command pause levels.
@ NoLandscaping
No landscaping actions may be executed.
@ NoConstruction
No construction actions may be executed.
@ NoActions
No user actions may be executed.
@ AllActions
All actions may be executed.
std::vector< uint8_t > CommandDataBuffer
Storage buffer for serialized command data.
Commands
List of commands.
@ CompanyControl
used in multiplayer to create a new companies etc.
Definition of stuff that is very close to a company, like the company struct itself.
bool CheckCompanyHasMoney(CommandCost &cost)
Verify whether the company can pay the bill.
static void SubtractMoneyFromCompany(Company *c, const CommandCost &cost)
Deduct costs of a command from the money of a company.
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.
bool IsLocalCompany()
Is the current company the local company?
static constexpr Owner OWNER_DEITY
The object is owned by a superuser / goal script.
static constexpr CompanyID COMPANY_SPECTATOR
The client is spectating.
#define Debug(category, level, format_string,...)
Output a line of debugging information.
Definition debug.h:37
Command definitions related to depots.
Command definitions related to the economy.
Endian-aware buffer.
Command definitions related to engines.
constexpr std::underlying_type_t< enum_type > to_underlying(enum_type e)
Implementation of std::to_underlying (from C++23).
Definition enum_type.hpp:17
Functions related to errors.
@ WL_INFO
Used for DoCommand-like (and some non-fatal AI GUI) errors/information.
Definition error.h:24
void ShowErrorMessage(EncodedString &&summary_msg, int x, int y, CommandCost &cc)
Display an error message in a window.
bool _generating_world
Whether we are generating the map or not.
Definition genworld.cpp:74
Functions related to world/map generation.
bool _shift_pressed
Is Shift pressed?
Definition gfx.cpp:40
PauseModes _pause_mode
The current pause mode.
Definition gfx.cpp:51
Command definitions related to goals.
Command definitions related to engine groups.
GUI functions that shouldn't be here.
void ShowEstimatedCostOrIncome(Money cost, int x, int y)
Display estimated costs.
Definition misc_gui.cpp:495
Command definitions related to industries.
int GetSlopePixelZ(int x, int y, bool ground_vehicle)
Return world Z coordinate of a given point of a tile.
Functions related to OTTD's landscape.
Command definitions related to landscape (slopes etc.).
Command definitions related to league tables.
static uint TileY(TileIndex tile)
Get the Y component of a tile.
Definition map_func.h:427
static uint TileX(TileIndex tile)
Get the X component of a tile.
Definition map_func.h:417
Miscellaneous command definitions.
void ShowCostOrIncomeAnimation(int x, int y, int z, Money cost)
Display animated income or costs on the map.
Definition misc_gui.cpp:513
bool _networking
are we in networking mode?
Definition network.cpp:66
Basic functions/variables used all over the place.
Types used for networking.
@ PSM_LEAVE_TESTMODE
Leave command test mode, revert to previous mode.
@ PSM_LEAVE_COMMAND
Leave command scope, revert to previous mode.
@ PSM_ENTER_COMMAND
Enter command scope, changes will be permanent.
@ PSM_ENTER_TESTMODE
Enter command test mode, changes will be temporary.
Command definitions related to news messages.
Base for all objects.
Command definitions related to objects.
@ CommandDuringPause
A game paused, and a command executed during the pause; resets on autosave.
Definition openttd.h:76
Command definitions related to orders.
Command definitions for rail.
Road related functions.
Command definitions related to road vehicles.
A number of safeguards to prevent using unsafe methods.
GameSettings _settings_game
Game settings of a running game or the scenario editor.
Definition settings.cpp:61
Command definitions related to settings.
void UpdateSignalsInBuffer()
Update signals in buffer Called from 'outside'.
Definition signal.cpp:573
Functions related to signals.
Command definitions related to signs.
Command definitions related to stations.
Definition of base types and functions in a cross-platform compatible way.
Command definitions related to stories.
std::string FormatArrayAsHex(std::span< const uint8_t > data)
Format a byte array into a continuous hex string.
Definition string.cpp:77
Functions related to low-level strings.
EncodedString GetEncodedString(StringID str)
Encode a string with no parameters into an encoded string.
Definition strings.cpp:90
Functions related to OTTD's strings.
uint32_t StringID
Numeric value that represents a string, independent of the selected language.
Class to backup a specific variable and restore it later.
void Trash()
Trash the backup.
bool Verify() const
Check whether the variable is currently equals the backup.
void Change(const U &new_value)
Change the value of the variable.
void Restore()
Restore the variable.
static void SwitchMode(PersistentStorageMode mode, bool ignore_prev_mode=false)
Clear temporary changes made since the last call to SwitchMode, and set whether subsequent changes sh...
Define a command with the flags which belongs to it.
Definition command.cpp:76
CommandType type
The type of command.
Definition command.cpp:79
std::string_view name
A human readable name for the procedure.
Definition command.cpp:77
CommandFlags flags
The (command) flags to that apply to this command.
Definition command.cpp:78
TileIndex last_build_coordinate
Coordinate of the last build thing by this company.
static Company * GetIfValid(auto index)
Command definitions related to subsidies.
Command definitions related to terraforming.
Functions related to text effects.
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
static constexpr uint TILE_SIZE
Tile size in world coordinates.
Definition tile_type.h:15
Definition of the game-economy-timer.
Command definitions related to timetables.
Base of the town class.
void SetTownRatingTestMode(bool mode)
Switch the town rating to test-mode, to allow commands to be tested without affecting current ratings...
Command definitions related to towns.
Command definitions related to trains.
Command definitions related to tree tiles.
Command definitions related to tunnels and bridges.
Command definitions for vehicles.
Command definitions related to viewports.
Command definitions related to water tiles.
Command definitions related to waypoints.