Release 2025.04
This is the changelog since version 2025.01 until version 2025.04, which is still in pre-release phase.
Caveats
These are the entries which may require special attention when migrating:
- network protocol:
NETMSG_MAPDRAW(used for scribbling-related stuff: draw, point, erase), with the ID 31, renamed toNETMSG_MAPDRAW_OLDand is no longer used.NETMSG_MAPDRAWis now ID 32, and now sends coordinates asuint32instead ofint16. - uniform location 5 in vertex shaders for models extended from uvec2 (boneID, boneWeight) to uvec3 (boneID low byte, boneWeight, boneID high byte). Existing shaders should generally keep working.
- missiles now obey
myGravitywhen expired. math.clampnow errors if the lower bound is higher than the upper bound.- added minimap rotation API which can screw up the minimap (see below), set it to nil at LuaUI entry point if you don’t want to handle it.
- added
UnitGhostIconsDimmingnumerical springsetting, multiplier for ghost icon color (both on main view and minimap). This defaults to 0.8 so icons will be dimmed, set to 1.0 to get the previous behaviour. - server no longer automatically forcestarts the game if there is nobody connected after 30s.
- units no longer drop Guard commands if the target was out of map for more than 5s. For a replacement look for
control_guard.luain basecontent. - fixed the
SPRING_LOG_SECTIONSenvironment var, it no longer requires a comma in front. gl.GetAtmosphere("skyDir")now returns nil. The skyDir was never actually used for anything.- updated Tracy to v0.11.1, see the changelog at https://github.com/wolfpld/tracy/releases
- archive cache was reworked. Should be much faster to process, but will take up more disk space and it’s not yet known how stable it is.
- added GL debug annotations to main engine GL draw calls, this is similar to tracy scopes for regular code.
The caveat here is that this can have a large performance cost in
/debugGLmode. The next release is planned to feature improvements in this area. - the default value for
FontOutlineWidthspringsetting changed from 3 to 2. This is because of a width fix so doesn’t actually change the looks. - Lua
loadstringno longer accepts bytecode by default, for safety and maintainability reasons. Still works in devmode.
Deprecation notice
- the metal view automatically toggling when building a metal extractor is now deprecated.
Disable it via
Spring.SetAutoShowMetal(see below) and reimplement manually. A replacement example has been provided at(basecontent)/examples/Widgets/gui_autoshowmetal.lua.
Features
Lua language server support
LDoc has been replaced by Lua Language Server compatible annotations. This allows for language server support when editing Lua code (namely autocompletion and type checking). Type definitions can be found in the Lua library repo. This is intended to be included as a submodule in projects that use the engine. [Lua API docs]({{ site.baseurl }}{% link lua-api/docs/index.md %}) are now generated from LLS definitions instead of LDoc. This has caused a regression in docs quality, with all docs on a single page and some docs missing information. Improvements to the docs are being considered. For more information see the [Lua Language Server guide]({{ site.baseurl }}{% link guides/lua-language-server.markdown %}).
Unit groups
- units no longer removed from groups at the start of their death animation.
- added
Spring.SetUnitNoGroup(unitID, bool noGroup) → nil, whether a unit can be added to groups. - added
Spring.GetUnitNoGroup(unitID) → bool noGroup.
Infra-adjacent
- added
--calc-checksum "Archive Name"CLI param, writes a single archive’s checksum to archive cache. Use from a lobby for preloading content, though be wary not to call it in paralllel. - added
--only-localCLI param tospring.exe, prevents a replay from opening a connection. Use for mass replay parsing. - fixed the
SPRING_LOG_SECTIONSenvironment var, it no longer requires a comma in front. - server no longer automatically forcestarts the game if there is nobody connected after 30s.
More model pieces
- models now support 65534 pieces, up from 255.
- this tends to have terrible performance for even hundred-piece models, so consider it a crutch and avoid it if possible.
- add
Engine.FeatureSupport.maxPiecesPerModelconstant you can read to get the piece number. - uniform location 5 in vertex shaders for models extended from uvec2 (boneID, boneWeight) to uvec3 (boneID low byte, boneWeight, boneID high byte). Existing shaders should generally keep working.
Automatic metalmap view
- added
Spring.SetAutoShowMetal(bool) → nil. If you set it to false then selecting a “build mex” command won’t automatically enable the metal view (use the widget from(basecontent)/examples/Widgets/gui_autoshowmetal.luato replicate it). If you leave it at true (default), engine will keep automatically enabling the metal view but also spam deprecation warnings. This function will be removed at some point in the future. - added
Engine.FeatureSupport.noAutoShowMetal, false. At some point in the future this will change totrueat which point the engine will stop automatically enabling the metal view for mexes (and removeSpring.SetAutoShowMetal).
Minimap 90°/270° rotations
- minimap can now be rotated to 90° and 270°, and can be rotated manually.
- added
Spring.SetMiniMapRotation(number angle) → nil, sets the minimap angle (in radians). Snaps to cardinal directions. Only works if the springsettingMiniMapCanFlipis set to 0 (if it’s 1, engine automatically flips vertically as previously). Geometry is kept, so it may end up stretched - readjust it manually viagl.ConfigMiniMap. Spring.GetMiniMapRotation() → number anglehandles the new rotations (i.e. can returnπ/2and3π/2).
Misc rendering
- add
Platform.glVersionNum, 1/10th the version of glsl (e.g. GL 3.0 → 30). - added
gl.ClearAttachmentFBO(number? target = GL.FRAMEBUFFER, string|number attachment, clearValue0, cv1, cv2, cv3) → bool ok. Clears the “attachment” of the currently bound FBO type “target” with “clearValues”. Attachment can be either string (“color0” .. “color15”, “depth”, or “stencil”) or the equivalentGL.COLOR_ATTACHMENT#constant. - minimap is sharper with super-sampling anti-aliasing enabled
- added 3D noise to basecontent bitmaps archive, under
bitmaps/noise/recoil_noise_2025_p5_p3_w6_w4_64x64x64_RGBA.ddsandbitmaps/noise/recoil_noise_2025_p5_p3_w6_w4_128x128x128_RGBA.dds. This allows for reliable generation of cheaper 3D noise in shaders. - added (immediately deprecated)
GL.POINT_SMOOTHandgl.GetFixedState("pointSmooth") → number pointSmoothinterfaces. Don’t use them, they are just a bandage for patching up legacy code that broke along the way. New code should use a shader instead.
Praise the sun!
- the shading texture (used by minimap, water, and grass) now tracks sun position changes immediately.
- the “modern” sky renderer now uses the actual sun color instead of hardcoded RGB 253/251/211.
Spinning skybox
- added new tag to
Spring.SetAtmosphere,skyAxisAngle, array of 4 numbers: X, Y, Z (defining an axis) and rotation around that axis. Lets the skybox be drawn at an angle and possibly spin. Complex rotations involve doing math yourself. - added
skyAxisAngletogl.GetAtmosphere, same format. - added
atmosphere.skyAxisAngleto mapinfo, same format. - removed
atmosphere.skyDirfrom mapinfo, it didn’t actually affect anything. gl.GetAtmosphere("skyDir")now returns nil.
Debugging for rendering
- added
VBO:GetID() → number, gets the internal OpenGL ID for use in debugging. - added a second return value to
gl.CreateShader, now also returns the internal ID for use in debugging. - added
gl.ObjectLabel(objType, objID, string identifier) → nil. Type is one of the new GL object type constants (GL.BUFFER,GL.PROGRAM_PIPELINEetc., see the list at the bottom). Adds a text label to an object for debugging tools. Conditionally available based on gfx drivers etc (do a nil check). - added
gl.PushDebugGroup(number ID, string message, bool isThirdParty) → nil. Conditionally available (depends on player’s platform, do a nil check). Creates a group (which is basically a scope, similar to tracy) with given message onto the debug stack. Meant for debugging tools, seems to be specifically for “nVidia nSight 2024.04”. Apparently tools can struggle to see the annotations when FBOs are raw bound. - added
gl.PopDebugGroup() → nil, conditionally available, pops a previously pushed debug group from the stack. - the engine now natively pushes some GL debug groups in relevant scopes, similar to tracy. A caveat is that this can have a noticeable perf cost when GL debug is enabled.
Unit rotations
Spring.GetUnitDirectionandSpring.GetFeatureDirectionnow return 9 values (from 3). The initial three stay as the XYZ of the “front” direction in unit space, the new ones are XYZ of the “right” direction and the XYZ of the “up” direction.
- local frontX, frontY, frontZ = Spring.GetUnitDirection(unitID)
+ local frontX, frontY, frontZ, rightX, rightY, rightZ, upX, upY, upZ = Spring.GetUnitDirection(unitID)
Spring.SetUnitDirectionandSpring.SetFeatureDirectionnow accept up to 6 args (from 3). The new ones are the XYZ for the “right” direction (since just the “front” is ambiguous) and are optional (same behaviour as previous, i.e. some direction based on ground). Note there is no way to specify the “up” dir (but given the “right” dir it is unambiguous; and if you have front+up dirs you can unambiguously find the right dir).
Spring.SetUnitDirection(unitID, frontX, frontY, frontZ) -- still works
+ Spring.SetUnitDirection(unitID, frontX, frontY, frontZ, rightX, rightY, rightZ) -- new
Engine feature support tags
Added a bunch of feature support entries to the Engine.FeatureSupport tags. These are for the future.
Most likely you don’t need to worry about those since games will receive patches if there is a change.
- number
gunshipCruiseAltitudeMultiplier, currently1.5. Right now gunship cruise altitude is multiplied by this. - bool
noRefundForConstructionDecay, currentlyfalse. At some point refunds for construction decay will be handed over to Lua. - bool
noRefundForFactoryCancel, currentlyfalse. At some point refunds for factory cancel will be handed over to Lua. - bool
noOffsetForFeatureID, currentlyfalse. At some point featureIDs in mixed contexts (e.g. target ID for reclaim) won’t require to be offset.
Font rendering performance
- added
UseFontConfigSystemFontsboolean springsetting, default true. Whether to search system fonts for fallbacks. - added
FontConfigSearchAttributesboolean springsetting, default true. Whether to try to match provided font attributes for fallbacks. Costs some perf but can result in better glyph matches. - added
FontConfigApplySubstitutionsboolean springsetting, default true. Whether to apply pattern config substitutions. Disabling substitutions can make fonts randomly break but you can take the gamble to save a bit of perf that way. - added
MaxPinnedFontsinteger springsetting, default 10. How many fonts are kept in a cache. Reduces perf spikes if many fonts are used (keep in mind players can send mixed chat with multiple exotic characters mxied), though if many fonts are loaded it can increase peak memory usage. - extra optimisations to baseline fonts perf even if you don’t touch any of the above.
- Lua
font:Begin(bool? userDefinedBlending)now takes an optional boolean param that makes it use whatever blending mode you set viagl.BlendFunc. The default, if set tofalse, is the current behaviour where it always usesGL.SRC_ALPHA, GL.ONE_MINUS_SRC_ALPHA. - fixed font outline widths. Previously values 2N+1 looked the same as 2N.
As a consequence, the default value for
FontOutlineWidthspringsetting changed from 3 to 2 to maintain the same look.
rmlUI
- add deep reactivity to rmlUI data models.
- add SVG support to rmlUI.
- fix rmlUI getting blurry with anti-aliasing.
Building ghosts
- added
UnitGhostIconsDimmingnumerical springsetting, multiplier for ghost icon color (both on main view and minimap). This defaults to 0.8 so icons will be dimmed, set to 1.0 to get the previous behaviour. - fix minimap icons revealing whether a building ghost was dead or not.
Misc
SendToUnsyncedcan now send (andgadget:RecvFromSyncedcan receive) tables. Unsynced receives a copy which is recursively stripped of anything that isn’t a supported type and has no metatable (same asSYNCED.proxy).- add
Spring.GetSoundDevices() → { { name = "...", }, { name = "...", }, ... }. May be extended with more info than just name in the future. - add
wupget:ActiveCommandChanged(cmdID?, cmdType?) → nil. - the
Spring.GiveOrderfamily of functions now acceptnilas params (same as{}) and options (same as0). - add
Spring.SetUnitStorage(unitID, "m"|"e", value) → nil. - add
Spring.GetUnitStorage(unitID) → numbers metal, energy. - add
Spring.GetProjectilesInSphere(x, y, z, radius, bool excludeWeaponProjectiles = false, bool excludePieceProjectiles = false) → {projID, projID, ...}. - add “ttl” field to
Spring.Set/GetUnitWeaponState, projectile lifetime in seconds. - added
Game.buildGridResolution, number which is currently 2. This means that buildings created via native build orders are aligned to 2 squares. - add
Spring.GetMouseButtonsPressed(indexA, indexB, ..., indexN) → bool pressedA, pressedB, ..., pressedN. For example,Get(4, 1)will return the states for mouse4 and LMB (mouse1). Has to provide at least 1 index. - add
Platform.totalRAM, in megabytes. - add
Spring.SetProjectileTimeToLive(projID, framesTLL) → nil. - add
Spring.GetUnitSeismicSignature(unitID) → number. The Set was already added earlier. - added
Engine.gameSpeedandEngine.textColorCodes, same as the existing entries inGame.. The practical effect is that the Engine table is available in some LuaParser environments that Game isn’t. - missiles now obey
myGravitywhen expired. - NaN and infinity coming from Lua is now sometimes rejected. Coverage isn’t yet comprehensive.
- units no longer drop Guard commands if the target was out of map for more than 5s.
socket.luamoved from being a loosely distributed file under./socket.luato basecontent./LuaSocket/socket.lua.- add
experience.experienceGradenumber modrule, same as calling Spring.SetExperienceGrade. - the
allowHoverUnitStrafingmodrule now defaults tofalse. Previously it defaulted tofalsefor HAPFS andtruefor QTPFS. - bumpmapped water (aka
/water 4) now has a different default texture. - add more data to desync dumps. The extra data will be dumped to files named
ClientGameState-<random number>-[<desync frame>-<desync frame>].txt, i.e. same as before. - Lua
loadstringno longer accepts bytecode, for safety and maintainability reasons.
Fixes
- fix
Spring.SetUnitHealth(build < 1)not reverting the unit into a nanoframe. - fix rmlUI getting extra blurry with anti-aliasing.
- fix minimap icons revealing whether a building ghost was dead or not.
- fix CPU pinning, no longer tries to pin itself to bad choices (efficiency cores, hyperthreads on the same physical core, performance cores on a dedicated server)
- fixed
Spring.ShareResources(teamID, "units", nil)breaking due to the explicit nil. - fix scribblings and labels breaking on maps larger than 64xN.
- fix basecontent
actions.luaproviding an incorrectKeyActionhandler. - fix height of buildings under construction not updating properly.
- fix landed aircraft starting to levitate when EMPed.
- fix units being stuck if an overlapping push-resistant unit stops.
- fix the “modern” sky renderer not adjusting to changes via
Spring.SetAtmosphere. - fix (or at least mostly reduce) a freeze when you remove multiple thousands of units from a factory queue.
- fix a crash when loading a 7z archive without the ‘mod time’ field set for its files.
- fix a crash when loading some WAV sounds.
- fix some crashes related to fonts.
- fix a minor stack corruption when calling
Spring.GiveOrderas a spectator. - fix some graphics crashes on Linux with Wayland.
- fix
Spring.GetUnitCurrentCommandnot actually accepting negative indices. - fix desyncs via unsynced Lua path requests.
- fix performance spikes when high unit counts get a move order.
GL object type constants
For use with the new gl.ObjectLabel (see above):
GL.BUFFERGL.SHADERGL.PROGRAMGL.VERTEX_ARRAYGL.QUERYGL.PROGRAM_PIPELINEGL.TRANSFORM_FEEDBACKGL.RENDERBUFFERGL.FRAMEBUFFER