Converted NetworkInterpolatedTransform to Photon, error

Options
legend411
legend411 ✭✭
So I converted the unity script "NetworkInterpolatedTransform" to the best of my ability to PUN, but it's throwing an error, presumably based on the use of stream.Serialize(), based on the line number of the ArgumentOutOfRangeException in PhotonClasses. Here's the code:
using UnityEngine;
using System.Collections;

public class NetworkInterpolatedTransform : Photon.MonoBehaviour {
	
	public double interpolationBackTime = 0.1; 
	
	internal struct  State
	{
		internal double timestamp;
		internal Vector3 pos;
		internal Quaternion rot;
	}

	// We store twenty states with "playback" information
	State[] m_BufferedState = new State[20];
	// Keep track of what slots are used
	int m_TimestampCount;
	
	void OnPhotonSerializeView(PhotonStream stream, PhotonMessageInfo info)
	{
		// Always send transform (depending on reliability of the network view)
		if (stream.isWriting)
		{
			Vector3 pos = transform.localPosition;
			Quaternion rot = transform.localRotation;
			stream.Serialize(ref pos);
			stream.Serialize(ref rot);
		}
		// When receiving, buffer the information
		else
		{
			// Receive latest state information
			Vector3 pos = Vector3.zero;
			Quaternion rot = Quaternion.identity;
			stream.Serialize(ref pos);
			stream.Serialize(ref rot);
			
			// Shift buffer contents, oldest data erased, 18 becomes 19, ... , 0 becomes 1
			for (int i=m_BufferedState.Length-1;i>=1;i--)
			{
				m_BufferedState[i] = m_BufferedState[i-1];
			}
			
			// Save currect received state as 0 in the buffer, safe to overwrite after shifting
			State state;
			state.timestamp = info.timestamp;
			state.pos = pos;
			state.rot = rot;
			m_BufferedState[0] = state;
			
			// Increment state count but never exceed buffer size
			m_TimestampCount = Mathf.Min(m_TimestampCount + 1, m_BufferedState.Length);

			// Check integrity, lowest numbered state in the buffer is newest and so on
			for (int i=0;i<m_TimestampCount-1;i++)
			{
				if (m_BufferedState[i].timestamp < m_BufferedState[i+1].timestamp)
					Debug.Log("State inconsistent");
			}
			
			//Debug.Log("stamp: " + info.timestamp + "my time: " + Network.time + "delta: " + (Network.time - info.timestamp));
		}
	}
	
	// This only runs where the component is enabled, which is only on remote peers (server/clients)
	void Update () {
		if(!photonView.isMine){
		
			double currentTime = PhotonNetwork.time;
			double interpolationTime = currentTime - interpolationBackTime;
			// We have a window of interpolationBackTime where we basically play 
			// By having interpolationBackTime the average ping, you will usually use interpolation.
			// And only if no more data arrives we will use extrapolation
			
			// Use interpolation
			// Check if latest state exceeds interpolation time, if this is the case then
			// it is too old and extrapolation should be used
			if (m_BufferedState[0].timestamp > interpolationTime)
			{
				for (int i=0;i<m_TimestampCount;i++)
				{
					// Find the state which matches the interpolation time (time+0.1) or use last state
					if (m_BufferedState[i].timestamp <= interpolationTime || i == m_TimestampCount-1)
					{
						// The state one slot newer (<100ms) than the best playback state
						State rhs = m_BufferedState[Mathf.Max(i-1, 0)];
						// The best playback state (closest to 100 ms old (default time))
						State lhs = m_BufferedState[i];
						
						// Use the time between the two slots to determine if interpolation is necessary
						double length = rhs.timestamp - lhs.timestamp;
						float t = 0.0F;
						// As the time difference gets closer to 100 ms t gets closer to 1 in 
						// which case rhs is only used
						if (length > 0.0001)
							t = (float)((interpolationTime - lhs.timestamp) / length);
						
						// if t=0 => lhs is used directly
						transform.localPosition = Vector3.Lerp(lhs.pos, rhs.pos, t);
						transform.localRotation = Quaternion.Slerp(lhs.rot, rhs.rot, t);
						return;
					}
				}
			}
			// Use extrapolation. Here we do something really simple and just repeat the last
			// received state. You can do clever stuff with predicting what should happen.
			else
			{
				State latest = m_BufferedState[0];
				
				transform.localPosition = latest.pos;
				transform.localRotation = latest.rot;
			}
			
		}
	}
}

