Handling serialization of Transform (Unity / Mirror networking)
I am learning how to make a multiplayer game in Unity and I'm leveraging Mirror to handle networking. It is a point and click game that uses pathfinding (A*). The player selects a tile game object to move towards. My 'tiles' in this instance are just game objects in the scene with a script attached with basic properties for the pathfinding.
public class OverlayTile : MonoBehaviour
{
[SerializeField] public int G;
[SerializeField] public int H;
public int F { get { return G + H; } }
[SerializeField] public bool isBlocked;
[SerializeField] public OverlayTile previous;
[SerializeField] public Vector3Int gridLocation;
}
I have a function that makes a call from my player to the server to move towards a tile. Because I have a custom type for a list in my parameter (custom type OverlayTile
) I have to handle serialization on my own. Custom types are not supported out of the box by Mirror. Currently I get this error without handling the serialization: NullReferenceException at (wrapper managed-to-native) UnityEngine.Component.get_transform(UnityEngine.Component)
. In the Mirror docs it says: Types that Inherit from UnityEngine.Component are not supported
. So this to me seems the cause of my error.
So I have been trying to figure out how to support serialization for transform on my own. I have looked at examples on the official Mirror docs, they have an example for collisions on rigid bodies. I have tried to translate this approach to Transform, but I keep getting that above error. I am continually trying to figure this out on my own through trial and error but my brain is really starting to hurt. Any advice/input would be deeply appreciated. I have no clue if I am even going in the right direction here, if I even need a rigidbody in my struct.. The only source of reference that I can find at all that I've been modeling my approach is in the Mirror docs (https://mirror-networking.gitbook.io/docs/guides/serialization)
This is the function making a call to the server with the custom list type parameter:
[Command]
public void CmdMoveAlongPath(List<OverlayTile> path)
{
var step = moveSpeed * Time.deltaTime;
var zIndex = path[0].transform.position.z;
character.transform.position = Vector2.MoveTowards(character.transform.position, path[0].transform.position, step);
if (Vector2.Distance(character.transform.position, path[0].transform.position) < 0.0001f)
{
PositionCharacterOnTile(path[0]);
path.RemoveAt(0);
}
}
This is my attempt at adding serialization support for my components transform .. I have a read/write function for properties in my OverlayTile component and another read/write function specifically just for Vector3 (Again, not even sure if I'm approaching this correctly):
public static class TileReaderWriter
{
public static void writeTile(this NetworkWriter writer, OverlayTile overlayTile)
{
writer.WriteInt(overlayTile.G);
writer.WriteInt(overlayTile.H);
writer.WriteBool(overlayTile.isBlocked);
writer.WriteVector3Int(overlayTile.gridLocation);
}
public static OverlayTile readTile(this NetworkReader reader)
{
return new Tile
{
G = reader.ReadInt(),
H = reader.ReadInt(),
isBlocked = reader.ReadBool(),
gridLocation = reader.ReadVector3Int(),
};
}
public struct MyTransform
{
public Vector3 transform;
public Rigidbody rigidbody;
}
public static void WriteMyTransform(this NetworkWriter writer, MyTransform value)
{
writer.WriteVector3(value.transform);
NetworkIdentity networkIdentity = value.rigidbody.GetComponent<NetworkIdentity>();
writer.WriteNetworkIdentity(networkIdentity);
}
public static MyTransform ReadMyCollision(this NetworkReader reader)
{
Vector3 transform = reader.ReadVector3();
return new MyTransform
{
transform = transform,
};
}
For even more context here is the full error I am getting back from my server:
System.Collections.Generic.List`1[OverlayTile]
Disconnecting connId=1 to prevent exploits from an Exception in MessageHandler: NullReferenceException
at (wrapper managed-to-native) UnityEngine.Component.get_transform(UnityEngine.Component)
at PlayerMovement.UserCode_CmdMoveAlongPath__List`1 (System.Collections.Generic.List`1[T] path) [0x0001a] in <e1eca6a72681489bab525a1eafd2d63f>:0
at PlayerMovement.InvokeUserCode_CmdMoveAlongPath__List`1 (Mirror.NetworkBehaviour obj, Mirror.NetworkReader reader, Mirror.NetworkConnectionToClient senderConnection) [0x00022] in <e1eca6a72681489bab525a1eafd2d63f>:0
at Mirror.RemoteCalls.RemoteProcedureCalls.Invoke (System.Int32 functionHash, Mirror.RemoteCalls.RemoteCallType remoteCallType, Mirror.NetworkReader reader, Mirror.NetworkBehaviour component, Mirror.NetworkConnectionToClient senderConnection) [0x00019] in <2d57b1f41c0b4d5a90f7ef7dbcf2e469>:0
at Mirror.NetworkIdentity.HandleRemoteCall (System.Byte componentIndex, System.Int32 functionHash, Mirror.RemoteCalls.RemoteCallType remoteCallType, Mirror.NetworkReader reader, Mirror.NetworkConnectionToClient senderConnection) [0x00065] in <2d57b1f41c0b4d5a90f7ef7dbcf2e469>:0
at Mirror.NetworkServer.OnCommandMessage (Mirror.NetworkConnectionToClient conn, Mirror.CommandMessage msg, System.Int32 channelId) [0x00085] in <2d57b1f41c0b4d5a90f7ef7dbcf2e469>:0
at (wrapper delegate-invoke) System.Action`3[Mirror.NetworkConnectionToClient,Mirror.CommandMessage,System.Int32].invoke_void_T1_T2_T3(Mirror.NetworkConnectionToClient,Mirror.CommandMessage,int)
at Mirror.MessagePacking+<>c__DisplayClass6_0`2[T,C].<WrapHandler>b__0 (Mirror.NetworkConnection conn, Mirror.NetworkReader reader, System.Int32 channelId) [0x0007a] in <2d57b1f41c0b4d5a90f7ef7dbcf2e469>:0