Updating this thread again. It’s like I’m going down a rabbit hole of some sort…
Before continuing, I must say SyncLists have this unintended behavior of not entirely following what the Unity Manual would say. The Unity Manual says SyncLists are updated when the server updates them, and the updated data will sync the clients to the server. If you tried simplifying this meaning, you will have a bad time…
I made a few commits of code on my repository, where it is used for fixing the SyncListStruct not being able to add new items to the list, and have them show up on the client side. Sometimes it wouldn’t show up on the server side for some reasons, so it was investigated.
Strangely, the best way to come up with this is to use a mix of [ClientRpc] and [Command] to sync the action of adding new items to the SyncListStruct.
More findings need to be conducted, but this is what I know so far.
The follow code is conducted in a completely brand new project:
using UnityEngine;
using UnityEngine.Networking;
public struct A {
public GameObject obj;
}
public class ASyncList : SyncListStruct<A> {
}
public class PrefabCode : NetworkBehaviour {
public ASyncList list = new ASyncList();
public GameObject prefab;
public void Start() {
if (!this.hasAuthority) {
return;
}
CmdAdd();
}
[Command]
public void CmdAdd() {
A a = new A();
a.obj = NetworkBehaviour.Instantiate<GameObject>(this.prefab);
a.obj.name = this.isServer ? "Server" : "Client";
NetworkServer.SpawnWithClientAuthority(a.obj, this.connectionToClient);
this.list.Add(a);
}
}
When this code is attached to a player prefab (set in the NetworkManager) as a component, and a separate prefab is set to this script, once the script executes, the SyncList are synced across both the host and the client.
I tried using similar code pattern in a bigger project (not some test projects or empty ones), but it fails immediately the moment the client connects to the host.
After further investigation, this is what I come up with that makes the SyncLists in my bigger project:
public class NewSpawner : NetworkBehaviour {
public GameObject newGameUnitPrefab;
public NetworkConnection owner;
public SplitGroupSyncList splitList = new SplitGroupSyncList();
public MergeGroupSyncList mergeList = new MergeGroupSyncList();
public UnitsSyncList unitList = new UnitsSyncList();
public UnitsSyncList selectedList = new UnitsSyncList();
[Command]
public void CmdAddUnit(GameObject obj, GameObject spawner) {
NewSpawner newSpawner = spawner.GetComponent<NewSpawner>();
if (newSpawner != null) {
newSpawner.unitList.Add(new NewUnitStruct(obj));
Debug.Log("Finished adding a new item to UnitList.");
}
}
[ClientRpc]
public void RpcAdd(GameObject obj, GameObject spawner) {
if (this.hasAuthority) {
CmdAddUnit(obj, spawner);
}
}
[Command]
public void CmdInitialize(GameObject spawner) {
Debug.Log("Creating a new game object.");
GameObject gameUnit = MonoBehaviour.Instantiate<GameObject>(this.newGameUnitPrefab);
Debug.Log("Setting the game object to the parent, NewSpawner.");
gameUnit.transform.SetParent(this.transform);
Debug.Log("Setting the game object position to be at NewSpawner.");
gameUnit.transform.position = this.transform.position;
Debug.Log("Network spawning the game object.");
NetworkServer.SpawnWithClientAuthority(gameUnit, this.connectionToClient);
RpcAdd(gameUnit, spawner);
RpcFilter();
}
[ClientRpc]
public void RpcFilter() {
NewGameUnit[] units = GameObject.FindObjectsOfType<NewGameUnit>();
NewSpawner[] spawners = GameObject.FindObjectsOfType<NewSpawner>();
for (int i = 0; i < spawners.Length; i++) {
if (spawners[i].hasAuthority) {
if (units[i].hasAuthority) {
units[i].transform.SetParent(spawners[i].transform);
}
}
else {
if (!units[i].hasAuthority) {
units[i].transform.SetParent(spawners[i].transform);
}
}
continue;
}
}
public void Start() {
if (!this.hasAuthority) {
return;
}
NetworkIdentity spawnerIdentity = this.GetComponent<NetworkIdentity>();
if (!spawnerIdentity.localPlayerAuthority) {
spawnerIdentity.localPlayerAuthority = true;
}
this.owner = this.isServer ? spawnerIdentity.connectionToClient : spawnerIdentity.connectionToServer;
Debug.Log("This is " + (this.isServer ? " Server." : " Client."));
if (this.minimapCamera == null) {
GameObject obj = GameObject.FindGameObjectWithTag("Minimap");
if (obj != null) {
this.minimapCamera = obj.GetComponent<Camera>();
if (this.minimapCamera == null) {
Debug.LogError("Failure to obtain minimap camera.");
}
}
}
if (Camera.main.gameObject.GetComponent<PostRenderer>() == null) {
PostRenderer renderer = Camera.main.gameObject.AddComponent<PostRenderer>();
renderer.minimapCamera = this.minimapCamera;
}
CmdInitialize(this.gameObject);
}
}
In short, I would have to resort to calling on a mix of [ClientRpc] and [Command] to make sure the SyncLists are updated and synchronized on both the host and the client. It’s a mixed bag of spaghetti code, where it requires precise timing, but it sort of works.
I’m, however, not 100% positive it works. More testing is required, and I do hope a Unity staff could come in and share their side of information with me.