From ae77ffd3c55af20d045fcdbbeba179bd39e3c9e3 Mon Sep 17 00:00:00 2001 From: Alexander Heistermann Date: Wed, 29 Jul 2026 08:43:40 -0500 Subject: [PATCH 1/6] Remove all instances of 32 bit support from cmake file (#3033) Co-authored-by: 12345swordy <12345swordy@user.noreply.github.com> --- CMakeLists.txt | 37 +++++++++---------------------------- 1 file changed, 9 insertions(+), 28 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 243ef8cb697..7b3cfb886f2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -100,13 +100,12 @@ endif (APPLE) ### Compiler flags and defines based on build type include(TestCXXFlags) -## 32bit or 64bit? +## 64-bit architecture check set(MARCH_FLAG ${MARCH} CACHE STRING "CPU optimization (use `generic` for generic optimization)") -if (CMAKE_SIZEOF_VOID_P EQUAL 8) - set(MARCH_BITS 64 CACHE INTERNAL "" FORCE) -else (CMAKE_SIZEOF_VOID_P EQUAL 8) - message(FATAL_ERROR "RecoilEngine does not support 32 bits." ) -endif (CMAKE_SIZEOF_VOID_P EQUAL 8) +if (NOT CMAKE_SIZEOF_VOID_P EQUAL 8) + message(FATAL_ERROR "RecoilEngine requires a 64-bit build environment.") +endif (NOT CMAKE_SIZEOF_VOID_P EQUAL 8) +set(MARCH_BITS 64 CACHE INTERNAL "" FORCE) # Detect architecture if (CMAKE_SYSTEM_PROCESSOR MATCHES "arm64|aarch64|armv8|ARM64|AARCH64") @@ -119,9 +118,8 @@ else() message(FATAL_ERROR "Unsupported architecture: ${CMAKE_SYSTEM_PROCESSOR}. Only ARM64 and x86-64 are supported.") endif() -message(STATUS "Building Spring on ${MARCH_BITS}bit environment") -set(BUILD_BITS "${MARCH_BITS}" CACHE STRING "Target arch machine type") -message(STATUS "Targetting ${BUILD_BITS}bit") +message(STATUS "Building Spring on a 64-bit environment") +set(BUILD_BITS 64 CACHE INTERNAL "Target architecture bitness" FORCE) ### Install paths (relative to CMAKE_INSTALL_PREFIX) @@ -489,12 +487,7 @@ if (CMAKE_COMPILER_IS_GNUCXX) set(FALLBACK_SSE_FLAGS "${FALLBACK_SSE_FLAGS} -mno-avx -mno-fma -mno-fma4 -mno-xop -mno-lwp") set(FALLBACK_SSE_FLAGS "${FALLBACK_SSE_FLAGS} -mno-avx2") - if (MARCH_BITS EQUAL 64) - set(FALLBACK_MARCH "x86-64") - else (MARCH_BITS EQUAL 64) - set(FALLBACK_MARCH "i686") - endif (MARCH_BITS EQUAL 64) - + set(FALLBACK_MARCH "x86-64") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=${FALLBACK_MARCH} -mtune=generic ${FALLBACK_SSE_FLAGS}") endif (NOT MARCH_FLAG STREQUAL "generic") @@ -581,22 +574,10 @@ else (MSVC) #set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -ggdb3") #cmake has -g it by default #set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} -ggdb3") #cmake has -g it by default - if (MARCH_BITS EQUAL 64 AND BUILD_BITS EQUAL 32) - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -m32") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -m32") - endif (MARCH_BITS EQUAL 64 AND BUILD_BITS EQUAL 32) if (MINGW) set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -static -static-libgcc -static-libstdc++ -Wl,--enable-auto-import") set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} -static -static-libgcc -static-libstdc++ -Wl,--enable-auto-import") - if (BUILD_BITS EQUAL 32) - # Increase memory limit from 2GB to 3GB on 32bit Windows and 2GB->4GB on 64bit Windows (assuming spring.exe is 32bit) - # http://msdn.microsoft.com/en-us/library/windows/desktop/aa366778(v=vs.85).aspx - message(STATUS "Enable IMAGE_FILE_LARGE_ADDRESS_AWARE (>2GB memory limit)") - set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--large-address-aware") - else (BUILD_BITS EQUAL 32) - message(WARNING "The 64bit version of spring on windows is experimental and may not sync with regular builds") - endif (BUILD_BITS EQUAL 32) endif (MINGW) endif (MSVC) @@ -723,4 +704,4 @@ if (INSTALL_PORTABLE) install(DIRECTORY DESTINATION maps) file(TOUCH ${CMAKE_CURRENT_BINARY_DIR}/springsettings_portable.cfg) # empty settings file for portable install install(FILES ${CMAKE_CURRENT_BINARY_DIR}/springsettings_portable.cfg DESTINATION . RENAME springsettings.cfg) -endif (INSTALL_PORTABLE) +endif (INSTALL_PORTABLE) \ No newline at end of file From 694a8320dd07271a64c6d22cbc48cd20c6cbad61 Mon Sep 17 00:00:00 2001 From: loveridge Date: Wed, 29 Jul 2026 13:47:42 +0000 Subject: [PATCH 2/6] send gameprogress packet on connection initialization (#2872) --- rts/Net/GameServer.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/rts/Net/GameServer.cpp b/rts/Net/GameServer.cpp index 4f8a7800c50..5be3e138612 100644 --- a/rts/Net/GameServer.cpp +++ b/rts/Net/GameServer.cpp @@ -3094,6 +3094,12 @@ unsigned CGameServer::BindConnection( } } + // inform player of the current frame + if (gameHasStarted) { + CBaseNetProtocol::PacketType progressPacket = CBaseNetProtocol::Get().SendCurrentFrameProgress(serverFrameNum); + newPlayer.SendData(progressPacket); + } + // finally send player all packets he missed until now for (const std::shared_ptr& p: packetCache) newPlayer.SendData(p); From 586b1cd9a0235325f722fa3290d58fb5c625025f Mon Sep 17 00:00:00 2001 From: Fx-Doo Date: Wed, 29 Jul 2026 16:50:46 +0200 Subject: [PATCH 3/6] Explicitly use InSonarJammer or InJammer depending on the tested sensor (#2980) * Explicitly use InSonarJammer or InJammer depending on the tested sensor * Update IsUnitInJammer and GetPositionLosState to return either InJammer() or InSonarJammer() depending on pos/unitpos return args: IsUnitInJammer() and select(4, GetPositionLosState()) true -> complete jam = can't be sensed by anything false -> no jam or incomplete jam = can be sensed I picked this update because it's the most consistent with what we had. The main difference being that we check before calling InJammer() or InSonarJammer(), rather than having that pos check and jammerType dispatch within InJammer() * Revert "Update IsUnitInJammer and GetPositionLosState to return either InJammer() or InSonarJammer() depending on pos/unitpos" This reverts commit 93b8fb528317c2a07bedaf5fc5aa505f1fafc6cf. --- rts/Sim/Misc/LosHandler.cpp | 40 +++++++++++++++++++++++++------------ rts/Sim/Misc/LosHandler.h | 9 +++++++-- 2 files changed, 34 insertions(+), 15 deletions(-) diff --git a/rts/Sim/Misc/LosHandler.cpp b/rts/Sim/Misc/LosHandler.cpp index a132ddeb382..5b92c0d1762 100644 --- a/rts/Sim/Misc/LosHandler.cpp +++ b/rts/Sim/Misc/LosHandler.cpp @@ -951,22 +951,24 @@ bool CLosHandler::InRadar(const float3 pos, int allyTeam) const bool CLosHandler::InRadar(const CUnit* unit, int allyTeam) const { RECOIL_DETAILED_TRACY_ZONE; - // unit is discoverable by sonar + // first attempt to discover with sonar: + // unit is discoverable by sonar only if not sonarStealth or not sonarJammed if (unit->IsInWater()) { if ((!unit->sonarStealth || unit->beingBuilt) && sonar.InSight(unit->pos, allyTeam) && - !InJammer(unit, allyTeam)) + !InSonarJammer(unit, allyTeam)) return true; } - // unit is completely submerged, only sonar can see it + // unit is InWater and UnderWater, but was not previously caught by sonar, skip it if (unit->IsUnderWater()) return false; - // radar stealth + // then attempt to discover with radar + // unit is radar stealth, can't be discovered if (unit->stealth && !unit->beingBuilt) return false; - + // use radar jamming, not sonar jamming here return (radar.InSight(unit->pos, allyTeam) && !InJammer(unit, allyTeam)); } @@ -976,13 +978,9 @@ bool CLosHandler::InJammer(const float3 pos, int allyTeam) const RECOIL_DETAILED_TRACY_ZONE; const int jammerAlly = modInfo.separateJammers ? allyTeam : 0; - if (pos.y < 0.0f) - return sonarJammer.InSight(pos, jammerAlly); - return jammer.InSight(pos, jammerAlly); } - bool CLosHandler::InJammer(const CUnit* unit, int allyTeam) const { RECOIL_DETAILED_TRACY_ZONE; @@ -990,11 +988,27 @@ bool CLosHandler::InJammer(const CUnit* unit, int allyTeam) const return false; //TODO handle ingame alliances + const int jammerAlly = modInfo.separateJammers ? unit->allyteam : 0; + + return jammer.InSight(unit->pos, jammerAlly); +} + +bool CLosHandler::InSonarJammer(const float3 pos, int allyTeam) const +{ + RECOIL_DETAILED_TRACY_ZONE; + const int jammerAlly = modInfo.separateJammers ? allyTeam : 0; + + return sonarJammer.InSight(pos, jammerAlly); +} +bool CLosHandler::InSonarJammer(const CUnit* unit, int allyTeam) const +{ + RECOIL_DETAILED_TRACY_ZONE; + if (allyTeam == unit->allyteam) + return false; + + //TODO handle ingame alliances const int jammerAlly = modInfo.separateJammers ? unit->allyteam : 0; - if (unit->IsUnderWater()) { - return sonarJammer.InSight(unit->pos, jammerAlly); - } - return jammer.InSight(unit->pos, jammerAlly); + return sonarJammer.InSight(unit->pos, jammerAlly); } diff --git a/rts/Sim/Misc/LosHandler.h b/rts/Sim/Misc/LosHandler.h index e30c4668bc1..9f17fa3ebc3 100644 --- a/rts/Sim/Misc/LosHandler.h +++ b/rts/Sim/Misc/LosHandler.h @@ -253,11 +253,16 @@ class CLosHandler : public CEventClient bool InRadar(const CUnit* unit, int allyTeam) const; - // returns whether a square is being radar- or sonar-jammed - // (even when the square is not in radar- or sonar-coverage) + // returns whether a square is being radar-jammed + // (even when the square is not in radar-coverage) bool InJammer(const float3 pos, int allyTeam) const; bool InJammer(const CUnit* unit, int allyTeam) const; + // returns whether a square is being sonar-jammed + // (even when the square is not in sonar-coverage) + bool InSonarJammer(const float3 pos, int allyTeam) const; + bool InSonarJammer(const CUnit* unit, int allyTeam) const; + bool InSeismicDistance(const CUnit* unit, int allyTeam) const { return seismic.InSight(unit->pos, allyTeam); From e7f84880a87e3c9c374cbd8e4b50482be8b78ee7 Mon Sep 17 00:00:00 2001 From: Keith Harvey Date: Wed, 29 Jul 2026 09:11:05 -0600 Subject: [PATCH 4/6] fix: minor Lua EmmyLua type annotations (#2888) * Lua: minor type annotation and doc fixes Series of minor EmmyLua annotation corrections and small documentation fixes that accumulated during the spring-split exploration. None of these depend on or relate to the spring-split refactor; pulled out as a standalone PR to keep the spring-split review focused. * expand check-lua to include Units/Scripts to capture UnitScriptTable in the shallow checkout --- .github/workflows/check-lua.yml | 3 + doc/site/mise.toml | 2 +- rts/Lua/LuaFonts.cpp | 77 ++++++++ rts/Lua/LuaObjectRendering.cpp | 131 +++++++++++++ rts/Lua/LuaOpenGL.cpp | 28 +-- rts/Lua/LuaPathFinder.cpp | 48 +++++ rts/Lua/LuaRules.cpp | 3 + rts/Lua/LuaSyncedCtrl.cpp | 119 ++++++------ rts/Lua/LuaSyncedRead.cpp | 126 +++++++++++-- rts/Lua/LuaUI.cpp | 6 + rts/Lua/LuaUnsyncedCtrl.cpp | 136 +++++++------- rts/Lua/LuaUnsyncedRead.cpp | 7 +- rts/Lua/LuaVBOImpl.cpp | 2 +- rts/Sim/Units/Scripts/LuaUnitScript.cpp | 233 ++++++++++++++++++++++++ 14 files changed, 769 insertions(+), 152 deletions(-) diff --git a/.github/workflows/check-lua.yml b/.github/workflows/check-lua.yml index 3e6e87a05c6..dcbef82f105 100644 --- a/.github/workflows/check-lua.yml +++ b/.github/workflows/check-lua.yml @@ -5,6 +5,7 @@ on: paths: - rts/Lua/** - rts/Rml/SolLua/**/*.cpp + - rts/Sim/Units/Scripts/**/*.cpp - doc/site/mise.toml push: branches: @@ -12,6 +13,7 @@ on: paths: - rts/Lua/** - rts/Rml/SolLua/**/*.cpp + - rts/Sim/Units/Scripts/**/*.cpp - doc/site/mise.toml env: RECOIL_LUA_LIBRARY_DIR: "rts/Lua/library" @@ -29,6 +31,7 @@ jobs: sparse-checkout: | rts/Lua rts/Rml/SolLua + rts/Sim/Units/Scripts doc/site/mise.toml - uses: jdx/mise-action@v2 diff --git a/doc/site/mise.toml b/doc/site/mise.toml index e827931ebe8..c473389ac1d 100644 --- a/doc/site/mise.toml +++ b/doc/site/mise.toml @@ -20,7 +20,7 @@ lua_doc_gen_dest = "$RECOIL_LUA_LIBRARY_DIR/generated" site_dir = 'doc/site' site_temp_dir = "{{vars.site_dir}}/temp" - lua_doc_paths = 'rts/{Lua,Rml/SolLua}/**/*.cpp' + lua_doc_paths = 'rts/{Lua,Rml/SolLua,Sim/Units/Scripts}/**/*.cpp' lua_pages_dir = "{{vars.site_dir}}/content/docs/lua-api" emmylua_cli_template = "{{vars.site_dir}}/emmylua-doc-cli-template" latest_release_data = 'data/latest_release.json' diff --git a/rts/Lua/LuaFonts.cpp b/rts/Lua/LuaFonts.cpp index 8bc2acda23f..c66908131c5 100644 --- a/rts/Lua/LuaFonts.cpp +++ b/rts/Lua/LuaFonts.cpp @@ -192,6 +192,15 @@ int LuaFonts::meta_index(lua_State* L) /******************************************************************************/ /******************************************************************************/ +/*** Load a font from a file. + * + * @function gl.LoadFont + * @param fontFile string + * @param size integer? + * @param outlineWidth integer? + * @param outlineWeight number? + * @return LuaFont font + */ int LuaFonts::LoadFont(lua_State* L) { RECOIL_DETAILED_TRACY_ZONE; @@ -208,6 +217,11 @@ int LuaFonts::LoadFont(lua_State* L) } +/*** Delete a font object. + * + * @function gl.DeleteFont + * @param font LuaFont + */ int LuaFonts::DeleteFont(lua_State* L) { RECOIL_DETAILED_TRACY_ZONE; @@ -261,6 +275,15 @@ int LuaFonts::ClearFallbackFonts(lua_State* L) /******************************************************************************/ /******************************************************************************/ +/*** Draws text in screen space at the given position. + * + * @function LuaFont:Print + * @param text string + * @param x number + * @param y number + * @param size number? Defaults to the font's point size. + * @param options string? Flag characters for alignment, outline, shadow, scaling, etc. (e.g. `"co"` for center and outline). + */ int LuaFonts::Print(lua_State* L) { RECOIL_DETAILED_TRACY_ZONE; @@ -309,6 +332,16 @@ int LuaFonts::Print(lua_State* L) return 0; } +/*** Draws text in world space at the given position. + * + * @function LuaFont:PrintWorld + * @param text string + * @param x number + * @param y number + * @param z number + * @param size number? Defaults to the font's point size. + * @param options string? Flag characters for alignment, outline, shadow, scaling, etc. (e.g. `"co"` for center and outline). + */ int LuaFonts::PrintWorld(lua_State* L) { RECOIL_DETAILED_TRACY_ZONE; @@ -388,6 +421,10 @@ int LuaFonts::Begin(lua_State* L) return 0; } +/*** Ends a font command block started with `LuaFont:Begin`. + * + * @function LuaFont:End + */ int LuaFonts::End(lua_State* L) { RECOIL_DETAILED_TRACY_ZONE; @@ -426,6 +463,16 @@ int LuaFonts::SubmitBuffered(lua_State* L) /******************************************************************************/ /******************************************************************************/ +/*** Wraps text to fit within a maximum width (and optional max height), in-place. + * + * @function LuaFont:WrapText + * @param text string + * @param maxWidth number + * @param maxHeight number? Defaults to an engine-defined maximum height. + * @param size number? Defaults to the font's point size. + * @return string wrappedText + * @return number lineCount + */ int LuaFonts::WrapText(lua_State* L) { RECOIL_DETAILED_TRACY_ZONE; @@ -447,6 +494,12 @@ int LuaFonts::WrapText(lua_State* L) /******************************************************************************/ /******************************************************************************/ +/*** Returns the horizontal extent of a string for this font at its current size. + * + * @function LuaFont:GetTextWidth + * @param text string + * @return number width + */ int LuaFonts::GetTextWidth(lua_State* L) { RECOIL_DETAILED_TRACY_ZONE; @@ -457,6 +510,14 @@ int LuaFonts::GetTextWidth(lua_State* L) } +/*** Returns layout metrics for a string: total height, descender depth, and line count. + * + * @function LuaFont:GetTextHeight + * @param text string + * @return number height + * @return number descender + * @return number lines + */ int LuaFonts::GetTextHeight(lua_State* L) { RECOIL_DETAILED_TRACY_ZONE; @@ -508,10 +569,26 @@ static int SetTextColorShared(lua_State* L, bool outline) return 0; } +/*** Sets the RGBA color used when drawing text (fill). + * + * @function LuaFont:SetTextColor + * @param color table Four-component RGBA array (`{r, g, b, a}`), or pass `r`, `g`, `b`, and optional `a` as separate numbers (requires at least three numeric components after the font). + */ int LuaFonts::SetTextColor(lua_State* L) { return (SetTextColorShared(L, false)); } + +/*** Sets the RGBA color used for text outline when outline rendering is enabled. + * + * @function LuaFont:SetOutlineColor + * @param color table Four-component RGBA array (`{r, g, b, a}`), or pass `r`, `g`, `b`, and optional `a` as separate numbers (requires at least three numeric components after the font). + */ int LuaFonts::SetOutlineColor(lua_State* L) { return (SetTextColorShared(L, true)); } +/*** When enabled, outline color is derived automatically instead of using `SetOutlineColor`. + * + * @function LuaFont:SetAutoOutlineColor + * @param enabled boolean + */ int LuaFonts::SetAutoOutlineColor(lua_State* L) { RECOIL_DETAILED_TRACY_ZONE; diff --git a/rts/Lua/LuaObjectRendering.cpp b/rts/Lua/LuaObjectRendering.cpp index c26b885be23..ac1f246e04c 100644 --- a/rts/Lua/LuaObjectRendering.cpp +++ b/rts/Lua/LuaObjectRendering.cpp @@ -1,5 +1,16 @@ /* This file is part of the Spring engine (GPL v2 or later), see LICENSE.html */ + +/*** +Object rendering API — controls LOD, materials, and custom draw for units/features. +Registered as `Spring.UnitRendering` and `Spring.FeatureRendering`. +@see rts/Lua/LuaObjectRendering.cpp +*/ + +/*** +@class ObjectRenderingTable +*/ + #include "LuaObjectRendering.h" #include "LuaMaterial.h" @@ -122,6 +133,13 @@ void LuaObjectRenderingImpl::PushFunction(lua_State* L, int (*fnPntr)(lua_State* +/*** Get the number of LOD levels and the current LOD. + * + * @function ObjectRenderingTable.GetLODCount + * @param objectID integer + * @return integer lodCount + * @return integer currentLOD + */ int LuaObjectRenderingImpl::GetLODCount(lua_State* L) { RECOIL_DETAILED_TRACY_ZONE; @@ -137,6 +155,13 @@ int LuaObjectRenderingImpl::GetLODCount(lua_State* L) return 2; } +/*** Set the number of LOD levels. + * + * @function ObjectRenderingTable.SetLODCount + * @param objectID integer + * @param lodCount integer + * @return nil + */ int LuaObjectRenderingImpl::SetLODCount(lua_State* L) { RECOIL_DETAILED_TRACY_ZONE; @@ -167,6 +192,14 @@ static int SetLODLengthCommon(lua_State* L, CSolidObject* obj, float scale) return 0; } +/*** Set the LOD transition length for a given level. + * + * @function ObjectRenderingTable.SetLODLength + * @param objectID integer + * @param lodLevel integer + * @param lodLength number + * @return nil + */ int LuaObjectRenderingImpl::SetLODLength(lua_State* L) { RECOIL_DETAILED_TRACY_ZONE; @@ -174,6 +207,14 @@ int LuaObjectRenderingImpl::SetLODLength(lua_State* L) return (SetLODLengthCommon(L, ParseSolidObject(L, __func__, 1, GetObjectType()), 1.0f)); } +/*** Set the LOD transition distance for a given level (scaled for 45-degree FOV). + * + * @function ObjectRenderingTable.SetLODDistance + * @param objectID integer + * @param lodLevel integer + * @param lodDistance number + * @return nil + */ int LuaObjectRenderingImpl::SetLODDistance(lua_State* L) { RECOIL_DETAILED_TRACY_ZONE; @@ -187,6 +228,15 @@ int LuaObjectRenderingImpl::SetLODDistance(lua_State* L) /******************************************************************************/ +/*** Set a display list for a piece at a given LOD and material. + * + * @function ObjectRenderingTable.SetPieceList + * @param objectID integer + * @param lodLevel integer + * @param piece integer + * @param ... any + * @return nil + */ int LuaObjectRenderingImpl::SetPieceList(lua_State* L) { RECOIL_DETAILED_TRACY_ZONE; @@ -334,6 +384,14 @@ static LuaMatRef ParseMaterial(lua_State* L, int index, LuaMatType matType) { /******************************************************************************/ /******************************************************************************/ +/*** Get a material reference for a LOD level. + * + * @function ObjectRenderingTable.GetMaterial + * @param objectID integer + * @param lodLevel integer + * @param materialName string + * @return userdata matRef + */ int LuaObjectRenderingImpl::GetMaterial(lua_State* L) { RECOIL_DETAILED_TRACY_ZONE; @@ -356,6 +414,15 @@ int LuaObjectRenderingImpl::GetMaterial(lua_State* L) /******************************************************************************/ /******************************************************************************/ +/*** Set a material for a LOD level. + * + * @function ObjectRenderingTable.SetMaterial + * @param objectID integer + * @param lodLevel integer + * @param materialName string + * @param materialTable table + * @return nil + */ int LuaObjectRenderingImpl::SetMaterial(lua_State* L) { RECOIL_DETAILED_TRACY_ZONE; @@ -392,6 +459,14 @@ int LuaObjectRenderingImpl::SetMaterial(lua_State* L) } +/*** Set the last LOD level that uses a given material. + * + * @function ObjectRenderingTable.SetMaterialLastLOD + * @param objectID integer + * @param materialName string + * @param lastLOD integer + * @return nil + */ int LuaObjectRenderingImpl::SetMaterialLastLOD(lua_State* L) { RECOIL_DETAILED_TRACY_ZONE; @@ -410,6 +485,15 @@ int LuaObjectRenderingImpl::SetMaterialLastLOD(lua_State* L) return 0; } +/*** Set display lists for a material. + * + * @function ObjectRenderingTable.SetMaterialDisplayLists + * @param objectID integer + * @param lodLevel integer + * @param materialName string + * @param displayListTable table + * @return nil + */ int LuaObjectRenderingImpl::SetMaterialDisplayLists(lua_State* L) { RECOIL_DETAILED_TRACY_ZONE; @@ -498,7 +582,17 @@ static int SetMaterialUniform(lua_State* L, LuaObjType objType, LuaMatShader::Pa return 1; } +/*** @function ObjectRenderingTable.SetDeferredMaterialUniform + * @param objectID integer + * @param ... any uniform values + * @return nil + */ int LuaObjectRenderingImpl::SetDeferredMaterialUniform(lua_State* L) { return (SetMaterialUniform(L, GetObjectType(), LuaMatShader::LUASHADER_PASS_DFR)); } +/*** @function ObjectRenderingTable.SetForwardMaterialUniform + * @param objectID integer + * @param ... any uniform values + * @return nil + */ int LuaObjectRenderingImpl::SetForwardMaterialUniform(lua_State* L) { return (SetMaterialUniform(L, GetObjectType(), LuaMatShader::LUASHADER_PASS_FWD)); } @@ -537,7 +631,17 @@ static int ClearMaterialUniform(lua_State* L, LuaObjType objType, LuaMatShader:: return 1; } +/*** @function ObjectRenderingTable.ClearDeferredMaterialUniform + * @param objectID integer + * @param ... any uniform indices + * @return nil + */ int LuaObjectRenderingImpl::ClearDeferredMaterialUniform(lua_State* L) { return (ClearMaterialUniform(L, GetObjectType(), LuaMatShader::LUASHADER_PASS_FWD)); } +/*** @function ObjectRenderingTable.ClearForwardMaterialUniform + * @param objectID integer + * @param ... any uniform indices + * @return nil + */ int LuaObjectRenderingImpl::ClearForwardMaterialUniform(lua_State* L) { return (ClearMaterialUniform(L, GetObjectType(), LuaMatShader::LUASHADER_PASS_DFR)); } @@ -559,18 +663,39 @@ static int SetObjectLuaDraw(lua_State* L, ObjectType* obj) } +/*** Enable or disable custom Lua drawing for a unit. + * + * @function ObjectRenderingTable.SetUnitLuaDraw + * @param unitID integer + * @param enable boolean + * @return nil + */ int LuaObjectRenderingImpl::SetUnitLuaDraw(lua_State* L) { RECOIL_DETAILED_TRACY_ZONE; return (SetObjectLuaDraw(L, unitHandler.GetUnit(luaL_checkint(L, 1)))); } +/*** Enable or disable custom Lua drawing for a feature. + * + * @function ObjectRenderingTable.SetFeatureLuaDraw + * @param featureID integer + * @param enable boolean + * @return nil + */ int LuaObjectRenderingImpl::SetFeatureLuaDraw(lua_State* L) { RECOIL_DETAILED_TRACY_ZONE; return (SetObjectLuaDraw(L, featureHandler.GetFeature(luaL_checkint(L, 1)))); } +/*** Enable or disable custom Lua drawing for a projectile. + * + * @function ObjectRenderingTable.SetProjectileLuaDraw + * @param projectileID integer + * @param enable boolean + * @return nil + */ int LuaObjectRenderingImpl::SetProjectileLuaDraw(lua_State* L) { RECOIL_DETAILED_TRACY_ZONE; @@ -602,6 +727,12 @@ static void PrintObjectLOD(const CSolidObject* obj, int lod) } +/*** Print debug info about the object's material data. + * + * @function ObjectRenderingTable.Debug + * @param objectID integer + * @return nil + */ int LuaObjectRenderingImpl::Debug(lua_State* L) { RECOIL_DETAILED_TRACY_ZONE; diff --git a/rts/Lua/LuaOpenGL.cpp b/rts/Lua/LuaOpenGL.cpp index 6c81432df53..a01bb055197 100644 --- a/rts/Lua/LuaOpenGL.cpp +++ b/rts/Lua/LuaOpenGL.cpp @@ -1369,7 +1369,7 @@ int LuaOpenGL::EndText(lua_State* L) * @param text string * @param x number * @param y number - * @param size number + * @param size number? * @param options string? concatenated string of option characters. * * - horizontal alignment: @@ -2065,8 +2065,8 @@ int LuaOpenGL::DrawFuncAtUnit(lua_State* L) * @param radius number * @param resolution integer * @param slope number - * @param gravity number - * @param weaponDefID integer + * @param gravity number? + * @param weaponDefID integer? */ int LuaOpenGL::DrawGroundCircle(lua_State* L) { @@ -2901,7 +2901,7 @@ int LuaOpenGL::TexRect(lua_State* L) /*** * @function gl.DispatchCompute - * @param numGroupX integer + * @param numGroupX integer? * @param numGroupY integer * @param numGroupZ integer * @param barriers integer? (Default: `0`) @@ -4538,7 +4538,7 @@ int LuaOpenGL::TexEnv(lua_State* L) * @param texNum integer * @param target GL * @param pname GL - * @param value number + * @param value number? */ /*** * @function gl.MultiTexEnv @@ -4609,7 +4609,7 @@ static void SetTexGenState(GLenum target, bool state) * @function gl.TexGen * @param target GL * @param pname GL - * @param value number + * @param value number? */ /*** * @function gl.TexGen @@ -4669,7 +4669,7 @@ int LuaOpenGL::TexGen(lua_State* L) * @param texNum integer * @param target GL * @param pname GL - * @param value number + * @param value number? */ /*** * @function gl.MultiTexGen @@ -5073,6 +5073,10 @@ int LuaOpenGL::GetEngineAtlasTextures(lua_State* L) { /******************************************************************************/ +/*** + * @function gl.Clear + * @param bits GL any buffer bit mask (e.g. `GL.STENCIL_BUFFER_BIT`). Clears with current/default values. + */ /*** * @function gl.Clear * @param bits GL `GL.DEPTH_BUFFER_BIT` or `GL.STENCIL_BUFFER_BIT`. @@ -6397,10 +6401,10 @@ int LuaOpenGL::ReadPixels(lua_State* L) /*** * @class SaveImageOptions * @x_helper - * @field alpha boolean (Default: `false`) - * @field yflip boolean (Default: `true`) - * @field grayscale16bit boolean (Default: `false`) - * @field readbuffer GL (Default: current read buffer) + * @field alpha boolean? (Default: `false`) + * @field yflip boolean? (Default: `true`) + * @field grayscale16bit boolean? (Default: `false`) + * @field readbuffer GL? (Default: current read buffer) */ /*** @@ -7081,7 +7085,7 @@ int LuaOpenGL::ObjectLabel(lua_State* L) { * @function gl.PushDebugGroup * @param id integer A numeric identifier for the group, can be any unique number. * @param message string A human-readable string describing the debug group. Will be truncated if longer than driver-specific limit - * @param sourceIsThirdParty boolean Set the source tag, true for GL_DEBUG_SOURCE_THIRD_PARTY, false for GL_DEBUG_SOURCE_APPLICATION. default false + * @param sourceIsThirdParty boolean? Set the source tag, true for GL_DEBUG_SOURCE_THIRD_PARTY, false for GL_DEBUG_SOURCE_APPLICATION. default false * @return nil */ int LuaOpenGL::PushDebugGroup(lua_State* L) { diff --git a/rts/Lua/LuaPathFinder.cpp b/rts/Lua/LuaPathFinder.cpp index 9e9c3dc24b8..3184bca2510 100644 --- a/rts/Lua/LuaPathFinder.cpp +++ b/rts/Lua/LuaPathFinder.cpp @@ -211,6 +211,18 @@ static void CreatePathMetatable(lua_State* L) /******************************************************************************/ /******************************************************************************/ +/*** + * @function Spring.RequestPath + * @param moveID number|string + * @param startX number + * @param startY number + * @param startZ number + * @param endX number + * @param endY number + * @param endZ number + * @param radius number? + * @return userdata? path + */ int LuaPathFinder::RequestPath(lua_State* L) { const MoveDef* moveDef = nullptr; @@ -250,6 +262,13 @@ int LuaPathFinder::RequestPath(lua_State* L) +/*** + * @function Spring.InitPathNodeCostsArray + * @param overlayIndex number + * @param sizeX number + * @param sizeZ number + * @return boolean success + */ int LuaPathFinder::InitPathNodeCostsArray(lua_State* L) { const unsigned int overlayIndex = luaL_checkint(L, 1); @@ -282,6 +301,11 @@ int LuaPathFinder::InitPathNodeCostsArray(lua_State* L) return 1; } +/*** + * @function Spring.FreePathNodeCostsArray + * @param overlayIndex number + * @return boolean success + */ int LuaPathFinder::FreePathNodeCostsArray(lua_State* L) { const unsigned int overlayIndex = luaL_checkint(L, 1); @@ -315,6 +339,11 @@ int LuaPathFinder::FreePathNodeCostsArray(lua_State* L) +/*** + * @function Spring.SetPathNodeCosts + * @param overlayIndex number + * @return boolean success + */ int LuaPathFinder::SetPathNodeCosts(lua_State* L) { const unsigned int overlayIndex = luaL_checkint(L, 1); @@ -339,6 +368,11 @@ int LuaPathFinder::SetPathNodeCosts(lua_State* L) return 1; } +/*** + * @function Spring.GetPathNodeCosts + * @param overlayIndex number + * @return boolean|table costs + */ int LuaPathFinder::GetPathNodeCosts(lua_State* L) { const unsigned int overlayIndex = luaL_checkint(L, 1); @@ -370,6 +404,14 @@ int LuaPathFinder::GetPathNodeCosts(lua_State* L) +/*** + * @function Spring.SetPathNodeCost + * @param overlayIndex number + * @param nodeX number + * @param nodeZ number + * @param cost number + * @return boolean success + */ int LuaPathFinder::SetPathNodeCost(lua_State* L) { const unsigned int overlayIndex = luaL_checkint(L, 1); @@ -400,6 +442,12 @@ int LuaPathFinder::SetPathNodeCost(lua_State* L) return 1; } +/*** + * @function Spring.GetPathNodeCost + * @param nodeX number + * @param nodeZ number + * @return number cost + */ int LuaPathFinder::GetPathNodeCost(lua_State* L) { const unsigned int hmx = luaL_checkint(L, 1); diff --git a/rts/Lua/LuaRules.cpp b/rts/Lua/LuaRules.cpp index 19b5ac69707..0c1a1ace8b0 100644 --- a/rts/Lua/LuaRules.cpp +++ b/rts/Lua/LuaRules.cpp @@ -70,6 +70,7 @@ int CLuaRules::GetInitSelectTeam() const } + /****************************************************************************** * Lua Rules * @@ -91,11 +92,13 @@ bool CLuaRules::AddUnsyncedCode(lua_State* L) { lua_getglobal(L, "Spring"); + /*** @field Spring.UnitRendering ObjectRenderingTable */ lua_pushliteral(L, "UnitRendering"); lua_createtable(L, 0, 17); LuaObjectRendering::PushEntries(L); lua_rawset(L, -3); + /*** @field Spring.FeatureRendering ObjectRenderingTable */ lua_pushliteral(L, "FeatureRendering"); lua_createtable(L, 0, 17); LuaObjectRendering::PushEntries(L); diff --git a/rts/Lua/LuaSyncedCtrl.cpp b/rts/Lua/LuaSyncedCtrl.cpp index 6f0a8941d8f..fe7eeea65e5 100644 --- a/rts/Lua/LuaSyncedCtrl.cpp +++ b/rts/Lua/LuaSyncedCtrl.cpp @@ -391,6 +391,7 @@ bool LuaSyncedCtrl::PushEntries(lua_State* L) if (!LuaSyncedMoveCtrl::PushMoveCtrl(L)) return false; + /*** @field Spring.UnitScript UnitScriptTable */ if (!CLuaUnitScript::PushEntries(L)) return false; @@ -1161,7 +1162,7 @@ int LuaSyncedCtrl::GameOver(lua_State* L) /*** Set tidal strength * * @function Spring.SetTidal - * @param strength number + * @param strength number? * @return nil */ int LuaSyncedCtrl::SetTidal(lua_State* L) @@ -1174,8 +1175,8 @@ int LuaSyncedCtrl::SetTidal(lua_State* L) /*** Set wind strength * * @function Spring.SetWind - * @param minStrength number - * @param maxStrength number + * @param minStrength number? + * @param maxStrength number? * @return nil */ int LuaSyncedCtrl::SetWind(lua_State* L) @@ -1613,7 +1614,7 @@ void SetRulesParam(lua_State* L, const char* caller, int offset, /*** * @function Spring.SetGameRulesParam * @param paramName string - * @param paramValue ?number|string numeric paramValues in quotes will be converted to number. + * @param paramValue (number|string|boolean)? numeric paramValues in quotes will be converted to number. * @param losAccess losAccess? * @return nil */ @@ -1628,7 +1629,7 @@ int LuaSyncedCtrl::SetGameRulesParam(lua_State* L) * @function Spring.SetTeamRulesParam * @param teamID integer * @param paramName string - * @param paramValue ?number|string numeric paramValues in quotes will be converted to number. + * @param paramValue (number|string|boolean)? numeric paramValues in quotes will be converted to number. * @param losAccess losAccess? * @return nil */ @@ -1646,7 +1647,7 @@ int LuaSyncedCtrl::SetTeamRulesParam(lua_State* L) * @function Spring.SetPlayerRulesParam * @param playerID integer * @param paramName string - * @param paramValue ?number|string numeric paramValues in quotes will be converted to number. + * @param paramValue (number|string|boolean)? numeric paramValues in quotes will be converted to number. * @param losAccess losAccess? * @return nil */ @@ -1670,7 +1671,7 @@ int LuaSyncedCtrl::SetPlayerRulesParam(lua_State* L) * @function Spring.SetUnitRulesParam * @param unitID integer * @param paramName string - * @param paramValue ?number|string numeric paramValues in quotes will be converted to number. + * @param paramValue (number|string|boolean)? numeric paramValues in quotes will be converted to number. * @param losAccess losAccess? * @return nil */ @@ -1690,7 +1691,7 @@ int LuaSyncedCtrl::SetUnitRulesParam(lua_State* L) * @function Spring.SetFeatureRulesParam * @param featureID integer * @param paramName string - * @param paramValue ?number|string numeric paramValues in quotes will be converted to number. + * @param paramValue (number|string|boolean)? numeric paramValues in quotes will be converted to number. * @param losAccess losAccess? * @return nil */ @@ -1860,7 +1861,7 @@ int LuaSyncedCtrl::GetCOBScriptID(lua_State* L) * @param posY number * @param posZ number * @param facing Facing - * @param teamID integer + * @param teamID integer? * @param build boolean? (Default: `false`) The unit is created in "being built" state with zero `buildProgress`. * @param flattenGround boolean? (Default: `true`) The unit flattens ground, if it normally does so. * @param unitID integer? Request a specific unitID. @@ -3023,8 +3024,8 @@ int LuaSyncedCtrl::SetUnitLosState(lua_State* L) * - if the boolean is true it takes the absolute value of it. * * @param unitID integer - * @param cloak boolean|number - * @param cloakArg boolean|number + * @param cloak (boolean|number)? + * @param cloakArg (boolean|number)? * @return nil */ int LuaSyncedCtrl::SetUnitCloak(lua_State* L) @@ -3186,7 +3187,7 @@ int LuaSyncedCtrl::SetUnitMetalExtraction(lua_State* L) * * @function Spring.SetUnitHarvestStorage * @param unitID integer - * @param metal number + * @param metal number? * @return nil */ int LuaSyncedCtrl::SetUnitHarvestStorage(lua_State* L) @@ -3208,7 +3209,7 @@ int LuaSyncedCtrl::SetUnitHarvestStorage(lua_State* L) * @function Spring.SetUnitBuildParams * @param unitID integer * @param paramName string one of `buildRange`|`buildDistance`|`buildRange3D` - * @param value number|boolean boolean when `paramName` is `buildRange3D`, otherwise number. + * @param value (number|boolean)? boolean when `paramName` is `buildRange3D`, otherwise number. * @return nil */ int LuaSyncedCtrl::SetUnitBuildParams(lua_State* L) @@ -3369,7 +3370,7 @@ int LuaSyncedCtrl::SetUnitBlocking(lua_State* L) /*** * @function Spring.SetUnitCrashing * @param unitID integer - * @param crashing boolean + * @param crashing boolean? * @return boolean success */ int LuaSyncedCtrl::SetUnitCrashing(lua_State* L) { @@ -3592,7 +3593,8 @@ int LuaSyncedCtrl::SetUnitNeutral(lua_State* L) * @param enemyUnitID integer? when nil drops the units current target. * @param dgun boolean? (Default: `false`) * @param userTarget boolean? (Default: `false`) - * @param weaponNum number? (Default: `-1`) + * @param dontForceTarget boolean? + * @param weaponNum integer? (Default: `-1`) * @return boolean success */ @@ -3604,7 +3606,8 @@ int LuaSyncedCtrl::SetUnitNeutral(lua_State* L) * @param z number? * @param dgun boolean? (Default: `false`) * @param userTarget boolean? (Default: `false`) - * @param weaponNum number? (Default: `-1`) + * @param dontForceTarget boolean? + * @param weaponNum integer? (Default: `-1`) * @return boolean success */ int LuaSyncedCtrl::SetUnitTarget(lua_State* L) @@ -3716,8 +3719,8 @@ int LuaSyncedCtrl::SetUnitMidAndAimPos(lua_State* L) /*** * @function Spring.SetUnitRadiusAndHeight * @param unitID integer - * @param radius number - * @param height number + * @param radius number? + * @param height number? * @return boolean success */ int LuaSyncedCtrl::SetUnitRadiusAndHeight(lua_State* L) @@ -3961,12 +3964,12 @@ int LuaSyncedCtrl::SetUnitSensorRadius(lua_State* L) * * @function Spring.SetUnitPosErrorParams * @param unitID integer - * @param posErrorVectorX number - * @param posErrorVectorY number - * @param posErrorVectorZ number - * @param posErrorDeltaX number - * @param posErrorDeltaY number - * @param posErrorDeltaZ number + * @param posErrorVectorX number? + * @param posErrorVectorY number? + * @param posErrorVectorZ number? + * @param posErrorDeltaX number? + * @param posErrorDeltaY number? + * @param posErrorDeltaZ number? * @param nextPosErrorUpdate number? * @return nil */ @@ -4062,6 +4065,7 @@ int LuaSyncedCtrl::SetUnitLandGoal(lua_State* L) /*** * @function Spring.ClearUnitGoal * @param unitID integer + * @param maneuver boolean? * @return nil */ int LuaSyncedCtrl::ClearUnitGoal(lua_State* L) @@ -4936,7 +4940,7 @@ int LuaSyncedCtrl::SetFeatureResources(lua_State* L) * @function Spring.SetFeatureResurrect * * @param featureID integer - * @param unitDef string|integer Can be a number id or a string name, this allows cancelling resurrection by passing `-1`. + * @param unitDef (string|integer)? Can be a number id or a string name, this allows cancelling resurrection by passing `-1`. * @param facing Facing? (Default: `"south"`) * @param progress number? Set the level of progress. * @return nil @@ -5277,8 +5281,8 @@ int LuaSyncedCtrl::SetFeatureMidAndAimPos(lua_State* L) /*** * @function Spring.SetFeatureRadiusAndHeight * @param featureID integer - * @param radius number - * @param height number + * @param radius number? + * @param height number? * @return boolean success */ int LuaSyncedCtrl::SetFeatureRadiusAndHeight(lua_State* L) @@ -5555,7 +5559,7 @@ int LuaSyncedCtrl::SetProjectileUseAirLos(lua_State* L) * @function Spring.SetProjectileMoveControl * * @param projectileID integer - * @param enable boolean + * @param enable boolean? */ int LuaSyncedCtrl::SetProjectileMoveControl(lua_State* L) { @@ -6533,7 +6537,6 @@ int LuaSyncedCtrl::SetHeightMap(lua_State* L) * ``` * * @param luaFunction function - * @param arg number * @param ... number * @return integer? absTotalHeightMapAmountChanged */ @@ -7296,7 +7299,7 @@ int LuaSyncedCtrl::ForceUnitCollisionUpdate(lua_State* L) * @param transporterID integer * @param passengerID integer * @param pieceNum number - * @param force boolean + * @param force boolean? * @return nil */ int LuaSyncedCtrl::UnitAttach(lua_State* L) @@ -7418,21 +7421,21 @@ int LuaSyncedCtrl::SetUnitLoadingTransport(lua_State* L) /*** * @class ProjectileParams * @x_helper - * @field pos xyz - * @field speed xyz - * @field spread xyz - * @field error xyz - * @field end xyz - * @field owner integer - * @field team integer - * @field ttl number - * @field gravity number - * @field tracking number - * @field maxRange number - * @field startAlpha number - * @field endAlpha number - * @field model string - * @field cegTag string + * @field pos xyz? + * @field speed xyz? + * @field spread xyz? + * @field error xyz? + * @field end xyz? + * @field owner integer? + * @field team integer? + * @field ttl number? + * @field gravity number? + * @field tracking number? + * @field maxRange number? + * @field startAlpha number? + * @field endAlpha number? + * @field model string? + * @field cegTag string? */ /*** @@ -7594,18 +7597,18 @@ static int SetExplosionParam(lua_State* L, CExplosionParams& params, DamageArray * * @class ExplosionParams * @x_helper - * @field weaponDef number - * @field owner number - * @field hitUnit number - * @field hitFeature number - * @field craterAreaOfEffect number - * @field damageAreaOfEffect number - * @field edgeEffectiveness number - * @field explosionSpeed number - * @field gfxMod number - * @field impactOnly boolean - * @field ignoreOwner boolean - * @field damageGround boolean + * @field weaponDef number? + * @field owner number? + * @field hitUnit number? + * @field hitFeature number? + * @field craterAreaOfEffect number? + * @field damageAreaOfEffect number? + * @field edgeEffectiveness number? + * @field explosionSpeed number? + * @field gfxMod number? + * @field impactOnly boolean? + * @field ignoreOwner boolean? + * @field damageGround boolean? */ /*** @@ -7616,7 +7619,7 @@ static int SetExplosionParam(lua_State* L, CExplosionParams& params, DamageArray * @param dirX number? (Default: `0`) * @param dirY number? (Default: `0`) * @param dirZ number? (Default: `0`) - * @param explosionParams ExplosionParams + * @param explosionParams ExplosionParams? * @return nil */ int LuaSyncedCtrl::SpawnExplosion(lua_State* L) diff --git a/rts/Lua/LuaSyncedRead.cpp b/rts/Lua/LuaSyncedRead.cpp index 1af92101752..65c2cd10400 100644 --- a/rts/Lua/LuaSyncedRead.cpp +++ b/rts/Lua/LuaSyncedRead.cpp @@ -91,6 +91,7 @@ using std::max; static const LuaHashString hs_n("n"); + /****************************************************************************** * Synced Read * @@ -99,10 +100,13 @@ static const LuaHashString hs_n("n"); bool LuaSyncedRead::PushEntries(lua_State* L) { - // allegiance constants + /*** @field Spring.ALL_UNITS number */ LuaPushNamedNumber(L, "ALL_UNITS", LuaUtils::AllUnits); + /*** @field Spring.MY_UNITS number */ LuaPushNamedNumber(L, "MY_UNITS", LuaUtils::MyUnits); + /*** @field Spring.ALLY_UNITS number */ LuaPushNamedNumber(L, "ALLY_UNITS", LuaUtils::AllyUnits); + /*** @field Spring.ENEMY_UNITS number */ LuaPushNamedNumber(L, "ENEMY_UNITS", LuaUtils::EnemyUnits); // READ routines, sync safe @@ -767,7 +771,6 @@ static int GetRulesParam(lua_State* L, const char* caller, int index, * @section gamestates ******************************************************************************/ - /*** * * @function Spring.IsCheatingEnabled @@ -1156,9 +1159,9 @@ int LuaSyncedRead::GetFeatureRulesParams(lua_State* L) * * @function Spring.GetGameRulesParam * - * @param ruleRef number|string the rule index or name + * @param name string rules-parameter key (only this argument is read) * - * @return number?|string value + * @return number|boolean|string|nil value */ int LuaSyncedRead::GetGameRulesParam(lua_State* L) { @@ -3072,6 +3075,7 @@ int LuaSyncedRead::GetUnitsInBox(lua_State* L) * @param x number * @param z number * @param radius number + * @param teamID integer? filter by team, defaults to all units * @return number[] unitIDs */ int LuaSyncedRead::GetUnitsInCylinder(lua_State* L) @@ -3683,6 +3687,9 @@ int LuaSyncedRead::ValidUnitID(lua_State* L) * * @function Spring.GetUnitStates * @param unitID integer + * @param retTable boolean? + * @param binState boolean? + * @param amtState boolean? * @return UnitState */ int LuaSyncedRead::GetUnitStates(lua_State* L) @@ -4053,8 +4060,8 @@ int LuaSyncedRead::GetUnitDefID(lua_State* L) * * @param unitID integer * -* @return integer|boolean|nil moveDefID -* @return string? moveDefName +* @return integer|false|nil moveDefID +* @return string|nil moveDefName */ int LuaSyncedRead::GetUnitMoveDefID(lua_State* L) @@ -5140,7 +5147,7 @@ int LuaSyncedRead::GetUnitMaxRange(lua_State* L) * * @param unitID integer * @param weaponNum number - * @param stateName string + * @param stateName string? * @return number stateValue */ int LuaSyncedRead::GetUnitWeaponState(lua_State* L) @@ -5364,6 +5371,13 @@ int LuaSyncedRead::GetUnitWeaponDamages(lua_State* L) * * @function Spring.GetUnitWeaponVectors * @param unitID integer + * @param weaponNum integer 1-indexed weapon number + * @return number? posX + * @return number posY + * @return number posZ + * @return number dirX + * @return number dirY + * @return number dirZ */ int LuaSyncedRead::GetUnitWeaponVectors(lua_State* L) { @@ -5404,6 +5418,19 @@ int LuaSyncedRead::GetUnitWeaponVectors(lua_State* L) * * @function Spring.GetUnitWeaponTryTarget * @param unitID integer + * @param weaponNum integer + * @param targetID integer + * @return boolean canTarget + */ +/*** + * + * @function Spring.GetUnitWeaponTryTarget + * @param unitID integer + * @param weaponNum integer + * @param posX number + * @param posY number + * @param posZ number + * @return boolean canTarget */ int LuaSyncedRead::GetUnitWeaponTryTarget(lua_State* L) { @@ -5449,6 +5476,19 @@ int LuaSyncedRead::GetUnitWeaponTryTarget(lua_State* L) * * @function Spring.GetUnitWeaponTestTarget * @param unitID integer + * @param weaponID integer weapon number (1-based Lua index) + * @param targetUnitID integer enemy unit to test (when fewer than five arguments) + * @return boolean validTarget + */ +/*** + * + * @function Spring.GetUnitWeaponTestTarget + * @param unitID integer + * @param weaponID integer weapon number (1-based Lua index) + * @param targetX number world X to test (with `targetY`, `targetZ`; used when at least five arguments are passed) + * @param targetY number + * @param targetZ number + * @return boolean validTarget */ int LuaSyncedRead::GetUnitWeaponTestTarget(lua_State* L) { @@ -5487,6 +5527,19 @@ int LuaSyncedRead::GetUnitWeaponTestTarget(lua_State* L) * * @function Spring.GetUnitWeaponTestRange * @param unitID integer + * @param weaponNum integer + * @param targetID integer + * @return boolean inRange + */ +/*** + * + * @function Spring.GetUnitWeaponTestRange + * @param unitID integer + * @param weaponNum integer + * @param posX number + * @param posY number + * @param posZ number + * @return boolean inRange */ int LuaSyncedRead::GetUnitWeaponTestRange(lua_State* L) { @@ -5525,6 +5578,43 @@ int LuaSyncedRead::GetUnitWeaponTestRange(lua_State* L) * * @function Spring.GetUnitWeaponHaveFreeLineOfFire * @param unitID integer + * @param weaponNum integer + * @param targetID integer + * @return boolean haveFreeLineOfFire + */ +/*** + * + * @function Spring.GetUnitWeaponHaveFreeLineOfFire + * @param unitID integer + * @param weaponNum integer + * @param srcPosX number + * @param srcPosY number + * @param srcPosZ number + * @return boolean haveFreeLineOfFire + */ +/*** + * + * @function Spring.GetUnitWeaponHaveFreeLineOfFire + * @param unitID integer + * @param weaponNum integer + * @param srcPosX number + * @param srcPosY number + * @param srcPosZ number + * @param targetID integer + * @return boolean haveFreeLineOfFire + */ +/*** + * + * @function Spring.GetUnitWeaponHaveFreeLineOfFire + * @param unitID integer + * @param weaponNum integer + * @param srcPosX number + * @param srcPosY number + * @param srcPosZ number + * @param tgtPosX number + * @param tgtPosY number + * @param tgtPosZ number + * @return boolean haveFreeLineOfFire */ int LuaSyncedRead::GetUnitWeaponHaveFreeLineOfFire(lua_State* L) { @@ -5591,6 +5681,11 @@ int LuaSyncedRead::GetUnitWeaponHaveFreeLineOfFire(lua_State* L) * * @function Spring.GetUnitWeaponCanFire * @param unitID integer + * @param weaponNum integer + * @param ignoreAngleGood boolean? + * @param ignoreTargetType boolean? + * @param ignoreRequestedDir boolean? + * @return boolean canFire */ int LuaSyncedRead::GetUnitWeaponCanFire(lua_State* L) { @@ -5844,6 +5939,9 @@ int LuaSyncedRead::GetUnitSeparation(lua_State* L) * * @function Spring.GetUnitFeatureSeparation * @param unitID integer + * @param featureID integer + * @param flat boolean? (Default: `false`) if true, XZ (2D) distance; otherwise 3D distance + * @return number distance */ int LuaSyncedRead::GetUnitFeatureSeparation(lua_State* L) { @@ -7793,6 +7891,12 @@ static void ParseMapCoords(lua_State* L, const char* caller, /*** * * @function Spring.GetGroundBlocked + * @param x number world x coordinate (or xMin when using 4-arg form) + * @param z number world z coordinate (or zMin when using 4-arg form) + * @param x2 number? world xMax (4-arg rectangle form) + * @param z2 number? world zMax (4-arg rectangle form) + * @return string? objectType `"feature"` or `"unit"` + * @return number? objectID the feature or unit ID */ int LuaSyncedRead::GetGroundBlocked(lua_State* L) { @@ -8260,7 +8364,7 @@ int LuaSyncedRead::IsPosInAirLos(lua_State* L) * @function Spring.GetUnitLosState * @param unitID integer * @param allyTeamID integer? - * @param raw false? Return a table. + * @param raw false? (Default: `false`) Return a table. * @return table<"los"|"radar"|"typed",boolean>? los A table of LOS state names as keys and booleans as values, or `nil` if `unitID` is invalid. */ int LuaSyncedRead::GetUnitLosState(lua_State* L) @@ -8309,7 +8413,7 @@ int LuaSyncedRead::GetUnitLosState(lua_State* L) * * @function Spring.IsUnitInLos * @param unitID integer - * @param allyTeamID integer + * @param allyTeamID integer? defaults to the calling widget/gadget's ally team * @return boolean inLos */ int LuaSyncedRead::IsUnitInLos(lua_State* L) @@ -8333,7 +8437,7 @@ int LuaSyncedRead::IsUnitInLos(lua_State* L) * * @function Spring.IsUnitInAirLos * @param unitID integer - * @param allyTeamID integer + * @param allyTeamID integer? defaults to the calling widget/gadget's ally team * @return boolean inAirLos */ int LuaSyncedRead::IsUnitInAirLos(lua_State* L) @@ -8357,7 +8461,7 @@ int LuaSyncedRead::IsUnitInAirLos(lua_State* L) * * @function Spring.IsUnitInRadar * @param unitID integer - * @param allyTeamID integer + * @param allyTeamID integer? defaults to the calling widget/gadget's ally team * @return boolean inRadar */ int LuaSyncedRead::IsUnitInRadar(lua_State* L) diff --git a/rts/Lua/LuaUI.cpp b/rts/Lua/LuaUI.cpp index 46f2bee87c6..002184fd1df 100644 --- a/rts/Lua/LuaUI.cpp +++ b/rts/Lua/LuaUI.cpp @@ -673,6 +673,12 @@ bool CLuaUI::GetLuaCmdDescList(lua_State* L, int index, vectorhaveShockFront = true; diff --git a/rts/Lua/LuaUnsyncedCtrl.cpp b/rts/Lua/LuaUnsyncedCtrl.cpp index 2079d9724ce..d140eab26f4 100644 --- a/rts/Lua/LuaUnsyncedCtrl.cpp +++ b/rts/Lua/LuaUnsyncedCtrl.cpp @@ -115,6 +115,7 @@ #undef Yield + /****************************************************************************** * Callouts to set state * @@ -467,12 +468,11 @@ static inline CUnit* ParseSelectUnit(lua_State* L, const char* caller, int index * @section console ******************************************************************************/ - /*** Send a ping request to the server * * @function Spring.Ping * - * @param pingTag number + * @param pingTag number? * * @return nil */ @@ -798,14 +798,14 @@ int LuaUnsyncedCtrl::LoadSoundDef(lua_State* L) /*** @function Spring.PlaySoundFile * @param soundfile string - * @param volume number? (Default: 1.0) - * @param posx number? + * @param volume number? (Default: 1.0) optional; all following arguments are optional + * @param posx number? world position X (use with `posy` and `posz`, or omit all three) * @param posy number? * @param posz number? - * @param speedx number? + * @param speedx number? velocity X (use with `speedy` and `speedz` after position, or omit all three) * @param speedy number? * @param speedz number? - * @param channel SoundChannel? (Default: `0|"general"`) + * @param channel SoundChannel? (Default: `0|"general"`) optional; parsed from the last argument index after position and speed triples * @return boolean playSound */ int LuaUnsyncedCtrl::PlaySoundFile(lua_State* L) @@ -1212,7 +1212,10 @@ static CCameraController::StateMap ParseCamStateMap(lua_State* L, int tableIdx) * @param x number * @param y number * @param z number - * @param transTime number? + * @param transTime number? (Default: `0.5`) transition duration; values below zero are clamped to zero + * @param dirX number? (Default: current camera direction X) + * @param dirY number? (Default: current camera direction Y) + * @param dirZ number? (Default: current camera direction Z) * @return nil */ int LuaUnsyncedCtrl::SetCameraTarget(lua_State* L) @@ -1330,7 +1333,7 @@ int LuaUnsyncedCtrl::RunDollyCamera(lua_State* L) /*** Pause Dolly Camera * * @function Spring.PauseDollyCamera - * @param fraction number Fraction of the total runtime to pause at, 0 to 1 inclusive. A null value pauses at current percent + * @param fraction number? Fraction of the total runtime to pause at, 0 to 1 inclusive. A null value pauses at current percent * @return nil */ int LuaUnsyncedCtrl::PauseDollyCamera(lua_State* L) @@ -1922,8 +1925,8 @@ static bool AddLightTrackingTarget(lua_State* L, GL::Light* light, bool trackEna * * @param lightHandle number * @param unitOrProjectileID integer - * @param enableTracking boolean - * @param unitOrProjectile boolean + * @param enableTracking boolean? + * @param unitOrProjectile boolean? * @return boolean success */ int LuaUnsyncedCtrl::SetMapLightTrackingState(lua_State* L) @@ -1958,8 +1961,8 @@ int LuaUnsyncedCtrl::SetMapLightTrackingState(lua_State* L) * * @param lightHandle number * @param unitOrProjectileID integer - * @param enableTracking boolean - * @param unitOrProjectile boolean + * @param enableTracking boolean? + * @param unitOrProjectile boolean? * @return boolean success */ int LuaUnsyncedCtrl::SetModelLightTrackingState(lua_State* L) @@ -2340,7 +2343,6 @@ int LuaUnsyncedCtrl::SetUnitLeaveTracks(lua_State* L) * * @function Spring.SetUnitSelectionVolumeData * @param unitID integer - * @param featureID integer * @param scaleX number * @param scaleY number * @param scaleZ number @@ -2663,7 +2665,7 @@ int LuaUnsyncedCtrl::SetUnitDefIcon(lua_State* L) * @function Spring.SetUnitDefImage * * @param unitDefID integer - * @param image string luaTexture|texFile + * @param image string? luaTexture|texFile * * @return nil */ @@ -2965,6 +2967,7 @@ int LuaUnsyncedCtrl::SetBoxSelectionByEngine(lua_State* L) * @param r number * @param g number * @param b number + * @param alpha number? * @return nil */ int LuaUnsyncedCtrl::SetTeamColor(lua_State* L) @@ -3529,7 +3532,7 @@ static bool CanGiveOrders(const lua_State* L) * * @function Spring.GiveOrder * @param cmdID CMD|integer The command ID. - * @param params CreateCommandParams Parameters for the given command. + * @param params CreateCommandParams? Parameters for the given command. * @param options CreateCommandOptions? * @param timeout integer? Absolute frame number. The command will be discarded after this frame. Only respected by mobile units. * @return boolean @@ -3798,7 +3801,7 @@ int LuaUnsyncedCtrl::SetBuildFacing(lua_State* L) /*** @function Spring.SendLuaUIMsg * @param message string - * @param mode string "s"/"specs" | "a"/"allies" + * @param mode string? "s"/"specs" | "a"/"allies" * @return nil */ int LuaUnsyncedCtrl::SendLuaUIMsg(lua_State* L) @@ -4108,12 +4111,12 @@ int LuaUnsyncedCtrl::MarkerErasePosition(lua_State* L) /*** * @class AtmosphereParams * @x_helper - * @field fogStart number - * @field fogEnd number - * @field sunColor rgba - * @field skyColor rgba - * @field cloudColor rgba - * @field skyAxisAngle xyzw rotation axis and angle in radians of skybox orientation + * @field fogStart number? + * @field fogEnd number? + * @field sunColor rgba? + * @field skyColor rgba? + * @field cloudColor rgba? + * @field skyAxisAngle xyzw? rotation axis and angle in radians of skybox orientation */ /*** Set atmosphere parameters @@ -4253,11 +4256,11 @@ int LuaUnsyncedCtrl::SetSunLighting(lua_State* L) * * @class MapRenderingParams * @x_helper - * @field splatTexMults rgba - * @field splatTexScales rgba - * @field voidWater boolean - * @field voidGround boolean - * @field splatDetailNormalDiffuseAlpha boolean + * @field splatTexMults rgba? + * @field splatTexScales rgba? + * @field voidWater boolean? + * @field voidGround boolean? + * @field splatDetailNormalDiffuseAlpha boolean? */ @@ -4571,44 +4574,44 @@ int LuaUnsyncedCtrl::SetVideoCapturingTimeOffset(lua_State* L) * * @class WaterParams * @x_helper - * @field absorb rgb - * @field baseColor rgb - * @field minColor rgb - * @field surfaceColor rgb - * @field diffuseColor rgb - * @field specularColor rgb - * @field planeColor rgb - * @field texture string file - * @field foamTexture string file - * @field normalTexture string file - * @field damage number - * @field repeatX number - * @field repeatY number - * @field surfaceAlpha number - * @field ambientFactor number - * @field diffuseFactor number - * @field specularFactor number - * @field specularPower number - * @field fresnelMin number - * @field fresnelMax number - * @field fresnelPower number - * @field reflectionDistortion number - * @field blurBase number - * @field blurExponent number - * @field perlinStartFreq number - * @field perlinLacunarity number - * @field perlinAmplitude number - * @field windSpeed number - * @field waveOffsetFactor number - * @field waveLength number - * @field waveFoamDistortion number - * @field waveFoamIntensity number - * @field causticsResolution number - * @field causticsStrength number - * @field numTiles integer - * @field shoreWaves boolean - * @field forceRendering boolean - * @field hasWaterPlane boolean + * @field absorb rgb? + * @field baseColor rgb? + * @field minColor rgb? + * @field surfaceColor rgb? + * @field diffuseColor rgb? + * @field specularColor rgb? + * @field planeColor rgb? + * @field texture string? file + * @field foamTexture string? file + * @field normalTexture string? file + * @field damage number? + * @field repeatX number? + * @field repeatY number? + * @field surfaceAlpha number? + * @field ambientFactor number? + * @field diffuseFactor number? + * @field specularFactor number? + * @field specularPower number? + * @field fresnelMin number? + * @field fresnelMax number? + * @field fresnelPower number? + * @field reflectionDistortion number? + * @field blurBase number? + * @field blurExponent number? + * @field perlinStartFreq number? + * @field perlinLacunarity number? + * @field perlinAmplitude number? + * @field windSpeed number? + * @field waveOffsetFactor number? + * @field waveLength number? + * @field waveFoamDistortion number? + * @field waveFoamIntensity number? + * @field causticsResolution number? + * @field causticsStrength number? + * @field numTiles integer? + * @field shoreWaves boolean? + * @field forceRendering boolean? + * @field hasWaterPlane boolean? */ /*** @@ -4885,7 +4888,7 @@ int LuaUnsyncedCtrl::PreloadSoundItem(lua_State* L) /*** @function Spring.LoadModelTextures * - * @param modelName string + * @param modelName string? * @return boolean? success */ int LuaUnsyncedCtrl::LoadModelTextures(lua_State* L) @@ -5470,6 +5473,7 @@ int LuaUnsyncedCtrl::Start(lua_State* L) * Note: *.ico images are not supported. * * @param iconFileName string + * @param autoFree boolean? * @return nil */ int LuaUnsyncedCtrl::SetWMIcon(lua_State* L) diff --git a/rts/Lua/LuaUnsyncedRead.cpp b/rts/Lua/LuaUnsyncedRead.cpp index 28f67f80dfc..91919e111ef 100644 --- a/rts/Lua/LuaUnsyncedRead.cpp +++ b/rts/Lua/LuaUnsyncedRead.cpp @@ -94,6 +94,7 @@ #include + /****************************************************************************** * Callouts to get state * @@ -485,7 +486,7 @@ static size_t PushSparseUnitTallyByDef(lua_State *const L, const T &v) * * @function Spring.IsReplay * - * @return boolean? isReplay + * @return boolean isReplay */ int LuaUnsyncedRead::IsReplay(lua_State* L) { @@ -4120,7 +4121,7 @@ int LuaUnsyncedRead::GetCurrentTooltip(lua_State* L) /*** * @function Spring.GetKeyFromScanSymbol - * @param scanSymbol string + * @param scanSymbol string? * @return string keyName */ int LuaUnsyncedRead::GetKeyFromScanSymbol(lua_State* L) @@ -5043,7 +5044,7 @@ int LuaUnsyncedRead::GetGroundDecalTextures(lua_State* L) /*** * - * @function Spring.SetGroundDecalTextureParams + * @function Spring.GetGroundDecalTextureParams * @param decalID integer * @return number? texWrapDistance If non-zero, sets the mode to repeat the texture along the left-right direction of the decal every texWrapFactor elmos. * @return number texTraveledDistance Shifts the texture repetition defined by texWrapFactor so the texture of a next line in the continuous multiline can start where the previous finished. For that it should collect all elmo lengths of the previously set multiline segments. diff --git a/rts/Lua/LuaVBOImpl.cpp b/rts/Lua/LuaVBOImpl.cpp index 9b1cfa00ee7..75cb5265d16 100644 --- a/rts/Lua/LuaVBOImpl.cpp +++ b/rts/Lua/LuaVBOImpl.cpp @@ -673,7 +673,7 @@ size_t LuaVBOImpl::Upload(const sol::stack_table& luaTblData, sol::optional * @param elementCount number? number of elements to download * @param forceGPURead boolean? (Default: `false`) force downloading the data from GPU buffer as opposed * to using shadow RAM buffer - * @return [number, ...][] vboData + * @return number[] vboData */ sol::as_table_t> LuaVBOImpl::Download(sol::optional attribIdxOpt, sol::optional elemOffsetOpt, sol::optional elemCountOpt, sol::optional forceGPUReadOpt) { diff --git a/rts/Sim/Units/Scripts/LuaUnitScript.cpp b/rts/Sim/Units/Scripts/LuaUnitScript.cpp index 1b6f4e7da4a..92047c1206e 100644 --- a/rts/Sim/Units/Scripts/LuaUnitScript.cpp +++ b/rts/Sim/Units/Scripts/LuaUnitScript.cpp @@ -1023,6 +1023,13 @@ void CLuaUnitScript::EndBurst(int weaponNum) { ZoneScoped; Call(LUAFN_EndBurst, /******************************************************************************/ +/*** + * UnitScript API — controls unit animation, piece visibility, and COB values. + * Accessed via `Spring.UnitScript` (synced only). + * + * @see Spring.UnitScript + * @class UnitScriptTable + */ bool CLuaUnitScript::PushEntries(lua_State* L) { RECOIL_DETAILED_TRACY_ZONE; @@ -1144,6 +1151,13 @@ static inline int ParseAxis(lua_State* L, const char* caller, int index) /******************************************************************************/ +/*** Create a Lua unit script for the given unit, replacing any existing script. + * + * @function UnitScriptTable.CreateScript + * @param unitID integer + * @param callIns table + * @return nil + */ int CLuaUnitScript::CreateScript(lua_State* L) { RECOIL_DETAILED_TRACY_ZONE; @@ -1172,6 +1186,14 @@ int CLuaUnitScript::CreateScript(lua_State* L) } +/*** Update or remove a callIn on a unit script. + * + * @function UnitScriptTable.UpdateCallIn + * @param unitID integer + * @param callin string + * @param func function? + * @return nil + */ int CLuaUnitScript::UpdateCallIn(lua_State* L) { RECOIL_DETAILED_TRACY_ZONE; @@ -1196,6 +1218,14 @@ int CLuaUnitScript::UpdateCallIn(lua_State* L) } +/*** Execute a function in the context of a unit's script environment. + * + * @function UnitScriptTable.CallAsUnit + * @param unitID integer + * @param func function + * @param ... any arguments passed to func + * @return any ... values returned by func + */ int CLuaUnitScript::CallAsUnit(lua_State* L) { RECOIL_DETAILED_TRACY_ZONE; @@ -1264,6 +1294,14 @@ int CLuaUnitScript::GetUnitValue(lua_State* L, CUnitScript* script, int arg) } +/*** Get a COB/script value for a unit (synced gadget API). + * + * @function UnitScriptTable.GetUnitCOBValue + * @param unitID integer + * @param val integer COB value ID (use COB constants) + * @param ... number optional extra args for certain COB values + * @return integer value + */ int CLuaUnitScript::GetUnitCOBValue(lua_State* L) { RECOIL_DETAILED_TRACY_ZONE; @@ -1276,6 +1314,12 @@ int CLuaUnitScript::GetUnitCOBValue(lua_State* L) } +/*** Get a COB/script value. Must be called from within a UnitScript callin. + * + * @function UnitScriptTable.GetUnitValue + * @param val integer COB value ID (use COB constants) + * @return integer value + */ int CLuaUnitScript::GetUnitValue(lua_State* L) { RECOIL_DETAILED_TRACY_ZONE; @@ -1307,6 +1351,14 @@ int CLuaUnitScript::SetUnitValue(lua_State* L, CUnitScript* script, int arg) } +/*** Set a COB/script value for a unit (synced gadget API). + * + * @function UnitScriptTable.SetUnitCOBValue + * @param unitID integer + * @param val integer COB value ID (use COB constants) + * @param param integer|boolean value to set + * @return nil + */ int CLuaUnitScript::SetUnitCOBValue(lua_State* L) { RECOIL_DETAILED_TRACY_ZONE; @@ -1319,6 +1371,13 @@ int CLuaUnitScript::SetUnitCOBValue(lua_State* L) } +/*** Set a COB/script value. Must be called from within a UnitScript callin. + * + * @function UnitScriptTable.SetUnitValue + * @param val integer COB value ID (use COB constants) + * @param param integer|boolean value to set + * @return nil + */ int CLuaUnitScript::SetUnitValue(lua_State* L) { RECOIL_DETAILED_TRACY_ZONE; @@ -1329,6 +1388,13 @@ int CLuaUnitScript::SetUnitValue(lua_State* L) } +/*** Set visibility of a piece. Must be called from within a UnitScript callin. + * + * @function UnitScriptTable.SetPieceVisibility + * @param piece integer 1-indexed piece number + * @param visible boolean + * @return nil + */ int CLuaUnitScript::SetPieceVisibility(lua_State* L) { RECOIL_DETAILED_TRACY_ZONE; @@ -1346,6 +1412,13 @@ int CLuaUnitScript::SetPieceVisibility(lua_State* L) } +/*** Emit a special effect at a piece. Must be called from within a UnitScript callin. + * + * @function UnitScriptTable.EmitSfx + * @param piece integer 1-indexed piece number + * @param type integer|string SFX type constant or CEG name + * @return nil + */ int CLuaUnitScript::EmitSfx(lua_State* L) { RECOIL_DETAILED_TRACY_ZONE; @@ -1362,6 +1435,13 @@ int CLuaUnitScript::EmitSfx(lua_State* L) } +/*** Attach a unit to a piece of another unit. + * + * @function UnitScriptTable.AttachUnit + * @param piece integer 1-indexed piece number + * @param transporteeID integer + * @return nil + */ int CLuaUnitScript::AttachUnit(lua_State* L) { RECOIL_DETAILED_TRACY_ZONE; @@ -1379,6 +1459,12 @@ int CLuaUnitScript::AttachUnit(lua_State* L) } +/*** Drop a transported unit. + * + * @function UnitScriptTable.DropUnit + * @param transporteeID integer + * @return nil + */ int CLuaUnitScript::DropUnit(lua_State* L) { RECOIL_DETAILED_TRACY_ZONE; @@ -1396,6 +1482,13 @@ int CLuaUnitScript::DropUnit(lua_State* L) } +/*** Explode a piece with the given flags. + * + * @function UnitScriptTable.Explode + * @param piece integer 1-indexed piece number + * @param flags integer SFX explosion flags (bitfield) + * @return nil + */ int CLuaUnitScript::Explode(lua_State* L) { RECOIL_DETAILED_TRACY_ZONE; @@ -1410,6 +1503,12 @@ int CLuaUnitScript::Explode(lua_State* L) } +/*** Show a flare at a piece. + * + * @function UnitScriptTable.ShowFlare + * @param piece integer 1-indexed piece number + * @return nil + */ int CLuaUnitScript::ShowFlare(lua_State* L) { RECOIL_DETAILED_TRACY_ZONE; @@ -1423,6 +1522,15 @@ int CLuaUnitScript::ShowFlare(lua_State* L) } +/*** Start spinning a piece around an axis. + * + * @function UnitScriptTable.Spin + * @param piece integer 1-indexed piece number + * @param axis integer axis (1=x, 2=y, 3=z) + * @param speed number angular speed in radians/frame + * @param accel number? angular acceleration (Default: `0`, instant) + * @return nil + */ int CLuaUnitScript::Spin(lua_State* L) { RECOIL_DETAILED_TRACY_ZONE; @@ -1440,6 +1548,14 @@ int CLuaUnitScript::Spin(lua_State* L) } +/*** Stop spinning a piece. + * + * @function UnitScriptTable.StopSpin + * @param piece integer 1-indexed piece number + * @param axis integer axis (1=x, 2=y, 3=z) + * @param decel number? angular deceleration (Default: `0`, instant) + * @return nil + */ int CLuaUnitScript::StopSpin(lua_State* L) { RECOIL_DETAILED_TRACY_ZONE; @@ -1456,6 +1572,15 @@ int CLuaUnitScript::StopSpin(lua_State* L) } +/*** Turn a piece to an angle. If speed is 0 or omitted, turns instantly. + * + * @function UnitScriptTable.Turn + * @param piece integer 1-indexed piece number + * @param axis integer axis (1=x, 2=y, 3=z) + * @param destination number target angle in radians + * @param speed number? angular speed (Default: `0`, instant) + * @return nil + */ int CLuaUnitScript::Turn(lua_State* L) { RECOIL_DETAILED_TRACY_ZONE; @@ -1479,6 +1604,15 @@ int CLuaUnitScript::Turn(lua_State* L) } +/*** Move a piece along an axis. If speed is 0 or omitted, moves instantly. + * + * @function UnitScriptTable.Move + * @param piece integer 1-indexed piece number + * @param axis integer axis (1=x, 2=y, 3=z) + * @param destination number target position + * @param speed number? movement speed (Default: `0`, instant) + * @return nil + */ int CLuaUnitScript::Move(lua_State* L) { RECOIL_DETAILED_TRACY_ZONE; @@ -1501,6 +1635,14 @@ int CLuaUnitScript::Move(lua_State* L) return 0; } +/*** Scale a piece. If speed is 0 or omitted, scales instantly. + * + * @function UnitScriptTable.Scale + * @param piece integer 1-indexed piece number + * @param destination number target scale factor + * @param speed number? scaling speed (Default: `0`, instant) + * @return nil + */ int CLuaUnitScript::Scale(lua_State* L) { RECOIL_DETAILED_TRACY_ZONE; @@ -1593,6 +1735,13 @@ int CLuaUnitScript::IsInAnimation(lua_State* L, const char* caller, AnimType typ } +/*** Check if a piece is currently turning. + * + * @function UnitScriptTable.IsInTurn + * @param piece integer 1-indexed piece number + * @param axis integer axis (1=x, 2=y, 3=z) + * @return boolean + */ int CLuaUnitScript::IsInTurn(lua_State* L) { RECOIL_DETAILED_TRACY_ZONE; @@ -1600,6 +1749,13 @@ int CLuaUnitScript::IsInTurn(lua_State* L) } +/*** Check if a piece is currently moving. + * + * @function UnitScriptTable.IsInMove + * @param piece integer 1-indexed piece number + * @param axis integer axis (1=x, 2=y, 3=z) + * @return boolean + */ int CLuaUnitScript::IsInMove(lua_State* L) { RECOIL_DETAILED_TRACY_ZONE; @@ -1607,12 +1763,25 @@ int CLuaUnitScript::IsInMove(lua_State* L) } +/*** Check if a piece is currently spinning. + * + * @function UnitScriptTable.IsInSpin + * @param piece integer 1-indexed piece number + * @param axis integer axis (1=x, 2=y, 3=z) + * @return boolean + */ int CLuaUnitScript::IsInSpin(lua_State* L) { RECOIL_DETAILED_TRACY_ZONE; return IsInAnimation(L, __func__, ASpin); } +/*** Check if a piece is currently scaling. + * + * @function UnitScriptTable.IsInScale + * @param piece integer 1-indexed piece number + * @return boolean + */ int CLuaUnitScript::IsInScale(lua_State* L) { RECOIL_DETAILED_TRACY_ZONE; @@ -1645,6 +1814,13 @@ int CLuaUnitScript::WaitForAnimation(lua_State* L, const char* caller, AnimType } +/*** Check whether the calling thread needs to wait for a turn to finish. + * + * @function UnitScriptTable.WaitForTurn + * @param piece integer 1-indexed piece number + * @param axis integer axis (1=x, 2=y, 3=z) + * @return boolean needsWait + */ int CLuaUnitScript::WaitForTurn(lua_State* L) { RECOIL_DETAILED_TRACY_ZONE; @@ -1652,12 +1828,25 @@ int CLuaUnitScript::WaitForTurn(lua_State* L) } +/*** Check whether the calling thread needs to wait for a move to finish. + * + * @function UnitScriptTable.WaitForMove + * @param piece integer 1-indexed piece number + * @param axis integer axis (1=x, 2=y, 3=z) + * @return boolean needsWait + */ int CLuaUnitScript::WaitForMove(lua_State* L) { RECOIL_DETAILED_TRACY_ZONE; return WaitForAnimation(L, __func__, AMove); } +/*** Check whether the calling thread needs to wait for a scale to finish. + * + * @function UnitScriptTable.WaitForScale + * @param piece integer 1-indexed piece number + * @return boolean needsWait + */ int CLuaUnitScript::WaitForScale(lua_State* L) { RECOIL_DETAILED_TRACY_ZONE; @@ -1677,6 +1866,12 @@ int CLuaUnitScript::WaitForScale(lua_State* L) } +/*** Signal that the Killed callin has finished. Must be called from Killed. + * + * @function UnitScriptTable.SetDeathScriptFinished + * @param wreckLevel integer? (Default: `-1`) + * @return nil + */ int CLuaUnitScript::SetDeathScriptFinished(lua_State* L) { RECOIL_DETAILED_TRACY_ZONE; @@ -1694,6 +1889,14 @@ int CLuaUnitScript::SetDeathScriptFinished(lua_State* L) /******************************************************************************/ +/*** Get the local translation of a piece relative to its rest position. + * + * @function UnitScriptTable.GetPieceTranslation + * @param piece integer 1-indexed piece number + * @return number x + * @return number y + * @return number z + */ int CLuaUnitScript::GetPieceTranslation(lua_State* L) { RECOIL_DETAILED_TRACY_ZONE; @@ -1705,6 +1908,14 @@ int CLuaUnitScript::GetPieceTranslation(lua_State* L) } +/*** Get the rotation of a piece in radians. + * + * @function UnitScriptTable.GetPieceRotation + * @param piece integer 1-indexed piece number + * @return number rx + * @return number ry + * @return number rz + */ int CLuaUnitScript::GetPieceRotation(lua_State* L) { RECOIL_DETAILED_TRACY_ZONE; @@ -1715,6 +1926,12 @@ int CLuaUnitScript::GetPieceRotation(lua_State* L) return ToLua(L, piece->GetRotation()); } +/*** Get the scale factor of a piece. + * + * @function UnitScriptTable.GetPieceScale + * @param piece integer 1-indexed piece number + * @return number scale + */ int CLuaUnitScript::GetPieceScale(lua_State* L) { RECOIL_DETAILED_TRACY_ZONE; @@ -1727,6 +1944,17 @@ int CLuaUnitScript::GetPieceScale(lua_State* L) } +/*** Get the world-space emit position and direction of a piece. + * + * @function UnitScriptTable.GetPiecePosDir + * @param piece integer 1-indexed piece number + * @return number posX + * @return number posY + * @return number posZ + * @return number dirX + * @return number dirY + * @return number dirZ + */ int CLuaUnitScript::GetPiecePosDir(lua_State* L) { RECOIL_DETAILED_TRACY_ZONE; @@ -1749,6 +1977,11 @@ int CLuaUnitScript::GetPiecePosDir(lua_State* L) /******************************************************************************/ /******************************************************************************/ +/*** Get the unit ID of the currently executing unit script. + * + * @function UnitScriptTable.GetActiveUnitID + * @return integer? unitID + */ int CLuaUnitScript::GetActiveUnitID(lua_State* L) { RECOIL_DETAILED_TRACY_ZONE; From f2a681d17066c0402beacd4a7e35e8d13925c33e Mon Sep 17 00:00:00 2001 From: Gajo Petrovic Date: Thu, 30 Jul 2026 00:20:26 +0900 Subject: [PATCH 5/6] Tweak ASAN compile & fix a couple of bugs (#2663) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Improve USE_ASAN configuration for all build types - Remove restriction to DEBUG builds only - ASAN works well with optimized builds - Add -fPIC for shared library compatibility with ASAN - Add -fno-omit-frame-pointer for better stack traces in error reports - Add -fno-optimize-sibling-calls to preserve full call chains - Set -O2 optimization level for better error detection than -O3 - Add explanatory comments for each flag 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude * Fix stack-use-after-scope in TextureCreationParams Replace std::initializer_list with std::array in wrapModes field. std::initializer_list is just a view into a temporary array, so storing it as a member variable creates a dangling pointer once the temporary is destroyed. ASAN error: ==559461==ERROR: AddressSanitizer: stack-use-after-scope on address 0x74d3039d3460 at pc 0x55c80ddb562f bp 0x7fffc14493c0 sp 0x7fffc14493b0 READ of size 4 at 0x74d3039d3460 thread T0 (recoil-main) #0 0x55c80ddb562e in GL::Impl::InitTexture(GL::TextureCreationParams const&, unsigned int, int) /build/src/rts/Rendering/Textures/Texture.cpp:33 #1 0x55c80ddb721c in GL::Texture2D::Texture2D(int, int, unsigned int, GL::TextureCreationParams const&, bool) /build/src/rts/Rendering/Textures/Texture.cpp:133 #2 0x55c80daf951c in CHeightTexture::CHeightTexture() /build/src/rts/Rendering/Map/InfoTexture/Modern/Height.cpp:49 #3 0x55c80db02149 in CInfoTextureHandler::CInfoTextureHandler() /build/src/rts/Rendering/Map/InfoTexture/Modern/InfoTextureHandler.cpp:26 #4 0x55c80daec745 in std::__detail::_MakeUniq::__single_object std::make_unique() /usr/include/c++/13/bits/unique_ptr.h:1070 #5 0x55c80daec745 in IInfoTextureHandler::Create() /build/src/rts/Rendering/Map/InfoTexture/IInfoTextureHandler.cpp:25 #6 0x55c80df3b1d1 in CWorldDrawer::InitPost() const /build/src/rts/Rendering/WorldDrawer.cpp:111 #7 0x55c8107955f2 in CGame::PostLoadRendering() /build/src/rts/Game/Game.cpp:756 #8 0x55c8107955f2 in CGame::Load(std::__cxx11::basic_string, std::allocator > const&) /build/src/rts/Game/Game.cpp:411 #9 0x55c81084498e in CLoadScreen::Init() /build/src/rts/Game/LoadScreen.cpp:146 #10 0x55c810845a80 in CLoadScreen::CreateInstance(std::__cxx11::basic_string, std::allocator >&&, std::__cxx11::basic_string, std::allocator >&&, ILoadSaveHandler*) /build/src/rts/Game/LoadScreen.cpp:215 #11 0x55c810845a80 in CLoadScreen::CreateDeleteInstance(std::__cxx11::basic_string, std::allocator >&&, std::__cxx11::basic_string, std::allocator >&&, ILoadSaveHandler*) /build/src/rts/Game/LoadScreen.cpp:200 #12 0x55c8108758d6 in CPreGame::UpdateClientNet() /build/src/rts/Game/PreGame.cpp:470 #13 0x55c810876b6f in CPreGame::Update() /build/src/rts/Game/PreGame.cpp:241 #14 0x55c80e883938 in SpringApp::Update() /build/src/rts/System/SpringApp.cpp:886 #15 0x55c80e890ffb in SpringApp::Run() /build/src/rts/System/SpringApp.cpp:927 #16 0x55c80e7fb939 in Run(int, char**) /build/src/rts/System/Main.cpp:51 #17 0x55c80cd63653 in main /build/src/rts/System/Main.cpp:104 #18 0x74d30642a1c9 in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58 #19 0x74d30642a28a in __libc_start_main_impl ../csu/libc-start.c:360 #20 0x55c80ce28fc9 in _start (/home/gajop/projects/spring-projects/spring-bar/build-linux/install/spring+0x931fc9) (BuildId: 4378a67e1e8529ce2acbdc4e3f13182a8665a60b) Address 0x74d3039d3460 is located in stack of thread T0 (recoil-main) at offset 1120 in frame #0 0x55c80daf83cf in CHeightTexture::CHeightTexture() /build/src/rts/Rendering/Map/InfoTexture/Modern/Height.cpp:23 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude * Fix heap-use-after-free in SolLuaEventListener with deferred deletion Implement deferred deletion pattern for SolLuaEventListener to prevent use-after-free when OnDetach is called during ProcessEvent execution. The issue occurred when a Lua event handler called SetInnerRML, which destroyed elements and triggered OnDetach callbacks. OnDetach immediately deleted the listener with "delete this", but ProcessEvent was still on the call stack trying to invoke the Lua callback. The fix defers deletion by: 1. Setting m_detached flag in OnDetach instead of immediate deletion 2. Checking the flag at the start of ProcessEvent (handles queued events) 3. Checking the flag at the end of ProcessEvent (handles mid-execution detachment) This ensures the listener object is only deleted when it's safe to do so, after all its methods have returned. ASAN Error: ==704643==ERROR: AddressSanitizer: heap-use-after-free on address 0x5060002a98d8 at pc 0x618574f3991e bp 0x7ffe2f43de80 sp 0x7ffe2f43de70 READ of size 8 at 0x5060002a98d8 thread T0 (recoil-main) #0 in sol::basic_reference::lua_state() const /build/src/rts/lib/sol2/sol.hpp:10244 #1 in sol::protected_function_result sol::basic_protected_function<>::invoke() /build/src/rts/lib/sol2/sol.hpp:20676 #2 in decltype(auto) sol::basic_protected_function<>::call<>(Rml::Event&, Rml::Element*&, Rml::SolLua::SolLuaDocument*&) /build/src/rts/lib/sol2/sol.hpp:20569 #3 in Rml::SolLua::SolLuaEventListener::ProcessEvent(Rml::Event&) /build/src/rts/Rml/SolLua/plugin/SolLuaEventListener.cpp:144 #4 in Rml::EventDispatcher::DispatchEvent() /build/src/rts/lib/RmlUi/Source/Core/EventDispatcher.cpp:194 #5 in Rml::Element::DispatchEvent() /build/src/rts/lib/RmlUi/Source/Core/Element.cpp:1201 #6 in Rml::Context::ProcessMouseButtonUp(int, int) /build/src/rts/lib/RmlUi/Source/Core/Context.cpp:738 #7-53 [Mouse event handling chain up to main] 0x5060002a98d8 is located 24 bytes inside of 56-byte region freed by thread T0 (recoil-main) here: #0 operator delete(void*, unsigned long) #1 in Rml::SolLua::SolLuaEventListener::~SolLuaEventListener() /build/src/rts/Rml/SolLua/plugin/SolLuaEventListener.h:47 #2 in Rml::SolLua::SolLuaEventListener::OnDetach(Rml::Element*) /build/src/rts/Rml/SolLua/plugin/SolLuaEventListener.cpp:118 #3 in Rml::EventDispatcher::~EventDispatcher() /build/src/rts/lib/RmlUi/Source/Core/EventDispatcher.cpp:65 #4 in Rml::ElementMeta::~ElementMeta() /build/src/rts/lib/RmlUi/Source/Core/Element.cpp:94 #5 in Rml::Element::~Element() /build/src/rts/lib/RmlUi/Source/Core/Element.cpp:151 #6-14 [Element destruction chain] #15 in Rml::Element::SetInnerRML() /build/src/rts/lib/RmlUi/Source/Core/Element.cpp:1100 #16-45 [Lua/Sol2 call stack from event handler calling SetInnerRML] #46 in Rml::SolLua::SolLuaEventListener::ProcessEvent(Rml::Event&) /build/src/rts/Rml/SolLua/plugin/SolLuaEventListener.cpp:144 #47 in Rml::EventDispatcher::DispatchEvent() /build/src/rts/lib/RmlUi/Source/Core/EventDispatcher.cpp:194 #48-53 [Outer event handling that triggered the inner event] previously allocated by thread T0 (recoil-main) here: #0 operator new(unsigned long) #1 in Rml::SolLua::functions::addEventListener() /build/src/rts/Rml/SolLua/bind/Element.cpp:47 #2-53 [Lua binding and event registration chain] 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude * Fix heap-use-after-free in RmlUI PopLayer by wrapping Context::Render Root cause: Context::Render was exposed to Lua without proper frame management (BeginFrame/PresentFrame). When called from Lua for render-to-texture workflows, PushLayer/PopLayer would execute outside the frame lifecycle, causing the layer stack to underflow and access freed memory. Solution: Wrap the Lua binding for Context::Render with a lambda that calls BeginFrame before and PresentFrame after rendering, ensuring proper layer stack management for all callers including RTT use cases. ASAN error: ==37278==ERROR: AddressSanitizer: heap-use-after-free on address 0x503000460eec at pc 0x5c05ab21d77a bp 0x7ffcce4f73e0 sp 0x7ffcce4f73d0 READ of size 4 at 0x503000460eec thread T0 (recoil-main) #0 0x5c05ab21d779 in RenderInterface_GL3_Recoil::PopLayer() /build/src/rts/Rml/Backends/RmlUi_Renderer_GL3_Recoil.cpp:1727 #1 0x5c05ad40d908 in Rml::RenderManager::PopLayer() /build/src/rts/lib/RmlUi/Source/Core/RenderManager.cpp:326 #2 0x5c05ad2fac86 in Rml::ElementEffects::RenderEffects(Rml::RenderStage) /build/src/rts/lib/RmlUi/Source/Core/ElementEffects.cpp:336 #3 0x5c05ad2a037a in Rml::Element::Render() /build/src/rts/lib/RmlUi/Source/Core/Element.cpp:255 #4 0x5c05ad2a02eb in Rml::Element::Render() /build/src/rts/lib/RmlUi/Source/Core/Element.cpp:253 #5 0x5c05ad2a02eb in Rml::Element::Render() /build/src/rts/lib/RmlUi/Source/Core/Element.cpp:253 #6 0x5c05ad1e000a in Rml::Context::Render() /build/src/rts/lib/RmlUi/Source/Core/Context.cpp:221 #7 0x5c05ab3a434b in bool sol::member_function_wrapper::call(bool (Rml::Context::*&)(), Rml::Context&) /build/src/rts/lib/sol2/sol.hpp:17338 #8 0x5c05ab3a434b in decltype(auto) sol::member_function_wrapper::caller::operator()(bool (Rml::Context::*&)(), Rml::Context&) const /build/src/rts/lib/sol2/sol.hpp:17344 #9 0x5c05ab3a434b in eval >&, sol::member_function_wrapper::caller, bool (Rml::Context::*&)(), Rml::Context&> /build/src/rts/lib/sol2/sol.hpp:16078 #10 0x5c05ab3a434b in decltype(auto) sol::stack::stack_detail::call::caller, bool (Rml::Context::*&)(), Rml::Context&>(sol::types, sol::types<>, std::integer_sequence, lua_State*, int, sol::member_function_wrapper::caller&&, bool (Rml::Context::*&)(), Rml::Context&) /build/src/rts/lib/sol2/sol.hpp:16131 #11 0x5c05ab3a434b in decltype(auto) sol::stack::call::caller, bool (Rml::Context::*&)(), Rml::Context&>(sol::types, sol::types<>, lua_State*, int, sol::member_function_wrapper::caller&&, bool (Rml::Context::*&)(), Rml::Context&) /build/src/rts/lib/sol2/sol.hpp:16150 #12 0x5c05ab3a434b in int sol::stack::call_into_lua::caller, bool (Rml::Context::*&)(), Rml::Context&>(sol::types, sol::types<>, lua_State*, int, sol::member_function_wrapper::caller&&, bool (Rml::Context::*&)(), Rml::Context&) /build/src/rts/lib/sol2/sol.hpp:16198 #13 0x5c05ab3a434b in int sol::call_detail::lua_call_wrapper::call(lua_State*, bool (Rml::Context::*&)(), Rml::Context&) /build/src/rts/lib/sol2/sol.hpp:18103 #14 0x5c05ab3a434b in int sol::call_detail::lua_call_wrapper::call(lua_State*, bool (Rml::Context::*&)()) /build/src/rts/lib/sol2/sol.hpp:18093 #15 0x5c05ab3a434b in int sol::call_detail::call_wrapped(lua_State*, bool (Rml::Context::*&)()) /build/src/rts/lib/sol2/sol.hpp:18506 #16 0x5c05ab3a434b in int sol::u_detail::binding::call_with_(lua_State*, void*) /build/src/rts/lib/sol2/sol.hpp:23023 #17 0x5c05ab3a434b in int sol::u_detail::binding::call_(lua_State*) /build/src/rts/lib/sol2/sol.hpp:23029 #18 0x5c05aa5925ba in sol::detail::lua_cfunction_trampoline(lua_State*, int (*)(lua_State*)) /build/src/rts/lib/sol2/sol.hpp:8398 #19 0x5c05ab361ccf in int sol::detail::static_trampoline<&(int sol::u_detail::binding::call_(lua_State*))>(lua_State*) /build/src/rts/lib/sol2/sol.hpp:8423 #20 0x5c05ab361ccf in int sol::detail::typed_static_trampoline::call_(lua_State*))>(lua_State*) /build/src/rts/lib/sol2/sol.hpp:8490 #21 0x5c05ab361ccf in int sol::u_detail::binding::call(lua_State*) /build/src/rts/lib/sol2/sol.hpp:23034 #22 0x5c05ac11b57f in luaD_precall(lua_State*, lua_TValue*, int) /build/src/rts/lib/lua/src/ldo.cpp:320 #23 0x5c05ac1499a2 in luaV_execute(lua_State*, int) /build/src/rts/lib/lua/src/lvm.cpp:620 #24 0x5c05ac11c484 in luaD_call(lua_State*, lua_TValue*, int) /build/src/rts/lib/lua/src/ldo.cpp:378 #25 0x5c05ac1056b8 in f_call /build/src/rts/lib/lua/src/lapi.cpp:812 #26 0x5c05ac119bb8 in luaD_rawrunprotected(lua_State*, void (*)(lua_State*, void*), void*) /build/src/rts/lib/lua/src/ldo.cpp:116 #27 0x5c05ac11cdc2 in luaD_pcall(lua_State*, void (*)(lua_State*, void*), void*, long, long) /build/src/rts/lib/lua/src/ldo.cpp:464 #28 0x5c05ac10d695 in lua_pcall(lua_State*, int, int, int) /build/src/rts/lib/lua/src/lapi.cpp:833 #29 0x5c05ac11272f in luaB_pcall /build/src/rts/lib/lua/src/lbaselib.cpp:389 #30 0x5c05ac11b57f in luaD_precall(lua_State*, lua_TValue*, int) /build/src/rts/lib/lua/src/ldo.cpp:320 #31 0x5c05ac1499a2 in luaV_execute(lua_State*, int) /build/src/rts/lib/lua/src/lvm.cpp:620 #32 0x5c05ac11c484 in luaD_call(lua_State*, lua_TValue*, int) /build/src/rts/lib/lua/src/ldo.cpp:378 #33 0x5c05ac1056b8 in f_call /build/src/rts/lib/lua/src/lapi.cpp:812 #34 0x5c05ac119bb8 in luaD_rawrunprotected(lua_State*, void (*)(lua_State*, void*), void*) /build/src/rts/lib/lua/src/ldo.cpp:116 #35 0x5c05ac11cdc2 in luaD_pcall(lua_State*, void (*)(lua_State*, void*), void*, long, long) /build/src/rts/lib/lua/src/ldo.cpp:464 #36 0x5c05ac10d695 in lua_pcall(lua_State*, int, int, int) /build/src/rts/lib/lua/src/lapi.cpp:833 #37 0x5c05aa1e5175 in ScopedLuaCall /build/src/rts/Lua/LuaHandle.cpp:397 #38 0x5c05aa1e5175 in CLuaHandle::RunCallInTraceback(lua_State*, LuaHashString const*, std::__cxx11::basic_string, std::allocator >*, int, int, int, bool) /build/src/rts/Lua/LuaHandle.cpp:483 #39 0x5c05aa1e6908 in CLuaHandle::RunCallInTraceback(lua_State*, LuaHashString const&, int, int, int, bool) /build/src/rts/Lua/LuaHandle.cpp:494 #40 0x5c05aa2102b2 in CLuaHandle::RunCallIn(lua_State*, LuaHashString const&, int, int) /build/src/rts/Lua/LuaHandle.h:425 #41 0x5c05aa2102b2 in CLuaHandle::DrawScreenCommon(LuaHashString const&) /build/src/rts/Lua/LuaHandle.cpp:2863 #42 0x5c05aa2102b2 in CLuaHandle::DrawScreen() /build/src/rts/Lua/LuaHandle.cpp:2881 #43 0x5c05ab9492b0 in CEventHandler::DrawScreen() /build/src/rts/System/EventHandler.cpp:708 #44 0x5c05aa24cdef in CLuaInputReceiver::Draw() /build/src/rts/Lua/LuaInputReceiver.cpp:71 #45 0x5c05ad971694 in CGame::DrawInputReceivers() /build/src/rts/Game/Game.cpp:1574 #46 0x5c05ad98b020 in CGame::Draw() /build/src/rts/Game/Game.cpp:1526 #47 0x5c05aba75cce in SpringApp::Update() /build/src/rts/System/SpringApp.cpp:889 #48 0x5c05aba8323b in SpringApp::Run() /build/src/rts/System/SpringApp.cpp:927 #49 0x5c05ab9edb79 in Run(int, char**) /build/src/rts/System/Main.cpp:51 #50 0x5c05a9f546d3 in main /build/src/rts/System/Main.cpp:104 #51 0x782b5082a1c9 in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58 #52 0x782b5082a28a in __libc_start_main_impl ../csu/libc-start.c:360 #53 0x5c05aa01a049 in _start (/home/gajop/projects/spring-projects/spring-bar/build-linux/install/spring+0x931049) (BuildId: 0100fe9eb07611ab4faec887c8a97ffbdbc3df06) 0x503000460eec is located 28 bytes inside of 32-byte region [0x503000460ed0,0x503000460ef0) freed by thread T0 (recoil-main) here: #0 0x782b512fc4d8 in free ../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:52 #1 0x782b40ef9d00 (/lib/x86_64-linux-gnu/libnvidia-glcore.so.535.274.02+0x14f9d00) (BuildId: 513f593c743a6a4f8ccb0183cb093aa171cef856) previously allocated by thread T0 (recoil-main) here: #0 0x782b512fd9c7 in malloc ../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:69 #1 0x782b40ef9851 (/lib/x86_64-linux-gnu/libnvidia-glcore.so.535.274.02+0x14f9851) (BuildId: 513f593c743a6a4f8ccb0183cb093aa171cef856) SUMMARY: AddressSanitizer: heap-use-after-free /build/src/rts/Rml/Backends/RmlUi_Renderer_GL3_Recoil.cpp:1727 in RenderInterface_GL3_Recoil::PopLayer() 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude * Fix heap-use-after-free in Element::SetClass via deferred deletion When a Lua event handler calls element.inner_rml = "...", it destroys all child elements. If Lua holds references to those children and tries to use them later in the same event handler (e.g., calling SetClass), it causes a use-after-free. Solution: Wrap SetInnerRML in the Lua binding to manually remove children and store them in a deferred deletion list. Children are kept alive until the event processing completes, preventing use-after-free when Lua accesses them. Original ASAN error: ==41062==ERROR: AddressSanitizer: heap-use-after-free on address 0x506000057c28 READ of size 8 at 0x506000057c28 thread T0 (recoil-main) #0 std::__cxx11::basic_string::size() at /usr/include/c++/13/bits/basic_string.h:1060 #6 Rml::ElementStyle::SetClass() at ElementStyle.cpp:255 #7 Rml::Element::SetClass() at Element.cpp:297 #8 [Sol2/Lua binding call chain] 0x506000057c28 is located 8 bytes inside of 64-byte region freed by thread T0: #7 Rml::ElementStyle::~ElementStyle() at ElementStyle.h:51 #11 Rml::Element::~Element() at Element.cpp:151 #26 std::vector::clear() at stl_vector.h:1603 #41 Rml::Element::~Element() at Element.cpp:148 #49 Rml::Element::SetInnerRML() at Element.cpp:1100 #52 [Sol2/Lua binding call chain - SetInnerRML called from Lua] The element was previously allocated during Element::SetClass(): #9 Rml::ElementStyle::SetClass() at ElementStyle.cpp:262 #10 Rml::Element::SetClass() at Element.cpp:297 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --------- Co-authored-by: Claude Co-authored-by: TarnishedKnight --- rts/Rml/Backends/RmlUi_Backend.cpp | 14 +++++++++++ rts/Rml/SolLua/bind/Context.cpp | 8 ++++++- rts/Rml/SolLua/bind/Element.cpp | 22 +++++++++++++++++- rts/Rml/SolLua/plugin/SolLuaEventListener.cpp | 23 ++++++++++++++++++- rts/Rml/SolLua/plugin/SolLuaEventListener.h | 2 +- 5 files changed, 65 insertions(+), 4 deletions(-) diff --git a/rts/Rml/Backends/RmlUi_Backend.cpp b/rts/Rml/Backends/RmlUi_Backend.cpp index 70383aa17fb..a46ae11982f 100644 --- a/rts/Rml/Backends/RmlUi_Backend.cpp +++ b/rts/Rml/Backends/RmlUi_Backend.cpp @@ -115,6 +115,9 @@ class BackendState : public Rml::Plugin { RmlGui::SVG::DynamicSVGPlugin* svgPlugin; Rml::UniquePtr> element_lua_texture_instancer; + + // Deferred element deletion: elements removed during event processing are kept alive here + std::vector pending_deletes; }; static Rml::UniquePtr state; @@ -124,6 +127,14 @@ bool RmlInitialized() return state && state->initialized; } +// Deferred element deletion helper - called from Lua bindings +// Elements are kept alive until RmlGui::Update() clears them +void AddPendingDelete(Rml::ElementPtr element) +{ + if (RmlInitialized() && element) + state->pending_deletes.push_back(std::move(element)); +} + bool RmlGui::Initialize() { LOG_L(L_INFO, "[RmlUi::%s] Beginning RmlUi Initialization", __func__); @@ -380,6 +391,9 @@ void RmlGui::Update() } state->contexts_to_remove.clear(); } + + // Clear deferred element deletions - safe point outside event processing + state->pending_deletes.clear(); } void RmlGui::RenderFrame() diff --git a/rts/Rml/SolLua/bind/Context.cpp b/rts/Rml/SolLua/bind/Context.cpp index bd40fd4e535..5cc64899396 100644 --- a/rts/Rml/SolLua/bind/Context.cpp +++ b/rts/Rml/SolLua/bind/Context.cpp @@ -33,6 +33,7 @@ #include "../plugin/SolLuaDataModel.h" #include "../plugin/SolLuaDocument.h" +#include "Rml/Backends/RmlUi_Backend.h" #include "sol2/sol.hpp" #include @@ -427,7 +428,12 @@ void bind_context(sol::table& namespace_table, SolLuaPlugin* slp) * @function RmlUi.Context:Render * @return boolean */ - "Render", &Rml::Context::Render, + "Render", [](Rml::Context& self) { + RmlGui::BeginFrame(); + bool result = self.Render(); + RmlGui::PresentFrame(); + return result; + }, /*** * Closes all documents currently loaded with the context. * @function RmlUi.Context:UnloadAllDocuments diff --git a/rts/Rml/SolLua/bind/Element.cpp b/rts/Rml/SolLua/bind/Element.cpp index 3bf3ecc7fea..405e047fa2a 100644 --- a/rts/Rml/SolLua/bind/Element.cpp +++ b/rts/Rml/SolLua/bind/Element.cpp @@ -36,6 +36,8 @@ #include +// Forward declaration for deferred element deletion +extern void AddPendingDelete(Rml::ElementPtr element); namespace Rml::SolLua { @@ -48,6 +50,24 @@ namespace Rml::SolLua self.AddEventListener(event, e, in_capture_phase); } + void setInnerRMLSafe(Rml::Element& self, const Rml::String& rml) + { + // Manually remove all DOM children and defer their deletion + // This prevents use-after-free when Lua holds references to children + while (self.GetNumChildren()) + { + Rml::Element* child = self.GetChild(0); + // RemoveChild returns an ElementPtr which owns the child + Rml::ElementPtr removed = self.RemoveChild(child); + // Store it for deferred deletion + AddPendingDelete(std::move(removed)); + } + + // Now set the new content + if (!rml.empty()) + self.SetInnerRML(rml); + } + void addEventListener(Rml::Element& self, const Rml::String& event, const Rml::String& code, sol::this_state s) { auto state = sol::state_view{ s }; @@ -497,7 +517,7 @@ namespace Rml::SolLua /*** @field RmlUi.Element.id string ID of this element, in the context of ``. */ "id", sol::property(&Rml::Element::GetId, &Rml::Element::SetId), /*** @field RmlUi.Element.inner_rml string Gets or sets the inner RML (markup) content of the element. */ - "inner_rml", sol::property(sol::resolve(&Rml::Element::GetInnerRML), &Rml::Element::SetInnerRML), + "inner_rml", sol::property(sol::resolve(&Rml::Element::GetInnerRML), &functions::setInnerRMLSafe), /*** @field RmlUi.Element.scroll_left integer Gets or sets the number of pixels that the content of the element is scrolled from the left. */ "scroll_left", sol::property(&Rml::Element::GetScrollLeft, &Rml::Element::SetScrollLeft), /*** @field RmlUi.Element.scroll_top integer Gets or sets the number of pixels that the content of the element is scrolled from the top. */ diff --git a/rts/Rml/SolLua/plugin/SolLuaEventListener.cpp b/rts/Rml/SolLua/plugin/SolLuaEventListener.cpp index d50575e93cc..39aee4ef964 100644 --- a/rts/Rml/SolLua/plugin/SolLuaEventListener.cpp +++ b/rts/Rml/SolLua/plugin/SolLuaEventListener.cpp @@ -115,11 +115,28 @@ namespace Rml::SolLua void SolLuaEventListener::OnDetach(Rml::Element* element) { - delete this; + // Mark as detached but don't delete immediately. + // Deletion will happen when ProcessEvent completes or on next ProcessEvent call. + m_detached = true; + m_element = nullptr; } void SolLuaEventListener::ProcessEvent(Rml::Event& event) { + // If we were detached, delete ourselves now that it's safe + if (m_detached) + { + delete this; + return; + } + + // Check if element is still valid (may have been destroyed during event processing) + if (m_element == nullptr) + return; + + if (m_element->GetContext() == nullptr) + return; + auto document = dynamic_cast(m_element->GetOwnerDocument()); if (document != nullptr && m_func.valid()) { @@ -139,6 +156,10 @@ namespace Rml::SolLua ErrorHandler(m_func.lua_state(), std::move(result)); } } + + // After processing, check if we were detached during the callback + if (m_detached) + delete this; } } // namespace Rml::SolLua diff --git a/rts/Rml/SolLua/plugin/SolLuaEventListener.h b/rts/Rml/SolLua/plugin/SolLuaEventListener.h index 5ed59c81a71..3702b0ebb7d 100644 --- a/rts/Rml/SolLua/plugin/SolLuaEventListener.h +++ b/rts/Rml/SolLua/plugin/SolLuaEventListener.h @@ -35,7 +35,6 @@ #include #include - namespace Rml { class Element; @@ -56,6 +55,7 @@ namespace Rml::SolLua private: sol::protected_function m_func; Rml::Element *m_element; + bool m_detached = false; }; } // namespace Rml::SolLua From 2cd3aacd146f793341369d8a6b040da53a65d629 Mon Sep 17 00:00:00 2001 From: lostsquirrel1 Date: Wed, 29 Jul 2026 16:47:06 +0100 Subject: [PATCH 6/6] update changelog --- doc/site/content/changelogs/_index.markdown | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/doc/site/content/changelogs/_index.markdown b/doc/site/content/changelogs/_index.markdown index 0906e9055e3..d771bc03834 100644 --- a/doc/site/content/changelogs/_index.markdown +++ b/doc/site/content/changelogs/_index.markdown @@ -20,7 +20,9 @@ This is the bleeding-edge changelog since version 2026.06, for **pre-release 202 - Added GetPrevFrameChecksum() to the Lua API - Add multi-platform sync testing (amd64-windows/linux + arm64-linux) [PR 2921](https://github.com/beyond-all-reason/RecoilEngine/pull/2921) - Optimize LuaPushNamedFoo using compile time key hashing [PR 2986](https://github.com/beyond-all-reason/RecoilEngine/pull/2986) -- fix(synctest): workaround widget timing nondeterminism [PR 3124](https://github.com/beyond-all-reason/RecoilEngine/pull/3124) +- Fixed (synctest): workaround widget timing nondeterminism [PR 3124](https://github.com/beyond-all-reason/RecoilEngine/pull/3124) +- Remove refrences to 32-bit support [PR 3033](https://github.com/beyond-all-reason/RecoilEngine/pull/3033) +- Improved setup foir ASAN build [PR 2663](https://github.com/beyond-all-reason/RecoilEngine/pull/2663) ## Documentation - Added 'First Steps with the Engine' guide for game developers @@ -58,6 +60,7 @@ This is the bleeding-edge changelog since version 2026.06, for **pre-release 202 - Added GetPrevFrameChecksum() to the Lua API [PR 2922](https://github.com/beyond-all-reason/RecoilEngine/pull/2922) - Fixes to Spring.SetMapShader [PR 3127](https://github.com/beyond-all-reason/RecoilEngine/pull/3127) - MouseHandler: route XButtons (Mouse4/5) as keybinds instead of mouse ownership [PR 2613](https://github.com/beyond-all-reason/RecoilEngine/pull/2613) +- Fixed Lua EmmyLua type annotations [PR 2888](https://github.com/beyond-all-reason/RecoilEngine/pull/2888) ## Misc - Restored lowercasing in FileSystem::GetExtension @@ -111,3 +114,5 @@ This is the bleeding-edge changelog since version 2026.06, for **pre-release 202 - Dump state handles resource packs - Fix units having the wrong path id after loading a save game. [PR 3120](https://github.com/beyond-all-reason/RecoilEngine/pull/3120) - Avoid UB in float-to-short angle casts (fixes arm64/x86 desync) [PR 3075](https://github.com/beyond-all-reason/RecoilEngine/pull/3075) +- Send gameprogress packet on connection initialization [PR 2872](https://github.com/beyond-all-reason/RecoilEngine/pull/2872) +- Separate and make sonar and RADAR jamming function as they logicall should. [PR 2980](https://github.com/beyond-all-reason/RecoilEngine/pull/2980)