Network.Instantiate & Destroy buffered RPC problem

Can somebody verify that Network.RemoveRPCs(viewId) will only work if the caller is also the owner of the networkViewId ? If so, it answers my question & provides me a path forward.

A sandbox project which demonstrates this is at this link

Original post below


This is an old problem discussed in many places, but none of the solutions fixes it in my case.

Problem:
I have a game which creates some temporary objects (lasers). When the objects expire they should be gone for good, however any client which joins after they expire, but before the player which created them leaves, gets the (already destroyed) objects sent to them by the server.

I’ve already been down a couple of trails in trying to fix this (it’s well documented & discussed), currently I use the solution referenced at this location to remove the buffered RPC instantiate events from the server using

if (Network.isServer)
{
    Network.RemoveRPCs (viewID);
}

But still when a new client joins the destroyed objects are sent to it. I’ve built a little sandbox to demonstrate the problem;

Source Only

or

Source + win32 exe

  • Start the server
  • Start the client (Client 1)
  • Start a second instance of the client (Client 2)
  • In Client 1 press the fire button 3 times at 1 second intervals
  • Wait 5 seconds for the spheres to expire
  • Keep the Server and Client 1 running, but close down Client 2
  • Start up Client 2 again, when it re-joins it receives the 3 spheres

Fairly obviously the 3 spheres are removed by the server when I call

Network.RemoveRPCs(Player)
Network.DestroyPlayerObjects(Player)

But not when the server executes

Network.RemoveRPCs(viewId)

which is what I need. The behavior I see is almost as if the Network.RemoveRPCs(viewId); call is broken (which it is documented as having been for some time, but reportedly fixed from Unity 4.3-ish). I am using Unity 4.5.4p3.

Here’s the content of my projectile script :

public class Projectile : MonoBehaviour 
{
	public float	Lifetime = 5;
	private float DestroyTime = 0.0f;

	// Use this for initialization
	void Start () 
	{
		// calculate the time we must destroy the projectile
		DestroyTime = Time.time + (float)Lifetime;
	}
	
	// Update is called once per frame
	void Update () 
	{
		// if we've exceeded the destroy time and we explode on expiry =, and we have a prefab
		if (Time.time > DestroyTime)
		{
			DestroyProjectile();
		}
	}

	void DestroyProjectile()
	{
		if (networkView.isMine)
		{
			gameObject.GetComponent<NetworkObject>().DestroyNO();
		}
	}
}

and the NetworkObject destroyer code :

public class NetworkObject : MonoBehaviour
{
	public void DestroyNO()
	{
		Debug.Log("Inside NetworkObject::DestroyNO");
		RemoveBufferedInstantiate (this.networkView.viewID);
		Network.Destroy (this.gameObject);
	}
	
	[RPC]
	public void RemoveBufferedInstantiate(NetworkViewID viewID)
	{
		Debug.Log("Inside RPC NetworkObject::RemoveBufferedInstantiate(" + viewID + ")");
		if (Network.isServer)
		{
			Debug.Log("Server calling Network.RemoveRPCs (" + viewID + ")");
			Network.RemoveRPCs (viewID);
		}
		else
		{
			Debug.Log("Client sending RPC call for viewID " + viewID);
			networkView.RPC ("RemoveBufferedInstantiate", RPCMode.Server, viewID);
		}
	}
}

And finally how I instantiate my temporary objects :

void OnGUI()
{
	    if (GUI.Button(new Rect(10, 10, 80, 20), "Fire"))
	    {
		    GameObject Sphere = Network.Instantiate(PrefabToFire, new Vector3(0, 0, 2), Quaternion.identity, 0) as GameObject;
		    Sphere.transform.rigidbody.AddForce(Vector3.forward * 100);
	    }
}

Any help would be greatly appreciated.

Let me just tell you what I’ve done what works just fine for me. It’s in another context. Every player in my game has a player object that gets instantiated in the:

	private void OnLevelWasLoaded(int level) {
		if(level == 1) {
			if(Network.isClient) {
				GameObject go = Network.Instantiate(PlayerPrefab, Vector3.zero, Quaternion.identity, 0) as GameObject;
				go.networkView.RPC("SetName", RPCMode.AllBuffered, Networking.UserName);
			}
		}
	}

as soon as the player disconnects the server removes every Network.Instantiate this user issued and also destroys those objects

	private void OnPlayerDisconnected(NetworkPlayer player) {
		Network.RemoveRPCs(player);
		Network.DestroyPlayerObjects(player);
	}