Difference between revisions of "Modding Tutorials/Writing custom code"

From RimWorld Wiki
Jump to navigation Jump to search
(Changed from actual numbers to a #numbered list, changed from space before text to <pre> and added 6. which elaborates on writing your class)
(Updated link to Plague Gun tutorial)
 
(25 intermediate revisions by 4 users not shown)
Line 1: Line 1:
In addition to creating data for the game to use, you can also write code. You could probably write in any .NET language, but I’ve only tested C#.
+
{{BackToTutorials}}
 +
<br/>
  
# Create a new class library project in your code editor of choice. I use Visual Studio, but MonoDevelop also works well.
+
This tutorial gives you a broad idea how to work on a C# solution.
# In your project, add references to these DLLs:<br/><pre>(RimWorldInstallFolder)/RimWorld_Data/Managed/Assembly-CSharp.dll&#10;(RimWorldInstallFolder)/RimWorld_Data/Managed/UnityEngine.dll</pre>
 
# In your project properties, change the target framework to .NET 3.5
 
# Create a new class in a new code file.
 
# You’ll want to add these namespace to each of your source files as necessary.<br/><pre>using UnityEngine;  //For all Unity functionality, rendering, resource management&#10;using AI; //RimWorld AI&#10;using Sound;         //RimWorld sound subsystem&#10;using UI; //RimWorld GUI&#10;</pre>
 
# Write your class. Use [http://ilspy.net/ ILSpy] (Download Binaries) to open Assembly-CSharp.dll, which decompiles the game's source code. Any modding questions can be asked on the [https://ludeon.com/forums/index.php?board=14.0 subforum].
 
# Compile your class into a .dll. Make sure your project's output type is "class library".<br/>''Note that by default, Visual Studio will compile all the references of the project as well, so you’ll get a copy of UnityEngine.dll and Assembly-CSharp.dll and some others. You don’t need these. Just take YourModName.dll.''
 
# Place the .dll in the YourModeName/Assemblies folder of your mod.
 
# Reference the classes in your .dll from the xml data in the YourModName/Defs folder. For example, you could create a new ThingDef with a <thingClass> that points to a class in your .dll.
 
# The game should load your class now.
 
# If you wish, you should also release your source code in the YourModName/Source directory.
 
  
 +
While it is possible to create many mods by modifying or creating XML, writing custom C# code, either as stand-alone classes or as Harmony patches, allows changing almost anything in the game.
  
You can find a small tutorial project here: [[Modding Tutorials/Assembly Modding Example]]
+
=Requirements=
 +
 
 +
* This tutorial requires you to have [[Modding Tutorials/Setting up a solution|set up a solution]].<br/><br/>
 +
 
 +
=Creating a class=
 +
Once you've [[Modding Tutorials/Setting up a solution|set up your solution]] correctly you'll usually be greeted with a class similar like the template below:
 +
====Template====
 +
 
 +
Let's go over the template:
 +
 
 +
<source lang="csharp">/*
 +
* Created by SharpDevelop.
 +
* User: You
 +
* Date: 01-01-1970
 +
* Time: 00:00
 +
*
 +
* To change this template use Tools | Options | Coding | Edit Standard Headers.
 +
*/
 +
using System;
 +
 
 +
namespace MyNameSpace
 +
{
 +
/// <summary>
 +
/// Description of MyClassName.
 +
/// </summary>
 +
public class MyClassName
 +
{
 +
public MyClassName()
 +
{
 +
}
 +
}
 +
}</source><br/>
 +
 
 +
====Using====
 +
 
 +
The first thing you notice is ''using System;'' which indicates this class can call anything from the namespace ''System'' without calling it like ''System.Class.SomeDataType'' but rather ''SomeDataType''. This is very useful because we don't have to worry about such calls anymore (for now).<br/>
 +
 
 +
The next section shows which namespaces you could put in ''using''.<br/><br/>
 +
 
 +
====Namespace====
 +
 
 +
Next up is the ''namespace MyNameSpace'' part - in principle everything inside of a namespace knows everything else inside of that namespace. If you were to make a folder inside of your project (''MyProjectName'' not Solution ''MySolutionName''), any classes inside of that folder would have the namespace ''MyNameSpace.MyFolderName''. '''That's a different namespace''' and unless you tell other classes that it exists (''using MyNameSpace.MyFolderName;'') they won't accept calls to parts of that namespace without that namespace as a prefix.<br/>
 +
 
 +
Anything inside of ''MyNameSpace.MyFolderName'' does however know of everything inside ''MyNameSpace'', just nothing about other subfolders of ''MyNameSpace''.<br/><br/>
 +
 
 +
By default your project knows nothing. Adding DLL references to the ''References'' folder (as done in the previous tutorial) allows you to reference them with ''using'':
 +
 
 +
<source lang="csharp">
 +
namespace MyNameSpace {} /* Assuming MyNameSpace is your ROOT NAMESPACE (MyProjectName ->
 +
Properties -> Application -> Root namespace), this will compile. */
 +
 
 +
namespace MyNameSpace.MyFolderName {} /* Assuming this class is inside of the folder MyFolderName inside of
 +
MyProjectName, this will compile. Other classes outside of the folder
 +
will have to reference this, this class will have to reference classes
 +
in different subfolders (but not the ones in the root folder). */
 +
 
 +
namespace RimWorld {} /* Don't do this. It will compile if RimWorld is your root namespace
 +
but it's bad practice. */
 +
 
 +
namespace System {} /* No, just no. */
 +
</source><br/>
 +
 
 +
You can set your project's namespace by going to visual studio and right-clicking MyProjectName -> Properties -> Application -> Root namespace(Visual Studio 2019 it is Default namespace). Please take some time to make it unique so no compatibility issues will arise with other mods, for your and everyone else's sanity.<br/><br/>
 +
 
 +
====Summary====
 +
 
 +
This summary is part of the template but it's not required. It can help other people understand your code better when you provide the source but besides that you really don't need it.<br/><br/>
 +
 
 +
====Class and Constructor====
 +
 
 +
This is your class definition. To access anything inside of a ''public class'' you will need a reference to an instance of that class:<br/>
 +
 
 +
<source lang="csharp">
 +
MyClassNamefoo = new MyClassName();
 +
</source><br/>
 +
 
 +
The brackets at ''new MyClassName()'' indicate you're creating this instance without ''parameters'' which is possible because your class contains a constructor (anything inside of ''class MyClassName'' which has parameters and isn't a method, often abbreviated '''.ctor''') which accepts a call without parameters:<br/>
 +
 
 +
