Modding

Mod API reference

The reference for mod makers: what a mod folder holds, the data files, and the code API. New to modding? Start with Making mods.

Updated 11 Oct 2026

The reference for mod makers: what a mod folder holds, the data files, and the code API. New to modding? Start with Making mods.

Players install mods from the website (Install opens the game), from Mods ▸ Browse in the main menu, or by putting folders in the Mods folder, and switch them on and off in main menu ▸ Mods. In multiplayer everyone plays with the host's mods: a joining player gets the mods they're missing from the host (after saying yes), keeps their own client-side mods, and goes back to their own mods when they leave.

Where mods go

FolderWhat
<persistent data>/Mods/<any name>/The player's mods (%USERPROFILE%\AppData\LocalLow\DefaultCompany\Seeds & Circuits\Mods on Windows). Open Folder in the Mods screen opens it
<game folder>/Mods/<any name>/Mods shipped next to the executable (in the editor: Mods next to Assets)
<persistent data>/Mods/.downloaded/<id>-<hash>/Mods downloaded from hosts
<persistent data>/Mods/mods.jsonWhich mods the player turned on or off

The player's own mods start on; downloaded ones start off (they're used in that host's games only, unless the player turns them on). Start the game with -nomods to load no mods at all.

What a mod is

A folder with a mod.json:

{
    "id": "someone.coppertools",
    "name": "Copper Tools",
    "version": "1.0",
    "author": "Someone",
    "side": "both",
    "description": "Copper axe, pickaxe and shovel.",
    "dependencies": ["someone.coremod", "someone.library >= 1.2 < 2.0"]
}

id (letters, digits, . _ -; the folder name if left out) must be unique: two mods with one id can't both be on. Mods load after their dependencies; a mod whose dependency is missing, or there in a version it can't use, stays off and the Mods screen says why ("Needs Core Mod 1.2 or newer (1.0 is installed)"). A dependency is an id, optionally followed by version rules (>=, >, <=, <, =), compared part by part as numbers (1.10 is newer than 1.9). gameVersion (written by the Mod Kit) is the game version a code mod was built for: the Mods screen notes "made for game version X" when that isn't this game's version (its code may not work; data mods don't care).

side says where the mod runs in multiplayer (default both):

SideRuns onIn multiplayerFor
bothHost and every playerEveryone must have it: joiners download it from the hostItems, recipes, shared gameplay (Waypoint Map)
clientWherever the player has itWorks on any host. A host's client-side mods are offered to joiners (ticked by default, they can untick)HUD, UI, compass, cosmetics (Trader Compass)
serverOnly on the hostNever sent to or required from players; a joiner's own server mods are off while joinedAdmin tools, rules (Ban Hammer)

Everything else in the folder is optional, and any mix works:

FileWhat it does
items.jsonAdds items or changes the game's items (below)
recipes.jsonAdds crafting recipes (below)
*.dllCode (below)
images, AssetBundles, anythingUsed by the above (icons, models)

Files and folders starting with . are ignored. The mod's hash (SHA-256 of every file's path and bytes) tells whether two machines have the same mod. A mod may be up to 512 MB. A mod never writes into its own folder (that would change its hash): code mods keep their saved data in Mod.DataFolder.

Items (items.json)

{
    "items": [
        // New item, starting from cooked pork (a food), with the clay pot's model and icon.
        { "id": "berry_jam", "copy": "cooked_pork", "name": "Berry Jam", "food": 30, "spoilDays": 0,
          "stackSize": 10, "model": "clay_pot" },
        // Same id as a game item: only the fields named here change.
        { "id": "rope", "stackSize": 50 }
    ]
}

// comments and trailing commas are allowed.

