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>
|
||||
public static void Process(TypeDefinition td)
|
||||
{
|
||||
SyncObjectProcessor.GenerateSerialization(td, 0, "SerializeKey", "DeserializeKey");
|
||||
SyncObjectProcessor.GenerateSerialization(td, 1, "SerializeItem", "DeserializeItem");
|
||||
GenericArgumentResolver resolver = new GenericArgumentResolver(2);
|
||||
|
||||
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
|
||||
/// </summary>
|
||||
/// <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);
|
||||
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;
|
||||
}
|
||||
MethodReference objectConstructor = Weaver.CurrentAssembly.MainModule.ImportReference(ctor);
|
||||
|
@ -10,19 +10,11 @@ public static class SyncObjectProcessor
|
||||
/// </summary>
|
||||
/// <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="baseType">the type that has generic arguments</param>
|
||||
/// <param name="serializeMethod">The name of the serialize 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);
|
||||
|
||||
MethodReference writeItemFunc = GenerateSerialization(serializeMethod, td, itemType);
|
||||
@ -47,6 +39,15 @@ static MethodReference GenerateSerialization(string methodName, TypeDefinition t
|
||||
if (existing != null)
|
||||
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 |
|
||||
MethodAttributes.Virtual |
|
||||
MethodAttributes.Public |
|
||||
@ -57,12 +58,6 @@ static MethodReference GenerateSerialization(string methodName, TypeDefinition t
|
||||
serializeFunc.Parameters.Add(new ParameterDefinition("item", ParameterAttributes.None, itemType));
|
||||
ILProcessor serWorker = serializeFunc.Body.GetILProcessor();
|
||||
|
||||
if (itemType.IsGenericInstance)
|
||||
{
|
||||
Weaver.Error($"{td} cannot have generic elements {itemType}");
|
||||
return null;
|
||||
}
|
||||
|
||||
MethodReference writeFunc = Writers.GetWriteFunc(itemType);
|
||||
if (writeFunc != null)
|
||||
{
|
||||
@ -88,6 +83,14 @@ static MethodReference GenerateDeserialization(string methodName, TypeDefinition
|
||||
if (existing != null)
|
||||
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 |
|
||||
MethodAttributes.Virtual |
|
||||
MethodAttributes.Public |
|
||||
|
@ -406,8 +406,7 @@ static bool WeaveMessage(TypeDefinition td)
|
||||
|
||||
static bool WeaveSyncObject(TypeDefinition td)
|
||||
{
|
||||
if (!td.IsClass)
|
||||
return false;
|
||||
bool modified = false;
|
||||
|
||||
// ignore generic classes
|
||||
// we can not process generic classes
|
||||
@ -415,16 +414,22 @@ static bool WeaveSyncObject(TypeDefinition td)
|
||||
if (td.HasGenericParameters)
|
||||
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
|
||||
|
||||
// We cant early return with non classes or Abstract classes
|
||||
// because we still need to check for embeded types
|
||||
if (td.IsClass || !td.IsAbstract)
|
||||
{
|
||||
if (td.IsDerivedFrom(SyncListType))
|
||||
{
|
||||
SyncListProcessor.Process(td);
|
||||
SyncListProcessor.Process(td, SyncListType);
|
||||
modified = true;
|
||||
}
|
||||
else if (td.IsDerivedFrom(SyncSetType))
|
||||
{
|
||||
SyncListProcessor.Process(td);
|
||||
SyncListProcessor.Process(td, SyncSetType);
|
||||
modified = true;
|
||||
}
|
||||
else if (td.IsDerivedFrom(SyncDictionaryType))
|
||||
@ -432,6 +437,7 @@ static bool WeaveSyncObject(TypeDefinition td)
|
||||
SyncDictionaryProcessor.Process(td);
|
||||
modified = true;
|
||||
}
|
||||
}
|
||||
|
||||
// check for embedded types
|
||||
foreach (TypeDefinition embedded in td.NestedTypes)
|
||||
|
@ -5,7 +5,43 @@ namespace Mirror.Weaver.Tests
|
||||
public class WeaverSyncListTests : WeaverTestsBuildFromTestName
|
||||
{
|
||||
[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(weaverErrors, Is.Empty);
|
||||
@ -15,11 +51,116 @@ public void SyncListValid()
|
||||
public void SyncListMissingParamlessCtor()
|
||||
{
|
||||
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 SyncListByteValid()
|
||||
public void SyncListMissingParamlessCtorManuallyInitialized()
|
||||
{
|
||||
Assert.That(CompilationFinishedHook.WeaveFailed, Is.False);
|
||||
Assert.That(weaverErrors, Is.Empty);
|
||||
}
|
||||
|
||||
[Test]
|
||||
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(weaverErrors, Is.Empty);
|
||||
|
@ -3,7 +3,7 @@
|
||||
|
||||
namespace MirrorTest
|
||||
{
|
||||
class MirrorTestPlayer : NetworkBehaviour
|
||||
class SyncListValid : NetworkBehaviour
|
||||
{
|
||||
public SyncListInt Foo;
|
||||
}
|
@ -3,7 +3,7 @@
|
||||
|
||||
namespace MirrorTest
|
||||
{
|
||||
class MirrorTestPlayer : NetworkBehaviour
|
||||
class SyncListByteValid : NetworkBehaviour
|
||||
{
|
||||
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
|
||||
{
|
||||
class MirrorTestPlayer : NetworkBehaviour
|
||||
class SyncListMissingParamlessCtor : NetworkBehaviour
|
||||
{
|
||||
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) => "";
|
||||
}
|
||||
|
||||
public SyncListString2 Foo;
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
{
|
||||
class MirrorTestPlayer : NetworkBehaviour
|
||||
class SyncListNestedStruct : NetworkBehaviour
|
||||
{
|
||||
struct MyStruct
|
||||
MyNestedStructList Foo;
|
||||
|
||||
struct MyNestedStruct
|
||||
{
|
||||
int potato;
|
||||
float floatingpotato;
|
||||
double givemetwopotatoes;
|
||||
}
|
||||
class MyStructClass : SyncList<MyStruct> {};
|
||||
MyStructClass Foo;
|
||||
class MyNestedStructList : SyncList<MyNestedStruct> { }
|
||||
}
|
||||
}
|
@ -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