Skip to main content

Getting Started with DST Lua

Welcome to the Don't Starve Together (DST) Lua API documentation. This guide covers everything from setting up your first mod to advanced scripting patterns.

Prerequisites​

  • Basic Lua programming knowledge
  • Familiarity with DST gameplay mechanics
  • Don't Starve Together installed via Steam

Setting Up Your Mod Environment​

Mods Directory​

PlatformPath
WindowsC:\Users\[Username]\Documents\Klei\DoNotStarveTogether\mods\
macOS~/Documents/Klei/DoNotStarveTogether/mods/
Linux~/.klei/DoNotStarveTogether/mods/

Mod Directory Structure​

my_mod/
├── modinfo.lua # Required: metadata and workshop configuration
├── modmain.lua # Required: entry point, runs on mod load
├── scripts/
│ ├── prefabs/ # Custom entity definitions
│ └── components/ # Custom component definitions
├── anim/ # Custom animations (.zip)
└── images/
└── inventoryimages/ # Item icons (.tex + .xml)

Core Concepts​

Entity-Component System​

DST uses a composition-based entity model:

  • Entities: Everything in the world — players, creatures, items, structures
  • Components: Modular behaviors attached to entities (health, combat, inventory, etc.)
  • Prefabs: Factory functions that create entities with a predefined set of components

Server / Client Split​

DST runs a server simulation alongside a client renderer. All gameplay logic lives on the server:

local function fn()
local inst = CreateEntity()

-- Client + server: visual setup
inst.entity:AddTransform()
inst.entity:AddAnimState()
inst.entity:AddNetwork()
inst.entity:SetPristine()

if not TheWorld.ismastersim then
return inst -- client stops here
end

-- Server only: gameplay components
inst:AddComponent("health")
inst:AddComponent("inventory")

return inst
end

Never add gameplay components before SetPristine() or without the ismastersim guard — they will fail to replicate correctly.

Your First Mod: A Custom Tool​

Step 1: modinfo.lua​

name = "My First Mod"
description = "A simple custom tool"
author = "Your Name"
version = "1.0.0"
api_version = 10

dst_compatible = true
dont_starve_compatible = false
all_clients_require_mod = true
client_only_mod = false

icon_atlas = "modicon.xml"
icon = "modicon.tex"

configuration_options = {
{
name = "item_durability",
label = "Item Durability",
options = {
{description = "Low", data = 50},
{description = "Normal", data = 100},
{description = "High", data = 150},
},
default = 100,
}
}

Step 2: modmain.lua​

PrefabFiles = { "mytool" }

Assets = {
Asset("ATLAS", "images/inventoryimages/mytool.xml"),
Asset("IMAGE", "images/inventoryimages/mytool.tex"),
}

AddRecipe2("mytool",
{Ingredient("twigs", 2), Ingredient("flint", 1)},
TECH.NONE,
{
atlas = "images/inventoryimages/mytool.xml",
image = "mytool.tex",
}
)

TUNING.MYTOOL_DURABILITY = GetModConfigData("item_durability") or 100

Step 3: scripts/prefabs/mytool.lua​

local assets = {
Asset("ANIM", "anim/mytool.zip"),
Asset("ATLAS", "images/inventoryimages/mytool.xml"),
Asset("IMAGE", "images/inventoryimages/mytool.tex"),
}

local function fn()
local inst = CreateEntity()

inst.entity:AddTransform()
inst.entity:AddAnimState()
inst.entity:AddNetwork()

inst.AnimState:SetBank("mytool")
inst.AnimState:SetBuild("mytool")
inst.AnimState:PlayAnimation("idle")

MakeInventoryPhysics(inst)

inst.entity:SetPristine()
if not TheWorld.ismastersim then return inst end

inst:AddComponent("inventoryitem")
inst.components.inventoryitem.imagename = "mytool"
inst.components.inventoryitem.atlasname = "images/inventoryimages/mytool.xml"

inst:AddComponent("inspectable")

inst:AddComponent("tool")
inst.components.tool:SetAction(ACTIONS.CHOP)

