Skip to content

refactor: move /take and /give to LUA#2423

Closed
keithharvey wants to merge 13 commits into
beyond-all-reason:masterfrom
keithharvey:move_take
Closed

refactor: move /take and /give to LUA#2423
keithharvey wants to merge 13 commits into
beyond-all-reason:masterfrom
keithharvey:move_take

Conversation

@keithharvey

@keithharvey keithharvey commented Jul 1, 2025

Copy link
Copy Markdown
Contributor

Work done

  • Add TakeActionExecutor class to handle /take command forwarding
  • Register /take command in synced_commands.json to forward to Lua
  • Similarly, /give was moved to LUA
  • Remove old C++ /take command implementation
  • Improve modularity by delegating command logic to Lua gadgets
  • introduced a reason pass through for both ChangeTeam and AllowUnitTransfer, with the engine. The engine retains knowledge of only the ChangeTeam actions it initiates with its own enum.

This change allows the /take command to be implemented in Lua for better modularity and easier maintenance, while maintaining the same user interface. It also adds a reason to pass through to AllowUnitTransfer.

Testing

Ensured /take still worked in skirmish

@keithharvey keithharvey changed the title feat: Add TakeActionExecutor for /take command forwarding to Lua feat: Move /take to Lua Jul 1, 2025
@keithharvey keithharvey marked this pull request as draft July 1, 2025 06:55

@sprunk sprunk left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Needs a replacement Lua gadget to be put into examples or basecontent.

Comment thread rts/Lua/LuaHandleSynced.cpp
Comment thread rts/Game/SyncedGameCommands.cpp Outdated
@keithharvey

Copy link
Copy Markdown
Contributor Author

Needs a replacement Lua gadget to be put into examples or basecontent.

This was our conversation about a proxy to preserve /take or am I misunderstanding? I believe this comment is resolved.

@keithharvey keithharvey marked this pull request as ready for review July 3, 2025 01:55
@keithharvey

Copy link
Copy Markdown
Contributor Author

Removing draft from this PR since I believe it is now ready for testing/review.

- Add TakeActionExecutor class to handle /take command forwarding
- Register /take command in synced_commands.json to forward to Lua
- Remove old C++ /take command implementation
- Improve modularity by delegating command logic to Lua gadgets

