Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
147 changes: 101 additions & 46 deletions rts/Game/SyncedGameCommands.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
#include "System/FileSystem/SimpleParser.h"
#include "System/Log/ILog.h"
#include "System/SafeUtil.h"
#include "System/StringUtil.h"

#include <string>
#include <vector>
Expand Down Expand Up @@ -175,46 +176,27 @@ class NoCostActionExecutor : public ISyncedActionExecutor {
};


class GiveActionExecutor : public ISyncedActionExecutor {
public:
GiveActionExecutor() : ISyncedActionExecutor(
"Give",
"Places one or multiple units of a single or multiple types "
"on the map, instantly; by default belonging to your own team",
true
) {
}

bool Execute(const SyncedAction& action) const final {
// not for autohosts
if (!playerHandler.IsValidPlayer(action.GetPlayerID()))
return false;
unitLoader->ParseAndExecuteGiveUnitsCommand(CSimpleParser::Tokenize(action.GetArgs(), 0), playerHandler.Player(action.GetPlayerID())->team);
return true;
}
};


class BaseDestroyActionExecutor : public ISyncedActionExecutor {
public:
BaseDestroyActionExecutor(const std::string& command, const std::string& description, bool runDeathScript)
: ISyncedActionExecutor(command, description, true), runDeathScript(runDeathScript) {}

bool Execute(const SyncedAction& action) const {
const std::vector<std::string>& args = CSimpleParser::Tokenize(action.GetArgs(), 0);
if (args.size() == 0) {
if (args.empty()) {
LOG_L(L_WARNING, "not enough arguments (\"/%s <unitID:int...>\")", this->GetCommand().c_str());
return false;
}

LOG("[%s] unitIDs: %s", this->GetCommand().c_str(), action.GetArgs().c_str());
for (const auto& it : args) {
int unitId = StringToInt<int>(it);
CUnit *unit = unitHandler.GetUnit(unitId);
CUnit* unit = unitHandler.GetUnit(unitId);

if (unit != nullptr) {
unit->KillUnit(nullptr, false, !this->runDeathScript, -CSolidObject::DAMAGE_KILLED_CHEAT);
} else {
}
else {
LOG("[%s] Wrong unitID: %i", this->GetCommand().c_str(), unitId);
}
}
Expand All @@ -225,6 +207,7 @@ class BaseDestroyActionExecutor : public ISyncedActionExecutor {
bool runDeathScript;
};


class DestroyActionExecutor : public BaseDestroyActionExecutor {
public:
DestroyActionExecutor() : BaseDestroyActionExecutor("Destroy", "Destroys one or multiple units by unitID immediately", true) {}
Expand Down Expand Up @@ -500,17 +483,52 @@ class AtmActionExecutor : public ISyncedActionExecutor {
};


class SkipActionExecutor : public ISyncedActionExecutor {
public:
SkipActionExecutor() : ISyncedActionExecutor("Skip", "Fast-forwards to a given frame, or stops fast-forwarding") {
}

bool Execute(const SyncedAction& action) const final {
if (action.GetArgs().find_first_of("start") == 0) {
std::istringstream buf(action.GetArgs().substr(6));
int targetFrame;
buf >> targetFrame;
game->StartSkip(targetFrame);
LOG("Skipping to frame %i", targetFrame);
}
else if (action.GetArgs() == "end") {
game->EndSkip();
LOG("Skip finished");
} else {
LOG_L(L_WARNING, "/%s: wrong syntax", GetCommand().c_str());
}
return true;
}
};

class TakeActionExecutor : public ISyncedActionExecutor {
public:
TakeActionExecutor() : ISyncedActionExecutor(
"Take",
"Transfers all units of allied teams without any "
"active players to the team of the issuing player"
) {
}
"[DEPRECATED] Transfers all units of allied teams without any active players to the team of the issuing player. Use Lua handlers instead."
) {}

bool Execute(const SyncedAction& action) const final {
const CPlayer* actionPlayer = playerHandler.Player(action.GetPlayerID());
// First, try to let Lua handle the command
const std::string& args = action.GetArgs();
const int playerID = action.GetPlayerID();

// Check if Lua has registered a handler for this command
if (eventHandler.SyncedActionFallback("take" + (args.empty() ? "" : " " + args), playerID)) {
// Lua handled it, we're done
return true;
}

// Lua didn't handle it, use fallback implementation
// @deprecated This fallback is deprecated and will be removed in future versions
LOG_L(L_WARNING, "/take command not implemented by Lua, using deprecated fallback");

const CPlayer* actionPlayer = playerHandler.Player(playerID);

if (actionPlayer->spectator && !gs->cheatEnabled)
return false;
Expand Down Expand Up @@ -544,25 +562,64 @@ class TakeActionExecutor : public ISyncedActionExecutor {
};


class SkipActionExecutor : public ISyncedActionExecutor {
class CaptureActionExecutor : public ISyncedActionExecutor {
public:
SkipActionExecutor() : ISyncedActionExecutor("Skip", "Fast-forwards to a given frame, or stops fast-forwarding") {
}
CaptureActionExecutor() : ISyncedActionExecutor(
"Capture",
"[DEPRECATED] Captures units and structures from enemy teams. Use Lua handlers instead."
) {}

bool Execute(const SyncedAction& action) const final {
if (action.GetArgs().find_first_of("start") == 0) {
std::istringstream buf(action.GetArgs().substr(6));
int targetFrame;
buf >> targetFrame;
game->StartSkip(targetFrame);
LOG("Skipping to frame %i", targetFrame);
// First, try to let Lua handle the command
const std::string& args = action.GetArgs();
const int playerID = action.GetPlayerID();

// Check if Lua has registered a handler for this command
if (eventHandler.SyncedActionFallback("capture" + (args.empty() ? "" : " " + args), playerID)) {
// Lua handled it, we're done
return true;
}
else if (action.GetArgs() == "end") {
game->EndSkip();
LOG("Skip finished");
} else {
LOG_L(L_WARNING, "/%s: wrong syntax", GetCommand().c_str());

// Lua didn't handle it, use fallback implementation
// @deprecated This fallback is deprecated and will be removed in future versions
LOG_L(L_WARNING, "/capture command not implemented by Lua, using deprecated fallback");

const CPlayer* actionPlayer = playerHandler.Player(playerID);
if (actionPlayer->spectator && !gs->cheatEnabled)
return false;

if (!game->playing)
return true;

// Basic fallback: capture from all enemy teams
for (int a = 0; a < teamHandler.ActiveTeams(); ++a) {
if (teamHandler.AlliedTeams(a, actionPlayer->team))
continue;

// Transfer all units from enemy team
const auto& teamUnits = unitHandler.GetUnitsByTeam(a);
for (CUnit* unit : teamUnits) {
if (unit != nullptr && !unit->isDead) {
unit->ChangeTeam(actionPlayer->team, static_cast<int>(CUnit::ChangeTeamReasonCpp::CAPTURED));
}
}

// Transfer resources
CTeam* sourceTeam = teamHandler.Team(a);
CTeam* destTeam = teamHandler.Team(actionPlayer->team);

if (sourceTeam != nullptr && destTeam != nullptr) {
if (sourceTeam->res.metal > 0.0f) {
destTeam->res.metal += sourceTeam->res.metal;
sourceTeam->res.metal = 0.0f;
}
if (sourceTeam->res.energy > 0.0f) {
destTeam->res.energy += sourceTeam->res.energy;
sourceTeam->res.energy = 0.0f;
}
}
}

return true;
}
};
Expand All @@ -583,7 +640,6 @@ void SyncedGameCommands::AddDefaultActionExecutors()
AddActionExecutor(AllocActionExecutor<GodModeActionExecutor>());
AddActionExecutor(AllocActionExecutor<GlobalLosActionExecutor>());
AddActionExecutor(AllocActionExecutor<NoCostActionExecutor>());
AddActionExecutor(AllocActionExecutor<GiveActionExecutor>());
AddActionExecutor(AllocActionExecutor<DestroyActionExecutor>());
AddActionExecutor(AllocActionExecutor<RemoveActionExecutor>());
AddActionExecutor(AllocActionExecutor<NoSpectatorChatActionExecutor>());
Expand All @@ -595,10 +651,9 @@ void SyncedGameCommands::AddDefaultActionExecutors()
AddActionExecutor(AllocActionExecutor<LuaGaiaActionExecutor>());
AddActionExecutor(AllocActionExecutor<DesyncActionExecutor>());
AddActionExecutor(AllocActionExecutor<AtmActionExecutor>());
if (modInfo.allowTake)
AddActionExecutor(AllocActionExecutor<TakeActionExecutor>());

AddActionExecutor(AllocActionExecutor<SkipActionExecutor>());
AddActionExecutor(AllocActionExecutor<TakeActionExecutor>());
AddActionExecutor(AllocActionExecutor<CaptureActionExecutor>());
}


Expand Down
11 changes: 8 additions & 3 deletions rts/Lua/LuaHandleSynced.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -721,13 +721,19 @@ std::pair <bool, bool> CSyncedLuaHandle::AllowUnitCreation(
* @param oldTeam integer
* @param newTeam integer
* @param capture boolean
* @return boolean whether or not the transfer is permitted.
* @return false to disallow unit transfer
*/
bool CSyncedLuaHandle::AllowUnitTransfer(const CUnit* unit, int newTeam, bool capture)
{
RECOIL_DETAILED_TRACY_ZONE;
LUA_CALL_IN_CHECK(L, true);
luaL_checkstack(L, 7, __func__);
luaL_checkstack(L, 2 + 5, __func__);

static bool s_warnedAllowUnitTransfer = false;
if (!s_warnedAllowUnitTransfer) {
LOG_L(L_WARNING, "SyncedCallins:AllowUnitTransfer is deprecated and will be removed in a future engine version. Use Lua handlers that do not depend on engine-side reasons.");
s_warnedAllowUnitTransfer = true;
}

static const LuaHashString cmdStr(__func__);
if (!cmdStr.GetGlobalFunc(L))
Expand All @@ -749,7 +755,6 @@ bool CSyncedLuaHandle::AllowUnitTransfer(const CUnit* unit, int newTeam, bool ca
return allow;
}


/*** Called just before a unit progresses its build percentage.
*
* @function SyncedCallins:AllowUnitBuildStep
Expand Down
4 changes: 2 additions & 2 deletions rts/Lua/LuaHandleSynced.h
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ class CSyncedLuaHandle : public CLuaHandle
bool AllowUnitTransportLoad(const CUnit* transporter, const CUnit* transportee, const float3& loadPos, bool allowed) override;
bool AllowUnitTransportUnload(const CUnit* transporter, const CUnit* transportee, const float3& unloadPos, bool allowed) override;
bool AllowUnitCloak(const CUnit* unit, const CUnit* enemy) override;
bool AllowUnitDecloak(const CUnit* unit, const CSolidObject* object, const CWeapon* weapon) override;
bool AllowUnitDecloak(const CUnit* unit, const CSolidObject* o, const CWeapon* w) override;
bool AllowUnitKamikaze(const CUnit* unit, const CUnit* target, bool allowed) override;
bool AllowFeatureCreation(const FeatureDef* featureDef, int allyTeamID, const float3& pos) override;
bool AllowFeatureBuildStep(const CUnit* builder, const CFeature* feature, float part) override;
Expand Down Expand Up @@ -122,7 +122,7 @@ class CSyncedLuaHandle : public CLuaHandle
const float3& hitPos
) override;

bool SyncedActionFallback(const std::string& line, int playerID) override;
bool SyncedActionFallback(const std::string& msg, int playerID) override;

protected:
CSyncedLuaHandle(CSplitLuaHandle* base, const std::string& name, int order);
Expand Down
35 changes: 22 additions & 13 deletions rts/Lua/LuaSyncedCtrl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@
#include "System/EventHandler.h"
#include "System/ObjectDependenceTypes.h"
#include "System/Log/ILog.h"
#include "LuaCallInCheck.h"

using std::max;

Expand Down Expand Up @@ -1842,39 +1843,47 @@ int LuaSyncedCtrl::DestroyUnit(lua_State* L)
* @function Spring.TransferUnit
* @param unitID integer
* @param newTeamID integer
* @param given boolean? (Default: `true`) if false, the unit is captured.
* @param given boolean (Default: `true`) if false, the unit is captured.
* @return nil
*/
int LuaSyncedCtrl::TransferUnit(lua_State* L)
{
CheckAllowGameChanges(L);
CUnit* unit = ParseUnit(L, __func__, 1);
const int args = lua_gettop(L);
if (args < 2) //unitID, newTeam, [given]
luaL_error(L, "Incorrect arguments to Spring.TransferUnit(unitID, newTeam, [given])");

CUnit* unit = ParseUnit(L, __func__, 1);
if (unit == nullptr)
return 0;

if (inTransferUnit >= MAX_CMD_RECURSION_DEPTH) {
luaL_error(L, "TransferUnit() recursion is not permitted, max depth: %d", MAX_CMD_RECURSION_DEPTH);
return 0;
}

const int newTeam = luaL_checkint(L, 2);
const bool given = (args >= 3) ? lua_toboolean(L, 3) : true;

if (!teamHandler.IsValidTeam(newTeam))
return 0;

const CTeam* team = teamHandler.Team(newTeam);
if (team == nullptr)
return 0;

bool given = true;
if (FullCtrl(L) && lua_isboolean(L, 3))
given = lua_toboolean(L, 3);

if (inTransferUnit >= MAX_CMD_RECURSION_DEPTH)
luaL_error(L, "TransferUnit() recursion is not permitted, max depth: %d", MAX_CMD_RECURSION_DEPTH);
// team is full
if (team->AtUnitLimit())
return 0;

++ inTransferUnit;
inTransferUnit++;
ASSERT_SYNCED(unit->id);
ASSERT_SYNCED((int)newTeam);
ASSERT_SYNCED(given);
unit->ChangeTeam(newTeam, given ? CUnit::ChangeGiven
: CUnit::ChangeCaptured);
-- inTransferUnit;
// given==true -> capture=false
const bool result = unit->ChangeTeam(newTeam, !given);
(void)result;
inTransferUnit--;

return 0;
}

Expand Down
Loading