- Home /
Crash when sending RPC
This baffled me for long enough. I have this small script here that crashes unity every time a RPC is called from it. It doesn't matter what the RPC does, even if it's empty. Unity just dies.
I've submitted a bug report, but the Unity team haven't answered yet.
This is the script (C#):
using UnityEngine;
public class SpawnScript : MonoBehaviour
{
public Transform PlayerPrefab;
private Transform spawned;
public Color PlayerColor = Color.white;
void OnLevelWasLoaded ()
{
if (Application.loadedLevel != 0 && Application.loadedLevel != 1)
Spawnplayer();
}
void Spawnplayer ()
{
spawned = Network.Instantiate(PlayerPrefab, transform.position, transform.rotation, 0) as Transform;
networkView.RPC("SetPlayerDetails", RPCMode.AllBuffered, GetComponent<LocalClient>().PlayerName);
}
[RPC]
void SetPlayerDetails(string pName)
{
spawned.name = pName;
spawned.renderer.material.color = PlayerColor;
}
void OnDisconnectedFromServer(NetworkDisconnection info)
{
Application.LoadLevel(0);
GetComponent<Menus>().CurrentWindow = 0;
}
}
Any ideas?
Interesting observation: Delaying the RPC call for a second (using yield return new WaitForSeconds(1)
) does not cause Unity to crash. Why?
Update!
This behaviour seems to repeat itself when the RPC is sent 'between' loading levels. If someone can confirm this it will be tremendously helpful.
Another update:
Apparently Unity crashes whenever an exception is thrown during level loading. It happened to me today when trying to display an unassigned texture during level load with LoadLevelAsync
.
I'd be glad if someone can confirm this as well.
Answer by Bunny83 · Mar 17, 2012 at 01:18 PM
It seems you have a little logic problem. I'm not sure if that's the reason for your crash but your code doesn't make much sense:
Network.Instantiate is realised via buffered RPC behind the scenes. The function itself returns the local object reference of the created object. You store this reference in your spawned-variable, but only on the client that spawns the player.
SetPlayerDetails is sent to all clients, but the spawned variable isn't set to the same object on all clients. Every client will hold it's own player object.
The usual way to set any variable (name / health / ...) for a specific player is to use the NetworkView on the playerobject itself. This way the RPC is sent to this specific object on each client. You also have to submit the data you want to set in your RPC. Data on different PCs isn't automatically available, it need to by synced in some way.
So on your player object you should have a script that receives the RPC:
[RPC]
void SetPlayerDetails(string aPlayerName,Color aPlayerColor)
{
name = "Player:" + aPlayerName;
renderer.material.color = aPlayerColor;
}
And send your RPC like this:
spawned = Network.Instantiate(PlayerPrefab, transform.position, transform.rotation, 0) as Transform;
spawned.networkView.RPC("SetPlayerDetails", RPCMode.AllBuffered, GetComponent<LocalClient>().PlayerName, PlayerColor);
Note: Your player prefab of course need a NetworkView attached. Network.Instantiate will take care of assigning a new NetworkViewID and sync it across all clients (with the buffered RPC).
I'm not sure if you can omit the int parameter on OnLevelWasLoaded, but i guess it should work since Unity calls most callback-functions via their SendMessage system with propably uses some kind of reflection. Anyway it's better to use the "correct" version since you actually need the current level inside the function.
void OnLevelWasLoaded (int aLoadedLevel)
{
if (aLoadedLevel > 1)
Spawnplayer();
}
Yes, yes, that is correct. This is a ripoff of another part of my code I used to demonstrate the problem with the RPC so I don't actually use it, but hanks for spotting it anyway.
As for the OnLevelWasLoaded
, while it's probably better to use the 'official' version, you can also compare the currently loaded level with Application.loadedLevel
so it's not impossible to omit the int parameter and retain functionality. :)
Not to mention other useful stuff you want to happen when a level is loaded regardless of what level it is.
Answer by Dreamside · Mar 17, 2012 at 12:21 PM
In scripting reference it shows that OnLevelWasLoaded function takes an integer parameter. It also says that this parameter is index of currently loaded level.There is not an explanation about OnLevelWasLoaded with no parameter.
http://unity3d.com/support/documentation/ScriptReference/MonoBehaviour.OnLevelWasLoaded.html
Hmm. I use it all the time without parameters, though, and it doesn't crash. Only when an RPC is involved.