List not syncing over network with RPC call.

Using RPC call to send the list of players works fine, but when I do it the same way with the selected characters name, it will not work.

The selected character will be the local selected character name on both players slots in the multiplayer room. This image can explain it: Dropbox - Error - Simplify your life

on the server the character is named server and is of type archer while on the client its named client and is of type mage.

Row 93 sets the value on the GUI while row 45-86 are the RPC calls.

It should say like this on both clients:
Player: server: Archer
Player: client: Mage

public class MultiplayerRoom : MonoBehaviour
{
    // character lists
    private string[] fileList;                          // list of files in character folder
    private int selectedItemIndex;                      // index of selected item in dropdown list
    private Character currentCharacter;                 // selected character

    // serialization
    private Serializer serializer = new Serializer();   // serializer

    // ComboBox
    private string[] itemList;                          // list of items
    private Rect comboBox;                               // box positioning
    private string selectedItem = "";                   // current selected item, empty as a default value if no items exists
    private bool editing = false;                       // keep the list open or not (start as closed)

    // level list toggeling
    private int selectedLevelIndex = -1;                // level selected at character selection screen

    // level data
    private LevelData levelData = new LevelData();      // object containing level data
    private List<Level> levelList;                      // list of levels

    // networking
    private List<NetworkPlayer> listOfPlayers = new List<NetworkPlayer>();	// list of connected players
	private List<String> selectedCharacters = new List<String>();			// list of connected players chosen characters

    void Start()
    {
        // get level data
        levelList = levelData.GetList();
        selectedLevelIndex = 0;
        selectedItemIndex = 0;
    }

    void OnGUI()
    {
        if(Network.peerType == NetworkPeerType.Disconnected)
        {
			// TODO: add room loading GUI with cancel button and a timer
            Debug.Log("Disconnected");
        }
        else
        {
			// set style of boxes
            GUI.skin = gameObject.GetComponent<GUIStyler>().mainMenuSkin;

            // Make a background box
            GUI.Box(new Rect((Screen.width / 2) - 150, (Screen.height / 2) - 300, 300, 400), "Multiplayer Game");

			// character ComboBox
			comboBox = new Rect((Screen.width / 2) - 80, (Screen.height / 2) - 255, 165, 25);
			
			if (GUI.Button(comboBox, selectedItem))
			{
				editing = true;
			}
			
			Event e = Event.current;
			
			if (editing)
			{
				for (int i = 0; i < itemList.Length; i++)
				{
					if (GUI.Button(new Rect(comboBox.x, (comboBox.height * i) + comboBox.y + comboBox.height, comboBox.width, comboBox.height), itemList*))*
  •  			{*
    

_ selectedItem = itemList*;_
_
selectedItemIndex = i;*_

* currentCharacter = serializer.Deserialize(fileList[selectedItemIndex]);*

* editing = false;*
* }*
* }*

* if (e.type == EventType.MouseDown)*
* {*
* editing = false;*
* }*
* }*
* else*
* {*
* // change character and push the data over the network only if it has changed*
* if( selectedItem != selectedCharacters[PlayerIndexToInt(Network.player)] )*
* {*
* networkView.RPC(“SetSelectedCharacter”, RPCMode.AllBuffered, PlayerIndexToInt(Network.player));*
* }*
* }*

// list of connected players
foreach (NetworkPlayer player in listOfPlayers)
{
_ GUI.Label(new Rect((Screen.width / 2) - 130, (Screen.height / 2) - 225 + (int.Parse(player.ToString()) * 30), 200, 50), "Player: " + selectedCharacters[PlayerIndexToInt(player)]);_
}

// level list
GUILayout.BeginArea(new Rect(Screen.width / 2 - 130, (Screen.height / 2) - 150, 250, 200));

if (fileList.Length > 0 && currentCharacter != null)
{
for (int i = 0; i < currentCharacter.characterLevel; i++)
{
if (GUILayout.Toggle(selectedLevelIndex == i, levelList_.Number + ": " + levelList*.Name, “toggle”))
{
selectedLevelIndex = i;
}
}
}*_

GUILayout.EndArea();

// buttons
if (GUI.Button(new Rect((Screen.width / 2) - 130, (Screen.height / 2) + 50, 80, 20), “Back”))
{
Network.Disconnect();
gameObject.GetComponent().SetGUI(false, false, false, false, false, false, false, false, true, false, false);
}
}
}

void Update()
{
fileList = serializer.ListFiles(); // create list of files
itemList = new string[fileList.Length];

// get character list
for (int i = 0; i < fileList.Length; i++)
{
Character aCharacter = serializer.Deserialize(fileList*);*
itemList = aCharacter.characterName + ": " + aCharacter.characterClass;
}

// select first option by default if any exists
if (itemList != null && itemList.Length > 0 && selectedItem.Equals(“”))
{
selectedItem = itemList[0];
currentCharacter = serializer.Deserialize(fileList[0]);
}
}

///


/// Create a list of connecting players
///

/// connecting player
void OnPlayerConnected(NetworkPlayer player)
{
networkView.RPC(“PushListOfPlayers”, RPCMode.AllBuffered, player);
* networkView.RPC(“AppendNewCharacter”, RPCMode.AllBuffered);*
}

///


/// Add the server creator to the list of players
///

void OnServerInitialized()
{
* networkView.RPC(“PushListOfPlayers”, RPCMode.AllBuffered, Network.player);*
* networkView.RPC(“AppendNewCharacter”, RPCMode.AllBuffered);*
}

* ///

*
* /// Pushs the list of players.*
* ///
*
* /// Player to add to the list*
* [RPC]*
* void PushListOfPlayers(NetworkPlayer player)*
* {*
* listOfPlayers.Add(player);*
* }*

* ///

*
* /// Appends a new character to be used in the player list.*
* ///
*
* [RPC]*
* void AppendNewCharacter()*
* {*
* selectedCharacters.Add(selectedItem);*
* }*

* ///

*
* /// Sets the selected character to be displayed in the player list.*
* ///
*
* /// index in selected characters list*
* [RPC]*
* void SetSelectedCharacter(int index)*
* {*
* selectedCharacters[index] = selectedItem;*
* }*

* ///

*
* /// Converts players index to int.*
* ///
*
* /// The index as an int*
* /// Player.*
* private int PlayerIndexToInt(NetworkPlayer player)*
* {*
* String thisPlayer = player.ToString(); // get id of player*
* int thisPlayerInt = int.Parse(thisPlayer); // convert id to int*

* return thisPlayerInt;*
* }*
}

