How to synchronize a list of "player stats" instances?

Hello. It’s probably o noob question, but important to me.

I’m using C#. I’ve just succesfuly made my code so that when player joins or hosts, he adds his “players stats” to a static List as an instance of the “PlayerStats” class.
It should work that way for every player - whenever someone joins, his instance of his statistics is added to the list.

Now, this list should be one, global, so I thought - it should be synchronized by server. Maybe as some kind of SyncVar, probably SyncList of some type. And here’s the problem - is there any way to synchronize a list of class instances? :confused:

I’ve checked Unity Documentation and seems that’s a bummer. Or maybe I understand it wrong. SyncList seems to have only basic types like integer, etc, there is also a struct, but… Do I really need to convert class with player stats into a struct for a server-wide synchronization?

Once again what I want to achieve: I need to have one, global list of players into which new joining players would be added and idententified. I’ve already done identifying - while adding new instance, I’m giving that player an ID = PlayerList.Count - 1. Probably best if elements of PlayerList could be PlayerStats class instances. So each player (at his own index) loads his data from disk into his instance in PlayerList. I thought it would be convenient that way (not sure how to do it otherwise, though). It works in singe player mode, but in Multiplayer I don’t know how to design it properly. Because I need that static list to be server-wide, aware of other players.

And what if I had to synchronize equipments, which are made of classes in my project.

Can you help me somehow with designing that? I’m trying to create a simple multiplayer rpg based on our new Unity Multiplayer tutorial.
Now there’s a need to identify players and load their data in proper place, data which is already generated by character generator and saved, and which properly loads in single player. I thought I should use a local singleton instance of a class PlayerStats for player data, and to keep those instances in global server-wide list.
My PlayerStats are a component of PlayerStatsObject (empty gameobject in hierarchy that has DontDestroyOnLoad). PlayerStats class, made into singleton instance, contains whole generated character, with his vitals, class, race, equipment lists made of classes, etc.
So basically, I need every player to be added to the list, have his own PlayerStats, and make those PlayerStats easily interactable/modifiable by other players (and AI enemies / Environment, but that’s easy, it’s single player). Adding PlayerStats as a component to Player prefab seems overkill to me, as those stats will be called by almost everything in game all the time. So that’s a ton of GameObject.Find(“Player”).GetComponent();. Seems that would be a disastrous design.

Yeah, the documentation on syncing a custom list (Or, as I would suggest, a struct) is pretty bare. Also, it’s generally bad practice to trust anything coming from the clients.
However, if you are just wanting to put something in place that works, not worrying about cheating/hacking, here’s what I suggest you do:
Create an object with a Network Identity with a script like this attached to it

using UnityEngine.Networking;
using System.Collections.Generic;
using UnityEngine;

public class statList : NetworkBehaviour;
{
    public class SyncListStats : SyncListStruct<statList> {}

    public struct statList
    {
        public NetworkInstanceID playerID;
        public List<int> stats;
    }
    public SyncListStats _statList = new SyncListStats();
}

There’s probably some stuff missing or incorrect, but you get the gist of it, right?

1 Like

Thanks for the response. Yes, I think I get the general idea, but will need to check that out in practice in my code soon :).
I’m afraid that’s gonna be probably in about 2-3 hours, got some urgent matters at the moment.

But what I understand now is you say I should put my whole PlayerStats class variables and lists of (for example) equipment into a struct. Which would be synchronized. Not sure why you put that into a new instance of a class though, but I’ll check that out in my project so maybe I’ll get it.

I’m creating mainly an iOS (and maybe Android) game for now, so there shouldn’t be a problem with hacking. Well, maybe not so good about Android…

So how should I keep data of players safe? My guess would be - similarly, but on a dedicated server host which identifies players via login and password.

From what I’ve read, and it works in practice (I use this method to sync building inventory), the reason you’re setting up the new class is because that class needs to inherit SyncListStruct.
You could try, I suppose, having the root class inherit SyncListStruct in addition to NetworkBehaviour, but it may throw errors or exhibit odd behavior.

The process goes a bit further than that. The server would be the one getting the player stats and adding them to the list. If your player stats are on a database. No client is able to modify the list, only read from it. Depending on what you’re going for, the server would also determine when and who can read the list.

Nice, checking that now. Your solution seems good.

Yeah, so there would be getters and setters involved, etc. And much more probably. But Unity’s new Multiplayer already has some easy ways to find out who is player and who is server, so that’s probably helpful.

Also, I’ve used Stack Overflow and a person there suggested that I shouldn’t use instances of classes at all. Because that could cause a lot of transfer data. Instead, if I understand him right, he suggests to make all PlayerStats data into a single string separated by delimiters… but isn’t that just a normal serialization, like with using json maybe? Not sure what he meant now. I have PlayerStats already serialized binarily into a file on disk and it properly loads anytime.
Check those suggestions yourself:
http://stackoverflow.com/questions/39577874/c-sharp-how-to-synchronize-a-list-of-player-stats-instances-unity/39580733#39580733

More importantly, he suggested to use an Observer Pattern. But that’s a bit much for me with my current skills.
I’ve looked into this: https://msdn.microsoft.com/en-us/library/ee850490(v=vs.110).aspx
And this: https://msdn.microsoft.com/en-us/library/ff506346(v=vs.110).aspx

But still have no idea how to implement that in my case. So maybe I’ll try your way for now.

I don’t know about Observer, but I can see the validity in putting them into a string. Seems like a lot of hassle to me, but might be smaller network-wise.
My interpretation of the suggestion is that you turn the stats for all players into a string, using characters like # or * to separate different players and their stats. Depending on how you have things stored before trying to put the string together, you may not be able to use ToString (the struct I showed previously would only return the struct name, not any of the values stored within it)
You would then have the string synced with [SyncVar] and clients would break down the string into form so that it is ordered and the data is accessible.
We could create a ToString override for our struct, but… eh.

1 Like