breaking: Users must initialize synclists (#391) (#2330)

* Users must initialize syncobjects (#391)

Previously we initialized syncobjects, so this is valid:
```cs
public class Pepe : NetworkBehavior {

    public SyncList<int> mylist;
}
```

With this change,  users must initialize their own fields:
```cs
public class Pepe : NetworkBehavior {

    public SyncList<int> mylist = new SyncList<int>();
}
```

BREAKING CHANGE: You must initialize all your SyncLists

* Add null check

* This is no longer a weaver error

* Update Assets/Mirror/Runtime/NetworkBehaviour.cs

Co-authored-by: James Frowen <jamesfrowendev@gmail.com>

* Remove unnecesary using

Co-authored-by: James Frowen <jamesfrowendev@gmail.com>
This commit is contained in:
Paul Pacheco 2020-10-12 02:37:26 -05:00 committed by vis2k
parent 08585c7379
commit 1d05dfc952
3 changed files with 5 additions and 50 deletions

View File

@ -1,4 +1,3 @@
using System.Linq;
using Mono.CecilX;
using Mono.CecilX.Cil;
@ -8,56 +7,10 @@ public static class SyncObjectInitializer
{
public static void GenerateSyncObjectInitializer(ILProcessor worker, FieldDefinition fd)
{
// call syncobject constructor
GenerateSyncObjectInstanceInitializer(worker, fd);
// register syncobject in network behaviour
GenerateSyncObjectRegistration(worker, fd);
}
// generates 'syncListInt = new SyncListInt()' if user didn't do that yet
static void GenerateSyncObjectInstanceInitializer(ILProcessor worker, FieldDefinition fd)
{
// check the ctor's instructions for an Stfld op-code for this specific sync list field.
foreach (Instruction ins in worker.Body.Instructions)
{
if (ins.OpCode.Code == Code.Stfld)
{
FieldDefinition field = (FieldDefinition)ins.Operand;
if (field.DeclaringType == fd.DeclaringType && field.Name == fd.Name)
{
// Already initialized by the user in the field definition, e.g:
// public SyncListInt Foo = new SyncListInt();
return;
}
}
}
// Not initialized by the user in the field definition, e.g:
// public SyncListInt Foo;
TypeDefinition fieldType = fd.FieldType.Resolve();
// find ctor with no parameters
MethodDefinition ctor = fieldType.Methods.FirstOrDefault(x => x.Name == ".ctor" && !x.HasParameters);
if (ctor == null)
{
Weaver.Error($"Can not initialize field {fd.Name} because no default constructor was found. Manually initialize the field (call the constructor) or add constructor without Parameter", fd);
return;
}
MethodReference objectConstructor = Weaver.CurrentAssembly.MainModule.ImportReference(ctor);
// if is SyncList<int> instead of SyncListInt then we need to make the ctor generic
if (fd.FieldType.IsGenericInstance)
{
GenericInstanceType genericInstance = (GenericInstanceType)fd.FieldType;
objectConstructor = objectConstructor.MakeHostInstanceGeneric(genericInstance);
}
worker.Append(worker.Create(OpCodes.Ldarg_0));
worker.Append(worker.Create(OpCodes.Newobj, objectConstructor));
worker.Append(worker.Create(OpCodes.Stfld, fd));
}
public static bool ImplementsSyncObject(TypeReference typeRef)
{
try

View File

@ -154,7 +154,10 @@ public int ComponentIndex
// We collect all of them and we synchronize them with OnSerialize/OnDeserialize
protected void InitSyncObject(SyncObject syncObject)
{
syncObjects.Add(syncObject);
if (syncObject == null)
Debug.LogError("Uninitialized SyncObject. Manually call the constructor on your SyncList, SyncSet or SyncDictionary", this);
else
syncObjects.Add(syncObject);
}
#region Commands

View File

@ -43,8 +43,7 @@ public void SyncListInheritance()
[Test]
public void SyncListMissingParamlessCtor()
{
HasError("Can not initialize field Foo because no default constructor was found. Manually initialize the field (call the constructor) or add constructor without Parameter",
"WeaverSyncListTests.SyncListMissingParamlessCtor.SyncListMissingParamlessCtor/SyncListString2 WeaverSyncListTests.SyncListMissingParamlessCtor.SyncListMissingParamlessCtor::Foo");
IsSuccess();
}
[Test]