API
Clone this wiki locally
Modding API
Examples
You can check other examples from the Multiplayer Compatibility Project.
A small example
In this section, we're going to focus on the basics of the api. Specifically, we're going to enable a mod for Multiplayer.
The boilerplate
By the time RimWorld reaches your mod the API is ready to use, you can call it wherever you see fit. See basic C# mod tutorials. The easiest place is [StaticConstructorOnStartup]
The following is the most basic boilerplate code.
using Multiplayer.API;
using Verse;
namespace MyExampleMod
{
[StaticConstructorOnStartup]
public static class MyExampleModCompat
{
static MyExampleModCompat()
{
if (!MP.enabled) return;
// This is where the magic happens and your attributes
// auto register, similar to Harmony's PatchAll.
MP.RegisterAll();
// You can choose to not auto register and do it manually
// with the MP.Register* methods.
// Use MP.IsInMultiplayer to act upon it in other places
// user can have it enabled and not be in session
}
}
}
Methods
Say you want a method call to trigger, for example a GUI click or a drag. You possibly already have it somewhere, so it's a matter of registering it as SyncMethod with the API. The simple way is to add the [SyncMethod] attribute to your method. There is alternatives in MP.Register* if you don't feel comfortable with adding code to your other files.
...
[HarmonyPatch(typeof(RimWorldWindowClass), nameof(DoWindowContents))]
static class RimWorldWindowClass_DoWindowContents_Patch
{
// Draw a 16x16 square with your texture and make it clickable in RimWorldWindowClass
static void Postfix(Rect inRect)
{
Rect buttonRect = new Rect(inRect.x, inRect.y, 16f, 16f);
if (Widgets.ButtonImage(buttonRect, ResourceBank.Textures.MyButtonTexture))
{
Click();
}
}
[SyncMethod]
static void Click() {
Log.Message("This will happen in all clients");
}
}
...
That's all! You are done.
Fields
Well... sometimes you aren't that lucky. Sometimes you want to change a specific Field. For those scenarios there is the MP.Watch* methods. Basically, at the beginning of the operation you take a snapshot of the Field value and then compare it later. MPAPI checks them if they need to broadcast it and then spreads the change accordingly.
...
[HarmonyPatch(typeof(RimWorldWindowClass), nameof(DoWindowContents))]
static class RimWorldWindowClass_DoWindowContents_Patch
{
// Draw a 160x16 slider in RimWorldWindowClass
static void Postfix(Rect inRect)
{
if (MP.IsInMultiplayer) {
MP.WatchBegin(); // Let's being watching
// This is here to set the variable if it changed on other clients
// It will update the variable and the logic will stay the same.
MP.Watch(instance, nameof(instance.weight));
}
Rect sliderRect = new Rect(inRect.x, inRect.y, 160f, 16f);
instance.weight = GUI.HorizontalSlider(sliderRect, instance.weight, -10f, 10f);
if (MP.IsInMultiplayer) {
MP.WatchEnd(); // We are done watching!
}
}
static MyObject instance = new MyObject();
class MyObject {
[SyncField]
public float weight;
}
}
...
That's all! So easy! Is it...?
Gizmos
Oh dear! Oh deity no no--- NO- Pray you aren't dealing with them with Reflection. See the Examples for dark magic. But if it's your own code, just move the Action delegate to a method anywhere and tag it with [SyncMethod].
SyncWorkers
Sometimes, when you use [SyncMethod], you will encounter...
Error writing type: MyType, ... Multiplayer.Client.SerializationException: No writer for type MyType
MPAPI needs to know how to reference your type to send it to the other clients. Basically if you have...
[SyncMethod]
static MyReturnType MyMethod(MyArgumentType arg1, MyArgumentType2 arg2, int arg3, float arg4) { ... }
You need to teach MPApi to handle your types, so they can send them over the wire to the other players. For our example, we will need to write a SyncWorker for MyReturnType, MyArgumentType and MyArgumentType2, most of RimWorld types are already handled so don't be afraid to use them.
Assuming MyReturnType is made of aString, anInt and aFloat, here is how you write a SyncWorker for it:
[SyncWorker(shouldConstruct = true)]
static void SyncMyReturnType(SyncWorker sync, ref MyReturnType type) {
sync.Bind(type.aString);
sync.Bind(type.anInt);
sync.Bind(type.aFloat);
}
Bind does the writing and reading for you, shouldConstruct makes it so type is constructed before being passed to the SyncWorker (not needed for structs). But if you need more complex situations, you can use Write and Read.
Assuming MyArgumentType requires an argument Pawn to construct, you'd write it this way:
[SyncWorker]
static void SyncMyArgumentType(SyncWorker sync, ref MyArgumentType type) {
if (sync.isWriting) {
sync.Write(type.Pawn);
} else {
Pawn pawn = sync.Read<Pawn>();
type = new MyArgumentType(pawn);
}
}
Structure
The following is more detailed documentation.
MP | ISyncCall |
SyncMethodAttribute | SyncFieldAttribute |
SyncWorkerAttribute | SyncWorkerDelegate |
ISyncField | ISynchronizable |
ISyncMethod | ISyncDelegate |
SyncWorker | SyncContext |
MP
The primary static class that contains methods used to interface with the multiplayer mod.
API
Contains the API version
enabled
Value
Returns if API is initialized.
IsHosting
Value
Returns if currently running on a host.
IsInMultiplayer
Value
Returns if currently running in a multiplayer session (both on client and host).
PlayerName
Value
Returns local player's name.
RegisterSyncDelegate(inType, nestedType, methodName, fields, args)
Registers the syncDelegate. Handles anonymous nested types, you will have to figure out the name of your target by decompiling.
Returns
The sync delegate.
Name | Description |
---|---|
inType |
System.Type In type. |
nestedType |
System.String Nested type. |
methodName |
System.String Method name. |
fields |
System.String[] Fields. |
args |
System.Type[] Arguments. |
RegisterSyncDelegate(type, nestedType, method)
Registers the syncDelegate. Handles anonymous nested types, you will have to figure out the name of your target by decompiling.
Returns
The sync delegate.
Name | Description |
---|---|
type |
System.Type Type. |
nestedType |
System.String Nested type. |
method |
System.String Method. |
RegisterSyncField(field)
Registers a field for syncing and returns it's ISyncField.
Remarks
It's recommended to use SyncFieldAttribute instead, unless you have to otherwise.
They must be Watched between MP.WatchBegin and MP.WatchEnd with the MP.Watch* methods
Name | Description |
---|---|
field |
System.Reflection.FieldInfo FieldInfo of a field to register |
Returns
A new registered ISyncField
RegisterSyncField(targetType, memberPath)
Registers a field for syncing and returns it's ISyncField.
Remarks
It's recommended to use SyncFieldAttribute instead, unless you have to otherwise.
They must be Watched between MP.WatchBegin and MP.WatchEnd with the MP.Watch* methods
Name | Description |
---|---|
targetType |
System.Type |
Type of the target class that contains the specified member |
if null, memberPath will point at field from the global namespace in the "Type/fieldName" format.
|
| memberPath | System.String
Path to a member. If the member is to be indexed, it has to end with /[] eg. "myArray/[]"
|
Returns
A new registered ISyncField
RegisterSyncMethod(method, argTypes)
Registers a method for syncing and returns its ISyncMethod.
Name | Description |
---|---|
method |
System.Reflection.MethodInfo MethodInfo of a method to register |
argTypes |
Multiplayer.API.SyncType[] Method's parameter types |
Remarks
It's recommended to use SyncMethodAttribute instead, unless you have to otherwise.
Returns
A new registered ISyncMethod
Example
Register a method for syncing using reflection and set it to debug only.
RegisterSyncMethod(typeof(MyType).GetMethod(nameof(MyType.MyMethod))).SetDebugOnly();
RegisterSyncMethod(type, methodOrPropertyName, argTypes)
Registers a method for syncing and returns its ISyncMethod.
Remarks
It's recommended to use SyncMethodAttribute instead, unless you have to otherwise.
Name | Description |
---|---|
type |
System.Type Type that contains the method |
methodOrPropertyName |
System.String Name of the method |
argTypes |
Multiplayer.API.SyncType[] Method's parameter types |
Returns
A new registered ISyncMethod
RegisterSyncWorker<T>(syncWorkerDelegate, targetType, isImplicit, shouldConstruct)
Registers the SyncWorker based on SyncWorkerDelegate.
Remarks
It's recommended to use SyncWorkerAttribute instead, unless you have to otherwise.
Name | Description |
---|---|
syncWorkerDelegate |
Multiplayer.API.SyncWorkerDelegate{``0} Sync worker delegate. |
targetType |
System.Type Type to handle. |
isImplicit |
System.Boolean If set to true the SyncWorker will handle the type and all the derivate Types. |
shouldConstruct |
System.Boolean If set to true the SyncWorker will be provided with an instance created with no arguments. |
Type Parameters
- T - Type to handle.
Watch(target, fieldName, index)
Helper method for ISyncField.Watch(System.Object,System.Object) given an instance.
Name | Description |
---|---|
target |
System.Object An object of type set in the ISyncField to watch |
fieldName |
System.String ISyncField name of the field to watch for changes |
index |
System.Object Index in the field path set in ISyncField |
Watch(memberPath, target, index)
Helper method for ISyncField.Watch(System.Object,System.Object) given an instance.
Name | Description |
---|---|
memberPath |
System.String ISyncField the memberPath of the ISyncField |
target |
System.Object An object of type set in the ISyncField to watch, null for static |
index |
System.Object Index in the field path set in ISyncField |
Watch(type, fieldName, index)
Helper method for ISyncField.Watch(System.Object,System.Object) given a type.
Name | Description |
---|---|
type |
System.Type An object of type set in the ISyncField to watch, for static types |
fieldName |
System.String ISyncField name of the field to watch for changes |
index |
System.Object Index in the field path set in ISyncField |
WatchBegin
Starts a new synchronization stack.
Remarks
Has to be called before invoking Watch methods.
See also ISyncField.Watch(System.Object,System.Object).
WatchEnd
Ends the current synchronization stack and executes it.
Remarks
Has to be called after invoking Watch methods.
See also ISyncField.Watch(System.Object,System.Object).
SyncContext
Context flags which are sent along with a command
CurrentMap
Send current map context
MapMouseCell
Send mouse cell context (emulates mouse position)
MapSelected
Send map selected context (object selected on the map)
None
Default value. (no context)
QueueOrder_Down
Send order queue context (emulates pressing KeyBindingDefOf.QueueOrder)
WorldSelected
Send world selected context (object selected on the world map)
SyncFieldAttribute
An attribute that is used to mark fields for syncing. It will be Watched for changes by the MPApi when instructed.
Example
An example showing how to mark a field for syncing.
public class MyClass
{
[SyncField]
bool myField;
...
}
Constructor(context)
Name | Description |
---|---|
context |
Multiplayer.API.SyncContext Context |
bufferChanges
Instructs SyncField to use a buffer instead of syncing instantly (when MP.WatchEnd is called).
cancelIfValueNull
Instructs SyncField to cancel synchronization if the value of the member it's pointing at is null.
debugOnly
Instructs SyncField to synchronize only in debug mode.
hostOnly
Instructs SyncField to synchronize only if it's invoked by the host.
inGameLoop
Instructs SyncField to sync in game loop.
version
SyncMethodAttribute
An attribute that is used to mark methods for syncing. The call will be replicated by the MPApi on all clients automatically.
Example
An example showing how to mark a method for syncing.
[SyncMethod]
public void MyMethod(...)
{
...
}
Constructor(context)
Name | Description |
---|---|
context |
Multiplayer.API.SyncContext Context |
cancelIfAnyArgNull
Instructs SyncMethod to cancel synchronization if any arg is null (see ISyncMethod.CancelIfAnyArgNull).
cancelIfNoSelectedMapObjects
Instructs SyncMethod to cancel synchronization if no map objects were selected during the call (see ISyncMethod.CancelIfNoSelectedMapObjects).
cancelIfNoSelectedWorldObjects
Instructs SyncMethod to cancel synchronization if no world objects were selected during call replication(see ISyncMethod.CancelIfNoSelectedWorldObjects).
debugOnly
Instructs SyncMethod to synchronize only in debug mode (see ISyncMethod.SetDebugOnly).
exposeParameters
A list of types to expose (see ISyncMethod.ExposeParameter(System.Int32))
SyncWorker
An abstract class that can be both a reader and a writer depending on implementation.
Remarks
See ISynchronizable and SyncWorkerAttribute for usage examples.
Bind(obj)
Reads or writes an object inheriting ISynchronizable interface.
Remarks
Does not create a new object.
Name | Description |
---|---|
obj |
Multiplayer.API.ISynchronizable@ object to bind |
Bind(obj)
Reads or writes an object referenced by obj.
Name | Description |
---|---|
obj |
System.Boolean@ object to bind |
Bind(obj)
Reads or writes an object referenced by obj.
Name | Description |
---|---|
obj |
System.Byte@ object to bind |
Bind(obj)
Reads or writes an object referenced by obj.
Name | Description |
---|---|
obj |
System.Double@ object to bind |
Bind(obj)
Reads or writes an object referenced by obj.
Name | Description |
---|---|
obj |
System.Int16@ object to bind |
Bind(obj)
Reads or writes an object referenced by obj.
Name | Description |
---|---|
obj |
System.Int32@ object to bind |
Bind(obj)
Reads or writes an object referenced by obj.
Name | Description |
---|---|
obj |
System.Int64@ object to bind |
Bind(obj, name)
Uses reflection to bind a field or property
Name | Description |
---|---|
obj |
System.Object |
object where the field or property can be found |
if null, name will point at field from the global namespace
|
| name | System.String
path to the field or property |
Bind(obj)
Reads or writes an object referenced by obj.
Name | Description |
---|---|
obj |
System.SByte@ object to bind |
Bind(obj)
Reads or writes an object referenced by obj.
Name | Description |
---|---|
obj |
System.Single@ object to bind |
Bind(obj)
Reads or writes an object referenced by obj.
Name | Description |
---|---|
obj |
System.String@ object to bind |
Bind(obj)
Reads or writes an object referenced by obj.
Name | Description |
---|---|
obj |
System.UInt16@ object to bind |
Bind(obj)
Reads or writes an object referenced by obj.
Name | Description |
---|---|
obj |
System.UInt32@ object to bind |
Bind(obj)
Reads or writes an object referenced by obj.
Name | Description |
---|---|
obj |
System.UInt64@ object to bind |
Bind<T>(obj)
Reads or writes an object referenced by obj
Remarks
Can read/write types using user defined syncers, ISynchronizables and readers/writers implemented by the multiplayer mod.
Type Parameters
- T - type of the object to bind
Name | Description |
---|---|
obj |
``0@ object to bind |
BindType<T>(type)
Reads or writes a System.Type referenced by type.
Type Parameters
- T - Base type that type derives from.
Name | Description |
---|---|
type |
System.Type@ type to bind |
isWriting
if is currently writing.
Read<T>
Read the specified Type from the memory stream, only active during reading.
Returns
The requested Type object. Null if writing.
Type Parameters
- T - The Type to read.
Write<T>(obj)
Write the specified obj, only active during writing.
Name | Description |
---|---|
obj |
``0 Object to write. |
Type Parameters
- T - Type to write.
SyncWorkerAttribute
An attribute that marks a method as a SyncWorker for a type specified in its second parameter.
Remarks
Method with this attribute has to be static.
Example
An implementation that manually constructs an object.
[SyncWorkerAttribute]
public static void MySyncWorker(SyncWorker sync, ref MyClass inst)
{
if(!sync.isWriting)
inst = new MyClass("hello");
sync.bind(ref inst.myField);
}
An implementation that instead of creating a new object, references its existing one which resides in MyThingComp that inherits ThingComp class.
Subclasses of ThingComp are sent as a reference by the multiplayer mod itself.
[SyncWorkerAttribute]
public static void MySyncWorker(SyncWorker sync, ref MyClass inst)
{
if(!sync.isWriting)
MyThingComp parent = null;
sync.Bind(ref parent); // Receive its parent
inst = new MyClass(parent);
else
sync.Bind(ref inst.parent); // Send its parent
sync.bind(ref inst.myField);
}
isImplicit
Decides if the type specified in the second parameter should also be used as a syncer for all of its subclasses.
shouldConstruct
Decides if the method should get an already constructed object in case of reading data.
SyncWorkerDelegate`1
SyncWorker signature for adding new Types.
Name | Description |
---|---|
obj |
``0 Target Type |
Remarks
SyncWorkerAttribute for usage examples.
UninitializedAPI
An exception that is thrown if you try to use the API without avaiable host.
ISyncCall
ISyncCall interface.
Remarks
Used internally
DoSync(target, args)
Manually calls the synced method.
Name | Description |
---|---|
target |
System.Object Object currently bound to that method. Null if the method is static. |
args |
System.Object[] Parameters to call the method with. |
Returns
if the original call should be canceled.
ISyncDelegate
Sync delegate.
Remarks
See MP.RegisterSyncDelegate(System.Type,System.String,System.String) and MP.RegisterSyncDelegate(System.Type,System.String,System.String,System.String[],System.Type[]) to see how to use it.
CancelIfNoSelectedObjects
Cancels if no selected objects.
Returns
self
RemoveNullsFromLists(listFields)
Removes the nulls from lists.
Returns
self
Name | Description |
---|---|
listFields |
System.String[] List fields. |
SetContext(context)
Sets the context.
Returns
self
Name | Description |
---|---|
context |
Multiplayer.API.SyncContext Context. |
SetDebugOnly
Sets the debug only.
Returns
self
ISyncField
SyncField interface.
Example
Creates and registers a SyncField that points to myField
in object of type MyType
and enables its change buffer.
MPApi.SyncField(typeof(MyType), "myField").SetBufferChanges();
Creates and registers a SyncField that points to myField
which resides in MyStaticClass
.
MPApi.SyncField(null, "MyAssemblyNamespace.MyStaticClass.myField");
Creates and registers a SyncField that points to myField
that resides in an object stored by myEnumberable defined in an object of type MyType
.
To watch this one you have to supply an index in ISyncField.Watch(System.Object,System.Object).
MPApi.SyncField(typeof(MyType), "myEnumerable/[]/myField");
CancelIfValueNull
Instructs SyncField to cancel synchronization if the value of the member it's pointing at is null.
Returns
self
DoSync(target, value, index)
Manually syncs a field.
Name | Description |
---|---|
target |
System.Object An object of type set in the ISyncField. Set to null if you're watching a static field. |
value |
System.Object Value to apply to the synced field. |
index |
System.Object Index in the field path set in ISyncField |
Returns
if the change should be canceled.
InGameLoop
Instructs SyncField to sync in game loop.
Returns
self
PostApply(action)
Adds an Action that runs after a field is synchronized.
Name | Description |
---|---|
action |
System.Action{System.Object,System.Object} An action ran after a field is synchronized. Called with target and value. |
Returns
self
PreApply(action)
Adds an Action that runs before a field is synchronized.
Name | Description |
---|---|
action |
System.Action{System.Object,System.Object} An action ran before a field is synchronized. Called with target and value. |
Returns
self
SetBufferChanges
Instructs SyncField to use a buffer instead of syncing instantly (when MP.WatchEnd is called).
Returns
self
SetDebugOnly
Instructs SyncField to synchronize only in debug mode.
Returns
self
SetHostOnly
Instructs SyncField to synchronize only if it's invoked by the host.
Returns
self
SetVersion(System.Int32)
Returns
self
Watch(target, index)
Name | Description |
---|---|
target |
System.Object An object of type set in the ISyncField. Set to null if you're watching a static field. |
index |
System.Object Index in the field path set in ISyncField. |
Returns
self
ISynchronizable
An interface that allows syncing objects that inherit it.
Sync(sync)
An entry point that is used when object is to be read/written.
Remarks
Requires a default constructor that takes no parameters.
Check SyncWorkerAttribute to see how to make a syncer that allows for a manual object construction.
Name | Description |
---|---|
sync |
Multiplayer.API.SyncWorker A SyncWorker that will read/write data bound with Bind methods. |
Example
A simple implementation that binds object's fields x, y, z for reading/writing.
public void Sync(SyncWorker sync)
{
sync.Bind(ref this.x);
sync.Bind(ref this.y);
sync.Bind(ref this.z);
}
An implementation that sends field a, but saves it back into field b when it's received.
public void Sync(SyncWorker sync)
{
if(sync.isWriting)
sync.Bind(ref this.a);
else
sync.Bind(ref this.b);
}
ISyncMethod
SyncMethod interface.
Remarks
See SyncMethodAttribute, MP.RegisterSyncMethod(System.Reflection.MethodInfo,Multiplayer.API.SyncType[]) and MP.RegisterSyncMethod(System.Type,System.String,Multiplayer.API.SyncType[]) to see how to use it.
CancelIfAnyArgNull
Instructs SyncMethod to cancel synchronization if any arg is null.
Returns
self
CancelIfNoSelectedMapObjects
Instructs SyncMethod to cancel synchronization if no map objects were selected during call replication.
Returns
self
CancelIfNoSelectedWorldObjects
Instructs SyncMethod to cancel synchronization if no world objects were selected during call replication.
Returns
self
ExposeParameter(index)
Use parameter's type's IExposable interface to transfer its data to other clients.
Remarks
IExposable is the interface used for saving data to the save which means it utilizes IExposable.ExposeData() method.
Name | Description |
---|---|
index |
System.Int32 Index at which parameter is to be marked to expose |
Returns
self
MinTime(time)
Currently unused in the Multiplayer mod.
Name | Description |
---|---|
time |
System.Int32 Milliseconds between resends |
Returns
self
SetContext(context)
Instructs method to send context along with the call.
Remarks
Context is restored after method is called.
Name | Description |
---|---|
context |
Multiplayer.API.SyncContext One or more context flags |
Returns
self
SetDebugOnly
Instructs SyncMethod to synchronize only in debug mode.
Returns
self
SetPostInvoke(action)
Adds an Action that runs after a call is replicated on client.
Name | Description |
---|---|
action |
System.Action{System.Object,System.Object[]} An action ran after a call is replicated on client. Called with target and value. |
Returns
self
SetPreInvoke(action)
Adds an Action that runs before a call is replicated on client.
Name | Description |
---|---|
action |
System.Action{System.Object,System.Object[]} An action ran before a call is replicated on client. Called with target and value. |
Returns
self
SetVersion(version)
Name | Description |
---|---|
version |
System.Int32 Handler version |
Returns
self