Sending array over the network and OnSerializeNetworkView

I’ve seen a few variants on the subject, all basically telling that you can’t send an array over the network.

Letting aside how ridiculously stupid this limitation is, I am trying to find the best solution for my need, which consists of sending one variable and two arrays from the server to the clients.

I’ve read about ‘serializing’ the arrays and creating a custom string variable putting all original values together, and splitting them back on reception, but this seems stupidly ineffective and cpu intensive (my array of vectors can have as much as 600 values).

So I’ve tried considering OnSerializeNetworkView, but there’s absolutely nowhere to look for a better explanation on how/ when to use it over a “simple” rpc. In particular: can you use OnSerializeNetworkView as a ‘trigger’? Actually only send the data you serialize upon a call from another function? And what about arrays? Can you send arrays with OnSerializeNetworkView?

Can anybody tell me if OnSerializeNetworkView is a better approach to solve my problem?
To reiterate, I have the need to somewhat constantly send

1x integer
1x string array [average 300 elements]
1x vector3 array [average 300 elements]

from the server to the clients. Since I can’t simply send an RPC call with the mere values (because RPC doesn’t accept arrays of any type as parameters), I need to find an efficient solution to be able to deliver such volume of data.
What bothers me further is the fact that, in the selection of the ‘manual serialization’ idea, I would need to rewrite my serialization wrapper if my types would change for any reason!

Hope that somebody with unity’s networking experience can shed some light into the issue.

thanks

Yes it is a better approach in the sense of network bandwidth :wink:

The most important thing is that you deserialize the exact same amount of data on the receiver as you serialized on the sending side.

The next thing you have to keep in mind is that the serializing process is triggered in more or less constant intervals, see Send rate.

You can send one time packets by specifing some kind of “header type value”.

Here’s an example of my network implementation. It’s an authoritative server setup so the server sends “snapshots” of all players to everyone.

void OnSerializeNetworkView(BitStream stream, NetworkMessageInfo info)
{
    NetworkUser U;
    int         ActiveMask = 0;
    int         PlayerID = 0;
    Vector3     PlayerPosition = Vector3.zero;
    float       xRotation = 0;
    float       yRotation = 0;
    Vector3     PlayerSpeed = Vector3.zero;
    int         PlayerInput = 0;
    int         PlayerState = 0;
    int         AnimationIndex = 0;

    if (stream.isWriting && Network.isServer)
    {
        for (int i = 0; i < NetworkUser.MAX_NETWORK_USERS - 1; i++)
        {
            ActiveMask |= (NetworkUser.Get(i).playerObject != null) ? (1<<i) : 0;
        }
    }

    stream.Serialize(ref ActiveMask);
    for (int i = 0;i<NetworkUser.MAX_NETWORK_USERS-1; i++)
    {
        if ((ActiveMask & (1<<i))==0)
            continue;

        if (stream.isWriting && Network.isServer)
        {
            U = NetworkUser.Get(i);
            PlayerID = i;

            PlayerObject PO = U.playerObject;
            PlayerPosition = PO.transform.position;
            xRotation = U.sUser.xRotation;
            yRotation = U.sUser.yRotation;
            PlayerSpeed = U.sUser.moveSpeed;
            PlayerInput = (int)U.sUser.input;
            PlayerState = (int)U.sUser.CurState;
    
            int AnimationTime  = (int)((Time.time-U.sUser.AnimationTime)*10000);
            AnimationIndex = ((AnimationTime&0xFFFF)<<16) | (U.sUser.AnimationIndex & 0xFFFF);
            
        }
        stream.Serialize(ref PlayerID);
        stream.Serialize(ref PlayerPosition);
        stream.Serialize(ref xRotation);
        stream.Serialize(ref yRotation);
        stream.Serialize(ref PlayerSpeed);
        stream.Serialize(ref PlayerInput); 
        stream.Serialize(ref PlayerState); 
        stream.Serialize(ref AnimationIndex);

        if (stream.isReading && Network.isClient)
        {
            
            if (PlayerID < 0 || PlayerID > NetworkUser.MAX_NETWORK_USERS)
            {
                CGDebug.LogWarning("OnSerializeNetworkView: received wrong playerID: "+PlayerID);
                break;
            }
            U = NetworkUser.Get(PlayerID);
            if (U.state == ENetworkUserState.Inactive)
            {
                CGDebug.LogWarning("OnSerializeNetworkView: received data for inactive player: "+PlayerID);
                continue;
            }
            if (U.playerObject == null )
            {
                CGDebug.LogWarning("OnSerializeNetworkView: received data, but playerobject doesn't exist : "+PlayerID);
                continue;
            }
            U.playerObject.AddSnapShot(info.timestamp,PlayerPosition,xRotation,yRotation,PlayerSpeed,PlayerInput,PlayerState,AnimationIndex);
        }
    }
}

In the whole game i use only one single NetworkView. All RPCs and serialization is done with it :wink: