diff --git a/rts/Lua/LuaHandleSynced.cpp b/rts/Lua/LuaHandleSynced.cpp index 904a92abea3..175429de6e7 100644 --- a/rts/Lua/LuaHandleSynced.cpp +++ b/rts/Lua/LuaHandleSynced.cpp @@ -1219,6 +1219,37 @@ bool CSyncedLuaHandle::AllowResourceTransfer(int oldTeam, int newTeam, const cha return allow; } +/*** Called when excess resources are added. + * Accumulates all excesses within a single gameframe. + * + * @function SyncedCallins:TeamResourceExcess + * @param teamID integer + * @param amountMetal number + * @param amountEnergy number + * @return boolean whether or not Lua handled the event + */ +bool CSyncedLuaHandle::TeamResourceExcess(int teamID, const SResourcePack& excess) +{ + RECOIL_DETAILED_TRACY_ZONE; + LUA_CALL_IN_CHECK(L, true); + luaL_checkstack(L, 3 + excess.MAX_RESOURCES, __func__); + + static const LuaHashString cmdStr(__func__); + if (!cmdStr.GetGlobalFunc(L)) + return false; + + lua_pushnumber(L, teamID); + for (int i = 0; i < excess.MAX_RESOURCES; ++i) + lua_pushnumber(L, excess[i]); + + if (!RunCallIn(L, cmdStr, 1 + excess.MAX_RESOURCES, 1)) + return false; + + const bool handled = luaL_optboolean(L, -1, false); + lua_pop(L, 1); + return handled; +} + /*** Determines if this unit can be controlled directly in FPS view. * diff --git a/rts/Lua/LuaHandleSynced.h b/rts/Lua/LuaHandleSynced.h index a5509285da7..cb31fce2ea0 100644 --- a/rts/Lua/LuaHandleSynced.h +++ b/rts/Lua/LuaHandleSynced.h @@ -59,6 +59,8 @@ class CSyncedLuaHandle : public CLuaHandle bool CommandFallback(const CUnit* unit, const Command& cmd) override; bool AllowCommand(const CUnit* unit, const Command& cmd, int playerNum, bool fromSynced, bool fromLua) override; + bool TeamResourceExcess(int teamID, const SResourcePack& excess) override; + std::pair AllowUnitCreation(const UnitDef* unitDef, const CUnit* builder, const BuildInfo* buildInfo) override; bool AllowUnitTransfer(const CUnit* unit, int newTeam, bool capture) override; bool AllowUnitBuildStep(const CUnit* builder, const CUnit* unit, float part) override; diff --git a/rts/Sim/Misc/Team.cpp b/rts/Sim/Misc/Team.cpp index d3f70b65eb1..9257cca5a06 100644 --- a/rts/Sim/Misc/Team.cpp +++ b/rts/Sim/Misc/Team.cpp @@ -45,6 +45,7 @@ CR_REG_METADATA(CTeam, ( CR_MEMBER(resPrevExpense), CR_MEMBER(resShare), CR_MEMBER(resDelayedShare), + CR_MEMBER(resOverflow), CR_MEMBER(resSent), CR_MEMBER(resPrevSent), CR_MEMBER(resReceived), @@ -156,7 +157,7 @@ void CTeam::AddMetal(float amount, bool useIncomeMultiplier) if (res.metal <= resStorage.metal) return; - resDelayedShare.metal += (res.metal - resStorage.metal); + resOverflow.metal += (res.metal - resStorage.metal); res.metal = resStorage.metal; } @@ -170,7 +171,7 @@ void CTeam::AddEnergy(float amount, bool useIncomeMultiplier) resIncome.energy += amount; if (res.energy > resStorage.energy) { - resDelayedShare.energy += (res.energy - resStorage.energy); + resOverflow.energy += (res.energy - resStorage.energy); res.energy = resStorage.energy; } } @@ -196,7 +197,7 @@ void CTeam::AddResources(SResourcePack amount, bool useIncomeMultiplier) if (res[i] <= resStorage[i]) continue; - resDelayedShare[i] += (res[i] - resStorage[i]); + resOverflow[i] += (res[i] - resStorage[i]); res[i] = resStorage[i]; } } @@ -316,6 +317,32 @@ void CTeam::ResetResourceState() resPrevReceived.energy = resReceived.energy; resReceived.energy = 0.0f; } +void CTeam::HandleFrameExcess() +{ + /* A team having no excess is also useful information that is a bit annoying + * to detect otherwise, but we make a conservative perf overhead guess. + * Perhaps we will have the leeway to remove this optimisation in the future. */ + if (resOverflow.empty()) + return; + + /* Having a metaaccumulator and relegating the "real" native behaviour to + * slowupdate rather than doing it immediately here is mostly for legacy, + * but it's also consistent with how most existing resource mechanics, + * in particular sharing excess to allies aka the red slider, mostly work + * per slowupdate. */ + if (!eventHandler.TeamResourceExcess(teamNum, resOverflow)) + resDelayedShare += resOverflow; + + resOverflow = 0.0f; +} + +void CTeam::GameFramePost(int frameNum) +{ + /* Excess being accumulated across a frame (rather than called for every resource income + * instance) is for perf, as you can have e.g. tens of thousands of windgens or whatnot. */ + HandleFrameExcess(); +} + void CTeam::SlowUpdate() { RECOIL_DETAILED_TRACY_ZONE; diff --git a/rts/Sim/Misc/Team.h b/rts/Sim/Misc/Team.h index 6bc74267dc9..4b6258d2918 100644 --- a/rts/Sim/Misc/Team.h +++ b/rts/Sim/Misc/Team.h @@ -24,6 +24,7 @@ class CTeam : public TeamBase void ResetResourceState(); void SlowUpdate(); + void GameFramePost(int frameNum); bool HaveResources(const SResourcePack& amount) const; void AddResources(SResourcePack res, bool useIncomeMultiplier = true); @@ -73,6 +74,9 @@ class CTeam : public TeamBase void AddUnit(CUnit* unit, AddType type); void RemoveUnit(CUnit* unit, RemoveType type); +private: + void HandleFrameExcess(); + public: int teamNum; unsigned int numUnits; // number of units this team controls @@ -89,6 +93,7 @@ class CTeam : public TeamBase SResourcePack resExpense, resPrevExpense; SResourcePack resShare; SResourcePack resDelayedShare; //< excess that might be shared next SlowUpdate + SResourcePack resOverflow; //< accumulates overflow over a gameframe SResourcePack resSent, resPrevSent; SResourcePack resReceived, resPrevReceived; SResourcePack resPrevExcess; diff --git a/rts/Sim/Misc/TeamHandler.cpp b/rts/Sim/Misc/TeamHandler.cpp index ed9f28eeb1e..1a4124712d2 100644 --- a/rts/Sim/Misc/TeamHandler.cpp +++ b/rts/Sim/Misc/TeamHandler.cpp @@ -116,6 +116,12 @@ void CTeamHandler::SetDefaultStartPositions(const CGameSetup* setup) void CTeamHandler::GameFrame(int frameNum) { RECOIL_DETAILED_TRACY_ZONE; + + for (int a = 0; a < ActiveTeams(); ++a) { + // a bit of a callin mismatch but teamHandler runs ~last within a GameFrame anyway + teams[a].GameFramePost(frameNum); + } + if ((frameNum % TEAM_SLOWUPDATE_RATE) != 0) return; diff --git a/rts/System/EventClient.h b/rts/System/EventClient.h index 03a4a4b4fcd..24a5cbb1cf8 100644 --- a/rts/System/EventClient.h +++ b/rts/System/EventClient.h @@ -35,6 +35,7 @@ struct BuildInfo; struct FeatureDef; class LuaMaterial; struct WeaponDef; +struct SResourcePack; #ifndef zipFile // might be defined through zip.h already @@ -122,6 +123,8 @@ class CEventClient virtual void TeamDied(int teamID) {} virtual void TeamChanged(int teamID) {} + virtual bool TeamResourceExcess(int teamID, const SResourcePack& excess) { return false; } + virtual void PlayerChanged(int playerID) {} virtual void PlayerAdded(int playerID) {} virtual void PlayerRemoved(int playerID, int reason) {} diff --git a/rts/System/EventHandler.cpp b/rts/System/EventHandler.cpp index bd48ef46c38..1ed2dbfda0c 100644 --- a/rts/System/EventHandler.cpp +++ b/rts/System/EventHandler.cpp @@ -580,6 +580,12 @@ void CEventHandler::TeamDied(int teamID) ITERATE_EVENTCLIENTLIST(TeamDied, teamID); } +bool CEventHandler::TeamResourceExcess(int teamID, const SResourcePack &excess) +{ + ZoneScoped; + return ControlIterateDefFalse(listTeamResourceExcess, &CEventClient::TeamResourceExcess, teamID, excess); +} + void CEventHandler::TeamChanged(int teamID) { ZoneScoped; diff --git a/rts/System/EventHandler.h b/rts/System/EventHandler.h index c72ba079a9a..a78e9f4f627 100644 --- a/rts/System/EventHandler.h +++ b/rts/System/EventHandler.h @@ -17,6 +17,7 @@ struct Command; struct BuildInfo; class LuaMaterial; struct WeaponDef; +struct SResourcePack; class CEventHandler { @@ -60,6 +61,8 @@ class CEventHandler void TeamDied(int teamID); void TeamChanged(int teamID); + bool TeamResourceExcess(int teamID, const SResourcePack& excess); + void PlayerChanged(int playerID); void PlayerAdded(int playerID); void PlayerRemoved(int playerID, int reason); diff --git a/rts/System/Events.def b/rts/System/Events.def index 5cf0440b4e9..fcbd6ecc2ac 100644 --- a/rts/System/Events.def +++ b/rts/System/Events.def @@ -31,8 +31,11 @@ SETUP_EVENT(GamePaused, MANAGED_BIT) SETUP_EVENT(GameFrame, MANAGED_BIT) SETUP_EVENT(GameFramePost, MANAGED_BIT) + SETUP_EVENT(TeamDied, MANAGED_BIT) SETUP_EVENT(TeamChanged, MANAGED_BIT) + SETUP_EVENT(TeamResourceExcess, MANAGED_BIT | CONTROL_BIT) + SETUP_EVENT(PlayerChanged, MANAGED_BIT | UNSYNCED_BIT) SETUP_EVENT(PlayerAdded, MANAGED_BIT | UNSYNCED_BIT) SETUP_EVENT(PlayerRemoved, MANAGED_BIT | UNSYNCED_BIT)