This change allows the /take command to be implemented in Lua for better
modularity and easier maintenance, while maintaining the same user interface.
Gave C++ its own enum for only the transfer types it is responsible for. LUA handles the rest and C++ simply passes everything through the appropriate hooks
@keithharvey keithharvey changed the title feat: Move /take to Lua refactor: ChangeTeam/AllowUnitTransfer, move /take and /give to LUA Jul 3, 2025
@keithharvey keithharvey changed the title refactor: ChangeTeam/AllowUnitTransfer, move /take and /give to LUA refactor: reason added to ChangeTeam/AllowUnitTransfer, move /take and /give to LUA Jul 3, 2025
@keithharvey keithharvey requested a review from sprunk July 3, 2025 02:12
@keithharvey keithharvey changed the title refactor: reason added to ChangeTeam/AllowUnitTransfer, move /take and /give to LUA refactor: ChangeTeam/AllowUnitTransfer simplification, move /take and /give to LUA Jul 3, 2025
Comment thread rts/Lua/LuaSyncedCtrl.cpp
int LuaSyncedCtrl::TransferUnit(lua_State* L)
{
CheckAllowGameChanges(L);
const int args = lua_gettop(L);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a breaking change, in general we avoid breaking changes.

We'd instead make a new overload (in the documentation) that accepts the new type, and mark the previous as deprecated.

The old method would still work.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function should be 100% untouched.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So I do think we should eventually deprecate the existing hook. capture is a useless parameter compared with reason.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It can be deprecated when capture handling is moved to Lua.

Comment thread rts/Sim/Units/Unit.h
#include "Sim/Units/Scripts/LuaUnitScript.h"

// for profiler
#include "System/TimeProfiler.h"

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this necessary?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No sorry this was a me profiling during testing.

// capture failed
if (team == gu->myTeam) {
LOG_L(L_WARNING, "%s: Capture failed, unit type limit reached", unitDef->humanName.c_str());
eventHandler.LastMessagePosition(pos);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the removal of a callin invocation intended? (the new one does not contain it)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removing LastMessagePosition is probably ok, adding UnitTaken isn't. Generally leave the UnitBla callins as they were.

"description" : "Fast-forwards to a given frame, or stops fast-forwarding"
},
"take" : {
"spectator" : {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

these two files are automatically generated, they are only committed within the repository to provide some default data to site (when the user does not perform the task to refresh them).

class GiveActionExecutor : public ISyncedActionExecutor {
public:
GiveActionExecutor() : ISyncedActionExecutor(
"Give",

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mark it as deprecated in the description recommending the alternative instead.

TakeActionExecutor() : ISyncedActionExecutor(
"Take",
"Transfers all units of allied teams without any "
"active players to the team of the issuing player"

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mark it as deprecated in the description recommending the alternative instead.

Comment thread rts/Sim/Units/Unit.h Outdated
// For the full list of reasons, see 'GG.CHANGETEAM_REASON' in luarules/gadgets.lua
// The two enums should be kept in sync for shared values.
enum class ChangeTeamReasonCpp {
RECLAIMED = 0,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"Reclaim" is already the term for gathering resources from wrecks.

I don't think this needs to introduce a new value since the engine shouldn't do anything about it anyway.

Comment thread rts/Sim/Units/Unit.h Outdated
ChangeGiven,
ChangeCaptured
// C++-side reasons for changing team.
// For the full list of reasons, see 'GG.CHANGETEAM_REASON' in luarules/gadgets.lua

@sprunk sprunk Jul 10, 2025

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Engine generally shouldn't refer to Lua (even if it provides examples of its own). This is because Lua is 100% replaceable (i.e. there may not be GG.CHANGETEAM_REASON, a GG table, or even gadgets.lua for any particular game). If Lua gets to define something then it's none of engine's business, and if engine has to define it then that is the source of truth and it is Lua who should refer to the engine.

@keithharvey keithharvey Jul 25, 2025

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, yeah. It is just a pass through, but the engine does refer to this enum a few places and the values match. But your point is taken. What is the best way to expose this to LUA so it is upstream.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The enum should be kept as-is, AFAICT its only use is to pick which of the two irrelevant stats this affects, "units sent" or "units outcaptured". These stats are a larger topic, see #2041 but the tl;dr is that we can't add a 3rd stat anyway for protocol reasons, and they would ideally be moved to Lua sometime anyway.

case RemoveGiven: {
GetCurrentStats().unitsSent++;
} break;
case RemoveCaptured: {
GetCurrentStats().unitsOutCaptured++;
} break;

Lua doesn't need to have the reason passed via the callin, see #2423 (comment)

{
ZoneScoped;

bool allow = true;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

result.first and result.second are much worse names than allow and drop.

Comment thread rts/Sim/Units/Unit.cpp
Comment on lines +1517 to +1518
if (oldTeam == newTeam)
return true;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this just for the factory buildee transfer?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I put it in a state with logging in it where I noticed it was getting called a LOT, so wanted to add a guard clause to save some pointless work.

* @return false to disallow unit transfer
*/
bool CSyncedLuaHandle::AllowUnitTransfer(const CUnit* unit, int newTeam, bool capture)
bool CSyncedLuaHandle::AllowUnitTransfer(const CUnit* unit, int newTeam, int reason)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function should also be 100% untouched.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmm, that was a key reason for this patch. It allows us to have better information in downstream widgets to make decisions about allowing unit transfer.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about this? Should work without any engine changes.

-- take_gadget.lua
function HandleTakeRequest()
   -- ...
   Spring.SetUnitRulesParam(unitID, "transfer_reason", "take")
   Spring.TransferUnit(unitID, teamID)
   Spring.SetUnitRulesParam(unitID, "transfer_reason", nil)
-- another_wupget.lua
function wupget:UnitTaken(unitID, ..., capture) -- or gadget:AllowUnitTransfer
    local reason = Spring.GetUnitRulesParam(unitID, "transfer_reason")
    if reason == "take" then
      -- ...
    elseif reason == "betrayal" then
      -- ...
    elseif reason == nil then
      if capture then
        -- engine capture mechanic
      else
        -- transfer request from a player
      end
      -- alternatively, a very early layer gadget could inject these two as reasons in AllowTransfer if it doesn't find any, so that it's never nil anywhere else
  end
end

If you want a master list accessible to both widgets and gadgets, do something like this

-- master_list_gadget.lua
function Initialize()
  -- not yet supported, see engine #1057
  -- Spring.SetGameRulesParam("transfer_reasons", {"take", "betrayal"})

  -- current workaround, manual enumeration
  Spring.SetGameRulesParam("transfer_reasons_n", 2)
  Spring.SetGameRulesParam("transfer_reasons_1", "take")
  Spring.SetGameRulesParam("transfer_reasons_2", "betrayal")

Comment thread doc/site/data/unsynced_commands.json Outdated
Comment thread cont/base/springcontent/LuaGadgets/Gadgets/share_delayed.lua Outdated
@sprunk

sprunk commented Jul 10, 2025

Copy link
Copy Markdown
Collaborator

Needs a replacement Lua gadget to be put into examples or basecontent.

This was our conversation about a proxy to preserve /take or am I misunderstanding? I believe this comment is resolved.

Sorry, what I meant was that something similar to cmd_take.lua from your beyond-all-reason/Beyond-All-Reason#5446 should be put into basecontent, except with no BAR specific code (at a glance this means no GG.CHANGETEAM_REASON, the rest of it looks kosher).

@keithharvey keithharvey marked this pull request as draft August 5, 2025 21:47
Daniel Harvey and others added 3 commits August 6, 2025 19:31
This allows building on Apple Silicon Macs by automatically detecting ARM64
and using --platform linux/amd64 for Docker commands.
@keithharvey keithharvey changed the title refactor: ChangeTeam/AllowUnitTransfer simplification, move /take and /give to LUA refactor: team transfer Aug 7, 2025
@sprunk

sprunk commented Aug 7, 2025

Copy link
Copy Markdown
Collaborator

TeamTransferWithReason can be (and should be, because the engine doesn't do anything meaningful with the reason) in Lua, see #2423 (comment). At the moment I don't think there's any engine change needed to add Lua /take.

keithharvey and others added 5 commits August 8, 2025 01:39
I figure we want it to match the actual best-implementation we implement in bar. Should probably include the gadget hander stuff
- Remove prompts/team_transfer/ directory (13,341+ lines of explanatory content)
- Remove examples/lua2cpp_example.lua and lua2cpp_output.cpp (auto-generated examples)
- Remove cont/base/springcontent/LuaGadgets/Gadgets/team_transfer.lua (example implementation)
- Preserve core team transfer deprecation/fallback functionality in C++ files
- Keep auto-generated synced_commands.json changes for take/capture commands

This creates a clean functional branch with only the intentional team transfer
deprecation and fallback changes, removing all explanatory and example content.

Co-Authored-By: Keith Harvey <keithdanielharvey@gmail.com>
cleanup: remove noise files and auto-generated content
Comment thread .gitignore
@@ -133,6 +133,9 @@ progress.make
/installer/Springlobby/
/installer/*.exe

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove this

@@ -266,11 +266,12 @@ end
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

revert all of the cont/base/springcontent changes

@@ -1137,9 +1137,9 @@ function gadgetHandler:AllowUnitCreation(unitDefID, builderID, builderTeam, x, y
end


Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we just delete this. We don't need it downstream in BAR. SO just keep the deprecated call and delete this reason based one.

@keithharvey keithharvey changed the title refactor: team transfer refactor: move /take and /give to LUA Aug 14, 2025
@keithharvey

keithharvey commented Aug 14, 2025

Copy link
Copy Markdown
Contributor Author

My hunt for ChangeTeamReason led me to realize all of these code paths were related and that all transfers should be handled by LUA. This simplifies things greatly on the engine side, and doing it piecemeal seemed harder since the test grid was already huge, even for extremely limited engine changes to transfers.

Closing this in favor of the new PR, which has the new focus: #2528

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants