byte[] Serialize()
    {
        serializationBytes.Clear();

        VoosNetworkTypes.ShortToBytes stb = new VoosNetworkTypes.ShortToBytes();
        VoosNetworkTypes.UintToBytes  utb = new VoosNetworkTypes.UintToBytes();
        int   totalCount       = 0;
        float totalProbability = 0;

        foreach (KeyValuePair <short, NetworkedActor> na in actors)
        {
            VoosActor va = na.Value.actor;
            if (va != null && va.reliablePhotonView.isMine && !va.IsParented())
            {
                Vector3    p = va.transform.position;
                Quaternion q = va.transform.rotation;
                if (na.Value.lastPosition != p || na.Value.lastRotation != q || na.Value.lastColor != va.GetTint())
                {
                    na.Value.lastPosition             = p;
                    na.Value.lastRotation             = q;
                    na.Value.lastColor                = va.GetTint();
                    na.Value.lastTransformUpdateFrame = Time.frameCount;
                    na.Value.messagesSinceLastUpdate  = 0;
                }
                na.Value.updateProbability = Mathf.Clamp(1.0f / (float)(1 + na.Value.messagesSinceLastUpdate), minimumProbabilityOfUpdate, 1);
                totalProbability          += na.Value.updateProbability;
            }
            totalCount++;
        }
        float probabilityOfPositionUpdate = 1;

        if (totalCount > MaxPositionUpdatesPerTick)
        {
            probabilityOfPositionUpdate = (float)MaxPositionUpdatesPerTick / totalProbability;
        }

        /// Message Header
        /// Transform count - will modify after the fact.
        serializationBytes.Add(0);
        serializationBytes.Add(0);

        /// Timestamp for prediction.
        utb.data = VoosNetworkTypes.CompressFloatToUint24(Time.time);
        serializationBytes.Add(utb.byte0);
        serializationBytes.Add(utb.byte1);
        serializationBytes.Add(utb.byte2);
        int index = 0;

        foreach (KeyValuePair <short, NetworkedActor> na in actors)
        {
            VoosActor va = na.Value.actor;
            if (va != null && va.reliablePhotonView.isMine && !va.IsParented())
            {
                if (Random.value > (probabilityOfPositionUpdate * na.Value.updateProbability))
                {
                    continue;
                }
                na.Value.messagesSinceLastUpdate++;
                /// View ID
                stb.data = (short)va.reliablePhotonView.viewID;
                serializationBytes.Add(stb.byte0);
                serializationBytes.Add(stb.byte1);

                /// Network data bitmask
                UnreliableBitmask mask = UnreliableBitmask.None;
                mask |= va.GetTeleport() ? UnreliableBitmask.Teleport : UnreliableBitmask.None;
                mask |= va.GetEnablePhysics() ? UnreliableBitmask.EnablePhysics : UnreliableBitmask.None;
                serializationBytes.Add((byte)mask);

                // Unset the "teleport" flag, as it's transient and should only be used for one network packet.
                va.SetTeleport(false);

                // SUBTLE: If physics is not enabled, then pos/rot is not likely to
                // change. However, any inaccuracies are also more likely to be noticed
                // (such as static walls being slightly tilted). So given this, always
                // send full precision updates for static objects. Sure, this is less
                // efficient for things like moving platforms which do not have physics
                // but often change, but that's likely to be a minority.

                /// Local Position
                utb.WriteUint24(serializationBytes, VoosNetworkTypes.CompressFloatToUint24(na.Value.lastPosition.x));
                utb.WriteUint24(serializationBytes, VoosNetworkTypes.CompressFloatToUint24(na.Value.lastPosition.y));
                utb.WriteUint24(serializationBytes, VoosNetworkTypes.CompressFloatToUint24(na.Value.lastPosition.z));

                // Static - send full precision rotation on change. TODO I'm sure
                // there's some way to compress it to 8 bytes without loss.
                var        ftb = new VoosNetworkTypes.FloatToBytes();
                Quaternion q   = na.Value.lastRotation;
                ftb.SerializeTo(serializationBytes, q.x);
                ftb.SerializeTo(serializationBytes, q.y);
                ftb.SerializeTo(serializationBytes, q.z);
                ftb.SerializeTo(serializationBytes, q.w);

                //Color
                serializationBytes.Add(na.Value.lastColor.r);
                serializationBytes.Add(na.Value.lastColor.g);
                serializationBytes.Add(na.Value.lastColor.b);
                serializationBytes.Add(na.Value.lastColor.a);

                /// Linear Velocity
                Rigidbody rb = va.GetComponent <Rigidbody>();
                Vector3   lv = Vector3.zero;
                if (rb != null && !rb.isKinematic)
                {
                    lv = rb.velocity;
                }
                utb.data = VoosNetworkTypes.CompressFloatToUint24(lv.x);
                serializationBytes.Add(utb.byte0);
                serializationBytes.Add(utb.byte1);
                serializationBytes.Add(utb.byte2);
                utb.data = VoosNetworkTypes.CompressFloatToUint24(lv.y);
                serializationBytes.Add(utb.byte0);
                serializationBytes.Add(utb.byte1);
                serializationBytes.Add(utb.byte2);
                utb.data = VoosNetworkTypes.CompressFloatToUint24(lv.z);
                serializationBytes.Add(utb.byte0);
                serializationBytes.Add(utb.byte1);
                serializationBytes.Add(utb.byte2);
                index++;
            }
        }
        stb.data = (short)index;
        serializationBytes[0] = stb.byte0;
        serializationBytes[1] = stb.byte1;

        if (index == 0)
        {
            return(null);
        }
        return(serializationBytes.ToArray());
    }