- Home /
Inheritance and uninitialized synclists
I initially posted this to the MP forums here but haven't had any luck figuring this out. The issue I am running into is with accessing or initializing a SyncList object that has been declared in a base class. Lets say I have something like this in my base class ThowableItem.cs
protected SyncListFloat inaccuracy = new SyncListFloat();
As long as I utilize this object within the ThrowableItem class everything works fine! However if I declare ThrowableItem.cs->Ball.cs and then attempt to access the inaccuracy element from Ball.cs I get an error that the sync list is uninitialized. This occurs even when attempting to access the inaccuracy object from a call to a base method. Here's the code running in OnStartServer generating the error from ThrowableItem.cs:OnStartServer when called from Ball.cs
// inaccuracy = new SyncListFloat();
for (int count = 0; count < bagSize; count++)
inaccuracy.Add(Random.Range(-inaccuracyRange, inaccuracyRange));
I also tried moving the initialization out the base class. I figured I would just initialize the variable declared in the base class in the descendant and everything would work. Nope, same error, synclist uninitialized. Do SyncLists need to be initialized within class scope and not method scope? How is it recommended to declare and initialize synclists for use with inheritance and polymorphism? I can't seem to figure out how to correctly use these things within a class hierarchy.
@darthbator I have exactly the same issue. Any luck finding a solution or workaround?
Nope :( I ended up establishing a standard list and only using a sync field for the "next" element.
Well, Synclists are not "normal" classes as Unity does a lot "Cecil magic" after your code is compiled to actually "write up" the automatic syncing. It's possible that their IL-weaver simply doesn't see the variable since it's not declared in the actual class. This however is just a guess.
Fact is that whenever you perform a changing action on the list, it will invoke it's private "Send$$anonymous$$sg" method which checks it the internal NetworkBehaviour variable has been set and if not, it shows that error:
private void Send$$anonymous$$sg(SyncList<T>.Operation op, int itemIndex, T item)
{
if (this.m_Behaviour == null)
{
if (LogFilter.logError)
{
Debug.LogError("SyncList not initialized");
}
return;
}
// [ ... ]
The behaviour reference is set by the public method "InitializeBehaviour" which most likely should be called by the injected autogenerated code. $$anonymous$$aybe it's enough to to call it manually but i doubt it. It needs an additional "command hash" which is most likely generated by the injected code and used to distribute the message to the list.
So i guess declaring a synclist in a base class simply isn't supported at the moment. You may want to file a bug-report about that.
haven't come back to this one in a while but it occured to me that the one case I don't believe I tested was to initialize the synclist at declaration within the base class and then call to it using the descendant (probably because the idea here was the try and be generic and the pool was not of a fixed size). Thinking more about the generative code element it could very well be that the "bug" here is that you cannot initialize a synclist at runtime and that it needs to be initialized at compile time?
This seems to be a problem that plagues all SyncLists. I had a similar problem with SyncList items on server not initializing on a client when the client connects.
Thanks for the replies :) - I also suspect it's an issue with the automagic that @Bunny83 describes. However, calling internal methods (especially that require hashes) sounds like dangerous ground indeed. Filing a bug report sounds like the sensible thing to do but I'm pretty disincentivised after my previous UNET bug report went completely ignored. Unity usually fixes these things given enough time. For now I'll fudge around the issue. Cheers.
Answer by YondernautsGames · Jul 14, 2017 at 09:07 AM
I know this question is old, but I think it deserves an answer for anyone else who's searching since I've been going nuts trying to sort this for almost a day now.
So the crux of the problem is uNet Weaver, the system that modifies and extends your code and classes to add all lovely uNet stuff. Weaver will add SyncList initialisation code to your class' Awake() function, however (at least in version 5.5) it seems to have no awareness of class inheritance, and so the base class Awake () will be overridden by the inherited class.
The easy fix is to define a protected virtual Awake () in your base class and then override in all inherited classes (making sure to call base.Awake ()). These functions can be empty or not. The point is Weaver will not create its own "inheritance unaware" Awake functions, it will just add you SyncList init to these and everything will be fine.
Hope someone finds this useful
@Survivalist-Games Thank-you very much! I was going nuts trying to figure out what was wrong with my code. I am beginning to realize that Unity is really bad with inheritance and polymorphism. It turns out I had implemented Awake() in an inherited class and it was, apparently, blocking the initialization of the base class. Exactly as described by @darthbator . Thanks again.
Your answer
Follow this Question
Related Questions
A node in a childnode? 1 Answer
Synchronizing game object across clients 2 Answers
UNET : SyncVar value not updating, hooks firing 1 Answer