Codechange: Un-bitstuff commands taking a ClientID (i.e. CMD_CLIENT_ID).

This commit is contained in:
Michael Lutz 2021-11-02 21:34:39 +01:00
parent ccefa76a46
commit 4f3ea3907e
29 changed files with 196 additions and 136 deletions

View File

@ -1292,8 +1292,8 @@ struct AIDebugWindow : public Window {
case WID_AID_RELOAD_TOGGLE:
if (ai_debug_company == OWNER_DEITY) break;
/* First kill the company of the AI, then start a new one. This should start the current AI again */
Command<CMD_COMPANY_CTRL>::Post(0, CCA_DELETE | ai_debug_company << 16 | CRR_MANUAL << 24, 0, {});
Command<CMD_COMPANY_CTRL>::Post(0, CCA_NEW_AI | ai_debug_company << 16, 0, {});
Command<CMD_COMPANY_CTRL>::Post(CCA_DELETE, ai_debug_company, CRR_MANUAL, INVALID_CLIENT_ID);
Command<CMD_COMPANY_CTRL>::Post(CCA_NEW_AI, ai_debug_company, CRR_NONE, INVALID_CLIENT_ID);
break;
case WID_AID_SETTINGS:

View File

@ -263,11 +263,10 @@ void GetAircraftSpriteSize(EngineID engine, uint &width, uint &height, int &xoff
* @param flags type of operation.
* @param tile tile of the depot where aircraft is built.
* @param e the engine to build.
* @param data unused.
* @param[out] ret the vehicle that has been built.
* @return the cost of this operation or an error.
*/
CommandCost CmdBuildAircraft(DoCommandFlag flags, TileIndex tile, const Engine *e, uint16 data, Vehicle **ret)
CommandCost CmdBuildAircraft(DoCommandFlag flags, TileIndex tile, const Engine *e, Vehicle **ret)
{
const AircraftVehicleInfo *avi = &e->u.air;
const Station *st = Station::GetByTile(tile);

View File

@ -14,6 +14,6 @@
#include "engine_type.h"
#include "vehicle_type.h"
CommandCost CmdBuildAircraft(DoCommandFlag flags, TileIndex tile, const Engine *e, uint16 data, Vehicle **v);
CommandCost CmdBuildAircraft(DoCommandFlag flags, TileIndex tile, const Engine *e, Vehicle **v);
#endif /* AIRCRAFT_CMD_H */

View File

@ -345,7 +345,7 @@ static CommandCost BuildReplacementVehicle(Vehicle *old_veh, Vehicle **new_vehic
}
/* Build the new vehicle */
cost = Command<CMD_BUILD_VEHICLE>::Do(DC_EXEC | DC_AUTOREPLACE, old_veh->tile, e | (CT_INVALID << 24), 0, {});
cost = Command<CMD_BUILD_VEHICLE>::Do(DC_EXEC | DC_AUTOREPLACE, old_veh->tile, e, true, CT_INVALID, INVALID_CLIENT_ID);
if (cost.Failed()) return cost;
Vehicle *new_veh = Vehicle::Get(_new_vehicle_id);
@ -471,11 +471,11 @@ static CommandCost ReplaceFreeUnit(Vehicle **single_unit, DoCommandFlag flags, b
}
/* Sell the old vehicle */
cost.AddCost(Command<CMD_SELL_VEHICLE>::Do(flags, 0, old_v->index, 0, {}));
cost.AddCost(Command<CMD_SELL_VEHICLE>::Do(flags, 0, old_v->index, false, false, INVALID_CLIENT_ID));
/* If we are not in DC_EXEC undo everything */
if ((flags & DC_EXEC) == 0) {
Command<CMD_SELL_VEHICLE>::Do(DC_EXEC, 0, new_v->index, 0, {});
Command<CMD_SELL_VEHICLE>::Do(DC_EXEC, 0, new_v->index, false, false, INVALID_CLIENT_ID);
}
}
@ -602,7 +602,7 @@ static CommandCost ReplaceChain(Vehicle **chain, DoCommandFlag flags, bool wagon
assert(RailVehInfo(wagon->engine_type)->railveh_type == RAILVEH_WAGON);
/* Sell wagon */
[[maybe_unused]] CommandCost ret = Command<CMD_SELL_VEHICLE>::Do(DC_EXEC, 0, wagon->index, 0, {});
[[maybe_unused]] CommandCost ret = Command<CMD_SELL_VEHICLE>::Do(DC_EXEC, 0, wagon->index, false, false, INVALID_CLIENT_ID);
assert(ret.Succeeded());
new_vehs[i] = nullptr;
@ -634,7 +634,7 @@ static CommandCost ReplaceChain(Vehicle **chain, DoCommandFlag flags, bool wagon
/* Sell the vehicle.
* Note: This might temporarily construct new trains, so use DC_AUTOREPLACE to prevent
* it from failing due to engine limits. */
cost.AddCost(Command<CMD_SELL_VEHICLE>::Do(flags | DC_AUTOREPLACE, 0, w->index, 0, {}));
cost.AddCost(Command<CMD_SELL_VEHICLE>::Do(flags | DC_AUTOREPLACE, 0, w->index, false, false, INVALID_CLIENT_ID));
if ((flags & DC_EXEC) != 0) {
old_vehs[i] = nullptr;
if (i == 0) old_head = nullptr;
@ -665,7 +665,7 @@ static CommandCost ReplaceChain(Vehicle **chain, DoCommandFlag flags, bool wagon
if ((flags & DC_EXEC) == 0) {
for (int i = num_units - 1; i >= 0; i--) {
if (new_vehs[i] != nullptr) {
Command<CMD_SELL_VEHICLE>::Do(DC_EXEC, 0, new_vehs[i]->index, 0, {});
Command<CMD_SELL_VEHICLE>::Do(DC_EXEC, 0, new_vehs[i]->index, false, false, INVALID_CLIENT_ID);
new_vehs[i] = nullptr;
}
}
@ -696,12 +696,12 @@ static CommandCost ReplaceChain(Vehicle **chain, DoCommandFlag flags, bool wagon
}
/* Sell the old vehicle */
cost.AddCost(Command<CMD_SELL_VEHICLE>::Do(flags, 0, old_head->index, 0, {}));
cost.AddCost(Command<CMD_SELL_VEHICLE>::Do(flags, 0, old_head->index, false, false, INVALID_CLIENT_ID));
}
/* If we are not in DC_EXEC undo everything */
if ((flags & DC_EXEC) == 0) {
Command<CMD_SELL_VEHICLE>::Do(DC_EXEC, 0, new_head->index, 0, {});
Command<CMD_SELL_VEHICLE>::Do(DC_EXEC, 0, new_head->index, false, false, INVALID_CLIENT_ID);
}
}
}

View File

@ -1229,7 +1229,7 @@ struct BuildVehicleWindow : Window {
if (!this->listview_mode) {
/* Query for cost and refitted capacity */
CommandCost ret = Command<CMD_BUILD_VEHICLE>::Do(DC_QUERY_COST, this->window_number, this->sel_engine | (cargo << 24), 0, {});
CommandCost ret = Command<CMD_BUILD_VEHICLE>::Do(DC_QUERY_COST, this->window_number, this->sel_engine, true, cargo, INVALID_CLIENT_ID);
if (ret.Succeeded()) {
this->te.cost = ret.GetCost() - e->GetCost();
this->te.capacity = _returned_refit_capacity;
@ -1472,7 +1472,7 @@ struct BuildVehicleWindow : Window {
CommandCallback *callback = (this->vehicle_type == VEH_TRAIN && RailVehInfo(sel_eng)->railveh_type == RAILVEH_WAGON) ? CcBuildWagon : CcBuildPrimaryVehicle;
CargoID cargo = this->cargo_filter[this->cargo_filter_criteria];
if (cargo == CF_ANY || cargo == CF_ENGINES) cargo = CF_NONE;
Command<CMD_BUILD_VEHICLE>::Post(GetCmdBuildVehMsg(this->vehicle_type), callback, this->window_number, sel_eng | (cargo << 24), 0, {});
Command<CMD_BUILD_VEHICLE>::Post(GetCmdBuildVehMsg(this->vehicle_type), callback, this->window_number, sel_eng, true, cargo, INVALID_CLIENT_ID);
}
break;
}

View File

@ -75,15 +75,13 @@ int RecursiveCommandCounter::_counter = 0;
* the #CMD_AUTO, #CMD_OFFLINE and #CMD_SERVER values.
*/
struct CommandInfo {
CommandProc *proc; ///< The procedure to actually executing
const char *name; ///< A human readable name for the procedure
CommandFlags flags; ///< The (command) flags to that apply to this command
CommandType type; ///< The type of command.
};
/* Helpers to generate the master command table from the command traits. */
template <typename T>
inline constexpr CommandInfo CommandFromTrait() noexcept { return { T::proc, T::name, T::flags, T::type }; };
inline constexpr CommandInfo CommandFromTrait() noexcept { return { T::name, T::flags, T::type }; };
template<typename T, T... i>
inline constexpr auto MakeCommandsFromTraits(std::integer_sequence<T, i...>) noexcept {
@ -101,14 +99,14 @@ static constexpr auto _command_proc_table = MakeCommandsFromTraits(std::make_int
/*!
* This function range-checks a cmd, and checks if the cmd is not nullptr
* This function range-checks a cmd.
*
* @param cmd The integer value of a command
* @return true if the command is valid (and got a CommandProc function)
*/
bool IsValidCommand(Commands cmd)
{
return cmd < _command_proc_table.size() && _command_proc_table[cmd].proc != nullptr;
return cmd < _command_proc_table.size();
}
/*!

View File

@ -11,6 +11,7 @@
#define COMMAND_FUNC_H
#include "command_type.h"
#include "network/network_type.h"
#include "company_type.h"
#include "company_func.h"
#include "core/backup_type.hpp"
@ -225,6 +226,22 @@ public:
}
protected:
/** Helper to process a single ClientID argument. */
template <class T>
static inline void SetClientIdHelper(T &data)
{
if constexpr (std::is_same_v<ClientID, T>) {
if (data == INVALID_CLIENT_ID) data = CLIENT_ID_SERVER;
}
}
/** Set all invalid ClientID's to the proper value. */
template<class Ttuple, size_t... Tindices>
static inline void SetClientIds(Ttuple &values, std::index_sequence<Tindices...>)
{
((SetClientIdHelper(std::get<Tindices>(values))), ...);
}
static bool InternalPost(StringID err_message, CommandCallback *callback, bool my_cmd, bool network_command, std::tuple<Targs...> args)
{
/* Where to show the message? */
@ -241,8 +258,8 @@ protected:
auto [err, estimate_only, only_sending] = InternalPostBefore(Tcmd, GetCommandFlags<Tcmd>(), tile, err_message, network_command);
if (err) return false;
/* 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;
/* Only set client IDs when the command does not come from the network. */
if (!network_command && GetCommandFlags<Tcmd>() & CMD_CLIENT_ID) SetClientIds(args, std::index_sequence_for<Targs...>{});
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);
@ -254,6 +271,24 @@ protected:
return res.Succeeded();
}
/** Helper to process a single ClientID argument. */
template <class T>
static inline bool ClientIdIsSet(T &data)
{
if constexpr (std::is_same_v<ClientID, T>) {
return data != INVALID_CLIENT_ID;
} else {
return true;
}
}
/** Check if all ClientID arguments are set to valid values. */
template<class Ttuple, size_t... Tindices>
static inline bool AllClientIdsSet(Ttuple &values, std::index_sequence<Tindices...>)
{
return (ClientIdIsSet(std::get<Tindices>(values)) && ...);
}
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 */
@ -261,10 +296,12 @@ protected:
assert(counter.IsTopLevel());
/* Command flags are used internally */
CommandFlags cmd_flags = GetCommandFlags<Tcmd>();
constexpr 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);
if constexpr ((cmd_flags & CMD_CLIENT_ID) != 0) {
/* Make sure arguments are properly set to a ClientID also when processing external commands. */
assert(AllClientIdsSet(args, std::index_sequence_for<Targs...>{}));
}
Backup<CompanyID> cur_company(_current_company, FILE_LINE);
if (!InternalExecutePrepTest(cmd_flags, tile, cur_company)) {

View File

@ -604,7 +604,7 @@ static bool MaybeStartNewCompany()
if (n < (uint)_settings_game.difficulty.max_no_competitors) {
/* Send a command to all clients to start up a new AI.
* Works fine for Multiplayer and Singleplayer */
return Command<CMD_COMPANY_CTRL>::Post(0, CCA_NEW_AI | INVALID_COMPANY << 16, 0, {});
return Command<CMD_COMPANY_CTRL>::Post(CCA_NEW_AI, INVALID_COMPANY, CRR_NONE, INVALID_CLIENT_ID );
}
return false;
@ -796,21 +796,16 @@ void CompanyAdminRemove(CompanyID company_id, CompanyRemoveReason reason)
/**
* Control the companies: add, delete, etc.
* @param flags operation to perform
* @param tile unused
* @param p1 various functionality
* - bits 0..15: CompanyCtrlAction
* - bits 16..23: CompanyID
* - bits 24..31: CompanyRemoveReason (with CCA_DELETE)
* @param p2 ClientID
* @param text unused
* @param cca action to perform
* @param company_id company to perform the action on
* @param client_id ClientID
* @return the cost of this operation or an error
*/
CommandCost CmdCompanyCtrl(DoCommandFlag flags, TileIndex tile, uint32 p1, uint32 p2, const std::string &text)
CommandCost CmdCompanyCtrl(DoCommandFlag flags, CompanyCtrlAction cca, CompanyID company_id, CompanyRemoveReason reason, ClientID client_id)
{
InvalidateWindowData(WC_COMPANY_LEAGUE, 0, 0);
CompanyID company_id = (CompanyID)GB(p1, 16, 8);
switch ((CompanyCtrlAction)GB(p1, 0, 16)) {
switch (cca) {
case CCA_NEW: { // Create a new company
/* This command is only executed in a multiplayer game */
if (!_networking) return CMD_ERROR;
@ -818,7 +813,6 @@ CommandCost CmdCompanyCtrl(DoCommandFlag flags, TileIndex tile, uint32 p1, uint3
/* Has the network client a correct ClientIndex? */
if (!(flags & DC_EXEC)) return CommandCost();
ClientID client_id = (ClientID)p2;
NetworkClientInfo *ci = NetworkClientInfo::GetByClientID(client_id);
/* Delete multiplayer progress bar */
@ -873,7 +867,6 @@ CommandCost CmdCompanyCtrl(DoCommandFlag flags, TileIndex tile, uint32 p1, uint3
}
case CCA_DELETE: { // Delete a company
CompanyRemoveReason reason = (CompanyRemoveReason)GB(p1, 24, 8);
if (reason >= CRR_END) return CMD_ERROR;
/* We can't delete the last existing company in singleplayer mode. */

View File

@ -12,7 +12,9 @@
#include "command_type.h"
CommandProc CmdCompanyCtrl;
enum ClientID : uint32;
CommandCost CmdCompanyCtrl(DoCommandFlag flags, CompanyCtrlAction cca, CompanyID company_id, CompanyRemoveReason reason, ClientID client_id);
CommandProc CmdGiveMoney;
CommandProc CmdRenameCompany;
CommandProc CmdRenamePresident;

View File

@ -52,16 +52,18 @@ struct Company;
typedef uint32 CompanyManagerFace; ///< Company manager face bits, info see in company_manager_face.h
/** The reason why the company was removed. */
enum CompanyRemoveReason {
enum CompanyRemoveReason : uint8 {
CRR_MANUAL, ///< The company is manually removed.
CRR_AUTOCLEAN, ///< The company is removed due to autoclean.
CRR_BANKRUPT, ///< The company went belly-up.
CRR_END, ///< Sentinel for end.
CRR_NONE = CRR_MANUAL, ///< Dummy reason for actions that don't need one.
};
/** The action to do with CMD_COMPANY_CTRL. */
enum CompanyCtrlAction {
enum CompanyCtrlAction : uint8 {
CCA_NEW, ///< Create a new company.
CCA_NEW_AI, ///< Create a new AI company.
CCA_DELETE, ///< Delete a company.

View File

@ -865,7 +865,7 @@ DEF_CONSOLE_CMD(ConResetCompany)
}
/* It is safe to remove this company */
Command<CMD_COMPANY_CTRL>::Post(0, CCA_DELETE | index << 16 | CRR_MANUAL << 24, 0, {});
Command<CMD_COMPANY_CTRL>::Post(CCA_DELETE, index, CRR_MANUAL, INVALID_CLIENT_ID);
IConsolePrint(CC_DEFAULT, "Company deleted.");
return true;
@ -1222,7 +1222,7 @@ DEF_CONSOLE_CMD(ConStartAI)
}
/* Start a new AI company */
Command<CMD_COMPANY_CTRL>::Post(0, CCA_NEW_AI | INVALID_COMPANY << 16, 0, {});
Command<CMD_COMPANY_CTRL>::Post(CCA_NEW_AI, INVALID_COMPANY, CRR_NONE, INVALID_CLIENT_ID);
return true;
}
@ -1258,8 +1258,8 @@ DEF_CONSOLE_CMD(ConReloadAI)
}
/* First kill the company of the AI, then start a new one. This should start the current AI again */
Command<CMD_COMPANY_CTRL>::Post(0, CCA_DELETE | company_id << 16 | CRR_MANUAL << 24, 0, {});
Command<CMD_COMPANY_CTRL>::Post(0, CCA_NEW_AI | company_id << 16, 0, {});
Command<CMD_COMPANY_CTRL>::Post(CCA_DELETE, company_id, CRR_MANUAL, INVALID_CLIENT_ID);
Command<CMD_COMPANY_CTRL>::Post(CCA_NEW_AI, company_id, CRR_NONE, INVALID_CLIENT_ID);
IConsolePrint(CC_DEFAULT, "AI reloaded.");
return true;
@ -1296,7 +1296,7 @@ DEF_CONSOLE_CMD(ConStopAI)
}
/* Now kill the company of the AI. */
Command<CMD_COMPANY_CTRL>::Post(0, CCA_DELETE | company_id << 16 | CRR_MANUAL << 24, 0, {});
Command<CMD_COMPANY_CTRL>::Post(CCA_DELETE, company_id, CRR_MANUAL, INVALID_CLIENT_ID);
IConsolePrint(CC_DEFAULT, "AI stopped, company deleted.");
return true;

View File

@ -1026,8 +1026,8 @@ struct DepotWindow : Window {
this->sel = INVALID_VEHICLE;
this->SetDirty();
int sell_cmd = (v->type == VEH_TRAIN && (widget == WID_D_SELL_CHAIN || _ctrl_pressed)) ? 1 : 0;
Command<CMD_SELL_VEHICLE>::Post(GetCmdSellVehMsg(v->type), v->tile, v->index | sell_cmd << 20 | MAKE_ORDER_BACKUP_FLAG, 0, {});
bool sell_cmd = (v->type == VEH_TRAIN && (widget == WID_D_SELL_CHAIN || _ctrl_pressed));
Command<CMD_SELL_VEHICLE>::Post(GetCmdSellVehMsg(v->type), v->tile, v->index, sell_cmd, true, INVALID_CLIENT_ID);
break;
}

View File

@ -630,7 +630,7 @@ static void CompanyCheckBankrupt(Company *c)
* player we are sure (the above check) that we are not the local
* company and thus we won't be moved. */
if (!_networking || _network_server) {
Command<CMD_COMPANY_CTRL>::Post(0, CCA_DELETE | (c->index << 16) | (CRR_BANKRUPT << 24), 0, {});
Command<CMD_COMPANY_CTRL>::Post(CCA_DELETE, c->index, CRR_BANKRUPT, INVALID_CLIENT_ID);
return;
}
break;

View File

@ -840,7 +840,7 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_MAP_DONE(Packet
* the server will give us a client-id and let us in */
_network_join_status = NETWORK_JOIN_STATUS_REGISTERING;
ShowJoinStatusWindow();
Command<CMD_COMPANY_CTRL>::SendNet(STR_NULL, nullptr, _local_company, 0, CCA_NEW, 0, {});
Command<CMD_COMPANY_CTRL>::SendNet(STR_NULL, nullptr, _local_company, CCA_NEW, INVALID_COMPANY, CRR_NONE, INVALID_CLIENT_ID);
}
} else {
/* take control over an existing company */

View File

@ -89,15 +89,17 @@ static CommandCallback * const _callback_table[] = {
template <Commands Tcmd> static CommandDataBuffer SanitizeCmdStrings(const CommandDataBuffer &data);
template <Commands Tcmd> static void UnpackNetworkCommand(const CommandPacket *cp);
template <Commands Tcmd> static void NetworkReplaceCommandClientId(CommandPacket &cp, ClientID client_id);
struct CommandDispatch {
CommandDataBuffer(*Sanitize)(const CommandDataBuffer &);
void (*ReplaceClientId)(CommandPacket &, ClientID);
void (*Unpack)(const CommandPacket *);
};
template<typename T, T... i>
inline constexpr auto MakeDispatchTable(std::integer_sequence<T, i...>) noexcept
{
return std::array<CommandDispatch, sizeof...(i)>{{ { &SanitizeCmdStrings<static_cast<Commands>(i)>, &UnpackNetworkCommand<static_cast<Commands>(i)> }... }};
return std::array<CommandDispatch, sizeof...(i)>{{ { &SanitizeCmdStrings<static_cast<Commands>(i)>, &NetworkReplaceCommandClientId<static_cast<Commands>(i)>, &UnpackNetworkCommand<static_cast<Commands>(i)> }... }};
}
static constexpr auto _cmd_dispatch = MakeDispatchTable(std::make_integer_sequence<std::underlying_type_t<Commands>, CMD_END>{});
@ -383,6 +385,35 @@ void NetworkGameSocketHandler::SendCommand(Packet *p, const CommandPacket *cp)
p->Send_uint8 (callback);
}
/** Helper to process a single ClientID argument. */
template <class T>
static inline void SetClientIdHelper(T &data, [[maybe_unused]] ClientID client_id)
{
if constexpr (std::is_same_v<ClientID, T>) {
data = client_id;
}
}
/** Set all invalid ClientID's to the proper value. */
template<class Ttuple, size_t... Tindices>
static inline void SetClientIds(Ttuple &values, ClientID client_id, std::index_sequence<Tindices...>)
{
((SetClientIdHelper(std::get<Tindices>(values), client_id)), ...);
}
template <Commands Tcmd>
static void NetworkReplaceCommandClientId(CommandPacket &cp, ClientID client_id)
{
/* Unpack command parameters. */
auto params = EndianBufferReader::ToValue<typename CommandTraits<Tcmd>::Args>(cp.data);
/* Insert client id. */
SetClientIds(params, client_id, std::make_index_sequence<std::tuple_size_v<decltype(params)>>{});
/* Repack command parameters. */
cp.data = EndianBufferWriter<CommandDataBuffer>::FromValue(params);
}
/**
* Insert a client ID into the command data in a command packet.
* @param cp Command packet to modify.
@ -390,14 +421,7 @@ void NetworkGameSocketHandler::SendCommand(Packet *p, const CommandPacket *cp)
*/
void NetworkReplaceCommandClientId(CommandPacket &cp, ClientID client_id)
{
/* Unpack command parameters. */
auto params = EndianBufferReader::ToValue<std::tuple<TileIndex, uint32, uint32, std::string>>(cp.data);
/* Insert client id. */
std::get<2>(params) = client_id;
/* Repack command parameters. */
cp.data = EndianBufferWriter<CommandDataBuffer>::FromValue(params);
_cmd_dispatch[cp.cmd].ReplaceClientId(cp, client_id);
}

View File

@ -1396,7 +1396,7 @@ static void AdminCompanyResetCallback(Window *w, bool confirmed)
{
if (confirmed) {
if (NetworkCompanyHasClients(_admin_company_id)) return;
Command<CMD_COMPANY_CTRL>::Post(0, CCA_DELETE | _admin_company_id << 16 | CRR_MANUAL << 24, 0, {});
Command<CMD_COMPANY_CTRL>::Post(CCA_DELETE, _admin_company_id, CRR_MANUAL, INVALID_CLIENT_ID);
}
}
@ -1536,9 +1536,9 @@ private:
static void OnClickCompanyNew(NetworkClientListWindow *w, Point pt, CompanyID company_id)
{
if (_network_server) {
Command<CMD_COMPANY_CTRL>::Post(0, CCA_NEW, _network_own_client_id, {});
Command<CMD_COMPANY_CTRL>::Post(CCA_NEW, INVALID_COMPANY, CRR_NONE, _network_own_client_id);
} else {
Command<CMD_COMPANY_CTRL>::SendNet(STR_NULL, nullptr, _local_company, 0, CCA_NEW, 0, {});
Command<CMD_COMPANY_CTRL>::SendNet(STR_NULL, nullptr, _local_company, CCA_NEW, INVALID_COMPANY, CRR_NONE, INVALID_CLIENT_ID);
}
}

View File

@ -1050,15 +1050,15 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::Receive_CLIENT_COMMAND(Packet
* to match the company in the packet. If it doesn't, the client has done
* something pretty naughty (or a bug), and will be kicked
*/
uint32 company_p1 = cp.cmd == CMD_COMPANY_CTRL ? std::get<1>(EndianBufferReader::ToValue<CommandTraits<CMD_COMPANY_CTRL>::Args>(cp.data)) : 0;
if (!(cp.cmd == CMD_COMPANY_CTRL && company_p1 == 0 && ci->client_playas == COMPANY_NEW_COMPANY) && ci->client_playas != cp.company) {
CompanyCtrlAction cca = cp.cmd == CMD_COMPANY_CTRL ? std::get<0>(EndianBufferReader::ToValue<CommandTraits<CMD_COMPANY_CTRL>::Args>(cp.data)) : CCA_NEW;
if (!(cp.cmd == CMD_COMPANY_CTRL && cca == CCA_NEW && ci->client_playas == COMPANY_NEW_COMPANY) && ci->client_playas != cp.company) {
IConsolePrint(CC_WARNING, "Kicking client #{} (IP: {}) due to calling a command as another company {}.",
ci->client_playas + 1, this->GetClientIP(), cp.company + 1);
return this->SendError(NETWORK_ERROR_COMPANY_MISMATCH);
}
if (cp.cmd == CMD_COMPANY_CTRL) {
if (company_p1 != 0 || cp.company != COMPANY_SPECTATOR) {
if (cca != CCA_NEW || cp.company != COMPANY_SPECTATOR) {
return this->SendError(NETWORK_ERROR_CHEATER);
}
@ -1556,7 +1556,7 @@ static void NetworkAutoCleanCompanies()
/* Is the company empty for autoclean_unprotected-months, and is there no protection? */
if (_settings_client.network.autoclean_unprotected != 0 && _network_company_states[c->index].months_empty > _settings_client.network.autoclean_unprotected && _network_company_states[c->index].password.empty()) {
/* Shut the company down */
Command<CMD_COMPANY_CTRL>::Post(0, CCA_DELETE | c->index << 16 | CRR_AUTOCLEAN << 24, 0, {});
Command<CMD_COMPANY_CTRL>::Post(CCA_DELETE, c->index, CRR_AUTOCLEAN, INVALID_CLIENT_ID);
IConsolePrint(CC_INFO, "Auto-cleaned company #{} with no password.", c->index + 1);
}
/* Is the company empty for autoclean_protected-months, and there is a protection? */
@ -1570,7 +1570,7 @@ static void NetworkAutoCleanCompanies()
/* Is the company empty for autoclean_novehicles-months, and has no vehicles? */
if (_settings_client.network.autoclean_novehicles != 0 && _network_company_states[c->index].months_empty > _settings_client.network.autoclean_novehicles && vehicles_in_company[c->index] == 0) {
/* Shut the company down */
Command<CMD_COMPANY_CTRL>::Post(0, CCA_DELETE | c->index << 16 | CRR_AUTOCLEAN << 24, 0, {});
Command<CMD_COMPANY_CTRL>::Post(CCA_DELETE, c->index, CRR_AUTOCLEAN, INVALID_CLIENT_ID);
IConsolePrint(CC_INFO, "Auto-cleaned company #{} with no vehicles.", c->index + 1);
}
} else {

View File

@ -144,15 +144,13 @@ void OrderBackup::DoRestore(Vehicle *v)
* Clear an OrderBackup
* @param flags For command.
* @param tile Tile related to the to-be-cleared OrderBackup.
* @param p1 Unused.
* @param p2 User that had the OrderBackup.
* @param text Unused.
* @param user_id User that had the OrderBackup.
* @return The cost of this operation or an error.
*/
CommandCost CmdClearOrderBackup(DoCommandFlag flags, TileIndex tile, uint32 p1, uint32 p2, const std::string &text)
CommandCost CmdClearOrderBackup(DoCommandFlag flags, TileIndex tile, ClientID user_id)
{
/* No need to check anything. If the tile or user don't exist we just ignore it. */
if (flags & DC_EXEC) OrderBackup::ResetOfUser(tile == 0 ? INVALID_TILE : tile, p2);
if (flags & DC_EXEC) OrderBackup::ResetOfUser(tile == 0 ? INVALID_TILE : tile, user_id);
return CommandCost();
}
@ -171,7 +169,7 @@ CommandCost CmdClearOrderBackup(DoCommandFlag flags, TileIndex tile, uint32 p1,
/* If it's not a backup of us, ignore it. */
if (ob->user != user) continue;
Command<CMD_CLEAR_ORDER_BACKUP>::Post(0, 0, user, {});
Command<CMD_CLEAR_ORDER_BACKUP>::Post(0, static_cast<ClientID>(user));
return;
}
}
@ -200,7 +198,7 @@ CommandCost CmdClearOrderBackup(DoCommandFlag flags, TileIndex tile, uint32 p1,
/* We need to circumvent the "prevention" from this command being executed
* while the game is paused, so use the internal method. Nor do we want
* this command to get its cost estimated when shift is pressed. */
Command<CMD_CLEAR_ORDER_BACKUP>::Unsafe(STR_NULL, nullptr, true, false, ob->tile, CommandTraits<CMD_CLEAR_ORDER_BACKUP>::Args{ ob->tile, 0, user, {} });
Command<CMD_CLEAR_ORDER_BACKUP>::Unsafe(STR_NULL, nullptr, true, false, ob->tile, CommandTraits<CMD_CLEAR_ORDER_BACKUP>::Args{ ob->tile, static_cast<ClientID>(user) });
} else {
/* The command came from the game logic, i.e. the clearing of a tile.
* In that case we have no need to actually sync this, just do it. */

View File

@ -19,7 +19,7 @@ CommandProc CmdInsertOrder;
CommandProc CmdOrderRefit;
CommandProc CmdCloneOrder;
CommandProc CmdMoveOrder;
CommandProc CmdClearOrderBackup;
CommandCost CmdClearOrderBackup(DoCommandFlag flags, TileIndex tile, ClientID user_id);
DEF_CMD_TRAIT(CMD_MODIFY_ORDER, CmdModifyOrder, 0, CMDT_ROUTE_MANAGEMENT)
DEF_CMD_TRAIT(CMD_SKIP_TO_ORDER, CmdSkipToOrder, 0, CMDT_ROUTE_MANAGEMENT)

View File

@ -254,11 +254,10 @@ void RoadVehUpdateCache(RoadVehicle *v, bool same_length)
* @param flags type of operation.
* @param tile tile of the depot where road vehicle is built.
* @param e the engine to build.
* @param data unused.
* @param[out] ret the vehicle that has been built.
* @return the cost of this operation or an error.
*/
CommandCost CmdBuildRoadVehicle(DoCommandFlag flags, TileIndex tile, const Engine *e, uint16 data, Vehicle **ret)
CommandCost CmdBuildRoadVehicle(DoCommandFlag flags, TileIndex tile, const Engine *e, Vehicle **ret)
{
/* Check that the vehicle can drive on the road in question */
RoadType rt = e->u.road.roadtype;

View File

@ -14,7 +14,7 @@
#include "engine_type.h"
#include "vehicle_type.h"
CommandCost CmdBuildRoadVehicle(DoCommandFlag flags, TileIndex tile, const Engine *e, uint16 data, Vehicle **v);
CommandCost CmdBuildRoadVehicle(DoCommandFlag flags, TileIndex tile, const Engine *e, Vehicle **v);
CommandProc CmdTurnRoadVeh;

View File

@ -347,6 +347,22 @@ namespace ScriptObjectInternal {
{
((SanitizeSingleStringHelper(std::get<Tindices>(values))), ...);
}
/** Helper to process a single ClientID argument. */
template <class T>
static inline void SetClientIdHelper(T &data)
{
if constexpr (std::is_same_v<ClientID, T>) {
if (data == INVALID_CLIENT_ID) data = (ClientID)UINT32_MAX;
}
}
/** Set all invalid ClientID's to the proper value. */
template<class Ttuple, size_t... Tindices>
static inline void SetClientIds(Ttuple &values, std::index_sequence<Tindices...>)
{
((SetClientIdHelper(std::get<Tindices>(values))), ...);
}
}
template <Commands Tcmd, typename... Targs>
@ -364,8 +380,8 @@ bool ScriptObject::ScriptDoCommandHelper<Tcmd, CommandCost(*)(DoCommandFlag, Tar
tile = std::get<0>(args);
}
/* Only set p2 when the command does not come from the network. */
if ((::GetCommandFlags<Tcmd>() & CMD_CLIENT_ID) != 0 && std::get<2>(args) == 0) std::get<2>(args) = UINT32_MAX;
/* Only set ClientID parameters when the command does not come from the network. */
if constexpr ((::GetCommandFlags<Tcmd>() & CMD_CLIENT_ID) != 0) ScriptObjectInternal::SetClientIds(args, std::index_sequence_for<Targs...>{});
/* Store the command for command callback validation. */
if (!estimate_only && networking) ScriptObject::SetLastCommand(tile, EndianBufferWriter<CommandDataBuffer>::FromValue(args), Tcmd);

View File

@ -72,7 +72,7 @@
EnforcePreconditionCustomError(VEHICLE_INVALID, !ScriptGameSettings::IsDisabledVehicleType((ScriptVehicle::VehicleType)type), ScriptVehicle::ERR_VEHICLE_BUILD_DISABLED);
if (!ScriptObject::Command<CMD_BUILD_VEHICLE>::Do(&ScriptInstance::DoCommandReturnVehicleID, depot, engine_id | (cargo << 24), 0, {})) return VEHICLE_INVALID;
if (!ScriptObject::Command<CMD_BUILD_VEHICLE>::Do(&ScriptInstance::DoCommandReturnVehicleID, depot, engine_id, true, cargo, INVALID_CLIENT_ID)) return VEHICLE_INVALID;
/* In case of test-mode, we return VehicleID 0 */
return 0;
@ -94,7 +94,7 @@
if (!ScriptEngine::IsBuildable(engine_id)) return -1;
if (!ScriptCargo::IsValidCargo(cargo)) return -1;
CommandCost res = ::Command<CMD_BUILD_VEHICLE>::Do(DC_QUERY_COST, depot, engine_id | (cargo << 24), 0, {});
CommandCost res = ::Command<CMD_BUILD_VEHICLE>::Do(DC_QUERY_COST, depot, engine_id, true, cargo, INVALID_CLIENT_ID);
return res.Succeeded() ? _returned_refit_capacity : -1;
}
@ -162,7 +162,7 @@
EnforcePrecondition(false, IsValidVehicle(vehicle_id));
const Vehicle *v = ::Vehicle::Get(vehicle_id);
return ScriptObject::Command<CMD_SELL_VEHICLE>::Do(0, vehicle_id | (v->type == VEH_TRAIN ? 1 : 0) << 20, 0, {});
return ScriptObject::Command<CMD_SELL_VEHICLE>::Do(0, vehicle_id, v->type == VEH_TRAIN, false, INVALID_CLIENT_ID);
}
/* static */ bool ScriptVehicle::_SellWagonInternal(VehicleID vehicle_id, int wagon, bool sell_attached_wagons)
@ -174,7 +174,7 @@
const Train *v = ::Train::Get(vehicle_id);
while (wagon-- > 0) v = v->GetNextUnit();
return ScriptObject::Command<CMD_SELL_VEHICLE>::Do(0, v->index | (sell_attached_wagons ? 1 : 0) << 20, 0, {});
return ScriptObject::Command<CMD_SELL_VEHICLE>::Do(0, v->index, sell_attached_wagons, false, INVALID_CLIENT_ID);
}
/* static */ bool ScriptVehicle::SellWagon(VehicleID vehicle_id, int wagon)

View File

@ -841,11 +841,10 @@ void Ship::SetDestTile(TileIndex tile)
* @param flags type of operation.
* @param tile tile of the depot where ship is built.
* @param e the engine to build.
* @param data unused.
* @param[out] ret the vehicle that has been built.
* @return the cost of this operation or an error.
*/
CommandCost CmdBuildShip(DoCommandFlag flags, TileIndex tile, const Engine *e, uint16 data, Vehicle **ret)
CommandCost CmdBuildShip(DoCommandFlag flags, TileIndex tile, const Engine *e, Vehicle **ret)
{
tile = GetShipDepotNorthTile(tile);
if (flags & DC_EXEC) {

View File

@ -14,6 +14,6 @@
#include "engine_type.h"
#include "vehicle_type.h"
CommandCost CmdBuildShip(DoCommandFlag flags, TileIndex tile, const Engine *e, uint16 data, Vehicle **v);
CommandCost CmdBuildShip(DoCommandFlag flags, TileIndex tile, const Engine *e, Vehicle **v);
#endif /* SHIP_CMD_H */

View File

@ -718,11 +718,11 @@ static void AddRearEngineToMultiheadedTrain(Train *v)
* @param flags type of operation.
* @param tile tile of the depot where rail-vehicle is built.
* @param e the engine to build.
* @param data bit 0 prevents any free cars from being added to the train.
* @param free_cars add any free cars to the train.
* @param[out] ret the vehicle that has been built.
* @return the cost of this operation or an error.
*/
CommandCost CmdBuildRailVehicle(DoCommandFlag flags, TileIndex tile, const Engine *e, uint16 data, Vehicle **ret)
CommandCost CmdBuildRailVehicle(DoCommandFlag flags, TileIndex tile, const Engine *e, bool free_cars, Vehicle **ret)
{
const RailVehicleInfo *rvi = &e->u.rail;
@ -789,7 +789,7 @@ CommandCost CmdBuildRailVehicle(DoCommandFlag flags, TileIndex tile, const Engin
v->ConsistChanged(CCF_ARRANGE);
UpdateTrainGroupID(v);
if (!HasBit(data, 0) && !(flags & DC_AUTOREPLACE)) { // check if the cars should be added to the new vehicle
if (free_cars && !(flags & DC_AUTOREPLACE)) { // check if the cars should be added to the new vehicle
NormalizeTrainVehInDepot(v);
}
@ -1357,18 +1357,16 @@ CommandCost CmdMoveRailVehicle(DoCommandFlag flags, TileIndex tile, uint32 p1, u
* Sell a (single) train wagon/engine.
* @param flags type of operation
* @param t the train wagon to sell
* @param data the selling mode
* - data = 0: only sell the single dragged wagon/engine (and any belonging rear-engines)
* - data = 1: sell the vehicle and all vehicles following it in the chain
* if the wagon is dragged, don't delete the possibly belonging rear-engine to some front
* @param sell_chain the selling mode
* - sell_chain = false: only sell the single dragged wagon/engine (and any belonging rear-engines)
* - sell_chain = true: sell the vehicle and all vehicles following it in the chain
* if the wagon is dragged, don't delete the possibly belonging rear-engine to some front
* @param backup_order make order backup?
* @param user the user for the order backup.
* @return the cost of this operation or an error
*/
CommandCost CmdSellRailWagon(DoCommandFlag flags, Vehicle *t, uint16 data, uint32 user)
CommandCost CmdSellRailWagon(DoCommandFlag flags, Vehicle *t, bool sell_chain, bool backup_order, ClientID user)
{
/* Sell a chain of vehicles or not? */
bool sell_chain = HasBit(data, 0);
Train *v = Train::From(t)->GetFirstEnginePart();
Train *first = v->First();
@ -1418,7 +1416,7 @@ CommandCost CmdSellRailWagon(DoCommandFlag flags, Vehicle *t, uint16 data, uint3
/* Copy other important data from the front engine */
new_head->CopyVehicleConfigAndStatistics(first);
GroupStatistics::CountVehicle(new_head, 1); // after copying over the profit
} else if (v->IsPrimaryVehicle() && data & (MAKE_ORDER_BACKUP_FLAG >> 20)) {
} else if (v->IsPrimaryVehicle() && backup_order) {
OrderBackup::Backup(v, user);
}

View File

@ -14,8 +14,8 @@
#include "engine_type.h"
#include "vehicle_type.h"
CommandCost CmdBuildRailVehicle(DoCommandFlag flags, TileIndex tile, const Engine *e, uint16 data, Vehicle **v);
CommandCost CmdSellRailWagon(DoCommandFlag flags, Vehicle *v, uint16 data, uint32 user);
CommandCost CmdBuildRailVehicle(DoCommandFlag flags, TileIndex tile, const Engine *e, bool free_cars, Vehicle **ret);
CommandCost CmdSellRailWagon(DoCommandFlag flags, Vehicle *t, bool sell_chain, bool backup_order, ClientID user);
CommandProc CmdMoveRailVehicle;
CommandProc CmdForceTrainProceed;

View File

@ -79,15 +79,13 @@ const StringID _send_to_depot_msg_table[] = {
* Build a vehicle.
* @param flags for command
* @param tile tile of depot where the vehicle is built
* @param p1 various bitstuffed data
* bits 0-15: vehicle type being built.
* bits 16-23: vehicle type specific bits passed on to the vehicle build functions.
* bits 24-31: refit cargo type.
* @param p2 User
* @param text unused
* @param eid vehicle type being built.
* @param use_free_vehicles use free vehicles when building the vehicle.
* @param cargo refit cargo type.
* @param client_id User
* @return the cost of this operation or an error
*/
CommandCost CmdBuildVehicle(DoCommandFlag flags, TileIndex tile, uint32 p1, uint32 p2, const std::string &text)
CommandCost CmdBuildVehicle(DoCommandFlag flags, TileIndex tile, EngineID eid, bool use_free_vehicles, CargoID cargo, ClientID client_id)
{
/* Elementary check for valid location. */
if (!IsDepotTile(tile) || !IsTileOwner(tile, _current_company)) return CMD_ERROR;
@ -95,11 +93,9 @@ CommandCost CmdBuildVehicle(DoCommandFlag flags, TileIndex tile, uint32 p1, uint
VehicleType type = GetDepotVehicleType(tile);
/* Validate the engine type. */
EngineID eid = GB(p1, 0, 16);
if (!IsEngineBuildable(eid, type, _current_company)) return_cmd_error(STR_ERROR_RAIL_VEHICLE_NOT_AVAILABLE + type);
/* Validate the cargo type. */
CargoID cargo = GB(p1, 24, 8);
if (cargo >= NUM_CARGO && cargo != CT_INVALID) return CMD_ERROR;
const Engine *e = Engine::Get(eid);
@ -140,10 +136,10 @@ CommandCost CmdBuildVehicle(DoCommandFlag flags, TileIndex tile, uint32 p1, uint
Vehicle *v = nullptr;
switch (type) {
case VEH_TRAIN: value.AddCost(CmdBuildRailVehicle(subflags, tile, e, GB(p1, 16, 8), &v)); break;
case VEH_ROAD: value.AddCost(CmdBuildRoadVehicle(subflags, tile, e, GB(p1, 16, 8), &v)); break;
case VEH_SHIP: value.AddCost(CmdBuildShip (subflags, tile, e, GB(p1, 16, 8), &v)); break;
case VEH_AIRCRAFT: value.AddCost(CmdBuildAircraft (subflags, tile, e, GB(p1, 16, 8), &v)); break;
case VEH_TRAIN: value.AddCost(CmdBuildRailVehicle(subflags, tile, e, use_free_vehicles, &v)); break;
case VEH_ROAD: value.AddCost(CmdBuildRoadVehicle(subflags, tile, e, &v)); break;
case VEH_SHIP: value.AddCost(CmdBuildShip (subflags, tile, e, &v)); break;
case VEH_AIRCRAFT: value.AddCost(CmdBuildAircraft (subflags, tile, e, &v)); break;
default: NOT_REACHED(); // Safe due to IsDepotTile()
}
@ -176,14 +172,14 @@ CommandCost CmdBuildVehicle(DoCommandFlag flags, TileIndex tile, uint32 p1, uint
if (v->IsPrimaryVehicle()) {
GroupStatistics::CountVehicle(v, 1);
if (!(subflags & DC_AUTOREPLACE)) OrderBackup::Restore(v, p2);
if (!(subflags & DC_AUTOREPLACE)) OrderBackup::Restore(v, client_id);
}
}
/* If we are not in DC_EXEC undo everything */
if (flags != subflags) {
Command<CMD_SELL_VEHICLE>::Do(DC_EXEC, 0, v->index, 0, {});
Command<CMD_SELL_VEHICLE>::Do(DC_EXEC, 0, v->index, false, false, INVALID_CLIENT_ID);
}
}
@ -197,17 +193,16 @@ CommandCost CmdBuildVehicle(DoCommandFlag flags, TileIndex tile, uint32 p1, uint
* Sell a vehicle.
* @param tile unused.
* @param flags for command.
* @param p1 various bitstuffed data.
* bits 0-19: vehicle ID being sold.
* bits 20-30: vehicle type specific bits passed on to the vehicle build functions.
* bit 31: make a backup of the vehicle's order (if an engine).
* @param p2 User.
* @aram v_id vehicle ID being sold.
* @param sell_chain sell the vehicle and all vehicles following it in the chain.
* @param backup_order make a backup of the vehicle's order (if an engine).
* @param client_id User.
* @param text unused.
* @return the cost of this operation or an error.
*/
CommandCost CmdSellVehicle(DoCommandFlag flags, TileIndex tile, uint32 p1, uint32 p2, const std::string &text)
CommandCost CmdSellVehicle(DoCommandFlag flags, TileIndex tile, VehicleID v_id, bool sell_chain, bool backup_order, ClientID client_id)
{
Vehicle *v = Vehicle::GetIfValid(GB(p1, 0, 20));
Vehicle *v = Vehicle::GetIfValid(v_id);
if (v == nullptr) return CMD_ERROR;
Vehicle *front = v->First();
@ -220,22 +215,22 @@ CommandCost CmdSellVehicle(DoCommandFlag flags, TileIndex tile, uint32 p1, uint3
if (!front->IsStoppedInDepot()) return_cmd_error(STR_ERROR_TRAIN_MUST_BE_STOPPED_INSIDE_DEPOT + front->type);
/* Can we actually make the order backup, i.e. are there enough orders? */
if (p1 & MAKE_ORDER_BACKUP_FLAG &&
if (backup_order &&
front->orders.list != nullptr &&
!front->orders.list->IsShared() &&
!Order::CanAllocateItem(front->orders.list->GetNumOrders())) {
/* Only happens in exceptional cases when there aren't enough orders anyhow.
* Thus it should be safe to just drop the orders in that case. */
p1 &= ~MAKE_ORDER_BACKUP_FLAG;
backup_order = false;
}
if (v->type == VEH_TRAIN) {
ret = CmdSellRailWagon(flags, v, GB(p1, 20, 12), p2);
ret = CmdSellRailWagon(flags, v, sell_chain, backup_order, client_id);
} else {
ret = CommandCost(EXPENSES_NEW_VEHICLES, -front->value);
if (flags & DC_EXEC) {
if (front->IsPrimaryVehicle() && p1 & MAKE_ORDER_BACKUP_FLAG) OrderBackup::Backup(front, p2);
if (front->IsPrimaryVehicle() && backup_order) OrderBackup::Backup(front, client_id);
delete front;
}
}
@ -694,7 +689,7 @@ CommandCost CmdDepotSellAllVehicles(DoCommandFlag flags, TileIndex tile, uint32
CommandCost last_error = CMD_ERROR;
bool had_success = false;
for (uint i = 0; i < list.size(); i++) {
CommandCost ret = Command<CMD_SELL_VEHICLE>::Do(flags, tile, list[i]->index | (1 << 20), 0, {});
CommandCost ret = Command<CMD_SELL_VEHICLE>::Do(flags, tile, list[i]->index, true, false, INVALID_CLIENT_ID);
if (ret.Succeeded()) {
cost.AddCost(ret);
had_success = true;
@ -872,11 +867,11 @@ CommandCost CmdCloneVehicle(DoCommandFlag flags, TileIndex tile, uint32 p1, uint
DoCommandFlag build_flags = flags;
if ((flags & DC_EXEC) && !v->IsPrimaryVehicle()) build_flags |= DC_AUTOREPLACE;
CommandCost cost = Command<CMD_BUILD_VEHICLE>::Do(build_flags, tile, v->engine_type | (1 << 16) | (CT_INVALID << 24), 0, {});
CommandCost cost = Command<CMD_BUILD_VEHICLE>::Do(build_flags, tile, v->engine_type, false, CT_INVALID, INVALID_CLIENT_ID);
if (cost.Failed()) {
/* Can't build a part, then sell the stuff we already made; clear up the mess */
if (w_front != nullptr) Command<CMD_SELL_VEHICLE>::Do(flags, w_front->tile, w_front->index | (1 << 20), 0, {});
if (w_front != nullptr) Command<CMD_SELL_VEHICLE>::Do(flags, w_front->tile, w_front->index, true, false, INVALID_CLIENT_ID);
return cost;
}
@ -896,8 +891,8 @@ CommandCost CmdCloneVehicle(DoCommandFlag flags, TileIndex tile, uint32 p1, uint
if (result.Failed()) {
/* The train can't be joined to make the same consist as the original.
* Sell what we already made (clean up) and return an error. */
Command<CMD_SELL_VEHICLE>::Do(flags, w_front->tile, w_front->index | 1 << 20, 0, {});
Command<CMD_SELL_VEHICLE>::Do(flags, w_front->tile, w->index | 1 << 20, 0, {});
Command<CMD_SELL_VEHICLE>::Do(flags, w_front->tile, w_front->index, true, false, INVALID_CLIENT_ID);
Command<CMD_SELL_VEHICLE>::Do(flags, w_front->tile, w->index, true, false, INVALID_CLIENT_ID);
return result; // return error and the message returned from CMD_MOVE_RAIL_VEHICLE
}
} else {
@ -978,7 +973,7 @@ CommandCost CmdCloneVehicle(DoCommandFlag flags, TileIndex tile, uint32 p1, uint
CommandCost result = Command<CMD_CLONE_ORDER>::Do(flags, 0, w_front->index | (p2 & 1 ? CO_SHARE : CO_COPY) << 30, v_front->index, {});
if (result.Failed()) {
/* The vehicle has already been bought, so now it must be sold again. */
Command<CMD_SELL_VEHICLE>::Do(flags, w_front->tile, w_front->index | 1 << 20, 0, {});
Command<CMD_SELL_VEHICLE>::Do(flags, w_front->tile, w_front->index, true, false, INVALID_CLIENT_ID);
return result;
}
@ -989,7 +984,7 @@ CommandCost CmdCloneVehicle(DoCommandFlag flags, TileIndex tile, uint32 p1, uint
* check whether the company has enough money manually. */
if (!CheckCompanyHasMoney(total_cost)) {
/* The vehicle has already been bought, so now it must be sold again. */
Command<CMD_SELL_VEHICLE>::Do(flags, w_front->tile, w_front->index | 1 << 20, 0, {});
Command<CMD_SELL_VEHICLE>::Do(flags, w_front->tile, w_front->index, true, false, INVALID_CLIENT_ID);
return total_cost;
}
}

View File

@ -12,8 +12,8 @@
#include "command_type.h"
CommandProc CmdBuildVehicle;
CommandProc CmdSellVehicle;
CommandCost CmdBuildVehicle(DoCommandFlag flags, TileIndex tile, EngineID eid, bool use_free_vehicles, CargoID cargo, ClientID client_id);
CommandCost CmdSellVehicle(DoCommandFlag flags, TileIndex tile, VehicleID v_id, bool sell_chain, bool backup_order, ClientID client_id);
CommandProc CmdRefitVehicle;
CommandProc CmdSendVehicleToDepot;
CommandProc CmdChangeServiceInt;