mirror of
https://github.com/MirrorNetworking/Mirror.git
synced 2024-11-18 11:00:32 +00:00
feat: Allowing extra base types to be used for SyncLists and other SyncObjects (#1729)
* Tests for most uses of sync list renaming classes for existing test files * improving error message for Paramless constructor * More tests for SyncLists * updating error message * ignore abstract classes we dont need to process abstract classes because classes that inherit from them will be processed instead * check and error for syncList item being genric * allowing extra base types for synclist * checking for nested types in abstract classes * test for nested types inside structs
This commit is contained in:
parent
ddd4b0b1fc
commit
9bf816a014
@ -0,0 +1,110 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using Mono.CecilX;
|
||||||
|
|
||||||
|
namespace Mirror.Weaver
|
||||||
|
{
|
||||||
|
public class GenericArgumentResolver
|
||||||
|
{
|
||||||
|
readonly Stack<TypeReference> stack = new Stack<TypeReference>();
|
||||||
|
readonly int maxGenericArgument;
|
||||||
|
|
||||||
|
public GenericArgumentResolver(int maxGenericArgument)
|
||||||
|
{
|
||||||
|
this.maxGenericArgument = maxGenericArgument;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool GetGenericFromBaseClass(TypeDefinition td, int genericArgument, TypeReference baseType, out TypeReference itemType)
|
||||||
|
{
|
||||||
|
itemType = null;
|
||||||
|
if (GetGenericBaseType(td, baseType, out GenericInstanceType parent))
|
||||||
|
{
|
||||||
|
TypeReference arg = parent.GenericArguments[genericArgument];
|
||||||
|
if (arg.IsGenericParameter)
|
||||||
|
{
|
||||||
|
itemType = FindParameterInStack(genericArgument);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
itemType = Weaver.CurrentAssembly.MainModule.ImportReference(arg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return itemType != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
TypeReference FindParameterInStack(int genericArgument)
|
||||||
|
{
|
||||||
|
while (stack.Count > 0)
|
||||||
|
{
|
||||||
|
TypeReference next = stack.Pop();
|
||||||
|
|
||||||
|
if (!(next is GenericInstanceType genericType))
|
||||||
|
{
|
||||||
|
// if type is not GenericInstanceType something has gone wrong
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (genericType.GenericArguments.Count < genericArgument)
|
||||||
|
{
|
||||||
|
// if less than `genericArgument` then we didnt find generic argument
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (genericType.GenericArguments.Count > maxGenericArgument)
|
||||||
|
{
|
||||||
|
// if greater than `genericArgument` it is hard to know which generic arg we want
|
||||||
|
// See SyncListGenericInheritanceWithMultipleGeneric test
|
||||||
|
Weaver.Error($"Too many generic argument for {next}");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
TypeReference genericArg = genericType.GenericArguments[genericArgument];
|
||||||
|
if (!genericArg.IsGenericParameter)
|
||||||
|
{
|
||||||
|
// if not generic, sucessfully found type
|
||||||
|
return Weaver.CurrentAssembly.MainModule.ImportReference(genericArg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// nothing left in stack, something went wrong
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GetGenericBaseType(TypeDefinition td, TypeReference baseType, out GenericInstanceType found)
|
||||||
|
{
|
||||||
|
stack.Clear();
|
||||||
|
TypeReference parent = td.BaseType;
|
||||||
|
found = null;
|
||||||
|
|
||||||
|
while (parent != null)
|
||||||
|
{
|
||||||
|
string parentName = parent.FullName;
|
||||||
|
|
||||||
|
// strip generic parameters
|
||||||
|
int index = parentName.IndexOf('<');
|
||||||
|
if (index != -1)
|
||||||
|
{
|
||||||
|
parentName = parentName.Substring(0, index);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parentName == baseType.FullName)
|
||||||
|
{
|
||||||
|
found = parent as GenericInstanceType;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
try
|
||||||
|
{
|
||||||
|
stack.Push(parent);
|
||||||
|
parent = parent.Resolve().BaseType;
|
||||||
|
}
|
||||||
|
catch (AssemblyResolutionException)
|
||||||
|
{
|
||||||
|
// this can happen for plugins.
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return found != null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: fd67b3f7c2d66074a9bc7a23787e2ffb
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@ -11,8 +11,26 @@ static class SyncDictionaryProcessor
|
|||||||
/// <param name="td">The synclist class</param>
|
/// <param name="td">The synclist class</param>
|
||||||
public static void Process(TypeDefinition td)
|
public static void Process(TypeDefinition td)
|
||||||
{
|
{
|
||||||
SyncObjectProcessor.GenerateSerialization(td, 0, "SerializeKey", "DeserializeKey");
|
GenericArgumentResolver resolver = new GenericArgumentResolver(2);
|
||||||
SyncObjectProcessor.GenerateSerialization(td, 1, "SerializeItem", "DeserializeItem");
|
|
||||||
|
if (resolver.GetGenericFromBaseClass(td, 0, Weaver.SyncDictionaryType, out TypeReference keyType))
|
||||||
|
{
|
||||||
|
SyncObjectProcessor.GenerateSerialization(td, keyType, "SerializeKey", "DeserializeKey");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Weaver.Error($"Could not find generic arguments for {Weaver.SyncDictionaryType} using {td}");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (resolver.GetGenericFromBaseClass(td, 1, Weaver.SyncDictionaryType, out TypeReference itemType))
|
||||||
|
{
|
||||||
|
SyncObjectProcessor.GenerateSerialization(td, itemType, "SerializeItem", "DeserializeItem");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Weaver.Error($"Could not find generic arguments for {Weaver.SyncDictionaryType} using {td}");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,9 +9,18 @@ static class SyncListProcessor
|
|||||||
/// Generates serialization methods for synclists
|
/// Generates serialization methods for synclists
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="td">The synclist class</param>
|
/// <param name="td">The synclist class</param>
|
||||||
public static void Process(TypeDefinition td)
|
public static void Process(TypeDefinition td, TypeReference baseType)
|
||||||
{
|
{
|
||||||
SyncObjectProcessor.GenerateSerialization(td, 0, "SerializeItem", "DeserializeItem");
|
GenericArgumentResolver resolver = new GenericArgumentResolver(1);
|
||||||
|
|
||||||
|
if (resolver.GetGenericFromBaseClass(td, 0, baseType, out TypeReference itemType))
|
||||||
|
{
|
||||||
|
SyncObjectProcessor.GenerateSerialization(td, itemType, "SerializeItem", "DeserializeItem");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Weaver.Error($"Could not find generic arguments for {baseType} using {td}");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -41,7 +41,7 @@ static void GenerateSyncObjectInstanceInitializer(ILProcessor ctorWorker, FieldD
|
|||||||
MethodDefinition ctor = fieldType.Methods.FirstOrDefault(x => x.Name == ".ctor" && !x.HasParameters);
|
MethodDefinition ctor = fieldType.Methods.FirstOrDefault(x => x.Name == ".ctor" && !x.HasParameters);
|
||||||
if (ctor == null)
|
if (ctor == null)
|
||||||
{
|
{
|
||||||
Weaver.Error($"{fd} does not have a default constructor");
|
Weaver.Error($"{fd} Can not intialize field because no default constructor was found. Manually intialize the field (call the constructor) or add constructor without Parameter");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
MethodReference objectConstructor = Weaver.CurrentAssembly.MainModule.ImportReference(ctor);
|
MethodReference objectConstructor = Weaver.CurrentAssembly.MainModule.ImportReference(ctor);
|
||||||
|
@ -10,19 +10,11 @@ public static class SyncObjectProcessor
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="td">The type of the class that needs serialization methods</param>
|
/// <param name="td">The type of the class that needs serialization methods</param>
|
||||||
/// <param name="genericArgument">Which generic argument to serialize, 0 is the first one</param>
|
/// <param name="genericArgument">Which generic argument to serialize, 0 is the first one</param>
|
||||||
|
/// <param name="baseType">the type that has generic arguments</param>
|
||||||
/// <param name="serializeMethod">The name of the serialize method</param>
|
/// <param name="serializeMethod">The name of the serialize method</param>
|
||||||
/// <param name="deserializeMethod">The name of the deserialize method</param>
|
/// <param name="deserializeMethod">The name of the deserialize method</param>
|
||||||
public static void GenerateSerialization(TypeDefinition td, int genericArgument, string serializeMethod, string deserializeMethod)
|
public static void GenerateSerialization(TypeDefinition td, TypeReference itemType, string serializeMethod, string deserializeMethod)
|
||||||
{
|
{
|
||||||
// find item type
|
|
||||||
GenericInstanceType gt = (GenericInstanceType)td.BaseType;
|
|
||||||
if (gt.GenericArguments.Count <= genericArgument)
|
|
||||||
{
|
|
||||||
Weaver.Error($"{td} should have {genericArgument} generic arguments");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
TypeReference itemType = Weaver.CurrentAssembly.MainModule.ImportReference(gt.GenericArguments[genericArgument]);
|
|
||||||
|
|
||||||
Weaver.DLog(td, "SyncObjectProcessor Start item:" + itemType.FullName);
|
Weaver.DLog(td, "SyncObjectProcessor Start item:" + itemType.FullName);
|
||||||
|
|
||||||
MethodReference writeItemFunc = GenerateSerialization(serializeMethod, td, itemType);
|
MethodReference writeItemFunc = GenerateSerialization(serializeMethod, td, itemType);
|
||||||
@ -47,6 +39,15 @@ static MethodReference GenerateSerialization(string methodName, TypeDefinition t
|
|||||||
if (existing != null)
|
if (existing != null)
|
||||||
return existing;
|
return existing;
|
||||||
|
|
||||||
|
|
||||||
|
// this check needs to happen inside GenerateSerialization because
|
||||||
|
// we need to check if user has made custom function above
|
||||||
|
if (itemType.IsGenericInstance)
|
||||||
|
{
|
||||||
|
Weaver.Error($"{td} Can not create Serialize or Deserialize for generic element. Override virtual methods with custom Serialize and Deserialize to use {itemType} in SyncList");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
MethodDefinition serializeFunc = new MethodDefinition(methodName, MethodAttributes.Public |
|
MethodDefinition serializeFunc = new MethodDefinition(methodName, MethodAttributes.Public |
|
||||||
MethodAttributes.Virtual |
|
MethodAttributes.Virtual |
|
||||||
MethodAttributes.Public |
|
MethodAttributes.Public |
|
||||||
@ -57,12 +58,6 @@ static MethodReference GenerateSerialization(string methodName, TypeDefinition t
|
|||||||
serializeFunc.Parameters.Add(new ParameterDefinition("item", ParameterAttributes.None, itemType));
|
serializeFunc.Parameters.Add(new ParameterDefinition("item", ParameterAttributes.None, itemType));
|
||||||
ILProcessor serWorker = serializeFunc.Body.GetILProcessor();
|
ILProcessor serWorker = serializeFunc.Body.GetILProcessor();
|
||||||
|
|
||||||
if (itemType.IsGenericInstance)
|
|
||||||
{
|
|
||||||
Weaver.Error($"{td} cannot have generic elements {itemType}");
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
MethodReference writeFunc = Writers.GetWriteFunc(itemType);
|
MethodReference writeFunc = Writers.GetWriteFunc(itemType);
|
||||||
if (writeFunc != null)
|
if (writeFunc != null)
|
||||||
{
|
{
|
||||||
@ -88,6 +83,14 @@ static MethodReference GenerateDeserialization(string methodName, TypeDefinition
|
|||||||
if (existing != null)
|
if (existing != null)
|
||||||
return existing;
|
return existing;
|
||||||
|
|
||||||
|
// this check needs to happen inside GenerateDeserialization because
|
||||||
|
// we need to check if user has made custom function above
|
||||||
|
if (itemType.IsGenericInstance)
|
||||||
|
{
|
||||||
|
Weaver.Error($"{td} Can not create Serialize or Deserialize for generic element. Override virtual methods with custom Serialize and Deserialize to use {itemType} in SyncList");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
MethodDefinition deserializeFunction = new MethodDefinition(methodName, MethodAttributes.Public |
|
MethodDefinition deserializeFunction = new MethodDefinition(methodName, MethodAttributes.Public |
|
||||||
MethodAttributes.Virtual |
|
MethodAttributes.Virtual |
|
||||||
MethodAttributes.Public |
|
MethodAttributes.Public |
|
||||||
|
@ -406,31 +406,37 @@ static bool WeaveMessage(TypeDefinition td)
|
|||||||
|
|
||||||
static bool WeaveSyncObject(TypeDefinition td)
|
static bool WeaveSyncObject(TypeDefinition td)
|
||||||
{
|
{
|
||||||
if (!td.IsClass)
|
bool modified = false;
|
||||||
return false;
|
|
||||||
|
|
||||||
// ignore generic classes
|
// ignore generic classes
|
||||||
// we can not process generic classes
|
// we can not process generic classes
|
||||||
// we give error if a generic syncObject is used in NetworkBehaviour
|
// we give error if a generic syncObject is used in NetworkBehaviour
|
||||||
if (td.HasGenericParameters)
|
if (td.HasGenericParameters)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
bool modified = false;
|
// ignore abstract classes
|
||||||
|
// we dont need to process abstract classes because classes that
|
||||||
|
// inherit from them will be processed instead
|
||||||
|
|
||||||
if (td.IsDerivedFrom(SyncListType))
|
// We cant early return with non classes or Abstract classes
|
||||||
|
// because we still need to check for embeded types
|
||||||
|
if (td.IsClass || !td.IsAbstract)
|
||||||
{
|
{
|
||||||
SyncListProcessor.Process(td);
|
if (td.IsDerivedFrom(SyncListType))
|
||||||
modified = true;
|
{
|
||||||
}
|
SyncListProcessor.Process(td, SyncListType);
|
||||||
else if (td.IsDerivedFrom(SyncSetType))
|
modified = true;
|
||||||
{
|
}
|
||||||
SyncListProcessor.Process(td);
|
else if (td.IsDerivedFrom(SyncSetType))
|
||||||
modified = true;
|
{
|
||||||
}
|
SyncListProcessor.Process(td, SyncSetType);
|
||||||
else if (td.IsDerivedFrom(SyncDictionaryType))
|
modified = true;
|
||||||
{
|
}
|
||||||
SyncDictionaryProcessor.Process(td);
|
else if (td.IsDerivedFrom(SyncDictionaryType))
|
||||||
modified = true;
|
{
|
||||||
|
SyncDictionaryProcessor.Process(td);
|
||||||
|
modified = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// check for embedded types
|
// check for embedded types
|
||||||
|
@ -5,7 +5,43 @@ namespace Mirror.Weaver.Tests
|
|||||||
public class WeaverSyncListTests : WeaverTestsBuildFromTestName
|
public class WeaverSyncListTests : WeaverTestsBuildFromTestName
|
||||||
{
|
{
|
||||||
[Test]
|
[Test]
|
||||||
public void SyncListValid()
|
public void SyncList()
|
||||||
|
{
|
||||||
|
Assert.That(CompilationFinishedHook.WeaveFailed, Is.False);
|
||||||
|
Assert.That(weaverErrors, Is.Empty);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void SyncListByteValid()
|
||||||
|
{
|
||||||
|
Assert.That(CompilationFinishedHook.WeaveFailed, Is.False);
|
||||||
|
Assert.That(weaverErrors, Is.Empty);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void SyncListGenericAbstractInheritance()
|
||||||
|
{
|
||||||
|
Assert.That(CompilationFinishedHook.WeaveFailed, Is.False);
|
||||||
|
Assert.That(weaverErrors, Is.Empty);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void SyncListGenericInheritance()
|
||||||
|
{
|
||||||
|
Assert.That(CompilationFinishedHook.WeaveFailed, Is.False);
|
||||||
|
Assert.That(weaverErrors, Is.Empty);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void SyncListGenericInheritanceWithMultipleGeneric()
|
||||||
|
{
|
||||||
|
Assert.That(CompilationFinishedHook.WeaveFailed, Is.True);
|
||||||
|
Assert.That(weaverErrors, Has.Some.Match(@"Mirror\.Weaver error: Could not find generic arguments for Mirror\.SyncList`1 using MirrorTest\.SomeListInt"));
|
||||||
|
Assert.That(weaverErrors, Has.Some.Match(@"Mirror\.Weaver error: Too many generic argument for MirrorTest\.SomeList`2<System.String,System.Int32>"));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void SyncListInheritance()
|
||||||
{
|
{
|
||||||
Assert.That(CompilationFinishedHook.WeaveFailed, Is.False);
|
Assert.That(CompilationFinishedHook.WeaveFailed, Is.False);
|
||||||
Assert.That(weaverErrors, Is.Empty);
|
Assert.That(weaverErrors, Is.Empty);
|
||||||
@ -15,11 +51,116 @@ public void SyncListValid()
|
|||||||
public void SyncListMissingParamlessCtor()
|
public void SyncListMissingParamlessCtor()
|
||||||
{
|
{
|
||||||
Assert.That(CompilationFinishedHook.WeaveFailed, Is.True);
|
Assert.That(CompilationFinishedHook.WeaveFailed, Is.True);
|
||||||
Assert.That(weaverErrors, Contains.Item("Mirror.Weaver error: MirrorTest.MirrorTestPlayer/SyncListString2 MirrorTest.MirrorTestPlayer::Foo does not have a default constructor"));
|
string weaverError = @"Mirror\.Weaver error:";
|
||||||
|
string fieldType = @"MirrorTest\.SyncListString2 MirrorTest\.SyncListMissingParamlessCtor::Foo";
|
||||||
|
string errorMessage = @"Can not intialize field because no default constructor was found\. Manually intialize the field \(call the constructor\) or add constructor without Parameter";
|
||||||
|
Assert.That(weaverErrors, Has.Some.Match($"{weaverError} {fieldType} {errorMessage}"));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void SyncListMissingParamlessCtorManuallyInitialized()
|
||||||
|
{
|
||||||
|
Assert.That(CompilationFinishedHook.WeaveFailed, Is.False);
|
||||||
|
Assert.That(weaverErrors, Is.Empty);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void SyncListByteValid()
|
public void SyncListNestedStruct()
|
||||||
|
{
|
||||||
|
Assert.That(CompilationFinishedHook.WeaveFailed, Is.False);
|
||||||
|
Assert.That(weaverErrors, Is.Empty);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void SyncListNestedInAbstractClass()
|
||||||
|
{
|
||||||
|
Assert.That(CompilationFinishedHook.WeaveFailed, Is.False);
|
||||||
|
Assert.That(weaverErrors, Is.Empty);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void SyncListNestedInAbstractClassWithInvalid()
|
||||||
|
{
|
||||||
|
// we need this negative test to make sure that SyncList is being processed
|
||||||
|
Assert.That(CompilationFinishedHook.WeaveFailed, Is.True);
|
||||||
|
Assert.That(weaverErrors, Has.Some.Match(@"Mirror\.Weaver error: UnityEngine\.Object MirrorTest\.SomeAbstractClass/MyNestedStruct::target has unsupported type\. Use a type supported by Mirror instead"));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void SyncListNestedInStruct()
|
||||||
|
{
|
||||||
|
Assert.That(CompilationFinishedHook.WeaveFailed, Is.False);
|
||||||
|
Assert.That(weaverErrors, Is.Empty);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void SyncListNestedInStructWithInvalid()
|
||||||
|
{
|
||||||
|
// we need this negative test to make sure that SyncList is being processed
|
||||||
|
Assert.That(CompilationFinishedHook.WeaveFailed, Is.True);
|
||||||
|
Assert.That(weaverErrors, Has.Some.Match(@"Mirror\.Weaver error: UnityEngine\.Object MirrorTest\.SomeData::target has unsupported type\. Use a type supported by Mirror instead"));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void SyncListStruct()
|
||||||
|
{
|
||||||
|
Assert.That(CompilationFinishedHook.WeaveFailed, Is.False);
|
||||||
|
Assert.That(weaverErrors, Is.Empty);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void SyncListStructWithCustomDeserializeOnly()
|
||||||
|
{
|
||||||
|
Assert.That(CompilationFinishedHook.WeaveFailed, Is.False);
|
||||||
|
Assert.That(weaverErrors, Is.Empty);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void SyncListStructWithCustomMethods()
|
||||||
|
{
|
||||||
|
Assert.That(CompilationFinishedHook.WeaveFailed, Is.False);
|
||||||
|
Assert.That(weaverErrors, Is.Empty);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void SyncListStructWithCustomSerializeOnly()
|
||||||
|
{
|
||||||
|
Assert.That(CompilationFinishedHook.WeaveFailed, Is.False);
|
||||||
|
Assert.That(weaverErrors, Is.Empty);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void SyncListErrorForGenericStruct()
|
||||||
|
{
|
||||||
|
Assert.That(CompilationFinishedHook.WeaveFailed, Is.True);
|
||||||
|
string weaverError = @"Mirror\.Weaver error:";
|
||||||
|
string type = @"MirrorTest\.MyGenericStructList";
|
||||||
|
string errorMessage = @"Can not create Serialize or Deserialize for generic element\. Override virtual methods with custom Serialize and Deserialize to use MirrorTest.MyGenericStruct`1<System.Single> in SyncList";
|
||||||
|
Assert.That(weaverErrors, Has.Some.Match($"{weaverError} {type} {errorMessage}"));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void SyncListErrorForGenericStructWithCustomDeserializeOnly()
|
||||||
|
{
|
||||||
|
Assert.That(CompilationFinishedHook.WeaveFailed, Is.True);
|
||||||
|
string weaverError = @"Mirror\.Weaver error:";
|
||||||
|
string type = @"MirrorTest\.MyGenericStructList";
|
||||||
|
string errorMessage = @"Can not create Serialize or Deserialize for generic element\. Override virtual methods with custom Serialize and Deserialize to use MirrorTest.MyGenericStruct`1<System.Single> in SyncList";
|
||||||
|
Assert.That(weaverErrors, Has.Some.Match($"{weaverError} {type} {errorMessage}"));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void SyncListErrorForGenericStructWithCustomSerializeOnly()
|
||||||
|
{
|
||||||
|
Assert.That(CompilationFinishedHook.WeaveFailed, Is.True);
|
||||||
|
string weaverError = @"Mirror\.Weaver error:";
|
||||||
|
string type = @"MirrorTest\.MyGenericStructList";
|
||||||
|
string errorMessage = @"Can not create Serialize or Deserialize for generic element\. Override virtual methods with custom Serialize and Deserialize to use MirrorTest.MyGenericStruct`1<System.Single> in SyncList";
|
||||||
|
Assert.That(weaverErrors, Has.Some.Match($"{weaverError} {type} {errorMessage}"));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void SyncListGenericStructWithCustomMethods()
|
||||||
{
|
{
|
||||||
Assert.That(CompilationFinishedHook.WeaveFailed, Is.False);
|
Assert.That(CompilationFinishedHook.WeaveFailed, Is.False);
|
||||||
Assert.That(weaverErrors, Is.Empty);
|
Assert.That(weaverErrors, Is.Empty);
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
namespace MirrorTest
|
namespace MirrorTest
|
||||||
{
|
{
|
||||||
class MirrorTestPlayer : NetworkBehaviour
|
class SyncListValid : NetworkBehaviour
|
||||||
{
|
{
|
||||||
public SyncListInt Foo;
|
public SyncListInt Foo;
|
||||||
}
|
}
|
@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
namespace MirrorTest
|
namespace MirrorTest
|
||||||
{
|
{
|
||||||
class MirrorTestPlayer : NetworkBehaviour
|
class SyncListByteValid : NetworkBehaviour
|
||||||
{
|
{
|
||||||
class MyByteClass : SyncList<byte> {};
|
class MyByteClass : SyncList<byte> {};
|
||||||
|
|
||||||
|
@ -0,0 +1,16 @@
|
|||||||
|
using Mirror;
|
||||||
|
|
||||||
|
namespace MirrorTest
|
||||||
|
{
|
||||||
|
class SyncListErrorForGenericStruct : NetworkBehaviour
|
||||||
|
{
|
||||||
|
MyGenericStructList harpseals;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct MyGenericStruct<T>
|
||||||
|
{
|
||||||
|
T genericpotato;
|
||||||
|
}
|
||||||
|
|
||||||
|
class MyGenericStructList : SyncList<MyGenericStruct<float>> { };
|
||||||
|
}
|
@ -0,0 +1,22 @@
|
|||||||
|
using Mirror;
|
||||||
|
|
||||||
|
namespace MirrorTest
|
||||||
|
{
|
||||||
|
class SyncListErrorForGenericStructWithCustomDeserializeOnly : NetworkBehaviour
|
||||||
|
{
|
||||||
|
MyGenericStructList harpseals;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct MyGenericStruct<T>
|
||||||
|
{
|
||||||
|
public T genericpotato;
|
||||||
|
}
|
||||||
|
|
||||||
|
class MyGenericStructList : SyncList<MyGenericStruct<float>>
|
||||||
|
{
|
||||||
|
protected override MyGenericStruct<float> DeserializeItem(NetworkReader reader)
|
||||||
|
{
|
||||||
|
return new MyGenericStruct<float>() { genericpotato = reader.ReadSingle() };
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
@ -0,0 +1,22 @@
|
|||||||
|
using Mirror;
|
||||||
|
|
||||||
|
namespace MirrorTest
|
||||||
|
{
|
||||||
|
class SyncListErrorForGenericStructWithCustomSerializeOnly : NetworkBehaviour
|
||||||
|
{
|
||||||
|
MyGenericStructList harpseals;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct MyGenericStruct<T>
|
||||||
|
{
|
||||||
|
public T genericpotato;
|
||||||
|
}
|
||||||
|
|
||||||
|
class MyGenericStructList : SyncList<MyGenericStruct<float>>
|
||||||
|
{
|
||||||
|
protected override void SerializeItem(NetworkWriter writer, MyGenericStruct<float> item)
|
||||||
|
{
|
||||||
|
writer.WriteSingle(item.genericpotato);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
@ -0,0 +1,13 @@
|
|||||||
|
using Mirror;
|
||||||
|
|
||||||
|
namespace MirrorTest
|
||||||
|
{
|
||||||
|
class SyncListGenericAbstractInheritance : NetworkBehaviour
|
||||||
|
{
|
||||||
|
readonly SomeListInt superSyncListString = new SomeListInt();
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract class SomeList<T> : SyncList<T> { }
|
||||||
|
|
||||||
|
public class SomeListInt : SomeList<int> { }
|
||||||
|
}
|
@ -0,0 +1,13 @@
|
|||||||
|
using Mirror;
|
||||||
|
|
||||||
|
namespace MirrorTest
|
||||||
|
{
|
||||||
|
class SyncListGenericInheritance : NetworkBehaviour
|
||||||
|
{
|
||||||
|
readonly SomeListInt someList = new SomeListInt();
|
||||||
|
}
|
||||||
|
|
||||||
|
public class SomeList<T> : SyncList<T> { }
|
||||||
|
|
||||||
|
public class SomeListInt : SomeList<int> { }
|
||||||
|
}
|
@ -0,0 +1,19 @@
|
|||||||
|
using Mirror;
|
||||||
|
|
||||||
|
namespace MirrorTest
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
This test will fail
|
||||||
|
It is hard to know which generic argument we want from `SomeList<string, int>`
|
||||||
|
So instead give a useful error for this edge case
|
||||||
|
*/
|
||||||
|
|
||||||
|
class SyncListGenericInheritanceWithMultipleGeneric : NetworkBehaviour
|
||||||
|
{
|
||||||
|
readonly SomeListInt someList = new SomeListInt();
|
||||||
|
}
|
||||||
|
|
||||||
|
public class SomeList<G, T> : SyncList<T> { }
|
||||||
|
|
||||||
|
public class SomeListInt : SomeList<string, int> { }
|
||||||
|
}
|
@ -0,0 +1,27 @@
|
|||||||
|
using Mirror;
|
||||||
|
|
||||||
|
namespace MirrorTest
|
||||||
|
{
|
||||||
|
class SyncListGenericStructWithCustomMethods : NetworkBehaviour
|
||||||
|
{
|
||||||
|
MyGenericStructList harpseals;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct MyGenericStruct<T>
|
||||||
|
{
|
||||||
|
public T genericpotato;
|
||||||
|
}
|
||||||
|
|
||||||
|
class MyGenericStructList : SyncList<MyGenericStruct<float>>
|
||||||
|
{
|
||||||
|
protected override void SerializeItem(NetworkWriter writer, MyGenericStruct<float> item)
|
||||||
|
{
|
||||||
|
writer.WriteSingle(item.genericpotato);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override MyGenericStruct<float> DeserializeItem(NetworkReader reader)
|
||||||
|
{
|
||||||
|
return new MyGenericStruct<float>() { genericpotato = reader.ReadSingle() };
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
@ -0,0 +1,15 @@
|
|||||||
|
using UnityEngine;
|
||||||
|
using Mirror;
|
||||||
|
|
||||||
|
namespace MirrorTest
|
||||||
|
{
|
||||||
|
class SyncListInheritance : NetworkBehaviour
|
||||||
|
{
|
||||||
|
readonly SuperSyncListString superSyncListString = new SuperSyncListString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public class SuperSyncListString : SyncListString
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -3,15 +3,15 @@
|
|||||||
|
|
||||||
namespace MirrorTest
|
namespace MirrorTest
|
||||||
{
|
{
|
||||||
class MirrorTestPlayer : NetworkBehaviour
|
class SyncListMissingParamlessCtor : NetworkBehaviour
|
||||||
{
|
{
|
||||||
public class SyncListString2 : SyncList<string>
|
|
||||||
{
|
|
||||||
public SyncListString2(int phooey) {}
|
|
||||||
protected override void SerializeItem(NetworkWriter w, string item) {}
|
|
||||||
protected override string DeserializeItem(NetworkReader r) => "";
|
|
||||||
}
|
|
||||||
|
|
||||||
public SyncListString2 Foo;
|
public SyncListString2 Foo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class SyncListString2 : SyncList<string>
|
||||||
|
{
|
||||||
|
public SyncListString2(int phooey) {}
|
||||||
|
protected override void SerializeItem(NetworkWriter w, string item) {}
|
||||||
|
protected override string DeserializeItem(NetworkReader r) => "";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,17 @@
|
|||||||
|
using UnityEngine;
|
||||||
|
using Mirror;
|
||||||
|
|
||||||
|
namespace MirrorTest
|
||||||
|
{
|
||||||
|
class SyncListMissingParamlessCtorManuallyInitialized : NetworkBehaviour
|
||||||
|
{
|
||||||
|
public SyncListString2 Foo = new SyncListString2(20);
|
||||||
|
}
|
||||||
|
|
||||||
|
public class SyncListString2 : SyncList<string>
|
||||||
|
{
|
||||||
|
public SyncListString2(int phooey) {}
|
||||||
|
protected override void SerializeItem(NetworkWriter w, string item) {}
|
||||||
|
protected override string DeserializeItem(NetworkReader r) => "";
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,21 @@
|
|||||||
|
using UnityEngine;
|
||||||
|
using Mirror;
|
||||||
|
|
||||||
|
namespace MirrorTest
|
||||||
|
{
|
||||||
|
class SyncListNestedStruct : NetworkBehaviour
|
||||||
|
{
|
||||||
|
SomeAbstractClass.MyNestedStructList Foo;
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract class SomeAbstractClass
|
||||||
|
{
|
||||||
|
public struct MyNestedStruct
|
||||||
|
{
|
||||||
|
public int potato;
|
||||||
|
public float floatingpotato;
|
||||||
|
public double givemetwopotatoes;
|
||||||
|
}
|
||||||
|
public class MyNestedStructList : SyncList<MyNestedStruct> { }
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,20 @@
|
|||||||
|
using UnityEngine;
|
||||||
|
using Mirror;
|
||||||
|
|
||||||
|
namespace MirrorTest
|
||||||
|
{
|
||||||
|
class SyncListNestedStructWithInvalid : NetworkBehaviour
|
||||||
|
{
|
||||||
|
SomeAbstractClass.MyNestedStructList Foo;
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract class SomeAbstractClass
|
||||||
|
{
|
||||||
|
public struct MyNestedStruct
|
||||||
|
{
|
||||||
|
public int potato;
|
||||||
|
public Object target;
|
||||||
|
}
|
||||||
|
public class MyNestedStructList : SyncList<MyNestedStruct> { }
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,17 @@
|
|||||||
|
using UnityEngine;
|
||||||
|
using Mirror;
|
||||||
|
|
||||||
|
namespace MirrorTest
|
||||||
|
{
|
||||||
|
class SyncListNestedStruct : NetworkBehaviour
|
||||||
|
{
|
||||||
|
SomeData.SyncList Foo;
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct SomeData
|
||||||
|
{
|
||||||
|
public int usefulNumber;
|
||||||
|
|
||||||
|
public class SyncList : Mirror.SyncList<SomeData> { }
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,18 @@
|
|||||||
|
using UnityEngine;
|
||||||
|
using Mirror;
|
||||||
|
|
||||||
|
namespace MirrorTest
|
||||||
|
{
|
||||||
|
class SyncListNestedInStructWithInvalid : NetworkBehaviour
|
||||||
|
{
|
||||||
|
SomeData.SyncList Foo;
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct SomeData
|
||||||
|
{
|
||||||
|
public int usefulNumber;
|
||||||
|
public Object target;
|
||||||
|
|
||||||
|
public class SyncList : Mirror.SyncList<SomeData> { }
|
||||||
|
}
|
||||||
|
}
|
@ -3,15 +3,16 @@
|
|||||||
|
|
||||||
namespace MirrorTest
|
namespace MirrorTest
|
||||||
{
|
{
|
||||||
class MirrorTestPlayer : NetworkBehaviour
|
class SyncListNestedStruct : NetworkBehaviour
|
||||||
{
|
{
|
||||||
struct MyStruct
|
MyNestedStructList Foo;
|
||||||
|
|
||||||
|
struct MyNestedStruct
|
||||||
{
|
{
|
||||||
int potato;
|
int potato;
|
||||||
float floatingpotato;
|
float floatingpotato;
|
||||||
double givemetwopotatoes;
|
double givemetwopotatoes;
|
||||||
}
|
}
|
||||||
class MyStructClass : SyncList<MyStruct> {};
|
class MyNestedStructList : SyncList<MyNestedStruct> { }
|
||||||
MyStructClass Foo;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -0,0 +1,17 @@
|
|||||||
|
using UnityEngine;
|
||||||
|
using Mirror;
|
||||||
|
|
||||||
|
namespace MirrorTest
|
||||||
|
{
|
||||||
|
class SyncListStruct : NetworkBehaviour
|
||||||
|
{
|
||||||
|
MyStructList Foo;
|
||||||
|
}
|
||||||
|
struct MyStruct
|
||||||
|
{
|
||||||
|
int potato;
|
||||||
|
float floatingpotato;
|
||||||
|
double givemetwopotatoes;
|
||||||
|
}
|
||||||
|
class MyStructList : SyncList<MyStruct> { }
|
||||||
|
}
|
@ -0,0 +1,23 @@
|
|||||||
|
using UnityEngine;
|
||||||
|
using Mirror;
|
||||||
|
|
||||||
|
namespace MirrorTest
|
||||||
|
{
|
||||||
|
class SyncListStructWithCustomDeserializeOnly : NetworkBehaviour
|
||||||
|
{
|
||||||
|
MyStructList Foo;
|
||||||
|
}
|
||||||
|
struct MyStruct
|
||||||
|
{
|
||||||
|
int potato;
|
||||||
|
float floatingpotato;
|
||||||
|
double givemetwopotatoes;
|
||||||
|
}
|
||||||
|
class MyStructList : SyncList<MyStruct>
|
||||||
|
{
|
||||||
|
protected override MyStruct DeserializeItem(NetworkReader reader)
|
||||||
|
{
|
||||||
|
return new MyStruct() { /* read some stuff here */ };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,28 @@
|
|||||||
|
using UnityEngine;
|
||||||
|
using Mirror;
|
||||||
|
|
||||||
|
namespace MirrorTest
|
||||||
|
{
|
||||||
|
class SyncListStructWithCustomMethods : NetworkBehaviour
|
||||||
|
{
|
||||||
|
MyStructList Foo;
|
||||||
|
}
|
||||||
|
struct MyStruct
|
||||||
|
{
|
||||||
|
int potato;
|
||||||
|
float floatingpotato;
|
||||||
|
double givemetwopotatoes;
|
||||||
|
}
|
||||||
|
class MyStructList : SyncList<MyStruct>
|
||||||
|
{
|
||||||
|
protected override void SerializeItem(NetworkWriter writer, MyStruct item)
|
||||||
|
{
|
||||||
|
// write some stuff here
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override MyStruct DeserializeItem(NetworkReader reader)
|
||||||
|
{
|
||||||
|
return new MyStruct() { /* read some stuff here */ };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,23 @@
|
|||||||
|
using UnityEngine;
|
||||||
|
using Mirror;
|
||||||
|
|
||||||
|
namespace MirrorTest
|
||||||
|
{
|
||||||
|
class SyncListStructWithCustomSerializeOnly : NetworkBehaviour
|
||||||
|
{
|
||||||
|
MyStructList Foo;
|
||||||
|
}
|
||||||
|
struct MyStruct
|
||||||
|
{
|
||||||
|
int potato;
|
||||||
|
float floatingpotato;
|
||||||
|
double givemetwopotatoes;
|
||||||
|
}
|
||||||
|
class MyStructList : SyncList<MyStruct>
|
||||||
|
{
|
||||||
|
protected override void SerializeItem(NetworkWriter writer, MyStruct item)
|
||||||
|
{
|
||||||
|
// write some stuff here
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user