diff options
Diffstat (limited to 'src/command_func.h')
-rw-r--r-- | src/command_func.h | 96 |
1 files changed, 92 insertions, 4 deletions
diff --git a/src/command_func.h b/src/command_func.h index 9d603abcf..b69e97b39 100644 --- a/src/command_func.h +++ b/src/command_func.h @@ -12,6 +12,8 @@ #include "command_type.h" #include "company_type.h" +#include "company_func.h" +#include "core/backup_type.hpp" #include "misc/endian_buffer.hpp" #include "tile_map.h" @@ -34,9 +36,6 @@ static const CommandCost CMD_ERROR = CommandCost(INVALID_STRING_ID); */ #define return_cmd_error(errcode) return CommandCost(errcode); -CommandCost DoCommandPInternal(Commands cmd, StringID err_message, CommandCallback *callback, bool my_cmd, bool estimate_only, bool network_command, TileIndex tile, uint32 p1, uint32 p2, const std::string &text); - -void NetworkSendCommand(Commands cmd, StringID err_message, CommandCallback *callback, CompanyID company, TileIndex tile, uint32 p1, uint32 p2, const std::string &text); void NetworkSendCommand(Commands cmd, StringID err_message, CommandCallback *callback, CompanyID company, TileIndex location, const CommandDataBuffer &cmd_data); extern Money _additional_cash_required; @@ -87,6 +86,10 @@ protected: static void InternalDoAfter(CommandCost &res, DoCommandFlag flags, bool top_level, bool test); static std::tuple<bool, bool, bool> InternalPostBefore(Commands cmd, CommandFlags flags, TileIndex tile, StringID err_message, bool network_command); static void InternalPostResult(const CommandCost &res, TileIndex tile, bool estimate_only, bool only_sending, StringID err_message, bool my_cmd); + static bool InternalExecutePrepTest(CommandFlags cmd_flags, TileIndex tile, Backup<CompanyID> &cur_company); + static std::tuple<bool, bool, bool> InternalExecuteValidateTestAndPrepExec(CommandCost &res, CommandFlags cmd_flags, bool estimate_only, bool network_command, Backup<CompanyID> &cur_company); + static CommandCost InternalExecuteProcessResult(Commands cmd, CommandFlags cmd_flags, const CommandCost &res_test, const CommandCost &res_exec, TileIndex tile, Backup<CompanyID> &cur_company); + static void LogCommandExecution(Commands cmd, StringID err_message, TileIndex tile, const CommandDataBuffer &args, bool failed); }; /** @@ -186,6 +189,41 @@ public: return InternalPost(err_message, callback, my_cmd, true, location, std::move(args)); } + /** + * Prepare a command to be send over the network + * @param cmd The command to execute (a CMD_* value) + * @param err_message Message prefix to show on error + * @param callback A callback function to call after the command is finished + * @param company The company that wants to send the command + * @param args Parameters for the command + */ + static void SendNet(StringID err_message, CommandCallback *callback, CompanyID company, Targs... args) + { + auto args_tuple = std::forward_as_tuple(args...); + + TileIndex tile{}; + if constexpr (std::is_same_v<TileIndex, std::tuple_element_t<0, decltype(args_tuple)>>) { + tile = std::get<0>(args_tuple); + } + + ::NetworkSendCommand(Tcmd, err_message, callback, _current_company, tile, EndianBufferWriter<CommandDataBuffer>::FromValue(args_tuple)); + } + + /** + * Top-level network safe command execution without safety checks. + * @param err_message Message prefix to show on error + * @param callback A callback function to call after the command is finished + * @param my_cmd indicator if the command is from a company or server (to display error messages for a user) + * @param estimate_only whether to give only the estimate or also execute the command + * @param location Tile location for user feedback. + * @param args Parameters for the command + * @return the command cost of this function. + */ + static CommandCost Unsafe(StringID err_message, CommandCallback *callback, bool my_cmd, bool estimate_only, TileIndex location, std::tuple<Targs...> args) + { + return Execute(err_message, callback, my_cmd, estimate_only, false, location, std::move(args)); + } + protected: static bool InternalPost(StringID err_message, CommandCallback *callback, bool my_cmd, bool network_command, std::tuple<Targs...> args) { @@ -206,7 +244,7 @@ protected: /* Only set p2 when the command does not come from the network. */ if (!network_command && GetCommandFlags<Tcmd>() & CMD_CLIENT_ID && std::get<2>(args) == 0) std::get<2>(args) = CLIENT_ID_SERVER; - CommandCost res = std::apply(DoCommandPInternal, std::tuple_cat(std::make_tuple(Tcmd, err_message, callback, my_cmd, estimate_only, network_command), args)); + CommandCost res = Execute(err_message, callback, my_cmd, estimate_only, network_command, tile, args); InternalPostResult(res, tile, estimate_only, only_sending, err_message, my_cmd); if (!estimate_only && !only_sending && callback != nullptr) { @@ -215,6 +253,56 @@ protected: return res.Succeeded(); } + + static CommandCost Execute(StringID err_message, CommandCallback *callback, bool my_cmd, bool estimate_only, bool network_command, TileIndex tile, std::tuple<Targs...> args) + { + /* Prevent recursion; it gives a mess over the network */ + RecursiveCommandCounter counter{}; + assert(counter.IsTopLevel()); + + /* Command flags are used internally */ + CommandFlags cmd_flags = GetCommandFlags<Tcmd>(); + + /* Make sure p2 is properly set to a ClientID also when processing external commands. */ + assert(!(cmd_flags & CMD_CLIENT_ID) || std::get<2>(args) != 0); + + Backup<CompanyID> cur_company(_current_company, FILE_LINE); + if (!InternalExecutePrepTest(cmd_flags, tile, cur_company)) { + cur_company.Trash(); + return CMD_ERROR; + } + + /* Test the command. */ + DoCommandFlag flags = CommandFlagsToDCFlags(cmd_flags); + CommandCost res = std::apply(CommandTraits<Tcmd>::proc, std::tuple_cat(std::make_tuple(flags), args)); + + auto [exit_test, desync_log, send_net] = InternalExecuteValidateTestAndPrepExec(res, cmd_flags, estimate_only, network_command, cur_company); + if (exit_test) { + if (desync_log) LogCommandExecution(Tcmd, err_message, tile, EndianBufferWriter<CommandDataBuffer>::FromValue(args), true); + cur_company.Restore(); + return res; + } + + /* If we are in network, and the command is not from the network + * send it to the command-queue and abort execution. */ + if (send_net) { + ::NetworkSendCommand(Tcmd, err_message, callback, _current_company, tile, EndianBufferWriter<CommandDataBuffer>::FromValue(args)); + cur_company.Restore(); + + /* Don't return anything special here; no error, no costs. + * This way it's not handled by DoCommand and only the + * actual execution of the command causes messages. Also + * reset the storages as we've not executed the command. */ + return CommandCost(); + } + + if (desync_log) LogCommandExecution(Tcmd, err_message, tile, EndianBufferWriter<CommandDataBuffer>::FromValue(args), false); + + /* Actually try and execute the command. */ + CommandCost res2 = std::apply(CommandTraits<Tcmd>::proc, std::tuple_cat(std::make_tuple(flags | DC_EXEC), args)); + + return InternalExecuteProcessResult(Tcmd, cmd_flags, res, res2, tile, cur_company); + } }; template <Commands Tcmd> |