Toggle menu
Toggle preferences menu
Toggle personal menu
Not logged in
Your IP address will be publicly visible if you make any edits.

Recoil:Migrating From Spring: Difference between revisions

From Fightorder
No edit summary
No edit summary
 
Line 19: Line 19:
Rendering fonts now obeys the GL color state. Text may appear in a different color than expected. To restore previous behavior, add a <code>gl.Color</code> call before <code>gl.Text</code>:
Rendering fonts now obeys the GL color state. Text may appear in a different color than expected. To restore previous behavior, add a <code>gl.Color</code> call before <code>gl.Text</code>:


<syntaxhighlight lang="lua">
<code>
gl.Color(1, 1, 1, 1)  -- set white before drawing text
gl.Color(1, 1, 1, 1)  -- set white before drawing text
gl.Text("Hello world", x, y, size, options)
gl.Text("Hello world", x, y, size, options)
</syntaxhighlight>
</code>


Alternatively, use inline colour codes: <code>\255\rrr\ggg\bbb</code>
Alternatively, use inline colour codes: <code>\255\rrr\ggg\bbb</code>

Latest revision as of 23:28, 5 March 2026

Recoil: Migrating From Spring

Recoil is a fork and continuation of the Spring RTS engine (version 105.0), maintained by the Beyond All Reason team. While Recoil is mostly compatible with Spring 105, divergences have naturally developed over time. This page lists the most relevant breaking changes and how to fix them when migrating a game or widget from Spring to Recoil.

For a quick overview of why you might choose Recoil over the original Spring, see Recoil:Why Recoil.

Versioning

Recoil switched to a date-based versioning scheme beginning with version 2025.01. Version numbers jump from 105-xxxx to 2025.xx. Use Engine.versionMajor, Engine.versionMinor, and Engine.commitsNumber instead of the old commit-count scheme where possible.

For feature detection without version comparisons, use the Engine.FeatureSupport table (see Feature Support Table below).

Lua API Changes

Font Rendering

Rendering fonts now obeys the GL color state. Text may appear in a different color than expected. To restore previous behavior, add a gl.Color call before gl.Text:

gl.Color(1, 1, 1, 1) -- set white before drawing text gl.Text("Hello world", x, y, size, options)

Alternatively, use inline colour codes: \255\rrr\ggg\bbb

Spring.Marker Functions

Spring.MarkerAddPoint, Spring.MarkerAddLine, and Spring.MarkerErasePosition no longer accept numerical 0 as false for the onlyLocal parameter. Lua semantics now apply (0 is truthy). Change 0 to false:

<syntaxhighlight lang="lua"> -- Before Spring.MarkerAddPoint(x, y, z, text, 0) Spring.MarkerAddLine(x1, y1, z1, x2, y2, z2, 0, playerId) Spring.MarkerErasePosition(x, y, z, noop, 0, playerId)

-- After Spring.MarkerAddPoint(x, y, z, text, false) Spring.MarkerAddLine(x1, y1, z1, x2, y2, z2, false, playerId) Spring.MarkerErasePosition(x, y, z, noop, false, playerId) </syntaxhighlight>

Spring.GetConfig Functions

Spring.GetConfigInt, Spring.GetConfigFloat, and Spring.GetConfigString now accept nil as the second argument (the default value to return if the key is not set). Previously, nil was treated as 0 (Int/Float) or "" (String).

To restore previous behavior:

<syntaxhighlight lang="lua"> local originalGetConfigInt = Spring.GetConfigInt Spring.GetConfigInt = function(key, def)

   return originalGetConfigInt(key, def) or 0

end </syntaxhighlight>

Spring.GetSelectedUnitsSorted and Spring.GetSelectedUnitsCounts

The tables returned by Spring.GetSelectedUnitsSorted and Spring.GetSelectedUnitsCounts no longer contain an n key with the number of unitDefs. Use the second return value instead:

<syntaxhighlight lang="lua"> -- Before local counts = Spring.GetSelectedUnitsCounts() local unitDefsCount = counts.n counts.n = nil

-- After local counts, unitDefsCount = Spring.GetSelectedUnitsCounts() </syntaxhighlight>

Spring.UnitIconSetDraw Renamed

Spring.UnitIconSetDraw has been renamed to Spring.SetUnitIconDraw. The old spelling still works but is deprecated and will be removed in a future version.

Spring.GetUnitCommands / Spring.GetCommandQueue

Spring.GetUnitCommands(unitID, 0) and Spring.GetCommandQueue are deprecated for counting queue size. Use the new dedicated function instead:

