-
Notifications
You must be signed in to change notification settings - Fork 16
Networking
LCAPI makes networking easy. As long as you have a serializable type, you can create a network message handler to accept messages for that type.
There are 2 main ways to create a network handler, the first is to add the NetworkMessage attribute to a STATIC method, like so:
[NetworkMessage("LCAPI_EXAMPLE")]
public static void ExampleHandler(ulong sender, ExampleClass message)
{
}The first parameter of the attribute is the message's unique name. There is also an optional parameter to relay the message to self when broadcasting.
The handler MUST have a ulong, T signature, where T is the type you will be broadcasting. The ulong parameter is the sender's client id.
THE METHOD MUST BE STATIC!
The second method is to add the NetworkMessage attribute to a class that extends NetworkMessageHandler. Here's an example:
[NetworkMessage("LCAPI_EXAMPLE")]
public class ExampleNetworkMessageClass : NetworkMessageHandler<ExampleClass>
{
public override void Handler(ulong sender, ExampleClass message)
{
}
}The same principals apply to this as the previous method, but it ensures your handler has the right signature.
You can also create empty messages that will allow you to send a message with no body (again static method):
[NetworkMessage("LCAPI_EXAMPLE")]
public static void ExampleHandler(ulong sender)
{
}[NetworkMessage("LCAPI_EXAMPLE")]
public class ExampleNetworkMessageClass : NetworkMessageHandler
{
public override void Handler(ulong sender)
{
}
}LC API will not automatically recognize your want to register these messages, you will have to make a call to Network.RegisterAll() to register all your messages.
If you wish for more flow control, you can register messages that reside in a specific type like so: Network.RegisterAll(Type). This will allow you to control what is registered.
You can call these methods at any time, the best time is in your mod's Awake method. LC API will handle the timing to register them and unregister them as players join/leave games.
You should not unregister unless you are implementing hot reloading capability. Use Network.UnregisterAll() or Network.UnregisterAll(Type) to do so.
Now that you've got your handlers, you need to broadcast a message, this is simple:
Network.Broadcast("LCAPI_EXAMPLE", new ExampleClass() { value = 50, test = "Hi", vector3s = new List<Vector3S>() { new Vector3(42, 42, 42) } })or for empty messages:
Network.Broadcast("LCAPI_EXAMPLE")The type of data you're sending MUST match the handler's expected type, or must not be present if the handler does not expect a body.
You can send anything that can be serialized to JSON using NewtonSoft.Json. This allows you to make complex messages very easily.
Here's an example class:
internal class ExampleClass
{
public int Value { get; set; }
public string Test { get; set; }
public List<Vector3S> Vector3s { get; set; }
}These are all properties, this is because NewtonSoft.Json does not serialize fields.
Currently only reference types are supported at the top level, value types (those being structs and the other primitive types such as int, float, etc) may be supported at the top level in the future. You can still use those value types in a class, however, but the serializer cannot serialize them at the top level.
The unmanaged unity types Vector2, Vector3, Vector2Int, Vector3Int, Vector4, Quaternion, Color, Color32, Ray, Ray2D are not serializable by default, however LCAPI provides serializers for you: Vector2S, Vector3S, Vector2IntS, Vector3IntS, Vector4S, QuaternionS, ColorS, Color32S, RayS, Ray2DS
These have implicit conversions to and from the serialized to the unity type, so you can use them without issue, you just need to ensure your serializable class uses the serializer for the job. In the ExampleClass, you can see it uses a List<Vector3S> and NOT a List<Vector3>, this is because Vector3 is not serializable.
Due to the implicit operators, you can use them nearly interchangeably, however. With the above ExampleClass, you can iterate vector3s like so:
foreach (Vector3 vector in exampleClass.vector3s)
{
Plugin.Log.LogInfo(vector);
}Since Vector3S can be implicitly converted to Vector3, you can use Vector3 instead of Vector3S when accessing. The same applies to all other serializers.
As a clear example, this is valid: Vector3 vector = new Vector3S(32, 32, 32); due to the implicit conversion.
There is no security on network messages, anyone can send them, therefore it is recommended to check sender client ids if needed.