Release 2026.07
Release 2026.07
This is the changelog since version 2025.07 until version 2025.07.04, which was released on 2026-08-04.
Caveats
- removed Java bindings for Skirmish AI.
- Lua environment sandboxing changes, each has a caveat. See below.
- builders now perform an extra block check immediately when a build command reaches the front of the queue, in addition to the existing periodic check. This can result in an event spam e.g. if a builder is on repeat between multiple queued buildings and they’re all blocked.
- mouse4 and mouse5 ignore mouse ownership and produce MousePress/MouseRelease events even if another mouse button is already pressed. Make sure your wupget handlers handle this correctly if they manage wupget mouse ownership.
- sonar jamming now jams sonar only; no longer blocks regular radar for units in water, i.e. surface ships. No easy replacement.
- errors when loading modrules are no longer silently ignored, rather there’s an error popup. Wrap everything in pcall if needed.
- removed
LimitDgunfrom being listed inEngineOptions.lua, add it to modoptions if you used it as such. - removed some basecontent gadgets that used
LimitDgunand other long-removed engineoptions. Copypaste them from older basecontent if needed.
Features
Lua environment sandboxing
- LuaSocket no longer inits before Lua sandboxing. Anything that relied on the previous order of execution won’t work.
- unsynced LuaRules (incl. unsynced LuaGaia) now has access to
ioandoslibraries. Watch out when loading maps etc. - unsynced LuaRules (incl. unsynced LuaGaia) now has access to the
debuglibrary by default (no longer requires devmode).
Replay path getters
- add
Spring.GetReplayFilePath() → string?, returns path of replay being watched. - add
Spring.GetReplayRecordingFilePath() → string?, returns path of replay to be produced. Note that this is just a prospective file path (nothing is written until the match ends), and that it possible to record a replay of a replay.
Build commands
- builders now perform an extra block check immediately when a build command reaches the front of the queue. This is in addition to the existing periodic (≈ 0.4 Hz) block check. A block check cancels a build command if the build site is hard-blocked (“red squares”, as opposed to “yellow squares” with reclaimables/mobiles).
- add
Spring.SetEngineBuildSquareRendering(bool) → nil, for disabling the native rendering of the footprint grid when a build command is selected. - add
wupget:DrawBuildSquare(unitDefID, x, z, facing, statuses) → nilunsynced callin, fires when a build command is selected. Statuses is a 1D array for the status of each tile: 0 blocked (red), 1 occupied (yellow), 2 reclaimable (yellow), 3 open (green). This fires even if native drawing is enabled.
Lua trace ray
- adds
Spring.TraceRayBetweenPositions(xA, yA, zA, xB, yB, zB, type) -> {{dist, objID, type}, ...} - adds
Spring.TraceRayInDirection(x, y, z, dx, dy, dz, length, type) -> {{dist, objID, type}, ...} - type is a string, “unit”, “feature”, or (for input only) “both”
- the returned array is sorted by distance, starting from closest.
Blank map splats
Blank map generator now interprets some mapoptions. These are for filling entries in the generated mapinfo.lua file.
blank_map_splatdetailtex, string with the pathblank_map_splatdistr, string with the pathblank_map_splattexscale1 .. 4, numberblank_map_splattexmult1 .. 4, numberblank_map_splatdetailnormaltex1 .. 4, string with the pathblank_map_splatdetailnormaldiffusealpha, bool
Other runtime map texturing stuff
- fix
Spring.SetSkyBoxTexturenot working if the map didn’t have a skybox from the start - fix map shaders having stale uniforms and being completely broken for forward rendering. This fix is signalled by the
Engine.FeatureSupport.reliableLuaMapShadersflag.
Input emulation
Added a bunch of debug.emulateFoo functions that emulate input. Useful for automated testing of UI. Buttons are considered pressed from any source for edge-based events (i.e. pressing a “real” button when it is already pressed via emulation, or vice versa, will not produce a KeyPressed event; ditto release if it is still pressed from the other source).
debug.emulateKeyPress(keycode). The event will have a scancode based on the current keyboard layout (i.e. possibly “unknown” if no keyboard, such as a headless VM).debug.emulateKeyRelease(keycode)debug.emulateMousePress(button)debug.emulateMouseRelease(button)debug.emulateMouseMove(dx, dy). Does not actually move the mouse, so getters won’t reflect it, but you canSpring.WarpMousealongside it.debug.emulateMouseWheel(number delta). Note that this accepts fractions, but most physical mice produce integer deltas (+1, -1).debug.clearEmulatedInput(). Releases all emulated presses.
Keybind-related work
- add
cancelcommandaction for binding, cancels the currently selected command. The hardcoded Escape key binding still works. - mouse4 and mouse5 ignore mouse ownership and produce MousePress/Release events even if another mouse button is already pressed.
- fix
sc_nonusbackslashscancode name (wassc_nonusbacklash) - fix
/unbindactionnot clearing scancode bindings - fix
/unbindnot working with keychains longer than 1 key - fix stale returns from
Spring.GetActionHotKeys
Resourcing
- add
gadget:ResourceExcess({[teamID] = {m, e, ...}}) -> bool handledGameside. Runs every frame. If you return false, the engine will do the existing behaviour where the excess is accumulated until a slowupdate and shared to teammates if possible. - add
Spring.AddTeamResourceExcessStats(teamID, resourcetype, amount). Adjusts the stats for endgame graphs purposes (does not do anything to the actual resources). Useful for when you handle excess yourself with the callin above.
Custom teamcolor palette
- add
Spring.SetCustomPaletteColor(paletteID, r, g, b) → nil. Sets a palette color for shader use. See below. - add
Spring.GetCustomPaletteColor(paletteID) → r, g, b. - add
Engine.maxCustomPaletteID, the highest available palette ID. - add
Spring.SetUnitPaletteIndex(unitID, paletteID?) → nil. Assigns a paletteID to a unit. Use nil to reset to the default palette. - add
Spring.GetUnitPaletteIndex(unitID) → paletteID?. - add
Spring.SetFeaturePaletteIndex(featureID, paletteID?). - add
Spring.GetFeaturePaletteIndex(featureID) → paletteID?. - the
teamColorarray in shaders now has much more room and contains both teamcolors and colors of the custom palette, as per above. - the actual teamID is now available as the fifth byte (first byte of the second 4-byte composite) in model uniform data.
- note that the value of the palette index is different than what is seen from Lua. Units with no custom paletteID have the index point to an entry that contains their team color.
- the basecontent teamcolor shader takes the above changes into account. Existing custom shaders that use
instData.z & 0xFFfor teamID should keep working as long as the palette feature isn’t used, to support it properly the constant needs to be0x7FFinstead. - ghosts of out-of-sight buildings now draw with the unit’s last seen palette color. This also fixes the bug where the teamcolor updated to always reflect the unit’s real team even if it changed out of sight.
- projectiles and building ghosts in constructor queues unchanged, they still just draw teamcolor naively and cannot use a shader.
Building ghosts
- building ghosts now use the custom palette teamcolor.
- fix building ghosts always showing their “true” teamcolor, now they stick to their last known teamcolor (including custom palette).
- fix full-view spectators seeing a ground decal wherever their spectated team sees a ghost with decal.
Misc
- removed Java bindings for Skirmish AI.
- entries returned by
VFS.GetAvailableAIs()now have a newisLuaAIboolean (skirmish AI otherwise). - errors when loading modrules are no longer silently ignored, rather there’s an error popup.
- add
Spring.GetPrevFrameSyncChecksum() -> stringandPlatform.hasSyncChecksums. Useful for correctness checks. Note that the checksum looks like a number but is NOT convertible to one in Lua (checksum is a 32-bit number but Lua numbers have 24-bit precision). - add string
Platform.architecture. Usually “x86_64”, with some ongoing work to support “arm64”. - add
Spring.GetClosestEnemyUnit(x, y, z, range = inf) → unitID?to LuaUI. - add
Spring.GetClosestEnemyUnit(x, y, z, range = inf, allyTeamID, bool useLoS = true, bool spherical = false, bool requireEnemyToSeePos = false) → unitID?to LuaRules. - add
Engine.FeatureSupport.reliableLuaMapShadersbool, means issues with map shaders having stale uniforms and being completely broken for forward rendering are fixed. - the
/dumpAtlascommand now accepts a file format (e.g./dumpatlas proj tga), defaults to the existingpng. - Barbarian AI bundled with the engine updated to version 1.6.28. Just maintenance, no new features.
Fixes
- fix LuaSocket initialisation for LuaMenu.
- fix unitsync not running functions passed to
Spring.TimeCheck. - fix Skirmish AI API compilation issues due to
AIFloat3having a non-trivial constructor. - attempt to fix the lack of
wupget:GameProgresscalls when initially catching up. - fix Lua
VBO::CopyToto copy VBO data CPU side in additon to GPU side. Formerly it only copied data GPU side. - fix QTPFS path cleanup when an immediate path search fails