Recoil:Migrating From Spring: Difference between revisions
More actions
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>: | ||
< | <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) | ||
</ | </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.glSupport16bitDepthBufferPlatform.glSupport24bitDepthBufferPlatform.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
edgeEffectivenesscan 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
- define pieceIndex (bonesInfo.x & 0x000000FFu)
// After 2025.04 (extended bone info) layout (location = 5) in uvec3 bonesInfo; // boneID low byte, boneWeight, boneID high byte
- 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 nevernil— 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
- Recoil:Choose Recoil — Why switch from Spring to Recoil
- Full Recoil Changelog
- Recoil Lua API Reference
- Recoil Discord — for help and support