Making mods
A beginner's walkthrough. The full reference is Mod API reference.
A beginner's walkthrough. The full reference is Mod API reference.
A mod is just a folder in the game's Mods folder. The game reads it at start (and whenever the Mods screen opens). There are two kinds, and one mod can be both:
| Kind | What you write | Tools | Can do |
|---|---|---|---|
| Data mod | JSON text files (items.json, recipes.json) | Any text editor | New items, changed items, new recipes |
| Code mod | C# code, built into a .dll | The free .NET SDK, plus an editor like VS Code | Anything the game's code can do: UI, new mechanics, commands, multiplayer features |
This is the same idea as Minecraft or Terraria mods: the game loads your code next to its own, and your code calls the game's functions.
1. Find the Mods folder
Main menu ▸ Mods ▸ Open Folder. On Windows it's
%USERPROFILE%\AppData\LocalLow\DefaultCompany\Seeds & Circuits\Mods. Each mod gets its own folder in there.
2. A data mod (5 minutes)
- Get the Berry Jam example (on the website's Mods page, or its files on the wiki's
Example mods page), put it in the Mods folder and rename the folder, say
My Food. - Open
mod.jsonand give it your ownid(e.g.yourname.myfood) andname. - Edit
items.json. Item ids come from the game: open the console in a world and typeinv.items(orinv.items berryto filter)."copy": "cooked_pork"starts your item from an existing one, so you only write what's different. - Open the Mods screen: your mod is listed (problems show in red). Start a world and type
inv.give <your item id>in the console to try it.
3. A code mod in Unity (the Mod Kit)
The easiest way: install Unity 6000.6, make a new project and add the Seeds & Circuits Mod Kit package
(download it from the website, unzip it, then Package Manager ▸ + ▸ Install package from disk ▸ the kit's
package.json). Then:
- Seeds and Circuits ▸ New Mod...: a mod folder with code already wired to the game.
- Write code in
Code/, put prefabs, models, textures and sounds inContent/, add Mod Items and Mod Recipes (Create ▸ Seeds and Circuits). - Select the mod asset ▸ Build (or Build and Start Game).
Everything below about Mod, sides, messages and saving works the same. With the kit, Unity compiles your code,
so prefabs can carry your own NetworkBehaviours with RPCs and NetworkVariables. The rest of this section is
the same thing without Unity, using the .NET SDK.
3b. A code mod without Unity
Set up (once)
- Install the .NET SDK (free, from Microsoft: https://dotnet.microsoft.com/download). Version 6 or newer.
- Install an editor: VS Code with the C# Dev Kit extension, Visual Studio Community, or JetBrains Rider. They show what the game's classes contain as you type.
Build an example
The example code mods are complete projects; their files (the .cs, .csproj and mod.json, plus the shared
Mod.props they import) are on the wiki's Example mods page. Put them in a folder, with
Mod.props one level above the example's folder, and build against your installed game's DLLs:
dotnet build HelloMod -c Release -p:GameManaged="C:/Games/Seeds & Circuits/Seeds & Circuits_Data/Managed"
That makes HelloMod/bin/Hello Mod/ (the DLL + mod.json). Copy that folder into the Mods folder, start the game,
and type hello in the console. The game's log shows the mod's messages ([Mod example.hello] ...) in Player.log
next to the Mods folder.
The built examples are also on the website's Mods page, to install and try first.
Make your own
- Copy an example folder (say
HelloMod) next to the others and rename the folder, the.csprojand the class. - In the
.csproj, changeAssemblyNameandModFolder. Inmod.json, set your ownid,nameandside. - Write code in your class (it derives from
Mod). The main places to start:
public sealed class MyMod : Mod
{
public override void OnLoad() { /* turned on: hook things up */ }
public override void OnWorldLoaded() { /* the world is running */ }
public override void OnUpdate() { /* every frame */ }
public override void OnUnload() { /* turned off: undo what OnLoad did */ }
}
- Build, copy
bin/<ModFolder>into the Mods folder, try it. Repeat. To load a new build without restarting, switch the mod off and on in the Mods screen (the old code stays in memory until the game quits, so restart now and then).
Pick a side
"side" in mod.json decides who needs the mod in multiplayer:
both: everyone runs it (gameplay, items, anything the host and players must agree on). Players download it from the host when they join. Example: Waypoint Map (shared waypoints go through the host).client: only changes what this player sees (HUD, UI). Works on any host. Example: Trader Compass.server: only the host runs it (admin tools, rules). Example: Ban Hammer.
Multiplayer in a mod
The host runs the world; players see what it sends them. In a both mod, the same code runs on every machine, so ask
where you are and talk to the other copies with messages:
public override void OnLoad()
{
// On the host: a player asked for something.
OnMessage("request", msg => { if (IsServer) SendToClients("update", "new state for everyone"); });
// On every player: the host's answer.
OnMessage("update", msg => Log("Host says " + msg.Text));
}
void SomethingHappened() => SendToServer("request", "please");
OnPlayerJoined(clientId) (host) is the place to send a new player what they missed.
Saving, events, keys and settings
- Saving with the world:
WorldData.Set("key", anything)on the host (e.g. inOnSavingWorld),WorldData.Get<T>("key")inOnWorldLoaded. - Reacting to the game:
GameEvents.PlayerDied += ...,TreeFelled,ItemCrafted,DayStarted... (host only). - A key:
var key = AddKey("Open Map", "<Keyboard>/m");thenif (key.WasPressedThisFrame())inOnUpdate. Players can rebind it in Settings ▸ Controls. - New content from code:
AddCrop,AddAnimal,AddStatusEffect,AddTool,AddTrade,AddSounds,AddMusic... (list in MODS.md ▸ Adding content from code). All of it disappears again when your mod is turned off. - NPCs and animals that act differently:
AddNpcJob,AddDialogueOption,AddAnimalBehaviour(MODS.md ▸ NPC and animal behaviour; the Town Life example). - Your own world type / terrain:
AddWorldType("my.world", "My World")inOnLoad, then change the island inOnMapGeneratedwhenworld.type == "my.world". The Volcano Island example does exactly this (MODS.md ▸ World generation). - Your own screen: set
ScreenOpen = truewhile it shows (cursor free, game controls off, so the mouse wheel doesn't change the hotbar),falsewhen it closes; overrideOnCloseScreen()to hide it when the player presses Escape. - Settings:
var range = AddSlider("range", "Range (m)", 50, 1000, 300, 50);thenrange.Value. It shows on Settings ▸ Mods. The Waypoint Map example does exactly this with its shared waypoints.
To test multiplayer alone: run the game twice, host in one and join with the join code in the other.
Finding your way around the game's code
Everything public in the game's assemblies (SeedsAndCircuits.*.dll) can be used: ItemCatalog (items),
RecipeCatalog (recipes), PlayerSpawn (players), TownNpc (NPCs and traders), Compass.CollectMarkers (compass
markers), DebugConsole (commands), and so on. Your editor lists what each class has, and the Mod Kit's
Documentation~/GameReference.md lists the game's item ids, stations and shaders.
Pictures and models
Small images (icons) can sit in the mod as PNG files (LoadTexture("icon.png"), or "icon" in items.json). Models,
materials and sounds go in an AssetBundle, built in a Unity project of the same Unity version (6000.6) and loaded
with LoadAssetBundle / LoadAsset<T>.
4. Share it
Zip the mod's folder (with mod.json at the top) and upload it on the website's Mods page. After a quick review
it's listed there and in the game's Mods ▸ Browse, and players install it with one click. For a new version, raise
version in mod.json and upload again from your mod's page.
Players who join a game whose host has the mod get it automatically (after the join screen asks them).
Rules of thumb
- Undo everything in
OnUnload(unsubscribe events, destroy objects you made): players can turn mods off without restarting. - Save your data in
DataFolder, never in the mod's own folder (that changes the mod's hash, and every joiner would have to download it again). - Keep
bothmods small: joiners download them. - Save and send your own classes with
Json.ToJson/Json.FromJson<T>(fromSeedsAndCircuits.Mods). Unity'sJsonUtilitycan't see classes in a mod's DLL and quietly writes{}. - Use
[ConsoleCommandAttribute("mymod.thing", "What it does")]on static methods for quick testing commands.