inst:AddComponent("finiteuses")
inst.components.finiteuses:SetMaxUses(TUNING.MYTOOL_DURABILITY)
inst.components.finiteuses:SetUses(TUNING.MYTOOL_DURABILITY)
inst.components.finiteuses:SetOnFinished(inst.Remove)

return inst
end

return Prefab("mytool", fn, assets)

Step 4: Test In-Game​

Enable your mod in the DST mod list, then use the in-game console (~):

c_give("mytool", 1) -- spawn into your inventory
c_spawn("mytool", 5) -- drop on ground at cursor
c_dumptable(inst.components) -- inspect a selected entity's components

Common Modding Patterns​

Custom Character​

-- modmain.lua
Assets = {
Asset("ANIM", "anim/mycharacter.zip"),
Asset("ANIM", "anim/ghost_mycharacter_build.zip"),
}

AddModCharacter("mycharacter", "FEMALE")

STRINGS.CHARACTER_NAMES.mycharacter = "Aria"
STRINGS.CHARACTER_DESCRIPTIONS.mycharacter = "She knows things others don't."
STRINGS.CHARACTER_QUOTES.mycharacter = [["Knowledge is survival."]]

AddPlayerPostInit(function(inst)
if inst.prefab ~= "mycharacter" then return end

inst.components.health:SetMaxHealth(120)
inst.components.sanity:SetMax(200)
inst.components.hunger:SetMax(140)
end)

Custom Component​

-- scripts/components/tracker.lua
local Tracker = Class(function(self, inst)
self.inst = inst
self.value = 0
self.max = 100
end)

function Tracker:SetValue(v)
self.value = math.clamp(v, 0, self.max)
self.inst:PushEvent("trackerchanged", {value = self.value})
end

function Tracker:OnSave()
return {value = self.value}
end

function Tracker:OnLoad(data)
if data then self.value = data.value or 0 end
end

return Tracker

Event Communication​

Components coordinate through events rather than direct calls:

-- Producer: fires event when state changes
inst.components.health:SetOnDelta(function(inst, data)
inst:PushEvent("healthchanged", data)
end)

-- Consumer: reacts without coupling to health directly
inst:ListenForEvent("healthchanged", function(inst, data)
if data.newpercent < 0.25 then
inst:AddTag("lowhealth")
inst.components.talker:Say("I need healing!")
end
end)

World Generation Hook​

AddRoomPreInit("Forest", function(room)
room.contents.distributeprefabs = room.contents.distributeprefabs or {}
table.insert(room.contents.distributeprefabs, "mycustomprefab")
end)

Debugging​

Console Commands​

c_give("prefab", n) -- give item to player
c_spawn("prefab", n) -- spawn at cursor
c_find("prefab") -- list all instances
c_sel() -- select entity under cursor
c_gonext("prefab") -- teleport to next instance
c_regenerateworld() -- force world regen (tests world gen hooks)

Mod Debug Helpers​

modprint("value:", myvar) -- prints only when mod debug is on
moderror("something went wrong") -- logs as mod error
modassert(condition, "message") -- crashes loudly on bad state

Common Issues​

SymptomLikely CauseFix
Mod not in listmodinfo.lua syntax errorCheck for missing commas, unclosed strings
c_give() failsPrefab not in PrefabFilesAdd prefab name to PrefabFiles table in modmain.lua
Works offline, breaks onlineComponent added outside ismastersim guardMove all gameplay components after SetPristine() + ismastersim check
Recipe not appearingWrong TECH level or missing tech unlockVerify TECH.* values and station requirements

Steam Workshop​

Pre-Upload Checklist​

  • All modinfo.lua required fields are filled
  • No Lua errors in the in-game console
  • Tested in both single-player and multiplayer
  • Images are compressed and assets are correctly referenced
  • all_clients_require_mod set correctly for your mod type

Upload​

Main Menu → Mods → Upload Mod → Select your mod folder

Name your prefabs with a mod-specific prefix to avoid conflicts with other mods:

-- Good: unique prefix prevents collisions
"mymod_enhanced_hoe"
"mymod_water_barrel"

-- Avoid: generic names that other mods may also use
"hoe"
"barrel"

Resources​