This is the changelog since version 2025.01 until version 2025.03, which is still in pre-release phase.

Caveats

These are the entries which may require special attention when migrating:

  • network protocol: scribbling-related (draw, point, erase) messages now send coordinates as uint32 instead of int16. This may break replay parsing.
  • 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 myGravity when expired.
  • math.clamp now 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.
  • server no longer automatically forcestarts the game if there is nobody connected after 30s.
  • fixed the SPRING_LOG_SECTIONS environment 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 /debugGL mode. The next release is planned to feature improvements in this area.

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 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.

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-local CLI param to spring.exe, prevents a replay from opening a connection. Use for mass replay parsing.
  • fixed the SPRING_LOG_SECTIONS environment 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.maxPiecesPerModel constant 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.lua to 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 to true at which point the engine will stop automatically enabling the metal view for mexes (and remove Spring.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 springsetting MiniMapCanFlip is 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 via gl.ConfigMiniMap.
  • Spring.GetMiniMapRotation() → number angle handles the new rotations (i.e. can return π/2 and 3π/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 equivalent GL.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.dds and bitmaps/noise/recoil_noise_2025_p5_p3_w6_w4_128x128x128_RGBA.dds. This allows for reliable generation of cheaper 3D noise in shaders.

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 skyAxisAngle to gl.GetAtmosphere, same format.
  • added atmosphere.skyAxisAngle to mapinfo, same format.
  • removed atmosphere.skyDir from 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_PIPELINE etc., 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.GetUnitDirection and Spring.GetFeatureDirection now 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. ```diff
  • local frontX, frontY, frontZ = Spring.GetUnitDirection(unitID)
  • local frontX, frontY, frontZ, rightX, rightY, rightZ, upX, upY, upZ = Spring.GetUnitDirection(unitID) ```
  • Spring.SetUnitDirection and Spring.SetFeatureDirection now 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). ```diff 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, currently 1.5. Right now gunship cruise altitude is multiplied by this.
  • bool noRefundForConstructionDecay, currently false. At some point refunds for construction decay will be handed over to Lua.
  • bool noRefundForFactoryCancel, currently false. At some point refunds for factory cancel will be handed over to Lua.
  • bool noOffsetForFeatureID, currently false. At some point featureIDs in mixed contexts (e.g. target ID for reclaim) won’t require to be offset.

Font rendering performance

  • added UseFontConfigSystemFonts boolean springsetting, default true. Whether to search system fonts for fallbacks.
  • added FontConfigSearchAttributes boolean springsetting, default true. Whether to try to match provided font attributes for fallbacks. Costs some perf but can result in better glyph matches.
  • added FontConfigApplySubstitutions boolean 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 MaxPinnedFonts integer 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.

Misc

  • 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.GiveOrder family of functions now accept nil as params (same as {}) and options (same as 0).
  • add Spring.SetUnitStorage(unitID, "m"|"e", value) → nil.
  • add Spring.GetUnitStorage(unitID) → numbers metal, energy.
  • added Game.buildGridResolution, number which is currently 2. This means that buildings created via native build orders are aligned to 2 squares.
  • add Platform.totalRAM, in megabytes.
  • added Engine.gameSpeed and Engine.textColorCodes, same as the existing entries in Game.. The practical effect is that the Engine table is available in some LuaParser environments that Game isn’t.
  • missiles now obey myGravity when expired.
  • NaN and infinity coming from Lua is now sometimes rejected. Coverage isn’t yet comprehensive.
  • socket.lua moved from being a loosely distributed file under ./socket.lua to basecontent ./LuaSocket/socket.lua.
  • add experience.experienceGrade number modrule, same as calling Spring.SetExperienceGrade.
  • the allowHoverUnitStrafing modrule now defaults to false. Previously it defaulted to false for HAPFS and true for QTPFS.
  • bumpmapped water (aka /water 4) now has a different default texture.

Fixes

  • fix Spring.SetUnitHealth(build < 1) not reverting the unit into a nanoframe.
  • 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.lua providing an incorrect KeyAction handler.
  • 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.

GL object type constants

For use with the new gl.ObjectLabel (see above):

  • GL.BUFFER
  • GL.SHADER
  • GL.PROGRAM
  • GL.VERTEX_ARRAY
  • GL.QUERY
  • GL.PROGRAM_PIPELINE
  • GL.TRANSFORM_FEEDBACK
  • GL.RENDERBUFFER
  • GL.FRAMEBUFFER

Written by: sprunk

sprunk