<source lang="csharp">
 +
public class MyClassName
 +
{
 +
public MyClassName() /* No parameters */
 +
{
 +
}
 +
 
 +
public MyClassName(string foo, int bar) /* Required parameters of datatypes string and int */
 +
{
 +
}
 +
 
 +
public MyClassName(string foo = "none supplied", int bar = 42) /* Default parameters " " " */
 +
{
 +
}
 +
}</source><br/>
 +
 
 +
The last implementation of the constructor (''MyNameSpace.MyClassName.MyClassName'' inside of ''MyNameSpace.MyClassName'') is capable of accepting zero to two parameters while the second implementation of the constructor will only accept calls with exactly two parameters.<br/><br/>
 +
 
 +
=Namespaces=
 +
Some very useful namespaces in general are the following:<br/><br/>
 +
 
 +
{|
 +
! Namespace !! What does it do?
 +
|-
 +
| System.Collections.Generic || Makes it so you can use part of the IEnumerable interface, which is used for datatypes like List<>.
 +
|-
 +
| System.Linq || Allows for cool operations on the IEnumerable interface such as ''.Where()'' which accepts lambda operations (oh my!).
 +
|-
 +
| System.Text.RegularExpressions || Introduces Regular Expressions (RegEx) to C# which allows for intricate operations on strings.
 +
|-
 +
| System.Collections || Holds other parts of the IEnumerable interface you will require when inheriting from that class.
 +
|-
 +
| System.Text || Contains the ''StringBuilder'' which accepts a list of strings and returns them in a specific way.
 +
|}<br/>
 +
 
 +
And the namespaces for RimWorld in particular are these:<br/><br/>
 +
 
 +
{|
 +
! Namespace !! What does it do?
 +
|-
 +
| RimWorld || Many RimWorld specific classes can be found in this namespace. This and the Verse namespace will be most used.
 +
|-
 +
| Verse || Many general use classes can be found in this namespace. This and the RimWorld namespace will be most used.
 +
|-
 +
| RimWorld.BaseGen || Everything that creates the layout and contents of the Outposts, Bases and Settlements is in here.
 +
|-
 +
| RimWorld.Planet || Everything (almost everything) having to do with the planet is in here.
 +
|-
 +
| Verse.AI || Holds the Jobs and general AI. Broadly speaking, what pawns do.
 +
|-
 +
| Verse.AI.Group || Holds the squad AI. This is useful when you want 2 or more pawns to work together.
 +
|-
 +
| Verse.Grammar || Contains functionality for the Languages folder and other things having to do with text operations such as the art flavour text generator.
 +
|-
 +
| Verse.Noise || Something something you'll never need this.
 +
|-
 +
| Verse.Sound || Much like Verse.Noise.
 +
|-
 +
| Verse.Steam || Steam integration by Tynan - you won't need it.
 +
|-
 +
| UnityEngine || Contains many more methods you most likely won't actively use. RimWorld mostly uses it for the GUI, Rect and Color methods.
 +
|-
 +
|}<br/>
 +
 
 +
