Last Update: 2023-08-01
Updating Mods for API Changes
This guide provides strategies and best practices for updating your mods when the Don't Starve Together API changes. Keeping your mods compatible with the latest game version ensures they continue to function correctly and provide a good experience for users.
Understanding API Changes
API changes in Don't Starve Together generally fall into these categories:
- Additions: New components, functions, or properties that expand functionality
- Modifications: Changes to existing components or functions that may alter behavior
- Deprecations: Features that still work but are planned for removal
- Removals: Features that have been completely removed
For a detailed history of all API changes, refer to the API Changelog.
Monitoring for Changes
Stay informed about API changes through these channels:
- Official Klei Forums: Check the Game Updates section, especially the "Notes for Modders" area
- API Changelog: Review our comprehensive API Changelog which documents all significant API changes between versions
- Steam Beta Branch: Opt into beta branches to test your mods before updates go live
- Community Discord: Join modding communities where changes are often discussed
When a new game update is released, your first step should always be to check the API Changelog to identify any changes that might affect your mod.
Defensive Coding Practices
Check for Feature Existence
Always verify that functions and components exist before using them:
-- Check if a function exists
if TheWorld.net.components.weather ~= nil and TheWorld.net.components.weather.GetMoisture ~= nil then
local moisture = TheWorld.net.components.weather:GetMoisture()
else
-- Fallback behavior
end
-- Check if a component exists
if inst.components.new_component ~= nil then
inst.components.new_component:DoSomething()
else
-- Alternative approach
end
Version Detection
Adapt behavior based on the game version:
local function GetGameVersion()
if TheSim and TheSim.GetGameVersion then
return TheSim:GetGameVersion()
end
return "0.0.0" -- Default for very old versions
end
local version = GetGameVersion()
if version >= "500000" then -- Version format may vary
-- Use newer API
else
-- Use older API
end
Feature Detection
When possible, check for specific features rather than version numbers:
-- Better than version checking in many cases
if TheWorld.ismastershard ~= nil then
-- This is a newer version with shard support
else
-- This is an older version
end
Graceful Degradation
Design your mod to work (perhaps with reduced functionality) even when preferred APIs are unavailable:
local function TryUseNewFeature()
if SomeNewFunction ~= nil then
return SomeNewFunction()
else
-- Fallback implementation that works on older versions
return LegacyImplementation()
end
end
Handling Common API Changes
Component Changes
When components change:
-- Handle renamed component
local health_component = inst.components.health or inst.components.old_health_name
-- Handle moved functionality
local function GetHealth(inst)
if inst.components.health ~= nil then
return inst.components.health.currenthealth
elseif inst.replica.health ~= nil then
return inst.replica.health:GetCurrent()
end
return 0
end
Function Signature Changes
When function parameters change:
-- Handle added parameters
local function SafeCall(inst, fn_name, ...)
local fn = inst[fn_name]
if fn ~= nil then
-- Try new signature first
local success, result = pcall(function() return fn(inst, ...) end)
if success then
return result
end
-- Try old signature as fallback
return fn(inst)
end
return nil
end
Network Synchronization Changes
When networking changes:
-- Handle changes in network variable synchronization
local function SetupNetworking(inst)
if TheWorld.ismastersim then
-- Server-side
if inst.components.health ~= nil then
-- New API
if inst.components.health.SetNetworkID ~= nil then
inst.components.health:SetNetworkID("health_" .. tostring(inst.GUID))
end
-- Old API fallback otherwise
end
end
end
Testing Your Updated Mod
After updating your mod for API changes:
- Test in Single Player: Verify basic functionality works
- Test in Multiplayer: Check for synchronization issues
- Test with Other Mods: Ensure compatibility with popular mods
- Test Edge Cases: Try unusual scenarios that might trigger bugs
- Test Performance: Check that your mod doesn't cause lag or crashes
Communicating Changes to Users
When updating your mod for API changes:
- Update the Mod Description: Note compatibility with game versions
- Maintain a Changelog: Document what changed and why
- Respond to Issues: Address user-reported problems promptly
- Consider Version Branches: Maintain separate versions for major game updates if needed
Backward Compatibility Techniques
Wrapper Functions
Create wrapper functions that work across versions:
local function GetWorldTemperature()
if TheWorld.state ~= nil and TheWorld.state.temperature ~= nil then
-- New API
return TheWorld.state.temperature
elseif TheWorld.components.temperature ~= nil then
-- Old API
return TheWorld.components.temperature:GetCurrent()
else
-- Fallback
return 20 -- Default temperature
end
end
Feature Toggles
Use configuration options to enable/disable features based on compatibility:
local CONFIG = {
use_new_network_api = true,
use_legacy_combat = false,
}
-- Later in code
if CONFIG.use_new_network_api and TheNet.GetClientTable ~= nil then
clients = TheNet:GetClientTable()
else
-- Legacy method to get clients
end
Compatibility Layers
Create abstraction layers that normalize API differences:
local PlayerManager = {}
function PlayerManager.GetPlayerByUserId(userid)
-- New method
if TheNet.GetClientTableForUser ~= nil then
local client = TheNet:GetClientTableForUser(userid)
if client ~= nil then
return client.player
end
end
-- Old method
for i, v in ipairs(AllPlayers) do
if v.userid == userid then
return v
end
end
return nil
end
Example: Updating a Mod for Major API Changes
Here's an example of updating a simple mod that was built for an older version:
Original Mod (Pre-Update)
-- Old mod code
local function OnInit()
-- Old API usage
local player = GetPlayer()
if player ~= nil then
player.old_component:DoSomething()
end
end
AddPrefabPostInit("world", function(inst)
inst:DoTaskInTime(0, OnInit)
end)
Updated Mod (Post-Update)
-- New mod code with compatibility
local function OnInit()
-- Check for ThePlayer (new) or GetPlayer() (old)
local player = ThePlayer or (GetPlayer ~= nil and GetPlayer())
if player ~= nil then
-- Try new component first, fall back to old
local component = player.new_component or player.old_component
if component ~= nil then
-- Check if the method exists
if component.DoSomething ~= nil then
component:DoSomething()
elseif component.DoSomethingNew ~= nil then
-- Method was renamed
component:DoSomethingNew()
end
end
end
end
-- Works in both single and multi-player
AddPrefabPostInit("world", function(inst)
inst:DoTaskInTime(0, OnInit)
end)
Conclusion
Updating mods for API changes requires vigilance, testing, and good coding practices. By following the strategies in this guide, you can maintain compatibility with new game versions while still supporting older versions when possible.
Remember that the Don't Starve Together modding community is collaborative - if you're struggling with API changes, don't hesitate to ask for help on the forums or Discord channels.