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.

xml
<Reference Include="TechTree">
  <HintPath>C:\Program Files (x86)\Steam\steamapps\common\Supermarket Simulator\BepInEx\plugins\rodopoulos\TechTree.dll</HintPath>
  <Private>false</Private>
</Reference>
Private=false is not optional

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.

xml
<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>
Il2Cppmscorlib is not optional

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:

AssemblyYou need it for
UnityEngine.CoreModuleAnything at all: GameObject, Transform, Mathf, Time, Resources
UnityEngine.UI + UnityEngine.UIModuleImage, Button, Canvas
Unity.TextMeshProAny on-screen text
UnityEngine.AIModuleNavMeshAgent, so anything about NPC movement
UnityEngine.InputLegacyModuleInput.GetKey and friends
UnityEngine.ImageConversionModuleLoading a PNG into a Texture2D
Unity.LocalizationReading the player's selected language
Not sure which one?

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.

csharp
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 missingYour plugin does not load at allYour plugin loads
Use it whenThe upgrades are your modThe upgrades are a bonus on top of a mod that works alone
Extra workNoneYou 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:

csharp
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.

csharp
if (TechTreeApi.ApiVersion < 1)
{
    Log.LogWarning("Tech Tree is too old for this mod.");
    return;
}

Shipping your mod

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.

PleaseWhy
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:

mod page
Requires Tech Tree by Rodopoulos, which provides the upgrade tree this mod
plugs into.
https://www.nexusmods.com/supermarketsimulator/mods/1658
And the other way round

If 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.