Mega Tree API

Project setup

The Mono csproj that actually builds, and how to reference MegaTree.dll without shipping a second copy of it.

This is the page that decides whether anything works

Megastore Simulator runs on Mono. Supermarket Simulator runs on IL2CPP. Everything on this page follows from that one difference, and getting it wrong produces the same symptom every time: the game starts, your mod is not in the plugin list, and nothing is written to the log.

Supermarket SimulatorMegastore Simulator
BepInEx6, IL2CPP build5, Mono build
Target frameworknet6.0netstandard2.1
BepInEx referencesBepInEx.Core + BepInEx.Unity.IL2CPPone BepInEx.dll
Il2CppInteroprequireddoes not exist
Game assembliesBepInEx/interop/Megastore Simulator_Data/Managed/
Base classBasePlugin, Load()BaseUnityPlugin, Awake()

Why netstandard2.1 and not net35

net35 is the classic target for a Mono game mod, and it is the first thing most people try. It fails on a modern SDK with MSB3644: the reference assemblies for .NETFramework v3.5 were not found - they do not ship with the SDK any more, and getting them means installing an old developer pack.

netstandard2.1 avoids that entirely: the reference assemblies come from NuGet on their own, and Unity's Mono runtime supports the profile natively. Mega Tree itself is built this way.

The complete csproj

This is the whole file. Nothing is omitted - if you copy it and fix the GameDir, it builds.

xml
<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>netstandard2.1</TargetFramework>
    <AssemblyName>BigBoxes</AssemblyName>
    <RootNamespace>BigBoxes</RootNamespace>
    <LangVersion>latest</LangVersion>
    <ImplicitUsings>disable</ImplicitUsings>
    <Nullable>disable</Nullable>

    <!-- Without this the build copies every referenced dll into your output. -->
    <CopyLocalLockFileAssemblies>false</CopyLocalLockFileAssemblies>

    <Version>1.0.0</Version>

    <!-- Change this one line and everything below follows. -->
    <GameDir>C:\Program Files (x86)\Steam\steamapps\common\Megastore Simulator</GameDir>
    <Managed>$(GameDir)\Megastore Simulator_Data\Managed</Managed>
  </PropertyGroup>

  <ItemGroup>
    <Reference Include="MegaTree">
      <HintPath>$(GameDir)\BepInEx\plugins\rodopoulos\MegaTree.dll</HintPath>
      <Private>false</Private>
    </Reference>

    <Reference Include="BepInEx">
      <HintPath>$(GameDir)\BepInEx\core\BepInEx.dll</HintPath>
      <Private>false</Private>
    </Reference>
    <Reference Include="0Harmony">
      <HintPath>$(GameDir)\BepInEx\core\0Harmony.dll</HintPath>
      <Private>false</Private>
    </Reference>

    <Reference Include="UnityEngine">
      <HintPath>$(Managed)\UnityEngine.dll</HintPath>
      <Private>false</Private>
    </Reference>
    <Reference Include="UnityEngine.CoreModule">
      <HintPath>$(Managed)\UnityEngine.CoreModule.dll</HintPath>
      <Private>false</Private>
    </Reference>

    <!-- The game itself. Here it is the real assembly, with real IL. -->
    <Reference Include="Assembly-CSharp">
      <HintPath>$(Managed)\Assembly-CSharp.dll</HintPath>
      <Private>false</Private>
    </Reference>
  </ItemGroup>
</Project>
Private=false is not optional

Without it, MSBuild copies MegaTree.dll (and the game assemblies) into your output folder. Anyone who then ships that folder puts a second Mega Tree into BepInEx/plugins. BepInEx drops one of the two over a duplicate guid, and which copy survives is not up to you - so the player may be running a version of Mega Tree you shipped by accident, months out of date.

It also means the zip on your mod page contains most of the game.

Adding more UnityEngine modules

Unity splits the engine across many assemblies, and the compiler error when one is missing does not look like a missing reference - it looks like your code is wrong:

You useAdd
Input.GetKeyDownUnityEngine.InputLegacyModule
Image, Button, ScrollRectUnityEngine.UI and UnityEngine.UIModule
TMP_TextUnity.TextMeshPro
ImageConversion.LoadImageUnityEngine.ImageConversionModule
Physics.RaycastUnityEngine.PhysicsModule
NavMeshAgentUnityEngine.AIModule

All of them live in the same Managed folder, so the block is always the same three lines with a different name.

The plugin skeleton

csharp
using BepInEx;
using HarmonyLib;
using MegaTree.Api;

namespace BigBoxes;

[BepInPlugin("BigBoxes", "Big Boxes", "1.0.0")]
[BepInDependency(MegaTreeApi.ModGuid)]
public sealed class Plugin : BaseUnityPlugin
{
    private void Awake()
    {
        var tree = MegaTreeApi.ForPlugin("BigBoxes", "Big Boxes");
        // ... your Register calls ...

        new Harmony("BigBoxes").PatchAll(typeof(Plugin).Assembly);
    }
}

What you do NOT need here

If you are porting from Supermarket Simulator, delete all of this - none of it exists in Mono and each one is a compile error waiting to confuse you:

And one rule that stops applying

In Supermarket Simulator, writing to a static field of a game class crashes the process - the field is const-backed in native code. That rule is IL2CPP's, not C#'s, and it does not apply here. In Mono a static field is a static field.

Building

bash
dotnet build BigBoxes.csproj -c Debug

Then close the game, drop the dll into BepInEx/plugins/, and start it again. BepInEx locks the file while the game runs, so copying over a running game fails silently - which reads as "my change did nothing".