diff --git a/.github/workflows/generate-lua-library.yml b/.github/workflows/generate-lua-library.yml index d078e7c0b13..5436a61465f 100644 --- a/.github/workflows/generate-lua-library.yml +++ b/.github/workflows/generate-lua-library.yml @@ -43,6 +43,10 @@ jobs: working-directory: ./recoil/doc/site run: mise run lua_library + - name: Generate bifurcated Lua types + working-directory: ./recoil/doc/site + run: mise run lua_library_bifurcated + - name: Check out library repo uses: actions/checkout@v4 with: diff --git a/doc/site/mise.toml b/doc/site/mise.toml index 8604af17fac..5b3d5fec1cf 100644 --- a/doc/site/mise.toml +++ b/doc/site/mise.toml @@ -92,7 +92,7 @@ [tasks.lua_check] description = "Sanity check lua library with lua language server" - depends = ["lua_library"] + depends = ["lua_library", "lua_library_bifurcated"] tools.lua-language-server = "{{env.LUA_LANGUAGE_SERVER_VERSION}}" dir = "../../" run = [ @@ -107,6 +107,20 @@ "lua-doc-extractor --src \"{{vars.lua_doc_paths}}\" --dest {{vars.lua_doc_gen_dest}} --repo \"${LUA_DOC_EXTRACTOR_SOURCE_REF}\"" ] + [tasks.lua_library_bifurcated] + description = "Generate SpringShared, SpringSynced, and SpringUnsynced context types" + depends = ["lua_library"] + tools."npm:lua-doc-extractor" = "{{env.LUA_DOC_EXTRACTOR_VERSION}}" + dir = "../../" + run = [ + "lua-doc-extractor rts/Lua/LuaSyncedRead.cpp rts/Lua/LuaUnsyncedCtrl.cpp --table-mapping \"Spring:SpringShared\" --strip-helpers --file SpringShared.lua --dest {{vars.lua_doc_gen_dest}} --repo \"${LUA_DOC_EXTRACTOR_SOURCE_REF}\"", + "sed -i '/^---@meta$/a\\\\n---@class SpringShared\\nSpringShared = {}' {{vars.lua_doc_gen_dest}}/SpringShared.lua", + "lua-doc-extractor rts/Lua/LuaSyncedCtrl.cpp --table-mapping \"Spring:SpringSynced\" --strip-helpers --file SpringSynced.lua --dest {{vars.lua_doc_gen_dest}} --repo \"${LUA_DOC_EXTRACTOR_SOURCE_REF}\"", + "sed -i '/^---@meta$/a\\\\n---@class SpringSynced : SpringShared\\nSpringSynced = {}' {{vars.lua_doc_gen_dest}}/SpringSynced.lua", + "lua-doc-extractor rts/Lua/LuaUnsyncedRead.cpp --table-mapping \"Spring:SpringUnsynced\" --strip-helpers --file SpringUnsynced.lua --dest {{vars.lua_doc_gen_dest}} --repo \"${LUA_DOC_EXTRACTOR_SOURCE_REF}\"", + "sed -i '/^---@meta$/a\\\\n---@class SpringUnsynced : SpringShared\\nSpringUnsynced = {}' {{vars.lua_doc_gen_dest}}/SpringUnsynced.lua" + ] + [tasks.latest_release_data] description = "Get latest release data" tools."aqua:jqlang/jq" = "{{env.JQ_VERSION}}" diff --git a/docker-build-v2/build.sh b/docker-build-v2/build.sh index 64c9c4957cc..b991ba9b765 100755 --- a/docker-build-v2/build.sh +++ b/docker-build-v2/build.sh @@ -135,11 +135,22 @@ else P="/" fi +# Handle git worktrees: mount the main repo's .git directory if needed +WORKTREE_MOUNTS="" +if [[ -f .git ]]; then + MAIN_GIT_DIR=$(cat .git | sed 's/gitdir: //') + MAIN_REPO_GIT=$(echo "$MAIN_GIT_DIR" | sed 's|/worktrees/.*||') + if [[ -d "$MAIN_REPO_GIT" ]]; then + WORKTREE_MOUNTS="-v $MAIN_REPO_GIT:$MAIN_REPO_GIT:ro" + fi +fi + $RUNTIME run -it --rm \ -v "$CWD${P}":/build/src:z,ro \ -v "$CWD${P}.cache${P}ccache-$OS":/build/cache:z,rw \ -v "$CWD${P}build-$OS":/build/out:z,rw \ $UID_FLAGS \ + $WORKTREE_MOUNTS \ -e CONFIGURE \ -e COMPILE \ -e CMAKE_BUILD_PARALLEL_LEVEL \ diff --git a/rts/Lua/LuaHandleSynced.cpp b/rts/Lua/LuaHandleSynced.cpp index 7668b9ef414..b847d53aed3 100644 --- a/rts/Lua/LuaHandleSynced.cpp +++ b/rts/Lua/LuaHandleSynced.cpp @@ -53,6 +53,7 @@ #include "System/Misc/TracyDefs.h" +#include LuaRulesParams::Params CSplitLuaHandle::gameParams; @@ -1219,6 +1220,42 @@ 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:ResourceExcess + * @param excesses table + * @return boolean whether or not Lua handled the event + */ +bool CSyncedLuaHandle::ResourceExcess(const std::map & excesses) +{ + ZoneScopedN("RE_LuaTotal"); + LUA_CALL_IN_CHECK(L, true); + luaL_checkstack(L, 3, __func__); + + static const LuaHashString cmdStr(__func__); + if (!cmdStr.GetGlobalFunc(L)) + return false; + + lua_createtable(L, excesses.size(), 1); + + for (const auto &[teamID, excess] : excesses) { + lua_createtable(L, excess.MAX_RESOURCES, 0); + for (const auto &[resourceID, resource] : std::views::enumerate(excess)) { + lua_pushnumber(L, resource); + lua_rawseti(L, -2, resourceID + 1); + } + lua_rawseti(L, -2, teamID); + } + + if (!RunCallIn(L, cmdStr, 1, 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..2dee634d91f 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 ResourceExcess(const std::map & 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/Lua/library/Spring.lua b/rts/Lua/library/Spring.lua index 402db91886b..e00ed6b2787 100644 --- a/rts/Lua/library/Spring.lua +++ b/rts/Lua/library/Spring.lua @@ -1,3 +1,4 @@ ---@meta +---@class Spring : SpringSynced, SpringUnsynced Spring = {} \ No newline at end of file diff --git a/rts/Sim/Misc/Team.cpp b/rts/Sim/Misc/Team.cpp index 1c89bb3ec34..2486a94ed7e 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(resExcessThisFrame), 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); + resExcessThisFrame.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); + resExcessThisFrame.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]); + resExcessThisFrame[i] += (res[i] - resStorage[i]); res[i] = resStorage[i]; } } diff --git a/rts/Sim/Misc/Team.h b/rts/Sim/Misc/Team.h index 6bc74267dc9..7bc426d30d8 100644 --- a/rts/Sim/Misc/Team.h +++ b/rts/Sim/Misc/Team.h @@ -73,6 +73,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 @@ -88,6 +91,7 @@ class CTeam : public TeamBase SResourcePack resIncome, resPrevIncome; SResourcePack resExpense, resPrevExpense; SResourcePack resShare; + SResourcePack resExcessThisFrame; //< accumulates excess over a gameframe SResourcePack resDelayedShare; //< excess that might be shared next SlowUpdate SResourcePack resSent, resPrevSent; SResourcePack resReceived, resPrevReceived; diff --git a/rts/Sim/Misc/TeamHandler.cpp b/rts/Sim/Misc/TeamHandler.cpp index d8b5d4e5afc..01d968a54d5 100644 --- a/rts/Sim/Misc/TeamHandler.cpp +++ b/rts/Sim/Misc/TeamHandler.cpp @@ -7,6 +7,8 @@ #include "Game/GameSetup.h" #include "Sim/Misc/GlobalConstants.h" #include "Sim/Misc/GlobalSynced.h" +#include "System/EventHandler.h" +#include "System/Misc/SpringTime.h" #include "System/Misc/TracyDefs.h" @@ -113,9 +115,35 @@ void CTeamHandler::SetDefaultStartPositions(const CGameSetup* setup) } } +void CTeamHandler::HandleFrameExcess() +{ + ZoneScopedN("ResourceExcess"); + const spring_time startTime = spring_gettime(); + + std::map excesses; + { + ZoneScopedN("RE_BuildTable"); + for (const auto &team : teams) + excesses.emplace(team.teamNum, team.resExcessThisFrame); + } + + if (!eventHandler.ResourceExcess(excesses)) + for (auto &team : teams) + team.resDelayedShare += team.resExcessThisFrame; + + for (auto &team : teams) + team.resExcessThisFrame = 0.0f; + + const auto totalUs = (spring_gettime() - startTime).toMicroSecsi(); + TracyPlot("Economy/TotalTime_us", static_cast(totalUs)); +} + void CTeamHandler::GameFrame(int frameNum) { RECOIL_DETAILED_TRACY_ZONE; + + HandleFrameExcess(); + if ((frameNum % TEAM_SLOWUPDATE_RATE) != 0) return; diff --git a/rts/Sim/Misc/TeamHandler.h b/rts/Sim/Misc/TeamHandler.h index 2e0e2aa7a2b..da7499d6581 100644 --- a/rts/Sim/Misc/TeamHandler.h +++ b/rts/Sim/Misc/TeamHandler.h @@ -161,6 +161,7 @@ class CTeamHandler bool TransferTeamMaxUnits(CTeam* fromTeam, CTeam* toTeam, int transferAmnt); private: + void HandleFrameExcess(); /** * @brief gaia team diff --git a/rts/System/EventClient.h b/rts/System/EventClient.h index 03a4a4b4fcd..a8ace4748c6 100644 --- a/rts/System/EventClient.h +++ b/rts/System/EventClient.h @@ -4,6 +4,7 @@ #define EVENT_CLIENT_H #include +#include #include #include #include @@ -35,6 +36,7 @@ struct BuildInfo; struct FeatureDef; class LuaMaterial; struct WeaponDef; +struct SResourcePack; #ifndef zipFile // might be defined through zip.h already @@ -122,6 +124,8 @@ class CEventClient virtual void TeamDied(int teamID) {} virtual void TeamChanged(int teamID) {} + virtual bool ResourceExcess(const std::map & 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..f0d22472ca4 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::ResourceExcess(const std::map &excess) +{ + ZoneScoped; + return ControlIterateDefFalse(listResourceExcess, &CEventClient::ResourceExcess, excess); +} + void CEventHandler::TeamChanged(int teamID) { ZoneScoped; diff --git a/rts/System/EventHandler.h b/rts/System/EventHandler.h index c72ba079a9a..aa52683e670 100644 --- a/rts/System/EventHandler.h +++ b/rts/System/EventHandler.h @@ -60,6 +60,8 @@ class CEventHandler void TeamDied(int teamID); void TeamChanged(int teamID); + bool ResourceExcess(const std::map & 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..5b8504ad80d 100644 --- a/rts/System/Events.def +++ b/rts/System/Events.def @@ -31,8 +31,12 @@ 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(ResourceExcess, 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)