2 Answers

2

You’re doing some really dangerous things here. First of all syncing lists like that is not a good idea. If an update of the list is somehow corrupt your lists will be out of sync forever. Also relying on an index into such a list which got filled with “Add” is also not a good idea.

The worst thing you do is using the parsed id you get from the ToString method of a NetworkPlayer. It’s not said that the id will be the same on different clients. You should create your own player IDs on the server. Also use a fixed sized array for player slots so the index becomes the player ID and it doesn’t change during the session. A player slot should store all required information of the player. Like his name, NetworkPlayer, character selection, gameobject … Whenever you want to address a certain player slot, just use the index of the slot. Only the server should create / activate new slots when someone joins.

In our hack and slash game we used an array with 32 slots and each slot was simply a custom class with all the information about the player. It looked like this:

public class NetworkUser
{
    public NetworkPlayer        netPlayer;
    public int                  netID;
    public RegistratedUser      regUser;
    public ServerUser           sUser;
    public bool                 active;
    public PlayerObject         playerObject;
    public string               nickName;
    public int                  team;

    private float               Health = 100.0f;
}

Each slot of the playerslots-array got assigned an instance of that class in Awake. The “active” bool determined if the slot was used or not. The “netID” integer was equal to the index into the slot array and was our own “player id”.

Whenever one of the values got changed, we synced most of the information in that class at once. Of course some things are local to each peer like the playerObject for example.

thank you, but how am I supposed to sync a list if not in the RPC method?

Well, you have to use RPCs to sync your data, but make sure the data is consistant and reliable which your "PlayerIndexToInt" isn't. Also, like i said using dynamic structures will just create more problems. What if a player leaves mid-game? Do you remove it from your player list? That means all indices would change.

Thank you, very informative!

NetworkView.Rpc does not support List
Valid RPC parameters are int, float, string, NetworkPlayer, NetworkViewID,Vector3 and Quaternion.

you can Make a string of List in which each data member can be saperated by a " "(space) and send through RPC. and in called function you can convert string into list again are array anything you want by using String.Split().

then how come PushListOfPlayers(NetworkPlayer player) works? This makes me confused.

I am only sending a string which then gets added to a list: networkView.RPC("AppendNewCharacter", RPCMode.AllBuffered, selectedItem); and then... [RPC] void AppendNewCharacter(String character) { selectedCharacters.Add(character); } how is this method wrong?