PUN Serialization

Hello,

I have been working on a dynamic day cycle system for my game and am now struggling to get it to work on other clients. The error is Exception: cannot serialize(): DynamicDayCycle+DynamicData.

I am under the impression that PUN can only serialize basic data types such as numbers, bools, Vectors, etc. Well I have a couple class ‘packages’ that only contains these basic data types. Take a look:

    /// <summary>
    /// Dynamic data to serialize consisting of sun and weather.
    /// </summary>
    [Serializable]
    public class DynamicData
    {
        /// <summary>
        /// The sun data.
        /// </summary>
        public SunData sunData = new SunData();
        /// <summary>
        /// The current weather.
        /// </summary>
        public Weather currentWeather;
        /// <summary>
        /// The current time.
        /// </summary>
        public float currentTime;

        /// <summary>
        /// Updates the DynamicData package to serialize.
        /// </summary>
        /// <param name="position">The current position of the sun.</param>
        /// <param name="rotation">The current rotation of the sun.</param>
        /// <param name="color">The current color of the sun.</param>
        /// <param name="intensity">The current intensity of the sun.</param>
        /// <param name="weather">The current weather.</param>
        /// <param name="time">The current time.</param>
        public void Set(Vector3 position, Quaternion rotation, Color color, float intensity, Weather weather, float time)
        {
            sunData.currentRotation = rotation;
            sunData.currentColor.Set(color);
            sunData.currentIntensity = intensity;
            currentWeather = weather;
            currentTime = time;
        }

        /// <summary>
        /// Sun data to serialize.
        /// </summary>
        [Serializable]
        public class SunData
        {
            /// <summary>
            /// The current rotation of the sun.
            /// </summary>
            public Quaternion currentRotation;
            /// <summary>
            /// The current color of the sun.
            /// </summary>
            public jColor currentColor = new jColor();
            /// <summary>
            /// The current intensity of the sun.
            /// </summary>
            public float currentIntensity;

            /// <summary>
            /// Serializable color class.
            /// </summary>
            [Serializable]
            public class jColor
            {
                /// <summary>
                /// Red.
                /// </summary>
                public float R;
                /// <summary>
                /// Green.
                /// </summary>
                public float G;
                /// <summary>
                /// Blue.
                /// </summary>
                public float B;
                /// <summary>
                /// Alpha.
                /// </summary>
                public float A;

                /// <summary>
                /// Sets this instance from a Unity Color class instance.
                /// </summary>
                /// <param name="color"></param>
                public void Set(Color color)
                {
                    R = color.r;
                    G = color.g;
                    B = color.b;
                    A = color.a;
                }

                /// <summary>
                /// Makes a Unity Color instance from this serialized package.
                /// </summary>
                /// <returns></returns>
                public Color ToColor()
                {
                    return new Color(R, G, B, A);
                }
            }
        }
    }

    /// <summary>
    /// Weather types.
    /// </summary>
    [Serializable]
    public enum Weather : int
    {
        /// <summary>
        /// Sunny.
        /// </summary>
        None = 0,
        /// <summary>
        /// Foggy.
        /// </summary>
        LightFog = 1,
        /// <summary>
        /// Very foggy.
        /// </summary>
        DenseFog = 2,
        /// <summary>
        /// Light rain.
        /// </summary>
        LightRain = 3,
        /// <summary>
        /// Stormy conditions.
        /// </summary>
        RainStorm = 4,
        /// <summary>
        /// Light snow.
        /// </summary>
        LightSnow = 5,
        /// <summary>
        /// Blizzard.
        /// </summary>
        SnowStorm = 6
    }

I believe that the only workaround would be to remove all the packages and have all of the data fields exposed in the base DynamicData class but that seems so rudimentary. Are there any better options before I do this?
Thanks

Serialize and deserialize your objects with BinaryFormatter and send the byte stream via remote procedrue call all to other clients.
That’s my code for custom object serialitazion:

public static class SerializedPlayerDataView
{
    public static byte[] SerializeGamePeerInfo(GamePeerInfo peer)
    {
        BinaryFormatter binaryF = new BinaryFormatter();
        using (MemoryStream memoryStream = new MemoryStream())
        {
            binaryF.Serialize(memoryStream,peer);

            return memoryStream.ToArray();
        }
    }

    public static GamePeerInfo DeserializeGamePeerInfo(byte[] dataStream)
    {
        using (MemoryStream memoryStream = new MemoryStream())
        {
            BinaryFormatter binaryF = new BinaryFormatter();

            memoryStream.Write(dataStream, 0, dataStream.Length);
            memoryStream.Seek(0, SeekOrigin.Begin);

            return (GamePeerInfo)binaryF.Deserialize(memoryStream);
        }
    }
}

And how to use:

```csharp
**public void sendSerializedData()
{
myClass cusObject = new myClass();
cusObject.Value = 10; // example

 byte[] serializedObjectStream = SerializedView.Serialize(cusObject);

view.RPC("GetStreamData",PhotonTargets.Others, (byte)serializedObjectStream);

}

[PunRPC]
private void GetStreamData(byte stream,photonMessageInfo senderInfo)
{
myClass obj = SerializedView.Deserialize(stream);

 myValue = obj.Value; // use it example

}**
```

2 Likes

Thanks for the code, but I would rather stay away from calling an remote procedure call every network tick. If I am unable to find a way to serialize my DynamicData class then I can always just expose all the fields and serialize it that way. I would not resort to an RPC.

What is the problem about rpc?

1 Like

There is no real ‘problem’ per say but RPC is more of an event based networking solution. The realtime stream from using OnPhotonSerializeView is intended to be invoked every frame so I would prefer to use the built-in Photon solution. Adding additional RPC’s every network tick cannot be good for performance and bandwidth.

You can sendNext the byte stream too by using OnPhotonSerializeView. OnPhotonSerializeView is not being called every frame, it’s called only when your value changes. You can develop a system that will send RPC’s only when it’s needed.

Unfortunately you are incorrect. OnPhotonSerializeView can be called every frame depending on the Observe Option you set in the PhotonView that observes the network component. Additionally, for a day cycle, an update does happen every frame. I would like to stay away from RPC’s every frame.

However, it may be possible to utilize your byte stream system on the OnPhotonSerializeView if Photon can serialize it.

Thanks

Did you try register custom types?

As I said you can still develop a system to lower the call rate to just few times per second

I was able to register a custom type using a similar byte array system that you have shown. On referencing the Photon API, you must also register a custom type before attempting to serialize it on the Photon stream. This is what I did to get it to work right using OnPhotonSerializeView. Thanks.

OnPhotonSerializeView gets called X times/sec. By default it’s 10 times but it can be set with the sendRateOnSerialize value.
A lower value is to be preferred, as it means less traffic and serialization.

You can sync a day/night cycle with the shared serverTimestamp, the same way “rounds” are synced. You just need to know when the day started (expressed as shared server time).
There are a few sample usages of that in the package.

On type serialization: You can register your own types but in many cases it’s way more effective, to add the corresponding int/string directly to the stream/RPC to be sent. A custom type makes sense when you have more complicated classes, which you need to use quite often. Keep in mind that a custom type adds 4 bytes to anything you actually want to send. For a bool, this would be quite some overhead.

I have done as you said but there is a problem about using RPC. I have used 2 scenes, the first scene serialize custom objects and send to another client in the second scene. There is an error message like “Received RPC … for viewID but this photonView does not exist! was remote PV…”

What is “GamePeerInfo” in your code?