The difference between RimWorld and Verse is sometimes blurred. "RimWorld" contains things which are reasonably specific to RimWorld functionality. Classes in Verse are more oriented towards general game functionality. Since it can be difficult to guess which namespace holds a class, you might as well add both using statements whenever you want.<br/>
 +
If you want to use the exact functionality of a base game class you're best off copying all its ''using'' statements, its ''namespace'' and the namespace of its parent.<br/><br/>
 +
 
 +
=Writing code=
 +
 
 +
# [[Modding Tutorials/Decompiling source code|Decompile source code]] to take a look at the game's existing code;
 +
## If you get stuck on anything, any modding questions can be asked on the [https://ludeon.com/forums/index.php?board=14.0 subforum],
 +
# Compile your class into a .dll;
 +
## Most IDE's, integrated development environments, have a button that says "Compile" or "Build" - there is usually a hotkey (F5, F8, etc). Compiling is entirely driven by the software, you only have to supply a location for it to compile to - as explained in [[Modding Tutorials/Setting up a solution|Setting up a solution]].
 +
## Make sure your project's output type is "class library";
 +
## '' '''Note:''' by default, Visual Studio will compile all the references of the project as well, so you’ll get a copy of UnityEngine.dll and Assembly-CSharp.dll and some others. Just take ''MyModName.dll'' and place it in the ''MyModName/Assemblies'' folder. If you have [[Modding Tutorials/Setting up a solution|set up a solution]] according to the tutorial you don't have this problem,
 +
# Reference the classes in your .dll (optionally: from the xml data in the MyModName/Defs folder);
 +
#* {{Main|Modding Tutorials/Linking XML and C#}}
 +
## '''Example:''' Write a [[Modding_Tutorials/Hello_World|Hello World]] mod
 +
## '''Example:''' Create a new ThingDef with a <thingClass> that points to a class in your .dll (see, for example, [[Modding_Tutorials/Def classes|Def classes]] and [[Modding_Tutorials/Modifying defs|Modifying defs]])
 +
# The game should load your class now;<br/><br/>
 +
 
 +
=Examples=
 +
* Write a [[Modding_Tutorials/Hello_World|Hello World]] mod: the simplest C# project.
 +
* [[Modding Tutorials/Assembly Modding Example|Assembly modding example]] contains a small modding example.
 +
* [[Plague_Gun_(1.1)|Plague Gun]] How to Make a RimWorld Mod - step by step guide by Jecrell.
 +
 
 +
=See also=
 +
* [[Modding_Tutorials/Distribution|Distribution]] details how to release your mod.
 +
 
 +
 
 +
[[Category:Modding tutorials]]

Latest revision as of 18:27, 24 April 2023

Modding Tutorials

This tutorial gives you a broad idea how to work on a C# solution.

While it is possible to create many mods by modifying or creating XML, writing custom C# code, either as stand-alone classes or as Harmony patches, allows changing almost anything in the game.

Requirements[edit]

Creating a class[edit]

Once you've set up your solution correctly you'll usually be greeted with a class similar like the template below:

Template[edit]

Let's go over the template:

/*
 * Created by SharpDevelop.
 * User: You
 * Date: 01-01-1970
 * Time: 00:00
 * 
 * To change this template use Tools | Options | Coding | Edit Standard Headers.
 */
using System;

namespace MyNameSpace
{
	/// <summary>
	/// Description of MyClassName.
	/// </summary>
	public class MyClassName
	{
		public MyClassName()
		{
		}
	}
}


Using[edit]

The first thing you notice is using System; which indicates this class can call anything from the namespace System without calling it like System.Class.SomeDataType but rather SomeDataType. This is very useful because we don't have to worry about such calls anymore (for now).

The next section shows which namespaces you could put in using.

Namespace[edit]

Next up is the namespace MyNameSpace part - in principle everything inside of a namespace knows everything else inside of that namespace. If you were to make a folder inside of your project (MyProjectName not Solution MySolutionName), any classes inside of that folder would have the namespace MyNameSpace.MyFolderName. That's a different namespace and unless you tell other classes that it exists (using MyNameSpace.MyFolderName;) they won't accept calls to parts of that namespace without that namespace as a prefix.

Anything inside of MyNameSpace.MyFolderName does however know of everything inside MyNameSpace, just nothing about other subfolders of MyNameSpace.

By default your project knows nothing. Adding DLL references to the References folder (as done in the previous tutorial) allows you to reference them with using:

namespace MyNameSpace {}		/* Assuming MyNameSpace is your ROOT NAMESPACE (MyProjectName ->
					Properties -> Application -> Root namespace), this will compile. */