<syntaxhighlight lang="lua"> -- Before local count = Spring.GetUnitCommands(unitID, 0)

-- After local count = Spring.GetUnitCommandCount(unitID) </syntaxhighlight>

Similarly, Spring.GetFactoryCommands(unitID, 0) is deprecated in favor of Spring.GetFactoryCommandCount(unitID).

Iterating Synced Proxy Tables

The functions snext, spairs, and sipairs for iterating synced proxy tables have been removed. They have been equivalent to the standard Lua next, pairs, and ipairs for years. Replace them:

<syntaxhighlight lang="lua"> -- Before for key, value in spairs(SYNCED.foo) do ... end

-- After for key, value in pairs(SYNCED.foo) do ... end </syntaxhighlight>

VFS Mapping API

Spring.MapArchive and Spring.UnmapArchive (and equivalently VFS.MapArchive / VFS.UnmapArchive) have been temporarily removed due to sync-unsafety. Use Spring.UseArchive / VFS.UseArchive in the meantime. The original functions may return at a future date.

UnitUnitCollision Return Value

The return value from the UnitUnitCollision callin is now ignored, and only one event fires per collision pair (instead of two). There is currently no replacement for the old two-event behavior.

gl.GetMatrix Removed

gl.GetMatrix and the interface accessible from its returned matrix object have been removed. These were unused; no direct replacement is available.

Removed Platform Constants

The following depth buffer support constants have been removed:

  • Platform.glSupport16bitDepthBuffer
  • Platform.glSupport24bitDepthBuffer
  • Platform.glSupport32bitDepthBuffer

Replace with:

<syntaxhighlight lang="lua"> Platform.glSupportDepthBufferBitDepth >= 16 -- or 24, or 32 </syntaxhighlight>

Game.allowTeamColors Removed

The deprecated Game.allowTeamColors entry (which was always true) has been removed. If you used it as a boolean condition, note that its removal inverts its effect — previously if Game.allowTeamColors then was always true; now the variable is nil (falsy).

Removed UnitDefs Keys

The following deprecated UnitDefs keys have been removed (they previously returned zero with a warning):

Removed Key
techLevel
harvestStorage
extractSquare
canHover
drag
isAirBase
cloakTimeout
minx, miny, minz
maxx, maxy, maxz
midx, midy, midz

tdfID Removed from WeaponDefs

The tdfID field has been removed from weapon defs and the WeaponDefs table. Any remaining interfaces receive weaponDefID instead.

loadstring Bytecode

loadstring in Lua no longer accepts bytecode by default, for safety and maintainability reasons. It still works in devmode.

Unit Defs

Hovercraft and Ships Upright Behavior

Hovercraft and ships brought out of water are no longer forced to be upright. To restore previous behavior, add upright = true to all unit defs whose movedef is of the hovercraft or ship type.

useFootPrintCollisionVolume

