Developing Mods
This document aims to give some general workflow-related advice for modders, and it serves as documentation for some of the mod loader features that are related.
Project Layout
Manually installed mods are always placed in the Mods
folder in Besiege_Data.
For relatively small or simple mods, it is possible to just directly place the mod there for developing it as well.
For most mods, one likely wants an extra directory to store additional resources like source code, Photoshop files, Blender files, source control, etc. Storing these inside the mod directory itself is not advised in order to prevent some issues, like them being uploaded to the Workshop along with the rest of the mod.
A recommended structure for these use-cases may look as follows:
<root mods folder> (wherever you want to keep your projects, this can be Besiege_Data/Mods)
|- SomeMod Project/ (keep this directory under source control)
|- SomeMod/ (this directory is what's loaded by the game)
|- Mod.xml
|- SomeBlock.xml
|- SomeAssembly.dll
|- Resources/
|- ...
|- src/
|- IDE Project file / build scripts / ...
|- Source files (possibly organized in more folders)
|- SomeOtherMod Project/
|- AThirdMod Project/
|- ...
This keeps all files required to develop the mod in one place. You can then set up your build environment such that the compiled assemblies are automatically copied to the mod folder, ideally using relative paths. This also makes sure the the mod is self-contained in one folder, which is useful for checking it into source control, for example for collaborating with other modders.
The last step is then to tell the game where to find the mod. There are two ways to do this:
- By launching it with a
-mod "<root mods folder>/SomeMod Project/SomeMod/
command line argument. You can set up a shortcut with all the command line options you need or set it up in Steam for example. - By once executing
addmodsdir "<root mods folder>/SomeMod Project/
in the in-game console. This will make the game look for any mods in this directory permanently, until it is removed from the list again. This happens automatically if the mod is created using thecreatemod
command.
Project Creation Console Commands
There are 4 console commands to make starting a new mod and adding content to it easier.
createmod
createmod "<name>" "[root mods folder]"
createmod
always creates a mod according to the recommended structure described in the
section above.
The second argument is optional, it corresponds to the <root mods folder>
described
above in the Project Layout section. It should be an absolute path on your computer. If the
path contains spaces, place quotes ("
) around it.
If it is not specified, Besiege_Data/Mods
is used as the default.
createblock and createentity
createblock "<modid | name>" "<name>"
createentity "<modid | name>" "<name>"
Adds a new block or entity to the specified name.
createassembly
createassembly "<modid | name>" <compiled | script> <assembly name> <default namespace> [noUnityTools | forceUnityTools]
Creates a new assembly and adds it to the mod.
Requires/assumes the recommended project structure, as used by createmod
.
With compiled
:
Creates (or updates) a solution and creates a project that can be opened using Visual Studio or MonoDevelop (tested with VS Community 2017 and MonoDevelop 5.9.6, should work with other versions).
The project contains references to appropriate game assemblies and a post-build action to copy the assembly to the mod directory and is set up in a portable way so that it can be checked in to source control and used from multiple machines.
Note: The references are set up using two environment variables that are set up when the game is first started with mods enabled.
If you have not restarted your computer since then, Visual Studio may not immediately
be able to find the referenced assemblies. To fix this, restart Visual Studio and make
sure to start it through the start menu and not by double-clicking the solution.
Alternatively, just restart your computer once after executing the createassembly
command and the references should work.
Regarding the unityTools
arguments: For developing code to run in the game, it is helpful
to have the Visual Studio Tools for Unity installed. They add a .NET Target Framework Profile
that matches exactly what is available in Unity, thereby preventing you from attempting to
use features that are not actually present in the runtime.
The createassembly
command will attempt to detect whether the Unity Profile is available,
and use it if it is. If it is not, it will print a warning and use the default profile instead.
The warning can be disabled by passing the noUnityTools
argument. In that case, the default
profile is always used.
If the game doesn't detect the Unity profile even though it is installed, the check can be
bypassed by passing the forceUnityTools
argument.
Debug Mode
Using the Debug
element of the mod manifest, it is possible to turn on debug mode for a mod.
It is generally recommended to turn on debug mode while developing a mod but turning it off
for any public releases.
Turning it on will have three main effects:
- Some additional error messages and warnings when validating the XML files in the mod.
- Enables value reloading.
- Makes the mod loader act as if the mod version was changed every time the mod is loaded.
Value reloading describes a feature of the mod loader that allows a mod author to change the definitions of blocks and entities while the game is running. This doesn't work for all of the elements that are part of a block or entity definition, but among others, it is available for Colliders, Adding Points and the transforms of Mesh elements.
Reloaded values will be applied to the block/entity prefabs but also to any blocks and entities already placed.
Combined with turning on debug mode for the blocks or entities themselves (which is separate from the debug mode for the whole mod!) this is very useful for positioning these elements correctly. One can just adjust a value (or even add or remove a collider, etc. completely), save, and immediately check in-game if it had the desired effect.
Reloading is normally triggered automatically when a file inside the mod directory is changed, but in case this doesn't work in can also be triggered manually by pressing the Reload key, Ctrl+H by default.
The last point is to avoid unexpected behaviour while developing mods. A version change triggers various maintenance tasks that should be performed when the mod is changed. During development, these tasks should usually be performed every game restart (e.g. recompiling script assemblies, which are otherwise cached) and enabling Debug means it is not required to change the Version element every time while developing.
Log Output
When developing mods containing custom code, it is very useful to be able to print debug messages and instantly see them in-game.
The in-game console is normally only used for entering commands, but by setting the
show_logs
variable to true
(enter show_logs true
) printing all log output to it can
be enabled. This will make all messages logged using the various Debug.Log*
methods
(from all mods and the game itself) as well as exceptions with stack traces appear in the
console. Additionally, the frame number a message was logged in is also displayed.
If the same exception is thrown more than once without any other messages in between, it won't be logged repeatedly to combat the "exception-spam" problem otherwise frequently seen (a message noting that one or more messages have been omitted is shown instead, the full log can be seen in the log file if necessary).
It is generally recommended to keep this option turned on when developing mods in order to be notified of exceptions that are thrown.
Debugging Code
If you have access to a development build of the game, it is possible to use a real debugger on your mods code. Visual Studio with Unity Tools for Visual Studio has a "Debug->Attach Unity Debugger" menu option and a running development version of the game will appear in the window which is opened by that option.
On its own, the running game/debugger will not have access to the mod's source code, but the .pdb file that is generated by Visual Studio for debug builds of the mod can be converted into a .mdb file using Mono's pdb2mdb utility. Placing this .mdb file next to the .dll file in the mod's directory will allow the debugger to find the debug information.
Combined, this allows complete debugging of mod code using the Visual Studio debugger, including e.g. breakpoints and stepping through code.
Developing Custom Code
As mentioned in the documentation for custom code, the compiled assemblies need to be located somewhere under the mod's root folder.
It is thus useful to automate copying them instead of having to do it manually every time.
Note: This is set up automatically when using the createassembly
command.
Considering it is generally possible to develop custom code using any IDE that support .NET (or even no IDE at all), it is not possible to explain how to set this up for all possible scenarios. However, here is an example of how to set up a post-build action to do this in Visual Studio:
In the properties of the project, open the "Build Events" section. Entering the following
command in the "post-build event command line" box will copy the target assembly to the
correct location (after replacing the placeholder with the correct path to the Besiege
installation and mod name of course):
copy "$(TargetPath)" "<path-to-besiege>\Mods\<mod-name>\$(TargetFileName)"
If the mod is an open-source or otherwise collaborative project, it is advisable to set
up the build event such that it does not contain machine/user-specific paths. A relatively
easy way of doing this is requiring everybody who is to work on the project set up an
environment variable (e.g. BESIEGE_LOCATION) that points to their Besiege installation:
copy "$(TargetPath") "%BESIEGE_LOCATION%Besiege_Data\Mods\<mod-name>\$(TargetFileName)"