Tech Tree API
Project setup
How to reference TechTree.dll so your mod compiles against it without shipping a second copy of it.
Where the dll comes from
There is no separate SDK package. The dll you compile against is the same file the
player installs: download the Tech Tree zip from
Nexus
and take TechTree.dll out of
BepInEx/plugins/rodopoulos/.
From 1.3.0 the zip also carries TechTree.xml. Keep it next to the
dll and Visual Studio / Rider will show the documentation for every API member as you type. It is
not needed at runtime.
The csproj block
This is the one line that is specific to Tech Tree. Everything else your project needs is the usual IL2CPP plugin set, listed in full below.
<Reference Include="TechTree">
<HintPath>C:\Program Files (x86)\Steam\steamapps\common\Supermarket Simulator\BepInEx\plugins\rodopoulos\TechTree.dll</HintPath>
<Private>false</Private>
</Reference>Without it MSBuild copies
TechTree.dll into your build output, and anyone who ships the whole
output folder puts a second Tech Tree into BepInEx/plugins.
BepInEx refuses to load two plugins with the same guid, so one copy is dropped with a duplicate guid error - and which one wins is not yours to choose. The player can end up running the version you happened to build against instead of the one they installed, and the bug report you get will make no sense.
A complete csproj
Copy this whole file, change GameDir and
AssemblyName, and it builds. This is the same reference set the
example mod uses, so it is known to compile rather than assembled from memory.
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<AssemblyName>BigBoxes</AssemblyName>
<RootNamespace>BigBoxes</RootNamespace>
<LangVersion>latest</LangVersion>
<Nullable>disable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<!-- Nothing from the game or from BepInEx belongs in your output folder. -->
<CopyLocalLockFileAssemblies>false</CopyLocalLockFileAssemblies>
<!-- Must be declared BEFORE the ItemGroup below: MSBuild evaluates in order,
and a HintPath built from an unset property silently resolves to nothing. -->
<GameDir>C:\Program Files (x86)\Steam\steamapps\common\Supermarket Simulator</GameDir>
</PropertyGroup>
<ItemGroup>
<!-- ── The Tech Tree API ─────────────────────────────────────────────── -->
<Reference Include="TechTree">
<HintPath>$(GameDir)\BepInEx\plugins\rodopoulos\TechTree.dll</HintPath>
<Private>false</Private>
</Reference>
<!-- ── What every IL2CPP plugin needs ────────────────────────────────── -->
<Reference Include="0Harmony">
<HintPath>$(GameDir)\BepInEx\core\0Harmony.dll</HintPath>
<Private>false</Private>
</Reference>
<Reference Include="BepInEx.Core">
<HintPath>$(GameDir)\BepInEx\core\BepInEx.Core.dll</HintPath>
<Private>false</Private>
</Reference>
<Reference Include="BepInEx.Unity.IL2CPP">
<HintPath>$(GameDir)\BepInEx\core\BepInEx.Unity.IL2CPP.dll</HintPath>
<Private>false</Private>
</Reference>
<Reference Include="Il2CppInterop.Runtime">
<HintPath>$(GameDir)\BepInEx\core\Il2CppInterop.Runtime.dll</HintPath>
<Private>false</Private>
</Reference>
<Reference Include="Il2Cppmscorlib">
<HintPath>$(GameDir)\BepInEx\interop\Il2Cppmscorlib.dll</HintPath>
<Private>false</Private>
</Reference>
<!-- ── The game, and the Unity modules you touch ─────────────────────── -->
<Reference Include="Assembly-CSharp">
<HintPath>$(GameDir)\BepInEx\interop\Assembly-CSharp.dll</HintPath>
<Private>false</Private>
</Reference>
<Reference Include="UnityEngine.CoreModule">
<HintPath>$(GameDir)\BepInEx\interop\UnityEngine.CoreModule.dll</HintPath>
<Private>false</Private>
</Reference>
</ItemGroup>
</Project>Leave it out and the build fails as
soon as your code touches an Il2CppSystem type - which happens the
moment you read a list off a game object, long before you meant to. It is not obvious from the
error message that this is the missing reference.
Other Unity modules
Add one Reference per module you actually use, all from
BepInEx\interop\. The ones that come up most:
| Assembly | You need it for |
|---|---|
UnityEngine.CoreModule | Anything at all: GameObject, Transform, Mathf, Time, Resources |
UnityEngine.UI + UnityEngine.UIModule | Image, Button, Canvas |
Unity.TextMeshPro | Any on-screen text |
UnityEngine.AIModule | NavMeshAgent, so anything about NPC movement |
UnityEngine.InputLegacyModule | Input.GetKey and friends |
UnityEngine.ImageConversionModule | Loading a PNG into a Texture2D |
Unity.Localization | Reading the player's selected language |
Add references as the compiler asks for them.
"The type X is defined in an assembly that is not referenced" names the assembly - and the file
with that name is sitting in BepInEx\interop\.
Declaring the dependency
[BepInDependency] does two things: it makes BepInEx load Tech Tree
first, and it tells the player which mod is missing if they install yours without it.
using BepInEx;
using BepInEx.Unity.IL2CPP;
using TechTree.Api;
[BepInPlugin("BigBoxes", "Big Boxes", "1.0.0")]
[BepInDependency(TechTreeApi.ModGuid)] // hard: your mod will not load without it
// [BepInDependency(TechTreeApi.ModGuid, BepInDependency.DependencyFlags.SoftDependency)]
public sealed class Plugin : BasePlugin
{
public override void Load() { }
}Hard or soft?
| Hard (default) | Soft | |
|---|---|---|
| Tech Tree missing | Your plugin does not load at all | Your plugin loads |
| Use it when | The upgrades are your mod | The upgrades are a bonus on top of a mod that works alone |
| Extra work | None | You must guard every API call, see below |
With a soft dependency, touching any API type when Tech Tree is absent throws
TypeLoadException at the point the method is JIT-compiled, not
where the call is written - so a plain if around the call is not
enough. Put the API code in its own method and check first:
public override void Load()
{
if (IL2CPPChainloader.Instance.Plugins.ContainsKey("TechTree"))
RegisterUpgrades(); // separate method: only JIT-ed if we actually call it
else
Log.LogInfo("Tech Tree not installed, upgrades disabled.");
}
// [MethodImpl(MethodImplOptions.NoInlining)] if your compiler gets clever.
private void RegisterUpgrades()
{
var tree = TechTreeApi.ForPlugin("BigBoxes", "Big Boxes");
// ...
}Checking the installed version
TechTreeApi.ApiVersion is deliberately
readonly and not const, so reading it gives
you the number from the dll the player actually has. This matters more than it sounds - see
Versioning.
if (TechTreeApi.ApiVersion < 1)
{
Log.LogWarning("Tech Tree is too old for this mod.");
return;
}Shipping your mod
- Ship only your own dll. Never bundle
TechTree.dll. - Embed your icon PNG as a resource or a base64 string - do not ask the player to drop a loose png next to the dll. See Icons.
Two things I ask of you
The API is free to use and there is nothing stopping you either way. These are asks, not licence terms - but the first one is really for your own sake.
| Please | Why |
|---|---|
| List Tech Tree as a requirement on your mod page, in the Requirements section, and link to it | Without it, players install your mod alone, nothing appears, and the bug report lands on your page. Nexus shows requirements before the download button for exactly this reason. |
| Credit Tech Tree and its author (Rodopoulos) somewhere in your description | A line is plenty. The tree, the saving, the pricing and the translations are doing a fair share of the work in your mod, and it costs you nothing to say so. |
Something like this in your description does both:
Requires Tech Tree by Rodopoulos, which provides the upgrade tree this mod
plugs into.
https://www.nexusmods.com/supermarketsimulator/mods/1658If you publish something built on the API, tell me. I will link it from the Tech Tree page - a tree that fills up with other people's upgrades is the entire point of this thing existing.