A working, buildable example mod for Final Factory, meant to be used as the starting point for your own mods. It adds three items (a combat ship, an assembler, and a printer), a technology branch to unlock them, a couple of config tweaks, and one custom ECS system — so every kind of mod you might write has an example to crib from.
Questions? Ask in the Final Factory Discord #modding channel — we're happy to help, and we often share game source snippets to unblock modders.
- Unity 6000.3.19f1 (the exact version is recorded in
ProjectSettings/ProjectVersion.txt; Unity Hub will offer to install it when you open the project) - A Final Factory install (Steam, or a local build)
- Clone the repo (use
git clone— downloading the zip does not work because of Git LFS). - Copy the game DLLs into the project — before opening it in Unity. The project references the game's assemblies, so they must exist the first time the editor opens:
- Rename
finalfactory.properties.templatetofinalfactory.properties, then edit it and setFinalFactoryDirto your Final Factory install folder (the one containingfinalfactory_Data). Use forward slashes; they work on Windows too.- Steam example:
FinalFactoryDir=C:/Program Files (x86)/Steam/steamapps/common/FinalFactory - If you build the game locally, point it at your build output instead.
- Steam example:
- Run the copy script from the project root:
- Windows:
copy-finalfactory-dlls.cmd(double-click it, or run it from a terminal) - Mac / Linux:
./copy-finalfactory-dlls.sh
- Windows:
- This copies
FFCore.dll,FFSystems.dll,FFComponents.dll,FFTechnology.dll,FFNetcode.dllandFFSpaghetti.dllintoAssets/FinalFactoryDlls/. Yourfinalfactory.propertiesis gitignored, so your local path stays out of git. When the game updates, re-run the script and rebuild your mod.
- Rename
- Open the project with Unity Hub (it will prompt to install the exact editor version if you don't have it).
- Add a Steam Workshop preview image: put a
Preview.pngorPreview.gif(under 1 MB — Steam's limit) in the project root. The build fails with a clear error if it's missing or too big.
NOTE Once the project is open, the DLL steps are also available as menu items:
Modding > Set Final Factory Path...andModding > Copy Final Factory DLLs. They're a convenience for later — the first copy must happen before the editor ever opens.
WARNING Known issue: on first open you may see "assembly failed to load" errors (e.g.
Unity.Netcode.Runtime). Clear the errors and restart Unity once; they don't come back.
Modding > Build X64 Modbuilds into<project root>/build/<YourModID>/— the managed DLL, the Burst native DLL, an AssetBundle with your prefabs/icons,manifest.properties, and your preview image.Modding > Build and Installadditionally copies that folder into the game's mod directory:- Windows:
%USERPROFILE%\AppData\LocalLow\Never Games\finalfactory\mods\<YourModID> - macOS:
~/Library/Application Support/Never Games/finalfactory/mods/<YourModID>
- Windows:
- Start Final Factory. Your mod loads at startup — check the in-game Mod Menu to confirm it's listed and enabled.
Important: mod code only runs inside the real game. Pressing Play in this Unity project does not load your mod — the template project is a build environment, not a game host. The loop is always: build → install → launch Final Factory.
- Start Final Factory with your mod installed.
- Open the Mod Menu — your mod has a blue
^icon next to it. - Click it to upload. The workshop listing can take a few minutes to appear; once it's fully published, refreshing the screen removes the
^icon. - To upload an update: increment your mod version in
UserMod.cs, rebuild, reinstall, and the upload icon reappears.
Every mod implements exactly one IUserMod — the mod's identity (see Assets/Scripts/UserMod.cs):
string ID { get; } // folder + workshop identity: letters/digits/underscore only, NO spaces
string FullName { get; }
string Description { get; }
string Author { get; }
string EmailContact { get; }
string Website { get; }
string[] Dependencies { get; } // IDs of mods yours requires (empty array if none)
FFVersion ModVersion { get; } // your mod's version, e.g. new(1, 0, 20, 0)Optionally, implement IUserModLoader to actually change the game (see Assets/Scripts/UserModLoader.cs):
List<EntityConfig> DefineEntityConfigs(); // add new items/buildings/ships (or return an empty list)
List<TechnologyConfig> AddTechnologies(); // add research that unlocks your items
void PostInitializationHook(); // after all config + systems load: tweak ANY config in the game
void OnGameStart(Canvas inGameUiCanvas); // when a new/loaded game starts: hook UI, late setup| File | Demonstrates |
|---|---|
Assets/Scripts/UserMod.cs |
The identity boilerplate |
Assets/Scripts/UserModLoader.cs |
New ship/assembler/printer configs with recipes; cloning an existing entity (the Gherik Connector); editing existing item, terrain, and global config; adding technologies |
Assets/Scripts/Systems/FleetRandomMovementSystem.cs |
A Burst-compiled ECS system with a job that changes the simulation safely for multiplayer: on the heartbeat, ordered after the game's own system, with randomness every peer agrees on |
Assets/Scripts/Examples/RepairBeacon/ |
A complete multiplayer-safe feature: a saved component on the Loth Printer, a heartbeat system that repairs nearby buildings, a player action (network operation) that switches it on and off, and a UI panel built in code |
Assets/Scripts/Utils/ConfigUtils.cs |
A small helper for editing accepted-ship lists |
Assets/Resources/ |
How icons (Icons/) and entity prefabs (ItemEntities/) get into your mod's AssetBundle |
New systems you write are auto-detected when the game loads your mod — no registration call needed. For how Final Factory's systems, groups, and update timing work (and the multiplayer determinism rules your systems must follow), read DOCUMENTATION.md.
Final Factory multiplayer runs the simulation in deterministic lockstep on every peer. A mod system that touches factory/simulation state must follow the same rules the game's own systems do, or it will desync multiplayer sessions:
- Put simulation logic in Fixed groups (
FFFixedPreTransformGroupis the usual home), with the heartbeat's time (FFTimeData.deltaTime); Controller groups are for presentation only. - Make decisions in
fpfixed-point math (Unity.Mathematics.FixedPoint), and never let the order entities come out of a query decide anything. - Never let input, the local player, wall-clock time, frame rate or
UnityEngine.Randomreach simulation state. Seed randomness from stable keys (seeFleetRandomMovementSystem.cs). - Send player actions as network operations; UI only reads (see
Assets/Scripts/Examples/RepairBeacon/). - Delete entities by adding
DeletionMarker, neverDestroyEntity.
CLAUDE.md has the full rules with the reasons, and Tools/check-mp-safety.sh scans your code for the common breaks. The game currently turns multiplayer off while any mod is enabled; following these rules is what lets your mod work there once it doesn't, and keeps single player correct meanwhile.
| Symptom | Cause / fix |
|---|---|
| Mod doesn't appear in the Mod Menu | Folder name must equal your mod ID exactly, and the folder needs <ID>.dll + manifest.properties. Build and Install gets this right — manual copies often don't. |
| "Invalid mod ID" | ID may only contain letters, digits, and underscores — no spaces. |
| Mod rejected: incompatible game version | Rebuild against the current game's DLLs: re-run the copy script, then rebuild. |
Mod rejected: no (or multiple) IUserMod |
Your assembly must contain exactly one IUserMod implementation. |
| Burst DLL ignored in-game | The native Burst DLL is exact-game-version locked; the game silently falls back to managed code after an update until you rebuild. |
Hundreds of CS0576/Debug errors in a Unity package |
The game DLLs got re-imported with Auto Reference enabled. The tracked .meta files under Assets/FinalFactoryDlls/ pin it off — don't delete or regenerate them. |
| Game update broke your mod | Re-run the DLL copy script, fix compile errors, rebuild, reinstall. |
You don't have the game's source code, and an AI coding agent (Claude Code, Codex, Cursor, ...) doesn't either. This template gives it the subset it needs:
CLAUDE.md(also read asAGENTS.md): how to build, check and test a mod, the mod API, and the multiplayer rules, written for an agent to follow.Documentation/API/: a reference of the game's public, mod-facing API (types, members, signatures and the game's own doc comments), generated from the game's DLLs.all-members.txtis one line per member, made for grep. Regenerate it after a game update withTools/generate-api-reference.sh.- Checks without opening Unity:
Tools/compile-check.shcompiles your mod in seconds with Unity's own compiler, andTools/check-mp-safety.shflags code that would break multiplayer. An agent can run both after every edit (on Windows, from Git Bash). - Worked examples that compile: new items, a building and a ship (
UserModLoader.cs), a multiplayer-safe simulation system, and a complete feature with a player action and a UI panel (Examples/RepairBeacon/). - Skills in
.claude/skills/:build-mod,add-entity,add-player-action,check-mp-safety,api-lookup. - The MCP for Unity bridge, so an agent can drive the editor (compile, run the build menu, read the console).
A good first prompt: "Read CLAUDE.md. Add a new building that ... ; run Tools/compile-check.sh and Tools/check-mp-safety.sh until both pass, then build and install the mod." What an agent cannot check for you is how the mod plays: start Final Factory and try it.
Writing behavioral mods needs Unity DOTS (ECS) knowledge. Good starting points:
- Entities Manual
- Turbo Makes Games
- WAYNGames
- Unity Discord —
#dots-forum - Final Factory Discord —
#modding
Final Factory deliberately ships without a restrictive EULA clause against decompiling for mod development — we encourage it. Please don't use our sources, assets, or IP for competing or commercial products; otherwise, dig in, and reach out in Discord if you get stuck.