namespace MyNameSpace.MyFolderName {}	/* Assuming this class is inside of the folder MyFolderName inside of
					MyProjectName, this will compile. Other classes outside of the folder
					will have to reference this, this class will have to reference classes
					in different subfolders (but not the ones in the root folder). */

namespace RimWorld {}			/* Don't do this. It will compile if RimWorld is your root namespace
					but it's bad practice. */

namespace System {}			/* No, just no. */


You can set your project's namespace by going to visual studio and right-clicking MyProjectName -> Properties -> Application -> Root namespace(Visual Studio 2019 it is Default namespace). Please take some time to make it unique so no compatibility issues will arise with other mods, for your and everyone else's sanity.

Summary[edit]

This summary is part of the template but it's not required. It can help other people understand your code better when you provide the source but besides that you really don't need it.

Class and Constructor[edit]

This is your class definition. To access anything inside of a public class you will need a reference to an instance of that class:

MyClassNamefoo = new MyClassName();


The brackets at new MyClassName() indicate you're creating this instance without parameters which is possible because your class contains a constructor (anything inside of class MyClassName which has parameters and isn't a method, often abbreviated .ctor) which accepts a call without parameters:

public class MyClassName
{
	public MyClassName()	/* No parameters */
	{
	}

	public MyClassName(string foo, int bar)	/* Required parameters of datatypes string and int */
	{
	}

	public MyClassName(string foo = "none supplied", int bar = 42)	/* Default parameters " " " */
	{
	}
}


The last implementation of the constructor (MyNameSpace.MyClassName.MyClassName inside of MyNameSpace.MyClassName) is capable of accepting zero to two parameters while the second implementation of the constructor will only accept calls with exactly two parameters.

Namespaces[edit]

Some very useful namespaces in general are the following:

Namespace What does it do?
System.Collections.Generic Makes it so you can use part of the IEnumerable interface, which is used for datatypes like List<>.
System.Linq Allows for cool operations on the IEnumerable interface such as .Where() which accepts lambda operations (oh my!).
System.Text.RegularExpressions Introduces Regular Expressions (RegEx) to C# which allows for intricate operations on strings.
System.Collections Holds other parts of the IEnumerable interface you will require when inheriting from that class.
System.Text Contains the StringBuilder which accepts a list of strings and returns them in a specific way.


And the namespaces for RimWorld in particular are these:

Namespace What does it do?
RimWorld Many RimWorld specific classes can be found in this namespace. This and the Verse namespace will be most used.
Verse Many general use classes can be found in this namespace. This and the RimWorld namespace will be most used.
RimWorld.BaseGen Everything that creates the layout and contents of the Outposts, Bases and Settlements is in here.
RimWorld.Planet Everything (almost everything) having to do with the planet is in here.
Verse.AI Holds the Jobs and general AI. Broadly speaking, what pawns do.
Verse.AI.Group Holds the squad AI. This is useful when you want 2 or more pawns to work together.
Verse.Grammar Contains functionality for the Languages folder and other things having to do with text operations such as the art flavour text generator.
Verse.Noise Something something you'll never need this.
Verse.Sound Much like Verse.Noise.
Verse.Steam Steam integration by Tynan - you won't need it.
UnityEngine Contains many more methods you most likely won't actively use. RimWorld mostly uses it for the GUI, Rect and Color methods.


The difference between RimWorld and Verse is sometimes blurred. "RimWorld" contains things which are reasonably specific to RimWorld functionality. Classes in Verse are more oriented towards general game functionality. Since it can be difficult to guess which namespace holds a class, you might as well add both using statements whenever you want.
If you want to use the exact functionality of a base game class you're best off copying all its using statements, its namespace and the namespace of its parent.

Writing code[edit]

  1. Decompile source code to take a look at the game's existing code;
    1. If you get stuck on anything, any modding questions can be asked on the subforum,
  2. Compile your class into a .dll;
    1. Most IDE's, integrated development environments, have a button that says "Compile" or "Build" - there is usually a hotkey (F5, F8, etc). Compiling is entirely driven by the software, you only have to supply a location for it to compile to - as explained in Setting up a solution.
    2. Make sure your project's output type is "class library";
    3. Note: by default, Visual Studio will compile all the references of the project as well, so you’ll get a copy of UnityEngine.dll and Assembly-CSharp.dll and some others. Just take MyModName.dll and place it in the MyModName/Assemblies folder. If you have set up a solution according to the tutorial you don't have this problem,
  3. Reference the classes in your .dll (optionally: from the xml data in the MyModName/Defs folder);
    1. Example: Write a Hello World mod
    2. Example: Create a new ThingDef with a <thingClass> that points to a class in your .dll (see, for example, Def classes and Modifying defs)
  4. The game should load your class now;

Examples[edit]

See also[edit]