Field
idItem id. A game item's id changes that item
copyStart from this item (any id, also another mod's)
modelUse this item's held, drop and place prefabs (and icon, unless icon is given)
name, descriptionShown in the UI
typeTool, Block, Building, Resource, Station, Parts, Clothing
stackSize1, 5, 10, 20, 50, 100 or 500 (the game's sizes)
iconicons/jam.png (PNG/JPG in the mod), item:<id> (a game item's icon) or bundle/path:Asset
heldPrefab, dropPrefab, placePrefabbundle/path:Asset Name, from an AssetBundle in the mod
food, poison, keepOnDeath, spoilDays, durabilityAs the Items table
fuel, burnTemperature, burnsInto, burnYieldCampfire fuel
cooksInto, cookTime, byproduct, smeltsInto, smeltTime, smeltTemperatureCooking and smelting
equipSlot (Hat, Top, Bottom, Boots, Backpack), bagSlots, armor, warmthClothing
blockType (normal, roof, glass, mesh: decides the shapes), blockHealth, blockMaterial (bundle/path:Material)Blocks

The game's Items table loads with the world, after the mods. Mod items are rebuilt and put back every time the item list changes, so copy, model and changes to game items work in the world even though those items don't exist in the main menu yet. Turning a mod off puts the game's own item back.

AssetBundles must be built with this game's Unity version (6000.6). Prefabs used as item models are only looks (networked objects: see Network prefabs).

Recipes (recipes.json)

{
    "recipes": [
        { "result": "berry_jam", "count": 1, "category": "Food", "craftTime": 3,
          "ingredients": [ { "item": "blueberry", "count": 5 }, { "item": "clay_pot", "count": 1 } ] }
    ]
}

id (default: the result), result, count, category (a Recipe Book tab: Tools, Materials, Building, Weapons, Equipment, Food, Other), station (e.g. Workbench; empty = by hand), stationTier, craftTime, requiresBlueprint, ingredients. A recipe with a game recipe's id replaces it.

Data files (no code)

Besides items.json and recipes.json, a mod without code can bring these (all optional, // comments allowed). Each entry can start from a game one (copy) and change only what it names. Everything goes when the mod is turned off.

FileListsFields
effects.jsoneffectsid, copy, name, description, icon (PNG, item:<id>, bundle ref), buff, seconds, maxSeconds, stacking (refresh / extend), healthPerSecond, regenMultiplier, stopsRegen, hungerDrainMultiplier
fooditem, effect, seconds: eating the item gives the effect
crops.jsoncropsid, copy (a game crop: its growth models), name, seed, produce, growMinutes, yield / seeds ([min, max]), regrows, seasons (["Spring", "Summer"]), stages (bundle prefab refs)
trades.jsontradesnpc (otto, margit, viggo, rosa, gunnar, ylva, pell, sabine), item, price, stock, buys (the NPC buys it instead)
dialogue.jsonlinesnpc (empty = every NPC), text: another "Tell me more." line
optionsnpc, text (the player), answer (the NPC)
worldtypes.jsonworldTypesid, name, startsFrom (Island, Archipelago, Highlands, Lakelands), flat, hills, mountains, erosion, lakeSize, caves (times the shape's), mountainShare, land (0..1), rivers, lakes
assets.jsonFrom the mod's AssetBundle ("Bundles/content:Assets/.../X.asset"): materials, biomes, scatter ([{ "set": ref, "biome": "Pine Forest" }]), animals (their stage prefabs are registered as network prefabs), birds, fish, npcTypes, npcs, music, sounds ([{ "set": "Tools/Axe/Chop", "clips": [refs] }])
// trades.json
{ "trades": [ { "npc": "viggo", "item": "sulfur", "price": 6, "stock": 20 } ] }

Mod creator (Mods ▸ Create)

Data mods can be made inside the game: Mods ▸ Create. Pick New mod (or one of your mods without code) and a section: General (name, id, version, author, description), Items, Recipes, Effects, Food effects, Crops, Trades, Dialogue lines, Dialogue choices, World types. List sections show their entries on the left (Add / Remove) and the chosen one's fields on the right; fields left empty aren't written. Item, effect, NPC and crop fields have a picker (with the game's items and icons, and the mod's own). Save writes the mod's JSON files into the Mods folder (a new mod gets a folder named after it); turn it on in the Mods list to try it. Open Folder shows the files (to add PNG icons, which icon fields name). Leaving with unsaved changes asks first.

Worlds and mods

A world's save remembers the mods it was played with (id, name and version; players' own client mods left out). Loading it from Play when one of them is off or another version asks first: their items, blocks and other things may be lost when the world is saved.

Code (*.dll)

The easiest way to make code mods is the Mod Kit: a Unity package with the game's API, so mods are made in Unity with prefabs, models and networked components, and built with one button.

The game uses Mono, so mods can be C# assemblies. Every .dll in the folder is loaded; each non-abstract class deriving from SeedsAndCircuits.Mods.Mod is created once (parameterless constructor) and gets:

MethodWhen
OnLoad()The mod is turned on: at start, in the Mods screen, or when joining a host that has it
OnWorldLoaded()The world is running (host and players)
OnUpdate()Every frame while the mod is on
OnGUI()Unity's immediate-mode GUI (GUI.*): simple screens and overlays (the Waypoint Map draws its map here)
OnWorldUnloaded()Leaving the world
OnUnload()The mod is turned off (or the game quits): undo everything OnLoad did
OnPlayerConnecting(ModConnection)Host: someone asks to join (ClientId, PlayerId, PlayerName). Return a reason to turn them away
OnPlayerJoined(clientId) / OnPlayerLeft(clientId)Host: a player is in (the host itself too) / left
OnSavingWorld()Host: the world is about to be saved; put the latest state in WorldData

Helpers on Mod:

Info, Foldermod.json (id, side, hash...) and the mod's folder
DataFolder<persistent data>/ModData/<id>/ for the mod's own saved files
IsServer, IsClient, IsHost, LocalClientIdWhere this copy runs in the current game
WorldNameThe save this machine hosts (null on players)
WorldDataHost: the mod's data in the world save (below)
AddKey(name, keyboard, gamepad)A rebindable key (below)
AddToggle / AddSlider / AddChoiceSettings on Settings ▸ Mods (below)
OnMessage(name, handler)Receive messages this mod sends from other machines (msg.Sender, msg.Data, msg.Text)
SendToServer(name, data)To the host (works on the host too)
SendToClients(name, data) / SendToClient(id, name, data)Host: to every player (the host included) / one player
RegisterNetworkPrefab(name, gameObject), SpawnNetworkPrefab(name, pos, rot)Networked objects (below)
Log, LogWarning, LogError, GetPath, ReadText, LoadTexture, LoadAssetBundle, LoadAsset<T>Logging and the mod's files (textures and bundles are freed with the mod)
Json.ToJson(obj, pretty), Json.FromJson<T>(text)Save/send your own classes as JSON (static, in SeedsAndCircuits.Mods)

Use Json, not Unity's JsonUtility, for classes in a mod: JsonUtility only knows types from assemblies Unity compiled, so a mod's classes silently come out as {}. Json works by reflection: public and [SerializeField] fields, strings, numbers, bools, enums, lists, arrays, string-keyed dictionaries, nested classes, Vector2/3/4, Quaternion, Color.

Messages take byte[] or a string; names are per mod (two mods can both use "sync"). They're reliable and ordered.

Mods may use any public game API (SeedsAndCircuits.*.dll, e.g. ItemCatalog, RecipeCatalog, PlayerSpawn, TownNpc, Compass.CollectMarkers), Unity and Netcode. Static [ConsoleCommand] methods become console commands while the mod is on (outside the console's namespace write [ConsoleCommandAttribute(...)]); commands without Local = true run on the host (for the host, and for players it gave the console with op).

  • Assemblies are loaded from bytes (the files stay unlocked, and two versions of a mod can be loaded in one run). They can't be unloaded: a mod turned off stops getting calls, but its code stays in memory until the game quits.
  • An exception in a mod is logged and shown on its row in the Mods screen; it doesn't stop the game. OnUpdate and OnGUI errors are logged once per mod.
  • Several assemblies in one mod (or a mod depending on another mod's assembly) find each other by name.
  • Prefer the Mod calls over your own MonoBehaviours: classes from DLLs loaded at run time can't always be added as components.

World data (saved with the world)

WorldData is the mod's part of the host's world save: text or any object (as JSON) by key. It's loaded with the world (read it in OnWorldLoaded), saved with it (OnSavingWorld runs just before), and moves, copies and gets deleted with the world. A new world starts empty. Only the host has it: send players what they need.

public override void OnWorldLoaded() { if (IsServer) _stats = WorldData.Get("stats", new Stats()); }
public override void OnSavingWorld() => WorldData.Set("stats", _stats);

Data of mods that are off stays in the save. Things a player keeps for themselves (their own waypoints) go in DataFolder instead.

Game events

SeedsAndCircuits.GameEvents (static events, assembly SeedsAndCircuits.GameEvents) tells mods what happens in the world. All fire on the host only, once per thing:

EventArguments
PlayerDied / PlayerRespawnedplayer GameObject, cause (Damage, Hunger, ...)
FoodEatenplayer, item id
ItemCraftedplayer (null: a station on its own), station id (null: by hand), item id, count
ItemPickedUpplayer, item id, count
BlocksPlaced / BlocksBrokenitem id, block-grid cells
PropPlaceditem id, position, player (or null)
TreeFelledtree kind, position
AnimalKilledanimal, species, killer (player or null)
DayStartedday number (also when sleeping past midnight)
SeasonChangedSpring / Summer / Autumn / Winter

Subscribe in OnLoad, unsubscribe in OnUnload. A handler that throws is logged and doesn't stop the others.

Keys

AddKey("Open Map", "<Keyboard>/m") (optional gamepad binding) returns an InputAction: check WasPressedThisFrame(). Each mod's keys are listed on Settings ▸ Controls as "Waypoint Map: Open Map" and rebound like the game's keys (a player's bindings are kept while the mod is off). They're only on while the game's own Player keys are and the mouse is captured, so not in menus, the inventory or other item windows, the console or while loading.

Screens

A mod that shows its own screen over the game (a map, a menu) sets ScreenOpen = true while it's up: the cursor is freed and the game's controls are off like in its own screens (moving, looking, tools, hotbar keys and mouse wheel, interacting, opening the inventory). Escape calls OnCloseScreen() instead of opening the pause menu; ScreenOpen goes back to false then, and when the mod turns off.

World map

TerrainWorld.Instance.MapArea(center) is the world's rectangle (or a square around center in a flat world) and RenderMap(area, size, pixels, firstRow, rowCount) draws a top-down map of it into a Color32[] (ground colours, water, hill shading) from the terrain data, so it works for unloaded land and on clients. Burst job: a 512² map is ~20 ms; draw some rows per frame for big maps (the Waypoint Map does 128 rows of 1024 a frame).

Settings

AddToggle(key, label, default), AddSlider(key, label, min, max, default, step) and AddChoice(key, label, options, default) put rows on Settings ▸ Mods (a header per mod), in both the main menu and the pause menu. Read .Value (.Index for a choice) and listen to .Changed. Values are stored per player with the game settings, so they follow the settings screen: changes show at once, Apply keeps them, Back undoes them.

Network prefabs

RegisterNetworkPrefab("beacon", go) in OnLoad (on every machine, so only in both mods) makes a prefab Netcode can spawn; the host calls SpawnNetworkPrefab("beacon", position, rotation) and every player sees it. A GameObject built in code becomes a hidden template (it gets a NetworkObject); an AssetBundle prefab needs a NetworkObject already. The prefab id comes from the mod id and the name, so it's the same everywhere.

Netcode's own components (NetworkTransform, NetworkRigidbody...) work on them. NetworkBehaviours of your own (RPCs and NetworkVariables) need Unity's code generation: they work in mods built with the Mod Kit, not in mods built with dotnet build. Without the kit, sync your own state with messages.

Adding content from code

Everything a Mod adds with these is taken out again by itself when the mod turns off (WhenTurnedOff(action) does the same for your own registrations). Assets (species, biomes, clips, prefabs) come from the mod's AssetBundle (LoadContent<T>) or are made in code. Content everyone has to agree on (items, species, crops, world generation, music) belongs in a both mod, added in OnLoad, so the host and every player have it.

CallAddsGame side
AddItem(ItemDefinition), AddRecipe(CraftingRecipe)Items and recipes from code (items.json / recipes.json are simpler and can change game items)ItemCatalog, RecipeCatalog
AddResearch(ResearchDefinition)A blueprint researchable at the research tableResearchCatalog
AddStatusEffect(StatusEffectDefinition), AddFoodEffect(item, effect, seconds)Buffs and sicknesses; food that gives themStatusEffectBook.AddEffect/AddFoodEffect
AddCrop(CropDefinition)A farm crop (its seed item plants it)FarmingSettings.AddCrop
AddTool(ToolStats), AddArrow(ArrowStats)Make an item a tool or weapon of a kind the game has (axe, pickaxe, spear, bow, rod...)ToolSwing.AddTool/AddArrow
AddAnimal(AnimalSpecies), AddBird(BirdSpecies), AddFish(FishSpecies)Wildlife (register its prefabs with RegisterNetworkPrefab first)Wildlife/Birds/Fishes.AddSpecies
AddNpcType(NpcType), AddNpc(NpcDefinition), AddTrade(character, item, price, stock, sells)Town roles, characters, trade lines (after the character's own, so saved stock still matches)Town.AddType/AddCharacter/AddTrade
AddSounds(set, clips), AddMusic(clip)A new sound set or one of the game's replaced (Resources/Sfx folder names); music tracksSfx.AddClips, Music.AddTrack
AddBiome, AddScatter, AddWorldType, AddTerrainMaterialWorld generation (below)TerrainModding
AddNpcJob, OnNpcSpawned, AddDialogueOption/Opening/Line, AddAnimalBehaviourBehaviour of NPCs and animals (below)Town, Dialogue, AnimalAgent

Anything else is plain Unity code: a mod's MonoBehaviours, its own prefabs (also as placeable items: placePrefab with a component implementing IInteractable / IAttackUse is interacted with like the game's objects), messages, world data and game events make whole new systems.

World generation

Generated worlds are worked out on every machine from the seed, so a mod can change them as long as it does the same everywhere (a both mod) and takes its randomness only from world.seed. In order, while a world starts:

HookWhenChange
OnConfigureWorld(world, ref landscape)Before the map is made (also for previews)ProceduralWorldParams: hill/mountain heights, mountain and land share, erosion, rivers, lakes, lake sizes
OnMapGenerated(world, map)The map is made, nothing has read it yetProceduralMap: Heights (640 × 640, index x + z * 640, cell x, z at map.Origin + (x, z) * map.Cell, metres), Moisture (0..1), Kinds (Land, Sea, LakeCell, RiverCell), Lakes, Sites. Edit in place; the map's hash (desync check) is redone after
OnBiomesGenerated(world, biomes)Climate biomes are placedTerrainBiomePainter: SetCell(x, z, biome) / Paint(center, radius, biome) over 4 m cells, GetCell
OnConfigureGround(world, ref ground)Every world's ground (play mode)TestGeneratorParams: caves (CavesEnabled, CaveDensity, CaveCellSize...), strata, ores, surface noise
  • AddWorldType(id, name, startsFrom, flat) adds a choice to New World ▸ World. The world saves world.type = id; the mod checks it in the hooks. A world of a type whose mod is gone loads as the type's starting shape.
  • AddBiome(TerrainBiomeDefinition) adds a biome after the game's (climate biomes grow where their moisture/height/slope ranges hold; natural ones by noise; any can be painted). AddScatter(TerrainScatterSet, biomeName) adds trees, plants, rocks, pickups and grass to a biome (null: every biome). TerrainModding.FindBiome(name) finds the game's.
  • AddTerrainMaterial(TerrainMaterialDefinition) adds ground a biome, strata layer or ore can be made of: textures of any size (top, and optional sides; albedo, normal, height), tint, tiling, smoothness, hardness, tool tier, drops. When a world starts, the game makes a play-time copy of its material database with the mods' materials after its own, and texture arrays with a slice for each (the game's slices copied on the GPU, the mods' scaled in). Ids follow the game's in mod order, so host and players need the same mods, and digging/building saved with a mod material keeps its id (without the mod it shows as another material).
  • Generated worlds also have outer islands: OnMapGenerated / OnBiomesGenerated run once for each, when it's made (also during play), with that island's seed and size. Its map starts at map.Params.Offset (zero for the start island; the painter's cell (0, 0) is the island's corner).
  • Set things up before the world starts (OnLoad); a running world keeps what it was made with.
  • Code that isn't a mod can use SeedsAndCircuits.Terrain.TerrainModding (the same events and lists) directly.

NPC and animal behaviour

Behaviour runs on the host; everyone sees the result.

  • NPC jobs. AddNpcJob(roleId, npc => new MyJob()) gives every town NPC of a role ("citizen", "shopkeeper"...; null = all) an INpcJob: WantsToWork(npc) is asked every frame, and while it's true Work(npc, deltaTime) runs instead of the NPC's own day (bedtime still sends it home to bed). Return null from the maker to leave an NPC to the game (shopkeepers keep their shop). NPCs already in the world get the job at once. OnNpcSpawned(npc => ...) runs for each NPC as it comes into the world; Town.Agents lists them.
  • What NPCs can do (TownNpc, host): WalkTo(point), StandAt(spot, yaw), Say(text, seconds) (a speech bubble everyone sees), PlayAction(state, seconds, speed) (a state of the character animator's Actions layer: Tool_Chop, Punch_R, Punch_L...), LookAt(point) / StopLooking(), Hold(itemId) (the item's held model in its right hand; null = nothing), SetWorking(bool), HoldForTalk(playerPosition).
  • Dialogue (each player's machine): AddDialogueOption(text, npc => answer, npc => shows) adds a choice to the talk window (the answer replaces the NPC's text; send a message to the host to change the world), AddDialogueOpening(npc => line or null) what NPCs say first, AddDialogueLine(characterId, line) more "Tell me more." lines (null = every NPC).
  • Animals. AddAnimalBehaviour(speciesId, animal => new MyBehaviour()) (null = every species): its Think(animal, now) runs five times a second before the animal thinks itself; return true to decide for it this time (GoTo(point, run), Stop(), LieDown(sleep), FaceTowards(point)), false to let it be. Its own state (Species, IsDead, IsMoving, herd and taming) stays as it is.

Examples

ModSideShows
Berry Jamboth (data)items.json (new item copying another, changing a game item), recipes.json
Hello ModbothLifecycle, console commands, OnPlayerJoined greeting, ping/pong messages, a network prefab (hello.beacon), game events counted into world data (hello.stats)
Waypoint MapbothM (rebindable) opens a map of the whole world, drawn from the terrain data (TerrainWorld.RenderMap) right after the world loads, centred on you (arrow = where you look). Drag: pan. Scroll: zoom. Click: private waypoint (saved on your machine per world). Shift+click: shared waypoint (kept in the world save, WorldData, and sent to everyone). Right-click: remove. Waypoints show on the compass. Settings: waypoints on the compass, map size. Console waypoint.add <name> [shared], waypoint.remove, waypoint.list
Trader CompassclientWandering traders show on the compass as gold diamonds (range on Settings ▸ Mods, default 300 m). Console traders
Volcano IslandbothWorld generation: a world type (New World ▸ World ▸ Volcano Island) whose island gets a volcano with a crater lake (OnMapGenerated), ash fields (a biome and a terrain material made in code, AddBiome, AddTerrainMaterial, OnBiomesGenerated), lower hills elsewhere (OnConfigureWorld) and more caves (OnConfigureGround); Viggo sells sulfur (AddTrade)
Town LifebothBehaviour: NPCs carry a lantern about in the evening (AddNpcJob, Say, Hold), tell you about animals nearby (AddDialogueOption) and a line of gossip (AddDialogueLine); pigs follow anyone holding a carrot (AddAnimalBehaviour)
Ban Hammerserverban <player name or clientId> [reason] kicks and keeps a player out (OnPlayerConnecting), unban <name or id>, bans. Bans are per host machine, by player id (a GUID the player's game keeps: it says who they are, it doesn't prove it)

Each one is on the website's Mods page (tagged example) to install, and its source is on the wiki (Example mods).

Mods screen

Main menu ▸ Mods: every installed mod with name, version, author, side and description; a switch to turn it on or off (at once, no restart) and Delete (click twice). Problems (bad mod.json, missing dependency, duplicate id, load errors) show in red under the mod. Browse lists the mods on the website with Install / Update. Open Folder opens the Mods folder. Create opens the mod creator. The list is read again each time the screen opens.

Multiplayer

When a player joins a host:

  • They must run the same both mods as the host (same files). Missing ones are offered for download from the host, and a mod's code is marked so players know it runs code.
  • The host's client mods are offered too, ticked by default; the player can untick them.
  • The host's server mods are never sent or compared, and the player's own client mods always stay on.
  • A different build of the game can't join ("You have a different version of the game than the host.").
  • Any running mod on the host can turn a player away with a reason (OnPlayerConnecting); the player sees the reason.
  • Leaving the game brings back the player's own mods.

Console

Command
modsInstalled mods: on/off, version, hash, code/downloaded, problems
mods.activeThe mods running now, in load order
mods.declined [clear]Hosts' client-side mods you turned down; clear has hosts offer them again