Exploring the game code
Mycopunk is a managed Unity game, so much of its game logic can be read from the assemblies in its Managed directory. A decompiler translates the compiled .NET instructions back into readable C#.
The result is useful for finding Harmony targets and understanding how game systems work, but it is not the original source code. Names and control flow may differ from what the developers wrote.
Find the game assemblies
Open this assembly from the Mycopunk installation:
Mycopunk_Data/Managed/Assembly-CSharp.dllIt contains most of the game-specific classes. The same directory contains the Unity modules and third-party libraries referenced by the game.
WARNING
Do not copy game assemblies into your repository or distribute them with your mod. Open them from your own game installation and only ship your mod's files.
Choosing a decompiler
| Feature | ILSpy | dnSpyEx | Rider | Visual Studio |
|---|---|---|---|---|
| Type | Standalone GUI and command-line tool | Standalone GUI | IDE feature | IDE feature |
| Desktop support | Windows GUI; command-line tool and VS Code extension are cross-platform | Windows | Windows, macOS, and Linux | Windows |
| Browse an assembly tree | Yes | Yes | Yes, with Assembly Explorer | Yes, with Object Browser |
| Search the whole assembly | Yes | Yes | Yes | Limited compared with standalone tools |
| Analyze callers and references | Yes | Yes | Yes, with navigation and Find Usages | Basic reference navigation |
| Debug managed code | No built-in debugger | Yes | Yes | Yes |
| Edit and save an assembly | No | Yes | No | No |
| Best fit | Focused browsing and search | Browsing plus managed debugging | Exploring targets while writing a mod | Exploring targets while writing a mod on Windows |
Features can vary between releases and installed extensions. You do not have to choose only one. An IDE decompiler is convenient while writing code, while ILSpy or dnSpyEx is often easier for exploring an unfamiliar system.
Using ILSpy
Open Assembly-CSharp.dll in ILSpy. It appears in the assembly tree, where you can expand namespaces, classes, and methods.
A basic workflow is:
- Search for a class, method, or field related to the feature.
- Select a result and read its fields first.
- Inspect the methods that use those fields.
- Use Analyze on a method or type to find callers and references.
- Check parameter types and overloads before writing a patch.
ILSpy can load dependencies from the same Managed directory. If a type is unresolved, open its assembly as well or check that ILSpy is searching the game's assembly directory.
Using dnSpyEx
Open Assembly-CSharp.dll in dnSpyEx and browse it through the Assembly Explorer. Search can find types, methods, fields, properties, and text stored in the assembly.
Right-click a type or member and use Analyze to inspect:
- Methods that call it
- Methods it calls
- Types that use it
- Interfaces it implements
- Methods that override it
This is useful when a method name looks correct but you do not know when it runs. Finding its callers usually reveals which game action reaches it.
Although dnSpyEx can edit assemblies, mods should normally use BepInEx and Harmony instead. Direct game-assembly edits are harder to distribute, conflict with other changes, and are replaced when the game updates.
Using Rider
Once Assembly-CSharp.dll is referenced by the mod project, Rider can decompile game types without a separate application.
Place the caret on a type or method and use Go to Declaration. Rider opens a read-only decompiled version when source code is unavailable. You can also use Find Usages, Type Hierarchy, and the Assembly Explorer to follow related code.
This works well when writing a patch because you can move between the patch and its target without leaving the IDE. For broad searches through the entire game, a standalone decompiler may be easier to navigate.
Using Visual Studio
Visual Studio can show metadata or decompiled source when you use Go to Definition on a type from a referenced assembly. Its Object Browser can also list assemblies, namespaces, types, and members.
If Visual Studio shows metadata instead of C#, check its decompilation settings and make sure you are using a current Visual Studio 2022 installation. See Microsoft's guide to decompiling .NET assemblies while debugging for the relevant settings and limitations.
Searching for a feature
Start with words that describe what the player sees. For the pickup-popup Harmony example, useful searches include:
upgrade
pickup
popupSearching for UpgradePopup finds a class with fields such as:
public TextMeshProUGUI nameText;
public TextMeshProUGUI rarityText;
public Image icon;Those fields show that the class controls the visible popup. Its methods include two Setup overloads:
public void Setup(PickupInfo info, bool start = true)
public void Setup(object reference, string name, /* other parameters */)Opening PickupInfo reveals the values supplied to the popup:
public struct PickupInfo
{
public string name;
public string subtitle;
public Color color;
public Rarity rarity;
}This is enough information to select the shorter Setup overload and change PickupInfo in a Harmony prefix.
Other ways to search
Class names are not always obvious. Try searching for:
- Text visible in the game
- Localization IDs passed to
TextBlocks.GetString - A related field type
- A base class or interface
- A method called by code you already understand
For example, the game may display translated text while the code contains an ID such as:
TextBlocks.GetString("mission_success")Searching for the final English text might return nothing, while searching for mission_success leads to the code that displays the mission result.
Reading decompiled code carefully
Decompiler output has a few common problems:
- Local variable names may be replaced with names such as
num,value, orresult. - Compiler-generated state machines can make coroutines and asynchronous methods harder to read.
- Generated networking methods such as
__rpc_handler_*contain implementation details that are poor patch targets. - Missing dependencies can produce unresolved types or invalid-looking casts.
- A decompiler may reconstruct a method differently from another tool even though both represent the same compiled instructions.
Prefer readable game methods over generated helpers. For networking code, patch the named RPC method rather than its generated handler unless you have a specific reason not to.
Before changing behavior, apply a prefix or postfix that only writes a log message. Trigger the feature in-game and confirm that the message appears in LogOutput.log. This verifies the target before the patch starts modifying values.
After a game update
Game updates can rename methods, change parameters, or move code into another class. Replace your local assembly references, rebuild the mod, and inspect each patch target again. A method that looks similar may still have changed enough to break a patch.