Units with useFootPrintCollisionVolume = true but no collisionVolumeScales set will now actually use the footprint volume (previously they mistakenly used the model's bounding sphere).

To keep the old hitvolume behavior, set useFootPrintCollisionVolume = false for units with no collisionVolumeScales. This can be done in unit def post-processing:

<syntaxhighlight lang="lua"> for unitDefID, unitDef in pairs(UnitDefs) do

   if not unitDef.collisionvolumescales then
       unitDef.usefootprintcollisionvolume = nil
   end

end </syntaxhighlight>

Tree Feature Defs

The feature defs treetype0 through treetype16 are now provided by the basecontent archive instead of the engine itself. Games that shipped their own basecontent will know what to do; most games are unaffected.

Firestarter Weapon Tag

The firestarter weapon tag is no longer capped at 10000 in defs (which was 100 in Lua WeaponDefs after rescaling). It is now uncapped.

To restore the previous cap:

<syntaxhighlight lang="lua"> for weaponDefID, weaponDef in pairs(WeaponDefs) do

   if weaponDef.firestarter then
       weaponDef.firestarter = math.min(weaponDef.firestarter, 10000)
   end

end </syntaxhighlight>

acceleration and brakeRate (Deprecation Warning)

The unit def tags acceleration and brakeRate are scheduled for a unit change from elmo/frame to elmo/second. They currently still work as before, but it is strongly recommended to migrate to maxAcc and maxDec respectively, which will remain in elmo/frame:

<syntaxhighlight lang="lua"> -- In unit def files or post-processing: -- acceleration → maxAcc (elmo/frame, unchanged) -- brakeRate → maxDec (elmo/frame, unchanged) </syntaxhighlight>

Def Validity Checks

Some invalid or missing def values are now handled more strictly:

  • Negative values for health, speed, reverse speed, metal/energy/buildtime cause the unit def to be rejected (previously clamped to 0.1).
  • Negative values for acceleration and brake rate cause rejection (previously, absolute value was used).
  • Metal cost can now be 0 (undefined metal cost defaults to 0 instead of 1).
  • Weapon edgeEffectiveness can now be exactly 1 (previously capped at 0.999).
  • Weapon damage and unit armor multiplier can now be 0.

trackStretch Reciprocal Change

The unit def trackStretch values are now treated reciprocally. A stretch factor of 2 now means the track is stretched 2× longer (previously it was squeezed 0.5× shorter). Adjust your values accordingly.

GroundDecals Springsetting

The GroundDecals springsetting is now a boolean. The previous behavior of using it as a decal duration multiplier is gone. Use the new scarTTL weapon def tag to set scar durations instead. Note: if you had explicitly set scarTTL values, those are now used directly as the TTL (previously they were multiplied by the ground decal level, which defaulted to 3).

Live Unit Footprint Size

Live mobile units' footprint is now the size of their movedef (previously the unit def footprint). This affects bugger-off behavior, construction blocking, and the size of the built-in selection square. Unit defs are unaffected; individual units adjust if their movedef changes at runtime.

Gameplay Behavior

Stop Command on Manual Share

Manually shared units no longer receive the Stop command automatically. To replicate the old behavior, use the UnitGiven callin in a gadget or widget:

<syntaxhighlight lang="lua"> function wupget:UnitGiven(unitID, unitDefID, newTeam)

   if newTeam == Spring.GetMyTeamID() then  -- if in unsynced context
       Spring.GiveOrderToUnit(unitID, CMD.STOP, 0, 0)
   end

end </syntaxhighlight>

Resurrecting Units

Resurrecting units no longer overrides their health to 5%. To restore the old behavior, add a gadget with the following callin:

<syntaxhighlight lang="lua"> function gadget:UnitCreated(unitID, unitDefID, teamID, builderID)

   if builderID and Spring.GetUnitWorkerTask(builderID) == CMD.RESURRECT then
       Spring.SetUnitHealth(unitID, Spring.GetUnitHealth(unitID) * 0.05)
   end

end </syntaxhighlight>

weapons groundBounce

Weapons with groundBounce = true will now bounce even if numBounces is undefined. The default value of numBounces = -1 now correctly results in infinite bounces instead of 0. Set an explicit positive value of numBounces if you want a finite number of bounces.

Default Targeting Priority Random Component Removed

The default targeting priority for typed units no longer includes a ±30% random component. Use gadget:AllowWeaponTarget to restore the previous randomized behavior.

Handicap No Longer Applies to Reclaim

Handicap (resource income multiplier) no longer applies to reclaim operations.

Camera

Camera Modifiers

The following keyboard modifiers were unhardcoded from the engine:

  • Spring Camera: rotating on the x (pitch) or y (yaw) axis with ALT + middle mouse button while moving the cursor.
  • Resetting camera: ALT + mousewheel scroll down.
  • Pitch rotation: CTRL + mousewheel.

If games and players do not change engine defaults, no action is needed. To explicitly enable these modifiers as before, add the following keybindings:

<syntaxhighlight lang="bash"> bind Any+ctrl movetilt -- rotates camera over x axis on mousewheel bind Any+alt movereset -- resets camera state on mousewheel move bind Any+alt moverotate -- rotates camera in x/y on MMB move (Spring cam) </syntaxhighlight>

Rendering

LOD Rendering Removed

LOD rendering (2D unit billboards when zoomed far out) has been removed, along with the /distdraw command and the UnitLodDist springsetting entry.

Dynamic Sky Removed

The AdvSky springsetting and the /dynamicsky command (which made clouds move across the sky) have been removed. There is no direct replacement, though similar behavior can be achieved via custom Lua rendering.

Screenshot Format Changed

The default /screenshot format is now PNG (was previously BMP/other). Review any automated processing pipelines that parse screenshot files. Screenshot and replay demo file names also now use a UTC timestamp format instead of a sequential number.

Paletted Image Files

Paletted image files are no longer accepted. Convert any paletted images to non-paletted format (e.g. standard RGBA PNG).

Shader Changes for Unit Rendering

Unit vertex shaders now receive TRS (Translation/Rotation/Scale) transforms instead of matrices. Code that calls vbo:ModelsVBO() must update shader code. The skinning/bones interface at uniform location 5 was extended in version 2025.04 from uvec2 to uvec3. Update shaders from:

<syntaxhighlight lang="glsl"> // Old (pre-1775) layout (location = 5) in uint pieceIndex;

// After 1775 (bones/skinning era) layout (location = 5) in uvec2 bonesInfo; // boneIDs, boneWeights

  1. define pieceIndex (bonesInfo.x & 0x000000FFu)

// After 2025.04 (extended bone info) layout (location = 5) in uvec3 bonesInfo; // boneID low byte, boneWeight, boneID high byte

  1. define pieceIndex (bonesInfo.x & 0x000000FFu)

</syntaxhighlight>

Feature Support Table

Recoil provides Engine.FeatureSupport, a table of feature availability flags for forward/backward compatibility. Use it instead of Script.IsEngineMinVersion where applicable, as it is self-documenting and does not assume linear commit numbering.

Example usage:

<syntaxhighlight lang="lua"> if Engine.FeatureSupport.rmlUiApiVersion then

   -- use rmlUI

end </syntaxhighlight>

Key entries include:

Key Value Notes
rmlUiApiVersion 1 RmlUI GUI framework available
hasExitOnlyYardmaps true 'e' yardmap tile available
NegativeGetUnitCurrentCommand true Negative indices work in GetUnitCurrentCommand
noAutoShowMetal false → future true Automatic metal map toggle deprecation tracking
maxPiecesPerModel 65534 Maximum pieces per model (was 254)

Death Event Changes

The wupget:UnitDestroyed callin received several changes:

  • Now receives a 7th argument: weaponDefID, the cause of death. This is never nil — all causes of death are attributable.
  • The builder is passed as the killer if a unit is reclaimed.

New Game.envDamageTypes constants identify the cause of death:

Constant Description
AircraftCrashed Aircraft hitting the ground
Kamikaze Unit exploding via kamikaze ability
SelfD Self-destruct command and countdown
ConstructionDecay Abandoned nanoframe disappearing
Reclaimed Killed via reclaim
TurnedIntoFeature Died due to isFeature tag on completion
TransportKilled Was in a transport with no releaseHeld when transport died
FactoryKilled Was being built in a factory that died
FactoryCancel Build order was cancelled in factory
UnitScript COB script ordered the unit's death
SetNegativeHealth Unit had negative health for non-damage reasons (e.g. set via Lua)
OutOfBounds Unit was thrown far out of map bounds
KilledByCheat /remove or /destroy command used
KilledByLua Default cause when using Spring.DestroyUnit

KilledByLua is guaranteed to be the last value. You can define custom damage type constants above it:

<syntaxhighlight lang="lua"> Game.envDamageTypes.MyCustomDeath = Game.envDamageTypes.KilledByLua - 1 </syntaxhighlight>

Other Notable Changes

Python AI Bindings Removed

Python bindings for AI have been removed. Only native C++ AIs are built by default via CMake.

32-bit Builds

32-bit builds are no longer supported.

Mandatory OpenGL Feature Support

Support for non-power-of-2 textures, float textures, and framebuffer objects (FBO) is now mandatory. Systems that do not support these (all of which were common 15+ years ago) are no longer supported.

/ally Command

The /ally command no longer announces the alliance to unrelated players via a console message. The affected players still see the message. Use the TeamChanged callin if you want to make alliance announcements public.

Group Command Change

/group add N no longer selects the entire group after adding units to it.

Automatic Metal View Toggle (Deprecated)

The metal view automatically toggling when a player begins building a metal extractor is deprecated. Disable it via Spring.SetAutoShowMetal(false) and re-implement manually. An example widget is available at (basecontent)/examples/Widgets/gui_autoshowmetal.lua.

DrawUnit and DrawFeature Widget Callins (Deprecated)

Widget drawcalls such as wupget:DrawUnit, wupget:DrawFeature, etc. are deprecated. Migrate to alternative rendering approaches.

Removed Springsettings

The following springsettings have been removed:

Springsetting Replacement / Notes
AdvSky No direct replacement; use custom Lua rendering
UnitLodDist LOD rendering removed entirely
UpdateWeaponVectorsMT Removed (MT is now always on)
UpdateBoundingVolumeMT Removed (MT is now always on)
AnimationMT Removed after proving stable; animations always MT
UsePBO Already did nothing; removed

Removed Commands

Command Notes
/distdraw LOD rendering removed
/dynamicsky Dynamic sky removed
/AdvModelShading Removed

See Also

Contents