Using it throws the error:
ArgumentOutOfRangeException: Argument is out of range.
Parameter name: index
System.Collections.Generic.List`1[System.Object].set_Item (Int32 index, System.Object value)
PhotonStream.Serialize (UnityEngine.Vector3& obj) (at Assets/Plugins/PhotonNetwork/PhotonClasses.cs:328)
NetworkInterpolatedTransform.OnPhotonSerializeView (.PhotonStream stream, .PhotonMessageInfo info) (at Assets/Scripts/NetworkInterpolatedTransform.cs:27)
System.Reflection.MonoMethod.Invoke (System.Object obj, BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[] parameters, System.Globalization.CultureInfo culture)

On line 328 of PhotonClasses.cs. Is there something different about the syntax of stream.Serialize() in PUN?

Comments

  • Leepo
    Options
    This bug has been fixed in our latest version, but this is not in 1.7 yet.
    To quickly fix this:
    in PhotonClasses.cs change all Serialize "if(write)" parts to:
    if (write)
            {
                this.data.Add(obj);
            }
    
  • legend411
    Options
    Cool beans. Thanks, Leepo.
  • legend411
    Options
    That didn't work, Leepo, it threw:
    Assets/Plugins/PhotonNetwork/PhotonClasses.cs(231,27): error CS0103: The name `obj' does not exist in the current context
    
  • Leepo
    Options
    Ah sorry it wasn't as dead easy as I said:

    Per Serialize, replace OBJ with the right parameter (E.g. myInt in your error case)
    Actually, here is my PhotonClasses:

    // ----------------------------------------------------------------------------
    // <copyright file="PhotonClasses.cs" company="Exit Games GmbH">
    //   PhotonNetwork Framework for Unity - Copyright (C) 2011 Exit Games GmbH
    // </copyright>
    // <summary>
    //   
    // </summary>
    // <author>developer@exitgames.com</author>
    // ----------------------------------------------------------------------------
    using System.Collections.Generic;
    
    using UnityEngine;
    using System.Collections;
    
    // Enums and classes
    
    class PhotonNetworkMessages
    {
        public const byte RPC = 200; 
        public const byte SendSerialize = 201;
        public const byte Instantiation = 202;
        public const byte CloseConnection = 203;
        public const byte Destroy = 204;
        public const byte RemoveCachedRPCs = 205;
    
    }
    
    public enum PhotonTargets { All, Others, MasterClient, AllBuffered, OthersBuffered } //.MasterClientBuffered? .Server?
    public enum NetworkLogLevel { ErrorsOnly, Informational, Full }
    
    
    namespace Photon
    {
        public class MonoBehaviour : UnityEngine.MonoBehaviour
        {
            public PhotonView photonView
            {
                get
                {
                    return PhotonView.Get(this);
                }
            }
            new public PhotonView networkView
            {
                get
                {
                    Debug.LogWarning("Why are you still using networkView? should be PhotonView?");
                    return PhotonView.Get(this);
                }
            }
        }
    }
    
    public class PhotonViewID 
    {
        private PhotonPlayer internalOwner;
        private int internalID = -1; // 1-256 (1-MAX_NETWORKVIEWS)
        
        public PhotonViewID(int ID, PhotonPlayer owner)
        {
            internalID = ID;
            internalOwner = owner;
        }
    
        public int ID
        {   
            // PLAYERNR*MAX_NETWORKVIEWS + internalID
            get
            {
                if(internalOwner == null)
                {
                    //Scene ID
                    return internalID;
                }
                else
                {
                    return (internalOwner.ID*PhotonNetwork.MAX_VIEW_IDS) + internalID;
                }
            }
        }
    
        public bool isMine
        {
            get { return owner.isLocal; }
        }
    
        public PhotonPlayer owner
        {
            get
            {
                int ownerNR = ID / PhotonNetwork.MAX_VIEW_IDS;
                return PhotonPlayer.Find(ownerNR);
            }
        }
    
        public override string ToString()
        {
            return this.ID.ToString();
        }
    
        public override bool Equals(object p)
        {
            PhotonViewID pp = p as PhotonViewID;
            return (pp != null && this.ID == pp.ID);
        }
    
        public override int GetHashCode()
        {
            return this.ID;
        }
    
        [System.Obsolete("Used for compatibility with Unity networking only.")]
        public static PhotonViewID unassigned
        {
            get
            {
                return new PhotonViewID(-1, null);
            }
        }
    }
    
    public class PhotonMessageInfo
    {
        /// <summary>
        /// To create an empty messageinfo only!
        /// </summary>
        public PhotonMessageInfo()
        {
            this.sender = PhotonNetwork.player;
            this.timeInt = (int)(PhotonNetwork.time*1000);
            this.photonView = null;
        }
        public PhotonMessageInfo(PhotonPlayer player, int timestamp, PhotonView view)
        {
            this.sender = player;
            this.timeInt = timestamp;
            this.photonView = view;
        }
        private int timeInt;
        public PhotonPlayer sender;
        public PhotonView photonView;
        public double timestamp
        {
            get { return (uint)timeInt / 1000.0f; }
        }
    
        public override string ToString()
        {
            return string.Format("[PhotonMessageInfo: player='{1}' timestamp={0}]", this.timestamp, this.sender);
        }
    }
    
    public class PhotonStream
    {
        bool write = false;
        List<object> data;
        byte currentItem = 0; //Used to track the next item to receive.
    
        public PhotonStream(bool write, object[] incomingData)
        {
            this.write = write;
            if (incomingData == null)
            {
                this.data = new List<object>();
            }
            else
            {
                this.data = new List<object>(incomingData);
            }
        }
    
        public bool isWriting
        {
            get { return this.write; }
        }
    
        public bool isReading
        {
            get { return !this.write; }
        }
    
        public object ReceiveNext()
        {
            if (this.write)
            {
                Debug.LogError("Error: you cannot read this stream that you are writing!");
                return null;
            }
    
            object obj = this.data[this.currentItem];
            this.currentItem++;
            return obj;
        }
    
        public void SendNext(object obj)
        {
            if (!this.write)
            {
                Debug.LogError("Error: you cannot write/send to this stream that you are reading!");
                return;
            }
    
            this.data.Add(obj);
        }
    
        public object[] ToArray()
        {
            return this.data.ToArray();
        }
    
        public void Serialize(ref bool myBool)
        {
            if (this.write)
            {
                this.data.Add(myBool);
            }
            else
            {
                if (this.data.Count > currentItem)
                {
                    myBool = (bool)data[currentItem];
                    this.currentItem++;
                }
            }
        }
    
        public void Serialize(ref int myInt)
        {
            if (write)
            {
                this.data.Add(myInt);
            }
            else
            {
                if (this.data.Count > currentItem)
                {
                    myInt = (int)data[currentItem];
                    currentItem++;
                }
            }
        }
    
        public void Serialize(ref string value)
        {
            if (write)
            {
                this.data.Add(value);
            }
            else
            {
                if (this.data.Count > currentItem)
                {
                    value = (string)data[currentItem];
                    currentItem++;
                }
            }
        }
    
        public void Serialize(ref char value)
        {
            if (write)
            {
                this.data.Add(value);
            }
            else
            {
                if (this.data.Count > currentItem)
                {
                    value = (char)data[currentItem];
                    currentItem++;
                }
            }
        }
    
        public void Serialize(ref short value)
        {
            if (write)
            {
                this.data.Add(value);
            }
            else
            {
                if (this.data.Count > currentItem)
                {
                    value = (short)data[currentItem];
                    currentItem++;
                }
            }
        }
    
        public void Serialize(ref float obj)
        {
            if (write)
            {
                this.data.Add(obj);
            }
            else
            {
                if (this.data.Count > currentItem)
                {
                    obj = (float)data[currentItem];
                    currentItem++;
                }
            }
        }
    
        public void Serialize(ref PhotonPlayer obj)
        {
            if (write)
            {
                this.data.Add(obj);
            }
            else
            {
                if (this.data.Count > currentItem)
                {
                    obj = (PhotonPlayer)data[currentItem];
                    currentItem++;
                }
            }
        }
    
        public void Serialize(ref Vector3 obj)
        {
            if (write)
            {
                this.data.Add(obj);
            }
            else
            {
                if (this.data.Count > currentItem)
                {
                    obj = (Vector3)data[currentItem];
                    currentItem++;
                }
            }
        }
    
        public void Serialize(ref Vector2 obj)
        {
            if (write)
            {
                this.data.Add(obj);
            }
            else
            {
                if (this.data.Count > currentItem)
                {
                    obj = (Vector2)data[currentItem];
                    currentItem++;
                }
            }
        }
    
        public void Serialize(ref Quaternion obj)
        {
            if (write)
            {
                this.data.Add(obj);
            }
            else
            {
                if (this.data.Count > currentItem)
                {
                    obj = (Quaternion)data[currentItem];
                    currentItem++;
                }
            }
        }
    
        public void Serialize(ref PhotonViewID obj)
        {
            if (write)
            {
                this.data.Add(obj);
            }
            else
            {
                int ID = (int)data[currentItem];
                currentItem++;
                
                int internalID = ID % PhotonNetwork.MAX_VIEW_IDS;
                int actorID = ID / PhotonNetwork.MAX_VIEW_IDS;
                PhotonPlayer owner = null;
                if (actorID > 0)
                {
                    owner = PhotonPlayer.Find(actorID);
                }
    
                obj = new PhotonViewID(internalID, owner);
            }
        }
    }