Skip to main content

Debugging and Testing Mods

Developing mods for Don't Starve Together often involves troubleshooting issues and ensuring your mod works correctly. This guide covers the tools and techniques available for debugging and testing your mods.

Debugging Techniques

Console Logging

The most basic debugging technique is using print() statements to output information to the console:

-- Basic print statement
print("Player health:", player.components.health.currenthealth)

-- Format a table for better readability
print("Inventory:", dumptable(player.components.inventory.itemslots))

To view these logs:

  1. Enable the console by pressing the tilde key (~)
  2. Look for your print statements in the console output
  3. Use CTRL+L to clear the console if it gets too cluttered

Debug Commands

Don't Starve Together includes several built-in debug commands that can be entered in the console:

c_give("prefab_name")        -- Spawn an item
c_spawn("prefab_name") -- Spawn an entity
c_godmode() -- Toggle god mode
c_select() -- Select an entity under the cursor
c_reveal() -- Reveal the map
c_supergodmode() -- Enable super god mode (unlimited resources)
c_freecrafting() -- Enable free crafting

Debug Rendering

You can enable various debug visualizations:

-- Enable debug rendering
TheSim:SetDebugRenderEnabled(true)

-- Show physics collision shapes
TheSim:SetDebugPhysicsRender(true)

-- Custom debug drawing
TheWorld.debugrender = true

-- In your entity's OnUpdate function:
if TheWorld.debugrender then
local x, y, z = self.inst.Transform:GetWorldPosition()
TheWorld.DebugRender:Line(x, y, z, x, y + 2, z, 255, 0, 0, 255)
TheWorld.DebugRender:String(x, y + 2, z, "Debug Text", 255, 255, 255, 255)
end

Mod Configuration

Create a configuration variable in your modmain.lua to enable/disable debug features:

-- In modmain.lua
GLOBAL.DEBUG_ENABLED = GetModConfigData("debug_mode") or false

-- Later in your code
if GLOBAL.DEBUG_ENABLED then
print("Debug info:", some_variable)
end

Debugging Common Issues

Network Synchronization Issues

When dealing with multiplayer synchronization problems:

-- Check if code is running on server or client
if TheNet:GetIsServer() then
print("Running on server")
else
print("Running on client")
end

-- Debug network variables
local net_var = net_float("mymod.myvar", "myvar_dirty")
net_var:set_local(5)
print("Local value:", net_var:value())
print("Is dirty:", net_var:is_dirty())

Component Debugging

Add a GetDebugString() method to your components for better diagnostics:

function MyComponent:GetDebugString()
return string.format("Value: %d, State: %s",
self.value,
self.active and "active" or "inactive")
end

-- View the debug string in-game with c_select() and looking at the console

State Graph Debugging

For debugging state graphs:

-- Print current state
print("Current state:", self.inst.sg:HasStateTag("idle") and "idle" or "not idle")

-- Log state transitions
local old_gotostate = self.inst.sg.GoToState
self.inst.sg.GoToState = function(sg, statename, ...)
print("State transition:", sg.currentstate.name, "->", statename)
return old_gotostate(sg, statename, ...)
end

Testing Strategies

Local Testing

Before sharing your mod, test it thoroughly in various scenarios:

  1. Solo Testing: Test in a single-player game first
  2. Host Testing: Host a server and test with multiple clients
  3. Dedicated Server Testing: Test on a dedicated server if possible

Test Cases

Create a checklist of test cases covering:

  • Mod initialization and loading
  • All features and functionality
  • Interaction with other game systems
  • Edge cases and error conditions
  • Performance under various conditions

Example test script:

local function RunTests()
if not GLOBAL.DEBUG_ENABLED then return end

print("=== RUNNING MOD TESTS ===")

-- Test item creation
local item = SpawnPrefab("my_custom_item")
assert(item, "Failed to spawn custom item")

-- Test component functionality
assert(item.components.mycomponent, "Missing component")
assert(item.components.mycomponent:TestFunction() == expected_result,
"Component function returned unexpected result")

print("All tests passed!")
end

-- Run tests when the world is ready
AddPrefabPostInit("world", function(world)
world:DoTaskInTime(1, RunTests)
end)

Automated Testing

For complex mods, consider creating an automated test system:

local tests = {}

function AddTest(name, fn)
table.insert(tests, {name = name, fn = fn})
end

function RunAllTests()
local passed = 0
local failed = 0

for _, test in ipairs(tests) do
print("Running test:", test.name)
local success, error_msg = pcall(test.fn)

if success then
print(" PASSED")
passed = passed + 1
else
print(" FAILED:", error_msg)
failed = failed + 1
end
end

print(string.format("Test results: %d passed, %d failed", passed, failed))
end

-- Define tests
AddTest("Item Creation", function()
local item = SpawnPrefab("my_custom_item")
assert(item, "Failed to spawn item")
end)

-- Run tests with console command
GLOBAL.c_runtests = RunAllTests

Common Errors and Solutions

Script Errors

ErrorPossible CauseSolution
attempt to index a nil valueTrying to access a property of a nil objectCheck if the object exists before accessing properties
attempt to call a nil valueCalling a function that doesn't existVerify function names and that required modules are loaded
bad argument #1 to functionPassing incorrect parameter typeCheck parameter types and documentation
stack overflowInfinite recursionLook for circular function calls

Mod Loading Errors

ErrorPossible CauseSolution
Mod doesn't appear in listIncorrect folder structureVerify modinfo.lua and folder naming
Mod crashes on loadError in modmain.luaCheck console for error messages
Assets not loadingIncorrect path or formatVerify asset paths and formats

Multiplayer Issues

IssuePossible CauseSolution
DesyncInconsistent state between server/clientUse proper network variables and RPC calls
LagInefficient code or too many entitiesOptimize performance-critical code
Client-only effectsNot using proper network codeSeparate client and server logic appropriately

Debug Tools and Mods

Several mods can help with debugging:

  1. Debug Menu: Provides in-game access to many debug commands
  2. Craft Pot: Allows spawning any item for testing
  3. World Control: Manipulate time, weather, and other world settings
  4. Gesture Wheel Debugger: Shows information about controller gestures

Best Practices

  1. Isolate Issues: Test one feature at a time
  2. Version Control: Use Git to track changes and revert if needed
  3. Incremental Testing: Test frequently as you develop
  4. Error Handling: Add pcall() around risky code to prevent crashes
  5. Logging Strategy: Use structured logging with severity levels
  6. Clean Up Debug Code: Remove or disable debug code before releasing

Advanced Debugging

Memory Profiling

For complex mods with potential memory issues:

local function CountInstances()
local counts = {}
for k, v in pairs(Ents) do
local prefab = v.prefab
counts[prefab] = (counts[prefab] or 0) + 1
end

for prefab, count in pairs(counts) do
if count > 10 then
print(prefab, count)
end
end
end

-- Call periodically to check for entity leaks
TheWorld:DoPeriodicTask(60, CountInstances)

Performance Monitoring

local function MeasureTime(fn, ...)
local start_time = os.clock()
local result = fn(...)
local end_time = os.clock()
print("Function took", (end_time - start_time) * 1000, "ms")
return result
end

-- Usage
MeasureTime(MyExpensiveFunction, arg1, arg2)

Conclusion

Effective debugging and testing are essential skills for mod development. By using these techniques and tools, you can identify and fix issues more quickly, resulting in more stable and enjoyable mods for players.

Remember that the most valuable debugging tool is a systematic approach: understand the problem, isolate the cause, fix the issue, and verify the solution works in all scenarios.